numpy實現鳶尾花數據集PCA降維

PCA降維過程

在前面的一篇博客中我已經從數學角度解釋了PCA降維的原理,我們從中也可以得到PCA降維的過程
1)將原始數據做轉置運算,每一行代表一個維度
2)每一行(代表一個屬性字段)進行零均值化,即減去這一行的均值
3)得到原始數據的協方差矩陣
4)求出協方差矩陣的特徵值及對應的特徵向量的單位向量
5)將特徵向量按對應特徵值大小從上到下按行排列成矩陣,取前k行組成矩陣P
6)用上面得到矩陣P和標準化後數據相乘,即可得到降維到k維後的數據

數據集

數據集我們使用sklearn庫中的iris數據集,數據集中的每個樣本有4個特徵參數

原始數據格式:
5.1, 3.5, 1.4, 0.2
4.9, 3.0, 1.4, 0.2
4.7, 3.2, 1.3, 0.2
4.6, 3.1, 1.5, 0.2
5.0, 3.6, 1.4, 0.2
5.4, 3.9, 1.7, 0.4
4.6, 3.4, 1.4, 0.3
5.0, 3.4, 1.5, 0.2
4.4, 2.9, 1.4, 0.2
4.9, 3.1, 1.5, 0.1

numpy實現PCA降維

加載數據集

數據我們使用sklearn庫中的iris中的數據集,所以需要導入sklearn庫

   data = datasets.load_iris()["data"]
   print(data)

我們看一下加載進來的數據

[[ 5.1  3.5  1.4  0.2]
 [ 4.9  3.   1.4  0.2]
 [ 4.7  3.2  1.3  0.2]
 [ 4.6  3.1  1.5  0.2]
 [ 5.   3.6  1.4  0.2]
 [ 5.4  3.9  1.7  0.4]
 ...
  [ 6.7  3.3  5.7  2.5]
 [ 6.7  3.   5.2  2.3]
 [ 6.3  2.5  5.   1.9]
 [ 6.5  3.   5.2  2. ]
 [ 6.2  3.4  5.4  2.3]
 [ 5.9  3.   5.1  1.8]]

數據標準化

原始數據每一列是同一個維度特徵,在標準化時候我們需要的也是對維度進行數據標準化處理,所以需要按列取數據,另外因爲後面我們需要計算協方差,所以對數據進行標準化方式是去均值

 #axis = 0,按列取值求均值
 mean_vector=np.mean(data,axis=0)
 print("均值向量爲:%s\n標準化數據:%s"% (mean_vector,data - mean_vector))

標準化後的數據爲:

均值向量爲:[ 5.84333333  3.054       3.75866667  1.19866667] 
標準化數據:
[[ -7.43333333e-01   4.46000000e-01  -2.35866667e+00  -9.98666667e-01]
 [ -9.43333333e-01  -5.40000000e-02  -2.35866667e+00  -9.98666667e-01]
 [ -1.14333333e+00   1.46000000e-01  -2.45866667e+00  -9.98666667e-01]
 [ -1.24333333e+00   4.60000000e-02  -2.25866667e+00  -9.98666667e-01]
 [ -8.43333333e-01   5.46000000e-01  -2.35866667e+00  -9.98666667e-01]
 ......
  [  4.56666667e-01  -5.54000000e-01   1.24133333e+00   7.01333333e-01]
 [  6.56666667e-01  -5.40000000e-02   1.44133333e+00   8.01333333e-01]
 [  3.56666667e-01   3.46000000e-01   1.64133333e+00   1.10133333e+00]
 [  5.66666667e-02  -5.40000000e-02   1.34133333e+00   6.01333333e-01]]

協方差矩陣

協方差計算的是不同維度之間的協方差,不是不同樣本的,首先我們要確定我們數據維度是按行還是列,我們的數據是一列是一個維度,並且要對標準化後的數據進行求協方差,這裏我們使用numpy中的 cov() 函數

 # rowvar=0表示數據的每一列代表一個維度
 cov_mat =np.cov(standData,rowvar=0)
print("協方差矩陣:\n%s"%cov_mat)

原始數據中一共四個維度,所以我們得到的協方差矩陣是一個4*4的矩陣

協方差矩陣:
[[ 0.68569351 -0.03926846  1.27368233  0.5169038 ]
[-0.03926846  0.18800403 -0.32171275 -0.11798121]
[ 1.27368233 -0.32171275  3.11317942  1.29638747]
[ 0.5169038  -0.11798121  1.29638747  0.58241432]]

協方差矩陣的特徵值和對應的特徵向量的計算

在另一篇博客中也說了協方差矩陣是實對稱方陣,所以是一定可以做矩陣分解的
在numpy的庫函數中,linalg.eig()可以用來計算計算特徵值和對應的特徵向量,輸入參數是一個方陣,得到兩個值:w,v
w:特徵值。每個特徵值根據它的多重性重複。這個數組將是複雜類型,除非虛數部分爲0。當傳進的參數a是實數時,得到的特徵值是實數
v:特徵向量。每個特徵值對應的特徵向量,相同特徵值可能對應的特徵向量不同

fvalue,fvector = np.linalg.eig(covMat)
print("特徵值爲:%s\n特徵向量爲:\n%s" %(fvalue,fvector))

可以得到四個特徵值,和四個特徵向量的單位向量

特徵值爲:[ 4.22484077  0.24224357  0.07852391  0.02368303]
特徵向量爲:
[[ 0.36158968 -0.65653988 -0.58099728  0.31725455]
[-0.08226889 -0.72971237  0.59641809 -0.32409435]
[ 0.85657211  0.1757674   0.07252408 -0.47971899]
[ 0.35884393  0.07470647  0.54906091  0.75112056]]

向量矩陣

前面我們已經得到協方差矩陣的特徵值和特徵向量,接下來需要按照特徵值的大小

#argsort函數返回的是數組值排序的索引值
#將特徵值按從大到小順序排列的索引值
fvaluesort=np.argsort(-fvalue)
print(fvaluesort)
#特徵值[ 4.22484077  0.24224357  0.07852391  0.02368303]
#output :[0 1 2 3]

argsort函數返回的是排序的索引值,默認是從小到大
eg:
x = np.array([3, 1, 2])
np.argsort(x)
out: array([1, 2, 0])
其他用法可以參考np.argsort的用法
我們假設現在要將數據降到二維,那麼就取最大的兩個特徵值對應的特徵向量

#先獲取特徵值較大的兩個索引以便於下一步得到對應的特徵向量
fValueTopN = fValueSort[:2 ]
#得到較大的特徵值對應的特徵向量
newdata = fVector[:,fValueTopN]
print(newdata)
#output:
[[ 0.36158968 -0.65653988]
 [-0.08226889 -0.72971237]
 [ 0.85657211  0.1757674 ]
 [ 0.35884393  0.07470647]]

我們和上面得到的特徵向量和特徵值做個比較,確定拿到的是較大的兩個特徵值對應的特徵向量

降維後數據

拿上面得到的前2個較大特徵值對應的特徵向量和標準化後數據做內積運算,便可得到降維後的數據

newdata = np.dot(data,vectorMatrix)
print(newdata)
#output
降維後的數據:
[[-2.68412563 -0.31939725]
[-2.71414169  0.17700123]
[-2.88899057  0.14494943]
[-2.74534286  0.31829898]
[-2.72871654 -0.32675451]
.......
[ 1.52716661  0.37531698]
[ 1.76434572 -0.07885885]
[ 1.90094161 -0.11662796]
[ 1.39018886  0.28266094]]

至此,我們的降維過程已經完成,我們再試着恢復數據,看看和原始數據有沒有很大的差別

print("最終重構結果爲:\n{}".format(np.mat(newdata) * fvectormat.T + mean_vector))
#output
復原的數據:
[[5.08303897 3.51741393 1.40321372 0.21353169]
[4.7462619  3.15749994 1.46356177 0.24024592]
[4.70411871 3.1956816  1.30821697 0.17518015]
[4.6422117  3.05696697 1.46132981 0.23973218]
[5.07175511 3.52655486 1.36373845 0.19699991]
[5.50581049 3.79140823 1.67552816 0.32616959] 
......
  [6.95201347 3.04358556 5.90548444 2.09665999]
[6.91756285 3.07544671 5.77722508 2.04293748]
[6.66904015 3.02994114 5.39094874 1.88173174]
[6.14880195 2.65421139 5.13134845 1.77482994]
[6.53272206 2.96578609 5.25579114 1.825527  ]
[6.60688475 2.98181821 5.3662607  1.87161698]
[6.16013695 2.73344296 4.99793961 1.71875852]] 
原始數據;
[[ 5.1  3.5  1.4  0.2]
[ 4.9  3.   1.4  0.2]
[ 4.7  3.2  1.3  0.2]
[ 4.6  3.1  1.5  0.2]
[ 5.   3.6  1.4  0.2]
[ 5.4  3.9  1.7  0.4]
...
 [ 6.7  3.3  5.7  2.5]
[ 6.7  3.   5.2  2.3]
[ 6.3  2.5  5.   1.9]
[ 6.5  3.   5.2  2. ]
[ 6.2  3.4  5.4  2.3]
[ 5.9  3.   5.1  1.8]] 

和原始數據做個比較就會發現和原始數據差別很小,因爲做降維相對來說會損失信息量,所以很難做到完整還原原始數據

完整代碼

from sklearn import datasets
import numpy as np

class PCAtest():
   def __init__(self,k):
       #降到k維
       self.k = k
       
   # 加載鳶尾花數據集中的特徵作爲PCA的原始數據集 
   def loadIris(self):
       data = datasets.load_iris()["data"]
       return data
   #數據標準化(去均值)
   def stand_data(self,data):
       #axis = 0,按列取值求均值
       mean_vector = np.mean(data,axis=0)
       return mean_vector,data -mean_vector

   # 計算協方差矩陣
   def getCovMat(self,standData):
       # rowvar=0表示數據的每一列代表一個維度
       return np.cov(standData,rowvar=0)

   # 計算協方差矩陣的特徵值和特徵向量
   def getFValueAndFVector(self,covMat):
       fValue,fVector = np.linalg.eig(covMat)
       return fValue,fVector

   # 得到特徵向量矩陣
   def getVectorMatrix(self,fValue,fVector):
       #從大到小排序,並返回排序後的原索引值
       fValueSort = np.argsort(-fValue)
       #print(fValueSort)
       fValueTopN = fValueSort[:self.k]
       #print(fValueTopN)
       return fVector[:,fValueTopN]

   # 得到降維後的數據
   def getResult(self,data,vectorMat):
       return np.dot(data,vectorMat)


if __name__=="__main__":
   pca = PCAtest(2)
   data = pca.loadIris()
   print("原始數據:\n%s"%data)
   (mean_vector ,standdata)= pca.stand_data(data)
   print("均值向量爲:%s \n標準化數據:\n%s" % (mean_vector, standdata))
   cov_mat = pca.getCovMat(standdata)
   print("協方差矩陣:\n%s"%cov_mat)
   fvalue,fvector = pca.getFValueAndFVector(cov_mat)
   print("特徵值爲:%s\n特徵向量爲:\n%s" %(fvalue,fvector))
   fvectormat = pca.getVectorMatrix(fvalue,fvector)
   print("最終需要的特徵向量:\n%s"%fvector)
   newdata = pca.getResult(standdata,fvectormat)
   print("降維後的數據:\n%s"%newdata)
   print("最終重構結果爲:\n{}".format(np.mat(newdata) * fvectormat .T + mean_vector))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章