| |
| """Oracle flow-aligned conditional Ridge probes on three AR4 backbones. |
| |
| The target is the current chunk/current denoising-step full-grid feature. The |
| first input is the current chunk/previous-step feature. The second input is |
| the previous chunk/same-step boundary map, either raw or warped by global, |
| correct, negated, or spatially shuffled target-to-source flow. |
| |
| This is an oracle diagnostic because the flow is computed from the generated |
| current RGB frame. It tests whether alignment makes the previous-chunk route |
| more predictive; it is not an inference-time implementation. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import csv |
| import json |
| import os |
| from collections import defaultdict |
| from pathlib import Path |
| from typing import Any |
|
|
|
|
| def preparse_gpu() -> str: |
| parser = argparse.ArgumentParser(add_help=False) |
| parser.add_argument("--gpu", default="0") |
| args, _ = parser.parse_known_args() |
| os.environ["CUDA_VISIBLE_DEVICES"] = str(args.gpu) |
| return str(args.gpu) |
|
|
|
|
| PHYSICAL_GPU = preparse_gpu() |
|
|
| import numpy as np |
| import torch |
| import torch.nn.functional as F |
|
|
| from analyze_fullgrid_bilinear_3models import ( |
| GridRun, |
| farneback, |
| load_causal_runs, |
| load_hy_runs, |
| load_self_runs, |
| resize_flow, |
| shuffled_flow, |
| warp, |
| ) |
|
|
|
|
| PROBES = ( |
| "step_only", |
| "both_raw", |
| "both_global", |
| "both_flow", |
| "both_negated_flow", |
| "both_shuffled_flow", |
| ) |
| LAYER_ROLES = {7: "early", 14: "middle", 22: "late", 29: "final"} |
|
|
|
|
| def parse_args() -> argparse.Namespace: |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--gpu", default=PHYSICAL_GPU) |
| parser.add_argument("--self_root", type=Path, required=True) |
| parser.add_argument("--causal_root", type=Path, required=True) |
| parser.add_argument("--hy_root", type=Path) |
| parser.add_argument("--hy_cache_root", type=Path) |
| parser.add_argument("--output_root", type=Path, required=True) |
| parser.add_argument("--projection_dim", type=int, default=64) |
| parser.add_argument("--ridge", type=float, default=1e-4) |
| parser.add_argument("--seed", type=int, default=20260828) |
| parser.add_argument( |
| "--multilayer_self_causal", |
| action="store_true", |
| help="Use four-layer projected full grids for Self/Causal and skip HY.", |
| ) |
| return parser.parse_args() |
|
|
|
|
| def write_csv(path: Path, rows: list[dict[str, Any]]) -> None: |
| if not rows: |
| return |
| fields: list[str] = [] |
| for row in rows: |
| for key in row: |
| if key not in fields: |
| fields.append(key) |
| path.parent.mkdir(parents=True, exist_ok=True) |
| with path.open("w", newline="", encoding="utf-8") as handle: |
| writer = csv.DictWriter(handle, fieldnames=fields) |
| writer.writeheader() |
| writer.writerows(rows) |
|
|
|
|
| def load_multilayer_self_runs(root: Path) -> dict[str, list[GridRun]]: |
| result = {role: [] for role in LAYER_ROLES.values()} |
| for path in sorted((root / "runs").glob("prompt_*.pt")): |
| state = torch.load(path, map_location="cpu", weights_only=False) |
| projected = state.get("projected_by_layer", {}) |
| if not projected: |
| raise ValueError(f"No multilayer projected features in {path}") |
| anchors = np.load(path.with_suffix(".anchors.npz"), allow_pickle=False)["frames"] |
| by_layer: dict[int, dict[tuple[int, int], torch.Tensor]] = defaultdict(dict) |
| for key, tensor in projected.items(): |
| layer, chunk, step = (int(value) for value in key.split(":")) |
| by_layer[layer][(chunk, step)] = tensor.float() |
| for layer, role in LAYER_ROLES.items(): |
| if layer not in by_layer: |
| raise ValueError(f"Missing layer {layer} in {path}") |
| result[role].append( |
| GridRun( |
| "self_forcing", |
| "none", |
| int(state.get("run_index", len(result[role]))), |
| anchors, |
| int(state["num_frame_per_block"]), |
| by_layer[layer], |
| path, |
| ) |
| ) |
| return result |
|
|
|
|
| def load_multilayer_causal_runs(root: Path) -> dict[str, list[GridRun]]: |
| result = {role: [] for role in LAYER_ROLES.values()} |
| for run_dir in sorted((root / "runs").glob("prompt_*")): |
| path = run_dir / "feature_snapshots.pt" |
| anchor_path = run_dir / "rgb_anchor_frames.npz" |
| if not path.exists() or not anchor_path.exists(): |
| continue |
| state = torch.load(path, map_location="cpu", weights_only=False) |
| projected = state.get("projected", {}) |
| if not projected: |
| raise ValueError(f"No projected features in {path}") |
| anchors = np.load(anchor_path, allow_pickle=False)["frames"] |
| by_layer: dict[int, dict[tuple[int, int], torch.Tensor]] = defaultdict(dict) |
| for key, tensor in projected.items(): |
| layer, chunk, step = (int(value) for value in key.split(":")) |
| by_layer[layer][(chunk, step)] = tensor.float() |
| for layer, role in LAYER_ROLES.items(): |
| if layer not in by_layer: |
| raise ValueError(f"Missing layer {layer} in {path}") |
| result[role].append( |
| GridRun( |
| "causal_forcing", |
| "none", |
| int(state["prompt_id"]), |
| anchors, |
| 3, |
| by_layer[layer], |
| path, |
| ) |
| ) |
| return result |
|
|
|
|
| def columns(name: str, data: dict[str, torch.Tensor]) -> list[torch.Tensor]: |
| ones = torch.ones_like(data["step"]) |
| mapping = { |
| "step_only": [data["step"], ones], |
| "both_raw": [data["step"], data["raw"], ones], |
| "both_global": [data["step"], data["global"], ones], |
| "both_flow": [data["step"], data["flow"], ones], |
| "both_negated_flow": [data["step"], data["negated"], ones], |
| "both_shuffled_flow": [data["step"], data["shuffled"], ones], |
| } |
| return mapping[name] |
|
|
|
|
| def collect_prompt_step(run: GridRun, target_step: int) -> dict[str, torch.Tensor]: |
| collected: dict[str, list[torch.Tensor]] = defaultdict(list) |
| for chunk in range(1, run.chunks): |
| source_frame_index = chunk * run.chunk_size - 1 |
| source_frame = run.anchors[source_frame_index] |
| source_map = run.features[(chunk - 1, target_step)][-1].float() |
| target_maps = run.features[(chunk, target_step)].float() |
| step_maps = run.features[(chunk, target_step - 1)].float() |
| for slot in range(run.chunk_size): |
| target_frame = run.anchors[chunk * run.chunk_size + slot] |
| flow = resize_flow(farneback(target_frame, source_frame)) |
| global_flow = torch.zeros_like(flow) |
| global_flow[0].fill_(float(torch.median(flow[0]))) |
| global_flow[1].fill_(float(torch.median(flow[1]))) |
| control_flows = { |
| "global": global_flow, |
| "flow": flow, |
| "negated": -flow, |
| "shuffled": shuffled_flow( |
| flow, |
| seed=(run.prompt_id + 1) * 100000 |
| + chunk * 1000 |
| + slot * 10 |
| + target_step, |
| ), |
| } |
| aligned: dict[str, torch.Tensor] = {} |
| masks: list[torch.Tensor] = [] |
| for name, control_flow in control_flows.items(): |
| aligned[name], mask = warp(source_map, control_flow) |
| masks.append(mask) |
| common_mask = torch.stack(masks).all(dim=0) |
| if not bool(common_mask.any()): |
| continue |
| collected["target"].append(target_maps[slot][common_mask]) |
| collected["step"].append(step_maps[slot][common_mask]) |
| collected["raw"].append(source_map[common_mask]) |
| for name in control_flows: |
| collected[name].append(aligned[name][common_mask]) |
| result = {key: torch.cat(values, dim=0).contiguous() for key, values in collected.items()} |
| expected = {"target", "step", "raw", "global", "flow", "negated", "shuffled"} |
| if set(result) != expected: |
| raise ValueError(f"Incomplete aligned sample for {run.source}: {set(result)}") |
| return result |
|
|
|
|
| def ridge_sufficient_statistics( |
| data: dict[str, torch.Tensor], |
| probe: str, |
| device: torch.device, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| design = torch.stack(columns(probe, data), dim=-1).to(device=device, dtype=torch.float64) |
| target = data["target"].to(device=device, dtype=torch.float64) |
| gram = torch.einsum("ndp,ndq->dpq", design, design) |
| rhs = torch.einsum("ndp,nd->dp", design, target) |
| return gram, rhs |
|
|
|
|
| def solve_ridge( |
| gram: torch.Tensor, |
| rhs: torch.Tensor, |
| ridge: float, |
| ) -> torch.Tensor: |
| parameter_count = gram.shape[-1] |
| device = gram.device |
| scale = gram.diagonal(dim1=-2, dim2=-1).mean(dim=-1).clamp_min(1e-8) |
| regularizer = torch.eye(parameter_count, dtype=torch.float64, device=device)[None] |
| regularizer = regularizer * (float(ridge) * scale[:, None, None]) |
| regularizer[:, -1, -1] = 0.0 |
| try: |
| weights = torch.linalg.solve(gram + regularizer, rhs.unsqueeze(-1)).squeeze(-1) |
| except torch.linalg.LinAlgError: |
| weights = (torch.linalg.pinv(gram + regularizer) @ rhs.unsqueeze(-1)).squeeze(-1) |
| return weights.float() |
|
|
|
|
| def evaluate( |
| data: dict[str, torch.Tensor], |
| probe: str, |
| weights: torch.Tensor, |
| device: torch.device, |
| ) -> dict[str, float]: |
| design = torch.stack(columns(probe, data), dim=-1).to(device=device, dtype=torch.float32) |
| target = data["target"].to(device=device, dtype=torch.float32) |
| prediction = torch.einsum("ndp,dp->nd", design, weights) |
| error = prediction - target |
| mse = error.square().mean() |
| variance = (target - target.mean()).square().mean().clamp_min(1e-12) |
| nmse = mse / variance |
| cosine = F.cosine_similarity(prediction, target, dim=-1, eps=1e-8).mean() |
| return { |
| "mse": float(mse), |
| "nMSE": float(nmse), |
| "nRMSE": float(torch.sqrt(nmse)), |
| "r2": float(1.0 - nmse), |
| "cosine": float(cosine), |
| } |
|
|
|
|
| def bootstrap(values: list[float], seed: int, rounds: int = 10000) -> tuple[float, float, float]: |
| array = np.asarray(values, dtype=np.float64) |
| generator = np.random.default_rng(seed) |
| indices = generator.integers(0, len(array), size=(rounds, len(array))) |
| means = array[indices].mean(axis=1) |
| return float(array.mean()), float(np.quantile(means, 0.025)), float(np.quantile(means, 0.975)) |
|
|
|
|
| def summarize(rows: list[dict[str, Any]], seed: int) -> list[dict[str, Any]]: |
| groups: dict[tuple[str, str, str], list[dict[str, Any]]] = defaultdict(list) |
| for row in rows: |
| groups[(row["model"], row["layer_role"], row["probe"])].append(row) |
| output = [] |
| for (model, role, probe), selected in sorted(groups.items()): |
| by_prompt: dict[int, list[dict[str, Any]]] = defaultdict(list) |
| for row in selected: |
| by_prompt[int(row["held_out_prompt"])].append(row) |
| prompt_rows = [] |
| for prompt_id, values in sorted(by_prompt.items()): |
| item = {"prompt_id": prompt_id} |
| for metric in ("mse", "nMSE", "nRMSE", "r2", "cosine"): |
| item[metric] = float(np.mean([float(row[metric]) for row in values])) |
| prompt_rows.append(item) |
| item: dict[str, Any] = { |
| "model": model, |
| "layer_role": role, |
| "probe": probe, |
| "prompt_count": len(prompt_rows), |
| "fold_count": len(selected), |
| } |
| for metric in ("mse", "nMSE", "nRMSE", "r2", "cosine"): |
| stable = seed + sum(map(ord, model + role + probe + metric)) |
| avg, low, high = bootstrap([row[metric] for row in prompt_rows], stable) |
| item[f"{metric}_mean"] = avg |
| item[f"{metric}_ci95_low"] = low |
| item[f"{metric}_ci95_high"] = high |
|
|
| output.append(item) |
|
|
| prompt_metric: dict[tuple[str, str, str, int], dict[str, float]] = {} |
| grouped: dict[tuple[str, str, str, int], list[dict[str, Any]]] = defaultdict(list) |
| for row in rows: |
| grouped[ |
| (row["model"], row["layer_role"], row["probe"], int(row["held_out_prompt"])) |
| ].append(row) |
| for key, values in grouped.items(): |
| prompt_metric[key] = { |
| metric: float(np.mean([float(row[metric]) for row in values])) |
| for metric in ("mse", "nMSE", "nRMSE", "r2", "cosine") |
| } |
| for item in output: |
| model, role, probe = item["model"], item["layer_role"], item["probe"] |
| if probe == "step_only": |
| continue |
| gain_step, gain_raw = [], [] |
| prompt_ids = sorted( |
| prompt_id |
| for candidate_model, candidate_role, candidate_probe, prompt_id in prompt_metric |
| if candidate_model == model and candidate_role == role and candidate_probe == probe |
| ) |
| for prompt_id in prompt_ids: |
| current = prompt_metric[(model, role, probe, prompt_id)]["mse"] |
| step = prompt_metric[(model, role, "step_only", prompt_id)]["mse"] |
| raw = prompt_metric[(model, role, "both_raw", prompt_id)]["mse"] |
| gain_step.append((step - current) / max(step, 1e-12)) |
| gain_raw.append((raw - current) / max(raw, 1e-12)) |
| avg, low, high = bootstrap( |
| gain_step, seed + 300000 + sum(map(ord, model + role + probe)) |
| ) |
| item.update({ |
| "mse_gain_vs_step_mean": avg, |
| "mse_gain_vs_step_ci95_low": low, |
| "mse_gain_vs_step_ci95_high": high, |
| "mse_gain_vs_step_wins": int(sum(value > 0 for value in gain_step)), |
| }) |
| avg, low, high = bootstrap( |
| gain_raw, seed + 600000 + sum(map(ord, model + role + probe)) |
| ) |
| item.update({ |
| "mse_gain_vs_raw_mean": avg, |
| "mse_gain_vs_raw_ci95_low": low, |
| "mse_gain_vs_raw_ci95_high": high, |
| "mse_gain_vs_raw_wins": int(sum(value > 0 for value in gain_raw)), |
| }) |
| if probe == "both_flow": |
| for baseline_probe, label in ( |
| ("both_global", "global"), |
| ("both_negated_flow", "negated_flow"), |
| ("both_shuffled_flow", "shuffled_flow"), |
| ): |
| gains = [] |
| for prompt_id in prompt_ids: |
| current = prompt_metric[(model, role, probe, prompt_id)]["mse"] |
| baseline = prompt_metric[(model, role, baseline_probe, prompt_id)]["mse"] |
| gains.append((baseline - current) / max(baseline, 1e-12)) |
| avg, low, high = bootstrap( |
| gains, |
| seed + 900000 + sum(map(ord, model + role + baseline_probe)), |
| ) |
| item.update({ |
| f"mse_gain_vs_{label}_mean": avg, |
| f"mse_gain_vs_{label}_ci95_low": low, |
| f"mse_gain_vs_{label}_ci95_high": high, |
| f"mse_gain_vs_{label}_wins": int(sum(value > 0 for value in gains)), |
| }) |
| return output |
|
|
|
|
| def main() -> None: |
| args = parse_args() |
| output = args.output_root.resolve() |
| output.mkdir(parents=True, exist_ok=True) |
| device = torch.device("cuda:0") |
| if not torch.cuda.is_available(): |
| raise RuntimeError("CUDA is required for this experiment") |
|
|
| if args.multilayer_self_causal: |
| model_runs = { |
| "self_forcing": load_multilayer_self_runs(args.self_root.resolve()), |
| "causal_forcing": load_multilayer_causal_runs(args.causal_root.resolve()), |
| } |
| else: |
| if args.hy_root is None or args.hy_cache_root is None: |
| raise ValueError("--hy_root and --hy_cache_root are required without multilayer mode") |
| model_runs = { |
| "self_forcing": {"final": load_self_runs(args.self_root.resolve())}, |
| "causal_forcing": {"final": load_causal_runs(args.causal_root.resolve())}, |
| "hy_static": { |
| "final": load_hy_runs( |
| args.hy_root.resolve(), |
| "static", |
| args.hy_cache_root.resolve(), |
| args.projection_dim, |
| device, |
| False, |
| ) |
| }, |
| } |
| fold_rows: list[dict[str, Any]] = [] |
| for model, role_runs in model_runs.items(): |
| for role, runs in role_runs.items(): |
| if len(runs) != 10: |
| raise ValueError(f"Expected 10 runs for {model}/{role}, found {len(runs)}") |
| for target_step in range(1, 4): |
| prepared = [collect_prompt_step(run, target_step) for run in runs] |
| token_counts = [int(data["target"].shape[0]) for data in prepared] |
| print( |
| f"[prepare] {model}/{role} step={target_step} tokens={token_counts}", |
| flush=True, |
| ) |
| statistics = { |
| probe: [ridge_sufficient_statistics(data, probe, device) for data in prepared] |
| for probe in PROBES |
| } |
| for held_out in range(10): |
| test = prepared[held_out] |
| for probe in PROBES: |
| grams, right_sides = zip(*statistics[probe]) |
| train_gram = torch.stack(grams).sum(dim=0) - grams[held_out] |
| train_rhs = torch.stack(right_sides).sum(dim=0) - right_sides[held_out] |
| weights = solve_ridge(train_gram, train_rhs, args.ridge) |
| values = evaluate(test, probe, weights, device) |
| fold_rows.append({ |
| "model": model, |
| "layer_role": role, |
| "target_step": target_step, |
| "held_out_prompt": held_out, |
| "train_prompts": 9, |
| "test_tokens": int(test["target"].shape[0]), |
| "probe": probe, |
| **values, |
| }) |
| print( |
| f"[fold] {model}/{role} step={target_step} heldout={held_out}", |
| flush=True, |
| ) |
| del prepared |
| torch.cuda.empty_cache() |
|
|
| summary = summarize(fold_rows, args.seed) |
| write_csv(output / "aligned_probe_folds.csv", fold_rows) |
| write_csv(output / "aligned_probe_summary.csv", summary) |
| summary_lookup = { |
| (row["model"], row["layer_role"], row["probe"]): row for row in summary |
| } |
| report = [ |
| "# Oracle flow-aligned conditional Ridge probe", |
| "", |
| "All gains are prompt-wise relative MSE reductions averaged over 10 held-out prompts and three target denoising steps. Flow is computed from the generated current RGB frame and is therefore an oracle diagnostic.", |
| "", |
| "| model | layer | raw chunk vs step-only | flow-aligned vs step-only | flow-aligned vs raw chunk | flow-aligned vs shuffled flow | flow-vs-raw wins |", |
| "|---|---|---:|---:|---:|---:|---:|", |
| ] |
| for model, role_runs in model_runs.items(): |
| for role in role_runs: |
| raw = summary_lookup[(model, role, "both_raw")] |
| flow = summary_lookup[(model, role, "both_flow")] |
| report.append( |
| f"| {model} | {role} | {100 * raw['mse_gain_vs_step_mean']:.2f}% | " |
| f"{100 * flow['mse_gain_vs_step_mean']:.2f}% | " |
| f"{100 * flow['mse_gain_vs_raw_mean']:.2f}% | " |
| f"{100 * flow['mse_gain_vs_shuffled_flow_mean']:.2f}% | " |
| f"{flow['mse_gain_vs_raw_wins']}/10 |" |
| ) |
| report.extend([ |
| "", |
| "Self-Forcing and Causal-Forcing are evaluated at early, middle, late, and final layers under one identical feature space, mask, split, and Ridge capacity.", |
| "", |
| "The experiment uses the common intersection of in-bounds masks for every warp, so all predictor variants see identical target tokens. Full-grid features are fixed 64-D signed random projections; conclusions concern within-model paired gains rather than native-space or cross-model absolute errors.", |
| ]) |
| (output / "REPORT.md").write_text("\n".join(report) + "\n", encoding="utf-8") |
| config = { |
| "gpu": str(args.gpu), |
| "models": list(model_runs), |
| "layer_roles": {model: list(role_runs) for model, role_runs in model_runs.items()}, |
| "prompt_count": 10, |
| "target_steps": [1, 2, 3], |
| "probes": list(PROBES), |
| "ridge": args.ridge, |
| "projection_dim": args.projection_dim, |
| "grid": [30, 52], |
| "split": "leave-one-prompt-out (9 train, 1 test)", |
| "support": "intersection of in-bounds masks for global/correct/negated/shuffled warps", |
| "flow": "Farneback target RGB to previous-chunk boundary RGB; oracle diagnostic", |
| "row_count": len(fold_rows), |
| } |
| (output / "config.json").write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") |
| print(f"[complete] {output} rows={len(fold_rows)}", flush=True) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|