sklearn中的降維算法

轉載自:【菜菜的sklearn】04 降維算法

1 概述

1.1 維度

針對每一張表,維度指的是樣本的數量或特徵的數量,一般無特別說明,指的都是特徵的數量。除了索引之外,一個特徵是一維,兩個特徵是二維,n個特徵是n維。

降維算法中的”降維“,指的是降低特徵矩陣中特徵的數量

降維的目的:
讓算法運算更快,效果更好,
數據可視化

1.2 sklearn中的降維算法——decomposition

2 PCA與SVD

在降維的過程中,即減少特徵的數量,又保留大部分有效信息

將那些帶有重複信息的特徵合併,並刪除那些帶無效信息的特徵

逐漸創造出能夠代表原特徵矩陣大部分信息的,特徵更少的,新特徵矩陣。

在降維中,PCA使用的信息量衡量指標,就是樣本方差,又稱可解釋性方差,方差越大,特徵所帶的信息量越多。

主成分分析PCA是一種通過降維來簡化數據結構的方法,即把原有的多個指標轉化成少數幾個代表性較好的綜合指標,這少數幾個指標能夠反映原來指標的大部分信息(80%以上),並且各個指標之間保持獨立,避免出現重疊信息。

2.1 降維究竟是怎樣實現?


在這裏插入圖片描述
F1 方差最大,爲第一主成分
F2即第二個線性組合。F2稱爲第二主成分

新指標能夠反映原來指標的大部分信息(80%以上)即可


PCA和SVD是兩種不同的降維算法,兩種算法中矩陣分解的方法不同,信息量的衡量指標不同

PCA使用方差作爲信息量的衡量指標,並且特徵值分解來找出空間V。PCA找到的每個新特徵向量就叫做“主成分”,而被丟棄的特徵向量被認爲信息量很少,這些信息很可能就是噪音。

SVD使用**奇異值**分解來找出空間V,其中Σ也是一個對角矩陣,不過它對角線上的元素是奇異值,這也是SVD中用來衡量特徵上的信息量的指標。

無論是PCA和SVD 都需要遍歷所有的特徵和樣本來計算信息量指標。並且在矩陣分解的過程之中,會產生比原來的特徵矩陣更大的矩陣,

因此,降維算法的計算量很大,運行比較緩慢,但無論如何,它們的功能無可替代


PCA和特徵選擇技術都是特徵工程的一部分,它們有什麼不同?

特徵工程中有三種方式:特徵提取特徵創造特徵選擇

特徵選擇是從已存在的特徵中選取攜帶信息最多的,選完之後的特徵依然具有可解釋性,我們依然知道這個特徵在原數據的哪個位置,代表着原數據上的什麼含義。

而PCA,是將已存在的特徵進行壓縮,降維完畢後的特徵不是原本的特徵矩陣中的任何一個特徵,而是通過某些方式組合起來的新特徵。

PCA建立的新特徵向量不具有可讀性

新特徵雖然帶有原始數據的信息,卻已經不是原數據上代表着的含義了。以PCA爲代表的降維算法因此是特徵創造的一種

可以想見,PCA一般不適用於探索特徵和標籤之間的關係的模型(如線性迴歸),因爲無法解釋的新特徵和標籤之間的關係不具有意義。在線性迴歸模型中,我們使用特徵選擇。

2.2 重要參數

sklearn.decomposition.PCA (
n_components=None, 
copy=True, 
whiten=False, 
svd_solver=’auto’, 
tol=0.0,
iterated_power=’auto’,
random_state=None)

n_components

n_components即降維後需要保留的特徵數量,如果留下的特徵太多,就達不到降維的效果,如果留下的特徵太少,那新特徵向量可能無法容納原始數據集中的大部分信息,因
此,n_components既不能太大也不能太小。

2.2.1 案例

導包

import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA

提取數據集

iris = load_iris()
y = iris.target  # 標籤
X = iris.data # 特徵矩陣

四個特徵,三種類別

import pandas as pd
pd.DataFrame(X)

'''
       0    1    2    3
0    5.1  3.5  1.4  0.2
1    4.9  3.0  1.4  0.2
2    4.7  3.2  1.3  0.2
3    4.6  3.1  1.5  0.2
4    5.0  3.6  1.4  0.2
..   ...  ...  ...  ...
145  6.7  3.0  5.2  2.3
146  6.3  2.5  5.0  1.9
147  6.5  3.0  5.2  2.0
148  6.2  3.4  5.4  2.3
149  5.9  3.0  5.1  1.8

[150 rows x 4 columns]
'''

iris.target_names
# array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

調用PCA——降維

# 兩個特徵
pca = PCA(n_components=2) #實例化
pca = pca.fit(X) #擬合模型
X_dr = pca.transform(X) #獲取新矩陣
X_dr
'''
array([[-2.68412563,  0.31939725],
       [-2.71414169, -0.17700123],
       ....
       [ 1.90094161,  0.11662796],
       [ 1.39018886, -0.28266094]])
'''
# 由四個特徵變爲兩個個特徵

可視化

X_dr[y == 0, 0] # 標籤爲0的第一個特徵
X_dr[y == 0, 1] # 標籤爲0的第二個特徵

X_dr[y == 1, 0] # 標籤爲1的第一個特徵
X_dr[y == 2, 0] # 標籤爲2的第一個特徵

...


plt.figure()
plt.scatter(X_dr[y==0, 0], X_dr[y==0, 1], c="red", label=iris.target_names[0])
plt.scatter(X_dr[y==1, 0], X_dr[y==1, 1], c="black", label=iris.target_names[1])
plt.scatter(X_dr[y==2, 0], X_dr[y==2, 1], c="orange", label=iris.target_names[2])
plt.legend()
plt.title('PCA of IRIS dataset')
plt.show()

在這裏插入圖片描述
屬性explained_variance_,查看降維後每個新特徵向量上所帶的信息量大小(可解釋性方差的大小

pca.explained_variance_
# array([4.22824171, 0.24267075]) 第一主成分(第一個新特徵)具有大部分的信息

屬性explained_variance_ratio,查看降維後每個新特徵向量所佔的信息量佔原始數據總信息量的百分比

一般來說,新特徵能夠反映原來特徵的大部分信息(80%以上)即可

pca.explained_variance_ratio_
# array([0.92461872, 0.05306648])

# 大部分信息都被有效地集中在了第一個特徵上
pca.explained_variance_ratio_.sum()
# 0.977685206318795

97.77% > 80%

選擇最好的n_components(特徵個數):累積可解釋方差貢獻率曲線

當參數n_components中不填寫任何值,則默認返回min(X.shape)個特徵,一般來說,樣本量都會大於特徵數目,所以什麼都不填就相當於轉換了新特徵空間,但沒有減少特徵的個數。一般來說,不會使用這種輸入方式。但我們卻可以使用這種輸入方式來畫出累計可解釋方差貢獻率曲線,以此選擇最好的n_components的整數取值。

累積可解釋方差貢獻率曲線是一條以降維後保留的特徵個數爲橫座標,降維後新特徵矩陣捕捉到的可解釋方差貢獻率爲縱座標的曲線,

累積可解釋方差貢獻率曲線 即選 n_components =1 時,能反映原始信息多少;n_components =2 時,能反映原始信息多少;…;n_components =k 時,能反映原始信息多少

能夠幫助我們決定n_components最好的取值
在這裏插入圖片描述

2.2.2 用最大似然估計自選 n_components (新特徵個數)

pca_mle = PCA(n_components="mle")
pca_mle = pca_mle.fit(X)
X_mle = pca_mle.transform(X)
X_mle
'''
array([[-2.68412563,  0.31939725, -0.02791483],
       [-2.71414169, -0.17700123, -0.21046427],
'''
#可以發現,mle爲我們自動選擇了3個特徵
pca_mle.explained_variance_ratio_.sum() # 0.9947878161267247

2.2.3 按信息量佔比選 n_components (新特徵個數)

輸入[0,1]之間的浮點數,並且讓參數svd_solver =='full',表示希望降維後的總解釋性方差佔比大於n_components 指定的百分比,即是說,希望保留百分之多少的信息量。

pca_f = PCA(n_components=0.97,svd_solver="full")
pca_f = pca_f.fit(X)
X_f = pca_f.transform(X)
'''
array([[-2.68412563,  0.31939725],
       [-2.71414169, -0.17700123],
'''
pca_f.explained_variance_ratio_ # array([0.92461872, 0.05306648])

pca_f.explained_variance_ratio_.sum() #  0.977685206318795

2.3 PCA中的SVD

2.3.1 PCA中的SVD哪裏來?

svd_solver是奇異值分解器的意思

SVD有一種驚人的數學性質,即是它可以跳過數學神祕的宇宙,不計算協方差矩陣,直接找出一個新特徵向量組成的n維空間,

通過SVD和PCA的合作,sklearn實現了一種計算更快更簡單,但效果卻很好的“合作降維“。很多人理解SVD,是把SVD當作PCA的一種求解方法,其實指的就是在矩陣分解時不使用PCA本身的特徵值分解,而使用奇異值分解來減少計算量。

在transform過程之後,fit中奇異值分解的結果除了V(k,n)以外,就會被捨棄,而V(k,n)會被保存在屬性components_ 當中,可以調用查看。

PCA(2).fit(X).components_
'''
array([[ 0.36138659, -0.08452251,  0.85667061,  0.3582892 ],
       [ 0.65658877,  0.73016143, -0.17337266, -0.07548102]])
'''

PCA(2).fit(X).components_.shape  # (2, 4)

2.3.2 重要參數svd_solver 與 random_state

參數svd_solver是在降維過程中,用來控制矩陣分解的一些細節的參數。有四種模式可選:“auto”, “full”, “arpack”,“randomized”,默認”auto"。

  • auto”:基於X.shape和n_components的默認策略來選擇分解器
  • full”,適合數據量比較適中,計算時間充足的情況
  • arpack”,可以加快運算速度,適合特徵矩陣很大的時候,但一般用於特徵矩陣爲稀疏矩陣的情況
  • randomized”,適合特徵矩陣巨大,計算量龐大的情況

而參數random_state在參數svd_solver的值爲"arpack" or "randomized"的時候生效,可以控制這兩種SVD模式中的隨機模式。通常我們就選用”auto“,不必對這個參數糾結太多

2.3.3 重要屬性components_

PCA與特徵選擇的區別,即特徵選擇後的特徵矩陣是可解讀的,而PCA降維後的特徵矩陣式不可解讀的:PCA是將已存在的特徵進行壓縮,降維完畢後的特徵不是原本的特徵矩陣中的任何一個特徵,而是通過某些方式組合起來的新特徵。通常來說,在新的特徵矩陣生成之前,我們無法知曉PCA都建立了怎樣的新特徵向量,新特徵矩陣生成之後也不具有可讀性

在transform過程之後,fit中奇異值分解的結果除了V(k,n)以外,就會被捨棄,而V(k,n)會被保存在屬性components_ 當中,可以調用查看。

在矩陣分解時,PCA是有目標的:在原有特徵的基礎上,找出能夠讓信息儘量聚集的新特徵向量。在sklearn使用的PCA和SVD聯合的降維方法中,這些新特徵向量組成的新特徵空間其實就是V(k,n)。當V(k,n)是數字時,我們無法判斷V(k,n)和原有的特徵究竟有着怎樣千絲萬縷的數學聯繫。但是,如果原特徵矩陣是圖像,V(k,n)這個空間矩陣也可以被可視化的話,我們就可以通過兩張圖來比較,就可以看出新特徵空間究竟從原始數據裏提取了
什麼重要的信息。

2.4 重要接口inverse_transform

還原回原始數據中的特徵矩陣

inverse_transform並沒有實現數據的完全逆轉。這是因爲,在降維的時候,部分信息已經被捨棄了,X_dr中往往不會包含原數據100%的信息,所以在逆轉的時候,即便維度升高,原數據中已經被捨棄的信息也不可能再回來了。所以,降維不是完全可逆的

2.4.1 用PCA做噪音過濾

降維的目的之一就是希望拋棄掉對模型帶來負面影響的特徵,而我們相信,帶有效信息的特徵的方差應該是遠大於噪音的,所以相比噪音,有效的特徵所帶的信息應該不會在PCA過程中被大量拋棄。inverse_transform能夠在不恢復原始數據的情況下,將降維後的數據返回到原本的高維空間,即是說能夠實現”保證維度,但去掉方差很小特徵所帶的信息“。利用inverse_transform的這個性質,我們能夠實現噪音過濾。

3 案例:PCA對手寫數字數據集的降維

導入需要的模塊和庫

from sklearn.decomposition import PCA # 主成分分析
from sklearn.ensemble import RandomForestClassifier as RFC # 隨機森林
from sklearn.model_selection import cross_val_score  # 交叉驗證
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

導入數據,探索數據

data = pd.read_csv(r"C:\digit recognizor.csv")
X = data.iloc[:,1:] # 特徵矩陣
y = data.iloc[:,0] # 標籤
X.shape

畫累計方差貢獻率曲線,找最佳降維後維度的範圍

plt.rcParams['font.sans-serif']=['SimHei'] #用來正常顯示中文標籤
plt.rcParams['axes.unicode_minus']=False #用來正常顯示負號

pca_line = PCA().fit(X)
plt.figure(figsize=[20,5])
plt.plot(np.cumsum(pca_line.explained_variance_ratio_))
plt.xlabel("降維後的特徵個數")
plt.ylabel("累計可解釋性方差")
plt.show()

在這裏插入圖片描述
降維後維度的學習曲線,繼續縮小最佳維度的範圍

score = []
for i in range(1,101,10):
    X_dr = PCA(i).fit_transform(X) # 降維 特徵個數從 1 到 100 ,查看效果如何
    # 用新的特徵矩陣 (隨機森林)
    # 交叉驗證 5折
    once = cross_val_score(RFC(n_estimators=10,random_state=0),X_dr,y,cv=5).mean()
    score.append(once)
    
plt.figure(figsize=[20,5]) # 畫布
plt.plot(range(1,101,10),score)
plt.show()

在這裏插入圖片描述
n_components 在20左右 模型效果最佳

細化學習曲線,找出降維後的最佳維度

score = []
for i in range(10,25):
    X_dr = PCA(i).fit_transform(X)
    once = cross_val_score(RFC(n_estimators=10,random_state=0),X_dr,y,cv=5).mean()
    score.append(once)
plt.figure(figsize=[20,5])
plt.plot(range(10,25),score)
plt.show()

在這裏插入圖片描述

score.index(max(score))+10   # 21

可知,但降到21個特徵的時候,模型效果最佳

導入找出的最佳維度進行降維,查看模型效果

X_dr = PCA(21).fit_transform(X)
cross_val_score(RFC(n_estimators=10,random_state=0),X_dr,y,cv=5).mean()
# 0.9176904761904762

調整隨機森林 n_estimators 參數

X_dr = PCA(21).fit_transform(X)
cross_val_score(RFC(n_estimators=100,random_state=0),X_dr,y,cv=5).mean()
# 0.9435952380952382

模型效果還好,跑出了94.36%的水平,但還是沒有我們使用嵌入法特徵選擇過後的96%高,有沒有什麼辦法能夠提高模型的表現呢?

們知道KNN的效果比隨機森林更好,KNN在未調參的狀況下已經達到96%的準確率,而隨機森林在未調參前只能達到93%,這是模型本身的限制帶來的,這個數據使用KNN效果就是會更好。

from sklearn.neighbors import KNeighborsClassifier as KNN
cross_val_score(KNN(),X_dr,y,cv=5).mean() # 默認k=5
# 0.9676428571428571

效果非常好

KNN的k值學習曲線

score = []
for i in range(10):
    X_dr = PCA(21).fit_transform(X)
    once = cross_val_score(KNN(i+1),X_dr,y,cv=5).mean()
    score.append(once)
plt.figure(figsize=[10,5])
plt.plot(range(10),score)
plt.show()
cross_val_score(KNN(4),X_dr,y,cv=5).mean() # 交叉驗證

可以發現,原本785列的特徵被我們縮減到21列之後,用KNN跑出了目前位置這個數據集上最好的結果。再進行更細緻的調整,我們也許可以將KNN的效果調整到98%以上。PCA爲我們提供了無限的可能,終於不用再因爲數據量太龐大而被迫選擇更加複雜的模型了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章