机器学习算法的类型目前主要分为三类:监督学习、无监督学习和强化学习。其中无监督学习代表算法主要有聚类和降维,降维的经典算法是PCA降维算法。PCA降维简单易用,效果较好,在一些任务中经常使用,如特征人脸的获得。
左图为若干张实际人脸照片组成的人脸库,右图为从人脸库中通过降维得到的特征人脸。
上图表示的是一张真实人脸可以视为 m m m张特征人脸的线性组合。
PCA(Principal Components Analysis),主成分分析。算法步骤如下:
INPUT: X p × n X_{p×n} Xp×n表示有n个样本,每个样本有p个特征的数据集;保留的特征值个数 d d d
step1: 数据标准化, X ∗ = X − μ X^{*}=X-\mu X∗=X−μ , μ \mu μ是每个特征的均值重复扩充得到的 p × n p×n p×n维矩阵
step2: 求协方差矩阵 C = 1 n − 1 X ∗ X ∗ T C=\frac{1}{n-1}X^{*}X^{*T} C=n−11X∗X∗T
step3: 对协方差矩阵进行特征值分解得到特征值和对应的特征向量
step4: 对特征值从大到小排序,选取前 d d d个特征值对应的特征向量构成变换矩阵 U d × p U_{d×p} Ud×p
step5: 进行线性变换, Y d × n = U d × p X p × n Y_{d×n}=U_{d×p}X_{p×n} Yd×n=Ud×pXp×n
OUTPUT: Y d × n Y_{d×n} Yd×n即为降维后得到的矩阵。
import numpy as np
def pca(x, n):
"""
:param x:输入数据,ndarray p*n维,每个数据具有p维特征,共n个数据组成数据集x
:param n:取得前n个特征向量
"""
# step1:数据集标准化
sum_x = np.sum(x, axis=1)
mean_x = (sum_x / x.shape[1]).reshape(x.shape[0], 1)
MEAN_X = np.tile(mean_x, reps=x.shape[1])
x_ = x - MEAN_X # 得到去中心化的数据x_
# step2:求x_*T(x)的协方差矩阵
T_x = x_.T
Cov = (1 / (x.shape[1] - 1)) * np.matmul(x_, T_x) # 得到协方差矩阵
# step3:对协方差矩阵进行特征分解
eigenvalue, featurevector = np.linalg.eig(Cov)
# step4:特征向量按照特征值大小进行排序
for i in range(len(eigenvalue) - 1):
for j in range(len(eigenvalue) - i - 1):
if abs(eigenvalue[j]) < abs(eigenvalue[j + 1]):
eigenvalue[j], eigenvalue[j + 1] = eigenvalue[j + 1], eigenvalue[j]
featurevector[:, [j, j + 1]] = featurevector[:, [j + 1, j]]
# step5:得到变换矩阵
U = featurevector[:, 0:n]
# step6:pca降维
y = np.matmul(U, x)
return y
待分析的数据如下代码所示
if __name__ == "__main__":
np.set_printoptions(threshold=np.inf)
x0 = np.array([[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1],
[2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9]])
利用2中PCA代码对x0进行PCA,得到两个主成分向量,得到特征向量实际上是一组正交基底,用特征向量组成的矩阵对原基底 ( 1 , 0 ) , ( 0 , 1 ) (1,0),(0,1) (1,0),(0,1)进行线性变换得到新基底如下(红色和绿色直线)。得到PCA降维的几何意义: p p p维空间下的点在 d d d维空间下的投影 ( d < p ) (d (d<p)
分析原始二维空间数据点在第一主成分一维空间下的投影和第二主成分一维空间下的投影,可以得到第一主成分一维空间下的投影更加分散,区间大约为 ( − 4.5 , − 1.2 ) (-4.5,-1.2) (−4.5,−1.2),而第二主成分一维空间下的投影明显非常集中,区间大约为 ( − 0.4 , 0.2 ) (-0.4,0.2) (−0.4,0.2)。
附可视化代码如下:
if __name__ == "__main__":
np.set_printoptions(threshold=np.inf)
x0 = np.array([[2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1],
[2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9]])
U, pca_x = pca(x0, 2)
cxy = np.array([[1, 0], [0, 1]]) # 原空间基底
new_cxy = np.matmul(U, cxy)
x = np.linspace(-5, 5, 100)
y1 = (new_cxy[1, 0] / new_cxy[0, 0]) * x
y2 = (new_cxy[1, 1] / new_cxy[0, 1]) * x
plt.figure(1, figsize=(8, 20))
ax1 = plt.subplot(2, 1, 1) # (行,列,活跃区)
plot_point(x0,"原始数据点")
plt.plot(x, y1, color='r',linewidth=2, label="新基底(第一主成分)")
plt.plot(x, y2, color='g', linewidth=2,label="新基底(第二主成分)")
plt.plot(x, np.full(fill_value=0, shape=x.shape), 'k--', linewidth=2, label="原基底")
plt.plot(np.full(fill_value=0, shape=x.shape), x, 'k--', linewidth=2)
plt.xlim(-8, 8)
plt.ylim(-8, 8)
plt.grid(True)
plt.legend(loc='best')
ax2 = plt.subplot(2,2,3)
first = np.matmul(U[:,0],x0)
plt.scatter(first,np.full(fill_value=0, shape=first.shape),label="第一主成分降维")
plt.grid(True)
plt.legend(loc='best')
ax3 = plt.subplot(2, 2, 4)
second = np.matmul(U[:,1],x0)
plt.scatter(second, np.full(fill_value=0, shape=second.shape), label="第二主成分降维")
plt.grid(True)
plt.legend(loc='best')
plt.show()