机器学习入门笔记09

PCA数据降维

系列文章

PCA数据降维

建立模型分析数据时,可能会面临数据的维度过大的问题。例如,一个体检模型可能会有上百个检查值。如果将所有特征全部用于拟合,可能会导致计算慢、拟合不佳等缺点。此时就需要酌情减少特征变量。

PCA简介

基本原理

PCA即主成分分析(Principal Component Analysis),是一种常用的数据降维算法。PCA主要通过坐标的线性变换发现数据的模式。

考虑一个二维的数据,它的主要分布可以用回归直线 \\( y=x \\) 来描述。为了让这些数据从二维降低到一维,可以用该回归直线作为新的坐标轴,建立 \\( x' \\) 轴,这样,原有的二维数据 \\( (x,y) \\) 便可以通过变换 \\( x'=\sqrt{x^2+y^2} \\) 来描述。

如果原特征变量有 \\( n \\) 个,这种 n 维空间降维的思路与二维一致:通过寻找合适的线性组合系数 \\( a_i \\) ,通过方程:

\\[ F = a_1X_1+a_2X_2+\dots+a_nX_n \\]

来减少特征变量的个数。

从 n 维数据直接降低到一维不太现实,但是完全可以将 n 维数据 \\( X_1,X_2,\dots,X_n \\) 降低成 k 维数据 \\( F_1,F_2,\dots,F_k \\)

\\[ F_1 = a_{11}X_1+a_{12}X_2+\dots+a_{1n}X_n\\ F_2 = a_{21}X_1+a_{22}X_2+\dots+a_{2n}X_n\\ \dots\\ F_k = a_{k1}X_1+a_{k2}X_2+\dots+a_{kn}X_n \\]

这些数据需要满足的条件如下:

  1. 每个主成分的系数平方和为 1 ,即
    \\[ a_{i1}^2+a_{i2}^2+\dots+a_{in}^2=1 \\]
  2. 每个主成分互不相关,即
    \\[ \text{Cov}(F_i,F_j)=0, \quad \forall i \neq j \\]
  3. 主成分的方差依次递减,重要性依次递减,即
    \\[ \text{Var}(F_1) \geq \text{Var}(F_2) \geq \dots \geq \text{Var}(F_k) \\]

代码实现

首先,检查一下原始数据的维度:

X.values.shape
(812, 18)

由于数据的维度很高,在使用 PCA 降维之前,首先需要对数据标准化处理,以消除量纲带来的影响:

from sklearn.preprocessing import StandardScaler
X_new = StandardScaler().fit_transform(X)

通过 Scikit-learn 库,搭建PCA模型降维的代码实现如下。

from sklearn.decomposition import PCA
pca = PCA(n_components=0.99)
pca.fit(X)
X_trans = pca.transform(X)
X_trans.shape
(812, 7)

PCA 类的初始化参数 n_components 可以设置成小于 1 的小数,此时表示降维后保留原特征的比例;还可以设置成大于 1 的整数,此时表示保留的成分个数。

可以看到,在保留 95% 的原特征时,原始数据依然可以从 18 维降低到 7 维。如果适当放宽保留的特征,还可以继续降维。

需要注意的是,n_components 设置成整数的情况下,其值不能大于 min(n_samples, n_features) ,即样本数和特征变量数的较小值。

通过如下代码可以获取线性组合系数,由于此处的维度过高,因此只显示降维后第一个维度的系数:

pca.components_[0]
array([ 0.27443837, 0.29401036, 0.30447424, 0.26875664, 0.08257263, 0.09813377, 0.31678357, -0.31329015, 0.31370604, 0.28125671, 0.30921504, 0.31426688, 0.27030013, -0.02553606, 0.03901848, 0.06370325, 0.03058309, 0.07893806])

实例:人脸识别模型

人脸识别的本质是根据每张人脸图中不同像素点的颜色建模与判断。但是人脸像素点即特征变量很多,需要利用PCA降维。

此处使用的数据集是纽约大学公开人脸数据库 Olivetti Faces ,可以在 https://cs.nyu.edu/~roweis/data/olivettifaces.gif 处下载。

原图是一整张图片,含有40个人的脸部照片,每人10张。可以参照如下代码拆分该图片:

from os import mkdir, path
from PIL import Image
img = Image.open('olivettifaces.gif')
split1_x = [-1, 46, 93, 140, 187, 234, 281, 328, 375, 422, 469]
split2_x = [470, 517, 564, 611, 658, 705, 752, 799, 846, 893, 940]
split_y = [-1, 56, 113, 170, 227, 284, 341, 398, 455, 512, 569,
           626, 683, 740, 797, 854, 911, 968, 1025, 1082, 1139]
if not path.exists('faces'): mkdir('faces')
for i in range(len(split1_x)-1):
    for j in range(len(split_y)-1):
        region = img.crop([split1_x[i]+1, split_y[j]+1, split1_x[i+1]-1, split_y[j+1]-1])
        region.save('.\\faces\\' + f'face_{j}_{i}.png')
for i in range(len(split2_x)-1):
    for j in range(len(split_y)-1):
        region = img.crop([split2_x[i]+1, split_y[j]+1, split2_x[i+1]-1, split_y[j+1]-1])
        region.save('.\\faces\\' + f'face_{j+20}_{i}.png')

接下来需要加载这些图像,并将图像处理成适合搭建模型的结果,具体包含以下几个步骤:

  1. 转化为灰度值
  2. 进一步压缩为 32×32 大小,节省计算成本
  3. 转化为数组数据
  4. 将二维数组处理为一维数组

使用以下函数完成上述操作:

def load(filename):
    img = Image.open(filename)
    img = img.convert('L').resize((32, 32))
    return pd.Series(np.array(img).reshape(1, -1).flatten())

以下代码,将所有的面部图片读取并整合在一起:

from os import listdir
X = pd.DataFrame()
for f in listdir('faces'):
    X = X.append(load('faces\\' + f), ignore_index=True)
print(X.values.shape)
(400, 1024)

结果显示 400 张图片都被成功读取了。

接下来还要提取目标变量(人脸编号),可以根据之前为图片命名信息获取:

Y = list(map(lambda name: int(name.split('_')[1]), listdir('faces')))

接下来使用K近邻算法搭建模型学习,具体步骤包括划分训练集与测试集、使用PCA降维(保留互不相关的100个新特征)、搭建KNN模型。这里仅以最后一步作为示例:

from time import perf_counter
from sklearn.neighbors import KNeighborsClassifier
now = perf_counter()
model = KNeighborsClassifier()
model.fit(X_train_pca, Y_train)
print(perf_counter() - now)
print(model.score(X_test_pca, Y_test))
0.00046870000000032164 0.8875

同时对不降维的数据搭建模型作为对比,结果如下:

from sklearn.neighbors import KNeighborsClassifier
now = perf_counter()
model = KNeighborsClassifier()
model.fit(X_train, Y_train)
print(perf_counter() - now)
print(model.score(X_test, Y_test))
0.007870000000000044 0.875

可以看到降维后的数据不管是学习花费的时间,还是预测的成功率都更优秀,这充分证明了使用PCA降维的优势。