1. 程式人生 > 其它 >機器學習實戰 | SKLearn最全應用指南

機器學習實戰 | SKLearn最全應用指南

作者:韓信子@ShowMeAI
教程地址http://www.showmeai.tech/tutorials/41
本文地址http://www.showmeai.tech/article-detail/203
宣告:版權所有,轉載請聯絡平臺與作者並註明出處
收藏ShowMeAI檢視更多精彩內容


引言

我們在上一篇SKLearn入門與簡單應用案例裡給大家講到了SKLearn工具的基本板塊與使用方法,在本篇內容中,我們展開講解SKLearn的進階與核心內容。SKLearn中有六大任務模組,如下圖所示:分別是分類、迴歸、聚類、降維、模型選擇和預處理。

在SKLearn中,因為做了上層的封裝,分類模型、迴歸模型、聚類與降維模型、前處理器等等都叫做估計器(estimator),就像在Python裡「萬物皆物件」,在SKLearn裡「萬物皆估計器」。

在本篇內容中,我們將給大家進一步深入講解scikit-learn工具庫的使用方法,力求完整覆蓋sklearn工具庫應用的方方面面。本文的內容板塊包括:

  • 機器學習基礎知識:機器學習定義與四要素:資料、任務、效能度量和模型。機器學習概念,以便和SKLearn對應匹配上。
  • SKLearn講解:API設計原理,sklearn幾大特點:一致性、可檢驗、標準類、可組合和預設值,以及SKLearn自帶資料以及儲存格式。
  • SKLearn三大核心API講解:包括估計器、預測器和轉換器。這個板塊很重要,大家實際應用時主要是藉助於核心API落地。
  • SKLearn高階API講解:包括簡化程式碼量的流水線(Pipeline估計器),整合模型(Ensemble估計器)、有多類別-多標籤-多輸出分類模型(Multiclass 和 Multioutput 估計器)和模型選擇工具(Model Selection估計器)。

1.機器學習簡介

關於本節內容,強烈推薦大家閱讀ShowMeAI

文章 圖解機器學習 | 機器學習基礎知識圖解機器學習 | 模型評估方法與準則ShowMeAI對相關知識內容展開做了詳細講解。

1.1 定義和構成元素

何為機器學習?大師湯姆米切爾(Tom Mitchell)對機器學習定義的原話是:

A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P if its performance at tasks in T, as measured by P, improves with experience E.

這段英文中有兩個詞computer program和learn,翻譯成中文就是機器(計算機程式)和學習,整體翻譯下來就是說:如果計算機程式在T任務上的效能(由P衡量)隨著經驗E而提高,則稱計算機程式從經驗E中學習某類任務T。

由上述機器學習的定義可知機器學習包含四個元素:

  • 資料(Data)
  • 任務(Task)
  • 效能度量(Quality Metric)
  • 演算法(Algorithm)

1.2 資料

資料(data)是資訊的載體。資料可以有以下劃分方式:

  • 從「資料具體型別」維度劃分:結構化資料和非結構化資料。

    • 結構化資料(structured data)是由二維表結構來邏輯表達和實現的資料。
    • 非結構化資料是沒有預定義的資料,不便用資料庫二維表來表現的資料。非結構化資料包括圖片,文字,語音和視訊等。
  • 從「資料表達形式」維度劃分:原始資料和加工資料。

  • 從「資料統計性質」維度劃分:樣本內資料和樣本外資料。

對於非結構資料,通常神經網路有更好的效果,可以參考ShowMeAI的文章Python機器學習演算法實踐中的影象建模例子。

機器學習模型很多時候使用的是結構化資料,即二維的資料表。我們這裡以iris花瓣資料集舉例,如下圖。

下面術語大家在深入瞭解機器學習前一定要弄清楚:

  • 每行的記錄(這是一朵鳶尾花的資料統計),稱為一個「樣本(sample)」。
  • 反映樣本在某方面的性質,例如萼片長度(Sepal Length)、花瓣長度(Petal Length),稱為「特徵(feature)」。
  • 特徵上的取值,例如「樣本1」對應的5.1、3.5稱為「特徵值(feature value)」。
  • 關於樣本結果的資訊,例如Setosa、Versicolor,稱為「類別標籤(class label)」。
  • 包含標籤資訊的示例,則稱為「樣例(instance)」,即樣例=(特徵,標籤)
  • 從資料中學得模型的過程稱為「學習(learning)」或「訓練(training)」。
  • 在訓練資料中,每個樣例稱為「訓練樣例(training instance)」,整個集合稱為「訓練集(training set)」。

1.3 任務

根據學習的任務模式(訓練資料是否有標籤),機器學習可分為幾大類:

  • 監督學習(有標籤)
  • 無監督學習(無標籤)
  • 半監督學習(有部分標籤)
  • 強化學習(有延遲的標籤)

下圖畫出機器學習各類之間的關係。

1.4 效能度量

迴歸和分類任務中最常見的誤差函式以及一些有用的效能度量如下,詳細內容可以參考ShowMeAI文章 機器學習評估與度量準則

2. SKLearn資料

SKLearn作為通用機器學習建模的工具包,包含六個任務模組和一個數據匯入模組:

首先看看SKLearn預設資料格式和自帶資料集。

2.1 SKLearn預設資料格式

Sklean裡模型能直接使用的資料有兩種形式:

  • Numpy二維陣列(ndarray)的稠密資料(dense data),通常都是這種格式。
  • SciPy矩陣(scipy.sparse.matrix)的稀疏資料(sparse data),比如文字分析每個單詞(字典有100000個詞)做獨熱編碼得到矩陣有很多0,這時用ndarray就不合適了,太耗記憶體。

2.2 自帶資料集

SKLearn裡面有很多自帶資料集供使用者使用。

比如在之前文章Python機器學習演算法實踐中用到的鳶尾花資料集,包含四個特徵(萼片長/寬和花瓣長/寬)和三個類別。

我們可以直接從SKLearn裡面的datasets模組中引入,程式碼如下(程式碼可以在 線上Jupyter環境 中執行):

# 匯入工具庫
from sklearn.datasets import load_iris    
iris = load_iris()

#資料是以「字典」格式儲存的,看看 iris 的鍵有哪些。
iris.keys()

輸出如下:

dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

讀取資料集的資訊:

#輸出iris 資料中特徵的大小、名稱等資訊和前五個樣本。
n_samples, n_features = iris.data.shape    
print((n_samples, n_features))    
print(iris.feature_names)    
print(iris.target.shape)    
print(iris.target_names)
iris.data[0:5]

輸出如下:

(150, 4)
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
(150,)
['setosa' 'versicolor' 'virginica']
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]])

構建Dataframe格式的資料集:

# 將X和y合併為Dataframe格式資料 
import pandas as pd
import seaborn as sns
iris_data = pd.DataFrame( iris.data,     
                          columns=iris.feature_names )    
iris_data['species'] = iris.target_names[iris.target]    
iris_data.head(3).append(iris_data.tail(3))

輸出如下:

sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
147 6.5 3.0 5.2 2.0 virginica
148 6.2 3.4 5.4 2.3 virginica
149 5.9 3.0 5.1 1.8 virginica

我們使用seaborn來做一些資料分析,檢視一下資料的分佈特性。這裡使用到的是成對維度的關聯分析,關於seaborn的使用方法可以參閱ShowMeAI的文章 seaborn工具與資料視覺化教程

# 使用Seaborn的pairplot檢視兩兩特徵之間的關係
sns.pairplot( iris_data, hue='species', palette='husl' )

2.3 資料集引入方式

前面提到的是鳶尾花iris資料集,我們通過load_iris載入進來,實際上SKLearn有三種引入資料形式。

  • 打包好的資料:對於小資料集,用sklearn.datasets.load_*
  • 分流下載資料:對於大資料集,用sklearn.datasets.fetch_*
  • 隨機建立資料:為了快速展示,用sklearn.datasets.make_*

上面這個星號*指代具體檔名,如果大家在Jupyter這種IDE環境中,可以通過tab製表符自動補全和選擇。

  • datasets.load_
  • datasets.fetch_
  • datasets.make_

比如我們呼叫load_iris

from sklearn import datasets
datasets.load_iris

輸出如下:

<function sklearn.datasets.base.load_iris(return_X_y=False)>

我們呼叫load_digits載入手寫數字影象資料集

digits = datasets.load_digits()
digits.keys()

輸出:

dict_keys(['data', 'target', 'target_names', 'images', 'DESCR'])

我們再來看看通過fetch拉取資料的示例:

#加州房屋資料集
california_housing = datasets.fetch_california_housing()    
california_housing.keys()

輸出:

dict_keys(['data', 'target', 'feature_names', 'DESCR'])

3.SKLearn核心API

我們前面提到SKLearn裡萬物皆估計器。估計器是個非常抽象的叫法,不嚴謹的一個理解,我們可以視其為一個模型(用來回歸、分類、聚類、降維),或一套流程(預處理、網格搜尋交叉驗證)。

本節三大API其實都是估計器:

  • 估計器(estimator)通常是用於擬合功能的估計器。
  • 預測器(predictor)是具有預測功能的估計器。
  • 轉換器(transformer)是具有轉換功能的估計器。

3.1 估計器

任何可以基於資料集對一些引數進行估計的物件都被稱為估計器,它有兩個核心點:

  • ① 需要輸入資料。
  • ② 可以估計引數。

估計器首先被建立,然後被擬合。

  • 建立估計器:需要設定一組超引數,比如

    • 線性迴歸裡超引數normalize=True
    • K均值裡超引數n_clusters=5
  • 擬合估計器:需要訓練集

    • 在監督學習中的程式碼正規化為model.fit(X_train, y_train)
    • 在無監督學習中的程式碼正規化為model.fit(X_train)

擬合之後可以訪問model裡學到的引數,比如線性迴歸裡的特徵係數coef,或K均值裡聚類標籤labels,如下(具體的可以在SKLearn文件的每個模型頁查到)。

  • model.coef_
  • model.labels_

下面看看監督學習的「線性迴歸」和無監督學習的「K均值聚類」的具體例子。

(1) 線性迴歸

首先從SKLearn工具庫的linear_model中引入LinearRegression;建立模型物件命名為model,設定超引數normalizeTrue(在每個特徵值上做標準化,這樣能保證擬合的穩定性,加速模型擬合速度)。

from sklearn.linear_model import LinearRegression
model = LinearRegression(normalize=True)
model

輸出:

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=True)

建立完後的估計器會顯示所有的超引數(比如剛才設定的normalize=True),未設定的超引數都使用預設值。

自己建立一個簡單資料集(一條直線上的資料點),簡單講解一下估計器裡面的特徵。

import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)    
y = 2 * x + 1    
plt.plot( x, y, 'o' )

在我們生成的資料裡,X是一維,我們做一點小小的調整,用np.newaxis加一個維度,把[1,2,3]轉成[[1],[2],[3]],這樣的資料形態可以符合sklearn的要求。接著把X和y送入fit()函式來擬合線性模型的引數。

X = x[:, np.newaxis]    
model.fit( X, y )

輸出為:

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=True)

擬合完後的估計器和建立完似乎沒有差別,但我們已經可以用model.param_訪問到擬合完資料的引數了,如下程式碼。

print( model.coef_ )    
print( model.intercept_ )
# 輸出結果
# [2.]
# 0.9999999999999982

(2) K均值

我們來看看聚類的例子,先從SKLearn的cluster中匯入KMeans,初始化模型物件命名為model,設定超引數n_cluster為3(為了展示方便而我們知道用的iris資料集有3類,實際上可以設定不同數量的n_cluster)。

雖然iris資料裡包含標籤y,但在無監督的聚類中我們不會使用到這個資訊。

from sklearn.cluster import KMeans    
model = KMeans( n_clusters=3 )    
model

輸出為:

KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
       n_clusters=3, n_init=10, n_jobs=None, precompute_distances='auto',
       random_state=None, tol=0.0001, verbose=0)

iris資料集包含四維特徵(萼片長、萼片寬、花瓣長、花瓣寬),在下面的例子中我們希望視覺化,這裡我們簡單選取兩個特徵(萼片長、萼片寬)來做聚類並且視覺化結果。

注意下面程式碼X = iris.data[:,0:2]其實就是提取特徵維度。

from sklearn.datasets import load_iris    
iris = load_iris()
X = iris.data[:,0:2]    
model.fit(X)

輸出為:

KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
       n_clusters=3, n_init=10, n_jobs=None, precompute_distances='auto',
       random_state=None, tol=0.0001, verbose=0)

擬合完後的估計器和建立完似乎沒有差別,但我們已經可以用model.param_訪問到擬合完資料的引數了,如下程式碼。

print( model.cluster_centers_, '\n')    
print( model.labels_, '\n' )    
print( model.inertia_, '\n')    
print(iris.target)
[[5.77358491 2.69245283]
 [6.81276596 3.07446809]
 [5.006      3.428     ]] 

[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 1 1 1 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 1 1 1 1
 1 1 0 0 1 1 1 1 0 1 0 1 0 1 1 0 0 1 1 1 1 1 0 0 1 1 1 0 1 1 1 0 1 1 1 0 1
 1 0] 

37.05070212765958 

[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]

這裡解釋一下KMeans模型這幾個引數:

  • model.clustercenters:簇中心。三個簇意味著有三個座標。
  • model.labels_:聚類後的標籤。
  • model.inertia_:所有點到對應的簇中心的距離平方和(越小越好)

小結

雖然上面以有監督學習的Linear Regression和無監督學習的KMeans舉例,但實際上你可以將它們替換成其他別的模型,比如監督學習的Logistic Regression和無監督學習的DBSCAN。它們都是「估計器」,因此都有fit()方法。

使用它們的通用虛擬碼如下:

# 有監督學習
from sklearn.xxx import SomeModel
# xxx 可以是 linear_model 或 ensemble 等
model = SomeModel( hyperparameter )
model.fit( X, y )

# 無監督學習
from sklearn.xxx import SomeModel
# xxx 可以是 cluster 或 decomposition 等
model = SomeModel( hyperparameter )
model.fit( X )

3.2 預測器

預測器是估計器做的一個延展,具備對資料進行預測的功能。

預測器最常見的是predict()函式:

  • model.predict(X_test):評估模型在新資料上的表現。
  • model.predict(X_train):確認模型在老資料上的表現。

為了進行新資料評估,我們先將資料分成80:20的訓練集(X_train, y_train)和測試集(X_test, y_test),再用從訓練集上擬合fit()的模型在測試集上預測predict()

from sklearn.datasets import load_iris    
iris = load_iris()
from sklearn.model_selection import train_test_split    
X_train, X_test, y_train, y_test = train_test_split( iris['data'],     
                    iris['target'],     
                    test_size=0.2 )    
print( 'The size of X_train is ', X_train.shape )    
print( 'The size of y_train is ', y_train.shape )    
print( 'The size of X_test is ', X_test.shape )    
print( 'The size of y_test is ', y_test.shape )
The size of X_train is  (120, 4)
The size of y_train is  (120,)
The size of X_test is  (30, 4)
The size of y_test is  (30,)

predict & predict_proba

對於分類問題,我們不僅想知道預測的類別是什麼,有時我們還希望獲取預測概率等資訊。前者用 predict(),後者用predict_proba()

y_pred = model.predict( X_test )
p_pred = model.predict_proba( X_test )
print( y_test, '\n' )
print( y_pred, '\n' )
print( p_pred )

score & decision_function

預測器裡還有額外的兩個函式可以使用。在分類問題中:

  • score()返回的是分類準確率。
  • decision_function()返回的是每個樣例在每個類下的分數值。
print( model.score( X_test, y_test ) )
print( np.sum(y_pred==y_test)/len(y_test) )
decision_score = model.decision_function( X_test )
print( decision_score )

小結

估計器都有fit()方法,預測器都有predict()score()方法,言外之意不是每個預測器都有predict_proba()decision_function()方法,這個在用的時候查查官方文件就清楚了(比如RandomForestClassifier就沒有decision_function()方法)。

使用它們的通用虛擬碼如下:

# 有監督學習
from sklearn.xxx import SomeModel
# xxx 可以是 linear_model 或 ensemble 等
model = SomeModel( hyperparameter )
model.fit( X, y )
y_pred = model.predict( X_new )
s = model.score( X_new )

# 無監督學習
from sklearn.xxx import SomeModel
# xxx 可以是 cluster 或 decomposition 等
model = SomeModel( hyperparameter )
model.fit( X )
idx_pred = model.predict( X_new )
s = model.score( X_new )

3.3 轉換器

轉換器是一種估計器,也有擬合功能,對比預測器做完擬合來預測,轉換器做完擬合來轉換。核心點如下:

  • 估計器裡fit + predict
  • 轉換器裡fit + transform

本節介紹兩大類轉換器:

  • 將類別型變數(categorical)編碼成數值型變數(numerical)
  • 規範化(normalize)或標準化(standardize)數值型變數

(1) 類別型變數編碼

① LabelEncoder&OrdinalEncoder

LabelEncoder和OrdinalEncoder都可以將字元轉成數字,但是:

  • LabelEncoder的輸入是一維,比如1d ndarray
  • OrdinalEncoder的輸入是二維,比如 DataFrame
# 首先給出要編碼的列表 enc 和要解碼的列表 dec。
enc = ['red','blue','yellow','red']    
dec = ['blue','blue','red']

# 從sklearn下的preprocessing中引入LabelEncoder,再建立轉換器起名LE,不需要設定任何超引數。
from sklearn.preprocessing import LabelEncoder    
LE = LabelEncoder()    
print(LE.fit(enc))    
print( LE.classes_ )    
print( LE.transform(dec) )
LabelEncoder()
['blue' 'yellow' 'red']
[0 1 2]

除了LabelEncoder,OrdinalEncoder也可以完成編碼。如下程式碼所示:

from sklearn.preprocessing import OrdinalEncoder    
OE = OrdinalEncoder()    
enc_DF = pd.DataFrame(enc)    
dec_DF = pd.DataFrame(dec)    
print( OE.fit(enc_DF) )    
print( OE.categories_ )    
print( OE.transform(dec_DF) )
OrdinalEncoder(categories='auto', dtype=<class 'numpy.float64'>)
[array(['blue', 'yellow', 'red'], dtype=object)]
[[0.]
 [1.]
 [2.]]

上面這種編碼的問題是,在編碼過後會帶來不同類別的大小關係,比如這裡3種顏色其實本質上是平等的,沒有大小關係。

我們的另外一種類別型資料編碼方式,獨熱向量編碼(one-hot encoding)可以解決這個問題,大家繼續往下看。

② OneHotEncoder

獨熱向量編碼其實就是把一個整數用向量的形式表現。上圖右側就是對顏色做獨熱向量編碼。轉換器OneHotEncoder可以接受兩種型別的輸入:

  • ① 用LabelEncoder編碼好的一維陣列
  • ② DataFrame

一、用LabelEncoder編碼好的一維陣列(元素為整數),重塑(用reshape(-1,1))成二維陣列作為OneHotEncoder輸入

from sklearn.preprocessing import OneHotEncoder    
OHE = OneHotEncoder()    
num = LE.fit_transform( enc )    
print( num )    
OHE_y = OHE.fit_transform( num.reshape(-1,1) )    
OHE_y
[2 0 1 2]

輸出為:

<4x3 sparse matrix of type '<class 'numpy.float64'>'
    with 4 stored elements in Compressed Sparse Row format>

上面結果解釋如下:

  • 第3行打印出編碼結果[2 0 1 2]。
  • 第5行將其轉成獨熱形式,輸出是一個「稀疏矩陣」形式,因為實操中通常類別很多,因此就一步到位用稀疏矩陣來節省記憶體。

想看該矩陣裡具體內容,用toarray()函式。

OHE_y.toarray()

輸出為:

array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

二、用DataFrame作為OneHotEncoder輸入

OHE = OneHotEncoder()    
OHE.fit_transform( enc_DF ).toarray()

輸出為:

array([[0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

(2) 特徵縮放

資料要做的最重要的轉換之一是特徵縮放(feature scaling)。類似邏輯迴歸,神經網路這種計算型模型,對於不同特徵的幅度大小差異是敏感的。

具體來說,對於某個特徵,我們有兩種變換方法:

  • 標準化(standardization):每個維度的特徵減去該特徵均值,除以該維度的標準差。
  • 規範化(normalization):每個維度的特徵減去該特徵最小值,除以該特徵的最大值與最小值之差。

① MinMaxScaler

如上圖所示,MinMaxScaler會根據特徵的最大最小取值,對資料進行幅度縮放。

from sklearn.preprocessing import MinMaxScaler    
X = np.array( [0, 0.5, 1, 1.5, 2, 100] )    
X_scale = MinMaxScaler().fit_transform( X.reshape(-1,1) )    
X_scale

輸出為:

array([[0.   ],
       [0.005],
       [0.01 ],
       [0.015],
       [0.02 ],
       [1.   ]])

② StandardScaler

StandardScaler做的事情是調整資料分佈,儘量接近正態分佈。

from sklearn.preprocessing import StandardScaler    
X_scale = StandardScaler().fit_transform( X.reshape(-1,1) )    
X_scale

輸出為:

array([[-0.47424487],
       [-0.46069502],
       [-0.44714517],
       [-0.43359531],
       [-0.42004546],
       [ 2.23572584]])

注意:fit()函式只能作用在訓練集上,如果希望對測試集變換,只要用訓練集上fit好的轉換器去transform即可。不能在測試集上fit再transform,否則訓練集和測試集的變換規則不一致,模型學習到的資訊就無效了。

4.高階API

我們在這節中給大家介紹SKLearn的「高階API」,即五大元估計器(整合功能的Ensemble,多分類和多標籤的Multiclass,多輸出的Multioutput,選擇模型的Model Selection,流水線的Pipeline)。

  • ensemble.BaggingClassifier
  • ensemble.VotingClassifier
  • multiclass.OneVsOneClassifier
  • multiclass.OneVsRestClassifier
  • multioutput.MultiOutputClassifier
  • model_selection.GridSearchCV
  • model_selection.RandomizedSearchCV
  • pipeline.Pipeline

4.1 Ensemble 估計器

如上圖:分類器統計每個子分類器的預測類別數,再用「多數投票」原則得到最終預測。迴歸器計算每個子迴歸器的預測平均值。

最常用的Ensemble估計器排列如下:

  • AdaBoostClassifier:逐步提升分類器
  • AdaBoostRegressor:逐步提升迴歸器
  • BaggingClassifier:Bagging分類器
  • BaggingRegressor:Bagging迴歸器
  • GradientBoostingClassifier:梯度提升分類器
  • GradientBoostingRegressor:梯度提升迴歸器
  • RandomForestClassifier:隨機森林分類器
  • RandomForestRegressor:隨機森林迴歸器
  • VotingClassifier:投票分類器
  • VotingRegressor:投票迴歸器

我們用鳶尾花資料iris,拿以下estimator來舉例:

  • 含同質估計器RandomForestClassifier
  • 含異質估計器VotingClassifier

首先將資料分成80:20的訓練集和測試集,並引入metrics來計算各種效能指標。

from sklearn.datasets import load_iris    
iris = load_iris()
from sklearn.model_selection import train_test_split    
from sklearn import metrics    
X_train, X_test, y_train, y_test = train_test_split(iris['data'], iris['target'], test_size=0.2)

(1) RandomForestClassifier

隨機森林RandomForestClassifier通過控制n_estimators超引數來決定基估計器的個數,在這裡是4棵決策樹(森林由樹組成);此外每棵樹的最大樹深為5(max_depth=5)。

from sklearn.ensemble import RandomForestClassifier    
RF = RandomForestClassifier( n_estimators=4, max_depth=5 )    
RF.fit( X_train, y_train )

輸出為:

RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
                       max_depth=5, max_features='auto', max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, n_estimators=4,
                       n_jobs=None, oob_score=False, random_state=None,
                       verbose=0, warm_start=False)

元估計器和預估器一樣也有fit()。下面看看隨機森林裡包含的估計器個數和其本身。

print( RF.n_estimators )    
RF.estimators_

輸出為:

4

[DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
                        max_features='auto', max_leaf_nodes=None,
                        min_impurity_decrease=0.0, min_impurity_split=None,
                        min_samples_leaf=1, min_samples_split=2,
                        min_weight_fraction_leaf=0.0, presort=False,
                        random_state=705712365, splitter='best'),
 DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
                        max_features='auto', max_leaf_nodes=None,
                        min_impurity_decrease=0.0, min_impurity_split=None,
                        min_samples_leaf=1, min_samples_split=2,
                        min_weight_fraction_leaf=0.0, presort=False,
                        random_state=1026568399, splitter='best'),
 DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
                        max_features='auto', max_leaf_nodes=None,
                        min_impurity_decrease=0.0, min_impurity_split=None,
                        min_samples_leaf=1, min_samples_split=2,
                        min_weight_fraction_leaf=0.0, presort=False,
                        random_state=1987322366, splitter='best'),
 DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=5,
                        max_features='auto', max_leaf_nodes=None,
                        min_impurity_decrease=0.0, min_impurity_split=None,
                        min_samples_leaf=1, min_samples_split=2,
                        min_weight_fraction_leaf=0.0, presort=False,
                        random_state=1210538094, splitter='best')]

擬合RF完再做預測,用metrics裡面的accuracy_score來計算準確率。訓練準確率98.33%,測試準確率100%。

print ( "RF - Accuracy (Train):  %.4g" %     
        metrics.accuracy_score(y_train, RF.predict(X_train)) )    
print ( "RF - Accuracy (Test):  %.4g" %     
        metrics.accuracy_score(y_test, RF.predict(X_test)) )
RF - Accuracy (Train):  1
RF - Accuracy (Test):  0.9667

(2) VotingClassifier

隨機森林由同質分類器「決策樹」不同,投票分類器由若干個異質分類器組成。下面我們用VotingClassifier建立個含有邏輯迴歸(Logistic regression)、隨機森林(RandomForest)和高斯樸素貝葉斯(GNB)三個分類器的整合模型。

RandomForestClassifier的基分類器只能是決策樹,因此只用通過控制n_estimators超引數來決定樹的個數,而VotingClassifier的基分類器要輸入每個異質分類器。

from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier

LR = LogisticRegression( solver='lbfgs', multi_class='multinomial' )
RF = RandomForestClassifier( n_estimators=5 )
GNB = GaussianNB()

Ensemble = VotingClassifier( estimators=[('lr', LR), (‘rf', RF), ('gnb', GNB)], voting='hard' )

Ensemble. fit( X_train, y_train )

結果如下:

VotingClassifier(estimators=[('lr', LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,intercept_scaling=1, max_iter=100, multi_class='multinomial',n_jobs=None, penalty='12', random_state=None, solver='lbfgs',tol=0.0001, verbose=6, warm_start=False)), ('rf', ...e, verbose=0,warm_start=False)), ('gnb', GaussianNB(priors=None, var_smoothing=1e-09))],flatten_transform=None, n_jobs=None, voting='hard', weights=None)

看看Ensemble整合模型裡包含的估計器個數和其本身。

print( len(Ensemble.estimators_) )        
Ensemble.estimators_

結果如下:

3

[LogisticRegression(C=1.0, class_weight-None, dual-False, fit_intercept=True,intercept_scaling=1, max_iter=100, multi_class='multinomial',n_jobs-None, penalty="12", random_state-None, solver='1bfgs',t01=0.0001, verbose=0, warm_start=False),

RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',max_depth=None, max_features='auto', max_leaf_nodes=None,min_impurity_decrease-0.0, min_impurity_splitmin_samples_leaf=1, min_samples_split=2,min_weight_fraction_leaf=0.0, n_estimator:oob_score=False, random_state-None, verbose=
warm_start=False),

GaussianNB(priors-None, var_smoothing=1e-9)]

對比元估計器和它三個組成元素的表現,下過表現如下:

# 擬合
LR.fit( X_train, y_train )        
RF.fit( X_train, y_train )        
GNB.fit( X_train, y_train )
# 評估效果
print ( "LR - Accuracy (Train): %.4g" % metrics.accuracy_score(y_train, LR.predict(X_train)) )
print ( "RF - Accuracy (Train): %.4g" % metrics.accuracy_score(y_train, RF.predict(X_train)) )
print ( "GNB - Accuracy (Train): %.4g" % metrics.accuracy_score(y_train, GNB.predict(X_train)) )
print ( "Ensemble - Accuracy (Train): %.4g" % metrics.accuracy_score(y_train, Ensemble.predict(X_train)) )
print ( "LR - Accuracy (Test): %.4g" % metrics.accuracy_score(y_test, LR.predict(X_test)) )

print ( "RF - Accuracy (Test): %.4g" % metrics.accuracy_score(y_test, RF.predict(x_test)) )
print ( "GNB - Accuracy (Test): %.4g" % metrics.accuracy_score(y_test, RF.predict(X_test)) )
print ( "Ensemble - Accuracy (Test): %.4g" % metrics.accuracy_score(y test, Ensemble.predict(X_test)) )
# 執行結果
LR - Accuracy (Train): 0.975
RF - Accuracy (Train): 0.9833
GNB - Accuracy (Train): 0.95
Ensemble - Accuracy (Train): 0.9833 
LR - Accuracy (Test): 1
RF - Accuracy (Test): 1
GNB - Accuracy (Test): 1
Ensemble - Accuracy (Test): 1

4.2 Multiclass 估計器

sklearn.multiclass可以處理多類別(multi-class) 的多標籤(multi-label) 的分類問題。下面我們會使用數字資料集digits作為示例資料來講解。我們先將資料分成 80:20 的訓練集和測試集。

# 匯入資料
from sklearn.datasets import load_digits                 
digits = load_digits()        
digits.keys()

輸出如下:

# 輸出結果
dict_keys(['data', 'target', 'target_names','images', 'DESCR'])

下面我們切分資料集:

# 資料集切分
X_train, X_test, y_train, y_test = train_test_split( digits['data'], digits['target'], test_size=0.2 )
                
print( 'The size of X_train is ', X_train.shape )        
print( 'The size of y_train is ', y_train.shape )        
print( 'The size of X_test is ', X_test.shape )        
print( 'The size of y_test is ', y_test.shape )

輸出如下

The size of X_train is (1437, 64)
The size of y_train is (1437,)
The size of X_test is (360, 64)
The size of y_test is (360,)

訓練集和測試集分別有1437和360張影象。每張照片是包含8×8的畫素,我們用flatten操作把2維的8×8展平為1維的64。

看看訓練集中前100張圖片和對應的標籤(如下圖)。畫素很低,但基本上還是能看清。

fig, axes = plt.subplots( 10, 16, figsize=(8, 8) )
fig.subplots_adjust( hspace=0.1, wspace=0.1 )
for i, ax in enumerate( axes.flat ):
    ax.imshow( X_train[i,:].reshape(8,8), cmap='binary’, interpolation='nearest’)
    ax.text( 0.05, 0.05, str(y_train[i]),
    transform=ax.transAxes, color='blue')
    ax.set_xticks([])
    ax.set_yticks([])

(1) 多類別分類

手寫數字有0-9十類,但手頭上只有二分類估計器(比如像支撐向量機)怎麼用呢?我們可以採取以下策略處理:

  • 一對一(One vs One,OvO):一個分類器用來處理數字0和數字1,一個用來處理數字0和數字2,一個用來處理數字1和2,以此類推。N個類需要N(N-1)/2個分類器。
  • 一對其他(One vs All,OvA):訓練10個二分類器,每一個對應一個數字,第一個分類「1」和「非1」,第二個分類「2」和「非2」,以此類推。N個類需要N個分類器。

① OneVsOneClassifier

考慮一個具體天氣多分類問題,天氣可以是晴天、陰天和雨天,在OvO中,三個分類器為f1、f2和f3。

  • f1負責區分橙色和綠色樣本
  • f2負責區分橙色和紫色樣本
  • f3負責區分綠色和紫色樣本

在下圖的例子中,f1和f2都預測為橙色,f3預測為紫色。根據多數原則得到的結合預測為橙色,如下圖所示。

回到數字分類問題上,程式碼及結果如下:

from sklearn.multiclass import OneVsOneClassifier
from sklearn.linear_model import LogisticRegression
ovo_lr = OneVsOneClassifier( LogisticRegression(solver='lbfgs', max_iter=200) )
ovo_lr.fit( X_train, y_train )
OnevsOneClassifier(estimator=LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,intercept_scaling=1, max_iter=200, multi_class=‘warn’,n_jobs=None, penalty='12', random_state=None, solver='lbfgs’,tol=0.0001, verbose=6, warm_start=False),n_jobs=None)

10*9/2=45,10類總共45個OvO分類器。

print( len(ovo_lr.estimators_) )        
ovo_lr.estimators_

結果如下:

45

(LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,intercept_scaling=1, max_iter=200, multi_class='warn',n_jobs=None, penalty='12', random_state=None, solver='lbfgs',tol=60.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=200, multi_class='warn', n_jobs=None, penalty='l2', random_state=None, solver='lbfgs',tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=200, multi_class='warn', n_jobs=None, penalty='12', random_state=None, solver='lbfgs', tol=60.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=200, multi_class='warn', n_jobs=None, penalty="12", random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
...

訓練集分類全對,測試集準確率98%。

print ( “OvO LR - Accuracy (Train): %.4g" % metrics.accuracy_score(y_train, ovo_Ir.predict(X_train)) )
print ( "OvO LR - Accuracy (Test): %.4g" % metrics.accuracy_score(y_test, ovo_lr.predict(X_test}) )
# 執行結果
OvO LR - Accuracy (Train): 1
OvO LR - Accuracy (Test): 0.9806

② OneVsRestClassifier

在OvA中,把資料分成“某個”和“其他”

  • 圖一,某個=橙色,其他=綠色和紫色
  • 圖二,某個=綠色,其他=橙色和紫色
  • 圖三,某個=紫色,其他=橙色和綠色

三分類分解成三個二分類,對應的分類器為f1、f2和f3。

  • f1預測負類,即預測綠色和紫色
  • f2預測負類,即預測橙色和紫色
  • f3預測正類,即預測紫色

三個分類器都預測了紫色,根據多數原則得到的預測是紫色,即陰天。

回到數字分類問題上,程式碼和結果如下:

from sklearn.multiclass import OneVsRestClassifier
ova_lr = OneVsRestClassifier( LogisticRegression(solver='lbfgs', max_iter=800) )
ova_lr.fit( X_train, y_train )
OnevsRestClassifier(estimator=LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=800, multi_class=‘warn’, n_jobs=None, penalty='12', random_state=None, solver='lbfgs’, tol=0.0001, verbose=6, warm_start=False), n_jobs=None)

10類總共10個OvA分類器。

print( len(ova_lr.estimators_) )        
ova_lr.estimators_

結果如下:

10

[LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=800, multi_class='warn', n_jobs=None, penalty='12', random_state=None, solver='lbfgs',tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=800, multi_class='warn', n_jobs=None, penalty='12', random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=800, multi_class=‘warn',
n_jobs=None, penalty='12', random_state=None, solver="lbfgs',
tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=800, multi_class='warn', n_jobs=None, penalty='12', random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
...

訓練集準確率幾乎100%,測試集準確率96%。程式碼與結果如下:

print ( “OvA LR - Accuracy (Train): %.4g" % metrics.accuracy_score(y_train, ova_Ir.predict(X_train)) )
print ( "OvA LR - Accuracy (Test): %.4g" % metrics.accuracy_score(y_test, ova_lr.predict(X_test}) )
OvA LR - Accuracy (Train): 6.9993
OvA LR - Accuracy (Test}: 6.9639

(2) 多標籤分類

到目前為止,所有的樣例都總是被分配到僅一個類。有些情況下,你也許想讓分類器給一個樣例輸出多個類別。在無人駕駛的應用中,在下圖識別出有車和指示牌,沒有交通燈和人。

物體識別是一個複雜的深度學習問題,我們在這裡暫且不深入探討。我們先看一個簡單點的例子,在手寫數字的例子上,我們特意為每個數字設計了兩個標籤:

  • 標籤1:奇數、偶數
  • 標籤2:小於等於4,大於4

我們構建多標籤y_train_multilabel,程式碼如下(OneVsRestClassifier也可以用來做多標籤分類):

from sklearn.multiclass import OneVsRestClassifier                 
y_train_multilabel = np.c_[y_train%2==0, y_train<=4 ]        
print(y_train_multilabel)
[[ True True] [False False] [False False] 
... 
[False False] [False False] [False False]]

看下圖訓練集第1和2個圖片是數字4和5,對應上面兩種標籤結果為:

  • [True True]:4是偶數,小於等於4
  • [False False]:5不是偶數,大於4

我們這次用y_train_multilabel來訓練模型。程式碼如下

ova_ml = OneVsRestClassifier( LogisticRegression(solver='lbfgs', max_iter=800) )
ova_ml.fit( X_train, y_train_multilabel )
# 執行結果
OnevsRestClassifier(estimator=LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=800, multi_class=‘warn’, n_jobs=None, penalty='12', random_state=None, solver='lbfgs', tol=0.0001, verbose=6, warm_start=False), n_jobs=None)

有兩個估計器,每個對應一個標籤。

print( len(ova_ml.estimators_) )        
ova_ml.estimators_

執行結果如下:

2

[LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=800, multi_class=‘warn', n_jobs=None, penalty='12°, random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False),

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=800, multi_class='warn', n_jobs=None, penalty='l2', random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False) ]

展示一下測試集上100張圖片。

fig, axes = plt.subplots( 10, 10, figsize=(8, 8) )
fig.subplots_adjust( hspace=0.1, wspace=0.1 )

for i, ax in enumerate( axes.flat ):
    ax.imshow( X_test[i,:].reshape(8,8), cmap='binary', interpolation='nearest')
    ax.text( 6.05, 0.05, str(y_test[i]), transform=ax.transAxes, color='blue')
    ax.set_xticks([])
    ax.set_yticks([])

第一張圖片是數字2,它是偶數(標籤1為true),小於等於4(標籤2為true)。

print( y_test[:1] )        
print( ova_ml.predict(X_test[:1,:]) )
[2]
[[1 1]]

4.3 Multioutput 估計器

sklearn.multioutput可以處理多輸出(multi-output)的分類問題。

多輸出分類是多標籤分類的泛化,在這裡每一個標籤可以是多類別(大於兩個類別)的。一個例子就是預測圖片每一個畫素(標籤)的畫素值是多少(從0到255的256個類別)。

Multioutput估計器有兩個:

  • MultiOutputRegressor:多輸出迴歸
  • MultiOutputClassifier:多輸出分類

這裡我們只關注多輸出分類。

(1) MultiOutputClassifier

首先引入MultiOutputClassifier和RandomForestClassifier。

from sklearn.multioutput import MultiOutputClassifier
from sklearn.ensemble import RandomForestClassifier

在手寫數字的例子上,我們也為特意每個數字設計了多標籤而且每個標籤的類別都大於二。

  • 標籤1:小於等於4,4和7之間,大於等於7(三類)
  • 標籤2:數字本身(十類)

程式碼如下:

y_train_1st = y_train.copy()
y_train_1st[ y_train<=4 ] = 0
y_train_1st[ np.logical_and{y_train>4, y_train<7) ] = 1
y_train_ist[ y_train>=7 ] = 2

y_train_multioutput = np.c_[y_train_1st, y_train]
y_train_multioutput
# 執行結果
array( [[0, 4],
        [1, 5],
        [2, 7],
        [1, 5],
        [2, 9],
        [2, 9]])

用含有100棵決策樹的隨機森林來解決這個多輸入分類問題。

MO = MultiOutputClassifier( RandomForestClassifier(n_estimators=100) )
MO.fit( X_train, y_train_multioutput )
# 結果
MultiOutputClassifier(estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini', max_depth=None, max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=None, oob_score=False, random_state=None, verbose=0, warm_start=False), n_jobs=None)

看看這個模型在測試集前五張照片上的預測。

MO.predict( X_test[:5,:] )
array([[0, 2],[0, 2],[0, 0],[2, 9],[1, 5]])

這個ndarray第一列是標籤1的類別,第二列是標籤2的類別。預測結果是這五張照片分別顯示數字2、2、0、9、5(標籤2),它們前三個數2、2、0都小於等於4(標籤1第一類),第四個數9大於等於7(標籤1第二類),而第五個數5在4和7之間(標籤1第三類)。

再看看真實標籤。

y_test_1st = y_test.copy()        
y_test_1st[ y_test<=4 ] = 0        
y_test_1st[ np.logical_and(y_test>4, y_test<7) ] = 1        
y_test_1st[ y_test>=7 ] = 2                 
y_test_multioutput = np.c_[ y_test_1st, y_test ]                 
y_test_multioutput[:5]
array([[0, 2],[0, 2],[0, 0],[2, 9],[1, 5]])

對比參考結果標籤,模型預測的結果還是很不錯的。

4.4 Model Selection 估計器

模型選擇(Model Selction)在機器學習非常重要,它主要用於評估模型表現,常見的Model Selection估計器有以下幾個:

  • cross_validate:評估交叉驗證的結果。
  • learning_curve:構建與繪製學習曲線。
  • GridSearchCV:用交叉驗證從超引數候選網格中搜索出最佳超引數。
  • RandomizedSearchCV:用交叉驗證從一組隨機超引數搜尋出最佳超引數。

這裡我們只關注調節超引數的兩個估計器,即GridSearchCVRandomizedSearchCV。我們先回顧一下交叉驗證(更詳細的講解請檢視ShowMeAI文章 圖解機器學習 | 模型評估方法與準則)。

(1) 交叉驗證

K-折交叉驗證(K-fold cross validation set),指的是把整個資料集平均但隨機分成K份,每份大概包含m/K個數據(m 是總資料數)。

在這K份,每次選K-1份作為訓練集擬合引數,在剩下1份驗證集上進行評估計算。由於遍歷了這K份資料,因此該操作稱為交叉驗證。操作如下圖所示

下圖展示了兩個調參的估計器:「網格搜尋」和「隨機搜尋」。

網格搜尋調參:引數1在[1,10,100,1000]中取值,引數2在[0.01, 0.1, 1 10] 中取值,注意並不是等間距取值。模型在所有16組超引數上實驗,選取交叉驗證誤差最小的引數。

隨機搜尋調參:根據指定分佈隨機搜尋,可以選擇獨立於引數個數,比如log(引數1)服從0到3的均勻分佈,log(引數2)服從-2到1的均勻分佈。

應用方式與參考程式碼如下:

from time import time
from scipy.stats import randint
from sklearn.model_selection import GridSearchCv
from sklearn.model_selection import RandomizedSearchcCv
from sklearn.ensemble import RandomForestClassifier

X, y = digits.data, digits.target
RFC = RandomForestClassifier(n_estimators=20)

# 隨機搜尋/Randomized Search
param_dist = {  "max_depth": [3, 5],
                                "max_features": randint(1, 11),
                                "min_samples_split": randint(2, 11),
                                "criterion": ["gini", "entropy"]}
n_iter_search = 20
random_search = RandomizedSearchCv( RFC, param_distributions=param_dist, n_iter=n_iter_search, cv=5 )}
start = time()
random_search.fit(X, y)
print("RandomizedSearchCv took %.2f seconds for %d candidates,parameter settings." % ((time() - start), n_iter_search))
print( random_search.best_params_ )
print( random_search.best_score_ )

# 網格搜尋/Grid Search
param_grid = {  "max_depth": [3, 5],
                                "max_features": [1, 3, 10],
                                "min_samples_ split": [2, 3, 10],
                                "criterion": ["gini", "entropy"]}
grid_search = GridSearchCV( RF, param_grid=param_grid, cv=5 )
start = time()
grid_search.fit(X, y)

print("\nGridSearchcv took %.2f seconds for %d candidate parameter settings." % (time() - start, len(grid_search.cv_results_['params'])))
print( grid_search.best_params_ )
print( grid_search.best_score_ )

輸出結果如下:

RandomizedSearchCv took 3.73 seconds for 20 candidates parameter settings.
{'criterion': 'entropy', '*max_depth': 5, 'max_features': 6, 'min_samples_split': 4}
0.8898163606010017

GridSearchCV took 2.30 seconds for 36 candidate parameter settings.
{'criterion': 'entropy', 'max_depth': 5, 'max_features': 10, 'min_samples_ split': 10}
0.841402337228714S5

這裡我們對程式碼做一個解釋:

  • 前5行引入相應工具庫。
  • 第7-8行準備好資料X和y,建立一個含20個決策樹的隨機森林模型。
  • 第10-14和23-27行為對隨機森林的超引數「最大樹深、最多特徵數、最小可分裂樣本數、分裂標準」構建候選引數分佈與引數網格。
  • 第15-18行是執行隨機搜尋
  • 第18-30行是執行網格搜尋

執行結果裡:

  • 第一行輸出每種追蹤法執行的多少次和花的時間。
  • 第二行輸出最佳超引數的組合。
  • 第三行輸出最高得分。

在本例中,隨機搜尋網格搜尋用更短時間內找到一組超引數,獲得了更高的得分。

4.5 Pipeline 估計器

Pipeline估計器又叫流水線,把各種估計器串聯(Pipeline)或並聯(FeatureUnion)的方式組成一條龍服務。用好了它真的能大大提高效率。

(1) Pipeline

Pipeline將若干個估計器按順序連在一起,比如:特徵提取 → 降維 → 擬合 → 預測

Pipeline的屬性永遠和最後一個估計器屬性一樣:

  • 如果最後一個估計器是預測器,那麼Pipeline是預測器。
  • 如果最後一個估計器是轉換器,那麼Pipeline是轉換器。

下面是一個簡單示例,使用Pipeline來完成「填補缺失值-標準化」這兩步的。我們先構建含缺失值NaN的資料X。

X = np.array([[56,40,30,5,7,10,9,np.NaN,12],
              [1.68,1.83,1.77,np.NaN,1.9,1.65,1.88,np.NaN,1.75]])
X = np.transpose(X)

我們用以下流程元件構建Pipeline:

  • 處理缺失值的轉換器SimpleImputer。
  • 做規劃化的轉換器MinMaxScaler。
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import MinMaxScaler

pipe = Pipeline([
                ('impute', SimpleImputer(missing_values=np.nan, strategy='mean')),
                ('normalize', MinMaxScaler())])

第5-7行建立了流水線,使用方式非常簡單,在Pipeline()裡輸入(名稱,估計器)這個元組構建的流水線列表。在本例中SimpleImputer起名叫impute,MinMaxScaler起名叫normalize。

因為最後一個估計器是轉換器,因此pipeline也是個轉換器。下面我們來執行一下,我們發現值都被填滿了,而且兩列也被標準化了。

X_proc = pipe.fit_transform( X )

來驗證上面流水線的引數,我們可以按順序來執行這兩個轉換器,結果是一樣的。

X_impute = SimpleImputer(missing values=np.nan, strategy='mean').fit_transform( X )
X_impute
# 執行結果
array( [[50, 1.68],
        [40, 1.83],
        [30, 1.77],
        [5, 1.78],
        [7, 1.9 ],
        [10, 1.65],
        [9, 1.88],
        [20.375, 1.78],
        [12, 1.75 ]])
X_normalize = MinMaxScaler().fit_transform( X_impute )
X_normalize

執行結果

array( [[1., 0.12 ],
        [0.77777778, 0.72],
        [0.55555556, 6.48],
        [0.52, 1],
        [0.04444444, 1.],
        [0.11111111, 9.],
        [0.08888889, 6.92],
        [0.34166667, 6.52],
        [0.15555556, 0.4 ]])

(2) FeatureUnion

如果我們想在一個節點同時執行幾個估計器,我們可用FeatureUnion。在下面的例子中,我們首先建立一個DataFrame資料,它有如下特點:

  • 前兩列欄位「智力IQ」和「脾氣temper」都是類別型變數。
  • 後兩列欄位「收入income」和「身高height」都是數值型變數。
  • 每列中都有缺失值。
d= { 'IQ' : ['high','avg','avg','low', high', avg', 'high', 'high',None],
'temper' : ['good', None,'good', 'bad', 'bad','bad', 'bad', None, 'bad'],
'income' : [50,40,30,5,7,10,9,np.NaN,12],
'height' : [1.68,1.83,1.77,np.NaN,1.9,1.65,1.88,np.NaN,1.75]}

X = pd.DataFrame( d )
X

結果如下:

我們現在按下列步驟來清洗資料。

  • 對類別型變數:獲取資料 → 中位數填充 → 獨熱編碼
  • 對數值型變數:獲取資料 → 均值填充 → 標準化

上面兩步是並行進行的。

首先我們自己定義一個從DataFrame裡面獲取資料列的類,起名叫DataFrameSelector

from sklearn.base import BaseEstimator, TransformerMixin

class DataFrameSelector( BaseEstimator, TransformerMixin ):
        def _init_( self, attribute_names ):
                self.attribute_names = attribute_names
        def fit( self, X, y=None ):
                return self
        def transform( self, X ):
                return X[self.attribute_names].values

上述程式碼在transform函式中,我們將輸入的DataFrame X根據屬性名稱來獲取其值。

接下來建立流水線full_pipe,它並聯著兩個流水線

  • categorical_pipe處理分型別變數

    • DataFrameSelector用來獲取
    • SimpleImputer用出現最多的值來填充None
    • OneHotEncoder來編碼返回非稀疏矩陣
  • numeric_pipe處理數值型變數

    • DataFrameSelector用來獲取
    • SimpleImputer用均值來填充NaN
    • normalize來規範化數值

程式碼如下:

from sklearn.pipeline import Pipeline
from sklearn.pipeline import FeatureUnion
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import OneHotEncoder

categorical features = ['IQ', 'temper']
numeric_features = ['income', 'height']

categorical pipe = Pipeline([
        ('select', DataFrameSelector(categorical_features)),
        ('impute', SimpleImputer(missing values=None, strategy='most_frequent')),
        ('one_hot_encode', OneHotEncoder(sparse=False))])

numeric_pipe = Pipeline([
        ('select', DataFrameSelector(numeric_features)),
        ('impute', SimpleImputer(missing values=np.nan, strategy='mean')),
        ('normalize', MinMaxScaler())])

full_pipe = FeatureUnion( transformer_list=[
        ('numeric_pipe', numeric_pipe),
        ('categorical_pipe', categorical_pipe)])

我們列印結果如下:

X_proc = full_pipe.fit_transform( X )        
print( X_proc )
[[1. 0.12 0. 1. 0. 0. 1. ] 
[0.77777778 0.72 1. 0. 0. 1. 0. ] 
[0.55555556 0.48 1. 0. 0. 0. 1. ] 
[0. 0.52 0. 0. 1. 1. 0. ] 
[0.04444444 1. 0. 1. 0. 1. 0. ] 
[0.11111111 0.   1. 0. 0. 1. 0. ] 
[0.08888889 0.92 0. 1. 0. 1. 0. ] 
[0.34166667 0.52 0. 1. 0. 1. 0. ] 
[0.15555556 0.4  0. 1. 0. 1. 0. ]]

5.總結

下面我們對上面講解到的sklearn工具庫應用知識做一個總結。

5.1 SKLearn五大原則

SKLearn的設計下,它的主要API遵循五大原則

(1) 一致性

所有物件的介面一致且簡單,在「估計器」中

  • 建立:model = Constructor(hyperparam)
  • 擬參:
    • 有監督學習:model.fit(X_train, y_train)
    • 無監督學習:model.fit(X_train)

在「預測器」中

  • 有監督學習裡預測標籤:y_pred = model.predict(X_test)
  • 無監督學習裡識別模式:idx_pred = model.predict( Xtest)

在「轉換器」中

  • 建立:trm = Constructor(hyperparam)
  • 獲參:trm.fit(X_train)
  • 轉換:X_trm = trm.transform(X_train)

(2) 可檢驗

所有估計器裡設定的超引數和學到的引數都可以通過例項的變數直接訪問來檢驗其值,區別是超引數的名稱最後沒有下劃線_,而引數的名稱最後有下劃線_。舉例如下:

  • 通例:model.hyperparameter
  • 特例:SVC.kernel
  • 通例:model.parameter_
  • 特例:SVC.support_vectors_

(3) 標準類

SKLearn模型接受的資料集的格式只能是「Numpy陣列」和「Scipy稀疏矩陣」。超引數的格式只能是「字元」和「數值」。
不接受其他的類!

(4) 可組成

模組都能重複「連在一起」或「並在一起」使用,比如兩種形式流水線(pipeline)

  • 任意轉換器序列
  • 任意轉換器序列+估計器

(5) 有預設

SKLearn給大多超引數提供了合理的預設值,大大降低了建模的難度。

5.2 SKLearn框架流程

sklearn的建模應用流程框架大概如下:

(1) 確定任務

是「有監督」的分類或迴歸?還是「無監督」的聚類或降維?確定好後基本就能知道用Sklearn裡哪些模型了。

(2) 資料預處理

這步最繁瑣,要處理缺失值、異常值;要編碼類別型變數;要正規化或標準化數值型變數,等等。但是有了Pipeline神器一切變得簡單高效。

(3) 訓練和評估

這步最簡單,訓練用估計器fit()先擬合,評估用預測器predict()來評估。

(4) 選擇模型

啟動ModelSelection估計器裡的GridSearchCV和RandomizedSearchCV,選擇得分最高的那組超引數(即模型)。

參考資料

ShowMeAI系列教程推薦

相關文章推薦