Text-to-Image
Diffusers
Safetensors
English
MOSSImagePipeline
moss-image
16gb
structured-pruning
alpha
Instructions to use AiArtLab/mossling with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use AiArtLab/mossling 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/mossling", 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: 10,376 Bytes
2e26e31 | 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | #!/usr/bin/env python3
"""Измерения и проверки для сжатия DiT: эталон, удаление блоков, метрики (LPIPS + CLIP).
python3 lab/lab.py baseline # база: картинки + метрики-эталон
python3 lab/lab.py drop 2 3 8 # удалить блоки (0-based) и измерить
Метрика — против базы с тем же сидом: LPIPS (ниже = ближе), CLIP-скор (выше = точнее промпт).
Всё в 512 px, 20 шагов, guidance 3.0, uncond_skip_layers=[] (иначе индексы блоков съезжают).
"""
import argparse
import glob
import json
import os
import sys
import time
import torch
import torch.nn.functional as F
from PIL import Image
MOSS = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # папка модели
LAB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "work") # кэши и прогоны
# diffusers ищет компоненты из model_index.json через importlib по имени модуля
# ("pipeline_moss_image"), поэтому папка модели обязана быть на sys.path.
if MOSS not in sys.path:
sys.path.insert(0, MOSS)
EVAL = [
"A red fox walks through a snowy forest at dusk, warm light breaking through the pines, 85mm",
'A vintage enamel sign bolted to a red brick wall, the sign reads "OPEN 24 HOURS", peeling paint',
"1girl, solo, long silver hair, blue eyes, black coat, tokyo street at night, cinematic",
'{"layout":[{"text":"SALE 50%","box":[40,40,420,120]},{"text":"only today","box":[40,600,420,660]}]}',
"Close-up portrait of an old fisherman, weathered skin, grey beard, sea spray, soft window light",
"A rustic wooden table with a bowl of ripe pears, a knife and a glass of red wine, morning sun",
"Two husky puppies playing in deep snow, motion blur, low winter sun, telephoto",
"A brutalist concrete library at dusk, warm interior glow through narrow windows, wet plaza",
"A matte black ceramic coffee mug on a linen cloth, product photography, soft shadow, 100mm macro",
"Abstract flowing ink in water, teal and amber, macro, high contrast, black background",
]
SIZE, STEPS, GUIDANCE, SEED = 512, 20, 3.0, 1234
# ---------------------------------------------------------------- модель и условия
def build():
from diffusers import DiffusionPipeline
pipe = DiffusionPipeline.from_pretrained(
MOSS, custom_pipeline="pipeline_moss_image", trust_remote_code=True,
dtype={"default": torch.bfloat16, "vae": torch.float32})
pipe.set_progress_bar_config(disable=True)
return pipe.to("cuda")
def conditions(pipe):
"""Эмбеддинги промптов и пустого негатива (считает адаптерный ТЕ), затем ТЕ отпускаем."""
import zlib
key = zlib.crc32("|".join(EVAL).encode()) # hash() рандомизирован между процессами
path = os.path.join(LAB, f"eval_cond_{len(EVAL)}_{key}.pt")
if os.path.exists(path):
return torch.load(path)
with torch.no_grad():
emb, mask = pipe.encode_prompt(EVAL, 4096)
nemb, nmask = pipe.encode_prompt([""], 4096)
cond = (emb.cpu(), mask.cpu(), nemb.cpu(), nmask.cpu())
torch.save(cond, path)
return cond
@torch.no_grad()
def sample(pipe, cond, size=SIZE, steps=STEPS, seed=SEED, guidance=GUIDANCE, prompts=None):
emb, mask, nemb, nmask = (t.to("cuda") for t in cond)
out = []
n = emb.shape[0] if prompts is None else len(prompts)
for i in range(n):
g = torch.Generator("cuda").manual_seed(seed)
out.append(pipe(prompt_embeds=emb[i:i + 1], prompt_attention_mask=mask[i:i + 1],
negative_prompt_embeds=nemb, negative_prompt_attention_mask=nmask,
height=size, width=size, num_inference_steps=steps,
guidance_scale=guidance, generator=g,
uncond_skip_layers=[]).images[0])
return out
def grid(images, path, cols=5):
w = images[0].width
rows = (len(images) + cols - 1) // cols
canvas = Image.new("RGB", (cols * (w + 6), rows * (w + 6)), "white")
for i, im in enumerate(images):
canvas.paste(im, ((i % cols) * (w + 6), (i // cols) * (w + 6)))
canvas.save(path)
# ---------------------------------------------------------------- метрики
class Metrics:
"""LPIPS к базе + CLIP-скор абсолютный. Модели грузятся один раз."""
def __init__(self, device="cuda"):
import lpips
from transformers import CLIPModel, CLIPImageProcessor, CLIPTokenizer
self.lp = lpips.LPIPS(net="alex", verbose=False).to(device).eval()
name = "openai/clip-vit-large-patch14"
self.clip = CLIPModel.from_pretrained(name, local_files_only=True).to(device).eval()
self.ip = CLIPImageProcessor.from_pretrained(name, local_files_only=True)
self.tk = CLIPTokenizer.from_pretrained(name, local_files_only=True)
self.device = device
@torch.no_grad()
def lpips_vs(self, images, base):
if images[0].size != base[0].size:
return None
vals = []
for a, b in zip(images, base):
ta = torch.tensor(list(a.convert("RGB").getdata()), dtype=torch.float32).reshape(a.height, a.width, 3)
tb = torch.tensor(list(b.convert("RGB").getdata()), dtype=torch.float32).reshape(b.height, b.width, 3)
ta = (ta / 127.5 - 1).permute(2, 0, 1)[None].to(self.device)
tb = (tb / 127.5 - 1).permute(2, 0, 1)[None].to(self.device)
vals.append(float(self.lp(ta, tb)))
return sum(vals) / len(vals)
@torch.no_grad()
def clip_score(self, images, prompts):
pix = self.ip(images=[im.convert("RGB") for im in images], return_tensors="pt")["pixel_values"].to(self.device)
tok = self.tk(prompts, padding=True, truncation=True, return_tensors="pt").to(self.device)
out = self.clip(pixel_values=pix, **tok)
ie = F.normalize(out.image_embeds, dim=-1)
te = F.normalize(out.text_embeds, dim=-1)
return float((ie * te).sum(-1).mean() * 100)
# ---------------------------------------------------------------- хирургия
def drop_blocks(tr, idxs):
drop = set(idxs)
tr.layers = torch.nn.ModuleList([l for i, l in enumerate(tr.layers) if i not in drop])
return tr
# ---------------------------------------------------------------- CLI
def cmd_metrics(a):
"""Пересчитать метрики по уже сгенерированным lab/runs/<tag>/ без прогона модели."""
m = Metrics()
base_dir = os.path.join(LAB, "runs", "base")
base = [Image.open(os.path.join(base_dir, f"{i:02d}.png")) for i in range(len(EVAL))]
rows = []
for tag in sorted(os.listdir(os.path.join(LAB, "runs"))):
d = os.path.join(LAB, "runs", tag)
if not os.path.isdir(d) or tag == "base":
continue
files = sorted(glob.glob(os.path.join(d, "*.png")))
if len(files) != len(EVAL):
continue
imgs = [Image.open(f) for f in files]
lp = m.lpips_vs(imgs, base)
rows.append({"tag": tag, "lpips_vs_base": None if lp is None else round(lp, 4),
"clip_score": round(m.clip_score(imgs, EVAL), 2)})
print(json.dumps(rows[-1]), flush=True)
print(json.dumps({"base_clip": round(m.clip_score(base, EVAL), 2)}), flush=True)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("cmd", choices=["baseline", "drop", "metrics"])
ap.add_argument("args", nargs="*")
ap.add_argument("--out", default=None, help="имя прогона в lab/runs/")
ap.add_argument("--size", type=int, default=SIZE)
ap.add_argument("--steps", type=int, default=STEPS)
ap.add_argument("--seed", type=int, default=SEED)
ap.add_argument("--probe", action="store_true", help="только скорость/VRAM, без картинок")
ap.add_argument("--tags", default="", help="сначала собрать модель из слияний lab/lora/<tag>.pt")
a = ap.parse_args()
if a.cmd == "metrics":
cmd_metrics(a)
return
pipe = build()
tr = pipe.transformer.eval()
cond = conditions(pipe)
if os.environ.get("DROP_TE", "1") == "1" and getattr(pipe, "text_encoder", None) is not None:
pipe.text_encoder = None
torch.cuda.empty_cache()
if a.tags:
from window import apply_spec
print("сборка из слияний:", apply_spec(tr, [t for t in a.tags.split(",") if t]), flush=True)
tag = a.out or (a.cmd + "_" + "_".join(a.args))
if a.cmd == "baseline":
tag = a.out or "base"
if a.cmd == "drop":
drop_blocks(tr, [int(x) for x in a.args])
n_par = sum(p.numel() for p in tr.parameters())
t0 = time.time()
images = sample(pipe, cond, size=a.size, steps=a.steps, seed=a.seed)
dt = time.time() - t0
run_dir = os.path.join(LAB, "runs", tag)
os.makedirs(run_dir, exist_ok=True)
for i, im in enumerate(images):
im.save(os.path.join(run_dir, f"{i:02d}.png"))
grid(images, os.path.join(LAB, "runs", f"{tag}.png"))
res = {"tag": tag, "blocks": len(tr.layers), "params_b": round(n_par / 1e9, 3),
"size": a.size, "steps": a.sample_steps, "seed": a.seed,
"gen_seconds": round(dt / len(images), 1),
"peak_vram_gib": round(torch.cuda.max_memory_allocated() / 2 ** 30, 2)}
base_dir = os.path.join(LAB, "runs", "base")
if tag != "base" and os.path.isdir(base_dir):
base_imgs = [Image.open(os.path.join(base_dir, f"{i:02d}.png")) for i in range(len(images))]
if (base_imgs[0].size == images[0].size):
m = Metrics()
lp = m.lpips_vs(images, base_imgs)
res["lpips_vs_base"] = None if lp is None else round(lp, 4)
res["clip_score"] = round(m.clip_score(images, EVAL), 2)
path = os.path.join(LAB, "results.jsonl")
with open(path, "a") as f:
f.write(json.dumps(res, ensure_ascii=False) + "\n")
print(json.dumps(res, ensure_ascii=False), flush=True)
if __name__ == "__main__":
main()
|