(白宁超 2018年10月22日10:14:18)html
摘要:主成分分析(英语:Principal components analysis,PCA)是一种分析、简化数据集的技术。主成分分析常常用于减小数据集的维数,同时保持数据集中的对方差贡献最大的特征。经常应用在文本处理、人脸识别、图片识别、天然语言处理等领域。能够作在数据预处理阶段很是重要的一环,本文首先对基本概念进行介绍,而后给出PCA算法思想、流程、优缺点等等。最后经过一个综合案例去实现应用。(本文原创,转载必须注明出处.)python
降维是对数据高维度特征的一种预处理方法。降维是将高维度的数据保留下最重要的一些特征,去除噪声和不重要的特征,从而实现提高数据处理速度的目的。在实际的生产和应用中,降维在必定的信息损失范围内,能够为咱们节省大量的时间和成本。降维也成为了应用很是普遍的数据预处理方法。git
咱们正经过电视观看体育比赛,在电视的显示器上有一个足球。显示器大概包含了100万像素点,而球则多是由较少的像素点组成,例如说一千个像素点。人们实时的将显示器上的百万像素转换成为一个三维图像,该图像就给出运动场上球的位置。在这个过程当中,人们已经将百万像素点的数据,降至为三维。这个过程就称为降维(dimensionality reduction)github
数据降维的目的:算法
适用范围:api
常见降维技术(PCA的应用目前最为普遍)数组
主成分分析(Principal Component Analysis, PCA):通俗理解:就是找出一个最主要的特征,而后进行分析。机器学习
主成分分析(英语:Principal components analysis,PCA)是一种分析、简化数据集的技术。主成分分析常常用于减小数据集的维数,同时保持数据集中的对方差贡献最大的特征。这是经过保留低阶主成分,忽略高阶主成分作到的。这样低阶成分每每可以保留住数据的最重要方面。可是,这也不是必定的,要视具体应用而定。因为主成分分析依赖所给数据,因此数据的准确性对分析结果影响很大。函数
主成分分析由卡尔·皮尔逊于1901年发明,用于分析数据及创建数理模型。其方法主要是经过对协方差矩阵进行特征分解,以得出数据的主成分(即特征向量)与它们的权值(即特征值)。PCA是最简单的以特征量分析多元统计分布的方法。其结果能够理解为对原数据中的方差作出解释:哪个方向上的数据值对方差的影响最大?换而言之,PCA提供了一种下降数据维度的有效办法;若是分析者在原数据中除掉最小的特征值所对应的成分,那么所得的低维度数据一定是最优化的(也即,这样下降维度一定是失去讯息最少的方法)。主成分分析在分析复杂数据时尤其有用,好比人脸识别。oop
PCA是最简单的以特征量分析多元统计分布的方法。一般状况下,这种运算能够被看做是揭露数据的内部结构,从而更好的解释数据的变量的方法。若是一个多元数据集可以在一个高维数据空间坐标系中被显现出来,那么PCA就可以提供一幅比较低维度的图像,这幅图像即为在讯息最多的点上原对象的一个‘投影’。这样就能够利用少许的主成分使得数据的维度下降了。PCA跟因子分析密切相关,而且已经有不少混合这两种分析的统计包。而真实要素分析则是假定底层结构,求得微小差别矩阵的特征向量。
PCA 场景
例如: 考察一我的的智力状况,就直接看数学成绩就行(存在:数学、语文、英语成绩)
PCA 思想
去除平均值 计算协方差矩阵 计算协方差矩阵的特征值和特征向量 将特征值排序 保留前N个最大的特征值对应的特征向量 将数据转换到上面获得的N个特征向量构建的新空间中(实现了特征压缩)
PCA 原理
PCA 算法流程
下面咱们看看具体的算法流程。
输入:n维样本集\( D=(x^{(1)},x^{(2)},...,x^{(m)}) \),要降维到的维数n.
输出:降维后的样本集\( D^′\)
1) 对全部的样本进行中心化:\( x^{(i)}=x^{(i)}−\frac{1}{m}\sum_{j=1}^{m}x^{(j)} \)
2) 计算样本的协方差矩阵\( XX^T\)
3) 对矩阵\( XX^T\)进行特征值分解
4)取出最大的n'个特征值对应的特征向量\( (w_1,w_2,...,w_n^′) \), 将全部的特征向量标准化后,组成特征向量矩阵W。
5)对样本集中的每个样本\( x^{(i)}\),转化为新的样本\( z^{(i)}=W^Tx^{(i)} \)
6) 获得输出样本集\( D^′=(z^{(1)},z^{(2)},...,z^{(m)}) \)
PCA 优缺点
优势:下降数据的复杂性,识别最重要的多个特征。
缺点:不必定须要,且可能损失有用信息。
适用数据类型:数值型数据。
真实的训练数据老是存在各类各样的问题:
这时能够采用主成分分析(PCA)的方法来解决部分上述问题。PCA的思想是将n维特征映射到k维上(k<n),这k维是全新的正交特征。这k维特征称为主元,是从新构造出来的k维特征,而不是简单地从n维特征中去除其他n-k维特征。
收集数据:提供文本文件,文件名:testSet.txt.文本文件部分数据格式以下:
10.235186 11.321997 10.122339 11.810993 9.190236 8.904943 9.306371 9.847394 8.330131 8.340352 10.152785 10.123532 10.408540 10.821986 9.003615 10.039206 9.534872 10.096991 9.498181 10.825446 9.875271 9.233426 10.362276 9.376892 10.191204 11.250851
数据集处理代码实现以下
'''加载数据集''' 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] #注意这里和python2的区别,须要在map函数外加一个list(),不然显示结果为 map at 0x3fed1d0 return mat(datArr)
在等式 Av=λv 中,v 是特征向量, λ 是特征值。表示 若是特征向量 v 被某个矩阵 A 左乘,那么它就等于某个标量 λ 乘以 v.
幸运的是: Numpy 中有寻找特征向量和特征值的模块 linalg,它有 eig() 方法,该方法用于求解特征向量和特征值。具体代码实现以下:
'''pca算法 cov协方差=[(x1-x均值)*(y1-y均值)+(x2-x均值)*(y2-y均值)+...+(xn-x均值)*(yn-y均值)]/(n-1) Args: dataMat 原数据集矩阵 topNfeat 应用的N个特征 Returns: lowDDataMat 降维后数据集 reconMat 新的数据集空间 ''' def pca(dataMat, topNfeat=9999999): # 计算每一列的均值 meanVals = mean(dataMat, axis=0) # print('meanVals', meanVals) # 每一个向量同时都减去均值 meanRemoved = dataMat - meanVals # print('meanRemoved=', meanRemoved) # rowvar=0,传入的数据一行表明一个样本,若非0,传入的数据一列表明一个样本 covMat = cov(meanRemoved, rowvar=0) # eigVals为特征值, eigVects为特征向量 eigVals, eigVects = linalg.eig(mat(covMat)) # print('eigVals=', eigVals) # print('eigVects=', eigVects) # 对特征值,进行从小到大的排序,返回从小到大的index序号 # 特征值的逆序就能够获得topNfeat个最大的特征向量 eigValInd = argsort(eigVals) # print('eigValInd1=', eigValInd) # -1表示倒序,返回topN的特征值[-1到-(topNfeat+1)不包括-(topNfeat+1)] eigValInd = eigValInd[:-(topNfeat+1):-1] # print('eigValInd2=', eigValInd) # 重组 eigVects 最大到最小 redEigVects = eigVects[:, eigValInd] # print('redEigVects=', redEigVects.T) # 将数据转换到新空间 # print( "---", shape(meanRemoved), shape(redEigVects)) lowDDataMat = meanRemoved * redEigVects reconMat = (lowDDataMat * redEigVects.T) + meanVals # print('lowDDataMat=', lowDDataMat) # print('reconMat=', reconMat) return lowDDataMat, reconMat
接下来咱们查看降维后的数据与原始数据可视化效果,咱们将原始数据采用绿色△表示,降维后的数据采用红色○表示。可视化代码以下:
'''降维后的数据和原始数据可视化''' def show_picture(dataMat, reconMat): fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(dataMat[:, 0].flatten().A[0], dataMat[:, 1].flatten().A[0], marker='^', s=90,c='green') ax.scatter(reconMat[:, 0].flatten().A[0], reconMat[:, 1].flatten().A[0], marker='o', s=50, c='red') plt.show()
调用代码:
# 2 主成分分析降维特征向量设置 lowDmat, reconMat = pca(dataMat, 1) print(shape(lowDmat)) # 3 将降维后的数据和原始数据一块儿可视化 show_picture(dataMat, reconMat)
运行结果显示:
半导体是在一些极为先进的工厂中制造出来的。设备的生命早期有限,而且花费极其巨大。虽然经过早期测试和频繁测试来发现有瑕疵的产品,但仍有一些存在瑕疵的产品经过测试。若是咱们经过机器学习技术用于发现瑕疵产品,那么它就会为制造商节省大量的资金。具体来说,它拥有590个特征。咱们看看可否对这些特征进行降维处理。对于数据的缺失值的问题,将缺失值NaN(Not a Number缩写),所有用平均值来替代(若是用0来处理的策略就太差了)。收集数据:提供文本文件,文件名:secom.data.文本文件部分数据格式以下:
3030.93 2564 2187.7333 1411.1265 1.3602 100 97.6133 0.1242 1.5005 0.0162 -0.0034 0.9455 202.4396 0 7.9558 414.871 10.0433 0.968 192.3963 12.519 1.4026 -5419 2916.5 -4043.75 751 0.8955 1.773 3.049 64.2333 2.0222 0.1632 3.5191 83.3971 9.5126 50.617 64.2588 49.383 66.3141 86.9555 117.5132 61.29 4.515 70 352.7173 10.1841 130.3691 723.3092 1.3072 141.2282 1 624.3145 218.3174 0 4.592
将数据集中NaN替换成平均值,代码实现以下:
'''将NaN替换成平均值函数''' def replaceNanWithMean(): datMat = loadDataSet('./secom.data', ' ') numFeat = shape(datMat)[1] for i in range(numFeat): # 对value不为NaN的求均值 # .A 返回矩阵基于的数组 meanVal = mean(datMat[nonzero(~isnan(datMat[:, i].A))[0], i]) # 将value为NaN的值赋值为均值 datMat[nonzero(isnan(datMat[:, i].A))[0],i] = meanVal return datMat
咱们拿到数据进行数据预处理以后,再跑下程序,看看中间结果若是,分析数据代码以下:
'''分析数据''' def analyse_data(dataMat): meanVals = mean(dataMat, axis=0) meanRemoved = dataMat-meanVals covMat = cov(meanRemoved, rowvar=0) eigvals, eigVects = linalg.eig(mat(covMat)) eigValInd = argsort(eigvals) topNfeat = 20 eigValInd = eigValInd[:-(topNfeat+1):-1] cov_all_score = float(sum(eigvals)) sum_cov_score = 0 for i in range(0, len(eigValInd)): line_cov_score = float(eigvals[eigValInd[i]]) sum_cov_score += line_cov_score ''' 咱们发现其中有超过20%的特征值都是0。 这就意味着这些特征都是其余特征的副本,也就是说,它们能够经过其余特征来表示,而自己并无提供额外的信息。 最前面15个值的数量级大于10^5,实际上那之后的值都变得很是小。 这就至关于告诉咱们只有部分重要特征,重要特征的数目也很快就会降低。 最后,咱们可能会注意到有一些小的负值,他们主要源自数值偏差应该四舍五入成0. ''' print('主成分:%s, 方差占比:%s%%, 累积方差占比:%s%%' % (format(i+1, '2.0f'), format(line_cov_score/cov_all_score*100, '4.2f'), format(sum_cov_score/cov_all_score*100, '4.1f')))
去均值化的特征值结果显示以下:
[ 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 ... 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 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 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 ]
数据分析结果以下:
主成分: 1, 方差占比:59.25%, 累积方差占比:59.3% 主成分: 2, 方差占比:24.12%, 累积方差占比:83.4% 主成分: 3, 方差占比:9.15%, 累积方差占比:92.5% 主成分: 4, 方差占比:2.30%, 累积方差占比:94.8% 主成分: 5, 方差占比:1.46%, 累积方差占比:96.3% 主成分: 6, 方差占比:0.52%, 累积方差占比:96.8% 主成分: 7, 方差占比:0.32%, 累积方差占比:97.1% 主成分: 8, 方差占比:0.31%, 累积方差占比:97.4% 主成分: 9, 方差占比:0.26%, 累积方差占比:97.7% 主成分:10, 方差占比:0.23%, 累积方差占比:97.9% 主成分:11, 方差占比:0.22%, 累积方差占比:98.2% 主成分:12, 方差占比:0.21%, 累积方差占比:98.4% 主成分:13, 方差占比:0.17%, 累积方差占比:98.5% 主成分:14, 方差占比:0.13%, 累积方差占比:98.7% 主成分:15, 方差占比:0.12%, 累积方差占比:98.8% 主成分:16, 方差占比:0.11%, 累积方差占比:98.9% 主成分:17, 方差占比:0.11%, 累积方差占比:99.0% 主成分:18, 方差占比:0.09%, 累积方差占比:99.1% 主成分:19, 方差占比:0.09%, 累积方差占比:99.2% 主成分:20, 方差占比:0.09%, 累积方差占比:99.3%
咱们发现其中有超过20%的特征值都是0。这就意味着这些特征都是其余特征的副本,也就是说,它们能够经过其余特征来表示,而自己并无提供额外的信息。最前面值的数量级大于10^5,实际上那之后的值都变得很是小。这就至关于告诉咱们只有部分重要特征,重要特征的数目也很快就会降低。最后,咱们可能会注意到有一些小的负值,他们主要源自数值偏差应该四舍五入成0.
根据实验结果咱们绘制半导体数据中前七个主要成分所占的方差百分好比下
主成分 | 方差百分比(%) | 累积方差百分比(%) |
---|---|---|
1 | 59.25 | 59.3 |
2 | 24.12 | 83.4 |
3 | 9.15 | 92.5 |
4 | 2.30 | 94.8 |
5 | 1.46 | 96.3 |
6 | 0.52 | 96.8 |
7 | 0.32 | 97.1 |
20 | 0.09 | 99.3 |
调用咱们上文写的代码以下:
lowDmat, reconMat = pca(dataMat, 20) print(shape(lowDmat)) show_picture(dataMat, reconMat)
运行结果以下:
本文版权归做者全部,旨在技术交流使用。未经做者赞成禁止转载,转载后需在文章页面明显位置给出原文链接,不然相关责任自行承担。