机器学习实战(用Scikit-learn和TensorFlow进行机器学习)(九)
上幾節(jié)介紹了多種模型(線性模型、支持向量機(jī)、集成學(xué)習(xí)),這一節(jié)介紹一類新的預(yù)處理方法。?
九、降維(Dimensionality Reduction)
??在現(xiàn)實(shí)生活中很多機(jī)器學(xué)習(xí)問題有上千維,甚至上萬維特征,這不僅影響了訓(xùn)練速度,通常還很難找到比較好的解。這樣的問題成為維數(shù)災(zāi)難(curse of dimensionality)
??幸運(yùn)的是,理論上降低維度是可行的。比如MNIST數(shù)據(jù)集大部分的像素總是白的,因此可以去掉這些特征;相鄰的像素之間是高度相關(guān)的,如果變?yōu)橐粋€(gè)像素,相差也并不大。
??需要注意:降低維度肯定會(huì)損失一些信息,這可能會(huì)讓表現(xiàn)稍微變差。因此應(yīng)該先在原維度訓(xùn)練一次,如果訓(xùn)練速度太慢再選擇降維。雖然有時(shí)候降為能去除噪聲和一些不必要的細(xì)節(jié),但通常不會(huì),主要是能加快訓(xùn)練速度。
??降維除了能訓(xùn)練速度以外,還能用于數(shù)據(jù)可視化。把高維數(shù)據(jù)降到2維或3維,然后就能把特征在2維空間(3維空間)表示出來,能直觀地發(fā)現(xiàn)一些規(guī)則。
??本節(jié)會(huì)將兩種主要的降維方法(projection and Manifold Learning),并且通過3中流行的降維技術(shù):PCA,Kernel PCA和LLE。?
1、維數(shù)災(zāi)難
??在高維空間中,許多表現(xiàn)和我們在低維認(rèn)識(shí)的差別很大。例如,在單位正方形(1×1)內(nèi)隨機(jī)選點(diǎn),那么隨機(jī)選的點(diǎn)離所有邊界大于0.001(靠近中間位置)的概率為0.4%(1 - 0.99821 - 0.9982),但在一個(gè)10000維單位超立方體(1×1×?×1),這個(gè)概率大于99 999999%(1 - 0.998100001 - 0.99810000)。因此高維超立方體中的大多數(shù)隨機(jī)點(diǎn)都非常接近邊界。
??在高維空間中還有一個(gè)與認(rèn)識(shí)不同的:如果在單位超立方體(1×1×?×1)隨機(jī)選兩個(gè)點(diǎn),在2維空間,兩個(gè)點(diǎn)之間平均距離約為0.52;在3維空間約為0.66,在1,000,000維空間,則約為408.25,可以看到在單位立方體中兩個(gè)點(diǎn)距離竟然能相差這么大。因此在高維空間,這很可能會(huì)使得訓(xùn)練集和測試集相差很大,導(dǎo)致訓(xùn)練過擬合。
??理論上,只要通過增加訓(xùn)練集,就能達(dá)到訓(xùn)練空間足夠密集。但是隨著維度的增加,需要的訓(xùn)練集是呈指數(shù)增長的。如果100維(比MNIST數(shù)據(jù)集還要小),要達(dá)到每個(gè)樣本的距離為0.1(平均的分散開)的密度,則需要的樣本為1010010100個(gè)樣本,可見需要的樣本之多。?
2、降維的主要方法
??降為的方法主要為兩種:projection 和 Manifold Learning。
投影(Projection)
??在大多數(shù)的真實(shí)問題,訓(xùn)練樣例都不是均勻分散在所有的維度,許多特征都是固定的,同時(shí)還有一些特征是強(qiáng)相關(guān)的。因此所有的訓(xùn)練樣例實(shí)際上可以投影在高維空間中的低維子空間中,下面看一個(gè)例子。
??????????
??可以看到3維空間中的訓(xùn)練樣例其實(shí)都分布在同一個(gè)2維平面,因此我們能夠?qū)⑺袠永纪队霸?維平面。對于更高維的空間可能能投影到低維的子空間中。
??然而投影(projection)不總是降維最好的方法在,比如許多情況下,空間可以扭轉(zhuǎn),如著名的瑞士卷(Swiss roll)數(shù)據(jù)。
?????????
??如果簡單的使用投影(project)降維(例如通過壓平第3維),那么會(huì)變成如下左圖的樣子,不同類別的樣例都混在了一起,而我們的預(yù)想是變成右下圖的形式。
???????
流行學(xué)習(xí)(Manifold Learning)
??瑞士卷(Swiss roll)是二維流形的例子。它可以在高維空間中彎曲。更一般地,一個(gè)d維流形在n維空間彎曲(其中d<n)。在瑞士卷的情況下,D=2和n=3。
??基于流行數(shù)據(jù)進(jìn)行建模的降維算法稱為流形學(xué)習(xí)(Manifold Learning)。它假設(shè)大多數(shù)現(xiàn)實(shí)世界的高維數(shù)據(jù)集接近于一個(gè)低維流形。
??流行假設(shè)通常隱含著另一個(gè)假設(shè):通過流形在低維空間中表達(dá),任務(wù)(例如分類或回歸)應(yīng)該變得簡單。如下圖第一行,Swiss roll分為兩類,在3D的空間看起來很復(fù)雜,但通過流行假設(shè)到2D就能變得簡單。
??但是這個(gè)假設(shè)并不總是能成立,比如下圖第二行,決策線為x=5,2D的的決策線明顯比3D的要復(fù)雜。因此在訓(xùn)練模型之前先降維能夠加快訓(xùn)練速度,但是效果可能會(huì)又增有減,這取決于數(shù)據(jù)的形式。
????
??下面介紹幾種著名的降維技術(shù)。?
3、主成分分析(PCA)
??主成分分析(PCA)是用的最出名的降維技術(shù),它通過確定最接近數(shù)據(jù)的超平面,然后將數(shù)據(jù)投射(project)到該超平面上。?
保留最大方差
??首先需要選擇一個(gè)好的超平面。先看下圖的例子,需要將2D降為1D,選擇不同的平面得到右圖不一樣的結(jié)果,第1個(gè)投影以后方差最大,第3個(gè)方差最小,選擇最大方差的一個(gè)感覺上應(yīng)該是比較合理的,因?yàn)檫@樣能保留更多的信息。
??????
??另外一種判斷的方式是:通過最小化原數(shù)據(jù)和投影后的數(shù)據(jù)之間的均方誤差。?
主成分(Principal Components)
??算法首先找到第一個(gè)主成分,使得投影后方差最大,如上圖的c1。然后再找第二個(gè)主成分(要和第一個(gè)主成分正交)使得上面投影后的數(shù)據(jù)集再投影后方差最大,由于上圖是2D平面,因此只有c2符合。如果是n維平面,則找第(t<=n)個(gè)主成分(要和1,2,…,t-1個(gè)主成分都正交),使得t-1投影后的數(shù)據(jù)再投影后方差最大。
??需要注意:主成分的方向是不穩(wěn)定的,如果稍微擾動(dòng)數(shù)據(jù),則可能使方向相反,不過仍會(huì)在同一個(gè)軸上。在某些情況下,一對主成分甚至可以旋轉(zhuǎn)或交換,但它們定義的平面一般保持不變。
??要如何才能在訓(xùn)練集中找到這些主成分?我們可以通過奇異值分解(SVD)來把X矩陣分解為三個(gè)矩陣U*∑*VTU*∑*VT,其中VTVT矩陣的每一個(gè)列向量就是我們要找的主成分。
??Numpy中提供svd()方法,下面為求主成分的例子
#產(chǎn)生數(shù)據(jù) import numpy as np x1=np.random.normal(0,1,100) x2=x1*1+np.random.rand(100) X=np.c_[x1,x2] #svd分解求出主成分 X_centered = X - X.mean(axis=0) U, s, V = np.linalg.svd(X_centered) c1 = V.T[:, 0] c2 = V.T[:, 1]- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
??需要注意:PCA假設(shè)數(shù)據(jù)以原點(diǎn)為中心,因此要先減去均值再svd分解。Scikit-learn中的PCA類已經(jīng)減去均值,但是如果在別的場景使用要記得先減去均值再求主成分。?
投影到d維空間
??得到主成分以后就能將數(shù)據(jù)降維,假設(shè)降到d維空間,則用數(shù)據(jù)矩陣與前d個(gè)主成分形成的矩陣相乘,得到降維后的矩陣。
Xd=X?WdXd=X?Wd??對應(yīng)的代碼為: d=1 Wd = V.T[:, :d] XD = X_centered.dot(Wd)
- 1
- 2
- 3
使用Scikit-Learn
??Scikit-learn提供了PCA類,n_components控制維數(shù)
from sklearn.decomposition import PCA pca = PCA(n_components = 1) XD = pca.fit_transform(X)- 1
- 2
- 3
??fit以后可以通過components_變量來輸出主成分,還可以通過explained_variance_ratio_來查看每個(gè)主成分占方差的比率。
#查看主成分 pca.components_.T #顯示PCA主成分比率 print("主成分方差比率為:") print(pca.explained_variance_ratio_)- 1
- 2
- 3
- 4
- 5
??
??在這個(gè)2D空間,可以看到第一個(gè)主成分占訓(xùn)練集方差的97%,即第二個(gè)主成分占訓(xùn)練集方差的3%,因此假設(shè)第二個(gè)主成分含有的信息比較少時(shí)比較合理的。?
選擇合理的維數(shù)
??合理的選擇維數(shù)而不是隨機(jī)選擇一個(gè)維數(shù),我們可以通過設(shè)置一個(gè)合適的方差比率(如95%),計(jì)算需要多少個(gè)主成分的方差比率和能達(dá)到這個(gè)比率,就選擇該維度,對應(yīng)代碼如下。(除非需要將數(shù)據(jù)降到2D或3D用于可視化等)
pca = PCA() pca.fit(X) cumsum = np.cumsum(pca.explained_variance_ratio_) #累加 d = np.argmax(cumsum >= 0.95) + 1- 1
- 2
- 3
- 4
??得到維度d后再次設(shè)置n_components進(jìn)行PCA降維。當(dāng)然還有更加簡便的方法,直接設(shè)置n_components_=0.95,那么Scikit-learn能直接作上述的操作。
pca = PCA(n_components=0.95) X_reduced = pca.fit_transform(X)- 1
- 2
增量PCA(IPCA)
??當(dāng)數(shù)據(jù)量較大時(shí),使用SVD分解會(huì)耗費(fèi)很大的內(nèi)存以及運(yùn)算速度較慢。幸運(yùn)的是,可以使用IPCA算法來解決。先將訓(xùn)練樣本分為mini-batches,每次給IPCA算法一個(gè)mini-batch,這樣就能處理大量的數(shù)據(jù),也能實(shí)現(xiàn)在線學(xué)習(xí)(當(dāng)有新的數(shù)據(jù)加入時(shí))。
??下面是使用Numpy的array_split()方法將MNIST數(shù)據(jù)集分為100份,再分別喂給IPCA,將數(shù)據(jù)降到154維。需要注意,這里對于mini-batch使用partial_fit()方法,而不是對于整個(gè)數(shù)據(jù)集的fit()方法。
#加載數(shù)據(jù) from sklearn.datasets import fetch_mldata mnist = fetch_mldata('MNIST original') X = mnist["data"] #使用np.array_split()方法的IPCA from sklearn.decomposition import IncrementalPCA n_batches = 100 inc_pca = IncrementalPCA(n_components=154) for X_batch in np.array_split(X, n_batches):inc_pca.partial_fit(X_batch) X_mnist_reduced = inc_pca.transform(X)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
??還可以使用Numpy 的memmap類來操縱儲(chǔ)存在硬盤上的二進(jìn)制編碼的大型數(shù)據(jù),只有當(dāng)數(shù)據(jù)被用到的時(shí)候才會(huì)將數(shù)據(jù)放入內(nèi)存。由于IPCA每次只需將一部分?jǐn)?shù)據(jù),因此能通過memmap來控制內(nèi)存。由于使用的是輸入的是整個(gè)數(shù)據(jù)集,因此使用的是fit()方法。
X_mm = np.memmap(filename, dtype="float32", mode="readonly", shape=(m, n)) batch_size = m // n_batches inc_pca = IncrementalPCA(n_components=154, batch_size=batch_size) inc_pca.fit(X_mm)- 1
- 2
- 3
- 4
隨機(jī)PCA
??隨機(jī)PCA是個(gè)隨機(jī)算法,能快速找到接近前d個(gè)主成分,它的計(jì)算復(fù)雜度為O(m?d3)+O(d3)O(m?d3)+O(d3),而不是O(m?n3)+O(n3)O(m?n3)+O(n3),如果d<
rnd_pca = PCA(n_components=154, svd_solver="randomized") X_reduced = rnd_pca.fit_transform(X_mnist)- 1
- 2
4、核PCA(Kernel PCA)
??在第六節(jié)的SVM中提到了核技巧,即通過數(shù)學(xué)方法達(dá)到增加特征類似的功能來實(shí)現(xiàn)非線性分類。類似的技巧還能用在PCA上,使得可以實(shí)現(xiàn)復(fù)雜的非線性投影降維,稱為kPCA。該算法善于保持聚類后的集群(clusters)后投影,有時(shí)展開數(shù)據(jù)接近于扭曲的流形。下面是使用RBF核的例子。
#生成Swiss roll數(shù)據(jù) from sklearn.datasets import make_swiss_roll data=make_swiss_roll(n_samples=1000, noise=0.0, random_state=None) X=data[0] y=data[1] #畫3維圖 import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D ax = plt.subplot(111, projection='3d') ax.scatter(X[:,0], X[:,1], X[:,2],c=y) plt.show() #kPCA from sklearn.decomposition import KernelPCA rbf_pca = KernelPCA(n_components = 2, kernel="rbf", gamma=0.04) X_reduced = rbf_pca.fit_transform(X)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
??需要注意,此方法要使用大量內(nèi)存,可能會(huì)使內(nèi)存溢出。
選擇合適的核與參數(shù)
??由于kPCA是非監(jiān)督算法,因此無法判斷性能的好壞,因此需要結(jié)合分類或回歸問題來判斷。通過GridSearch來選擇合適的核與參數(shù),下面是一個(gè)例子:
from sklearn.datasets import fetch_mldata mnist = fetch_mldata('MNIST original') X,y = mnist["data"],mnist["target"]from sklearn.decomposition import KernelPCA from sklearn.model_selection import GridSearchCV from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline clf = Pipeline([ ("kpca", KernelPCA(n_components=2)), ("log_reg", LogisticRegression()) ]) param_grid = [{ "kpca__gamma": np.linspace(0.03, 0.05, 10), "kpca__kernel": ["rbf", "sigmoid"] }] grid_search = GridSearchCV(clf, param_grid, cv=3) grid_search.fit(X, y) print(grid_search.best_params_)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
5、LLE
??局部線性嵌入(Locally Linear Embedding)是另一種非線性降維技術(shù)。它基于流行學(xué)習(xí)而不是投影。LLE首先測量每個(gè)訓(xùn)練樣例到其最近的鄰居(CN)的線性關(guān)系,然后尋找一個(gè)低維表示使得這種相關(guān)性保持得最好(具體細(xì)節(jié)后面會(huì)說)。這使得它特別擅長展開扭曲的流形,特別是沒有太多的噪音的情況。
??下面是使用Scikit-learn中的LocallyLinearEmbedding類來對Swiss roll數(shù)據(jù)降維。
#生成Swiss roll數(shù)據(jù) from sklearn.datasets import make_swiss_roll data=make_swiss_roll(n_samples=1000, noise=0.0, random_state=None) X=data[0] y=data[1] #畫3維圖 import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D ax = plt.subplot(111, projection='3d') ax.scatter(X[:,0], X[:,1], X[:,2],c=y) plt.show() #LLe降維 from sklearn.manifold import LocallyLinearEmbedding lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10) #畫出降為圖 X_reduced = lle.fit_transform(X) plt.scatter(X_reduced[:,0],X_reduced[:,1],c=y)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
?????
??可以看到,Swiss roll展開后局部保持的距離比較好。然而,但是對大的整體距離并不是保持的很好(右下角部分被擠壓,左上角伸展)。但是LLE算法還是在流形中表現(xiàn)的很好。?
LLE原理
第一步:對于每一個(gè)訓(xùn)練樣本x(i)x(i),尋找離它最近的k個(gè)樣本(如k=10),然后嘗試重建x(i)x(i)與這些鄰居的線性關(guān)系,即用這些鄰居來線性表示x(i)x(i)。如下公式,最小化x(i)x(i)與這個(gè)線性表示的距離。其中非鄰居的wi,jwi,j=0。
W^=argminW∑i=1m||x(i)?∑j=1mwi,jx(j)||2W^=arg?minW?∑i=1m||x(i)?∑j=1mwi,jx(j)||2 wi,j=0????if????i,j??is??not??neighborwi,j=0????if????i,j??is??not??neighbor ∑j=1mwi,jx(j)=1∑j=1mwi,jx(j)=1第二步:將訓(xùn)練集降為d維(d < n),由于上一步已經(jīng)求出局部特征矩陣W^W^,因此在d維空間也要盡可能符合這個(gè)矩陣W^W^,假設(shè)樣本x(i)x(i)降維變?yōu)?span id="ozvdkddzhkzd" class="MathJax" style="line-height:normal;font-size:18px;text-align:left;word-spacing:normal;white-space:nowrap;float:none;min-width:0px;min-height:0px;border:0px;padding:0px;margin:0px;">z(i)z(i),如下公式:?
??計(jì)算k個(gè)鄰居的計(jì)算復(fù)雜度為:O(mlog(m)nlog(k))O(mlog?(m)nlog?(k));優(yōu)化權(quán)值的復(fù)雜度為:O(mnk3)O(mnk3);重建低維向量的復(fù)雜度為:O(dm2)O(dm2)。如果訓(xùn)練樣本數(shù)量過大,那么在最后一步重建m2m2會(huì)很慢。?
6、其它降維技術(shù)
??還有非常多降維的技術(shù),有一些Scikit-learn也提供支持。下面簡單列舉一些比較有名的算法。
1、Multidimensional Scaling (MDS)降維的同時(shí)保留樣本之間的距離,如下圖。
2、Isomap通過連接每個(gè)樣本和它的最近鄰居來創(chuàng)建一個(gè)圖,然后降低維的同時(shí)嘗試保留樣本間的測地距離(兩個(gè)樣本之間最少經(jīng)過幾個(gè)點(diǎn))。
3、t-Distributed Stochastic Neighbor Embedding (t-SNE),減少維度的同時(shí)試圖保持相似的樣本靠近和不同的樣本分離。它主要用于可視化,特別是可視化高維空間中的聚類。
4、Linear Discriminant Analysis (LDA),是一種分類算法,但是在訓(xùn)練定義了一個(gè)超平面來投影數(shù)據(jù)。投影使得同一類的樣本靠近,不同一類的樣本分開,所以在運(yùn)行另一分類算法(如SVM分類器)之前,LDA是一種很好的減少維數(shù)的技術(shù)。
????????
總結(jié)
以上是生活随笔為你收集整理的机器学习实战(用Scikit-learn和TensorFlow进行机器学习)(九)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 机器学习实战(用Scikit-learn
- 下一篇: sklearn API 文档