| |
| """ |
| Code apapted from https://github.com/bioinf-jku/TTUR to use PyTorch instead of Tensorflow |
| Copyright 2018 Institute of Bioinformatics, JKU Linz |
| Licensed 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 CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| """ |
|
|
| from os.path import dirname, abspath, exists, join |
| import math |
| import os |
| import shutil |
|
|
| from torch.nn import DataParallel |
| from torch.nn.parallel import DistributedDataParallel |
| from torchvision.utils import save_image |
| from scipy import linalg |
| from tqdm import tqdm |
| import torch |
| import numpy as np |
|
|
| import utils.sample as sample |
| import utils.losses as losses |
|
|
|
|
| def frechet_inception_distance(mu1, sigma1, mu2, sigma2, eps=1e-6): |
| mu1 = np.atleast_1d(mu1) |
| mu2 = np.atleast_1d(mu2) |
|
|
| sigma1 = np.atleast_2d(sigma1) |
| sigma2 = np.atleast_2d(sigma2) |
|
|
| assert mu1.shape == mu2.shape, \ |
| "Training and test mean vectors have different lengths." |
| assert sigma1.shape == sigma2.shape, \ |
| "Training and test covariances have different dimensions." |
|
|
| diff = mu1 - mu2 |
|
|
| |
| covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False) |
| if not np.isfinite(covmean).all(): |
| offset = np.eye(sigma1.shape[0]) * eps |
| covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset)) |
|
|
| |
| if np.iscomplexobj(covmean): |
| if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3): |
| m = np.max(np.abs(covmean.imag)) |
| raise ValueError("Imaginary component {}".format(m)) |
| covmean = covmean.real |
|
|
| tr_covmean = np.trace(covmean) |
| return (diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean) |
|
|
|
|
| def calculate_moments(data_loader, eval_model, num_generate, batch_size, quantize, world_size, |
| DDP, disable_tqdm, fake_feats=None): |
| if fake_feats is not None: |
| total_instance = num_generate |
| acts = fake_feats.detach().cpu().numpy()[:num_generate] |
| else: |
| eval_model.eval() |
| total_instance = len(data_loader.dataset) |
| data_iter = iter(data_loader) |
| num_batches = math.ceil(float(total_instance) / float(batch_size)) |
| if DDP: num_batches = int(math.ceil(float(total_instance) / float(batch_size*world_size))) |
|
|
| acts = [] |
| for i in tqdm(range(0, num_batches), disable=disable_tqdm): |
| start = i * batch_size |
| end = start + batch_size |
| try: |
| images, labels = next(data_iter) |
| except StopIteration: |
| break |
|
|
| images, labels = images.to("cuda"), labels.to("cuda") |
|
|
| with torch.no_grad(): |
| embeddings, logits = eval_model.get_outputs(images, quantize=quantize) |
| acts.append(embeddings) |
|
|
| acts = torch.cat(acts, dim=0) |
| if DDP: acts = torch.cat(losses.GatherLayer.apply(acts), dim=0) |
| acts = acts.detach().cpu().numpy()[:total_instance].astype(np.float64) |
|
|
| mu = np.mean(acts, axis=0) |
| sigma = np.cov(acts, rowvar=False) |
| return mu, sigma |
|
|
|
|
| def calculate_fid(data_loader, |
| eval_model, |
| num_generate, |
| cfgs, |
| pre_cal_mean=None, |
| pre_cal_std=None, |
| quantize=True, |
| fake_feats=None, |
| disable_tqdm=False): |
| eval_model.eval() |
|
|
| if pre_cal_mean is not None and pre_cal_std is not None: |
| m1, s1 = pre_cal_mean, pre_cal_std |
| else: |
| m1, s1 = calculate_moments(data_loader=data_loader, |
| eval_model=eval_model, |
| num_generate="N/A", |
| batch_size=cfgs.OPTIMIZATION.batch_size, |
| quantize=quantize, |
| world_size=cfgs.OPTIMIZATION.world_size, |
| DDP=cfgs.RUN.distributed_data_parallel, |
| disable_tqdm=disable_tqdm, |
| fake_feats=None) |
|
|
| m2, s2 = calculate_moments(data_loader="N/A", |
| eval_model=eval_model, |
| num_generate=num_generate, |
| batch_size=cfgs.OPTIMIZATION.batch_size, |
| quantize=quantize, |
| world_size=cfgs.OPTIMIZATION.world_size, |
| DDP=cfgs.RUN.distributed_data_parallel, |
| disable_tqdm=disable_tqdm, |
| fake_feats=fake_feats) |
|
|
| fid_value = frechet_inception_distance(m1, s1, m2, s2) |
| return fid_value, m1, s1 |
|
|