Machine Learning in Action 读书笔记
第13章 利用PCA来简化数据
一、降维技术
降维(dimensionality reduction),可以将数据从高维度将低维度,在低维下,数据更容易进行处理。
对数据进行简化有如一系列原因:
- 使得数据集更易使用
- 降低很多算法的计算开销
- 去除噪声
- 使得结果易懂
本章主要关注为标注数据上的降维技术,该技术同时也可以应用于已标注的数据。
降维方法:
- 主成分分析(Principal Component Analysis,PCA):在PCA中,数据从原来的坐标系转换到了新的坐标系,新坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二个新坐标轴的选择和第一个坐标轴正交(垂直)且具有最大方差的方向。该过程一直重复,重复次数为原始数据中特征的数目。
- 因子分析(Factor Analysis):假设在观察数据的生成中有一些观察不到的隐变量(latent variable),通过找到隐变量就可以实现数据的降维。
- 独立成分分析(Independent Component Analysis,ICA):和因子分析一样,如果数据源的数目少于观察数据的数目,则可以实现降维过程。
二、主成分分析
- 优点:降低数据的复杂性,识别最重要的多个特征
- 缺点:不一定需要,且可能损失有用信息
- 适用数据类型:数值型数据
通过python中的Numpy来实现PCA。
1.移动坐标轴
在PCA中,我们对数据的坐标进行了旋转,该旋转的过程取决于数据的本身。第一条坐标轴旋转到覆盖数据的最大方差位置(最长直线,也是数据集中差异化最大的方向),数据的最大方差给出了数据的最重要的信息。在选择了覆盖数据最大差异性的坐标轴之后,选择第二条坐标轴,该坐标轴与第一条坐标轴垂直(正交,orthogonal),他就是覆盖数据次大差异性的坐标轴。,利用PCA,可以将数据坐标轴旋转至数据角度上的那些最重要的方向。
通过PCA进行降维处理,就可以同时获得SVM和决策树的优点:一方面,得到了和决策树一样简单的分类器,同时分类间隔和SVM(支持向量机)一样好。
前面提到,第一个主成分就是从数据差异性最大(即方差最大)的方向提取出来的。第二个主成分则来自于数据差异性次大的方向,并且该方向与第一个主成分方向正交。通过数据集的协方差矩阵及其特征值分析,就可以求得这些主成分的值。
一旦得到协方差矩阵的特征向量,就可以保留最大的N个值,这些特征向量也给出了N个最重要特征的真实结构。可以通过将数据乘上这N个特征向量而将它转换到新的空间。
在Numpy中实现PCA的伪代码:
去除平均值
计算协方差矩阵
计算协方差矩阵的特征值和特征向量
将特征值从大到小排序
保留最上面的N个特征向量
将数据转换到上述N个特征向量构建的新空间中
PCA算法:
from numpy import *
import matplotlib
import matplotlib.pyplot as plt
# PCA(主成分分析)算法
def loadDataSet(fileName, delim='\t'):
fr = open(fileName)
stringArr = [line.strip().split(delim) for line in fr.readlines()] # 使用两个列表推导式来构建矩阵
datArr = [list(map(float,line)) for line in stringArr]
return mat(datArr)
def pca(dataMat, topNfeat=9999999): # 参数:用于进行pca操作的数据集,应用的N个特征(可以选择,如果不指定,函数就会返回前9999999个特征,或者原始数据中给的全部特征)
meanVals = mean(dataMat, axis=0) # 计算原始数据集的平均值
meanRemoved = dataMat - meanVals # 减去原始数据集的平均值
covMat = cov(meanRemoved, rowvar=0) # 计算协方差矩阵
eigVals,eigVects = linalg.eig(mat(covMat)) # 计算协方差矩阵的特征值和特征向量
# print(eigVals) #[0.36651371 2.89713496]
# print('==============================================')
# print(eigVects) #[[-0.85389096 -0.52045195] [ 0.52045195 -0.85389096]]
eigValInd = argsort(eigVals) #利用argsort()函数对特征值进行从小到大的排序
eigValInd = eigValInd[:-(topNfeat+1):-1] #根据特征值排序结果的逆序得到topNfeat个最大的特征向量
redEigVects = eigVects[:,eigValInd] #重组从最大到最小的特征向量
lowDDataMat = meanRemoved * redEigVects #将数据乘上N个特征向量,利用N个特征将原始数据转换到新空间中
reconMat = (lowDDataMat * redEigVects.T) + meanVals
return lowDDataMat, reconMat # 返回降维之后的数据集和重构后的原始数据,返回重构后的原始数据用于调试
通过以下代码将二维原始数据和降维后的数据绘制出来:
if __name__ == '__main__':
dataMat = loadDataSet('testSet.txt')
# print(dataMat)
# print(shape(dataMat)) # (1000, 2)
lowDMat, reconMat = pca(dataMat, 1)
# print(shape(lowDMat)) # (1000, 1)
# 将降维后的数据和原始数据一起绘制出来
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(dataMat[:, 0].flatten().A[0], dataMat[:, 1].flatten().A[0], marker='^', s=90)
ax.scatter(reconMat[:, 0].flatten().A[0], reconMat[:, 1].flatten().A[0], marker='o', s=50, c='red')
plt.show()
二维原始数据和降维后的数据(红)
三、示例:利用PCA对半导体制造数据降维
'''示例:利用PCA对半导体制造数据降维'''
# 将NaN(not a number)替换成平均值的函数
def replaceNanWithMean():
datMat = loadDataSet('secom.data', ' ')
numFeat = shape(datMat)[1] # 获得特征数量(列数)
for i in range(numFeat):
# 为每一列(每个特征)计算非NAN值的平均值
meanVal = mean(datMat[nonzero(~isnan(datMat[:,i].A))[0],i]) #nonzero返回非零元素的索引值;mat.A可以将矩阵转化为数组;isnan逐个元素测试是否为NaN并以布尔数组形式返回结果。
datMat[nonzero(isnan(datMat[:,i].A))[0],i] = meanVal #将NaN用上面计算的平均值替换
return datMat
if __name__ == '__main__':
'''示例:利用PCA对半导体制造数据降维'''
dataMat = replaceNanWithMean()
meanVals = mean(dataMat, axis=0)
meanRemoved = dataMat - meanVals
covMat = cov(meanRemoved, rowvar=0)
eigVals, eigVects = linalg.eig(mat(covMat))
# print(eigVals)
'''
从求得的特征值可以知道,其中有超多百分之二十的特征值都是0,这就意味着这些特征都是其他特征的副本,可以通过其他特征来表示,本身并没有提供额外的信息
从求得的特征的大小可以知道,从开始到后面特征值在慢慢减小,后面的值变得非常小,这说明只有部分重要特征,重要特征的数目也很快就会下降
[ 5.34151979e+07 2.17466719e+07 8.24837662e+06 2.07388086e+06
1.31540439e+06 4.67693557e+05 2.90863555e+05 2.83668601e+05
2.37155830e+05 2.08513836e+05 1.96098849e+05 1.86856549e+05
1.52422354e+05 1.13215032e+05 1.08493848e+05 1.02849533e+05
1.00166164e+05 8.33473762e+04 8.15850591e+04 7.76560524e+04
...
5.01137874e-09 3.48686481e-09 2.91267158e-09 2.77880632e-09
1.73093443e-09 1.42391218e-09 1.16455138e-09 1.11816007e-09
9.24977357e-10 1.80003603e-10 1.97062639e-10 2.61907926e-10
6.95076631e-10 6.13339126e-10 5.27540889e-10 -1.85894956e-15
2.27343919e-15 2.69163026e-16 0.00000000e+00 0.00000000e+00
0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
...
'''
数据集的前面多个主成分中包含信息量,通过尝试不同的截断值来检验它们的性能。有些人使用能包含90%信息量的主成分数量,而其他人使用前20个主成分,我们无法精确知道需要的主成分数目,必须通过在实验中取不同的值来确定。有效的主成分数目取决于数据集和具体应用。