データの前処理〜白色化 その2〜
はじめに
おさらい
$N$個の$D$次元ベクトル${\bf x}$からなる行列${\bf X}$の分散共分散行列$\Phi_{\bf X}$は対角化可能で
$$ \Phi_{\bf X} = {\bf A \Lambda A}^{\mathrm T} $$
となる。ただし、$\Lambda$および${\bf A}$はそれぞれ固有値、固有ベクトルを要素にもつ行列である。 ここで、ベクトル${\bf x}$に行列${\bf P}$による線形変換${\bf u}={\bf Px}$を実行する。 変換後のベクトルの分散共分散行列$\Phi_{\bf U}$が単位行列になるように${\bf P}$を定めると
$$ \Phi_{\bf X}^{-1} = {\bf P}^{\mathrm T}{\bf P} $$
が得られる。したがって、${\bf P}$は
$$ {\bf P} = {\bf Q} \Lambda ^{-\frac{1}{2}} {\bf A}^{\mathrm T} $$
と表せる。ただし、${\bf Q}$は任意の直交行列である。
PCA白色化は${\bf Q}={\bf I}$とすることで得られ、変換後のベクトルは
$$ {\bf x}^{PCA} = \Lambda ^{-\frac{1}{2}} {\bf A}^{\mathrm T}{\bf x} \tag{1} $$
である。
ZCA白色化は${\bf Q}={\bf A}$とすることで得られ、変換後のベクトルは
$$ {\bf x}^{ZCA} = {\bf A} \Lambda ^{-\frac{1}{2}} {\bf A}^{\mathrm T}{\bf x} \tag{2} $$
である。
一枚の画像は複数の波の線形結合によって表現することができる(cf. Fourier Transform)。 対角化により得られる固有値$\lambda _{i} (i=1,\cdots ,D)$と波の振動数$\omega _{i} (i=1,\cdots ,D)$との間には
$$ \lambda _{i} \propto \frac{1}{\omega _{i}} $$
の関係があり、各振動数に対応したフィルタ(固有ベクトル)が存在している。
実装
式(1)および(2)の実装に際しては$\Lambda$の値が発散することを防ぐために小さい値$\epsilon$を用いて、以下の式
$$ {\bf x}^{PCA} = \left( \Lambda + \epsilon {\bf I} \right)^{-\frac{1}{2}} {\bf A}^{\mathrm T}{\bf x} $$
$$ {\bf x}^{ZCA} = {\bf A} \left( \Lambda + \epsilon {\bf I} \right)^{-\frac{1}{2}} {\bf A}^{\mathrm T}{\bf x} $$
を用いる。
Global Contrast Normalization (GCN)
GCNでは画像ごと、チャンネル(RGB)ごとに画素値を正規化(平均:0, 分散:1)する。以降の白色化の前にGCNを実行する。
def gcn(x): mean = np.mean(x, axis=(1,2,3), keepdims=True) std = np.std(x, axis=(1,2,3), keepdims=True) return (x - mean) / (std + 1.E-6)
PCA Whitening
class PCA_Whitening: def __init__(self, epsilon=1E-6): self.epsilon = epsilon self.mean = None self.PCA_mat = None def fit(self, x): x = x.reshape(x.shape[0],-1) self.mean = np.mean(x,axis=0) x -= self.mean cov_mat = np.dot(x.T, x) / x.shape[0] A, L, _ = np.linalg.svd(cov_mat) self.PCA_mat = np.dot(np.diag(1. / (np.sqrt(L) + self.epsilon)), A.T) def transform(self, x): shape = x.shape x = x.reshape(x.shape[0],-1) x -= self.mean x = np.dot(x, self.PCA_mat) return x.reshape(shape)
ZCA Whitening
class ZCA_Whitening: def __init__(self, epsilon=1E-6): self.epsilon = epsilon self.mean = None self.PCA_mat = None def fit(self, x): x = x.reshape(x.shape[0],-1) self.mean = np.mean(x,axis=0) x -= self.mean cov_mat = np.dot(x.T, x) / x.shape[0] A, L, _ = np.linalg.svd(cov_mat) self.ZCA_mat = np.dot(A, np.dot(np.diag(1. / (np.sqrt(L) + self.epsilon)), A.T)) def transform(self, x): shape = x.shape x = x.reshape(x.shape[0],-1) x -= self.mean x = np.dot(x, self.ZCA_mat) return x.reshape(shape)
PCAおよびZCA whiteningにおいて分散共分散行列の対角化にはnp.linalg.svd
を用いた。厳密には特異値分解であるが、正方行列においては対角化と違いはない(np.linalg.eig
との相違は未確認)。
結果
10x10x3サイズの自然画像60,000枚を用いて分散共分散行列をつくり、白色化を実行した。
元の画像を以下に示す。
Global Contrast Normalization
GCNを実行した後の画像を以下に示す。
PCA, and ZCA Whitening
以下にPCAおよびZCA whiteningを実行した後の画像を示す(upper: PCA, lower: ZCA)。 PCAでは元の画像の座標軸が失われいるように見える。一方、ZCAでは元の画像の様子が確認でき、座標軸が保存されていることが確認できた。
(a) PCA whitening
(b) ZCA whitening
PCA, ZCA Matrix
10x10x3の画像サイズからは300個のPCAおよびZCA白色化のフィルタが得られた。 下図に40個のフィルタを示す。
PCAのフィルタは空間的に広がっており、振動数の小さい方から順に並んでいる。 一方、ZCAのフィルタは局所的な空間を持ち、ピクセル、チャンネルの順に並んでいる。 文献[2]の表現を借りるなら、PCAは"global in space and local in frequency“で、ZCAは”local in space and global in frequency“と言うのがわかりやすいであろう。
(a) PCA filer
(b) ZCA filter
メモ
PCAおよびZCAフィルタの特徴を確認した。 300枚あるZCAフィルタのうちの200枚を用いて元画像の変換を行った。ZCAフィルタは”local in space”であるから画像の一部のみが変換されて、現れるはずである。以下に示す通り、確認できた。
また、低振動数領域と高振動数領域で分割し、それぞれの領域をもつZCAフィルタで画像を変換した。以下に結果を示す。ただし、この操作では32x32x3サイズの画像を用いた。
元の画像(cifar-10)
一番上に全領域(低振動+高振動)を含む、通常のZCAフィルタを用いた結果、 中央は低振動数領域のみを含むZCAフィルタを用いた結果、 一番下は高振動数領域のみを含むZCAフィルタを用いた結果である。
低振動数領域のフィルタでは元画像からぼやけた画像になっている(b)。 一方、高振動数領域のフィルタでは輪郭(エッジ)だけが強調された画像になっている(c)。
(a) ZCA filter with low and high frequency
(b) with low frequency
(c) with high freqency
参考文献
[1] 岡谷貴之, 「深層学習」第5章, 講談社, 2017.
[2] Bell AJ, Sejnowski TJ, Vision Res. 1997;37,3327.