Text-to-Image
Diffusers
Safetensors
flow-matching
pyramidal-unet
256x256
ettin-150m
from-scratch
ra
udit
Instructions to use AiArtLab/Ra with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use AiArtLab/Ra with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("AiArtLab/Ra", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
File size: 6,245 Bytes
b4f5f26 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | """Экспорт обученного UDM в `transformer/`: safetensors + config.json.
Три части в одном файле, по префиксам:
body.* — тело (8 блоков из энкодера AE + 4 новых), поток в латентном пространстве
text_mixer.* — обучаемый выбор слоёв Ettin (SliceMixer)
tail.* — декодер AE (рендер латента в пиксели), дообученный как обычный flow
python3 src/export.py --ckpt tmp/run256/last.pt --out transformer
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import torch
from safetensors.torch import save_file
ROOT = Path(__file__).resolve().parent.parent
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--ckpt", default=str(ROOT / "tmp" / "run256" / "last.pt"))
ap.add_argument("--out", default=str(ROOT / "transformer"))
ap.add_argument("--metrics", default="", help="дописать метрики в config.json (строка)")
ap.add_argument("--half", action="store_true", help="хранить веса в fp16 (вдвое меньше, для публикации)")
args = ap.parse_args()
ck = torch.load(args.ckpt, map_location="cpu")
tensors = {}
for name, sd in (("body", ck["body"]), ("text_mixer", ck["mixer"]), ("tail", ck.get("decoder") or {})):
for k, v in sd.items():
tensors[f"{name}.{k}"] = (v.half() if args.half else v).contiguous()
out = Path(args.out)
out.mkdir(parents=True, exist_ok=True)
save_file(tensors, str(out / "diffusion_pytorch_model.safetensors"))
ae_cfg = json.loads((ROOT / "vae" / "config.json").read_text())
ta = ck.get("args") or {} # чекпойнт помнит свои аргументы — не сочиняем заново
training = {
"dataset": ta.get("hf_dataset") or ta.get("data") or "~/dataset/butterflies_256x64",
"parts": ta.get("parts"),
"size": ta.get("size"),
"steps": int(ck["step"]),
"batch": ta.get("batch", 8),
"accum": ta.get("accum", 1),
"lr": ta.get("lr", 1e-4),
"lr_tail": ta.get("lr_tail", 3e-5),
"drop_text": ta.get("drop_text", 0.1),
"init": ta.get("init"),
"metrics": args.metrics,
}
cfg = {
"_class_name": "UDiT",
"name": "Ra",
"format": "udm/v1",
"step": int(ck["step"]),
"latent": {
"channels": ae_cfg["latent_channels"],
"spatial_factor": ae_cfg["patch_size"],
"mean": ae_cfg["latent_mean"],
"std": ae_cfg["latent_std"],
"source": "vae/ (Muinez/f8c32-ae)",
},
"text": {
"encoder": "text_encoder/ (jhu-clsp/ettin-encoder-150m, 768-dim, 22 слоя)",
"max_len": 192,
"slices": [2, 5, 8, 11, 14, 17, 20],
"slice_blocks": 2,
"trainable": "text_mixer.*",
},
"body": {
"kind": "пирамидальный U: ширина растёт к низу",
"levels": [
{"level": 0, "tokens": "÷1", "width": 192, "blocks": "conv 6 enc / 4 dec + 1 self-attn на сторону",
"text": "кросс-внимание в каждом conv-блоке и в self-attn"},
{"level": 1, "tokens": "÷2", "width": 512, "blocks": "attn 4 enc / 3 dec", "text": "кросс-внимание"},
{"level": 2, "tokens": "÷4", "width": 1024, "blocks": "attn 3 enc / 2 dec", "text": "кросс-внимание"},
{"level": 3, "tokens": "÷8", "width": 1536, "blocks": "attn 2 (боттлнек)", "text": "кросс-внимание"},
],
"skips": "аддитивные (SD-стиль): e0/e1/e2 прибавляются в декодере",
"down": "mean-pool 2× + Linear",
"up": "Linear + nearest 2×",
"rope": "2D RoPE, base 100, фаза в латентных пикселях на каждом уровне",
"parametrization": "x0 латента",
},
"tail": {
"source": "декодер AE, 6 блоков @512, PixelShuffle ×3",
"trained_as": "flow по пикселям, условие z0 + (ошибка тела).detach()",
"sample_steps": 4,
},
"params": {
"body": round(sum(v.numel() for k, v in tensors.items() if k.startswith("body.")) / 1e6, 2),
"text_mixer": round(sum(v.numel() for k, v in tensors.items()
if k.startswith("text_mixer.")) / 1e6, 2),
"tail": round(sum(v.numel() for k, v in tensors.items() if k.startswith("tail.")) / 1e6, 2),
},
"training": {k: v for k, v in training.items() if v is not None},
"storage_dtype": "float16" if args.half else "float32",
}
cfg["params"]["total"] = round(sum(cfg["params"].values()), 2)
(out / "config.json").write_text(json.dumps(cfg, indent=2, ensure_ascii=False))
print(f"сохранено: {out}/diffusion_pytorch_model.safetensors "
f"({sum(v.numel() for v in tensors.values())/1e6:.1f}M параметров, "
f"{(out / 'diffusion_pytorch_model.safetensors').stat().st_size/1e6:.0f} МБ)")
print(f"части: {cfg['params']}")
def load_exported(model, enc, ae, path, device=None):
"""Загрузить из `transformer/` (config.json + safetensors с префиксами body./text_mixer./tail.)."""
import json
from safetensors.torch import load_file
path = Path(path)
sd = load_file(str(path / "diffusion_pytorch_model.safetensors"), device=str(device) if device else None)
def sub(prefix):
return {k[len(prefix) + 1:]: v for k, v in sd.items() if k.startswith(prefix + ".")}
model.body.load_state_dict(sub("body"), strict=False)
enc.mixer.load_state_dict(sub("text_mixer"))
ae.decoder.load_state_dict(sub("tail"))
cfg_path = path / "config.json"
return json.loads(cfg_path.read_text()) if cfg_path.exists() else {}
if __name__ == "__main__":
main()
|