-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathevaluate_diffusion_2d_cond.py
More file actions
175 lines (152 loc) · 5.92 KB
/
Copy pathevaluate_diffusion_2d_cond.py
File metadata and controls
175 lines (152 loc) · 5.92 KB
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
"""Evaluation for the Diffusion 2d_cond w/ seed looping and CSV saving."""
from __future__ import annotations
import dataclasses
import os
from diffusers import UNet2DConditionModel
from engibench.utils.all_problems import BUILTIN_PROBLEMS
import numpy as np
import pandas as pd
import torch as th
import tyro
import wandb
from engiopt import metrics
from engiopt.checkpoint_store import resolve_named_checkpoint
from engiopt.dataset_sample_conditions import sample_conditions
from engiopt.diffusion_2d_cond.diffusion_2d_cond import beta_schedule
from engiopt.diffusion_2d_cond.diffusion_2d_cond import denormalize_designs_from_diffusion_range
from engiopt.diffusion_2d_cond.diffusion_2d_cond import DiffusionSampler
@dataclasses.dataclass
class Args:
"""Command-line arguments for a single-seed Diffusion 2D Conditional evaluation."""
problem_id: str = "beams2d"
"""Problem identifier."""
seed: int = 1
"""Random seed to run."""
wandb_project: str = "engiopt"
"""Wandb project name."""
wandb_entity: str | None = None
"""Wandb entity name."""
hf_entity: str = "IDEALLab"
"""HF org/user where checkpoints are stored."""
hf_repo_prefix: str = "engiopt"
"""HF repo prefix used for model-family repositories."""
n_samples: int = 50
"""Number of generated samples per seed."""
sigma: float = 10.0
"""Kernel bandwidth for MMD and DPP metrics."""
output_csv: str = "diffusion_2d_cond_{problem_id}_metrics.csv"
"""Output CSV path template; may include {problem_id}."""
if __name__ == "__main__":
args = tyro.cli(Args)
seed = args.seed
problem = BUILTIN_PROBLEMS[args.problem_id]()
problem.reset(seed=seed)
# Seeding for reproducibility
th.manual_seed(seed)
rng = np.random.default_rng(seed)
th.backends.cudnn.deterministic = True
# Select device
if th.backends.mps.is_available():
device = th.device("mps")
elif th.cuda.is_available():
device = th.device("cuda")
else:
device = th.device("cpu")
### Set up testing conditions ###
conditions_tensor, sampled_conditions, sampled_designs_np, _ = sample_conditions(
problem=problem,
n_samples=args.n_samples,
device=device,
seed=seed,
)
# Add channel dim
conditions_tensor = conditions_tensor.unsqueeze(1)
### Set Up Diffusion Model ###
resolved = resolve_named_checkpoint(
model_source="auto",
problem_id=args.problem_id,
algo="diffusion_2d_cond",
seed=seed,
hf_entity=args.hf_entity,
hf_repo_prefix=args.hf_repo_prefix,
required_files=["model.pth"],
wandb_project=args.wandb_project,
wandb_entity=args.wandb_entity,
wandb_artifact_names={"model.pth": f"{args.problem_id}_diffusion_2d_cond_model"},
)
run_config = resolved.run_config
ckpt_path = resolved.files["model.pth"]
ckpt = th.load(ckpt_path, map_location=device)
# Build UNet
model = UNet2DConditionModel(
sample_size=problem.design_space.shape,
in_channels=1,
out_channels=1,
cross_attention_dim=64,
block_out_channels=(32, 64, 128, 256),
down_block_types=("CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "DownBlock2D"),
up_block_types=("UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D"),
layers_per_block=run_config["layers_per_block"],
transformer_layers_per_block=1,
encoder_hid_dim=len(problem.conditions_keys),
only_cross_attention=True,
).to(device)
# Noise schedule
options = {
"cosine": run_config["noise_schedule"] == "cosine",
"exp_biasing": run_config["noise_schedule"] == "exp",
"exp_bias_factor": 1,
}
betas = beta_schedule(
t=run_config["num_timesteps"],
start=1e-4,
end=0.02,
scale=1.0,
options=options,
)
ddm_sampler = DiffusionSampler(run_config["num_timesteps"], betas)
model.load_state_dict(ckpt["model"])
model.eval()
# Generate and reshape
design_shape: tuple = problem.design_space.shape
gen_designs = th.randn((args.n_samples, 1, *design_shape), device=device)
assert run_config["num_timesteps"] is not None
for i in reversed(range(run_config["num_timesteps"])):
t = th.full((args.n_samples,), i, device=device, dtype=th.long)
gen_designs = ddm_sampler.sample_timestep(model, gen_designs, t, conditions_tensor)
gen_designs = gen_designs.squeeze(1)
if "design_min" in ckpt and "design_max" in ckpt:
design_min = ckpt["design_min"].to(device)
design_max = ckpt["design_max"].to(device)
gen_designs = denormalize_designs_from_diffusion_range(gen_designs, design_min, design_max)
gen_designs_np = gen_designs.detach().cpu().numpy().reshape(args.n_samples, *problem.design_space.shape)
if "design_min" in ckpt and "design_max" in ckpt:
design_min_np = ckpt["design_min"].detach().cpu().numpy()
design_max_np = ckpt["design_max"].detach().cpu().numpy()
gen_designs_np = np.clip(gen_designs_np, design_min_np, design_max_np)
else:
gen_designs_np = np.clip(gen_designs_np, 1e-3, 1.0)
# Compute metrics
metrics_dict = metrics.metrics(
problem,
gen_designs_np,
sampled_designs_np,
sampled_conditions,
sigma=args.sigma,
)
# Add metadata to metrics
metrics_dict.update(
{
"seed": seed,
"problem_id": args.problem_id,
"model_id": "diffusion_2d_cond",
"n_samples": args.n_samples,
"sigma": args.sigma,
}
)
# Append result row to CSV
metrics_df = pd.DataFrame([metrics_dict])
out_path = args.output_csv.format(problem_id=args.problem_id)
write_header = not os.path.exists(out_path)
metrics_df.to_csv(out_path, mode="a", header=write_header, index=False)
print(f"Seed {seed} done; appended to {out_path}")