主成分分析法
主成分分析法:(Principle Component Analysis, PCA),是一個非監督機器學習算法,主要用於數據降維,通過降維,可以發現便於人們理解的特征,其他應用:可視化和去噪等。
一、主成分分析的理解

先假設用數據的兩個特征畫出散點圖,如果我們只保留特征1或者只保留特征2。那么此時就有一個問題,留個哪個特征比較好呢?

通過上面對兩個特征的映射結果可以發現保留特征1比較好,因為保留特征1,當把所有的點映射到x軸上以后,點和點之間的距離相對較大,也就是說,擁有更高的可區分度,同時還保留着部分映射之前的空間信息。那么如果把點都映射到y軸上,發現點與點距離更近了,這不符合數據原來的空間分布。所以保留特征1相比保留特征2更加合適,但是這是最好的方案嗎?

也就是說,我們需要找到讓這個樣本間距最大的軸?那么如何定義樣本之間的間距呢?一般我們會使用方差(Variance),Var(x)=\frac{1}{m}\sum_{i=1}m(x_{i}-\overline{x})2,找到一個軸,使得樣本空間的所有點映射到這個軸的方差最大。
- 第一步:將樣本進行均值歸0,此時$Var(x)=\frac{1}{m}\sum_{i=1}m(x_{i})2$,此時計算過程就少一項,這就是為什么要進行樣本均值歸0。

- 第二步:需要定義一個軸的方向w=(w1, w2),使得我們的樣本,映射到w以后,有:
Var(X_{project}) = \frac{1}{m}\sum_{i=1}m(X_{project}(i) - \overline{x}{project})^2最大。其實括號中的部分是一個向量,更加准確的描述應該是Var(X{project}) = \frac{1}{m}\sum_{i=1}m\lVertX_{project}(i) - \overline{x}{project}\lVert^2,因為前面已經去均值,所以,這里只需Var(X{project}) = \frac{1}{m}\sum_{i=1}m\lVertX_{project}(i) \lVert^2最大

X^{(i)} \cdot w = \lVert X^{(i)} \lVert \cdot \lVert w \lVert \cdot cos\theta
設w為單位向量,X^{(i)} \cdot w = \lVert X^{(i)} \lVert \cdot cos\theta
最終,X^{(i)} \cdot w = \lVert X_{project}^{(i)} \lVert
所以,最終只需Var(X_{project}) = \frac{1}{m}\sum_{i=1}^m\lVert X^{(i)} \cdot w \lVert^2最大

目標:求w,使得Var(X_{project}) = \frac{1}{m}\sum_{i=1}^m( X^{(i)} \cdot w )^2最大
Var(X_{project}) = \frac{1}{m}\sum_{i=1}^m( X_{1}^{(i)}w_{1} +X_{2}{(i)}w_{2}+\dots+X_{n}{(i)}w_{n})^2
Var(X_{project}) = \frac{1}{m}\sum_{i=1}^m( X^{(i)} \cdot w )^2
一個目標函數優化問題,使用梯度上升法解決。
二、使用梯度上升法求解PCA


首先生成一組數據:
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0., 10., size=100)
plt.scatter(X[:, 0], X[:, 1])
plt.show()
def demean(X):
# 不使用standardscale標准化數據,求解PCA需要方差,只去均值。
return X - np.mean(X, axis=0) # axis=0表示最終求得的均值是一個行向量,也就是說將每一列求和
x_demean = demean(X)
plt.scatter(x_demean[:,0], x_demean[:,1])
plt.show()
np.mean(x_demean[:,0])
np.mean(x_demean[:,1])
def f(w ,x):
return np.sum((x.dot(w) ** 2)) / len(x)
def df_math(w, x):
return x.T.dot(x.dot(w)) * 2. / len(x)
def df_denug(w, x, epsilon=0.0001):
res = np.empty(len(w))
for i in range(len(w)):
w_1 = w.copy()
w_1[i] += epsilon
w_2 = w.copy()
w_2[i] -= epsilon
res[i] = (f(w_1, x) - f(w_2, x)) / (2 * epsilon)
return res
def direction(w):
return w / np.linalg.norm(w)
def gradient_ascent(df, x, init_w, eta, n_iters=1e-4, epsilon=1e-8):
w = direction(init_w)
cur_iter = 0
while cur_iter < n_iters:
gradient = df(w, x)
last_w = w
w = w + eta * gradient
w = direction(w)
if (abs(f(w, x) - f(last_w, x)) < epsilon):
break
cur_iter += 1
return w
測試:
init_w = np.random.random(X.shape[1]) # 不能0向量開始
init_w
eta = 0.001
gradient_ascent(df_denug, x_demean, init_w, eta)
輸出結果:array([0.84612666, 0.53298187])
w = gradient_ascent(df_math, x_demean, init_w, eta)
plt.scatter(x_demean[:, 0], x_demean[:,1])
plt.plot([0, w[0]*30], [0, w[1]*30], color='r')
plt.show()
這只是一個從2維降到1維的情況,但在實際應用中往往都不這么簡單。
三、求數據的前n個主成分
求出第一主成分以后,如何求出下一個主成分?數據進行改變,將數據在第一個主成分上的分量去掉。然后在新的數據上求第一主成分。

第一步:先求出第一主成分
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0., 10., size=100)
def demean(X):
return X - np.mean(X, axis=0)
X = demean(X)
plt.scatter(X[:,0], X[:,1])
plt.show()
def f(w ,x):
return np.sum((x.dot(w) ** 2)) / len(x)
def df(w, x):
return x.T.dot(x.dot(w)) * 2. / len(x)
def direction(w):
return w / np.linalg.norm(w)
def first_component(df, x, init_w, eta, n_iters=1e-4, epsilon=1e-8):
w = direction(init_w)
cur_iter = 0
while cur_iter < n_iters:
gradient = df(w, x)
last_w = w
w = w + eta * gradient
w = direction(w)
if (abs(f(w, x) - f(last_w, x)) < epsilon):
break
cur_iter += 1
return w
init_w = np.random.random(X.shape[1])
init_w
eta = 0.01
w = first_component(df, X, init_w, eta)
w
輸出結果: array([0.71849226, 0.69553495])
第二步:去掉第一主成分
X2 = np.empty(X.shape)
for i in range(len(X)):
X2[i] = X[i] - np.dot(X[i], w) * w
# X2 = X - X.dot(w).reshape(-1, 1) * w
plt.scatter(X2[:,0], X2[:,1])
plt.show()
第三步:求第二主成分
init_w2 = np.random.random(X2.shape[1])
eta = 0.01
w2 = first_component(df, X2, init_w2, eta)
w2
輸出結果: array([ 0.98482183, -0.17356834])
第四步:畫出主成分
plt.scatter(X2[:,0], X2[:,1])
plt.plot([0, w[0]*30], [0, w[1]*30])
plt.plot([0, w2[0]*30], [0, w2[1]*30])
plt.show()

import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0., 10., size=100)
def demean(X):
return X - np.mean(X, axis=0)
X = demean(X)
def direction(w):
return w / np.linalg.norm(w)
def df(w, x):
return x.T.dot(x.dot(w)) * 2. / len(x)
def f(w ,x):
return np.sum((x.dot(w) ** 2)) / len(x)
ef gradient_ascent(df, x, init_w, eta, n_iters=1e-4, epsilon=1e-8):
w = direction(init_w)
cur_iter = 0
while cur_iter < n_iters:
gradient = df(w, x)
last_w = w
w = w + eta * gradient
w = direction(w)
if (abs(f(w, x) - f(last_w, x)) < epsilon):
break
cur_iter += 1
return w
def fisrt_n_component(n, x, eta=0.01, n_iters=1e-4, epsilon=1e-8):
x_pca = x.copy()
x_pca = demean(x_pca)
res = []
for i in range(n):
init_w = np.random.random(x_pca.shape[1])
w = gradient_ascent(df, x_pca, init_w, eta)
res.append(w)
x_pca = x_pca - x_pca.dot(w).reshape(-1, 1) * w
return res
fisrt_n_component(2, X)
輸出結果:[array([0.75978266, 0.65017714]), array([ 0.96416996, -0.26528531])]
四、將高維數據向低維數據映射
假設有數據矩陣X,通過主成分分析法得到前k個主成分對應的軸的單位方向向量之后,我們就可以將數據從高維向低維映射,同樣也可以從低維在映射回高維空間。

import numpy as np
class PCA(object):
def __init__(self, n_components):
assert n_components >= 1, "n_component must be valid"
self.n_components = n_components
self.components_ = None
def fit(self, x, eta=0.01, n_iters=1e-4):
assert self.n_components <= x.shape[1], "n_components must be greater than the feature number of x"
def demean(x):
return x - np.mean(x, axis=0)
def f(w, x):
return np.sum((x.dot(w) ** 2)) / len(x)
def df(w, x):
return x.T.dot(x.dot(w)) * 2. / len(x)
def direction(w):
return w / np.linalg.norm(w)
def gradient_ascent(df, x, init_w, eta, n_iters=1e-4, epsilon=1e-8):
w = direction(init_w)
cur_iter = 0
while cur_iter < n_iters:
gradient = df(w, x)
last_w = w
w = w + eta * gradient
w = direction(w)
if (abs(f(w, x) - f(last_w, x)) < epsilon):
break
cur_iter += 1
return w
x_pca = demean(x)
self.components_ = np.empty(shape=(self.n_components, X.shape[1]))
for i in range(self.n_components):
init_w = np.random.random(x_pca.shape[1])
w = gradient_ascent(df, x_pca, init_w, eta, n_iters)
self.components_[i, :] = w
x_pca = x_pca - x_pca.dot(w).reshape(-1, 1) * w
return self
def transform(self, x):
"將給定的x,映射到各個主成分分量中"
assert x.shape[1] == self.components_.shape[1]
return x.dot(self.components_.T)
def inverse_transform(self, x):
assert x.shape[1] == self.components_.shape[0]
return x.dot(self.components_)
def __repr__(self):
return "PCA(n_components=%d)" % self.n_components
if __name__ == '__main__':
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0., 10., size=100)
pca = PCA(n_components=1)
pca.fit(X)
print(pca.components_)
x_reduction = pca.transform(X)
print(x_reduction.shape)
x_restore = pca.inverse_transform(x_reduction)
print(x_restore.shape)
plt.scatter(X[:, 0], X[:, 1], color='b', alpha=0.5)
plt.scatter(x_restore[:, 0], x_restore[:, 1], color='r', alpha=0.5)
plt.show()

藍色的點是隨機初始生成,紅色的點就是由pca降維之后,在從低維映射到高維的描述。其實完全可以只有一個軸來表示這些紅色的點,也就完成了降維。
五、scikit-learn中的PCA
from sklearn.decomposition import PCA
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0., 10., size=100)
def demean(X):
return X - np.mean(X, axis=0)
X = demean(X)
pca = PCA(n_components=1)
pca.fit(X)
pca.components_
輸出結果:array([[-0.7896098, -0.6136093]])
x_reduction = pca.transform(X)
x_reduction.shape
x_restore = pca.inverse_transform(x_reduction)
x_restore.shape
plt.scatter(X[:,0], X[:,1], color='b', alpha=0.5)
plt.scatter(x_restore[:,0], x_restore[:,1], color='r', alpha=0.5)
plt.show()

接下來使用真實的數據集進行測試:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
digits = datasets.load_digits()
x = digits.data
y = digits.target
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=666)
x_train.shape
接下來使用KNN先進行一個簡單的測試:
%%time
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train, y_train)
輸出結果:Wall time: 4.88 ms
knn_clf.score(x_test, y_test)
輸出結果:0.9888888888888889
接下來使用降維:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(x_train)
#PCA(copy=True, iterated_power='auto', n_components=2, random_state=None,svd_solver='auto', tol=0.0, whiten=False)
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
%%time
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train_reduction, y_train)
輸出結果:Wall time: 976 µs 相比降維之前速度快了很多。
knn_clf.score(x_test_reduction, y_test)
輸出結果:0.6055555555555555,但是測試結果的准確率大打折扣。這是因為直接從64維降到2維,損失了太多信息。在sklearn中封裝了一個函數,可以看出損失的詳細信息。
pca.explained_variance_ratio_
輸出結果:array([0.1450646 , 0.13714246]),可以看出這兩個主成分分別損失了14.51%和13.71%,加和共損失28.22%。顯然這不是想要的效果。
那么我們查看一個64個主成分分別損失情況:
pca = PCA(n_components=x_train.shape[1])
pca.fit(x_train)
pca.explained_variance_ratio_
輸出結果:
array([1.45064600e-01, 1.37142456e-01, 1.19680004e-01, 8.43768923e-02,
5.87005941e-02, 5.01797333e-02, 4.34065700e-02, 3.61375740e-02,
3.39661991e-02, 3.00599249e-02, 2.38906921e-02, 2.29417581e-02,
1.81335935e-02, 1.78403959e-02, 1.47411385e-02, 1.41290045e-02,
1.29333094e-02, 1.25283166e-02, 1.01123057e-02, 9.08986879e-03,
8.98365069e-03, 7.72299807e-03, 7.62541166e-03, 7.09954951e-03,
6.96433125e-03, 5.84665284e-03, 5.77225779e-03, 5.07732970e-03,
4.84364707e-03, 4.34595748e-03, 3.73352381e-03, 3.57655938e-03,
3.30727680e-03, 3.18129431e-03, 3.06969704e-03, 2.89170006e-03,
2.51205204e-03, 2.27743660e-03, 2.22760483e-03, 2.00065017e-03,
1.89529684e-03, 1.56877138e-03, 1.42740894e-03, 1.39115781e-03,
1.20896097e-03, 1.10149976e-03, 9.81702199e-04, 8.82376601e-04,
5.69898729e-04, 4.10322729e-04, 2.32125043e-04, 8.49807543e-05,
5.37426557e-05, 5.27990816e-05, 1.03398093e-05, 6.20749843e-06,
5.03430895e-06, 1.15881302e-06, 1.09689390e-06, 5.51564753e-07,
5.65215369e-08, 1.78867947e-33, 8.83490979e-34, 8.55393580e-34])
從上面結果可以看出,64個主成分損失從大到小排列,最大14.5%,最小8.55393580e-34可以忽略不計。接下來使用曲線圖繪制一下:
plt.plot([i for i in range(x_train.shape[1])],
[np.sum(pca.explained_variance_ratio_[:i+1]) for i in range(x_train.shape[1])])
plt.show()

從圖中,可以大概看出當降到30維的時候,保留信息大概在96%左右,因此可以考慮降到30為左右比較合適,既能保證精度又能保證速度。其實,sklearn中已經封裝好了能夠實現類似功能。
pca = PCA(0.95)
pca.fit(x_train)
pca.n_components_
輸出結果:28,可以發現當損失信息為5%時,可以將數據從64維降到28維。
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
%%time
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train_reduction, y_train)
輸出結果:Wall time: 2.93 ms
knn_clf.score(x_test_reduction, y_test)
輸出結果:0.9833333333333333
對比降維前后:
- 時間上,降維前:Wall time: 4.88 ms,降維后:Wall time: 2.93 ms
- 精度上,降維前:0.9888888888888889,降維后0.9833333333333333
通過以上對比,可以發現在精度損失0.5%的情況下速度能提高一倍。這是一個比較的維度,如果維度很大,效果會更加優秀。
在整個測試的過程中,曾嘗試把數據降到2維,精度損失很大,但並不能說明這樣做沒有意義,通常情況下,我們會將高維數據降到3維或者2維進行可視化。
pca = PCA(n_components=2)
pca.fit(x)
x_reduction = pca.transform(x)
x_reduction.shape
for i in range(10):
plt.scatter(x_reduction[y==i, 0], x_reduction[y==i, 1], alpha=0.8)
plt.show()

通過對數據進行可視化,可以發現有藍色、紅色、紫色這些點在降到2維的情況下,依舊可以實現很高的識別度。這說明這些樣本在原來的高維空間中差異就比較大。所以,一般會將數據進行可視化,進行一些簡單的分析。
六、對真實數據集MNIST使用PCA
首先下載MNIST數據集的時候有個坑?
import numpy as np
from sklearn.datasets import fetch_mldata
mnist = fetch_mldata("MNIST original", data_home='./datasets')
如果直接運行會一直報錯,反正就是下載不下來,但是會在datasets目錄下生成一個文件夾,./datasets/mldata。所以需要下載數據集放在這個文件夾下,再次運行就可以了。
數據集地址:
鏈接:https://pan.baidu.com/s/15k6Sykf5TICN40KeEc6xgQ
提取碼:ujrq
mnist
{'DESCR': 'mldata.org dataset: mnist-original',
'COL_NAMES': ['label', 'data'],
'target': array([0., 0., 0., ..., 9., 9., 9.]),
'data': array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]], dtype=uint8)}
x, y = mnist['data'], mnist['target']
x.shape
y.shape
輸出結果:(70000, 784) (70000,)
x_train = np.array(x[:60000], dtype=float)
y_train = np.array(y[:60000], dtype=float)
x_test = np.array(x[60000:], dtype=float)
x_test = np.array(y[60000:], dtype=float)
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
%time knn_clf.fit(x_train, y_train)
輸出結果:
Wall time: 28 s
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=5, p=2,
weights='uniform')
%time knn_clf.score(x_test, y_test)
輸出結果:
Wall time: 11min 6s
0.9688
在這里,值得一提的是,之前討論knn的時候,由於相比較樣本之間距離,所以通常會對數據進行歸一化。但是這里沒用使用standardscale,因為這是圖像。因為圖像的像素點都在0-255,所以進行距離的比較是有意的,只有當尺度不一致時,才會考慮使用歸一化。
接下來使用PCA進行降維:
from sklearn.decomposition import PCA
pca = PCA(0.9)
pca.fit(x_train)
x_train_reduction = pca.transform(x_train)
x_train.shape
輸出結果:(60000, 87) 可以發現通過降維實現了只有87維代替原來的784維,能夠保留90的信息。
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
%time knn_clf.fit(x_train_reduction, y_train)
輸出結果:Wall time: 436 ms 降維前使用了28s完成現在只需436ms。
x_test_reduction = pca.transform(x_test)
%time knn_clf.score(x_test_reduction, y_test)
Wall time: 1min 17s
0.9728
將降維前后進行對比:
- 時間上:
- 訓練時間-降維前:28 s,降維后:436 ms
- 測試時間-降維前:11min 6s,降維后:1min 17s
- 精度上:降維前:0.9688,降維后:0.9728
經過上面的對比,發現經過降維之后速度提高了很多很多,而且准確率反而高了,這是為什么呢?這是因為其實降維還有另外一個作用,就是去噪。
七、使用PCA降噪
import numpy as np
import matplotlib.pyplot as plt
X = np.empty((100, 2))
X[:, 0] = np.random.uniform(0., 100., size=100)
X[:, 1] = 0.75 * X[:, 0] + 3. + np.random.normal(0, 5, size=100)
plt.scatter(X[:,0], X[:,1])
plt.show()
from sklearn.decomposition import PCA
pca = PCA(n_components=1)
pca.fit(X)
x_reduction = pca.transform(X)
x_restore = pca.inverse_transform(x_reduction)
plt.scatter(x_restore[:, 0], x_restore[:, 1])
plt.show()


通過這個例子,發現pca還可以降噪,將高維數據降低到低維數據,這個過程中也可以理解成降低了維度,丟失了信息,同時也去除了噪音。為了更加直觀,接下來使用手寫數字識別數據集。
from sklearn import datasets
digits = datasets.load_digits()
x = digits.data
y = digits.target
# 手動添加噪聲
noisy_digits = x + np.random.normal(0, 4, size=x.shape)
# 取出部分數據做可視化
example_digits = noisy_digits[y==0,:][:10]
for num in range(1, 10):
x_num = noisy_digits[y==num,:][:10]
example_digits = np.vstack([example_digits, x_num])
example_digits.shape
def plot_digits(data):
fig, axes = plt.subplots(10, 10, figsize=(10,10),
subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(data[i].reshape(8, 8),
cmap='binary', interpolation='nearest',
clim=(0, 16))
plt.show()
plot_digits(example_digits)

接下來使用pca去噪,
from sklearn.decomposition import PCA
pca = PCA(0.5)
pca.fit(noisy_digits)
pca.n_components_
輸出結果:12,可以看出保留50%的信息,只需要12維。
components = pca.transform(example_digits)
filterd_digits = pca.inverse_transform(components)
plot_digits(filterd_digits)

通過這個例子,可以對pca降維有一個感性的認識。
八、PCA與人臉識別

假設X一張人臉圖像,W是錢k個主成分組成的矩陣,其實可以把W看做X的一些比較明顯的特征。這就是特征臉(Eigenface)。
首先下載數據集,sklearn中的lfw_people數據集:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people()
這里依舊有個小坑,就是數據下載不下來,不過沒事,我們可以手動下載,復制這個網址 https://ndownloader.figshare.com/files/5976015 到迅雷中,下載到完整的lfwfunneded.tgz文件。並把這個文件放在C:\Users\Mogul\scikit_learn_data\lfw_home路徑下,解壓。然后再次運行就可以了。
faces
faces.keys()
faces.data.shape
#(13233, 2914),數據集中一共13233個樣本,每個樣本維度2914
faces.images.shape
# (13233, 62, 47),每個圖片的大小(62, 47)
random_indexes = np.random.permutation(len(faces.data))
# 將數據集的順序打亂
X = faces.data[random_indexes]
# 從中取除前36個
example_faces = X[:36, :]
example_faces.shape
def plot_faces(faces):
fig, axes = plt.subplots(6, 6, figsize=(10, 10),
subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(faces[i].reshape(62, 47), cmap='bone')
plt.show()
plot_faces(example_faces)

faces.target_names
# array(['AJ Cook', 'AJ Lamas', 'Aaron Eckhart', ..., 'Zumrati Juma',
'Zurab Tsereteli', 'Zydrunas Ilgauskas'], dtype='<U35')
len(faces.target_names)
# 5749,數據集中一共有5749個人物,一共有13233張圖片,差不多每個人2張。
# 但實際情況並不是這樣,因為樣本並不均衡,有些可能只有一張,有些可能很多張。
%%time
from sklearn.decomposition import PCA
pca = PCA(svd_solver='randomized')
pca.fit(X)
輸出結果:Wall time: 18.4 s
pca.components_.shape
# (2914, 2914)
# 取出前36個特征臉進行可視化
plot_faces(pca.components_[:36, :])

剛剛提到這個數據集的樣本分布是不均衡的,那么接下來看看具體:
faces2 = fetch_lfw_people(min_faces_per_person=60)
faces2.data.shape
# (1348, 2914)
faces2.target_names
輸出結果:
array(['Ariel Sharon', 'Colin Powell', 'Donald Rumsfeld', 'George W Bush',
'Gerhard Schroeder', 'Hugo Chavez', 'Junichiro Koizumi',
'Tony Blair'], dtype='<U17')
根據輸出結果可以發現,最少有60張圖像的數據集一共有1348張圖片,只有8個人的圖像大於60,使用這樣的數據進行人臉識別相對就比較合理了。
我是尾巴:
其實在PCA的背后是強大的數學支撐。在這里使用的梯度上升法對PCA進行求解,其實它是可以通過數學推導出相應的公式,具體的數學解釋,以后會在寫一篇。
本次推薦:
你是如何強迫自己不斷學習提升的?
看過更大的世界,更優秀的人,就再也不甘心留在原地,不甘心就是動力!
繼續加油~
