主成分分析(PCA)
我们希望将N维数据降低为K维数据,对数据简化有如下一系列原因:
1 使得数据集更易使用使用
2 降低很多算法的计算开销
3 去除噪声
4 使得结果易懂
在所有的降维技术中,PCA的应用目前最为广泛,在PCA中,数据从原来的坐标系转换到了新的坐标系,新坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二个新坐标轴的选择和第一个坐标轴正交且具有最大方差的方向。
1移动坐标轴
第一条坐标轴的选择要求覆盖到数据最大方差的地方,数据的最大方差给出了数据最重要的信息。我们继续选择一条覆盖数据方差次大,并且和第一条坐标轴正交的向量轴作为第二条坐标轴,利用PCA我们将数据坐标轴旋转到数据角度上的那些最重要的方向。
这里在书上看到了非常重要的一句话,也是移动坐标轴的原理:正交向量(互相正交的单位向量)构成的任意矩阵都代表欧式空间下的一个坐标旋转。
为了求出新的坐标轴,我们需要构建协方差矩阵(协方差矩阵反应出了各个特征之间的相关性)。百度百科上对于协方差矩阵的定义如下:
吴恩达的视频中用一行公式来表达了数据的协方差矩阵
一个移动坐标轴的示例
下图表示原始空间中的四个点
2 降维
在移动坐标轴之后的空间下,第一个坐标轴(对应最大特征值的那个轴)最重要,形式化的说法是沿着该轴点的方差最大。而次特征对应的第二个坐标轴,在相同意义上的说是第二重要的,其余可以依此类推。如果想将原始数据变换到一个低维的空间,那么保留最重要部分的做法是使用最大的前k个特征对应的k个向量并忽略其他特征值。
左图表示先将数据旋转到新的坐标系下,右图就表示使用最重要的一个特征值对应的特征向量,将二维数据降到了一维。
3 选取主成分的数量
我们要将N维数据降到K维,那么如何选择K的值呢?在吴恩达的视频中,他给了我们一个公式,这里分母表示每个样本的平均长度,分子是原始数据与投影之后的数据的距离的平均值。然后将k值代入,直到不等式成立。这里也可以取5%等其它值。
4 在NumPy中应用PCA
"""
Created on Wed Nov 7 10:17:22 2018
@author: wangguangbo
pca.py
"""
from numpy import *
def loadDataSet(fileName,delim='\t'):
fr=open(fileName)
#两个list comprehension 生成数据矩阵
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):
#求每一个特征的平均值
meanVals=mean(dataMat,axis=0)
#减去平均值
meanRemoved=dataMat-meanVals
#计算协方差矩阵
covMat=cov(meanRemoved,rowvar=0)
#求出特征值和特征向量
eigVals,eigVects=linalg.eig(mat(covMat))
#将特征值从小到大排序,返回索引
eigValInd=argsort(eigVals)
#将索引逆序,这就是从大到小的特征值的索引
eigValInd=eigValInd[:-(topNfeat+1):-1]
#根据索引得到相关特征值的特征向量
redEigVects=eigVects[:,eigValInd]
#相乘得到低纬度矩阵
lowDDataMat=meanRemoved*redEigVects
#将降维之后的数据,再转回原坐标系
reconMat=(lowDDataMat*redEigVects.T)+meanVals
return lowDDataMat,reconMat,redEigVects
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 9 09:37:08 2018
@author: wangguangbo
testPCA.py
"""
from numpy import *
import pca
import matplotlib.pyplot as plt
dataMat=pca.loadDataSet('testSet.txt')
lowDMat,reconMat,redEigVects=pca.pca(dataMat,1)
m=shape(lowDMat)
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()