Numpy implementation of the Frechet Distance. The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1) and X_2 ~ N(mu_2, C_2) is d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)). Stable version by Dougal J. Sutherland. Params: -- mu1 : Num
(mu1, sigma1, mu2, sigma2, eps=1e-6)
| 93 | |
| 94 | |
| 95 | def calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6): |
| 96 | """Numpy implementation of the Frechet Distance. |
| 97 | The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1) |
| 98 | and X_2 ~ N(mu_2, C_2) is |
| 99 | d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)). |
| 100 | Stable version by Dougal J. Sutherland. |
| 101 | Params: |
| 102 | -- mu1 : Numpy array containing the activations of a layer of the |
| 103 | inception net (like returned by the function 'get_predictions') |
| 104 | for generated samples. |
| 105 | -- mu2 : The sample mean over activations, precalculated on an |
| 106 | representative data set. |
| 107 | -- sigma1: The covariance matrix over activations for generated samples. |
| 108 | -- sigma2: The covariance matrix over activations, precalculated on an |
| 109 | representative data set. |
| 110 | Returns: |
| 111 | -- : The Frechet Distance. |
| 112 | """ |
| 113 | |
| 114 | mu1 = np.atleast_1d(mu1) |
| 115 | mu2 = np.atleast_1d(mu2) |
| 116 | |
| 117 | sigma1 = np.atleast_2d(sigma1) |
| 118 | sigma2 = np.atleast_2d(sigma2) |
| 119 | |
| 120 | assert mu1.shape == mu2.shape, \ |
| 121 | 'Training and test mean vectors have different lengths' |
| 122 | assert sigma1.shape == sigma2.shape, \ |
| 123 | 'Training and test covariances have different dimensions' |
| 124 | |
| 125 | diff = mu1 - mu2 |
| 126 | |
| 127 | # Product might be almost singular |
| 128 | covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False) |
| 129 | if not np.isfinite(covmean).all(): |
| 130 | msg = ('fid calculation produces singular product; ' |
| 131 | 'adding %s to diagonal of cov estimates') % eps |
| 132 | print(msg) |
| 133 | offset = np.eye(sigma1.shape[0]) * eps |
| 134 | covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset)) |
| 135 | |
| 136 | # Numerical error might give slight imaginary component |
| 137 | if np.iscomplexobj(covmean): |
| 138 | if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3): |
| 139 | m = np.max(np.abs(covmean.imag)) |
| 140 | raise ValueError('Imaginary component {}'.format(m)) |
| 141 | covmean = covmean.real |
| 142 | |
| 143 | tr_covmean = np.trace(covmean) |
| 144 | |
| 145 | return (diff.dot(diff) + np.trace(sigma1) + |
| 146 | np.trace(sigma2) - 2 * tr_covmean) |