Repository navigation
Expand file tree
/
Copy pathenhanced_experiment.py
More file actions
290 lines (235 loc) · 10.7 KB
/
Copy pathenhanced_experiment.py
File metadata and controls
290 lines (235 loc) · 10.7 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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import os
import math
import torch
from torch import optim
from models import BaseVAE
from models.types_ import *
from utils import data_loader
import pytorch_lightning as pl
from torchvision import transforms
import torchvision.utils as vutils
from torchvision.datasets import CelebA
from torch.utils.data import DataLoader
from evaluation_metrics import VAEEvaluator
import numpy as np
from typing import Dict, List
import json
class EnhancedVAEXperiment(pl.LightningModule):
"""
Enhanced VAE experiment with comprehensive evaluation metrics
"""
def __init__(self,
vae_model: BaseVAE,
params: dict,
evaluation_frequency: int = 10) -> None:
super(EnhancedVAEXperiment, self).__init__()
self.model = vae_model
self.params = params
self.curr_device = None
self.hold_graph = False
self.evaluation_frequency = evaluation_frequency
# Initialize evaluator
self.evaluator = VAEEvaluator(device='cuda' if torch.cuda.is_available() else 'cpu')
# Store metrics history
self.metrics_history = {
'train': [],
'val': [],
'test': []
}
try:
self.hold_graph = self.params['retain_first_backpass']
except:
pass
def forward(self, input: Tensor, **kwargs) -> Tensor:
return self.model(input, **kwargs)
def training_step(self, batch, batch_idx, optimizer_idx=0):
real_img, labels = batch
self.curr_device = real_img.device
results = self.forward(real_img, labels=labels)
train_loss = self.model.loss_function(*results,
M_N=self.params['kld_weight'],
optimizer_idx=optimizer_idx,
batch_idx=batch_idx)
# Log all loss components
self.log_dict({key: val.item() for key, val in train_loss.items()}, sync_dist=True)
# Store metrics for analysis
if batch_idx % 100 == 0: # Store every 100 batches
self.metrics_history['train'].append({
'epoch': self.current_epoch,
'batch': batch_idx,
**{key: val.item() for key, val in train_loss.items()}
})
return train_loss['loss']
def validation_step(self, batch, batch_idx, optimizer_idx=0):
real_img, labels = batch
self.curr_device = real_img.device
results = self.forward(real_img, labels=labels)
val_loss = self.model.loss_function(*results,
M_N=1.0,
optimizer_idx=optimizer_idx,
batch_idx=batch_idx)
self.log_dict({f"val_{key}": val.item() for key, val in val_loss.items()}, sync_dist=True)
# Store validation metrics
self.metrics_history['val'].append({
'epoch': self.current_epoch,
'batch': batch_idx,
**{key: val.item() for key, val in val_loss.items()}
})
def on_validation_end(self) -> None:
self.sample_images()
# Run comprehensive evaluation every N epochs
if self.current_epoch % self.evaluation_frequency == 0:
self.run_comprehensive_evaluation()
def sample_images(self):
# Get sample reconstruction image
test_input, test_label = next(iter(self.trainer.datamodule.test_dataloader()))
test_input = test_input.to(self.curr_device)
test_label = test_label.to(self.curr_device)
recons = self.model.generate(test_input, labels=test_label)
vutils.save_image(recons.data,
os.path.join(self.logger.log_dir,
"Reconstructions",
f"recons_{self.logger.name}_Epoch_{self.current_epoch}.png"),
normalize=True,
nrow=12)
try:
samples = self.model.sample(144,
self.curr_device,
labels=test_label)
vutils.save_image(samples.cpu().data,
os.path.join(self.logger.log_dir,
"Samples",
f"{self.logger.name}_Epoch_{self.current_epoch}.png"),
normalize=True,
nrow=12)
except Warning:
pass
def run_comprehensive_evaluation(self):
"""
Run comprehensive evaluation including FID, LPIPS, and disentanglement metrics
"""
print(f"Running comprehensive evaluation at epoch {self.current_epoch}")
# Get test dataloader
test_dataloader = self.trainer.datamodule.test_dataloader()
# Run evaluation
metrics = self.evaluator.evaluate_model(
model=self.model,
dataloader=test_dataloader,
num_samples=1000
)
# Log metrics
for metric_name, value in metrics.items():
self.log(f"eval_{metric_name}", value, sync_dist=True)
# Store evaluation metrics
self.metrics_history['test'].append({
'epoch': self.current_epoch,
**metrics
})
# Save evaluation results
eval_save_path = os.path.join(self.logger.log_dir,
f"evaluation_epoch_{self.current_epoch}.json")
self.save_evaluation_results(metrics, eval_save_path)
print(f"Evaluation completed. FID: {metrics.get('fid', 'N/A'):.4f}, "
f"LPIPS: {metrics.get('lpips', 'N/A'):.4f}")
def save_evaluation_results(self, metrics: Dict[str, float], save_path: str):
"""
Save evaluation results to JSON file
"""
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, 'w') as f:
json.dump(metrics, f, indent=2)
print(f"Evaluation results saved to {save_path}")
def on_train_end(self):
"""
Final evaluation and cleanup
"""
print("Training completed. Running final evaluation...")
# Run final comprehensive evaluation
test_dataloader = self.trainer.datamodule.test_dataloader()
final_metrics = self.evaluator.evaluate_model(
model=self.model,
dataloader=test_dataloader,
num_samples=5000
)
# Save final results
final_save_path = os.path.join(self.logger.log_dir, "final_evaluation.json")
self.save_evaluation_results(final_metrics, final_save_path)
# Save metrics history
history_save_path = os.path.join(self.logger.log_dir, "metrics_history.json")
with open(history_save_path, 'w') as f:
json.dump(self.metrics_history, f, indent=2)
print("Final evaluation completed and saved.")
def configure_optimizers(self):
optims = []
scheds = []
optimizer = optim.Adam(self.model.parameters(),
lr=self.params['LR'],
weight_decay=self.params['weight_decay'])
optims.append(optimizer)
# Check if more than 1 optimizer is required (Used for adversarial training)
try:
if self.params['LR_2'] is not None:
optimizer2 = optim.Adam(getattr(self.model, self.params['submodel']).parameters(),
lr=self.params['LR_2'])
optims.append(optimizer2)
except:
pass
try:
if self.params['scheduler_gamma'] is not None:
scheduler = optim.lr_scheduler.ExponentialLR(optims[0],
gamma=self.params['scheduler_gamma'])
scheds.append(scheduler)
# Check if another scheduler is required for the second optimizer
try:
if self.params['scheduler_gamma_2'] is not None:
scheduler2 = optim.lr_scheduler.ExponentialLR(optims[1],
gamma=self.params['scheduler_gamma_2'])
scheds.append(scheduler2)
except:
pass
return optims, scheds
except:
return optims
class DisentanglementExperiment(EnhancedVAEXperiment):
"""
Specialized experiment for disentanglement-focused models
"""
def __init__(self, vae_model: BaseVAE, params: dict, evaluation_frequency: int = 5):
super(DisentanglementExperiment, self).__init__(vae_model, params, evaluation_frequency)
def run_latent_traversal_evaluation(self):
"""
Run latent space traversal evaluation for disentanglement analysis
"""
print(f"Running latent traversal evaluation at epoch {self.current_epoch}")
test_dataloader = self.trainer.datamodule.test_dataloader()
# Run latent traversal evaluation
traversal_results = self.evaluator.evaluate_latent_traversal(
model=self.model,
dataloader=test_dataloader,
num_factors=10,
num_steps=10
)
# Save traversal results
for factor_name, traversals in traversal_results.items():
save_path = os.path.join(self.logger.log_dir,
"Traversals",
f"{factor_name}_epoch_{self.current_epoch}.png")
os.makedirs(os.path.dirname(save_path), exist_ok=True)
vutils.save_image(traversals,
save_path,
normalize=True,
nrow=10)
print("Latent traversal evaluation completed.")
def on_validation_end(self) -> None:
super().on_validation_end()
# Run latent traversal evaluation more frequently for disentanglement models
if self.current_epoch % (self.evaluation_frequency // 2) == 0:
self.run_latent_traversal_evaluation()
def create_experiment(model: BaseVAE, params: dict, experiment_type: str = 'standard') -> pl.LightningModule:
"""
Factory function to create appropriate experiment type
"""
if experiment_type == 'disentanglement':
return DisentanglementExperiment(model, params)
else:
return EnhancedVAEXperiment(model, params)