Spaces:
Running
Running
# Copyright 2020 Erik Härkönen. All rights reserved. | |
# This file is licensed to you under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. You may obtain a copy | |
# of the License at http://www.apache.org/licenses/LICENSE-2.0 | |
# Unless required by applicable law or agreed to in writing, software distributed under | |
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | |
# OF ANY KIND, either express or implied. See the License for the specific language | |
# governing permissions and limitations under the License. | |
import torch, numpy as np | |
from types import SimpleNamespace | |
import itertools | |
import sys | |
from pathlib import Path | |
sys.path.insert(0, str(Path(__file__).parent.parent)) | |
from models import get_instrumented_model | |
SEED = 1369 | |
SAMPLES = 100 | |
B = 10 | |
torch.backends.cudnn.benchmark = True | |
has_gpu = torch.cuda.is_available() | |
device = torch.device('cuda' if has_gpu else 'cpu') | |
def compare(model, layer, z1, z2): | |
# Run partial forward | |
torch.manual_seed(0) | |
np.random.seed(0) | |
inst._retained[layer] = None | |
with torch.no_grad(): | |
model.partial_forward(z1, layer) | |
assert inst._retained[layer] is not None, 'Layer not retained (partial)' | |
feat_partial = inst._retained[layer].cpu().numpy().copy().reshape(-1) | |
# Run standard forward | |
torch.manual_seed(0) | |
np.random.seed(0) | |
inst._retained[layer] = None | |
with torch.no_grad(): | |
model.forward(z2) | |
assert inst._retained[layer] is not None, 'Layer not retained (full)' | |
feat_full = inst.retained_features()[layer].cpu().numpy().copy().reshape(-1) | |
diff = np.sum(np.abs(feat_partial - feat_full)) | |
return diff | |
configs = [] | |
# StyleGAN2 | |
models = ['StyleGAN2'] | |
layers = ['convs.0',] | |
classes = ['cat', 'ffhq'] | |
configs.append(itertools.product(models, layers, classes)) | |
# StyleGAN | |
models = ['StyleGAN'] | |
layers = [ | |
'g_synthesis.blocks.128x128.conv0_up', | |
'g_synthesis.blocks.128x128.conv0_up.upscale', | |
'g_synthesis.blocks.256x256.conv0_up', | |
'g_synthesis.blocks.1024x1024.epi2.style_mod.lin' | |
] | |
classes = ['ffhq'] | |
configs.append(itertools.product(models, layers, classes)) | |
# ProGAN | |
models = ['ProGAN'] | |
layers = ['layer2', 'layer7'] | |
classes = ['churchoutdoor', 'bedroom'] | |
configs.append(itertools.product(models, layers, classes)) | |
# BigGAN | |
models = ['BigGAN-512', 'BigGAN-256', 'BigGAN-128'] | |
layers = ['generator.layers.2.conv_1', 'generator.layers.5.relu', 'generator.layers.10.bn_2'] | |
classes = ['husky'] | |
configs.append(itertools.product(models, layers, classes)) | |
# Run all configurations | |
for config in configs: | |
for model_name, layer, outclass in config: | |
print('Testing', model_name, layer, outclass) | |
inst = get_instrumented_model(model_name, outclass, layer, device) | |
model = inst.model | |
# Test negative | |
z_dummy = model.sample_latent(B) | |
z1 = torch.zeros_like(z_dummy).to(device) | |
z2 = torch.ones_like(z_dummy).to(device) | |
diff = compare(model, layer, z1, z2) | |
assert diff > 1e-8, 'Partial and full should differ, but they do not!' | |
# Test model randomness (should be seeded away) | |
z1 = model.sample_latent(1) | |
inst._retained[layer] = None | |
with torch.no_grad(): | |
model.forward(z1) | |
feat1 = inst._retained[layer].reshape(-1) | |
model.forward(z1) | |
feat2 = inst._retained[layer].reshape(-1) | |
diff = torch.sum(torch.abs(feat1 - feat2)) | |
assert diff < 1e-8, f'Layer {layer} output contains randomness, diff={diff}' | |
# Test positive | |
torch.manual_seed(SEED) | |
np.random.seed(SEED) | |
latents = model.sample_latent(SAMPLES, seed=SEED) | |
for i in range(0, SAMPLES, B): | |
print(f'Layer {layer}: {i}/{SAMPLES}', end='\r') | |
z = latents[i:i+B] | |
diff = compare(model, layer, z, z) | |
assert diff < 1e-8, f'Partial and full forward differ by {diff}' | |
del model | |
torch.cuda.empty_cache() |