TobiasLogic commited on
Commit
a012ee3
·
verified ·
1 Parent(s): f7bb418

PixelModel v5: same architecture, 36x the data

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ examples.png filter=lfs diff=lfs merge=lfs -text
37
+ model.png filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ pipeline_tag: text-to-image
4
+ language:
5
+ - en
6
+ tags:
7
+ - text-to-image
8
+ - diffusion
9
+ - rectified-flow
10
+ - dit
11
+ - tiny
12
+ - custom-code
13
+ ---
14
+
15
+ # PixelModel v5 🖼️
16
+
17
+ <img src="examples.png" alt="Nine 256px images generated by PixelModel v5" style="width: 100%; max-width: 860px;">
18
+
19
+ PixelModel v5 is a 40,013,980 parameter text-to-image diffusion transformer. It generates 256x256
20
+ images in 50 sampling steps and it trained in about 7 hours on one A100.
21
+
22
+ The architecture is identical to [v4](https://huggingface.co/bench-labs/PixelModel-v4). Same DiT,
23
+ same width, same depth, same VAE, same text encoder, same objective. Nothing in the model changed.
24
+ The only difference is that v5 saw 36 times more data.
25
+
26
+ That was the whole experiment, and it is worth being blunt about why.
27
+
28
+ ## v4 was starving, not undersized
29
+
30
+ v4 trained on MS-COCO, which is about 83,000 images. At batch 256 for 80,000 steps that works out to
31
+ roughly 247 passes over the same photographs. A model that sees the same 83,000 images 247 times is
32
+ not learning what the world looks like, it is memorising a small album.
33
+
34
+ We spent a while looking at architecture changes first. Linear attention, deeper stacks, a
35
+ higher compression autoencoder, representation alignment. All of them are real techniques and none
36
+ of them address the actual problem, because the bottleneck was never the shape of the network.
37
+
38
+ <img src="pixelmodel-v5-data.png" alt="Training set size, v4 at 83k images versus v5 at 2.99M" style="width: 100%; max-width: 780px;">
39
+
40
+ v5 trains on 2,986,571 image and caption pairs, which is about 10 epochs instead of 247.
41
+
42
+ ## The captions matter as much as the count
43
+
44
+ The images come from CC12M. Raw CC12M alt text is close to unusable for this, because it is scraped
45
+ SEO copy. A typical caption reads like "This is the best recipe I have ever tried for Cuban bread.
46
+ Cuban Recipes, Bread Recipes, Cooking Recipes, Pan Cubano".
47
+
48
+ v5 uses a recaptioned version where a vision model rewrote every caption in COCO style:
49
+
50
+ > A purple-lit subway train sits at a station platform.
51
+ >
52
+ > An old red tractor with gold fenders and red wheels stands in a snowy field.
53
+ >
54
+ > A compact kitchenette in a studio apartment features a stainless steel refrigerator, a microwave,
55
+ > and a sink with a glass faucet.
56
+
57
+ Those describe what is in the frame, which is what a text-to-image model needs. They also match the
58
+ caption distribution that FID and CLIP score are computed against, so the training signal and the
59
+ evaluation are speaking the same language.
60
+
61
+ ## Training
62
+
63
+ <img src="pixelmodel-v5-loss.png" alt="Training and held-out loss over 120,000 steps" style="width: 100%; max-width: 860px;">
64
+
65
+ 120,000 steps at batch 256. AdamW at 2e-4 with betas (0.9, 0.99), no weight decay, gradient clipping
66
+ at 1.0, cosine decay to a tenth of peak after a 1,000 step warmup, EMA at 0.9999 starting after
67
+ warmup, and 10% caption dropout for classifier free guidance. It held 4.6 steps per second at
68
+ 44.2 GiB the whole way.
69
+
70
+ The held-out curve climbs for the first 10,000 steps or so, which looks alarming and is not. EMA at
71
+ 0.9999 has a time constant of roughly 10,000 steps, so early on those weights are still mostly
72
+ initialisation. The number only becomes meaningful once EMA has caught up, and from there it falls
73
+ without interruption.
74
+
75
+ <img src="pixelmodel-v5-val.png" alt="Held-out loss falling monotonically with no turn-up" style="width: 100%; max-width: 860px;">
76
+
77
+ **v4 had no validation split at all**, so it shipped whichever weights the last step happened to
78
+ produce. v5 holds out 4,096 pairs, evaluates every 2,000 steps, and writes the best checkpoint to a
79
+ separate file. Across the whole run the held-out loss improved at every single check and never once
80
+ turned upward, which is the clearest evidence that the data change worked. Our sibling model
81
+ [VoxelModel v1](https://huggingface.co/bench-labs/VoxelModel-v1), trained on 28,415 meshes for 811
82
+ epochs, peaked at step 64,000 and got 3.08% worse by the end. Same recipe, same team, different data
83
+ regime, opposite outcome.
84
+
85
+ ## Results
86
+
87
+ Measured on 5,000 MS-COCO val2014 images at 256 center crop, using `torchmetrics` FID at 2048
88
+ features and `torchmetrics` CLIPScore with `openai/clip-vit-base-patch32`. This is v4's protocol.
89
+
90
+ | | FID ↓ | CLIP ↑ |
91
+ |---|---|---|
92
+ | PixelModel v4 | 39.54 | 28.04 |
93
+ | **PixelModel v5, cfg 5.0** | **34.06** | **29.27** |
94
+ | PixelModel v5, cfg 3.0 | 35.03 | 28.59 |
95
+ | PixelModel v5, cfg 6.0 | 35.18 | 29.28 |
96
+ | real photographs | n/a | 30.05 |
97
+
98
+ FID is 13.9% lower and CLIP score is 1.23 higher, at an identical parameter count. Nothing in the
99
+ network changed between v4 and v5, so the entire difference is the training data.
100
+
101
+ The last row is there because a metric without a reference point is hard to read. Feeding the real
102
+ COCO photographs and their own captions through the same CLIPScore gives 30.05, so v5 at 29.27 is
103
+ within 0.78 of the ceiling this metric can award. That number also served as a sanity check on the
104
+ harness, since `torchmetrics` CLIPScore is incompatible with transformers 5.x and needed patching
105
+ before it would run at all.
106
+
107
+ Guidance scale was swept rather than assumed. FID bottoms out at cfg 5.0 and CLIP keeps climbing
108
+ slightly to cfg 6.0, which is the usual tension between the two. Anyone reproducing this should
109
+ also know that FID is heavily biased at small sample sizes. The same checkpoint at cfg 5.0 scores
110
+ 74.35 on 1,000 images and 34.06 on 5,000, so sample count has to match before two FID numbers can be
111
+ compared at all.
112
+
113
+ One caveat worth stating plainly. v4's cached evaluation set is not published in its repository, so
114
+ the reference images here were rebuilt from COCO val2014 with a fixed seed. The protocol is the same
115
+ but the exact 5,000 photographs may not be. CLIP score is unaffected by this since it compares each
116
+ generated image against its own caption, but the FID comparison carries that asterisk.
117
+
118
+ ## Specification
119
+
120
+ | | |
121
+ |---|---|
122
+ | trainable parameters | 40,013,980 |
123
+ | frozen VAE, `sd-vae-ft-mse` | 83,653,863 |
124
+ | frozen text encoder, CLIP ViT-B/32 | 63,165,952 |
125
+ | **combined total** | **186,833,795** |
126
+ | architecture | DiT, dim 384, depth 12, heads 6, patch 2 |
127
+ | tokens | 256, over a 32x32x4 latent |
128
+ | objective | rectified flow, target `x1 - x0`, logit-normal timesteps |
129
+ | conditioning | cross attention on 40 CLIP text tokens, plus adaLN-zero |
130
+ | resolution | 256x256 |
131
+ | sampling | 50 steps, cfg 5.0 for best FID, cfg 6.0 for best CLIP |
132
+
133
+ A note on that combined number, because it is higher than what v4 published. v4's card reports
134
+ 161,500,000 total. That figure counts the CLIP text transformer but leaves out its 49,408 by 512
135
+ token embedding table, which is another 25.3M parameters. The encoder does not run without it, so it
136
+ counts. The correct total for both v4 and v5 is 186,833,795.
137
+
138
+ ## What it does well and what it does not
139
+
140
+ After a few hundred samples the boundary is consistent.
141
+
142
+ It is good at material and light. Food, landscapes, skies, water, snow, foliage, stone, interiors,
143
+ depth of field. A bowl of ramen, a wet cobblestone street, a wheat field under storm light, a library
144
+ of wooden shelves. These come back looking like photographs.
145
+
146
+ It is bad at structure. Faces and animal heads deform. Limbs merge. Wings on aircraft are stubby or
147
+ fused to the fuselage. Any text it renders, on a shop sign or an airliner livery, is convincing
148
+ looking gibberish. Prompts asking for one isolated object in an empty scene often return the scene
149
+ without the object, so "a lighthouse on a cliff at sunset" gives a very nice sunset over a cliff and
150
+ no lighthouse.
151
+
152
+ Frequency in the training data matters more than complexity. Aircraft are structurally complicated
153
+ and come out fine because CC12M is full of them. Hot air balloons are simpler and fail, because they
154
+ are rare.
155
+
156
+ That failure profile is what a 40M model looks like once data is no longer the constraint. It has
157
+ learned appearance thoroughly and topology poorly, and topology is what capacity buys. v6 is where
158
+ that gets spent.
159
+
160
+ ## Usage
161
+
162
+ ```bash
163
+ python main.py "a bowl of ramen with a soft boiled egg" --out ramen.png --cfg 6.0 --steps 50
164
+ ```
165
+
166
+ As with every model in this line the weights are also stored as a PNG. `model.png` is not a picture
167
+ of the network, it is the network, with each parameter packed across the red and green channels of
168
+ one pixel.
169
+
170
+ ## Files
171
+
172
+ | file | contents |
173
+ |---|---|
174
+ | `model.safetensors` | combined weights, backbone and VAE and text encoder |
175
+ | `model.png` | the same backbone weights as an image |
176
+ | `dit.py` | the model |
177
+ | `train_v5.py` | training loop with the held-out split |
178
+ | `prep_v5.py` | streams the dataset, encodes latents, caches captions |
179
+ | `main.py` | inference |
__pycache__/dit.cpython-312.pyc ADDED
Binary file (12.7 kB). View file
 
config.json ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architecture": "latent-diffusion-dit",
3
+ "objective": "rectified-flow",
4
+ "dit": {
5
+ "dim": 384,
6
+ "depth": 12,
7
+ "heads": 6,
8
+ "patch": 2,
9
+ "latent_ch": 4,
10
+ "latent_size": 32,
11
+ "text_dim": 512,
12
+ "mlp_ratio": 4.0
13
+ },
14
+ "vae": "stabilityai/sd-vae-ft-mse",
15
+ "text_encoder": "openai/clip-vit-base-patch32",
16
+ "max_tokens": 40,
17
+ "resolution": 256,
18
+ "trainable_parameters": 40013980,
19
+ "vae_parameters": 83653863,
20
+ "text_encoder_parameters": 63165952,
21
+ "total_parameters_incl_frozen": 186833795,
22
+ "training": {
23
+ "steps": 120000,
24
+ "batch_size": 256,
25
+ "pairs": 2986571,
26
+ "val_pairs": 4096,
27
+ "optimizer": "adamw",
28
+ "lr": 0.0002,
29
+ "betas": [
30
+ 0.9,
31
+ 0.99
32
+ ],
33
+ "warmup": 1000,
34
+ "ema": 0.9999,
35
+ "cfg_dropout": 0.1,
36
+ "best_val_loss": 0.7852968573570251,
37
+ "dataset": "undefined443/cc12m-wds-coco-recaptioned"
38
+ },
39
+ "sampling": {
40
+ "steps": 50,
41
+ "cfg": 5.0
42
+ },
43
+ "eval": {
44
+ "fid": 34.06,
45
+ "clip_score": 29.27,
46
+ "n": 5000,
47
+ "cfg": 5.0,
48
+ "dataset": "MS-COCO val2014, 256 center crop",
49
+ "protocol": "torchmetrics FrechetInceptionDistance + CLIPScore (openai/clip-vit-base-patch32)",
50
+ "real_image_clip_score": 30.05
51
+ }
52
+ }
dit.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import numpy as np
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+
9
+ def modulate(x, shift, scale):
10
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
11
+
12
+ def timestep_embedding(t, dim, max_period=10000):
13
+ half = dim // 2
14
+ freqs = torch.exp(-math.log(max_period) * torch.arange(half, device=t.device) / half)
15
+ args = t[:, None].float() * freqs[None]
16
+ emb = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
17
+ if dim % 2:
18
+ emb = torch.cat([emb, torch.zeros_like(emb[:, :1])], dim=-1)
19
+ return emb
20
+
21
+ def sincos_2d(dim, grid_size):
22
+ g = np.arange(grid_size, dtype=np.float32)
23
+ gx, gy = np.meshgrid(g, g, indexing="xy")
24
+ assert dim % 4 == 0
25
+ d4 = dim // 4
26
+ omega = 1.0 / (10000 ** (np.arange(d4, dtype=np.float32) / d4))
27
+ def emb1(p):
28
+ out = p.reshape(-1)[:, None] * omega[None]
29
+ return np.concatenate([np.sin(out), np.cos(out)], axis=1)
30
+ pe = np.concatenate([emb1(gx), emb1(gy)], axis=1)
31
+ return torch.from_numpy(pe).float()
32
+
33
+ class Attention(nn.Module):
34
+ def __init__(self, dim, heads):
35
+ super().__init__()
36
+ self.heads = heads
37
+ self.q = nn.Linear(dim, dim)
38
+ self.kv = nn.Linear(dim, dim * 2)
39
+ self.proj = nn.Linear(dim, dim)
40
+
41
+ def forward(self, x, ctx=None):
42
+ ctx = x if ctx is None else ctx
43
+ B, N, C = x.shape
44
+ M = ctx.shape[1]
45
+ h = self.heads
46
+ q = self.q(x).reshape(B, N, h, C // h).transpose(1, 2)
47
+ kv = self.kv(ctx).reshape(B, M, 2, h, C // h).permute(2, 0, 3, 1, 4)
48
+ k, v = kv[0], kv[1]
49
+ o = F.scaled_dot_product_attention(q, k, v)
50
+ o = o.transpose(1, 2).reshape(B, N, C)
51
+ return self.proj(o)
52
+
53
+ class Block(nn.Module):
54
+ def __init__(self, dim, heads, mlp_ratio=4.0):
55
+ super().__init__()
56
+ self.norm1 = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
57
+ self.attn = Attention(dim, heads)
58
+ self.norm_ca = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
59
+ self.cross = Attention(dim, heads)
60
+ self.norm2 = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
61
+ hidden = int(dim * mlp_ratio)
62
+ self.mlp = nn.Sequential(nn.Linear(dim, hidden), nn.GELU(approximate="tanh"),
63
+ nn.Linear(hidden, dim))
64
+ self.ada = nn.Sequential(nn.SiLU(), nn.Linear(dim, 6 * dim))
65
+ self.cross_gate = nn.Parameter(torch.zeros(1))
66
+
67
+ def forward(self, x, c, text):
68
+ shift1, scale1, gate1, shift2, scale2, gate2 = self.ada(c).chunk(6, dim=1)
69
+ x = x + gate1.unsqueeze(1) * self.attn(modulate(self.norm1(x), shift1, scale1))
70
+ x = x + self.cross_gate * self.cross(self.norm_ca(x), text)
71
+ x = x + gate2.unsqueeze(1) * self.mlp(modulate(self.norm2(x), shift2, scale2))
72
+ return x
73
+
74
+ class DiT(nn.Module):
75
+ def __init__(self, latent_ch=4, latent_size=32, patch=2, dim=384, depth=12,
76
+ heads=6, text_dim=512, mlp_ratio=4.0):
77
+ super().__init__()
78
+ self.latent_ch = latent_ch
79
+ self.latent_size = latent_size
80
+ self.patch = patch
81
+ self.grid = latent_size // patch
82
+ self.patch_dim = latent_ch * patch * patch
83
+ self.x_embed = nn.Linear(self.patch_dim, dim)
84
+ self.register_buffer("pos", sincos_2d(dim, self.grid).unsqueeze(0))
85
+ self.t_mlp = nn.Sequential(nn.Linear(dim, dim), nn.SiLU(), nn.Linear(dim, dim))
86
+ self.text_proj = nn.Linear(text_dim, dim)
87
+ self.text_pool = nn.Linear(text_dim, dim)
88
+ self.blocks = nn.ModuleList([Block(dim, heads, mlp_ratio) for _ in range(depth)])
89
+ self.norm_out = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6)
90
+ self.ada_out = nn.Sequential(nn.SiLU(), nn.Linear(dim, 2 * dim))
91
+ self.head = nn.Linear(dim, self.patch_dim)
92
+ self.dim = dim
93
+ self._init()
94
+
95
+ def _init(self):
96
+ for m in self.modules():
97
+ if isinstance(m, nn.Linear):
98
+ nn.init.xavier_uniform_(m.weight)
99
+ if m.bias is not None:
100
+ nn.init.zeros_(m.bias)
101
+ for b in self.blocks:
102
+ nn.init.zeros_(b.ada[-1].weight); nn.init.zeros_(b.ada[-1].bias)
103
+ nn.init.zeros_(self.ada_out[-1].weight); nn.init.zeros_(self.ada_out[-1].bias)
104
+ nn.init.zeros_(self.head.weight); nn.init.zeros_(self.head.bias)
105
+
106
+ def patchify(self, x):
107
+ B, C, H, W = x.shape
108
+ p = self.patch
109
+ x = x.reshape(B, C, H // p, p, W // p, p)
110
+ x = x.permute(0, 2, 4, 1, 3, 5).reshape(B, (H // p) * (W // p), C * p * p)
111
+ return x
112
+
113
+ def unpatchify(self, x):
114
+ B, N, _ = x.shape
115
+ p = self.patch
116
+ g = self.grid
117
+ C = self.latent_ch
118
+ x = x.reshape(B, g, g, C, p, p).permute(0, 3, 1, 4, 2, 5)
119
+ return x.reshape(B, C, g * p, g * p)
120
+
121
+ def forward(self, x, t, text_seq, text_pool):
122
+ x = self.x_embed(self.patchify(x)) + self.pos
123
+ c = self.t_mlp(timestep_embedding(t, self.dim)) + self.text_pool(text_pool)
124
+ text = self.text_proj(text_seq)
125
+ for blk in self.blocks:
126
+ x = blk(x, c, text)
127
+ shift, scale = self.ada_out(c).chunk(2, dim=1)
128
+ x = modulate(self.norm_out(x), shift, scale)
129
+ x = self.head(x)
130
+ return self.unpatchify(x)
131
+
132
+ def num_params(self):
133
+ return sum(p.numel() for p in self.parameters())
eval_v5.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse, json, os, sys, numpy as np, torch
2
+ from diffusers import AutoencoderKL
3
+ from transformers import CLIPTextModel, CLIPTokenizer, CLIPModel
4
+ sys.path.insert(0, '/root')
5
+ from dit import DiT
6
+
7
+ SCALE = 0.18215
8
+
9
+ def _img_feats(self, pixel_values=None, **kw):
10
+ return self.visual_projection(self.vision_model(pixel_values=pixel_values).pooler_output)
11
+
12
+ def _txt_feats(self, input_ids=None, attention_mask=None, **kw):
13
+ return self.text_projection(self.text_model(input_ids=input_ids,
14
+ attention_mask=attention_mask).pooler_output)
15
+
16
+ CLIPModel.get_image_features = _img_feats
17
+ CLIPModel.get_text_features = _txt_feats
18
+
19
+ @torch.no_grad()
20
+ def sample(model, seq, pool, ns, npool, steps, cfg, dev):
21
+ B = seq.shape[0]
22
+ x = torch.randn(B, 4, 32, 32, device=dev)
23
+ a, b = ns.expand(B, -1, -1), npool.expand(B, -1)
24
+ dt = 1.0 / steps
25
+ for i in range(steps):
26
+ t = torch.full((B,), i * dt, device=dev)
27
+ with torch.autocast('cuda', dtype=torch.bfloat16):
28
+ vc = model(x, t, seq, pool)
29
+ vu = model(x, t, a, b)
30
+ x = x + (vu + cfg * (vc - vu)).float() * dt
31
+ return x
32
+
33
+ @torch.no_grad()
34
+ def main():
35
+ ap = argparse.ArgumentParser()
36
+ ap.add_argument('--ckpt', default='/root/runs/pm5/best.pt')
37
+ ap.add_argument('--data', default='/root/pm5eval/eval_256.npz')
38
+ ap.add_argument('--n', type=int, default=5000)
39
+ ap.add_argument('--batch', type=int, default=100)
40
+ ap.add_argument('--steps', type=int, default=50)
41
+ ap.add_argument('--cfgs', default='2,3,4,5,6')
42
+ ap.add_argument('--out', default='/root/pm5_eval.json')
43
+ args = ap.parse_args()
44
+ dev = 'cuda'
45
+
46
+ ck = torch.load(args.ckpt, map_location=dev)
47
+ model = DiT(dim=384, depth=12, heads=6).to(dev).eval()
48
+ model.load_state_dict(ck['ema'])
49
+ print(f"[eval] ckpt step {ck.get('step')} val {ck.get('val')}", flush=True)
50
+
51
+ vae = AutoencoderKL.from_pretrained('stabilityai/sd-vae-ft-mse').to(dev).half().eval()
52
+ tok = CLIPTokenizer.from_pretrained('openai/clip-vit-base-patch32')
53
+ txt = CLIPTextModel.from_pretrained('openai/clip-vit-base-patch32').to(dev).half().eval()
54
+ e = tok([''], padding='max_length', max_length=40, truncation=True, return_tensors='pt').to(dev)
55
+ o = txt(**e)
56
+ ns, npool = o.last_hidden_state.float(), o.pooler_output.float()
57
+
58
+ d = np.load(args.data, allow_pickle=True)
59
+ real = d['images'][:args.n]
60
+ caps = [str(x) for x in d['captions'][:args.n]]
61
+ n = len(caps)
62
+ print(f'[eval] {n} real images and captions', flush=True)
63
+
64
+ from torchmetrics.image.fid import FrechetInceptionDistance
65
+ from torchmetrics.multimodal.clip_score import CLIPScore
66
+
67
+ ref = CLIPScore(model_name_or_path='openai/clip-vit-base-patch32').to(dev)
68
+ for i in range(0, n, args.batch):
69
+ rb = torch.from_numpy(real[i:i + args.batch]).permute(0, 3, 1, 2).to(dev)
70
+ ref.update(rb, caps[i:i + args.batch])
71
+ real_clip = float(ref.compute().item())
72
+ print(f'[eval] REAL images CLIP score {real_clip:.2f} (metric sanity check / ceiling)', flush=True)
73
+ del ref
74
+ torch.cuda.empty_cache()
75
+
76
+ results = []
77
+ for cfg in [float(x) for x in args.cfgs.split(',')]:
78
+ fid = FrechetInceptionDistance(feature=2048, normalize=True).to(dev)
79
+ clip = CLIPScore(model_name_or_path='openai/clip-vit-base-patch32').to(dev)
80
+ for i in range(0, n, args.batch):
81
+ rb = torch.from_numpy(real[i:i + args.batch].astype(np.float32) / 255.0).permute(0, 3, 1, 2).to(dev)
82
+ fid.update(rb, real=True)
83
+ for i in range(0, n, args.batch):
84
+ cb = caps[i:i + args.batch]
85
+ t = tok(cb, padding='max_length', max_length=40, truncation=True, return_tensors='pt').to(dev)
86
+ oo = txt(**t)
87
+ z = sample(model, oo.last_hidden_state.float(), oo.pooler_output.float(),
88
+ ns, npool, args.steps, cfg, dev)
89
+ img = vae.decode((z / SCALE).half()).sample.float()
90
+ img = (img.clamp(-1, 1) + 1) / 2
91
+ fid.update(img, real=False)
92
+ clip.update((img * 255).to(torch.uint8), cb)
93
+ f = float(fid.compute().item()); c = float(clip.compute().item())
94
+ results.append({'cfg': cfg, 'fid': round(f, 2), 'clip_score': round(c, 2),
95
+ 'n': n, 'steps': args.steps})
96
+ print(f'[eval] cfg {cfg}: FID {f:.2f} CLIP {c:.2f}', flush=True)
97
+ del fid, clip
98
+ torch.cuda.empty_cache()
99
+
100
+ best = min(results, key=lambda r: r['fid'])
101
+ json.dump({'ckpt_step': ck.get('step'), 'val_loss': ck.get('val'),
102
+ 'real_image_clip_score': round(real_clip, 2),
103
+ 'results': results, 'best_fid': best}, open(args.out, 'w'), indent=2)
104
+ print(f"[eval] BEST FID {best['fid']} at cfg {best['cfg']} (CLIP {best['clip_score']})", flush=True)
105
+ print('EVALDONE', flush=True)
106
+
107
+ if __name__ == '__main__':
108
+ main()
examples.png ADDED

Git LFS Details

  • SHA256: 7edb4abf3698f2112bac11fbabc0e389bca7213475f55a539e98246453639ee5
  • Pointer size: 132 Bytes
  • Size of remote file: 1.02 MB
main.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+
7
+ import numpy as np
8
+ import torch
9
+ from PIL import Image
10
+ from safetensors.torch import load_file
11
+ from diffusers import AutoencoderKL
12
+ from transformers import CLIPTextModel, CLIPTokenizer
13
+
14
+ from dit import DiT
15
+
16
+ SCALE = 0.18215
17
+
18
+ @torch.no_grad()
19
+ def sample(model, seq, pool, null_seq, null_pool, steps, cfg, dev, seed=None):
20
+ B = seq.shape[0]
21
+ g = None
22
+ if seed is not None:
23
+ g = torch.Generator(device=dev).manual_seed(seed)
24
+ x = torch.randn(B, 4, 32, 32, device=dev, generator=g)
25
+ ns, npool = null_seq.expand(B, -1, -1), null_pool.expand(B, -1)
26
+ dt = 1.0 / steps
27
+ for i in range(steps):
28
+ t = torch.full((B,), i * dt, device=dev)
29
+ with torch.autocast("cuda", dtype=torch.bfloat16):
30
+ vc = model(x, t, seq, pool)
31
+ vu = model(x, t, ns, npool)
32
+ x = x + (vu + cfg * (vc - vu)).float() * dt
33
+ return x
34
+
35
+ @torch.no_grad()
36
+ def main():
37
+ ap = argparse.ArgumentParser()
38
+ ap.add_argument("prompts", nargs="+")
39
+ ap.add_argument("--out", default="out.png")
40
+ ap.add_argument("--cfg", type=float, default=5.0)
41
+ ap.add_argument("--steps", type=int, default=50)
42
+ ap.add_argument("--seed", type=int, default=None)
43
+ ap.add_argument("--device", default="cuda")
44
+ ap.add_argument("--weights", default="model.safetensors")
45
+ ap.add_argument("--config", default="config.json")
46
+ ap.add_argument("--vae", default="stabilityai/sd-vae-ft-mse")
47
+ ap.add_argument("--clip", default="openai/clip-vit-base-patch32")
48
+ ap.add_argument("--max-tokens", type=int, default=40)
49
+ args = ap.parse_args()
50
+ dev = args.device
51
+
52
+ d = json.load(open(args.config))["dit"] if os.path.exists(args.config) else {
53
+ "dim": 384, "depth": 12, "heads": 6}
54
+ model = DiT(dim=d["dim"], depth=d["depth"], heads=d["heads"]).to(dev).eval()
55
+ sd = load_file(args.weights)
56
+ model.load_state_dict({k[len("dit."):]: v for k, v in sd.items() if k.startswith("dit.")})
57
+
58
+ vae = AutoencoderKL.from_pretrained(args.vae).to(dev).half().eval()
59
+ tok = CLIPTokenizer.from_pretrained(args.clip)
60
+ txt = CLIPTextModel.from_pretrained(args.clip).to(dev).eval()
61
+
62
+ def enc(strings):
63
+ t = tok(strings, padding="max_length", max_length=args.max_tokens,
64
+ truncation=True, return_tensors="pt").to(dev)
65
+ o = txt(**t)
66
+ return o.last_hidden_state.float(), o.pooler_output.float()
67
+
68
+ seq, pool = enc(args.prompts)
69
+ null_seq, null_pool = enc([""])
70
+ z = sample(model, seq, pool, null_seq, null_pool, args.steps, args.cfg, dev, args.seed)
71
+ img = vae.decode((z / SCALE).half()).sample.float()
72
+ img = ((img.clamp(-1, 1) + 1) / 2).permute(0, 2, 3, 1).cpu().numpy()
73
+
74
+ n = len(args.prompts)
75
+ if n == 1:
76
+ Image.fromarray((img[0] * 255).round().astype(np.uint8)).save(args.out)
77
+ paths = [args.out]
78
+ else:
79
+ root, ext = os.path.splitext(args.out)
80
+ paths = []
81
+ for i in range(n):
82
+ p = f"{root}_{i}{ext}"
83
+ Image.fromarray((img[i] * 255).round().astype(np.uint8)).save(p)
84
+ paths.append(p)
85
+ for p, s in zip(paths, args.prompts):
86
+ print(f'[pixelmodel] "{s}" -> {p} (cfg {args.cfg}, {args.steps} steps)')
87
+
88
+ if __name__ == "__main__":
89
+ main()
model.png ADDED

Git LFS Details

  • SHA256: 5ddfd5e4747fe1b6b9c6b542d95a40a61242354b5d56a0a13d67dcaf1d457f8d
  • Pointer size: 133 Bytes
  • Size of remote file: 89.8 MB
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:927864ec43cc1534849ed27c9be86d6f5a59b4fe13fd451806df4b7a66939178
3
+ size 747804492
model_png.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"cfg": {"dim": 384, "depth": 12, "heads": 6}, "params": [{"name": "x_embed.weight", "shape": [384, 16], "numel": 6144}, {"name": "x_embed.bias", "shape": [384], "numel": 384}, {"name": "t_mlp.0.weight", "shape": [384, 384], "numel": 147456}, {"name": "t_mlp.0.bias", "shape": [384], "numel": 384}, {"name": "t_mlp.2.weight", "shape": [384, 384], "numel": 147456}, {"name": "t_mlp.2.bias", "shape": [384], "numel": 384}, {"name": "text_proj.weight", "shape": [384, 512], "numel": 196608}, {"name": "text_proj.bias", "shape": [384], "numel": 384}, {"name": "text_pool.weight", "shape": [384, 512], "numel": 196608}, {"name": "text_pool.bias", "shape": [384], "numel": 384}, {"name": "blocks.0.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.0.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.0.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.0.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.0.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.0.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.0.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.0.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.0.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.0.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.0.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.0.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.0.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.0.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.0.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.0.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.0.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.0.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.0.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.1.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.1.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.1.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.1.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.1.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.1.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.1.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.1.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.1.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.1.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.1.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.1.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.1.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.1.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.1.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.1.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.1.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.1.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.1.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.2.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.2.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.2.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.2.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.2.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.2.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.2.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.2.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.2.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.2.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.2.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.2.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.2.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.2.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.2.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.2.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.2.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.2.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.2.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.3.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.3.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.3.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.3.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.3.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.3.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.3.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.3.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.3.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.3.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.3.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.3.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.3.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.3.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.3.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.3.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.3.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.3.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.3.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.4.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.4.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.4.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.4.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.4.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.4.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.4.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.4.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.4.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.4.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.4.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.4.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.4.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.4.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.4.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.4.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.4.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.4.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.4.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.5.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.5.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.5.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.5.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.5.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.5.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.5.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.5.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.5.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.5.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.5.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.5.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.5.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.5.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.5.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.5.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.5.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.5.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.5.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.6.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.6.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.6.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.6.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.6.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.6.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.6.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.6.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.6.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.6.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.6.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.6.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.6.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.6.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.6.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.6.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.6.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.6.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.6.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.7.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.7.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.7.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.7.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.7.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.7.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.7.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.7.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.7.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.7.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.7.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.7.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.7.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.7.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.7.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.7.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.7.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.7.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.7.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.8.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.8.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.8.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.8.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.8.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.8.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.8.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.8.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.8.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.8.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.8.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.8.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.8.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.8.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.8.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.8.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.8.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.8.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.8.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.9.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.9.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.9.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.9.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.9.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.9.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.9.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.9.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.9.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.9.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.9.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.9.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.9.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.9.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.9.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.9.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.9.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.9.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.9.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.10.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.10.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.10.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.10.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.10.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.10.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.10.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.10.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.10.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.10.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.10.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.10.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.10.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.10.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.10.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.10.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.10.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.10.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.10.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "blocks.11.cross_gate", "shape": [1], "numel": 1}, {"name": "blocks.11.attn.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.11.attn.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.11.attn.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.11.attn.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.11.attn.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.11.attn.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.11.cross.q.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.11.cross.q.bias", "shape": [384], "numel": 384}, {"name": "blocks.11.cross.kv.weight", "shape": [768, 384], "numel": 294912}, {"name": "blocks.11.cross.kv.bias", "shape": [768], "numel": 768}, {"name": "blocks.11.cross.proj.weight", "shape": [384, 384], "numel": 147456}, {"name": "blocks.11.cross.proj.bias", "shape": [384], "numel": 384}, {"name": "blocks.11.mlp.0.weight", "shape": [1536, 384], "numel": 589824}, {"name": "blocks.11.mlp.0.bias", "shape": [1536], "numel": 1536}, {"name": "blocks.11.mlp.2.weight", "shape": [384, 1536], "numel": 589824}, {"name": "blocks.11.mlp.2.bias", "shape": [384], "numel": 384}, {"name": "blocks.11.ada.1.weight", "shape": [2304, 384], "numel": 884736}, {"name": "blocks.11.ada.1.bias", "shape": [2304], "numel": 2304}, {"name": "ada_out.1.weight", "shape": [768, 384], "numel": 294912}, {"name": "ada_out.1.bias", "shape": [768], "numel": 768}, {"name": "head.weight", "shape": [16, 384], "numel": 6144}, {"name": "head.bias", "shape": [16], "numel": 16}], "total_parameters": 40013980, "side": 6326, "dtype": "float16", "channels": "R=hi,G=lo,B=unused", "step": 119999, "val_loss": 0.7852968573570251}
pixelmodel-v5-data.png ADDED
pixelmodel-v5-loss.png ADDED
pixelmodel-v5-val.png ADDED
png_codec.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import math
6
+ import os
7
+
8
+ import numpy as np
9
+ import torch
10
+ from PIL import Image
11
+
12
+ from dit import DiT
13
+
14
+ Image.MAX_IMAGE_PIXELS = None
15
+
16
+ def build(cfg):
17
+ return DiT(dim=cfg["dim"], depth=cfg["depth"], heads=cfg["heads"])
18
+
19
+ def encode(ckpt, png_path, cfg_path, dim=384, depth=12, heads=6, which="ema"):
20
+ ck = torch.load(ckpt, map_location="cpu")
21
+ cfg = {"dim": dim, "depth": depth, "heads": heads}
22
+ model = build(cfg)
23
+ model.load_state_dict(ck[which])
24
+ parts, manifest = [], []
25
+ for name, p in model.named_parameters():
26
+ a = p.detach().to(torch.float16).contiguous().view(-1).numpy()
27
+ parts.append(a)
28
+ manifest.append({"name": name, "shape": list(p.shape), "numel": int(a.size)})
29
+ flat = np.concatenate(parts)
30
+ N = flat.size
31
+ side = math.ceil(math.sqrt(N))
32
+ u16 = flat.view(np.uint16)
33
+ img = np.zeros((side * side, 3), dtype=np.uint8)
34
+ img[:N, 0] = (u16 >> 8).astype(np.uint8)
35
+ img[:N, 1] = (u16 & 0xFF).astype(np.uint8)
36
+ Image.fromarray(img.reshape(side, side, 3), "RGB").save(png_path)
37
+ json.dump({"cfg": cfg, "params": manifest, "total_parameters": int(N),
38
+ "side": side, "dtype": "float16", "channels": "R=hi,G=lo,B=unused",
39
+ "step": ck.get("step"), "val_loss": ck.get("val")},
40
+ open(cfg_path, "w"))
41
+ print(f"[png] encoded {N:,} params into a {side}x{side} PNG "
42
+ f"({os.path.getsize(png_path)/1e6:.1f} MB)")
43
+ return N, side
44
+
45
+ def load_model_png(png_path, cfg_path, device="cpu"):
46
+ meta = json.load(open(cfg_path))
47
+ model = build(meta["cfg"])
48
+ arr = np.asarray(Image.open(png_path).convert("RGB")).reshape(-1, 3)
49
+ total = meta["total_parameters"]
50
+ hi = arr[:total, 0].astype(np.uint16)
51
+ lo = arr[:total, 1].astype(np.uint16)
52
+ flat = ((hi << 8) | lo).astype(np.uint16).view(np.float16)
53
+ sd = dict(model.named_parameters())
54
+ off = 0
55
+ with torch.no_grad():
56
+ for m in meta["params"]:
57
+ n = m["numel"]
58
+ chunk = flat[off:off + n].astype(np.float16)
59
+ sd[m["name"]].copy_(torch.from_numpy(chunk.copy()).view(*m["shape"]).to(torch.float32))
60
+ off += n
61
+ return model.to(device).eval()
62
+
63
+ def verify(ckpt, png_path, cfg_path, which="ema"):
64
+ model = load_model_png(png_path, cfg_path)
65
+ ref = torch.load(ckpt, map_location="cpu")[which]
66
+ worst, name = 0.0, ""
67
+ for k, p in model.named_parameters():
68
+ d = (p.detach() - ref[k].float()).abs().max().item()
69
+ if d > worst:
70
+ worst, name = d, k
71
+ scale = max(1e-12, ref[name].float().abs().max().item())
72
+ print(f"[png] round-trip max abs error {worst:.3e} on {name} (relative {worst/scale:.2e})")
73
+ return worst
74
+
75
+ def main():
76
+ ap = argparse.ArgumentParser()
77
+ ap.add_argument("--ckpt", default="/root/runs/pm5/best.pt")
78
+ ap.add_argument("--png", default="model.png")
79
+ ap.add_argument("--manifest", default="model_png.json")
80
+ args = ap.parse_args()
81
+ encode(args.ckpt, args.png, args.manifest)
82
+ verify(args.ckpt, args.png, args.manifest)
83
+
84
+ if __name__ == "__main__":
85
+ main()
prep_v5.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, sys, io, gc, time, tarfile, argparse, numpy as np, torch
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ from PIL import Image
4
+ from huggingface_hub import hf_hub_download
5
+ from diffusers import AutoencoderKL
6
+ from transformers import CLIPTokenizer
7
+
8
+ REPO = 'undefined443/cc12m-wds-coco-recaptioned'
9
+ OUT = '/root/v5data'
10
+ TMP = '/root/v5tmp'
11
+ SCALE = 0.18215
12
+ RES = 256
13
+ MAXTOK = 40
14
+
15
+ ap = argparse.ArgumentParser()
16
+ ap.add_argument('--start', type=int, default=0)
17
+ ap.add_argument('--end', type=int, default=598)
18
+ ap.add_argument('--batch', type=int, default=64)
19
+ args = ap.parse_args()
20
+
21
+ os.makedirs(OUT, exist_ok=True)
22
+ os.makedirs(TMP, exist_ok=True)
23
+ dev = 'cuda'
24
+ vae = AutoencoderKL.from_pretrained('stabilityai/sd-vae-ft-mse').to(dev).half().eval()
25
+ for p in vae.parameters():
26
+ p.requires_grad_(False)
27
+ tok = CLIPTokenizer.from_pretrained('openai/clip-vit-base-patch32')
28
+
29
+ def prep_img(raw):
30
+ try:
31
+ im = Image.open(io.BytesIO(raw)).convert('RGB')
32
+ except Exception:
33
+ return None
34
+ w, h = im.size
35
+ s = RES / min(w, h)
36
+ im = im.resize((max(RES, int(w * s + 0.5)), max(RES, int(h * s + 0.5))), Image.BICUBIC)
37
+ w, h = im.size
38
+ l, t = (w - RES) // 2, (h - RES) // 2
39
+ return np.asarray(im.crop((l, t, l + RES, t + RES)), dtype=np.uint8)
40
+
41
+ pool = ThreadPoolExecutor(16)
42
+ t_start = time.time()
43
+ done_total = 0
44
+
45
+ def fetch(si):
46
+ return hf_hub_download(REPO, f'cc12m-coco-{si:04d}.tar', repo_type='dataset', local_dir=TMP)
47
+
48
+ todo = [i for i in range(args.start, args.end) if not os.path.exists(f'{OUT}/shard_{i:04d}.npz')]
49
+ print(f'{len(todo)} shards to process', flush=True)
50
+ dl = ThreadPoolExecutor(2)
51
+ nxt = dl.submit(fetch, todo[0]) if todo else None
52
+
53
+ for k, si in enumerate(todo):
54
+ dst = f'{OUT}/shard_{si:04d}.npz'
55
+ try:
56
+ p = nxt.result()
57
+ except Exception as e:
58
+ print(f' shard {si} download failed: {type(e).__name__}', flush=True)
59
+ nxt = dl.submit(fetch, todo[k + 1]) if k + 1 < len(todo) else None
60
+ continue
61
+ nxt = dl.submit(fetch, todo[k + 1]) if k + 1 < len(todo) else None
62
+
63
+ pairs = {}
64
+ try:
65
+ with tarfile.open(p) as tf:
66
+ for m in tf:
67
+ if not m.isfile():
68
+ continue
69
+ key, _, ext = m.name.partition('.')
70
+ if ext == 'jpg':
71
+ pairs.setdefault(key, {})['img'] = tf.extractfile(m).read()
72
+ elif ext == 'txt':
73
+ pairs.setdefault(key, {})['cap'] = tf.extractfile(m).read().decode('utf-8', 'ignore').strip()
74
+ except Exception as e:
75
+ print(f' shard {si} tar failed: {type(e).__name__}', flush=True)
76
+ os.remove(p); continue
77
+ os.remove(p)
78
+
79
+ items = [(v['img'], v['cap']) for v in pairs.values() if 'img' in v and 'cap' in v and v['cap']]
80
+ del pairs
81
+ if not items:
82
+ continue
83
+
84
+ lat_all, tok_all = [], []
85
+ for i in range(0, len(items), args.batch):
86
+ chunk = items[i:i + args.batch]
87
+ arrs = list(pool.map(prep_img, [c[0] for c in chunk]))
88
+ keep = [j for j, a in enumerate(arrs) if a is not None]
89
+ if not keep:
90
+ continue
91
+ x = np.stack([arrs[j] for j in keep])
92
+ t = torch.from_numpy(x).to(dev).half().permute(0, 3, 1, 2).div_(127.5).sub_(1.0)
93
+ with torch.no_grad():
94
+ lat = vae.encode(t).latent_dist.sample() * SCALE
95
+ lat_all.append(lat.cpu().numpy().astype(np.float16))
96
+ ids = tok([chunk[j][1] for j in keep], padding='max_length', max_length=MAXTOK,
97
+ truncation=True, return_tensors='np')['input_ids']
98
+ tok_all.append(ids.astype(np.int32))
99
+
100
+ if not lat_all:
101
+ continue
102
+ lat = np.concatenate(lat_all); ids = np.concatenate(tok_all)
103
+ np.savez(dst, latents=lat, tokens=ids)
104
+ done_total += len(lat)
105
+ el = time.time() - t_start
106
+ print(f' shard {si:04d}: {len(lat)} imgs total {done_total} {done_total/el:.0f} img/s '
107
+ f'{el/60:.1f} min elapsed', flush=True)
108
+ del lat_all, tok_all, lat, ids, items
109
+ gc.collect()
110
+
111
+ print('PREPDONE', done_total, flush=True)
train_v5.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import copy
5
+ import glob
6
+ import json
7
+ import math
8
+ import os
9
+ import time
10
+
11
+ import numpy as np
12
+ import torch
13
+ import torch.nn.functional as F
14
+ from transformers import CLIPTextModel
15
+
16
+ from dit import DiT
17
+
18
+ def build_cache(data, cache):
19
+ shards = sorted(glob.glob(f"{data}/shard_*.npz"))
20
+ if not shards:
21
+ raise SystemExit(f"no shards in {data}")
22
+ lat, tok = [], []
23
+ for i, s in enumerate(shards):
24
+ z = np.load(s)
25
+ lat.append(z["latents"]); tok.append(z["tokens"])
26
+ if (i + 1) % 50 == 0:
27
+ print(f" loaded {i+1}/{len(shards)} shards", flush=True)
28
+ lat = np.concatenate(lat); tok = np.concatenate(tok)
29
+ np.save(f"{cache}_lat.npy", lat); np.save(f"{cache}_tok.npy", tok)
30
+ return lat, tok
31
+
32
+ def main():
33
+ ap = argparse.ArgumentParser()
34
+ ap.add_argument("--data", default="/root/v5data")
35
+ ap.add_argument("--cache", default="/root/v5cache")
36
+ ap.add_argument("--out", default="/root/runs/pm5")
37
+ ap.add_argument("--steps", type=int, default=120000)
38
+ ap.add_argument("--batch", type=int, default=256)
39
+ ap.add_argument("--lr", type=float, default=2e-4)
40
+ ap.add_argument("--warmup", type=int, default=1000)
41
+ ap.add_argument("--dim", type=int, default=384)
42
+ ap.add_argument("--depth", type=int, default=12)
43
+ ap.add_argument("--heads", type=int, default=6)
44
+ ap.add_argument("--cfg-dropout", type=float, default=0.1)
45
+ ap.add_argument("--ema", type=float, default=0.9999)
46
+ ap.add_argument("--val-size", type=int, default=4096)
47
+ ap.add_argument("--val-every", type=int, default=2000)
48
+ ap.add_argument("--log-every", type=int, default=200)
49
+ ap.add_argument("--save-every", type=int, default=5000)
50
+ ap.add_argument("--clip", default="openai/clip-vit-base-patch32")
51
+ ap.add_argument("--resume", default="")
52
+ ap.add_argument("--seed", type=int, default=0)
53
+ args = ap.parse_args()
54
+
55
+ dev = "cuda"
56
+ os.makedirs(args.out, exist_ok=True)
57
+ torch.manual_seed(args.seed)
58
+ torch.backends.cuda.matmul.allow_tf32 = True
59
+ torch.backends.cudnn.allow_tf32 = True
60
+
61
+ if os.path.exists(f"{args.cache}_lat.npy"):
62
+ lat = np.load(f"{args.cache}_lat.npy", mmap_mode="r")
63
+ tok = np.load(f"{args.cache}_tok.npy")
64
+ else:
65
+ lat, tok = build_cache(args.data, args.cache)
66
+
67
+ N = len(lat)
68
+ perm = np.random.RandomState(args.seed).permutation(N)
69
+ val_i = np.sort(perm[:args.val_size])
70
+ tr_i = perm[args.val_size:]
71
+ print(f"[data] {N} pairs, {len(tr_i)} train, {len(val_i)} val", flush=True)
72
+
73
+ txt = CLIPTextModel.from_pretrained(args.clip).to(dev).eval()
74
+ for p in txt.parameters():
75
+ p.requires_grad_(False)
76
+
77
+ @torch.no_grad()
78
+ def encode(ids):
79
+ o = txt(input_ids=ids)
80
+ return o.last_hidden_state.float(), o.pooler_output.float()
81
+
82
+ null_ids = torch.full((1, tok.shape[1]), 0, dtype=torch.long, device=dev)
83
+ null_ids[0, 0] = 49406; null_ids[0, 1:] = 49407
84
+ null_seq, null_pool = encode(null_ids)
85
+
86
+ tok_t = torch.from_numpy(tok.astype(np.int64))
87
+ vlat = torch.from_numpy(np.asarray(lat[val_i])).float()
88
+ vseq, vpool = [], []
89
+ with torch.no_grad():
90
+ for i in range(0, len(val_i), 512):
91
+ s, p = encode(tok_t[val_i[i:i+512]].to(dev))
92
+ vseq.append(s); vpool.append(p)
93
+ vseq = torch.cat(vseq); vpool = torch.cat(vpool)
94
+ vg = torch.Generator(device=dev).manual_seed(1234)
95
+ vx1 = vlat.to(dev)
96
+ vx0 = torch.randn(vx1.shape, device=dev, generator=vg)
97
+ vt = torch.sigmoid(torch.randn(vx1.shape[0], device=dev, generator=vg))
98
+
99
+ model = DiT(dim=args.dim, depth=args.depth, heads=args.heads).to(dev)
100
+ ema = copy.deepcopy(model).eval()
101
+ for p in ema.parameters():
102
+ p.requires_grad_(False)
103
+ opt = torch.optim.AdamW(model.parameters(), lr=args.lr, betas=(0.9, 0.99), weight_decay=0.0)
104
+ print(f"[model] {sum(p.numel() for p in model.parameters())/1e6:.2f}M trainable", flush=True)
105
+
106
+ start, best = 0, float("inf")
107
+ if args.resume and os.path.exists(args.resume):
108
+ ck = torch.load(args.resume, map_location=dev)
109
+ model.load_state_dict(ck["model"]); ema.load_state_dict(ck["ema"])
110
+ opt.load_state_dict(ck["opt"]); start = ck["step"] + 1; best = ck.get("best", best)
111
+
112
+ def lr_at(s):
113
+ if s < args.warmup:
114
+ return args.lr * (s + 1) / args.warmup
115
+ p = (s - args.warmup) / max(1, args.steps - args.warmup)
116
+ return args.lr * (0.1 + 0.9 * 0.5 * (1 + math.cos(math.pi * min(1.0, p))))
117
+
118
+ @torch.no_grad()
119
+ def val_loss():
120
+ tot, n = 0.0, 0
121
+ for j in range(0, vx1.shape[0], args.batch):
122
+ sl = slice(j, j + args.batch)
123
+ m = vx1[sl].shape[0]
124
+ tb = vt[sl].view(-1, 1, 1, 1)
125
+ xt = (1 - tb) * vx0[sl] + tb * vx1[sl]
126
+ with torch.autocast("cuda", dtype=torch.bfloat16):
127
+ v = ema(xt, vt[sl], vseq[sl], vpool[sl])
128
+ tot += F.mse_loss(v.float(), vx1[sl] - vx0[sl]).item() * m
129
+ n += m
130
+ return tot / n
131
+
132
+ logf = open(f"{args.out}/log.jsonl", "a")
133
+ gen = torch.Generator(device=dev).manual_seed(args.seed)
134
+ run, t0 = 0.0, time.time()
135
+ for step in range(start, args.steps):
136
+ i = tr_i[np.random.randint(0, len(tr_i), args.batch)]
137
+ x1 = torch.from_numpy(np.asarray(lat[np.sort(i)])).to(dev).float()
138
+ seq, pool = encode(tok_t[np.sort(i)].to(dev))
139
+ drop = torch.rand(x1.shape[0], device=dev, generator=gen) < args.cfg_dropout
140
+ seq = torch.where(drop[:, None, None], null_seq, seq)
141
+ pool = torch.where(drop[:, None], null_pool, pool)
142
+
143
+ x0 = torch.randn(x1.shape, device=dev, generator=gen)
144
+ t = torch.sigmoid(torch.randn(x1.shape[0], device=dev, generator=gen))
145
+ tb = t.view(-1, 1, 1, 1)
146
+ xt = (1 - tb) * x0 + tb * x1
147
+ target = x1 - x0
148
+
149
+ for g in opt.param_groups:
150
+ g["lr"] = lr_at(step)
151
+ with torch.autocast("cuda", dtype=torch.bfloat16):
152
+ v = model(xt, t, seq, pool)
153
+ loss = F.mse_loss(v.float(), target)
154
+ opt.zero_grad(set_to_none=True)
155
+ loss.backward()
156
+ gn = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
157
+ opt.step()
158
+ d = args.ema if step > args.warmup else 0.0
159
+ with torch.no_grad():
160
+ for pe, pm in zip(ema.parameters(), model.parameters()):
161
+ pe.mul_(d).add_(pm.detach(), alpha=1 - d)
162
+ for be, bm in zip(ema.buffers(), model.buffers()):
163
+ be.copy_(bm)
164
+
165
+ run += loss.item()
166
+ if (step + 1) % args.log_every == 0:
167
+ el = time.time() - t0
168
+ sps = args.log_every / el
169
+ print(f"[s{step+1:06d}] loss={run/args.log_every:.4f} lr={lr_at(step):.2e} "
170
+ f"gnorm={gn:.2f} {sps:.2f} steps/s eta={(args.steps-step-1)/sps/3600:.1f}h", flush=True)
171
+ logf.write(json.dumps({"step": step + 1, "loss": run / args.log_every,
172
+ "steps_per_s": sps}) + "\n"); logf.flush()
173
+ run, t0 = 0.0, time.time()
174
+
175
+ if (step + 1) % args.val_every == 0 or step + 1 == args.steps:
176
+ vl = val_loss()
177
+ tag = ""
178
+ if vl < best:
179
+ best = vl
180
+ torch.save({"ema": ema.state_dict(), "step": step, "val": vl}, f"{args.out}/best.pt")
181
+ tag = " *best*"
182
+ print(f"[s{step+1:06d}] val_loss={vl:.5f}{tag}", flush=True)
183
+ logf.write(json.dumps({"step": step + 1, "val_loss": vl}) + "\n"); logf.flush()
184
+ t0 = time.time()
185
+
186
+ if (step + 1) % args.save_every == 0 or step + 1 == args.steps:
187
+ torch.save({"model": model.state_dict(), "ema": ema.state_dict(), "opt": opt.state_dict(),
188
+ "step": step, "best": best}, f"{args.out}/latest.pt")
189
+
190
+ print("TRAINDONE best_val", best, flush=True)
191
+
192
+ if __name__ == "__main__":
193
+ main()