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: 9,231 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 | #!/usr/bin/env python3
"""Шаг 1: цели учителя (Qwen3.5-9B) на диск.
Зачем отдельным шагом: 15.63 ГиБ весов учителя при 16 ГиБ карты не живут вместе ни с чем, а цель
(пост-норм `last_hidden_state` слоя 32) считается один раз на промпт и дальше не меняется.
Кэш позволяет перебирать гиперпараметры адаптера часами без единого прогона учителя.
Формат кэша (shard_XXXXX.pt, torch.save, грузится с weights_only=True):
{"items": [{"ids": int32[L], "y": bf16[L, 4096]}, ...], "texts": [...], "kinds": [...]}
Пробы и val — те же поля отдельными файлами, плюс meta.json с параметрами и статистикой норм.
python3 src/cache_targets.py --prompts-dir ~/dataset --out cache/teacher --len 256
# продолжить прерванный прогон: те же аргументы, готовые шарды пропускаются
"""
import argparse
import json
import os
import sys
import time
import numpy as np
import torch
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from moss_lib import MOSS_DIR, encode, load_teacher, load_tokenizer # noqa: E402
from prompts import NEGATIVES, PROBE, load_prompts # noqa: E402
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def save_items(path, items, texts, kinds):
torch.save({"items": items, "texts": texts, "kinds": kinds}, path)
def load_items(path):
return torch.load(path, map_location="cpu", weights_only=True)
def main():
ap = argparse.ArgumentParser(description="цели учителя MOSS (Qwen3.5-9B) -> кэш на диске")
ap.add_argument("--moss-dir", default=MOSS_DIR)
ap.add_argument("--teacher-dir", default=None, help="папка учителя (по умолчанию <moss-dir>/text_encoder)")
ap.add_argument("--prompts-dir", default=os.path.expanduser("~/dataset"))
ap.add_argument("--out", default=os.path.join(ROOT, "cache", "teacher"))
ap.add_argument("--limit", type=int, default=0, help="0 — весь пул")
ap.add_argument("--len", type=int, default=256, help="макс. длина последовательности (обрезка хвоста)")
ap.add_argument("--shard", type=int, default=500, help="промптов в шарде")
ap.add_argument("--val-size", type=int, default=128, help="сколько промптов отложить на val")
ap.add_argument("--batch", type=int, default=1,
help="промптов за один forward; 1 — бит в бит как в пайплайне, >1 меняет "
"цель на ~0.5%% (разные блоки ядер, bf16)")
ap.add_argument("--prefer-8bit", action="store_true", help="сразу 8 бит, без попытки bf16")
ap.add_argument("--seed", type=int, default=0)
args = ap.parse_args()
tok = load_tokenizer(args.moss_dir)
pool = load_prompts(args.prompts_dir, limit=args.limit, seed=args.seed)
if len(pool) < args.val_size + 1:
raise SystemExit(f"пул слишком мал: {len(pool)}")
# спец-промпты (пустой + негативы) отдельным файлом, val — хвост пула
special_texts = [""] + list(NEGATIVES)
special_kinds = ["empty"] + ["neg"] * len(NEGATIVES)
val_texts = pool[:args.val_size]
train_texts = pool[args.val_size:]
probe_texts = [t for _, t in PROBE]
print(f"кэш: {len(train_texts)} обучающих + special {len(special_texts)} + val {len(val_texts)} "
f"+ probe {len(probe_texts)}, len={args.len}", flush=True)
t0 = time.time()
ids = encode(tok, special_texts + train_texts + val_texts + probe_texts, args.len)
n_special, n_train, n_val = len(special_texts), len(train_texts), len(val_texts)
print(f"токенизация: {time.time() - t0:.1f} c, медиана длины "
f"{int(np.median([len(r) for r in ids[n_special:n_special + n_train]]))} из {args.len}",
flush=True)
teacher, mode = load_teacher(prefer_8bit=args.prefer_8bit, path=args.teacher_dir)
def targets(rows):
"""list[list[int]] -> list[(ids, y)]: батч с правым паддингом, цель обрезается по длине.
Паддинг справа + маска: реальные токены от него не зависят (внимание причинное), это
проверяется selfcheck-ом ниже.
"""
out = []
for s in range(0, len(rows), args.batch):
chunk = rows[s:s + args.batch]
length = max(len(r) for r in chunk)
idb = torch.full((len(chunk), length), tok.pad_token_id, dtype=torch.long)
mb = torch.zeros((len(chunk), length), dtype=torch.long)
for i, r in enumerate(chunk):
idb[i, :len(r)] = torch.tensor(r)
mb[i, :len(r)] = 1
y = teacher.last_hidden(idb.to("cuda"), mb.to("cuda"))
if not torch.isfinite(y).all(): # 8-бит считает в fp16: ловим переполнение сразу
raise SystemExit("в цели NaN/inf — учитель посчитал в fp16 и переполнился")
for i, r in enumerate(chunk):
out.append((torch.tensor(r, dtype=torch.int32),
y[i, :len(r)].to(torch.bfloat16).cpu()))
return out
if args.batch > 1: # паддинг в батче меняет только округление bf16
check_rows = ids[:4]
single = torch.cat([y for r in check_rows for _, y in targets([r])]).float()
batched = torch.cat([y for _, y in targets(check_rows)]).float()
rel = (single - batched).abs().max().item() / single.norm(dim=-1).mean().item()
print(f" selfcheck: батч меняет цель на {rel * 100:.2f}% (округление bf16, ждём <1%)", flush=True)
if rel > 0.01:
raise SystemExit("батч с паддингом меняет цель сильнее округления — считай --batch 1")
os.makedirs(args.out, exist_ok=True)
special = [{"ids": r, "y": y} for r, y in targets(ids[:n_special])]
save_items(os.path.join(args.out, "special.pt"), special, special_texts, special_kinds)
probe = [{"ids": r, "y": y} for r, y in targets(ids[n_special + n_train + n_val:])]
save_items(os.path.join(args.out, "probe.pt"), probe, probe_texts, [n for n, _ in PROBE])
val = [{"ids": r, "y": y} for r, y in targets(ids[n_special + n_train:n_special + n_train + n_val])]
save_items(os.path.join(args.out, "val.pt"), val, val_texts, ["val"] * n_val)
norms, shards, tok_total, done, t0 = [], [], 0, 0, time.time()
for s in range(0, n_train, args.shard):
path = os.path.join(args.out, f"shard_{s // args.shard:05d}.pt")
shards.append(os.path.basename(path))
if os.path.exists(path): # возобновление прерванного прогона
data = load_items(path)
items = data["items"]
done += len(items)
else:
chunk = list(range(s, min(s + args.shard, n_train)))
items = [{"ids": r, "y": y} for r, y in targets([ids[n_special + i] for i in chunk])]
save_items(path, items, [train_texts[i] for i in chunk], ["pool"] * len(chunk))
done += len(items)
el = time.time() - t0
t0 = time.time()
print(f" {done}/{n_train} промптов, {el:.0f} c на шард, "
f"осталось ~{el * (n_train - done) / max(1, len(items)) / 60:.0f} мин", flush=True)
norms += [it["y"].float().norm(dim=-1).numpy() for it in items]
tok_total += int(sum(it["ids"].numel() for it in items))
norm = np.concatenate(norms)
meta = {"moss_dir": args.moss_dir, "prompts_dir": args.prompts_dir, "teacher_mode": mode,
"len": args.len, "batch": args.batch,
"n_train": n_train, "n_val": n_val, "n_probe": len(probe_texts),
"n_special": n_special, "shards": shards, "tokens": tok_total,
"norms": {f"p{q}": float(np.percentile(norm, q)) for q in (1, 10, 50, 90, 99)},
"norm_mean": float(norm.mean()), "norm_floor": float(np.median(norm))}
with open(os.path.join(args.out, "meta.json"), "w") as f:
json.dump(meta, f, indent=1)
raw = sum(os.path.getsize(os.path.join(args.out, s)) for s in shards)
print(f"готово: {args.out}, {n_train} промптов, {tok_total / 1e6:.1f}M токенов, "
f"{raw / 2**30:.1f} ГиБ; норма цели медиана {meta['norm_floor']:.0f} "
f"(p1 {meta['norms']['p1']:.0f}, p99 {meta['norms']['p99']:.0f}); учитель {mode}", flush=True)
if __name__ == "__main__":
main()
|