概述
- 是一种非监督的机器学习法
- 主要用于数据的降维
- 通过降维,便于理解数据的特征
- 可以用于数据的去噪
主成分分析法的目标是求 w w w,使得 V a r ( X p r o j e c t ) = 1 m ∑ i = 1 m ( ∑ j = 1 m X j ( i ) w j ) 2 Var(X_{project})=\frac{1}{m}\sum^m_{i=1}(\sum^m_{j=1}X_j^{(i)}w_j)^2 Var(Xproject)=m1∑i=1m(∑j=1mXj(i)wj)2。
公式的意思是:把m维的点,映射到 w w w坐标系上面,使得这些点在 w w w坐标系上的方差最大(最稀疏)。其中 w w w坐标系的每一个维度就被称为主成分。
在梯度下降法中,我求解了方差最小,相应的,我使用梯度上升法可以去求解方差最大。
任何梯度算法都需要使用导数,因此在计算之前,首先应当对导数的公式进行推导:
先不推了,就这样吧,直接sklearn实战
scikit-learn中的PCA
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets # sklearn自带的数据
digits = datasets.load_digits() # 手写数字的数据
x = digits.data
y = digits.target
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=666) # 先区分训练集和测试集
PCA是对自变量降维,只保留其中具有解释力的维度。降维后的数据在进行机器学习运算时会更快。
在KNN算法的研究中,我进行写手写数字的识别。如果使用PCA算法对自变量数据进行降维,那么计算的速度将会大大的提高。
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train, y_train)
%time knn_clf.score(x_test, y_test) # 查看需要的时间
CPU times: user 68.4 ms, sys: 1.54 ms, total: 69.9 ms
Wall time: 68.7 ms
Out[65]:
0.9866666666666667
发现KNN在计算模型的score时使用了69.9ms,准确度为98.67%
如果先对x进行降维处理,那么:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(x_train)
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train_reduction, y_train)
%time knn_clf.score(x_test_reduction, y_test)
CPU times: user 20 ms, sys: 5.03 ms, total: 25 ms
Wall time: 20.3 ms
Out[68]:
0.6066666666666667
我们发现虽然计算的时间从69.9ms降低到了25ms,但是准确率却下降到了60.67%。
这是因为在进行降维时,我们过度的减少了原有数据的维度,将原本64维的数据降低为2维,这本身就不合理。
我们通过 pca.explained_variance_ratio_
可以查看这两个维度的解释力度发现:array([0.14566817, 0.13735469])
,两个加起来对原数据的方差解释不到30%,意味着我们丢失了70%的解释力度。
我们可以保持数据的64维不变,来看看每个维度的解释力度:
pca = PCA(n_components=x_train.shape[1])
pca.fit(x_train)
pca.explained_variance_ratio_
array([1.45668166e-01, 1.37354688e-01, 1.17777287e-01, 8.49968861e-02,
5.86018996e-02, 5.11542945e-02, 4.26605279e-02, 3.60119663e-02,
3.41105814e-02, 3.05407804e-02, 2.42337671e-02, 2.28700570e-02,
1.80304649e-02, 1.79346003e-02, 1.45798298e-02, 1.42044841e-02,
1.29961033e-02, 1.26617002e-02, 1.01728635e-02, 9.09314698e-03,
8.85220461e-03, 7.73828332e-03, 7.60516219e-03, 7.11864860e-03,
6.85977267e-03, 5.76411920e-03, 5.71688020e-03, 5.08255707e-03,
4.89020776e-03, 4.34888085e-03, 3.72917505e-03, 3.57755036e-03,
3.26989470e-03, 3.14917937e-03, 3.09269839e-03, 2.87619649e-03,
2.50362666e-03, 2.25417403e-03, 2.20030857e-03, 1.98028746e-03,
1.88195578e-03, 1.52769283e-03, 1.42823692e-03, 1.38003340e-03,
1.17572392e-03, 1.07377463e-03, 9.55152460e-04, 9.00017642e-04,
5.79162563e-04, 3.82793717e-04, 2.38328586e-04, 8.40132221e-05,
5.60545588e-05, 5.48538930e-05, 1.08077650e-05, 4.01354717e-06,
1.23186515e-06, 1.05783059e-06, 6.06659094e-07, 5.86686040e-07,
7.44075955e-34, 7.44075955e-34, 7.44075955e-34, 7.15189459e-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()
sklearn中的PCA算法提供了一个很好用的参数:
pca = PCA(0.95) # 不知道取多少个主成分,但是可以解释95%以上的方差
pca.fit(x_train)
x_train_reduction = pca.transform(x_train)
x_test_reduction = pca.transform(x_test)
knn_clf = KNeighborsClassifier()
knn_clf.fit(x_train_reduction, y_train)
%time knn_clf.score(x_test_reduction, y_test)
CPU times: user 36 ms, sys: 1.36 ms, total: 37.3 ms
Wall time: 35.8 ms
Out[72]:
0.98
我们发现不但准确率变高了,而且计算速度也变快了。
用PCA发现人脸识别中的特征脸
其实特征脸就是寻找人脸中的主成分,这些特征脸对模型中的脸又很好的解释力度。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_lfw_people # sklearn自带的人脸数据
faces = fetch_lfw_people() # 数据下载
faces.keys() # dict_keys(['data', 'images', 'target', 'target_names', 'DESCR'])
faces.data.shape # (13233, 2914),也就是13233个数据,每个数据都有2914个像素点组成
random_indexes = np.random.permutation(len(faces.data))
x = faces.data[random_indexes]
# 绘制36张脸作为实例
example_faces = x[:36, :]
example_faces.shape # (36, 2914)
def plot_faces(faces):
fig, axs = 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(axs.flat):
ax.imshow(faces[i].reshape(62, 47), cmap='bone')
plt.show()
plot_faces(example_faces)
进行PCA降维后就可以得到特征脸
from sklearn.decomposition import PCA
pca = PCA(svd_solver='randomized')
pca.fit(x)
plot_faces(pca.components_[:36])