在體育比賽中,人們面對的原本是百萬像素的數(shù)據(jù),但是只有球的三維位置才最重要,這就成為降維(dimensionlity reduction)。通常而言,我們在應用其他機器學習算法之前,必須先識別出其相關(guān)特征。
第一種降維稱作主方法分析(Principal Component Analysis,PCA)。
第一個坐標值選擇的是原始數(shù)據(jù)中方差最大的方向,第二個新坐標軸的選擇和第一個坐標軸正交且具有最大方差的方向。
另一種降維技術(shù)是因子分析(Factor Analysis)。在因子分析中,我們假設在觀察數(shù)據(jù)的生成一些觀察不到的隱變量(latent variable)。假設觀察數(shù)據(jù)是這些隱變量和某些噪聲的線性組合。那么隱變量的數(shù)據(jù)可能比觀察數(shù)據(jù)的數(shù)目少,也就是說通過隱變量可以實現(xiàn)數(shù)據(jù)的降維。
還有一種方法就是獨立成分分析(Independent Component Analysis,ICA)。ICA假設數(shù)據(jù)是從N個數(shù)據(jù)源生成的,這一點和因子分析有些類似。假設數(shù)據(jù)為多個數(shù)據(jù)源的混合觀察結(jié)果,這些數(shù)據(jù)源之間在統(tǒng)計上是互相獨立的,而在PCA中之假設數(shù)據(jù)是不相關(guān)的。
在上述3種降維技術(shù)中,PCA的應用目前最為廣泛,因此本章主要關(guān)注PCA。在下一節(jié)中,我們將會對PCA進行介紹,然后再通過一段Python代碼來運行PCA。
主成分分析
優(yōu)點:降低數(shù)據(jù)的復雜性,識別最重要的多個特征
缺點:不一定需要,且可能損失有用信息
適用數(shù)據(jù)類型:數(shù)據(jù)型數(shù)據(jù)
將數(shù)據(jù)轉(zhuǎn)化成前N個主成分的偽代碼大致如下:
去除平均值
計算協(xié)方差矩陣
計算協(xié)方差矩陣的特征值和特征向量
將特征值從打到小排序
保留最上面的N個特征向量
將數(shù)據(jù)轉(zhuǎn)換到上述N個特征向量構(gòu)建的新空間中
下面開始構(gòu)建PCA算法
from numpy import *
def loadDataSet(fileName, delim='\t'):
fr = open(fileName)
stringArr = [line.strip().split(delim) for line in fr.readlines()]
datArr = [map(float,line) for line in stringArr]
return mat(datArr)
def pca(dataMat, topNfeat=9999999):#數(shù)據(jù)集,返回特征數(shù)
meanVals = mean(dataMat, axis=0)
meanRemoved = dataMat - meanVals #去平均值
covMat = cov(meanRemoved, rowvar=0) #協(xié)方差
eigVals,eigVects = linalg.eig(mat(covMat))#特征值,特征矩陣
eigValInd = argsort(eigVals) #從小到大排序
eigValInd = eigValInd[:-(topNfeat+1):-1] #逆序,從大到小
redEigVects = eigVects[:,eigValInd]
lowDDataMat = meanRemoved * redEigVects#將數(shù)據(jù)轉(zhuǎn)化到新的維度空間
reconMat = (lowDDataMat * redEigVects.T) + meanVals
return lowDDataMat, reconMat
我們在testSet.txt文件中加入一個由1000個數(shù)據(jù)點組成的數(shù)據(jù)集,開始進行PCA操作:
In [17]: import pca
...: dataMat = pca.loadDataSet('testSet.txt')
...: lowDMat, reconMat = pca.pca(dataMat, 1)
...: shape(lowDMat)
...:
Out[17]: (1000L, 1L)
lowDMat,包含了降維之后的矩陣。我們可以通過如下命令將降維后的數(shù)據(jù)和原始數(shù)據(jù)一起繪制出來:
import matplotlib
import matplotlib.pyplot as plt
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調(diào)用,也會看到類似結(jié)果:
lowDMat, reconMat = pca.pca(dataMat, 2)
下面我們先處理一些異常值,用平均值代替NaN:
def replaceNanWithMean():
datMat = loadDataSet('secom.data', ' ')
numFeat = shape(datMat)[1]
for i in range(numFeat):
meanVal = mean(datMat[nonzero(~isnan(datMat[:,i].A))[0],i])
datMat[nonzero(isnan(datMat[:,i].A))[0],i] = meanVal #將所有nan置為平均值
return datMat
PCA會給出數(shù)據(jù)中所包含的信息量。數(shù)據(jù)(data)和信息(information)之間具有巨大的差別。數(shù)據(jù)指的是接受的原始材料,其中包含噪聲和不相關(guān)的信息。信息指數(shù)據(jù)中的相關(guān)部分。下面開始操作,首先將所有的NaN值替換為平均值:
In [36]: import pca
...: dataMat = pca.replaceNanWithMean()
...:
接下來從pca()函數(shù)中借用一些代碼來達到我們的目的,之所以借用是因為我們想了解中間結(jié)果而非最后輸出結(jié)果,調(diào)用如下語句去除均值:
In [37]: meanVals = mean(dataMat, axis = 0)
...: meanRemoved = dataMat - meanVals
...:
然后計算協(xié)方差矩陣:
In [38]: covMat = cov(meanRemoved, rowvar=0)
最后對該矩陣進行特征值分析:
In [39]: eigVals,eigVects = linalg.eig(mat(covMat))
...:
...: eigVals
...:
Out[39]:
array([ 53415197.85687517+0.j, 21746671.90465918+0.j,
8248376.61529074+0.j, ..., 0.00000000+0.j,
0.00000000+0.j, 0.00000000+0.j])
我們會發(fā)現(xiàn)超過20%的特征值都是0。這意味著這些特征都是其他特征的副本,也就是說,他們可以通過其他特征來表示,而本身并沒有提供額外的信息。
接下來,我們了解一下部分數(shù)值的數(shù)量級。最前面15個值的數(shù)量級大于10的五次方,實際上那以后的值都變得非常小。這相當于告訴我們部分重要特征,重要特征的數(shù)目也很快就會下降。
最后,我們注意到一些負值,他們主要源于數(shù)值誤差,應該四舍五入為0。