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
File size: 5,438 Bytes
b4f5f26 cb6884f 9b3ae10 cb6884f b4f5f26 cb6884f b4f5f26 cb6884f 9b3ae10 b4f5f26 cb6884f b4f5f26 cb6884f 234d8d3 cb6884f b4f5f26 cb6884f b4f5f26 cb6884f b4f5f26 8096951 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 | """Экспорт обученного 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 {}
counts = {} # сколько блоков в каждом списке — по ключам тензоров
for k in ck["body"]:
g = k.split(".")[0]
if g[0] in "ed" and "." in k:
idx = k.split(".")[1]
if idx.isdigit():
counts.setdefault(g, set()).add(idx)
nblk = {g: len(v) for g, v in counts.items()}
# конфиг описывает ТОЛЬКО тело; статистики латента — в vae/config.json, шаг — параметр запуска
cfg = {
"_class_name": "UDiT",
"format": "udm/v1",
"storage_dtype": "float16" if args.half else "float32",
"latent_channels": ae_cfg["latent_channels"],
"text_dim": ta.get("text_dim", 768),
"body": {
"kind": "пирамидальный U: ширина растёт к низу",
"widths": [192, 512, 1024, 1536],
"heads": [6, 8, 8, 8],
"blocks": nblk,
"levels": [
{"level": 0, "tokens": "÷1", "width": 192,
"blocks": "conv 6 enc / 4 dec + 2 self-attn на сторону"},
{"level": 1, "tokens": "÷2", "width": 512, "blocks": "attn 5 enc / 4 dec"},
{"level": 2, "tokens": "÷4", "width": 1024, "blocks": "attn 5 enc / 4 dec"},
{"level": 3, "tokens": "÷8", "width": 1536, "blocks": "attn 4 (боттлнек)"},
],
"skips": "аддитивные (SD-стиль): e0/e1/e2 прибавляются в декодере",
"down": "mean-pool 2x + Linear",
"up": "Linear + nearest 2x",
"positions": {
"rope": {"kind": "2d", "base": 100, "phase": "в латентных пикселях на каждом уровне"},
"absolute": {"kind": "2d sincos", "dim": 64,
"into": "zero-init Linear(64 -> width) к выходу stem, в паре с RoPE"},
},
"text": "кросс-внимание на всех уровнях",
"parametrization": "x0 латента",
"params": round(sum(v.numel() for k, v in tensors.items() if k.startswith("body.")) / 1e6, 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['body']['params']}M, блоки {nblk}")
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"))
if getattr(ae, "decoder", None) is not None and any(k.startswith("tail.") for k in sd):
ae.decoder.load_state_dict(sub("tail")) # хвоста может не быть (--vae sdxs)
cfg_path = path / "config.json"
return json.loads(cfg_path.read_text()) if cfg_path.exists() else {}
if __name__ == "__main__":
main()
|