使用 scikit-learn 構建機器學習模型的萬能模板
你只需要兩步就能構建起自己的機器學習模型:
明確你需要解決的問題是什么類型,以及知道解決該類型問題所對應的算法。 從skicit-learn中調用相應的算法構建模型即可。是的!在機器學習領域,如果你只是抱著體驗機器學習的心態(tài),實現(xiàn)起來就是這么簡單。
第一步很好解決
常見的問題類型只有三種:分類、回歸、聚類。而明確具體問題對應的類型也很簡單。比如,如果你需要通過輸入數(shù)據(jù)得到一個類別變量,那就是分類問題。分成兩類就是二分類問題,分成兩類以上就是多分類問題。常見的有:判別一個郵件是否是垃圾郵件、根據(jù)圖片分辯圖片里的是貓還是狗等等。
如果你需要通過輸入數(shù)據(jù)得到一個具體的連續(xù)數(shù)值,那就是回歸問題。比如:預測某個區(qū)域的房價等。
常用的分類和回歸算法算法有:SVM (支持向量機) 、xgboost、, KNN、LR算法、SGD (隨機梯度下降算法)、Bayes (貝葉斯估計)以及隨機森林等。這些算法大多都既可以解分類問題,又可以解回歸問題。
如果你的數(shù)據(jù)集并沒有對應的屬性標簽,你要做的,是發(fā)掘這組樣本在空間的分布, 比如分析哪些樣本靠的更近,哪些樣本之間離得很遠, 這就是屬于聚類問題。常用的聚類算法有k-means算法。
在本文中,我們主要解決第二步:通過skicit-learn構建模型。告訴你你一套讓你簡單到想笑的通用模型構建模板。只要scikit-learn實現(xiàn)的算法,都可以通過這種方式快速調用。牢記這三個萬能模板,你就能輕松構建起自己的機器學習模型。
預備工作
在介紹萬能模板之前,為了能夠更深刻地理解這三個模板,我們加載一個Iris(鳶尾花)數(shù)據(jù)集來作為應用萬能模板的小例子,Iris數(shù)據(jù)集在前邊的文章中已經提到過多次了,這里不再贅述。它是一個典型的多分類問題。加載步驟如下:
1、加載數(shù)據(jù)集
因為原始的數(shù)據(jù)集中包含很多空值,而且類別特征用英文名表示各個花的名字,也需要我們轉換成數(shù)字。
在scikit-learn下的datasets子包里,也自帶了一個Iris數(shù)據(jù)集,這個數(shù)據(jù)集和原始數(shù)據(jù)集的區(qū)別就是scikit-learn已經幫我們提前處理好了空值等問題,可以直接輸入模型用來訓練。所以為了方便起見,我們直接使用scikit-learn的數(shù)據(jù)集。加載方法如下:
from?sklearn.datasets?import?load_iris
data?=?load_iris()
x?=?data.data
y?=?data.target
x值如下,可以看到scikit-learn把數(shù)據(jù)集經過去除空值處理放在了array里,所以x是一個(150,4)的數(shù)組,保存了150個數(shù)據(jù)的4個特征:
array([[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], [4.6, 3.4, 1.4, 0.3], [5. , 3.4, 1.5, 0.2], [4.4, 2.9, 1.4, 0.2], [4.9, 3.1, 1.5, 0.1], [5.4, 3.7, 1.5, 0.2], [4.8, 3.4, 1.6, 0.2], [4.8, 3. , 1.4, 0.1], [4.3, 3. , 1.1, 0.1], …………
y值如下,共有150行,其中0、1、2分別代表三類花:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
2、數(shù)據(jù)集拆分
數(shù)據(jù)集拆分是為了驗證模型在訓練集和測試集是否過擬合,使用train_test_split的目的是保證從數(shù)據(jù)集中均勻拆分出測試集。這里,簡單把10%的數(shù)據(jù)集拿出來用作測試集。
from?sklearn.model_selection?import?train_test_split
train_x,test_x,train_y,test_y?=?train_test_split(x,y,test_size=0.1,random_state=0)
萬能模板V1.0版
助你快速構建一個基本的算法模型

不同的算法只是改變了名字,以及模型的參數(shù)不同而已。
有了這個萬能模板,接下來就是簡單的復制粘貼改名字了:
而且在scikit-learn中,每個包的位置都是有規(guī)律的,比如:隨機森林就是在集成學習文件夾下。
模板1.0應用案例
1、構建SVM分類模型
通過查閱資料,我們知道svm算法在scikit-learn.svm.SVC下,所以:
算法位置填入: svm算法名填入: SVC()模型名自己起,這里我們就叫 svm_model
套用模板得到程序如下:
#?svm分類器
from?sklearn.svm?import?SVC
from?sklearn.metrics?import?accuracy_score
svm_model?=?SVC()
svm_model.fit(train_x,train_y)
pred1?=?svm_model.predict(train_x)
accuracy1?=?accuracy_score(train_y,pred1)
print('在訓練集上的精確度:?%.4f'%accuracy1)
pred2?=?svm_model.predict(test_x)
accuracy2?=?accuracy_score(test_y,pred2)
print('在測試集上的精確度:?%.4f'%accuracy2)
輸出:
在訓練集上的精確度: 0.9810
在測試集上的精確度: 0.9778
2、構建LR分類模型
同理,找到LR算法在sklearn.linear_model.LogisticRegression下,所以:
算法位置填入: linear_model算法名填入: LogisticRegression模型名叫做:lr_model。
程序如下:
套用模板得到程序如下:
#?LogisticRegression分類器
from?sklearn.linear_model?import?LogisticRegression
from?sklearn.metrics?import?accuracy_score?#評分函數(shù)用精確度評估
lr_model?=?LogisticRegression()
lr_model.fit(train_x,train_y)
pred1?=?lr_model.predict(train_x)
accuracy1?=?accuracy_score(train_y,pred1)
print('在訓練集上的精確度:?%.4f'%accuracy1)
pred2?=?lr_model.predict(test_x)
accuracy2?=?accuracy_score(test_y,pred2)
print('在測試集上的精確度:?%.4f'%accuracy2)
輸出:
在訓練集上的精確度: 0.9429
在測試集上的精確度: 0.8889
3、構建隨機森林分類模型
隨機森林算法在sklearn.ensemble.RandomForestClassifier?下,好了,現(xiàn)在你應該可以自己寫了,這個作為本文的一個小測試,歡迎在評論區(qū)寫下你的答案。
萬能模板V2.0版
加入交叉驗證,讓算法模型評估更加科學
在1.0版的模板中,當你多次運行同一個程序就會發(fā)現(xiàn):每次運行得到的精確度并不相同,而是在一定范圍內浮動,這是因為數(shù)據(jù)輸入模型之前會進行選擇,每次訓練時數(shù)據(jù)輸入模型的順序都不一樣。所以即使是同一個程序,模型最后的表現(xiàn)也會有好有壞。
更糟糕的是,有些情況下,在訓練集上,通過調整參數(shù)設置使模型的性能達到了最佳狀態(tài),但在測試集上卻可能出現(xiàn)過擬合的情況。這個時候,我們在訓練集上得到的評分不能有效反映出模型的泛化性能。
為了解決上述兩個問題,還應該在訓練集上劃分出驗證集(validation set)并結合交叉驗證來解決。首先,在訓練集中劃分出不參與訓練的驗證集,只是在模型訓練完成以后對模型進行評估,接著再在測試集上進行最后的評估。
但這樣大大減少了可用于模型學習的樣本數(shù)量,所以還需要采用交叉驗證的方式多訓練幾次。比如說最常用的k-折交叉驗證如下圖所示,它主要是將訓練集劃分為 k 個較小的集合。然后將k-1份訓練子集作為訓練集訓練模型,將剩余的 1 份訓練集子集作為驗證集用于模型驗證。這樣需要訓練k次,最后在訓練集上的評估得分取所有訓練結果評估得分的平均值。

這樣一方面可以讓訓練集的所有數(shù)據(jù)都參與訓練,另一方面也通過多次計算得到了一個比較有代表性的得分。唯一的缺點就是計算代價很高,增加了k倍的計算量。
原理就是這樣,但理想很豐滿,現(xiàn)實很骨干。在自己實現(xiàn)的時候卻有一個很大的難題擺在面前:怎么能夠把訓練集均勻地劃分為K份?
這個問題不用思考太多,既然別忘了,我們現(xiàn)在是站在巨人的肩膀上,scikit-learn已經將優(yōu)秀的數(shù)學家所想到的均勻拆分方法和程序員的智慧融合在了cross_val_score()?這個函數(shù)里了,只需要調用該函數(shù)即可,不需要自己想什么拆分算法,也不用寫for循環(huán)進行循環(huán)訓練。
萬能模板2.0如下:

把模型、數(shù)據(jù)、劃分驗證集的個數(shù)一股腦輸入函數(shù),函數(shù)會自動執(zhí)行上邊所說的過程。
在求精確度的時候,我們可以簡單地輸出平均精確度:
#?輸出精確度的平均值
#?print("訓練集上的精確度:?%0.2f?"?%?scores1.mean())
但是既然我們進行了交叉驗證,做了這么多計算量,單求一個平均值還是有點浪費了,可以利用下邊代碼捎帶求出精確度的置信度:
#?輸出精確度的平均值和置信度區(qū)間
print("訓練集上的平均精確度:?%0.2f?(+/-?%0.2f)"?%?(scores2.mean(),?scores2.std()?*?2))
模板2.0應用案例:
1、構建SVM分類模型
程序如下:
### svm分類器
from sklearn.model_selection import cross_val_score
from sklearn.svm import SVC
svm_model = SVC()
svm_model.fit(train_x,train_y)
scores1 = cross_val_score(svm_model,train_x,train_y,cv=5, scoring='accuracy')
# 輸出精確度的平均值和置信度區(qū)間
print("訓練集上的精確度: %0.2f (+/- %0.2f)" % (scores1.mean(), scores1.std() * 2))
scores2 = cross_val_score(svm_model,test_x,test_y,cv=5, scoring='accuracy')
# 輸出精確度的平均值和置信度區(qū)間
print("測試集上的平均精確度: %0.2f (+/- %0.2f)" % (scores2.mean(), scores2.std() * 2))
print(scores1)
print(scores2)
輸出:
訓練集上的精確度: 0.97 (+/- 0.08)
測試集上的平均精確度: 0.91 (+/- 0.10)
[1. 1. 1. 0.9047619 0.94736842]
[1. 0.88888889 0.88888889 0.875 0.875 ]
2、構建LR分類模型
#?LogisticRegression分類器
from?sklearn.model_selection?import?cross_val_score
from?sklearn.linear_model?import?LogisticRegression
lr_model?=?LogisticRegression()
lr_model.fit(train_x,train_y)
scores1?=?cross_val_score(lr_model,train_x,train_y,cv=5,?scoring='accuracy')
#?輸出精確度的平均值和置信度區(qū)間
print("訓練集上的精確度:?%0.2f?(+/-?%0.2f)"?%?(scores1.mean(),?scores1.std()?*?2))
scores2?=?cross_val_score(lr_model,test_x,test_y,cv=5,?scoring='accuracy')
#?輸出精確度的平均值和置信度區(qū)間
print("測試集上的平均精確度:?%0.2f?(+/-?%0.2f)"?%?(scores2.mean(),?scores2.std()?*?2))
print(scores1)
print(scores2)
輸出:
訓練集上的精確度: 0.94 (+/- 0.07)
測試集上的平均精確度: 0.84 (+/- 0.14)
[0.90909091 1. 0.95238095 0.9047619 0.94736842]
[0.90909091 0.88888889 0.88888889 0.75 0.75 ]
隨機森林依舊留作小測試。
注:?如果想要一次性評估多個指標,也可以使用可以一次性輸入多個評估指標的?cross_validate()函數(shù)。
三、萬能模板V3.0版
調參讓算法表現(xiàn)更上一層樓
以上都是通過算法的默認參數(shù)來訓練模型的,不同的數(shù)據(jù)集適用的參數(shù)難免會不一樣,自己設計算法是設計不來的,只能調調參這樣子,調參,是廣大算法工程師最后的尊嚴。再說,若是做算法不調參,豈不是辱沒了算法工程師在江湖上大名鼎鼎的“煉丹工程師”的名聲?
scikit-learn對于不同的算法也提供了不同的參數(shù)可以自己調節(jié)。如果細說起來,又能寫好幾篇文章,本文目的是構建一個萬能算法框架構建模板,所以,這里只介紹一下一個通用的自動化調參方法,至于更細節(jié)的每個算法對應參數(shù)的含義以及手動調參方法,會在以后的文章中結合實例具體慢慢介紹。
首先要明確的是,scikit-learn提供了算法().get_params()方法來查看每個算法可以調整的參數(shù),比如說,我們想查看SVM分類器算法可以調整的參數(shù),可以:
SVC().get_params()
輸出的就是SVM算法可以調節(jié)的參數(shù)以及系統(tǒng)默認的參數(shù)值。每個參數(shù)的具體含義會在以后的文章中介紹。
{'C': 1.0,
'cache_size': 200,
'class_weight': None,
'coef0': 0.0,
'decision_function_shape': 'ovr',
'degree': 3,
'gamma': 'auto',
'kernel': 'rbf',
'max_iter': -1,
'probability': False,
'random_state': None,
'shrinking': True,
'tol': 0.001,
'verbose': False}
接著,就可以引出我們的V3.0版萬能模板了。

參數(shù)的形式如下:

程序就會按照順序測試這幾個參數(shù)的組合效果,根本不需要自己辛辛苦苦實現(xiàn)。寫到這里,感謝各為大佬編寫了scikit-learn這么方便的機器學習包。忽然就想到了一句話:哪有什么歲月靜好,只是因為有人替你負重前行。
看到這里,可能有人會有疑惑:為什么要采用列表、字典、列表三層嵌套的方式呢?params直接是字典的形式不行嗎?答案是:行,但是不好。
讓我們先算一個小的數(shù)學題:假如我們要調節(jié)n個參數(shù),每個參數(shù)有4個備選值。那么程序就會訓練??。當n為10的時候,?,這是一個對于計算機來說龐大的計算量。而當我們將這10個參數(shù)拆分成5組,每次只調節(jié)兩個參數(shù),其他參數(shù)采用默認值,那么計算量就是??,計算量會大大減少。
列表的作用這是如此,保證了每次只調節(jié)列表中的一個字典中的參數(shù)。
運行之后,best_model就是我們得到的最優(yōu)模型,可以利用這個模型進行預測。
當然,best_model?還有好多好用的屬性:
best_model.cv_results_:可以查看不同參數(shù)情況下的評價結果。best_model.param_?:得到該模型的最優(yōu)參數(shù)best_model.best_score_:?得到該模型的最后評分結果
模板3.0應用案例
實現(xiàn)SVM分類器
###1、svm分類器
from?sklearn.model_selection?import?cross_val_score,GridSearchCV
from?sklearn.svm?import?SVC
svm_model?=?SVC()
params?=?[
????????{'kernel':?['linear'],?'C':?[1,?10,?100,?100]},
????????{'kernel':?['poly'],?'C':?[1],?'degree':?[2,?3]},
????????{'kernel':?['rbf'],?'C':?[1,?10,?100,?100],?'gamma':[1,?0.1,?0.01,?0.001]}
????????]
best_model?=?GridSearchCV(svm_model,?param_grid=params,cv?=?5,scoring?=?'accuracy')
best_model.fit(train_x,train_y)
1)查看最優(yōu)得分:
best_model.best_score_
輸出:
0.9714285714285714
2)查看最優(yōu)參數(shù):
best_model.best_params_?
輸出:
{'C': 1, 'kernel': 'linear'}
3)查看最優(yōu)模型的所有參數(shù):
best_model.best_estimator_?
這個函數(shù)會顯示出沒有調參的參數(shù),便于整體查看模型的參數(shù)。
4)查看每個參數(shù)的交叉驗證結果:
best_model.cv_results_
注:
1、以前版本是best_model.grid_scores_,現(xiàn)在已經移除
2、這個函數(shù)輸出很多數(shù)據(jù),不方便查看,一般不用
在實際使用中,如果計算資源夠用,一般采用第三種萬能公式。如果,為了節(jié)約計算資源盡快算出結果,也會采用以后介紹的手動調參方式。
當然,本文為了說明萬能模板的使用方法,在Iris數(shù)據(jù)集上將所有算法都實現(xiàn)了一遍,在實際應用中,如果項目時間緊急,根據(jù)自己的需求和數(shù)據(jù)量級選擇一個合適的算法使用即可。具體怎么選擇,scikit-learn官方非常貼心地畫了一個圖,供大家根據(jù)數(shù)據(jù)量和算法類型選擇合適的模型,這副圖建議收藏:

來源:https://zhuanlan.zhihu.com/p/88729124

加入知識星球【我們談論數(shù)據(jù)科學】
500+小伙伴一起學習!
·?推薦閱讀?·
