作者:寒小陽
時間:2016年1月。
出處:http://blog.csdn.net/han_xiaoyang/article/details/50469334
聲明:版權所有,轉載請聯(lián)系作者并注明出處
提起筆來寫這篇博客,突然有點愧疚和尷尬。愧疚的是,工作雜事多,加之懶癌嚴重,導致這個系列一直沒有更新,向關注該系列的同學們道個歉。尷尬的是,按理說,機器學習介紹與算法一覽應該放在最前面寫,詳細的應用建議應該在講完機器學習常用算法之后寫,突然莫名奇妙在中間插播這么一篇,好像有點打亂主線。
老話說『亡羊補牢,為時未晚』,前面開頭忘講的東西,咱在這塊兒補上。我們先帶著大家過一遍傳統(tǒng)機器學習算法,基本思想和用途。把問題解決思路和方法應用建議提前到這里的想法也很簡單,希望能提前給大家一些小建議,對于某些容易出錯的地方也先給大家打個預防針,這樣在理解后續(xù)相應機器學習算法之后,使用起來也有一定的章法。
按照不同的分類標準,可以把機器學習的算法做不同的分類。
我們先從機器學習問題本身分類的角度來看,我們可以分成下列類型的算法:
機器學習中有一大部分的問題屬于『監(jiān)督學習』
的范疇,簡單口語化地說明,這類問題中,給定的訓練樣本中,每個樣本的輸入
這里的預測結果如果是離散值(很多時候是類別類型,比如郵件分類問題中的垃圾郵件/普通郵件,比如用戶會/不會購買某商品),那么我們把它叫做分類問題(classification problem);如果預測結果是連續(xù)值(比如房價,股票價格等等),那么我們把它叫做回歸問題(regression problem)。
有一系列的機器學習算法是用以解決監(jiān)督學習問題的,比如最經(jīng)典的用于分類問題的樸素貝葉斯、邏輯回歸、支持向量機等等;比如說用于回歸問題的線性回歸等等。
有另外一類問題,給我們的樣本并沒有給出『標簽/標準答案』,就是一系列的樣本。而我們需要做的事情是,在一些樣本中抽取出通用的規(guī)則。這叫做『無監(jiān)督學習』
。包括關聯(lián)規(guī)則和聚類算法在內的一系列機器學習算法都屬于這個范疇。
這類問題給出的訓練數(shù)據(jù),有一部分有標簽,有一部分沒有標簽。我們想學習出數(shù)據(jù)組織結構的同時,也能做相應的預測。此類問題相對應的機器學習算法有自訓練(Self-Training)、直推學習(Transductive Learning)、生成式模型(Generative Model)等。
總體說來,最常見是前兩類問題,而對應前兩類問題的一些機器學習算法如下:
我們也可以從算法的共性(比如功能,運作方式)角度對機器學習算法分類。下面我們根據(jù)算法的共性去對它們歸個類。不過需要注意的是,我們下面的歸類方法可能對分類和回歸有比較強的傾向性,而這兩類問題也是最常遇到的。
scikit-learn作為一個豐富的python機器學習庫,實現(xiàn)了絕大多數(shù)機器學習的算法,有相當多的人在使用,于是我這里很無恥地把machine learning cheat sheet for sklearn搬過來了,原文可以看這里。哈哈,既然講機器學習,我們就用機器學習的語言來解釋一下,這是針對實際應用場景的各種條件限制,對scikit-learn里完成的算法構建的一顆決策樹,每一組條件都是對應一條路徑,能找到相對較為合適的一些解決方法,具體如下:
首先樣本量如果非常少的話,其實所有的機器學習算法都沒有辦法從里面『學到』通用的規(guī)則和模式,so多弄點數(shù)據(jù)是王道。然后根據(jù)問題是有/無監(jiān)督學習和連續(xù)值/離散值預測,分成了分類
、聚類
、回歸
和維度約減
四個方法類,每個類里根據(jù)具體情況的不同,又有不同的處理方法。
上面帶著代價走馬觀花過了一遍機器學習的若干算法,下面我們試著總結總結在拿到一個實際問題的時候,如果著手使用機器學習算法去解決問題,其中的一些注意點以及核心思路。主要包括以下內容:
多說一句,這里寫的這個小教程,主要是作為一個通用的建議和指導方案,你不一定要嚴格按照這個流程解決機器學習問題。
我們先使用scikit-learn的make_classification函數(shù)來生產(chǎn)一份分類數(shù)據(jù),然后模擬一下拿到實際數(shù)據(jù)后我們需要做的事情。
#numpy科學計算工具箱import numpy as np#使用make_classification構造1000個樣本,每個樣本有20個featurefrom sklearn.datasets import make_classificationX, y = make_classification(1000, n_features=20, n_informative=2, n_redundant=2, n_classes=2, random_state=0)#存為dataframe格式from pandas import DataFramedf = DataFrame(np.hstack((X, y[:, None])),columns = range(20) + ["class"])
我們生成了一份包含1000個分類數(shù)據(jù)樣本的數(shù)據(jù)集,每個樣本有20個數(shù)值特征。同時我們把數(shù)據(jù)存儲至pandas中的DataFrame
數(shù)據(jù)結構中。我們取前幾行的數(shù)據(jù)看一眼:
df[:6]
不幸的是,肉眼看數(shù)據(jù),尤其是維度稍微高點的時候,很有可能看花了也看不出看不出任何線索。幸運的是,我們對于圖像的理解力,比數(shù)字好太多,而又有相當多的工具可以幫助我們『可視化』數(shù)據(jù)分布。
我們在處理任何數(shù)據(jù)相關的問題時,了解數(shù)據(jù)都是很有必要的,而可視化可以幫助我們更好地直觀理解數(shù)據(jù)的分布和特性
數(shù)據(jù)的可視化有很多工具包可以用,比如下面我們用來做數(shù)據(jù)可視化的工具包Seaborn。最簡單的可視化就是數(shù)據(jù)散列分布圖和柱狀圖,這個可以用Seanborn的pairplot
來完成。以下圖中2種顏色表示2種不同的類,因為20維的可視化沒有辦法在平面表示,我們取出了一部分維度,兩兩組成pair看數(shù)據(jù)在這2個維度平面上的分布狀況,代碼和結果如下:
import matplotlib.pyplot as pltimport seaborn as sns#使用pairplot去看不同特征維度pair下數(shù)據(jù)的空間分布狀況_ = sns.pairplot(df[:50], vars=[8, 11, 12, 14, 19], hue="class", size=1.5)plt.show()
我們從散列圖和柱狀圖上可以看出,確實有些維度的特征相對其他維度,有更好的區(qū)分度,比如第11維和14維看起來很有區(qū)分度。這兩個維度上看,數(shù)據(jù)點是近似線性可分的。而12維和19維似乎呈現(xiàn)出了很高的負相關性。接下來我們用Seanborn中的corrplot
來計算計算各維度特征之間(以及最后的類別)的相關性。代碼和結果圖如下:
import matplotlib.pyplot as pltplt.figure(figsize=(12, 10))_ = sns.corrplot(df, annot=False)plt.show()
相關性圖很好地印證了我們之前的想法,可以看到第11維特征和第14維特征和類別有極強的相關性,同時它們倆之間也有極高的相關性。而第12維特征和第19維特征卻呈現(xiàn)出極強的負相關性。強相關的特征其實包含了一些冗余的特征,而除掉上圖中顏色較深的特征,其余特征包含的信息量就沒有這么大了,它們和最后的類別相關度不高,甚至各自之間也沒什么先慣性。
插一句,這里的維度只有20,所以這個相關度計算并不費太大力氣,然而實際情形中,你完全有可能有遠高于這個數(shù)字的特征維度,同時樣本量也可能多很多,那種情形下我們可能要先做一些處理,再來實現(xiàn)可視化了。別著急,一會兒我們會講到。
數(shù)據(jù)的情況我們大致看了一眼,確定一些特征維度之后,我們可以考慮先選用機器學習算法做一個baseline的系統(tǒng)出來了。這里我們繼續(xù)參照上面提到過的機器學習算法使用圖譜。
我們只有1000個數(shù)據(jù)樣本,是分類問題,同時是一個有監(jiān)督學習,因此我們根據(jù)圖譜里教的方法,使用LinearSVC
(support vector classification with linear kernel)試試。注意,LinearSVC
需要選擇正則化方法以緩解過擬合問題;我們這里選擇使用最多的L2正則化,并把懲罰系數(shù)C設為10。我們改寫一下sklearn中的學習曲線繪制函數(shù),畫出訓練集和交叉驗證集上的得分:
from sklearn.svm import LinearSVCfrom sklearn.learning_curve import learning_curve#繪制學習曲線,以確定模型的狀況def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None, train_sizes=np.linspace(.1, 1.0, 5)): """ 畫出data在某模型上的learning curve. 參數(shù)解釋 ---------- estimator : 你用的分類器。 title : 表格的標題。 X : 輸入的feature,numpy類型 y : 輸入的target vector ylim : tuple格式的(ymin, ymax), 設定圖像中縱坐標的最低點和最高點 cv : 做cross-validation的時候,數(shù)據(jù)分成的份數(shù),其中一份作為cv集,其余n-1份作為training(默認為3份) """ plt.figure() train_sizes, train_scores, test_scores = learning_curve( estimator, X, y, cv=5, n_jobs=1, train_sizes=train_sizes) train_scores_mean = np.mean(train_scores, axis=1) train_scores_std = np.std(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) test_scores_std = np.std(test_scores, axis=1) plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color="r") plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color="g") plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score") plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Cross-validation score") plt.xlabel("Training examples") plt.ylabel("Score") plt.legend(loc="best") plt.grid("on") if ylim: plt.ylim(ylim) plt.title(title) plt.show()#少樣本的情況情況下繪出學習曲線plot_learning_curve(LinearSVC(C=10.0), "LinearSVC(C=10.0)", X, y, ylim=(0.8, 1.01), train_sizes=np.linspace(.05, 0.2, 5))
這幅圖上,我們發(fā)現(xiàn)隨著樣本量的增加,訓練集上的得分有一定程度的下降,交叉驗證集上的得分有一定程度的上升,但總體說來,兩者之間有很大的差距,訓練集上的準確度遠高于交叉驗證集。這其實意味著我們的模型處于過擬合的狀態(tài),也即模型太努力地刻畫訓練集,一不小心把很多噪聲的分布也擬合上了,導致在新數(shù)據(jù)上的泛化能力變差了。
問題來了,過擬合咋辦?
針對過擬合,有幾種辦法可以處理:
這個比較好理解吧,過擬合的主要原因是模型太努力地去記住訓練樣本的分布狀況,而加大樣本量,可以使得訓練集的分布更加具備普適性,噪聲對整體的影響下降。恩,我們提高點樣本量試試:
#增大一些樣本量plot_learning_curve(LinearSVC(C=10.0), "LinearSVC(C=10.0)", X, y, ylim=(0.8, 1.1), train_sizes=np.linspace(.1, 1.0, 5))
是不是發(fā)現(xiàn)問題好了很多?隨著我們增大訓練樣本量,我們發(fā)現(xiàn)訓練集和交叉驗證集上的得分差距在減少,最后它們已經(jīng)非常接近了。增大樣本量,最直接的方法當然是想辦法去采集相同場景下的新數(shù)據(jù),如果實在做不到,也可以試試在已有數(shù)據(jù)的基礎上做一些人工的處理生成新數(shù)據(jù)(比如圖像識別中,我們可能可以對圖片做鏡像變換、旋轉等等),當然,這樣做一定要謹慎,強烈建議想辦法采集真實數(shù)據(jù)。
比如在這個例子中,我們之前的數(shù)據(jù)可視化和分析的結果表明,第11和14維特征包含的信息對識別類別非常有用,我們可以只用它們。
plot_learning_curve(LinearSVC(C=10.0), "LinearSVC(C=10.0) Features: 11&14", X[:, [11, 14]], y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
從上圖上可以看出,過擬合問題也得到一定程度的緩解。不過我們這是自己觀察后,手動選出11和14維特征。那能不能自動進行特征組合和選擇呢,其實我們當然可以遍歷特征的組合樣式,然后再進行特征選擇(前提依舊是這里特征的維度不高,如果高的話,遍歷所有的組合是一個非常非常非常耗時的過程?。?:
from sklearn.pipeline import Pipelinefrom sklearn.feature_selection import SelectKBest, f_classif# SelectKBest(f_classif, k=2) 會根據(jù)Anova F-value選出 最好的k=2個特征plot_learning_curve(Pipeline([("fs", SelectKBest(f_classif, k=2)), # select two features ("svc", LinearSVC(C=10.0))]), "SelectKBest(f_classif, k=2) + LinearSVC(C=10.0)", X, y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
如果你自己跑一下程序,會發(fā)現(xiàn)在我們自己手造的這份數(shù)據(jù)集上,這個特征篩選的過程超級順利,但依舊像我們之前提過的一樣,這是因為特征的維度不太高。
從另外一個角度看,我們之所以做特征選擇,是想降低模型的復雜度,而更不容易刻畫到噪聲數(shù)據(jù)的分布。從這個角度出發(fā),我們還可以有(1)多項式你和模型中降低多項式次數(shù) (2)神經(jīng)網(wǎng)絡中減少神經(jīng)網(wǎng)絡的層數(shù)和每層的結點數(shù) (c)SVM中增加RBF-kernel的bandwidth等方式來降低模型的復雜度。
話說回來,即使以上提到的辦法降低模型復雜度后,好像能在一定程度上緩解過擬合,但是我們一般還是不建議一遇到過擬合,就用這些方法處理,優(yōu)先用下面的方法:
plot_learning_curve(LinearSVC(C=0.1), "LinearSVC(C=0.1)", X, y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
調整正則化系數(shù)后,發(fā)現(xiàn)確實過擬合現(xiàn)象有一定程度的緩解,但依舊是那個問題,我們現(xiàn)在的系數(shù)是自己敲定的,有沒有辦法可以自動選擇最佳的這個參數(shù)呢?可以。我們可以在交叉驗證集上做grid-search查找最好的正則化系數(shù)(對于大數(shù)據(jù)樣本,我們依舊需要考慮時間問題,這個過程可能會比較慢):
from sklearn.grid_search import GridSearchCVestm = GridSearchCV(LinearSVC(), param_grid={"C": [0.001, 0.01, 0.1, 1.0, 10.0]})plot_learning_curve(estm, "LinearSVC(C=AUTO)", X, y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))print "Chosen parameter on 100 datapoints: %s" % estm.fit(X[:500], y[:500]).best_params_
在500個點得到的結果是:{‘C’: 0.01}
使用新的C參數(shù),我們再看看學習曲線:
對于特征選擇的部分,我打算多說幾句,我們剛才看過了用sklearn.feature_selection中的SelectKBest來選擇特征的過程,也提到了在高維特征的情況下,這個過程可能會非常非常慢。那我們有別的辦法可以進行特征選擇嗎?比如說,我們的分類器自己能否甄別那些特征是對最后的結果有益的?這里有個實際工作中用到的小技巧。
我們知道:
那基于這個理論,我們可以把SVC中的正則化替換成l1正則化,讓其自動甄別哪些特征應該留下權重。
plot_learning_curve(LinearSVC(C=0.1, penalty='l1', dual=False), "LinearSVC(C=0.1, penalty='l1')", X, y, ylim=(0.8, 1.0), train_sizes=np.linspace(.05, 0.2, 5))
好了,我們一起來看看最后特征獲得的權重:
estm = LinearSVC(C=0.1, penalty='l1', dual=False)estm.fit(X[:450], y[:450]) # 用450個點來訓練print "Coefficients learned: %s" % est.coef_print "Non-zero coefficients: %s" % np.nonzero(estm.coef_)[1]
得到結果:
Coefficients learned: [[ 0. 0. 0. 0. 0. 0.01857999 0. 0. 0. 0.004135 0. 1.05241369 0.01971419 0. 0. 0. 0. -0.05665314 0.14106505 0. ]]Non-zero coefficients: [5 9 11 12 17 18]
你看,5 9 11 12 17 18這些維度的特征獲得了權重,而第11維權重最大,也說明了它影響程度最大。
我們再隨機生成一份數(shù)據(jù)[1000*20]的數(shù)據(jù)(但是分布和之前有變化),重新使用LinearSVC來做分類。
#構造一份環(huán)形數(shù)據(jù)from sklearn.datasets import make_circlesX, y = make_circles(n_samples=1000, random_state=2)#繪出學習曲線plot_learning_curve(LinearSVC(C=0.25),"LinearSVC(C=0.25)",X, y, ylim=(0.5, 1.0),train_sizes=np.linspace(.1, 1.0, 5))
簡直爛出翔了有木有,二分類問題,我們做隨機猜測,準確率都有0.5,這比隨機猜測都高不了多少!??!怎么辦?
不要盲目動手收集更多資料,或者調整正則化參數(shù)。我們從學習曲線上其實可以看出來,訓練集上的準確度和交叉驗證集上的準確度都很低,這其實就對應了我們說的『欠擬合』狀態(tài)。別急,我們回到我們的數(shù)據(jù),還是可視化看看:
f = DataFrame(np.hstack((X, y[:, None])), columns = range(2) + ["class"])_ = sns.pairplot(df, vars=[0, 1], hue="class", size=3.5)
你發(fā)現(xiàn)什么了,數(shù)據(jù)根本就沒辦法線性分割?。?!,所以你再找更多的數(shù)據(jù),或者調整正則化參數(shù),都是無濟于事的?。?!
那我們又怎么解決欠擬合問題呢?通常有下面一些方法:
# 加入原始特征的平方項作為新特征X_extra = np.hstack((X, X[:, [0]]**2 + X[:, [1]]**2))plot_learning_curve(LinearSVC(C=0.25), "LinearSVC(C=0.25) + distance feature", X_extra, y, ylim=(0.5, 1.0), train_sizes=np.linspace(.1, 1.0, 5))
from sklearn.svm import SVC# note: we use the original X without the extra featureplot_learning_curve(SVC(C=2.5, kernel="rbf", gamma=1.0), "SVC(C=2.5, kernel='rbf', gamma=1.0)",X, y, ylim=(0.5, 1.0), train_sizes=np.linspace(.1, 1.0, 5))
你看,效果依舊很贊。
我們在小樣本的toy dataset上,怎么搗鼓都有好的方法。但是當數(shù)據(jù)量和特征樣本空間膨脹非常厲害時,很多東西就沒有那么好使了,至少是一個很耗時的過程。舉個例子說,我們現(xiàn)在重新生成一份數(shù)據(jù)集,但是這次,我們生成更多的數(shù)據(jù),更高的特征維度,而分類的類別也提高到5。
在上面提到的那樣一份數(shù)據(jù)上,我們用LinearSVC可能就會有點慢了,我們注意到機器學習算法使用圖譜推薦我們使用SGDClassifier
。其實本質上說,這個模型也是一個線性核函數(shù)的模型,不同的地方是,它使用了隨機梯度下降做訓練,所以每次并沒有使用全部的樣本,收斂速度會快很多。再多提一點,SGDClassifier
對于特征的幅度非常敏感,也就是說,我們在把數(shù)據(jù)灌給它之前,應該先對特征做幅度調整,當然,用sklearn的StandardScaler
可以很方便地完成這一點。
StandardScaler
每次只使用一部分(mini-batch)做訓練,在這種情況下,我們使用交叉驗證(cross-validation)并不是很合適,我們會使用相對應的progressive validation:簡單解釋一下,estimator每次只會拿下一個待訓練batch在本次做評估,然后訓練完之后,再在這個batch上做一次評估,看看是否有優(yōu)化。
#生成大樣本,高緯度特征數(shù)據(jù)X, y = make_classification(200000, n_features=200, n_informative=25, n_redundant=0, n_classes=10, class_sep=2, random_state=0)#用SGDClassifier做訓練,并畫出batch在訓練前后的得分差from sklearn.linear_model import SGDClassifierest = SGDClassifier(penalty="l2", alpha=0.001)progressive_validation_score = []train_score = []for datapoint in range(0, 199000, 1000): X_batch = X[datapoint:datapoint+1000] y_batch = y[datapoint:datapoint+1000] if datapoint > 0: progressive_validation_score.append(est.score(X_batch, y_batch)) est.partial_fit(X_batch, y_batch, classes=range(10)) if datapoint > 0: train_score.append(est.score(X_batch, y_batch))plt.plot(train_score, label="train score")plt.plot(progressive_validation_score, label="progressive validation score")plt.xlabel("Mini-batch")plt.ylabel("Score")plt.legend(loc='best') plt.show()
得到如下的結果:
從這個圖上的得分,我們可以看出在50個mini-batch迭代之后,數(shù)據(jù)上的得分就已經(jīng)變化不大了。但是好像得分都不太高,所以我們猜測一下,這個時候我們的數(shù)據(jù),處于欠擬合狀態(tài)。我們剛才在小樣本集合上提到了,如果欠擬合,我們可以使用更復雜的模型,比如把核函數(shù)設置為非線性的,但遺憾的是像rbf核函數(shù)是沒有辦法和SGDClassifier
兼容的。因此我們只能想別的辦法了,比如這里,我們可以把SGDClassifier
整個替換掉了,用多層感知神經(jīng)網(wǎng)
來完成這個任務,我們之所以會想到多層感知神經(jīng)網(wǎng)
,是因為它也是一個用隨機梯度下降訓練的算法,同時也是一個非線性的模型。當然根據(jù)機器學習算法使用圖譜,也可以使用核估計(kernel-approximation)來完成這個事情。
大樣本數(shù)據(jù)的可視化是一個相對比較麻煩的事情,一般情況下我們都要用到降維的方法先處理特征。我們找一個例子來看看,可以怎么做,比如我們數(shù)據(jù)集取經(jīng)典的『手寫數(shù)字集』,首先找個方法看一眼這個圖片數(shù)據(jù)集。
#直接從sklearn中l(wèi)oad數(shù)據(jù)集from sklearn.datasets import load_digitsdigits = load_digits(n_class=6)X = digits.datay = digits.targetn_samples, n_features = X.shapeprint "Dataset consist of %d samples with %d features each" % (n_samples, n_features)# 繪制數(shù)字示意圖n_img_per_row = 20img = np.zeros((10 * n_img_per_row, 10 * n_img_per_row))for i in range(n_img_per_row): ix = 10 * i + 1 for j in range(n_img_per_row): iy = 10 * j + 1 img[ix:ix + 8, iy:iy + 8] = X[i * n_img_per_row + j].reshape((8, 8))plt.imshow(img, cmap=plt.cm.binary)plt.xticks([])plt.yticks([])_ = plt.title('A selection from the 8*8=64-dimensional digits dataset')plt.show()
我們總共有1083個訓練樣本,包含手寫數(shù)字(0,1,2,3,4,5),每個樣本圖片中的像素點平鋪開都是64位,這個維度顯然是沒辦法直接可視化的。下面我們基于scikit-learn的示例教程對特征用各種方法做降維處理,再可視化。
隨機投射
我們先看看,把數(shù)據(jù)隨機投射到兩個維度上的結果:
#import所需的packagefrom sklearn import (manifold, decomposition, random_projection)rp = random_projection.SparseRandomProjection(n_components=2, random_state=42)#定義繪圖函數(shù)from matplotlib import offsetboxdef plot_embedding(X, title=None): x_min, x_max = np.min(X, 0), np.max(X, 0) X = (X - x_min) / (x_max - x_min) plt.figure(figsize=(10, 10)) ax = plt.subplot(111) for i in range(X.shape[0]): plt.text(X[i, 0], X[i, 1], str(digits.target[i]), color=plt.cm.Set1(y[i] / 10.), fontdict={'weight': 'bold', 'size': 12}) if hasattr(offsetbox, 'AnnotationBbox'): # only print thumbnails with matplotlib > 1.0 shown_images = np.array([[1., 1.]]) # just something big for i in range(digits.data.shape[0]): dist = np.sum((X[i] - shown_images) ** 2, 1) if np.min(dist) < 4e-3: # don't show points that are too close continue shown_images = np.r_[shown_images, [X[i]]] imagebox = offsetbox.AnnotationBbox( offsetbox.OffsetImage(digits.images[i], cmap=plt.cm.gray_r), X[i]) ax.add_artist(imagebox) plt.xticks([]), plt.yticks([]) if title is not None: plt.title(title)#記錄開始時間start_time = time.time()X_projected = rp.fit_transform(X)plot_embedding(X_projected, "Random Projection of the digits (time: %.3fs)" % (time.time() - start_time))
結果如下:
PCA降維
在維度約減/降維領域有一個非常強大的算法叫做PCA(Principal Component Analysis,主成分分析),它能將原始的絕大多數(shù)信息用維度遠低于原始維度的幾個主成分表示出來。PCA在我們現(xiàn)在的數(shù)據(jù)集上效果還不錯,我們來看看用PCA對原始特征降維至2維后,原始樣本在空間的分布狀況:
from sklearn import (manifold, decomposition, random_projection)#TruncatedSVD 是 PCA的一種實現(xiàn)X_pca = decomposition.TruncatedSVD(n_components=2).fit_transform(X)#記錄時間start_time = time.time()plot_embedding(X_pca,"Principal Components projection of the digits (time: %.3fs)" % (time.time() - start_time))
得到的結果如下:
我們可以看出,效果還不錯,不同的手寫數(shù)字在2維平面上,顯示出了區(qū)域集中性。即使它們之間有一定的重疊區(qū)域。
如果我們用一些非線性的變換來做降維操作,從原始的64維降到2維空間,效果更好,比如這里我們用到一個技術叫做t-SNE,sklearn的manifold對其進行了實現(xiàn):
from sklearn import (manifold, decomposition, random_projection)#降維tsne = manifold.TSNE(n_components=2, init='pca', random_state=0)start_time = time.time()X_tsne = tsne.fit_transform(X)#繪圖plot_embedding(X_tsne, "t-SNE embedding of the digits (time: %.3fs)" % (time.time() - start_time))
我們發(fā)現(xiàn)結果非常的驚人,似乎這個非線性變換降維過后,僅僅2維的特征,就可以將原始數(shù)據(jù)的不同類別,在平面上很好地劃分開。不過t-SNE也有它的缺點,一般說來,相對于線性變換的降維,它需要更多的計算時間。也不太適合在大數(shù)據(jù)集上全集使用。
損失函數(shù)的選擇對于問題的解決和優(yōu)化,非常重要。我們先來看一眼各種不同的損失函數(shù):
import numpy as npimport matplotlib.plot as plt# 改自http://scikit-learn.org/stable/auto_examples/linear_model/plot_sgd_loss_functions.htmlxmin, xmax = -4, 4xx = np.linspace(xmin, xmax, 100)plt.plot([xmin, 0, 0, xmax], [1, 1, 0, 0], 'k-', label="Zero-one loss")plt.plot(xx, np.where(xx < 1, 1 - xx, 0), 'g-', label="Hinge loss")plt.plot(xx, np.log2(1 + np.exp(-xx)), 'r-', label="Log loss")plt.plot(xx, np.exp(-xx), 'c-', label="Exponential loss")plt.plot(xx, -np.minimum(xx, 0), 'm-', label="Perceptron loss")plt.ylim((0, 8))plt.legend(loc="upper right")plt.xlabel(r"Decision function $f(x)$")plt.ylabel("$L(y, f(x))$")plt.show()
得到結果圖像如下:
不同的損失函數(shù)有不同的優(yōu)缺點:
全文到此就結束了。先走馬觀花看了一遍機器學習的算法,然后給出了對應scikit-learn的『秘密武器』機器學習算法使用圖譜,緊接著從了解數(shù)據(jù)(可視化)、選擇機器學習算法、定位過/欠擬合及解決方法、大量極的數(shù)據(jù)可視化和損失函數(shù)優(yōu)缺點與選擇等方面介紹了實際機器學習問題中的一些思路和方法。本文和文章機器學習系列(3)_邏輯回歸應用之Kaggle泰坦尼克之災都提及了一些處理實際機器學習問題的思路和方法,有相似和互補之處,歡迎大家參照著看。
聯(lián)系客服