protovar-repro / smoke_test.py
junwatu's picture
Upload smoke_test.py with huggingface_hub
8880ad5 verified
Raw
History Blame Contribute Delete
7.69 kB
"""
ProtoVAR Smoke Test
A minimal test to verify the ProtoVAR implementation works correctly.
This test uses a small model and synthetic data for quick verification.
"""
import os
import sys
import time
import json
from pathlib import Path
import torch
import torch.nn as nn
import torch.nn.functional as F
# Add VAR to path
sys.path.insert(0, '/tmp/VAR')
class MiniVAR(nn.Module):
"""
Miniature VAR model for smoke testing.
This is a simplified version of VAR that can run on CPU
without requiring pre-trained weights.
"""
def __init__(
self,
num_classes: int = 10,
embed_dim: int = 64,
num_heads: int = 4,
depth: int = 4,
vocab_size: int = 256,
patch_nums: tuple = (1, 2, 4, 8),
):
super().__init__()
self.num_classes = num_classes
self.embed_dim = embed_dim
self.patch_nums = patch_nums
self.vocab_size = vocab_size
# Embeddings
self.class_emb = nn.Embedding(num_classes + 1, embed_dim)
self.word_emb = nn.Embedding(vocab_size, embed_dim)
# Position embeddings
total_tokens = sum(pn**2 for pn in patch_nums)
self.pos_emb = nn.Embedding(total_tokens, embed_dim)
# Transformer blocks
self.blocks = nn.ModuleList([
nn.TransformerEncoderLayer(
d_model=embed_dim,
nhead=num_heads,
dim_feedforward=embed_dim * 4,
batch_first=True,
)
for _ in range(depth)
])
# Output head
self.head = nn.Linear(embed_dim, vocab_size)
def forward(self, labels: torch.Tensor, tokens: torch.Tensor) -> torch.Tensor:
"""Forward pass for training."""
B = labels.shape[0]
# Class embedding
cls_emb = self.class_emb(labels) # (B, embed_dim)
# Token embeddings
token_emb = self.word_emb(tokens) # (B, L, embed_dim)
# Combine
x = torch.cat([cls_emb.unsqueeze(1), token_emb], dim=1)
# Add position embeddings
pos = self.pos_emb(torch.arange(x.shape[1], device=x.device))
x = x + pos.unsqueeze(0)
# Transformer
for block in self.blocks:
x = block(x)
return self.head(x)
@torch.no_grad()
def generate(self, class_label: int, num_samples: int = 1) -> torch.Tensor:
"""Generate samples for a class."""
self.eval()
batch_size = num_samples
labels = torch.full((batch_size,), class_label, dtype=torch.long)
# Start with class token
x = self.class_emb(labels).unsqueeze(1) # (B, 1, embed_dim)
generated_tokens = []
for scale_idx, pn in enumerate(self.patch_nums):
# Generate tokens for this scale
for i in range(pn * pn):
# Add position embedding
pos_idx = sum(p**2 for p in self.patch_nums[:scale_idx]) + i
pos = self.pos_emb(torch.tensor([pos_idx], device=x.device))
# Forward
out = self.head(x[:, -1:, :] + pos)
# Sample
token = torch.argmax(out, dim=-1) # (B, 1)
generated_tokens.append(token)
# Update x
token_emb = self.word_emb(token)
x = torch.cat([x, token_emb], dim=1)
return torch.cat(generated_tokens, dim=1) # (B, total_tokens)
class MiniPrototypeBank(nn.Module):
"""Miniature prototype bank for smoke testing."""
def __init__(self, num_classes: int = 10, embed_dim: int = 64, num_scales: int = 4, input_dim: int = 256):
super().__init__()
self.num_classes = num_classes
self.num_scales = num_scales
self.prototypes = nn.Parameter(torch.randn(num_classes, num_scales, embed_dim) * 0.02)
self.projections = nn.ModuleList([nn.Linear(input_dim, embed_dim) for _ in range(num_scales)])
def get_guidance(self, class_idx: int, scale_idx: int) -> torch.Tensor:
return self.prototypes[class_idx, scale_idx]
def smoke_test():
"""Run smoke test."""
print("=" * 60)
print("ProtoVAR Smoke Test")
print("=" * 60)
device = 'cpu'
num_classes = 10
# Create mini VAR model
print("\n[1/3] Creating mini VAR model...")
model = MiniVAR(
num_classes=num_classes,
embed_dim=64,
num_heads=4,
depth=4,
vocab_size=256,
patch_nums=(1, 2, 4, 8),
)
print(f" Model parameters: {sum(p.numel() for p in model.parameters()) / 1e3:.1f}K")
# Create prototype bank
print("\n[2/3] Creating prototype bank...")
proto_bank = MiniPrototypeBank(
num_classes=num_classes,
embed_dim=64,
num_scales=4,
input_dim=256, # vocab_size
)
print(f" Prototype parameters: {sum(p.numel() for p in proto_bank.parameters()) / 1e3:.1f}K")
# Test generation
print("\n[3/3] Testing generation...")
model.eval()
start_time = time.time()
all_images = []
all_labels = []
for class_idx in range(num_classes):
# Generate samples
tokens = model.generate(class_label=class_idx, num_samples=5)
all_images.append(tokens)
all_labels.append(torch.full((5,), class_idx, dtype=torch.long))
# Get prototype guidance
guidance = proto_bank.get_guidance(class_idx, scale_idx=0)
print(f" Class {class_idx}: generated {tokens.shape[0]} samples, guidance shape: {guidance.shape}")
elapsed = time.time() - start_time
# Combine
all_images = torch.cat(all_images, dim=0)
all_labels = torch.cat(all_labels, dim=0)
print(f"\n Total generation time: {elapsed:.2f}s")
print(f" Generated dataset shape: {all_images.shape}")
print(f" Labels shape: {all_labels.shape}")
# Test prototype matching loss
print("\n[4/4] Testing prototype matching...")
model.train()
# Dummy forward pass
dummy_labels = torch.randint(0, num_classes, (4,))
dummy_tokens = torch.randint(0, 256, (4, 12)) # (B, L)
logits = model(dummy_labels, dummy_tokens)
# Compute prototype loss using logits projected to embed_dim
total_loss = 0.0
for scale_idx in range(4):
# Project logits to embed_dim (from vocab_size)
projected = proto_bank.projections[scale_idx](logits.mean(dim=1)) # (B, embed_dim)
proto = proto_bank.prototypes[dummy_labels, scale_idx] # (B, embed_dim)
similarity = F.cosine_similarity(projected, proto, dim=-1)
loss = (1 - similarity).mean()
total_loss += loss
print(f" Prototype matching loss: {total_loss.item():.4f}")
# Save results
output_dir = './smoke_test_outputs'
os.makedirs(output_dir, exist_ok=True)
results = {
'model_params': sum(p.numel() for p in model.parameters()),
'proto_params': sum(p.numel() for p in proto_bank.parameters()),
'generation_time': elapsed,
'dataset_shape': list(all_images.shape),
'prototype_loss': total_loss.item(),
}
with open(os.path.join(output_dir, 'smoke_test_results.json'), 'w') as f:
json.dump(results, f, indent=2)
print("\n" + "=" * 60)
print("Smoke Test PASSED")
print("=" * 60)
print(f"Results saved to {output_dir}/smoke_test_results.json")
return results
if __name__ == '__main__':
smoke_test()