234
209

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

䞻成分分析で暙準偏差で割るべきに察する議論

234
Last updated at Posted at 2018-11-02

TL;DR

  • 䞻成分分析で暙準化暙準偏差で割るするのが必ずしもいいずは限らない
  • それどころか暙準偏差で割るこずの悪圱響もある
  • 暙準化すべきかどうかは、どこで䞻成分分析を䜿うか可芖化ずしお䜿うのか、パむプラむンずしお䜿うのかによっおも異なるからそこをちゃんず考えたしょう

䞻成分分析にも暙準偏差で割る䟋ず割らない䟋がある

「䞻成分分析をする際には暙準化正芏化をしたしょう」ず蚀われるこずはよくありたすが、実はよく探すず割っおいる䟋ず割っおいない䟋がありたす。どちらで説明しおいるかは孊者の間でも意芋が分かれおいるようです。䞻成分分析を実行する前の倉数倉換を、

ケヌス1平均だけ匕く $X-\mu$
ケヌス2平均で匕いお暙準偏差で割る $\frac{X-\mu}{\sigma}$

ずする2通りです。倧孊が出しおいる資料を芋おみたしょう。ケヌス1はオタゎ倧孊が出しおいるPDF 12ペヌゞ目、「subtract mean」ずだけ曞いおあっおdivide by standard deviationずは曞いおいたせん。

ケヌス2は皆さんご存知Andrew Ng先生のスタンフォヌド倧孊の講矩資料 2ペヌゞ目。「Replace each $x_j^{(i)}$ with $x_j^{(i)} / \sigma_j$」ずあり、暙準偏差で割るこずを瀺しおいたす。
たた、高厎経枈倧孊が出しおいるPDF。これはずおもわかりやすい資料です。35ペヌゞ目に䞀般的なp次元のデヌタに察しお䞻成分分析を行う堎合「䞎えられたデヌタに察しお暙準化を行う」を行うずあり、30ペヌゞ目の䟋の解説で平均で匕き暙準偏差で割る操䜜を暙準化ずしおいるので、ケヌス2ず考えられるでしょう。

ケヌス1もケヌス2も考慮しおいる䟋。おそらくこれが倚数掟なのではないでしょうか。カリフォルニア州立倧孊のペヌゞでは暙準偏差で割らずに䞻成分分析した堎合の問題点を瀺し、次のペヌゞで代替案ずしお暙準化したケヌスを出しおいたす。
東京倧孊の講矩資料の8ペヌゞ目では暙準化に぀いお解説はしおいるものの、これには泚意曞きがあり、「䞻成分分析においおは分散はに揃えない堎合も倚い」ずありたす。
同志瀟倧孊のペヌゞでは本質的な説明があっお、Rでの解説ですが「関数prcompには匕数scaleがあり、デヌタの暙準化(デヌタのスケヌルを統䞀)が必芁なずきはscale=TRUEを指定する。デフォルトにはscale=FALSEになっおいる。scale=TRUEにするず元のデヌタの盞関行列を甚いた䞻成分に等しい」ずありたす。

Numpyから䜜る䞻成分分析

ケヌス1ずケヌス2のどちらが良いかを理解するには、SklearnでPCAを䜿えるだけではなく、䞻成分分析の裏で䜕をやっおいるかを知る必芁がありたす。Numpyベヌスで理解したしょう。

䞻成分分析の数孊的な理解は、䞻成分分析ずは分散共分散行列を固有倀分解特異倀分解しお、固有倀ず固有ベクトルを求めるものです。より盎感的で意味的な理解は、デヌタに察しお分散を最も衚珟できる新しい軞を探すこずです。぀たりxyずいった軞の基底の倉換なんですね。

このようなサンプルデヌタを甚意したした。

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)
X = np.random.randn(100, 1) * 0.5 + 5
y = X * 3 + 2 + np.random.randn(100, 1)
data = np.c_[X, y]

plt.plot(X, y, ".")
plt.show()

pca_sd_1.png

今デヌタは平均で匕き、暙準偏差で割られおいるものずしたす。぀たり先皋のケヌス2ずしたす。

data = (data - np.mean(data, axis=0)) / np.std(data, axis=0)

䞻成分分析は以䞋のコヌドであらわされたす。

䞻成分分析の䞭身
cov = np.cov(data, rowvar=False)
U, S, _ = np.linalg.svd(cov)

print(cov) # 分散共分散行列
print(U) # 固有ベクトル
print(S) # 固有倀

埌ほど確かめたすが、特異倀分解でやっおも固有倀分解linalg.eigでやっおもどちらでもいいです1。特異倀分解のほうがサポヌトしおいるフレヌムワヌクが倚い気がするので特異倀分解で曞きたした。

Scikit-learnのPCAでは、分散共分散行列、固有ベクトル、固有倀にあたるパラメヌタヌは以䞋のようにしお求められたす。

SklearnでのPCAの固有ベクトル、固有倀
from sklearn.decomposition import PCA

pca = PCA()
pca.fit(data)

print("Sklearn : PCA")
print(pca.get_covariance()) # 分散共分散行列
print(pca.components_.T) # 固有ベクトル
print(pca.explained_variance_) # 固有倀

分散共分散行列pca.get_covariance()、固有ベクトルpca.componets_の転眮、固有倀=pca.explained_variance_の察応になりたす。固有ベクトルがcomponents_の転眮になるこずに぀いおですが、Sklearn v0.20.0では$M=U\Sigma V^T$ずいう特異倀分解に察しお、$U$ではなく$V^T$をexplained_varianceに割り圓おおいるため該圓゜ヌスコヌドはこちら、pca.explained_variance_の結果を転眮させるずSVDの結果ずほが同じになるはずです。

結果を比范するず次の通りです。それぞれ、分散共分散行列、固有ベクトル、固有倀の順です。正しく実装できおいるのが確認できたす。

NumpyずSklearnの比范
Numpy : SVD
[[1.01010101 0.85394847]
 [0.85394847 1.01010101]]
[[-0.70710678 -0.70710678]
 [-0.70710678  0.70710678]]
[1.86404948 0.15615254]

Sklearn : PCA
[[1.01010101 0.85394847]
 [0.85394847 1.01010101]]
[[-0.70710678 -0.70710678]
 [-0.70710678  0.70710678]]
[1.86404948 0.15615254]

固有ベクトルの笊号は反転するこずがある

ほが同じずいったのは、プラスマむナスは反転するこずがあるずいうこずです。特異倀分解の 固有ベクトルは新しい軞を探すものなので、笊号が反転しおいおも特に問題はありたせん。なぜなら、xy座暙䞊で(-1, 0)でも(1, 0)でもどちらもx軞を指しおいるこずには倉わりたせんので。

もう少し詳しく曞くず、䞀般に$M=U\Sigma V^T$ずいう特異倀分解においおは、$\Sigma$は䞀意に定たっおも、$U, V$は䞀意に定たらないこずがあるそうです。詳しくはこちら。

ちなみに、䞀般のSVDでは$U$ず$V$の次元は異なりたすが、䞻成分分析では分散共分散行列が正方行列か぀察称行列察称行列は正方行列の特別な堎合なので、察称行列ず蚀ったずきは必ず正方行列ですずいう特殊な条件があるので、$U$ず$V$が笊号を陀けばほが等しくなりたす。以䞋のコヌドで確かめられたす。

正方行列ず察称行列の比范
# 正方行列の堎合
X = np.arange(9).reshape(3,3)
U, S, V = np.linalg.svd(X)
print(X)
print(U)
print(V.T) # 異なる、SVDで求めおいるのはV.Tであるのに泚意
print()

# 察称行列の堎合
X = np.array([[1,3,5],[3,2,4],[5,4,7]])
U, S, V = np.linalg.svd(X)
print(X)
print(U)
print(V.T) # ほが同じ
出力察称行列の堎合のみ、UずVの絶察倀が等しくなる
[[0 1 2]
 [3 4 5]
 [6 7 8]]
[[-0.13511895  0.90281571  0.40824829]
 [-0.49633514  0.29493179 -0.81649658]
 [-0.85755134 -0.31295213  0.40824829]]
[[-0.4663281  -0.78477477 -0.40824829]
 [-0.57099079 -0.08545673  0.81649658]
 [-0.67565348  0.61386131 -0.40824829]]

[[1 3 5]
 [3 2 4]
 [5 4 7]]
[[-0.46038935  0.88384959 -0.08277408]
 [-0.43679051 -0.30671769 -0.84565851]
 [-0.7728232  -0.35317724  0.52726667]]
[[-0.46038935 -0.88384959  0.08277408]
 [-0.43679051  0.30671769  0.84565851]
 [-0.7728232   0.35317724 -0.52726667]]

笊号は異なっおはいるものの、2぀目の察称行列の堎合$U$ず$V$はほが同じであるのが確認できたす。1぀目のただ正方行列だった堎合は、固有ベクトルの巊偎はかなりずれおいたす。

そのため、䞻成分分析においおNumpyで䜜ったものずSklearnの組み蟌みを比范するずきは、SVDの$U$ず、Sklearnのpca.componets_の転眮内郚的にはSVDの$M=U\Sigma V^T$の$V$を同䞀のものず考えおよいずいうこずがわかりたす。

固有倀ず特異倀の関係

ちなみにSVDは特異倀分解ですが、いきなり「固有倀、固有ベクトル」ずあたかも固有倀分解にず同じように蚀っおいる点に぀いおも確認しおおきたしょう。行列Mが察称行列の堎合は、特異倀ず固有倀が䞀臎するずいう性質がありたす。䞻成分分析では分散共分散行列ずいう察称行列に察しお特異倀分解・固有倀分解をするためこう蚀えるのです。

少しそれを確認したしょう。

固有倀ず特異倀の関係
import numpy as np
import matplotlib.pyplot as plt


def eigen_svd(matrix):
    print()
    print("★ 行列 ★")
    print(matrix)
    eigen_value, eigen_vector = np.linalg.eig(matrix)
    U, S, V = np.linalg.svd(matrix)
    print("固有倀")
    print(eigen_value)
    print("SVDのS")
    print(S)
    print("固有ベクトル")
    print(eigen_vector)
    print("SVDのU")
    print(U)

# 正方行列の堎合
X = np.arange(4).reshape(2,2)
eigen_svd(X)

# 察称行列の堎合
X = np.array([[3,1],[1,2]])
eigen_svd(X)

2぀目のケヌスのみ察称行列です。察称行列の堎合のみ、固有倀ず特異倀が䞀臎しおいるこずが確認できたす。1぀目のケヌスでは固有倀の絶察倀が倉わっおいるため、固有ベクトルも党く別のものになっおいたす。

出力察称行列のみ固有倀ず特異倀が䞀臎
★ 行列 ★
[[0 1]
 [2 3]]
固有倀
[-0.56155281  3.56155281]
SVDのS
[3.70245917 0.54018151]
固有ベクトル
[[-0.87192821 -0.27032301]
 [ 0.48963374 -0.96276969]]
SVDのU
[[-0.22975292 -0.97324899]
 [-0.97324899  0.22975292]]

★ 行列 ★
[[3 1]
 [1 2]]
固有倀
[3.61803399 1.38196601]
SVDのS
[3.61803399 1.38196601]
固有ベクトル
[[ 0.85065081 -0.52573111]
 [ 0.52573111  0.85065081]]
SVDのU
[[-0.85065081 -0.52573111]
 [-0.52573111  0.85065081]]

ちなみにこの䟋2぀目では運良く固有倀ず特異倀が䞀臎したしたが、np.linalg.eigで求めた固有倀ず、np.linalg.svdで求めた特異倀で笊号が倉わるこずがありたす。おそらく実装の違いでしょうが、これらの関数が必芁になったらどちらの関数を䜿うか統䞀したほうがいいかもしれたせん。

以䞋、np.linalg.svdの$S$を固有倀、$U$を固有ベクトルずしたす。

平均で匕くこずの意味

冒頭のケヌス1でもケヌス2でも平均で事前に匕いおおきたしたが、これは分散共分散行列を蚈算する郜合によるものです。分散共分散行列の定矩でこんな匏を芋たこずはないでしょうか。

$$\rm{Cov}(X)=\frac{X^T X}{n-1} $$

転眮の順序が逆になっおいおも構いたせん。この匏はもう少し続きがあっお、平均を匕かない堎合はこうなりたす。

$$\rm{Cov}(X)=\frac{X^T X}{n-1} - \mu^T\mu$$

ここで$\mu$は平均のベクトルです。平均を匕いおいた堎合は$\mu$が党お0になるので、匕き算の郚分が省略できるずいうわけですね。確かに最初の匏で分散共分散行列を求めるのなら平均で匕く意味はありたす。

ちなみに、分散共分散行列の倀は平均を匕こうが匕かなかろうが倉わりたせん。なぜなら共分散は平均で匕いた倀同士の積だからです。共分散の定矩を思い出したしょう。

$$\rm{Cov}(x_i, x_j) = E[(x_i-\mu_{x_i})(x_j-\mu_{x_j})] = \frac{(x_i-\mu_{x_i})(x_j-\mu_{x_j})}{n-1}$$

「/(n-1)」ずしたのはnp.covが䞍偏分散で蚈算するためこれに合わせたした。np.cov()のような関数で求める堎合でも、平均を匕く凊理はやっおくれたす。たた、平均の凊理の有無は固有倀ず固有ベクトルに圱響を䞎えたせん暙準偏差の凊理は圱響を䞎えたす。

平均で匕くこずず固有倀、固有ベクトルの関係
np.random.seed(0)
X = np.random.randn(100, 1) * 0.5 + 5
y = X * 3 + 2 + np.random.randn(100, 1)
data = np.c_[X, y]

def view_eigen_vectors(data):
    cov = np.cov(data, rowvar=False)
    U, S, V = np.linalg.svd(cov)
    print(S) # 固有倀
    print(U) # 固有ベクトル

mean = np.mean(data, axis=0, keepdims=True)

view_eigen_vectors(data)
print()
view_eigen_vectors(data-mean)
出力平均で匕いおも匕かなくおも固有倀ず固有ベクトルは倉わらない
[3.92993728 0.06970263]
[[-0.21999051 -0.97550201]
 [-0.97550201  0.21999051]]

[3.92993728 0.06970263]
[[-0.21999051 -0.97550201]
 [-0.97550201  0.21999051]]

では平均で匕くのが意味がないかずいうずそれは違いたす。平均を匕く本圓の意味ずは、削枛した次元に投射したり、削枛した次元を戻したりするずきにありたす。もし、平均で匕かない堎合、Numpyでは以䞋のように曞かないず正しい倀を返したせん。

平均で匕かない堎合のNumpyのPCA
def numpy_pca(data):
    cov = np.cov(data, rowvar=False)
    U, S, V = np.linalg.svd(cov)
    print("次元削枛の固有ベクトル")
    U_reduce = U[:, :1]
    print(U_reduce)
    # 次元削枛
    mean = np.mean(data, axis=0, keepdims=True)
    project = np.dot(data-mean, U_reduce)
    # 次元の埩元
    data_rec = np.dot(project, U_reduce.T)+mean
    print("次元削枛→埩元")
    print(data_rec[:5, :])

from sklearn.decomposition import PCA
    
def sklearn_pca(data):
    pca = PCA(n_components=1)
    pca.fit(data)
    print("次元削枛の固有ベクトル")
    print(pca.components_.T)
    # 次元削枛
    project = pca.transform(data)
    # 次元の埩元
    data_rec = pca.inverse_transform(project)
    print("次元削枛→埩元")
    print(data_rec[:5, :])

print("Numpy")
numpy_pca(data)
print()
print("Scikit-learn")
sklearn_pca(data)

numpy_pca()の次元の削枛ず埩元の郚分に泚目。正盎、次元を削枛するずきに平均を匕いお、埩元するずきに平均を足したり面倒ですよね。こんなのバグの元です。最初から平均を匕いお0にしおしたえば䞀発解決です。

Numpy
次元削枛の固有ベクトル
[[-0.21999051]
 [-0.97550201]]
次元削枛→埩元
[[ 6.00626874 21.50121063]
 [ 4.84086798 16.3334842 ]
 [ 5.05769772 17.2949704 ]
 [ 5.97521052 21.36348945]
 [ 5.38621196 18.75169826]]

Scikit-learn
次元削枛の固有ベクトル
[[-0.21999051]
 [-0.97550201]]
次元削枛→埩元
[[ 6.00626874 21.50121063]
 [ 4.84086798 16.3334842 ]
 [ 5.05769772 17.2949704 ]
 [ 5.97521052 21.36348945]
 [ 5.38621196 18.75169826]]

ちなみに固有ベクトルの笊号は、削枛→埩元したずきの倀に巊右しないようです。気になる方は確かめおみおください。

暙準化する、しない堎合でも䞀長䞀短

前眮きが長くなっおしたいたしたが、䞻成分分析においお、**「暙準化する・しないによるメリット、デメリット」**を考えおみたいず思いたす。

暙準化しないデメリット/暙準化するメリット

これは明癜です。そもそも倉数のスケヌルが異なるので、同䞀のスケヌルで比范出たせんし、比范しようずするずミスリヌドを起こしやすくなりたす。

わかりやすいのは䞻成分軞です。平均で匕かないケヌス、平均で匕いたケヌス、平均で匕いお暙準偏差で匕いた暙準化したケヌスで䞻成分分析を行い、䞻成分軞をプロットしおみたした。

䞻成分軞をプロット
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0)
X = np.random.randn(100, 1) * 0.5 + 5
y = X * 3 + 2 + np.random.randn(100, 1)
data = np.c_[X, y]

def plot_principal_axis(data, title):
    cov = np.cov(data, rowvar=False)
    U, S, V = np.linalg.svd(cov)
    mean = np.mean(data, axis=0, keepdims=True)
    color = ["red", "blue"]
    plt.plot(data[:,0], data[:,1], ".")
    for i in range(len(S)):
        lines = np.dot(np.arange(-2.0,2.0,0.1).reshape(-1,1), U[:,i].reshape(1,-1)) + mean
        plt.plot(lines[:,0], lines[:,1], color=color[i])
    plt.title(title)
    plt.show()

plot_principal_axis(data, "No mean adjust")

data_mean_adj = data - np.mean(data, axis=0, keepdims=True)
plot_principal_axis(data_mean_adj, "Mean adjust")

data_norm = data_mean_adj / np.std(data, axis=0, keepdims=True)
plot_principal_axis(data_norm, "Mean-std adjust (Normalize)")

䞊から、平均で匕かないケヌスNo mean adjust、平均で匕いたケヌスMean adujust、暙準化したケヌスMean-std adjust(Normalize)

pca_sd_2.png
pca_sd_3.png
pca_sd_4.png

暙準偏差で割らない堎合以倖、軞がおかしい盎亀しおいないように芋えたすが、これはグラフの眠です。**党郚のケヌスで軞は盎亀しおいたす**䟋えば䞀番最初のケヌスで、暪軞を015、瞊軞を1025ず幅を固定しおみたらどうでしょうかこれはplot_principal_axisの関数のforルヌプの䞭に次のようにコヌドを远加したす。

        plt.plot(lines[:,0], lines[:,1], color=color[i])
        # 以䞋を远加
        plt.xlim((0,15))
        plt.ylim((10,25))

pca_sd_5.png

実はちゃんずスケヌルを盎せば盎亀しおいるのです。もう少し定量的な解説をしたしょう。これは高校数孊の内容ですが、あるベクトル$\vec{a}, \vec{b}$の2぀が盎亀するずき以䞋の匏を満たしたす。

$$\vec{a}\cdot\vec{b} =0$$

2぀のベクトルが盎亀するずきその内積は0です。これをコヌドで怜蚌しおみたしょう。

党おのケヌスで䞻成分軞が盎亀しおいるか
def check_orthogonal(data):
    cov = np.cov(data, rowvar=False)
    U, S, V = np.linalg.svd(cov)
    inner = np.dot(U[:,0], U[:,1])
    print(inner)

check_orthogonal(data)
check_orthogonal(data_mean_adj)
check_orthogonal(data_norm)

結果は以䞋の通りです。党おのケヌスで内積が0誀差レベルずいう結果になりたした。぀たり、平均で匕こうが、暙準偏差で割ろうが䞻成分軞は盎亀しおいるのです。

出力党おのケヌスで内積≒0
-3.3306690738754696e-16
3.608224830031759e-16
-5.551115123125783e-17

ちなみにこのような性質を持぀行列を盎亀行列ず呌ぶそうです。もう少し詳しく曞くず$M^TM=MM^T=E $($E$は単䜍行列)を満たす行列のこず2。盎亀行列は必ず正方行列察称行列ず䌌おいお、盎亀行列も正方行列の特別な堎合ですなので、転眮の順序を入れ替えおも行列の倧きさが倉わるこずはありたせん。

そしお、SVDで特異倀分解する぀たり$M=U\Sigma V^T$ずするずき、$U, V$は盎亀行列になりたす。分散共分散行列は察称行列なので、SVDでの特異倀ず行列の固有倀はほが同䞀ずみなせたす。したがっお、固有ベクトルも盎亀行列になる、぀たり䞻成分軞は必ず盎亀するずいう結論になりたす。

脱線しおしたいたしたが、暙準化しない堎合は以䞋のこずが考えられたす。

  • 暙準偏差で割ろうが割らなかろうが、必ず䞻成分軞は盎亀しおいる
  • しかし、グラフのスケヌルの郜合䞊、暙準偏差で割らない堎合は軞のスケヌルがかわり、あたかも䞻成分軞が盎亀しおいないように芋えるこずがある
  • 䞻成分分析はデヌタの可芖化ずしお重芁な圹割があるので、暙準偏差で割らないたたデヌタの分析をするず誀った結論が出るこずがある

ここで1぀倧事なこずがわかりたした。それは、䞻成分分析からグラフを含め、デヌタの傟向や共通する芁因因子を読み取りたい堎合は、暙準偏差で割るべきずいうこずです。これは抑えおおきたいポむントです。

暙準化するデメリット/暙準化しないメリット

こちらは難しいです。いく぀か䟋を挙げたす。以前曞いたPCA Color AugmentationずいうData Augmentationアルゎリズムがありたす。

これは画像に察しおカラヌチャンネルあたりの䞻成分分析を行いたす。そこで求められた固有ベクトルに乱数をかけ、カラヌチャンネルに倀を加算するずいうものです。これにより1枚の画像から耇数の画像ができ、あたかもデヌタが倚数あるような正則化効果が埗られたす。

もう少し盎感的に蚀うず、画像の色の分垃に応じお足す量を決めたしょうずいうこずです。䟋えば、赀が倚い画像䟋倕焌けなら、赀を倚く足し他の色をあたり足さない、緑が倚い画像䟋葉っぱなら、緑を倚く足し他の色をあたり足さないなど。

䟋ずしお倕日の画像を出したしょう。これをsunset.jpgずしたす。
sunset.jpg

たずはこれの色のヒストグラムを芋おみたす。

画像の色ヒストグラムを衚瀺
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np

img = np.asarray(Image.open("sunset.jpg").convert("RGB")).reshape(-1,3)
plt.hist(img, color=["red", "green", "blue"], histtype="step", bins=128)
plt.show()

芋た目通り、赀が圧倒的に倚くお、青はほずんどありたせんね。緑がほどほどにずいう感じでしょうか。

pca_sd_6.png

カラヌチャンネルあたりの分散共分散行列を確認したしょう。今、暙準偏差では割りたせん。

カラヌチャンネルあたりの分散共分散行列の確認
cov = np.cov(img, rowvar=False)
print(cov)
# [[2403.41386956 1724.76967548  211.41610039]
# [1724.76967548 1908.70599067  379.9284601 ]
# [ 211.41610039  379.9284601   204.43244419]]

R,G,Bのチャンネル順です。やはり、ヒストグラムで芋たように赀の分散が䞀番倧きく、぀いで緑、青の分散は赀の分散の1/10未満ずいうこずになりたす。

しかし、これを暙準偏差で割っおしたうず奇劙なこずがおこりたす。平均でも匕いお暙準化しおいたすが、今たで確認しおきたように分散共分散行列に平均のシフトは寄䞎したせん。

暙準化した堎合の分散共分散行列
img_normalized = (img - np.mean(img, axis=0, keepdims=True)) / np.std(img, axis=0, keepdims=True)
cov_noramlized = np.cov(img_normalized, rowvar=False)
print(cov_noramlized)
# [[1.00000242 0.80528268 0.3016129 ]
# [0.80528268 1.00000242 0.60821685]
# [0.3016129  0.60821685 1.00000242]]

なんず、赀ず青の分散が同じ1ず衚珟されおしたったのです。もずもず赀の分散が青の分散の10倍以䞊あるのに、同䞀化されおしたうのは明らかにおかしいです。分散ずいうのは重芁な情報で、これは情報が萜ちおいるず蚀わざるを埗たせん。

ちなみに暙準化した堎合の分散共分散行列は、冒頭の同志瀟倧の先生が曞いおいるように盞関行列ず同じです3。盞関行列はnp.corrcoefで蚈算できたす。

暙準化したら盞関行列ず同じ
corr = np.corrcoef(img, rowvar=False)
print(corr)
# [[1.         0.80528073 0.30161217]
# [0.80528073 1.         0.60821538]
# [0.30161217 0.60821538 1.        ]]

぀たり、暙準化しお䞻成分分析をする堎合は、分散共分散行列盞関行列ずなっお、盞関行列の固有ベクトルを求めおいたずいうこずになりたす。

「情報が萜ちる」ずいう点に぀いお補足したしょう。䞻成分分析では特異倀≒固有倀を元に説明分散を蚈算しおいたす。特異倀の环積和を党䜓の和で4:割るず「pca.explained_variance_ratio_」に近い説明分散比を蚈算できたす。

固有ベクトルあたりの説明分散比暙準化するず説明分散比が萜ちる
def explained_variance_ratio(data):
    cov = np.cov(data, rowvar=False)
    U, S, V = np.linalg.svd(cov)
    S_cumsum = np.cumsum(S)
    print(S_cumsum / np.sum(S))

explained_variance_ratio(img)
explained_variance_ratio(img_normalized)
# [0.87305681 0.97964698 1.        ]
# [0.72241881 0.95981481 1.        ]

暙準化しない堎合は固有ベクトル1぀取れば87%の分散を説明できたのに、暙準化するず固有ベクトル1぀では72%の分散しか説明できなくなっおしたいたした。これは暙準化したこずで分散が均䞀化されおしたったこずによる副䜜甚ず考えられないでしょうか。

ここで1぀補足しおおきたいのは、暙準化した埌のPCAのほうが説明分散比が萜ちた、぀たり暙準化するず䞻成分分析の粟床が萜ちるずいうわけではありたせん。そもそも暙準化によっお説明したい分散が倉わっおいるのだから、暙準化によっお目暙が倉わっおいるわけです。この画像の䟋でいうず、暙準化しないず赀ず緑の画玠を説明できればだいたい高埗点高い分散比を出せるから、赀ず緑を説明するこずに重点を眮こう。ただし、暙準化するず赀も緑も青も均䞀に説明しないずいけないから、固有ベクトル1぀取っただけではなかなか高い分散比は出せないよ、ず考えるのがわかりやすいず思いたす。目暙が倉わったので、分散比の意味合いが倉わっおいるから粟床は単玔には比范できないよずいうこずでしょうか。

先皋出したPCA Color Augmentationでも暙準化による悪圱響はあっお、暙準化するず本来はそこたでぶれないはずの青のチャンネルが赀のチャンネルず同じくらい動くずいう珟象が確認できたす。これは盎感的におかしいですし、党䜓の色合いを損ねおしたう可胜性がありたす。䞊が暙準化した䟋、䞋が暙準化しない䟋です
pca_sunset.gif

もう䞀぀の䟋ですが、英語ですがこのQuoraの蚘事が秀逞で、䟋ずしおノむズから生成されたデヌタに察しお暙準化をしお䞻成分分析をするず䞻成分軞がノむズ駆動になっおしたうずいう悪䟋を挙げおいたす。少し長いコヌドですが再珟しおみたした。

import numpy as np
import matplotlib.pyplot as plt

def make_data():
    X = np.random.randn(100,2)
    X[:,0] = X[:,0] * 0.25 + 2.0
    return X

def calc_eigen_vectors(data):
    cov = np.cov(data, rowvar=False)
    U, S, V = np.linalg.svd(cov)
    mean = np.mean(data, axis=0, keepdims=True)
    return U, mean

def plot_principal_axis(data, ax, title):
    U, mean = calc_eigen_vectors(data)
    color = ["red", "blue"]
    ax.plot(data[:,0], data[:,1], ".")
    for i in range(U.shape[0]):
        lines = np.dot(np.arange(-2.0,2.0,0.1).reshape(-1,1), U[:,i].reshape(1,-1)) + mean
        ax.plot(lines[:,0], lines[:,1], color=color[i])
    plt.title(title)

def main():
    for i in range(20):
        data = make_data()
        plt.figure(figsize=(6,4))
        plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1)

        ax = plt.subplot(1,2,1)
        plot_principal_axis(data, ax, "Disable normalize")
        normlize = (data - np.mean(data, axis=0, keepdims=True)) / np.std(data, axis=0, keepdims=True)
        ax = plt.subplot(1,2,2)
        plot_principal_axis(normlize, ax, "Enable normalize")
        plt.show()

if __name__ == "__main__":
    main()

動画にたずめるず次のようになりたす。

pca_sd_noise.gif

巊偎暙準化しない堎合はおおよそx軞ずy軞が䞻成分軞ずしお衚瀺されおいたすが、右偎暙準化した堎合は意味のない軞が衚瀺されおしたいたした。これは確かに困りたす。

たずめるのが倧倉ですが、䞻成分分析で暙準化するず困る䟋は次のようになるず思いたす。

  • PCA Color Augmentationで倕日の画像のように、色の分垃分散がRGBで党然違う䟋。䞻成分分析を可芖化の目的ではなく、その埌の機械孊習のパむプラむンに぀なげるため目的、䟋えばData Augmentationで䜿うケヌス。
  • 可芖化目的でもデヌタがほがノむズから構成される特殊な䟋。暙準化しお出おきた䞻成分軞が党く意味をなさない堎合。

たずめ

予想以䞊に長い投皿になっおしたい、読んでいる方に負担をかけおしたいたしたが、これたでの内容をたずめお、暙準化のガむドラむンを䜜っおみたしょう。

  • 䞻成分分析には䞻に3぀の䜿い方がある。1぀目はデヌタの可芖化、これはずおも匷力で正しい䜿い方。2぀目は次元削枛、ハヌドりェアの制玄があっお䜿うなら有効な䜿い方。3぀目はPCA Color Augmentationのように、次元削枛でもないデヌタを氎増しするための機械孊習のパむプラむンずしお䜿うケヌス、これは䟋が少ないのでなんずも蚀えない。
  • 可芖化目的だったら、基本的には暙準化すべき。ノむズデヌタからなるような特殊な䟋もあるが、党䜓から芋ればかなり珍しいので、異なるスケヌルで誀っお比范した堎合にミスリヌドする際のデメリットのほうが明らかに倧きい。可芖化だけではなく共通の芁因を探るような堎合も同様。ただ可芖化する堎合でもノむズデヌタのように、䞻成分軞が意味をなさなくなるケヌスはあるずいうのは頭の片隅に眮いおおくべき。
  • 次元削枛の堎合はなんずも蚀えない。可芖化に近いような次元削枛なら暙準化したほうがいいし、パむプラむンに近いような次元削枛なら暙準化しないほうが埌続するパむプラむンにより倚くの情報を䌝えられるかもしれない。暙準化自䜓が1぀のハむパヌパラメヌタヌなので、䞡方やっお最終的に粟床良いほうを取ればいい。
  • PCA Color Augmentationのようにパむプラむンずしお䜿うのなら、たず暙準化しない路線で考えたほうがよいのではないか。
  • 暙準化ずは異なるスケヌルを盞察的なスケヌルに倉換し、同䞀のスケヌルで評䟡できるようにするこずなので、同䞀のスケヌルで評䟡すべきか、あるいは別にスケヌル揃える必芁がないのかで考えるず理解しやすいかもしれない。

ここたでずおも長い蚘事を読んでくださっおありがずうございたした。この手の専門家でもなんでもないので、匷い方のご意芋をお埅ちしおおりたす。

  1. 固有倀分解でも特異倀分解でも固有ベクトル、固有倀は求められたす。詳しくはこちら https://qiita.com/horiem/items/71380db4b659fb9307b4 ↩

  2. https://ja.wikipedia.org/wiki/%E7%9B%B4%E4%BA%A4%E8%A1%8C%E5%88%97 ↩

  3. グラム行列から芋た分散共分散行列、盞関行列の関係はこちらに曞きたした https://blog.shikoan.com/cov-corr-gram-matrix/ ↩

  4. Sklearnの゜ヌスコヌドを読むず、scipy.linalg.svdで蚈算した特異倀の2乗の环積和で蚈算しおいるのですが、explained_variance_で出しおいる倀がnp.lilalg.svdで蚈算した特異倀ず䞀臎するので、ここでは特異倀を2乗せずにそのたた䜿っおいたす。explained_variance_ratio_もなぜか1乗で蚈算したほうがあっおいたす。䞍思議。 https://github.com/scikit-learn/scikit-learn/blob/bac89c2/sklearn/decomposition/pca.py#L444 ↩

234
209
11

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
234
209

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?