《機器學習系統設計》第11章 降維 學習筆記
針對書上的內容和網絡上的資料記錄下來的筆記,大家一起學習交流。
一.為什么需要降維
(一) 多余的特征會影響或誤導學習器
(二) 更多特征意味着更多參數需要調整,過擬合風險也越大
(三) 數據的維度可能只是虛高,真實維度可能比較小
(四) 維度越少意味着訓練越快,更多東西可以嘗試,能夠得到更好的結果
(五) 如果我們想要可視化數據,就必須限制在兩個或三個維度上
因此,我們需要通過降維把無關或冗余的特征刪掉。
二.降維方法
(一)特征抽取 V.S 特征選擇
特征抽取和特征選擇是降維的兩種方法,針對於維數災難(隨着維數的增加,計算量呈指數倍增長的一種現象。),都可以達到降維的目的。
特征抽取(Feature Extraction):特征抽取后的新特征是原來特征的一個映射。
特征選擇(Feature Selection):特征選擇后的特征是原來特征的一個子集。
(二)特征選擇法
1.什么是特征選擇
特征選擇是指從全部特征中選取一個特征子集,使構造出來的模型更好。
2.為什么要做特征選擇
在機器學習的實際應用中,特征數量往往較多,其中可能存在不相關的特征,特征之間也可能存在相互依賴,容易導致如下的后果:
- 特征個數越多,分析特征、訓練模型所需的時間就越長。
- 特征個數越多,容易引起“維度災難”,模型也會越復雜,其推廣能力會下降。
特征選擇能剔除不相關或亢余的特征,從而達到減少特征個數,提高模型精確度,減少運行時間的目的。另一方面,選取出真正相關的特征簡化了模型,使研究人員易於理解數據產生的過程。
特征選擇法兩種通用的做法:篩選器(filter)和封裝器(wrapper)
3.用篩選器檢測冗余特征
篩選器通過分析特征子集內部的特點來衡量其好壞。
(1)篩選器工作流程
(2)相關性
通過使用相關性,我們很容易看到特征之間的線性關系。
這兒采用的是scipy.stat里的pearsonr()函數,計算皮爾遜相關系數【p.s. 比集體智慧編程第三章中的皮爾遜相關系數計算代碼簡單多了有木有,有的包還是 很方便的。】
兩個變量之間的相關系數越高,從一個變量去預測另一個變量的精確度就越高,這是因為相關系數越高,就意味着這兩個變量的共變部分越多,所以從其中一個變量
的變化就可越多地獲知另一個變量的變化。如果兩個變量之間的相關系數為1或-1,那么你完全可由變量X去獲知變量Y的值。
from scipy.stats import pearsonr pearsonr([1,2,3],[1,2,3.1]) #輸出 (0.99962228516121843, 0.017498096813278487)
輸出結果為(皮爾遜相關系數,p-值)
前三個具有高相關系數的情形中,我們可能要把X1或X2扔掉,因為它們似乎傳遞了相似的信息。然而在最后一種情況中,我們應該把兩個特征都保留。
盡管這種方法在前面的例子中工作得不錯,但在實際應用中卻不如意。
基於相關性的特征選擇方法的一個最大缺點就是:只能檢測出線性關系。
很明顯,相關性在檢測線性關系中是很有用的,但對於其他關系就不行了。
(3)互信息
對於非線性關系需要用到互信息。
互信息(Mutual Information)是信息論里一種有用的信息度量,它可以看成是一個隨機變量中包含的關於另一個隨機變量的信息量,或者說是一個隨機變量由於已知另一個隨機變量而減少的不肯定性。
要理解互信息是怎樣工作的,我們需要深入理解一些信息熵的知識。
信息是個很抽象的概念。人們常常說信息很多,或者信息較少,但卻很難說清楚信息到底有多少。比如一本五十萬字的中文書到底有多少信息量。直到1948年,香農提出了“信息熵”的概念,才解決了對信息的量化度量問題。信息論之父 C. E. Shannon 在 1948 年發表的論文“通信的數學理論( A Mathematical Theory of Communication )”中, Shannon 指出,任何信息都存在冗余,冗余大小與信息中每個符號(數字、字母或單詞)的出現概率或者說不確定性有關。
假設我們有一個公平的硬幣,在旋轉它之前,它是正面還是反面的不確定性是最大的,因為兩種情況都有50%的概率。這種不確定性可以通過信息熵來衡量:
在公平硬幣情境下,我們有兩種情況令x0代表硬幣正面,x1代表硬幣反面,p(x0)=p(x1)=0.5
因此我們得到下面的式子:
現在想象我們事先不知道這個硬幣實際上並不是公平的,旋轉之后有60%的可能性會出現硬幣的正面:
我們可以看到這種情形有較少的不確定性。不管正面出現的概率為0%還是100%,不確定性都將會遠離我們在0.5時所得到的熵,到達極端的0值。
隨機變量x,y之間的互信息定義為:
為了把互信息量限制在[0,1]區間,需要把它除以每個獨立變量的信息熵之和,然后就可以得到歸一化后的互信息量:
互信息量的一個較好的性質在於,跟相關性不同,它並不只關注線性關系,如下圖所示:
所以,我們需要計算每一對特征的歸一互信息量,對於具有較高互信息量的特征對,我們會把其中一個特征扔掉。
在介紹封裝器之前插播一個Scikit-learn的小介紹
scikit-learn是Python的一個開源機器學習模塊,它建立在NumPy,SciPy和matplotlib模塊之上。
scikit-learn最大的特點就是,為用戶提供各種機器學習算法接口,可以讓用戶簡單、高效地進行數據挖掘和數據分析。
安裝方法:
pip install scikit-learn
安裝scikit-learn可能會提示你電腦需要裝Microsoft Visual C++ 9.0,解決方法就是利用下邊的網址安裝好Microsoft Visual C++ Compiler for Python 2.7,即可pip install scikit-learn
(四)用封裝器讓模型選擇特征
封裝器實質上是一個分類器,封裝器用選取的特征子集對樣本集進行分類,分類的精度作為衡量特征子集好壞的標准。
- 封裝器工作流程圖
在sklearn.feature_selection包里有各種優秀的封裝器類。這個領域中的一個真正的主力軍叫做RFE(特征遞歸消除 recursive feature elimination)。它會把一個估算其和預期數量的特征當做參數,然后只要發現一個足夠小的特征子集,就在這個特征集合里訓練估算器。
例子:通過datasets的make_classification()函數,創建了一個人工構造的分類問題。它包含100個樣本,我們創建了10個特征,其中只有3個對解決這個分類問題是有價值的。
# -*- coding:utf-8 -*- from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression from sklearn.datasets import make_classification X,y = make_classification(n_samples=100,n_features=10,n_informative=3,random_state=0) #make_classification:Generate a random n-class classification problem創建一個隨機的分類問題 clf = LogisticRegression()#Logistic回歸是一個線性分類器。LogisticRegression實現了該分類器 clf.fit(X,y) #擬合模型 selector = RFE(clf,n_features_to_select=3)#特征遞歸消除 selector = selector.fit(X,y)#擬合 print selector.support_ print selector.ranking_ #輸出 [False True False True True False False False False False] [7 1 4 1 1 3 2 8 6 5]
(三)特征抽取法
1.主成分分析(PCA)
(1)PCA概述
PCA主要 用於數據降維,對於一系列例子的特征組成的多維向量,多維向量里的某些元素本身沒有區分性,比如某個元素在所有的例子中都為1,或者與1差距不大,那么這個元素本身就沒有區分性,用它做特征來區分,貢獻會非常小。所以我們的目的是找那些變化大的元素,即方差大的那些維,而去除掉那些變化不大的維,從而使特征留下的都是精品,而且計算量也變小了。
(2)PCA應用
基於 Python 中 sklearn 模塊的 PCA 算法實現
#創建人造數據集 import numpy as np x1 = np.arange(0,10,.2) #arange([start,] stop[, step,], dtype=None) #Return evenly spaced values within a given interval. #起點,終點,步長值。含起點值,不含終點值 x2 = x1+np.random.normal(loc=0,scale=1,size=50) #normal(loc=0.0, scale=1.0, size=None) #Draw random samples from a normal (Gaussian) distribution. #均值,標准差,生成的隨機數的個數 X=np.c_[(x1,x2)] #np.r_按row來組合array, #np.c_按colunm來組合array
##創建分類 good = (x1>5)|(x2>5) #x1大於5或x2大於5 bad = ~good #~為取反
##主成分分析 from sklearn import linear_model,decomposition,datasets pca = decomposition.PCA(n_components=1) #把數據降為1個維度 #sklearn.decomposition.PCA(n_components=None, copy=True, whiten=False) '''n_components: 類型:int 或者 string,缺省時默認為None,所有成分被保留。 賦值為int,比如n_components=1,將把原始數據降到一個維度。 賦值為string,比如n_components=‘mle‘,將自動選取特征個數n,使得滿足所要求的方差百分比。 意義:PCA算法中所要保留的主成分個數n,也即保留下來的特征個數n copy: 類型:bool,True或者False,缺省時默認為True。 意義:表示是否在運行算法時,將原始訓練數據復制一份。若為True,則運行PCA算法后,原始訓練數據的值不會有任何改變,因為是在原始數據的副本上進行運算;若為False,則運行PCA算法后,原始訓練數據的值會改,因為是在原始數據上進行降維計算。 whiten: 類型:bool,缺省時默認為False 意義:白化,使得每個特征具有相同的方差。''' Xtrans = pca.fit_transform(X) #pca.fit_transform(X):用X來訓練PCA模型,同時返回降維后的數據。
print pca.explained_variance_ratio_ #查看在變換中保留下來的數據方差 #output [ 0.9663034]
這意味着,例子中在數據從二維變成一維之后,我們仍然剩下96%的方差
####其它參數解釋
PCA對象的屬性
components_ :返回具有最大方差的成分。
explained_variance_ratio_:返回 所保留的n個成分各自的方差百分比。
n_components_:返回所保留的成分個數n。
PCA對象的方法
fit(X,y=None)
fit()可以說是scikit-learn中通用的方法,每個需要訓練的算法都會有fit()方法,它其實就是算法中的“訓練”這一步驟。因為PCA是無監督學習算法,此處y自然等於None。fit(X),表示用數據X來訓練PCA模型。函數返回值:調用fit方法的對象本身。比如pca.fit(X),表示用X對pca這個對象進行訓練。用X來訓練PCA模型,同時返回降維后的數據。
- fit_transform(X)
newX=pca.fit_transform(X),newX就是降維后的數據。將降維后的數據轉換成原始數據,X=pca.inverse_transform(newX)
- inverse_transform()
將數據X轉換成降維后的數據。當模型訓練好后,對於新輸入的數據,都可以用transform方法來降維。
- transform(X)
3.PCA的局限性&線性判別式分析(LDA)
作為一個線性方法,PCA在處理非線性數據時就有局限性了。
LDA與PCA的一大不同點在於,LDA是有監督的算法,而PCA是無監督的,因為PCA算法沒有考慮數據的標簽(類別),只是把原數據映射到一些方差比較大的方向上去而已,而LDA算法則考慮了數據的標簽。
import numpy as np x1 = np.arange(0,10,.2) x2 = x1+np.random.normal(loc=0,scale=1,size=50) X=np.c_[(x1,x2)] good =x1>x2 bad = ~good
from sklearn import lda lda_inst = lda.LDA(n_components=1) Xtrans = lda_inst.fit_transform(X,good)
使用PCA和LDA同時對樣本集合X進行降維,都降至2維,得到的散點圖如下:
可以看出,對於PCA降維后的樣本散點圖來說,其整體樣本沿着X軸的方差最大,而沿着Y軸的方差次大。
而從LDA圖可以看出,X軸方向對各類樣本的區分度最好,而Y軸對各類樣本的區分度次好。
所以可以看出,如果僅僅用於分類的話,LDA降維的效果要比PCA好一些。PCA更適合於解釋樣本在不同方向上的變化幅度大小。
(二)多維標度法(MDS)
一方面,PCA試圖對保留下來的數據方差進行優化,而另一方面,MDS在降低維度的時候試圖盡可能保留樣本間的相對距離。當我們有一個高維數據集,並希望獲得一個視覺印象的時候,這是非常有用的。
多維標度法的目標:當n個對象總各對對象之間的相似性(或距離)給定時,確定這些對象在低維空間中的表示,並使其盡可能原先的相似性“大體匹配”,使得由降維引起的任何變形達到最小。
import numpy as np x1 = np.arange(0,10,.2) x2 = x1+np.random.normal(loc=0,scale=1,size=50) X=np.c_[(x1,x2)] good =x1>x2 bad = ~good from sklearn import manifold mds = manifold.MDS(n_components=3) Xtrans = mds.fit_transform(X)