(eval_inputs:list[dict], unet:UNetModel, cond_stage:FrozenOpenClipEmbedder, first_stage:AutoencoderKL,
inception:FidInceptionV3, clip:OpenClipEncoder)
| 360 | |
| 361 | @Tensor.train(mode=False) |
| 362 | def eval_unet(eval_inputs:list[dict], unet:UNetModel, cond_stage:FrozenOpenClipEmbedder, first_stage:AutoencoderKL, |
| 363 | inception:FidInceptionV3, clip:OpenClipEncoder) -> tuple[float, float]: |
| 364 | # Eval is divided into 5 jits, one per model |
| 365 | # It doesn't make sense to merge these jits, e.g. unet repeats 50 times in isolation; images fork to separate inception/clip |
| 366 | # We're generating and scoring 30,000 images per eval, and all the data can flow through one jit at a time |
| 367 | # To maximize throughput for each jit, we have only one model/jit on the GPU at a time, and pool outputs from each jit off-GPU |
| 368 | for model in (unet, first_stage, inception, clip): |
| 369 | Tensor.realize(*[p.to_("CPU") for p in get_parameters(model)]) |
| 370 | |
| 371 | uc_written = False |
| 372 | models = (cond_stage, unet, first_stage, inception, clip) |
| 373 | jits = (jit_context:=TinyJit(cond_stage.embed_tokens), denoise_step, vae_decode, jit_inception:=TinyJit(inception), |
| 374 | jit_clip:=TinyJit(clip.get_clip_score)) |
| 375 | all_bs = (CONTEXT_BS, DENOISE_BS, DECODE_BS, INCEPTION_BS, CLIP_BS) |
| 376 | if (EVAL_SAMPLES:=getenv("EVAL_SAMPLES", 0)) and EVAL_SAMPLES > 0: |
| 377 | eval_inputs = eval_inputs[0:EVAL_SAMPLES] |
| 378 | output_shapes = [(ns:=len(eval_inputs),77), (ns,77,1024), (ns,4,64,64), (ns,3,512,512), (ns,2048), (ns,)] |
| 379 | # Writing progress to disk lets us resume eval if we crash |
| 380 | stages = ["tokens", "embeds", "latents", "imgs", "inception", "clip"] |
| 381 | disk_tensor_names, disk_tensor_shapes = stages + ["end", "uc"], output_shapes + [(6,), (1,77,1024)] |
| 382 | if not all(os.path.exists(f"{EVAL_CKPT_DIR}/{name}.bytes") for name in disk_tensor_names): |
| 383 | for name, shape in zip(disk_tensor_names, disk_tensor_shapes): |
| 384 | file = Path(f"{EVAL_CKPT_DIR}/{name}.bytes") |
| 385 | file.unlink(missing_ok=True) |
| 386 | with file.open("wb") as f: f.truncate(prod(shape) * 4) |
| 387 | progress = {name: Tensor.empty(*shape, device=f"disk:{EVAL_CKPT_DIR}/{name}.bytes", dtype=dtypes.int if name in {"tokens", "end"} else dtypes.float) |
| 388 | for name, shape in zip(disk_tensor_names, disk_tensor_shapes)} |
| 389 | |
| 390 | def embed_tokens(tokens:Tensor) -> Tensor: |
| 391 | nonlocal uc_written |
| 392 | if not uc_written: |
| 393 | with Context(BEAM=0): progress["uc"].assign(cond_stage.embed_tokens(cond_stage.tokenize("").to(GPUS)).to("CPU").realize()).realize() |
| 394 | uc_written = True |
| 395 | return jit_context(shard_tensor(tokens)) |
| 396 | |
| 397 | def generate_latents(embeds:Tensor) -> Tensor: |
| 398 | uc_c = Tensor.stack(progress["uc"].to("CPU").expand(bs, 77, 1024), embeds, dim=1).reshape(-1, 77, 1024) |
| 399 | uc_c = shard_tensor(uc_c) |
| 400 | x = shard_tensor(Tensor.randn(bs,4,64,64)) |
| 401 | for step_idx, timestep in enumerate(tqdm(eval_timesteps)): |
| 402 | reversed_idx = Tensor([50 - step_idx - 1], device=GPUS) |
| 403 | alpha_prev = eval_alphas_prev[reversed_idx] |
| 404 | ts = Tensor.full(bs, fill_value=timestep, dtype=dtypes.int, device="CPU") |
| 405 | ts_ts = shard_tensor(ts.cat(ts)) |
| 406 | ts = shard_tensor(ts) |
| 407 | sqrt_alphas_cumprod_t = sqrt_alphas_cumprod[ts].reshape(bs, 1, 1, 1) |
| 408 | sqrt_one_minus_alphas_cumprod_t = sqrt_one_minus_alphas_cumprod[ts].reshape(bs, 1, 1, 1) |
| 409 | x_x = shard_tensor(Tensor.stack(x.to("CPU"), x.to("CPU"), dim=1).reshape(-1, 4, 64, 64)) |
| 410 | x.assign(denoise_step(x, x_x, ts_ts, uc_c, sqrt_alphas_cumprod_t, sqrt_one_minus_alphas_cumprod_t, alpha_prev, unet, GPUS)).realize() |
| 411 | return x |
| 412 | |
| 413 | def decode_latents(latents:Tensor) -> Tensor: return vae_decode(shard_tensor(latents), first_stage, disable_beam=True) |
| 414 | def generate_inception(imgs:Tensor) -> Tensor: return jit_inception(shard_tensor(imgs))[:,:,0,0] |
| 415 | |
| 416 | def calc_clip_scores(batch:Tensor, batch_tokens:Tensor) -> Tensor: |
| 417 | # Tensor.interpolate does not yet support bicubic, so we use PIL |
| 418 | batch = (batch.to(GPUS[0]).permute(0,2,3,1) * 255).clip(0, 255).cast(dtypes.uint8).numpy() |
| 419 | batch = [np.array(PIL.Image.fromarray(batch[i]).resize((224,224), PIL.Image.BICUBIC)) for i in range(bs)] |
no test coverage detected
searching dependent graphs…