我不同意@Avjiit 的观点,即问题是由于形状错误造成的。这个问题很常见,因为 keras 主要贡献者 confirmed 问题是 zca 计算需要非常非常长的时间,因为它 uses numpy.linalg.svd() 即使在矩阵上计算量也很大(n * m * 3) , n~m~100。
有一些方法可以计算 svd 的快速近似值,例如 scikitlearn 的随机 svd、irlb 的截断 svd、Lanczos 方法 svd,但它们并不总是在数值上稳定。
我想我找到了另一种非常简单的方法,它非常快,并且给出的结果与标准方法完全相同!如果您有一个形状为 (m x n) 的数据矩阵,其中 m 远小于 n - 例如,它会有所帮助。您的图像(m~1000)比像素数(n~100 x 100 x 3 = 30000 像素)少得多。在这种情况下,keras 将使用sigma.shape=(30000,30000) 计算linalg.svd(sigma),这在计算上太费力并且需要很长时间。但在一个很好的近似值中,您可以在 (mxm) 矩阵上计算它,而不是在 (nxn) 矩阵上计算 svd,只需旋转输入数据 X 或翻转 sigma 计算的顺序看起来像 @987654337 @。使用这种方法,计算只需大约 ca。 10 秒,如果 m~1000 和 n~30000。好消息是linalg.svd(sigma) 的特征向量在两种情况下都是相同的,直到一个因子,请参阅here,幻灯片 30。您可以在您的数据集或使用 from keras.datasets import cifar10 的内置 cifer 集上进行测试.
数学证明可以在here 和here 中找到。为了直观起见,我分享了这些图片:
以下是用于创建 zca 计算的修改后的 keras 代码。您可以使用它来修改位于here 的 keras zca 代码。我所做的修改是在需要时放置用于转置数据矩阵的 if 子句:
from keras.datasets import cifar10
import numpy as np
from scipy import linalg
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X = X_train[:1000]
flat_x = np.reshape(x, (x.shape[0], x.shape[1] * x.shape[2] * x.shape[3]))
# normalize x:
flat_x = flat_x / 255.
flat_x = flat_x - flat_x.mean(axis=0)
# CHANGES HAPPEN BELOW.
# if m>n execute the svd as usual
if flat_x.shape[0] => flat_x.shape[1]:
sigma = np.dot(flat_x.T, flat_x) / flat_x.shape[0]
u, s, _ = linalg.svd(sigma)
# and if m<n do the trnaspose trick
if flat_x.shape[0] < flat_x.shape[1]:
sigma = np.dot(flat_x, flat_x.T) / flat_x.shape[0]
u, s, _ = linalg.svd(sigma)
u = np.dot(flat_x.T, u) / np.sqrt(s*flat_x.shape[0])
s_inv = 1. / np.sqrt(s[np.newaxis] + 0.1) # the 0.1 is the epsilon value for zca
principal_components = (u * s_inv).dot(u.T)
whitex = np.dot(flat_x, principal_components)