非常詳細的sklearn介紹
Sklearn (全稱 Scikit-Learn) 是基於 Python 語言的機器學習工具。它建立在 NumPy, SciPy, Pandas 和 Matplotlib 之上,里面的 API 的設計非常好,所有對象的接口簡單,很適合新手上路。
在 Sklearn 里面有六大任務模塊:分別是分類、回歸、聚類、降維、模型選擇和預處理,如下圖從其官網的截屏。
要使用上述六大模塊的方法,可以用以下的偽代碼,注意 import 后面我用的都是一些通用名稱,如 SomeClassifier, SomeRegressor, SomeModel,具體化的名稱由具體問題而定,比如
-
SomeClassifier = RandomForestClassifier
-
SomeRegressor = LinearRegression
-
SomeModel = KMeans, PCA
-
SomeModel = GridSearchCV, OneHotEncoder
上面具體化的例子分別是隨機森林分類器、線性回歸器、K 均值聚類、主成分分析、網格追蹤法、獨熱編碼。
-
from sklearn import SomeClassifier
-
from sklearn.linear_model import SomeClassifier
-
from sklearn.ensemble import SomeClassifier
-
from sklearn import SomeRegressor
-
from sklearn.linear_model import SomeRegressor
-
from sklearn.ensemble import SomeRegressor
from sklearn.cluster import SomeModel
from sklearn.decomposition import SomeModel
from sklearn.model_selection import SomeModel
from sklearn.preprocessing import SomeModel
SomeClassifier, SomeRegressor, SomeModel 其實都叫做估計器 (estimator),就像 Python 里「萬物皆對象」那樣,Sklearn 里「萬物皆估計器」。
此外,Sklearn 里面還有很多自帶數據集供,引入它們的偽代碼如下。
from sklearn.datasets import SomeData
本貼我們用以下思路來講解:
-
第一章介紹機器學習,從定義出發引出機器學習四要素:數據、任務、性能度量和模型。加這一章的原因是不把機器學習相關概念弄清楚之后很難完全弄明白 Sklearn。
-
第二章介紹 Sklearn,從其 API 設計原理出發分析其五大特點:一致性、可檢驗、標准類、可組合和默認值。最后再分析 Sklearn 里面自帶數據以及儲存格式。
-
第三章介紹 Sklearn 里面的三大核心 API,包括估計器、預測器和轉換器。這一章的內容最重要,幾乎所有模型都會用到這三大 API。
-
第四章介紹 Sklearn 里面的高級 API,即元估計器,有可以大大簡化代碼量的流水線 (Pipeline 估計器),有集成模型 (Ensemble 估計器)、有多類別-多標簽-多輸出分類模型 (Multiclass 和 Multioutput 估計器) 和模型選擇工具 (Model Selection 估計器)。
本帖目錄如下:
很多介紹 Sklearn 的文章是不會像我這么詳細介紹「機器學習」里的概念的,但是不弄清出這些概念或術語,學 Sklearn 只是走馬觀花,只看到表面,抓不到實質。
建議認真仔細讀第一章!
建議認真仔細讀第一章!
建議認真仔細讀第一章!
1.1
定義和組成元素
什么是機器學習?字面上來講就是 (人用) 計算機來學習。談起機器學習就一定要提起湯姆米切爾 (Tom M.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,翻譯成中文就是機器 (計算機程序) 和學習,再把上面英譯中:
假設用性能度量 P 來評估機器在某類任務 T 的性能,若該機器通利用經驗 E 在任務 T 中改善其性能 P,那么可以說機器對經驗 E 進行了學習。
在該定義中,除了核心詞機器和學習,還有關鍵詞經驗 E,性能度量 P 和任務 T。在計算機系統中,通常經驗 E 是以數據 D 的形式存在,而機器學習就是給定不同的任務 T 從數據中產生模型 M,模型 M 的好壞就用性能度量 P 來評估。
由上述機器學習的定義可知機器學習包含四個元素
-
數據 (Data)
-
任務 (Task)
-
性能度量 (Quality Metric)
-
模型 (Model)
下面四小節分別介紹數據、任務、性能度量和模型。
1.2
數據
數據 (data) 是經驗的另一種說法,也是信息的載體。數據可分為
-
結構化數據和非結構化數據 (按數據具體類型划分)
-
原始數據和加工數據 (按數據表達形式划分)
-
樣本內數據和樣本外數據 (按數據統計性質划分)
結構化數據 (structured data) 是由二維表結構來邏輯表達和實現的數據。非結構化數據是沒有預定義的數據,不便用數據庫二維表來表現的數據。
非結構化數據包括圖片,文字,語音和視屏等如下圖。
對於以上的非結構數據,相關應用實例有
-
深度學習的卷積神經網絡 (convolutional neural network, CNN) 對圖像數據做人臉識別或物體分類
-
深度學習的循環神經網絡 (recurrent neural network, RNN) 對語音數據做語音識別或機器對話,對文字數據做文本生成或閱讀理解
-
增強學習的阿爾法狗 (AlphaGo) 對棋譜數據學習無數遍最終打敗了圍棋世界冠軍李世石和柯潔
計算機追根到底還是只能最有效率的處理數值型的結構化數據,如何從原始數據加工成計算機可應用的數據會在后面講明。
機器學習模型主要使用的是結構化數據,即二維的數據表。非結構化數據可以轉換成結構化數據,比如把
-
圖像類數據里像素張量重塑成一維數組
-
文本類數據用獨熱編碼轉成二維數組
對於結構化數據,我們用勒布朗詹姆斯 (Lebron James) 四場比賽的數據舉例。
下面術語大家在深入了解機器學習前一定要弄清楚:
-
每行的記錄 (這是一場比賽詹姆斯的個人統計) ,稱為一個示例 (instance)
-
反映對象在某方面的性質,例如得分,籃板,助攻,稱為特征 (feature) 或輸入(input)
-
特征上的取值,例如「示例 1」對應的 27, 10, 12 稱為特征值 (feature value)
-
關於示例結果的信息,例如贏,稱為標簽 (label) 或輸出 (output)
-
包含標簽信息的示例,則稱為樣例 (example),即樣例 = (特征, 標簽)
-
從數據中學得模型的過程稱為學習 (learning) 或訓練 (training)
-
在訓練數據中,每個樣例稱為訓練樣例 (training example),整個集合稱為訓練集(training set)
計算機處理數值型的結構型數據最有效率,但是現實世界到處出是原始數據,分為兩類
-
非結構數據比如圖片和文字型數據 (情況一)
-
結構型數據的分類型變量 (情況二)
拿情況一的圖片為例,通過特定函數 imread 將彩色圖片用 RGB 像素表示出來,再按紅綠藍的順序,將所有像素排成一個數值列向量 (column vector),而計算機可以接受這樣的輸入。具體轉換過程見下圖。
推特 (twitter) 的每條推文 (tweet) 規定只能發 280 個字符。在編碼推文時,將 280 個字符的序列用獨熱編碼 (one-hot encoding) 到包含 128 個字符的 ASCII 表,如下所示。
這樣,每條推文都可以編碼為 2 維張量形狀 (280, 128),比如一條 tweet 是 “I love python :)”,這句話映射到 ASCII 表變成:
如果收集到 1 百萬條推文,那么整個數據集的形狀為 (1000000, 280, 128)。傳統機器學習的對率回歸可以來做情感分析。
籃球比賽結果非輸即贏,是一個二類 (binary class) 變量
二類變量用「0-1編碼」,比如比賽結果= {贏, 輸} 表示成 y= [1 0 0 1],1 代表贏,0 代表輸。
而足球比賽結果是有贏、平、輸三種,是一個多類 (multi-class) 變量。
多類變量分別用 0, 1, 2 來表示,那么 y = [0 1 0 2]。但更常見的是用獨熱編碼 (one-hot encoding),即
在統計中,把研究對象的全體稱為總體 (population),而把組成總體的各個元素稱為個體,把從總體中抽取的若干個體稱為樣本 (sample)。
舉個調查中國男性平均身高的例子:
-
全國的男性就是總體
-
每個男性是個體
普查所有男性金錢花費和時間成本太高,通常會抽取若干男性作為樣本,計算樣本里的男性平均身高作為總體里的所有男性平均身高的推理 (inference)。
統計學中做的事情就是用樣本數據的統計 (statistics) 來推出總體數據的參數 (parameter)。樣本數據也叫做樣本內數據,除樣本內數據之外的總體數據叫做樣本外數據。
在機器學習中,樣本內和樣本外數據的定義稍微有些不同,如下圖:
樣本內數據是用來訓練模型的數據,也叫訓練數據。它們是已知的,可計算統計的。
樣本外數據是未來的沒見過的新數據。它們是未知的,不可計算統計的。
機器學習在樣本內數據訓練模型用來預測:
-
樣本內預測:根據訓練模型對樣本內數據進行預測,可與已知標簽比較來評估模型表現
-
樣本外預測:根據訓練模型對樣本外數據進行預測,不能與未知的標簽比較
1.3
任務
根據學習的任務模式 (訓練數據是否有標簽),機器學習可分為四大類:
-
有監督學習 (有標簽)
-
無監督學習 (無標簽)
-
半監督學習 (有部分標簽)
-
增強學習 (有評級標簽)
深度學習只是一種方法,而不是任務模式,因此與上面四類不屬於同一個維度,但是深度學習與它們可以疊加成:深度有監督學習、深度非監督學習、深度半監督學習和深度增強學習。遷移學習也是一種方法,也可以分類為有監督遷移學習、非監督遷移學習、半監督遷移學習和增強遷移學習。
下圖畫出機器學習各類之間的關系。
由於 Sklearn 里面模型主要處理「有監督學習」和「無監督學習」兩類,我們接下來也只討論這兩類。
有監督學習 (supervised learning) 利用輸入數據及其對應標簽來訓練模型。這種學習方法類似學生通過研究問題和參考答案來學習,在掌握問題和答案之間的對應關系后,學生可自己給出相似新問題的答案了。
在有監督學習中,數據 = (特征,標簽),而其主要任務是分類和回歸。以上述詹姆斯的個人統計為例。
如果預測的是離散值 (discrete value),例如比賽結果贏或輸,此類學習任務稱為分類 (classification)。
如果預測的是連續值 (continuous value),例如詹姆斯效率 65.1, 70.3 等等,此類學習任務稱為回歸 (regression)。
無監督學習 (unsupervised learning) 是找出輸入數據的模式。比如,它可以根據電影的各種特征做聚類,用這種方法收集數據為電影推薦系統提供標簽。此外無監督學習還可以降低數據的維度,它可以幫助我們更好的理解數據。
在無監督學習中,數據 = (特征,)。
除了根據詹姆斯個人統計來預測騎士隊輸贏或者個人效率值外,我們還可以對該數據做聚類 (clustering),即將訓練集中的數據分成若干組,每組成為一個簇 (cluster)。
假設聚類方法將數據聚成二個簇 A 和 B,如下圖
后來發現簇 A 代表贏,簇 B 代表輸。聚類的用處就是可以找到一個潛在的原因來解釋為什么樣例 1 和 3 可以贏球。難道真的是只要詹姆斯三雙就可以贏球?
注:下面對降維的理解不是那么嚴謹,只為了讓小白對降維大概有個概念。
詹姆斯完整統計數據還有搶斷、蓋帽和犯規,但這些對預測比賽輸贏、效率值都沒什么用,因此可以通過降維的方法將其去除。
1.4
性能度量
回歸和分類任務中最常見的誤差函數以及一些有用的性能度量如下。
回歸任務的誤差函數估量在數據集 D 上模型的連續型預測值 h(x) 與連續型真實值 y 的距離,h(x) 和 y 可以取任意實數。誤差函數是一個非負實值函數,通常使用 ED[h] 來表示。圖表展示如下。
分類任務的誤差函數估量在數據集 D 上模型的離散型預測值 h(x) 與離散型真實值 y 的不一致程度,慣例是 y 和 h(x) 取±1,比如正類取 1 負類取 -1。圖表展示如下。
除上述損失函數之外,分類任務還有很多其他有用的性能度量。
錯誤率:分類錯誤的樣本數占樣本總數的比例稱為錯誤率 (error rate),相應的分類正確的樣本數占樣本總數的比例稱為精度 (accuracy)。在 10 個樣本中有 2 個樣本分類錯誤,則錯誤率為 20%,而精度為 80%。
查准率和查全率:錯誤率和精度雖然常用,但是不能滿足所有任務需求。假定用訓練好的模型預測騎士贏球,顯然,錯誤率衡量了多少比賽實際是贏球但預測成輸球。但是若我們關心的是“預測出的比賽中有多少是贏球”,或“贏球的比賽中有多少被預測出了”,那么錯誤率這個單一指標顯然就不夠用了,這時需要引進更為細分的性能度量,即查准率 (precision) 和查全率 (recall)。
其他概念比如混淆矩陣、ROC、AUC 我們再下帖的實例用到時再細講。
1.5
模型
有監督模型如下圖所示:
無監督模型包括各種聚類分析 (KMeans, DBSCAN)、主成分分析 (PCA)、獨立成分分析 (ICA)、隱含狄利克雷分配 (LDA) 等等。
如要了解更多機器學習的細節,請參考本帖次條的〖機器學習帖子匯總〗,里面是我寫的所有關於「有監督學習」的內容。
費了這么多時間來介紹機器學習,無非就是讓大家在使用 Sklearn 時知道自己在做什么,知道那些概念在說什么就夠了。
Sklearn 和之前討論的 NumPy, SciPy, Pandas, Matplotlib 相似,就是一個處理特殊任務的包,Sklearn 就是處理機器學習 (有監督學習和無監督學習) 的包,更精確的說,它里面有六個任務模塊和一個數據引入模塊:
-
有監督學習的分類任務
-
有監督學習的回歸任務
-
無監督學習的聚類任務
-
無監督學習的降維任務
-
數據預處理任務
-
模型選擇任務
-
數據引入
本節就來看看 Sklearn 里數據格式和自帶數據集。
2.1
數據格式
在 Sklean 里,模型能即用的數據有兩種形式:
-
Numpy 二維數組 (ndarray) 的稠密數據 (dense data),通常都是這種格式。
-
SciPy 矩陣 (scipy.sparse.matrix) 的稀疏數據 (sparse data),比如文本分析每個單詞 (字典有 100000 個詞) 做獨熱編碼得到矩陣有很多 0,這時用 ndarray 就不合適了,太耗內存。
上述數據在機器學習中通常用符號 X 表示,是模型自變量。它的大小 = [樣本數, 特征數],圖下圖所示。該房屋數據有 21000 條包括平方英尺,卧室數,樓層,日期,翻新年份等等 21 欄。該數據形狀為 [21000, 21]
有監督學習除了需要特征 X 還需要標簽 y,而 y 通常就是 Numpy 一維數組,無監督學習沒有 y。
2.2
自帶數據集
Sklearn 里面有很多自帶數據集供用戶使用。
數據集包括 150 條鳶尾花的四個特征 (萼片長/寬和花瓣長/寬) 和三個類別。在盤 Seaborn 時是從 csv 文件讀取的,本帖從 Sklearn 里面的 datasets 模塊中引入,代碼如下:
-
from sklearn.datasets import load_iris
-
iris = load_iris()
數據是以「字典」格式存儲的,看看 iris 的鍵有哪些。
iris.keys()
-
dict_keys([ 'data', 'target',
-
'target_names', 'DESCR',
-
'feature_names', 'filename'])
鍵里面的名稱解釋如下:
-
data:特征值 (數組)
-
target:標簽值 (數組)
-
target_names:標簽 (列表)
-
DESCR:數據集描述
-
feature_names:特征 (列表)
-
filename:iris.csv 文件路徑
具體感受一下 iris 數據中特征的大小、名稱和前五個示例。
-
n_samples, n_features = iris.data.shape
-
print((n_samples, n_features))
-
print(iris.feature_names)
-
iris.data[ 0: 5]
-
( 150, 4)
-
-
[ 'sepal length (cm)', 'sepal width (cm)',
-
'petal length (cm)', 'petal width (cm)']
-
-
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]])
150 個樣本,4 個特征,沒毛病!再感受一下標簽的大小、名稱和全部示例。
-
print(iris.target.shape)
-
print(iris.target_names)
-
iris.target
-
( 150,)
-
-
[ 'setosa' 'versicolor' 'virginica']
-
-
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])
150 個標簽,3 類別 (分別用 0, 1, 2 數值來代表 setosa, versicolor, virginica)。
用 Pandas 的 DataFrame (將 X 和 y 合並) 和 Seaborn 的 pairplot (看每個特征之間的關系) 來用表格和圖來展示一下數據集的內容。

Pandas DataFrame
-
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))

Seaborn Pairplot
sns.pairplot( iris_data, hue='species', palette='husl' );
看完鳶尾花的 iris 數據展示后,現在來看看 Sklearn 三種引入數據形式。
-
打包好的數據:對於小數據集,用 sklearn.datasets.load_*
-
分流下載數據:對於大數據集,用
sklearn.datasets.fetch_*
-
隨機創建數據:為了快速展示,用 sklearn.datasets.make_*
上面這個星號 * 是什么意思,指的是具體文件名,敲完
-
datasets.load_<TAB>
-
datasets.fetch_<TAB>
-
datasets.make_<TAB>
點擊鍵盤上的 <TAB> 鍵就可以看到很多完整的文件名,看下面動圖就明白了。
Load 一個數字小數據集 digits?
-
digits = datasets.load_digits()
-
digits.keys()
-
dict_keys([ 'data', 'target', 'target_names',
-
'images', 'DESCR'])
Fetch 一個加州房屋大數據集 california_housing?
-
california_housing = datasets.fetch_california_housing()
-
california_housing.keys()
-
dict_keys([ 'data', 'target',
-
'feature_names', 'DESCR'])
Make 一個高斯分位數數據集 gaussian_quantile?
-
gaussian_quantiles = datasets.make_gaussian_quantiles()
-
type(gaussian_quantiles), len(gaussian_quantiles)
(tuple, 2)
Sklearn 里萬物皆估計器。估計器是個非常抽象的叫法,可把它不嚴謹的當成一個模型 (用來回歸、分類、聚類、降維),或當成一套流程 (預處理、網格最終)。
本節三大 API 其實都是估計器:
-
估計器 (estimator) 當然是估計器
-
預測器 (predictor) 是具有預測功能的估計器
-
轉換器 (transformer) 是具有轉換功能的估計器
這三句看似廢話,其實蘊藏了很多內容。其實我對第 1 點這個估計器的起名不太滿意,我覺得應該叫擬合器 (fitter) - 具有擬合功能的估計器。看完這一節你就會明白「擬合器」這種叫法更合理。
3.1
估計器
定義:任何可以基於數據集對一些參數進行估計的對象都被稱為估計器。
兩個核心點:1. 需要輸入數據,2. 可以估計參數。估計器首先被創建,然后被擬合。
創建估計器:需要設置一組超參數,比如
-
線性回歸里超參數 normalize=True
-
K 均值里超參數 n_clusters=3
在創建好的估計器 model 可以直接訪問這些超參數,用 . 符號。
-
model.normalize
-
model.n_clusters
但 model 中有很多超參數,你不可能一開始都知道要設置什么值,沒設置的用 Sklearn 會給個合理的默認值,因此新手不用擔心。
擬合估計器:需要訓練集。在有監督學習中的代碼范式為
model.fit( X_train, y_train )
在無監督學習中的代碼范式為
model.fit( X_train )
擬合之后可以訪問 model 里學到的參數,比如線性回歸里的特征前的系數 coef_,或 K 均值里聚類標簽 labels_。
-
model.coef_
-
model.labels_
說了這么多抽象的東西,現在展示有監督學習的「線性回歸」和無監督學習的「K 均值」的具體例子。
首先從 sklearn 下的 linear_model 中引入 LinearRegression,再創建估計器起名 model,設置超參數 normalize 為 True,指的在每個特征值上做標准化,這樣會加速數值運算。
-
from sklearn.linear_model import LinearRegression
-
-
-
model = LinearRegression(normalize=True)
-
model
創建完后的估計器會顯示所有的超參數,比如我們設置好的 normalize=True,其他沒設置的都是去默認值,比如 n_jobs=None 是只用一個核,你可以將其設為 2 就是兩核並行,甚至設為 -1 就是電腦里所有核並行。
自己創建一個簡單數據集 (沒有噪聲完全線性) 只為了講解估計器里面的特征。
-
x = np.arange( 10)
-
y = 2 * x + 1
-
plt.plot( x, y, 'o' );
還記得 Sklearn 里模型要求特征 X 是個兩維變量么 (樣本數×特征數)?但在本例中 X 是一維,因為我們用 np.newaxis 加一個維度,它做的事情就是把 [1, 2, 3] 轉成 [[1],[2],[3]]。再把 X 和 y 丟進 fit() 函數來擬合線性模型的參數。
-
X = x[:, np.newaxis]
-
model.fit( X, y )
擬合完后的估計器和創建完的樣子看起來一樣,但是已經用「model.param_」可以訪問到學好的參數了,展示如下。
-
print( model.coef_ )
-
print( model.intercept_ )
-
[ 2.]
-
1.0
斜率為 2,截距為 1,沒毛病。和訪問超參數時不一樣,注意訪問參數要加一個下划線 _。
首先從 sklearn 下的 cluster 中引入 KMeans,再創建估計器起名 model,設置超參數 n_cluster 為 3 (為了展示方便而我們知道用的 iris 數據集有 3 類,實際上應該選不同數量的 n_cluster,根據 elbow 圖來決定,下帖細講)。
再者,iris 數據里是有標簽 y 的,我們假裝沒有 y 才能無監督的聚類啊,要不然應該做有監督的分類的。
-
from sklearn.cluster import KMeans
-
-
-
model = KMeans( n_clusters= 3 )
-
model
創建完后的估計器會顯示所有的超參數,比如我們設置好的 n_cluster=3,其他沒設置的都是去默認值,比如 max_iter=300 是最多迭代次數為 300,算法不收斂也停了。
還記得 iris 里的特征有四個嗎 (萼片長、萼片寬、花瓣長、花瓣寬)?四維特征很難可視化,因此我們只取兩個特征 (萼片長、萼片寬) 來做聚類並且可視化結果。注意下面代碼 X = iris.data[:,0:2]。
-
X = iris.data[:, 0: 2]
-
model.fit(X)
擬合完后的估計器和創建完的樣子看起來一樣,但是已經用「model.param_」可以訪問到學好的參數了,展示如下。
-
print( model.cluster_centers_, '\n')
-
print( model.labels_, '\n' )
-
print( model.inertia_, '\n')
-
print( iris.target )
有點亂,解釋一下 KMeans 模型這幾個參數:
-
model.cluster_centers_:簇中心。三個簇那么有三個坐標。
-
model.labels_:聚類后的標簽
-
model.inertia_:所有點到對應的簇中心的距離平方和 (越小越好)
需要強調的是真實標簽 iris.label 和聚類標簽 model.labels_ 看起來差的很遠。類別 0 都一致,但是類別 1 和 2 弄反了,這是因為在 KMeans 算法里標注的類別索引和真實類別索引不一樣 (我現在也沒找到什么方法能夠讓它們一致)。
雖然上面以有監督學習的 LinearRegression 和無監督學習的 KMeans 舉例,但實際上你可以將它們替換成其他別的模型,比如有監督學習的 LogisticRegression 和無監督學習的 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
預測器
定義:預測器在估計器上做了一個延展,延展出預測的功能。
兩個核心點:1. 基於學到的參數預測,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,)
讓我們來看個有監督學習的「對率回歸」和繼續上節無監督學習的「K 均值」的例子。
首先從 sklearn 下的 linear_model 中引入 LogisticRegression,再創建估計器起名 model,設置超參數 mutli_class 為 multinomial 因為有三種鳶尾花,是個多分類問題。
接着再訓練集上擬合參數,這時估計器 model 里面已經可以訪問這些參數了。
predict
& predict_proba
對於分類問題,我們不僅想知道預測的類別是什么,有時還想知道預測該類別的信心如何。前者用 predict(),后者用 predict_proba()。
代碼如下,在測試集上比較預測標簽 y_pred 和真實標簽 y_test 發現它們完全吻合,准確率 100% (iris 數據太簡單 )。
-
y_pred = model.predict( X_test )
-
p_pred = model.predict_proba( X_test )
-
print( y_test, '\n' )
-
print( y_pred, '\n' )
-
print( p_pred )
解釋一下 p_pred - 測試集里有 30 個數據,鳶尾花有 3 類,因此 predict_proba() 生成一個 30×3 的數組,每行的概率加起來為 1。
為了驗證我們的理解,我們看 Sklearn 是不是把「每行中最大概率值對應的那一類」作為預測結果。
-
s = [ 'Class 1 Prob', 'Class 2 Prob', 'Class 3 Prob']
-
prob_DF = pd.DataFrame( p_pred, columns=s )
-
prob_DF[ 'Predicted Class'] = y_pred
-
prob_DF.head()
是的!前三行 Class 1 Prob 最大,預測是第一類;第四行 Class 2 Prob 最大,預測是第二類;第四行 Class 3 Prob 最大,預測是第三類。
score
& decision_function
預測器里還有額外的兩個函數可以使用。在分類問題中
-
score() 返回的是分類准確率
-
decision_function() 返回的是每個樣例在每個類下的分數值
-
print( model.score( X_test, y_test ) )
-
print( np.sum(y_pred==y_test)/len(y_test) )
-
1.0
-
1.0
-
decision_score = model.decision_function( X_test )
-
print( decision_score )
為了驗證我們的理解,我們看 Sklearn 是不是把「每行中最高得分值對應的那一類」作為預測結果。
-
s = [ 'Class 1 Score', 'Class 2 Score', 'Class 3 Score']
-
decision_DF = pd.DataFrame( decision_score, columns=s )
-
decision_DF[ 'Predicted Class'] = y_pred
-
decision_DF.tail()
是的!前兩行 Class 3 Score 最大,預測是第三類;后三行 Class 1 Score 最大,預測是第一類。
繼續上一節的 KMeans 模型,首先用 fit() 訓練。
再用 predict() 在測試集上預測出類別 inx_pred,和真實標簽 y_test 比對。再次強調,inx_pred 和 y_test 給三個類別的索引定義是不同的。
-
idx_pred = model.predict( X_test[:, 0: 2] )
-
print( index_pred )
-
print( y_test )
最后畫出兩幅圖 (都是在測試集上),左圖是根據聚類預測的標簽畫出散點圖,而右圖是根據真實標簽畫出散點圖,對比兩幅圖看很像,聚類的效果也不錯。
KMeans 模型里也有 score() 函數,輸出是值是它要優化的目標函數的對數。
model.score( X_test[:,0:2] )
-9.662259042197803
估計器都有 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) 數值型變量

LabelEncoder & OrdinalEncoder
LabelEncoder
和 OrdinalEncoder 都可以將字符轉成數字,但是
-
LabelEncoder
的輸入是一維,比如 1d ndarray -
OrdinalEncoder 的輸入是二維,比如 DataFrame
首先給出要編碼的列表 enc 和要解碼的列表 dec。
-
enc = [ 'win', 'draw', 'lose', 'win']
-
dec = [ 'draw', 'draw', 'win']
從 sklearn 下的 preprocessing 中引入 LabelEncoder,再創建轉換器起名 LE,不需要設置任何超參數。
-
from sklearn.preprocessing import LabelEncoder
-
LE = LabelEncoder()
-
-
-
print( LE.fit(enc) )
-
print( LE.classes_ )
-
print( LE.transform(dec) )
-
LabelEncoder()
-
[ 'draw' 'lose' 'win']
-
[ 0 0 2]
上面結果解釋如下
-
第 4 行轉換器 fit 之后還是轉換器,只不過從數據 enc 上學到了些東西
-
第 5 行的 LE.classes_ 就是學到的東西之一,系統把 'draw', 'lose', 'win' 分別賦予 0, 1, 2
-
第 6 行用學到的東西來解碼 ['draw','draw','win'] 得到 [0 0 2]
除了LabelEncoder 能編碼,OrdinalEncoder 也可以。首先從 sklearn 下的 preprocessing 中引入 OrdinalEncoder,再創建轉換器起名 OE,不需要設置任何超參數。 下面結果和上面類似,就不再多解釋了。
-
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(['draw', 'lose', 'win'], dtype=object)]
-
-
[[0.]
-
[0.]
-
[2.]]
上面這種編碼的問題是,機器學習算法會認為兩個臨近的值比兩個疏遠的值要更相似。顯然這樣不對 (比如,0 和 1 比 0 和 2 距離更近,難道 draw 和 win 比 draw 和 lose更相似?)。
要解決這個問題,一個常見的方法是給每個分類創建一個二元屬性,即獨熱編碼 (one-hot encoding)。如何用它看下段。

OneHotEncoder
獨熱編碼其實就是把一個整數用向量的形式表現。下圖就是對數字 0-9 做獨熱編碼。
轉換器 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>
上面結果解釋如下
-
第 5 行打印出編碼結果 [2 0 1 2]
-
第 6 行將其轉成獨熱形式,輸出是一個「稀疏矩陣」形式,因為實操中通常類別很多,因此就一步到位用稀疏矩陣來節省內存
想看該矩陣里具體內容,用 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.]])
和上面結果類似,不解釋了。
數據要做的最重要的轉換之一是特征縮放 (feature scaling)。當輸入的數值的量剛不同時,機器學習算法的性能都不會好。
具體來說,對於某個特征,我們有兩種方法:
-
標准化 (standardization):每個維度的特征減去該特征均值,除以該維度的標准差。
-
規范化 (normalization):每個維度的特征減去該特征最小值,除以該特征的最大值與最小值之差。

MinMaxScaler
整套轉換器「先創建再 fit 在 transform」的流程應該很清楚了。自己讀下面代碼看看是不是秒懂。唯一需要注意的就是輸入 X 要求是兩維。
-
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
牢記轉換器「先創建再 fit 在 transform」的流程就行了。
-
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 出來的均值和標准差參數,來對測試集做標准化。
Sklearn 里核心 API 接口是估計器,那高級 API 接口就是元估計器 (meta-estimator),即由很多基估計器 (base estimator) 組合成的估計器。
meta_model( base_model )
本節討論五大元估計器,分別帶集成功能的 ensemble,多分類和多標簽的 multiclass,多輸出的 multioutput,選擇模型的 model_selection,和流水線的 pipeline。
-
ensemble.BaggingClassifier
-
ensemble.VotingClassifier
-
multiclass.OneVsOneClassifier
-
multiclass.OneVsRestClassifier
-
multioutput.MultiOutputClassifier
-
model_selection.GridSearchCV
-
model_selection.RandomizedSearchCV
-
pipeline.Pipeline
在下面五節,我們會用的鳶尾花數據 iris 和數字數據 digits,還有一些自己創建的數據。
4.1
Ensemble 估計器
-
分類器統計每個子分類器的預測類別數,再用「多數投票」原則得到最終預測。
-
回歸器計算每個子回歸器的預測平均值。
最常用的 Ensemble 估計器排列如下:
-
AdaBoostClassifier
: 逐步提升分類器 -
AdaBoostRegressor
: 逐步提升回歸器 -
BaggingClassifier
: 裝袋分類器 -
BaggingRegressor
: 裝袋回歸器 -
GradientBoostingClassifier
: 梯度提升分類器 -
GradientBoostingRegressor
: 梯度提升回歸器 -
RandomForestClassifier
: 隨機森林分類器 -
RandomForestRegressor
: 隨機森林回歸器 -
VotingClassifier
: 投票分類器 -
VotingRegressor
: 投票回歸器
我們用鳶尾花數據 iris,拿
-
含同質估計器 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 )

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 )
估計器有 fit(),元估計器當然也有 fit()。在估計器那一套又可以照搬到元估計器 (起名 RF) 上了。看看 RF 里包含的估計器個數和其本身。
-
print( RF.n_estimators )
-
RF.estimators_
擬合 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): 0.9833
-
RF - Accuracy (Test): 1

VotingClassifier
和隨機森林由同質分類器「決策樹」不同,投票分類器由若干個異質分類器組成。下例用 VotingClassifier 建立個含有對率回歸 (LR)、隨機森林 (RF) 和高斯朴素貝葉斯 (GNB) 三個分類器的集成模型。
RandomForestClassifier 的基分類器只能是決策樹,因此只用通過控制 n_estimators 超參數來決定樹的個數,而 VotingClassifier 的基分類器要實實在在的輸入其本身。
看看 Ensemble 里包含的估計器個數和其本身。
-
print( len(Ensemble.estimators_) )
-
Ensemble.estimators_
比如元估計器和它三個組成元素的表現。還是集成后的 Ensemble 表現最好。
-
LR.fit( X_train, y_train )
-
RF.fit( X_train, y_train )
-
GNB.fit( X_train, y_train )
-
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) 的分類問題。
從小節 4.2 到 4.4,我們都會使用數字數據集 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 張圖片和對應的標簽 (左下角藍色小字)。像素很低 (為了我們跑模型快些),但基本上還是能看清。
手寫數字有 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 預測 ●
根據多數原則得到的結合預測為 ●,如下圖所示。
回到數字分類問題上,代碼如下:
10 類 45 個 OvO 分類器,沒錯,10*9/2 = 45。
-
print( len(ovo_lr.estimators_) )
-
ovo_lr.estimators_
訓練集分類全對,測試集准確率 98%。

OneVsRestClassifier
在 OvA 中,把數據分成“某個”和“其他”
-
圖一,某個 = 三角形,其他 = 正方形和圓形
-
圖二,某個 = 正方形,其他 = 三角形和圓形
-
圖三,某個 = 圓形,其他 = 三角形和正方形
三分類分解成三個二分類,對應的分類器為 f1 , f2 和 f3 。
-
f1 預測負類,即預測 ● 和 ◼
-
f2 預測負類,即預測 ▲ 和 ●
-
f3 預測正類,即預測 ●
三個分類器都預測了 ●,根據多數原則得到的預測是 ●。
回到數字分類問題上,代碼如下:
10 類 45 個 OvA 分類器,沒錯。
-
print( len(ova_lr.estimators_) )
-
ova_lr.estimators_
訓練集准確率幾乎 100%,測試集准確率 96%。
到目前為止,所有的樣例都總是被分配到僅一個類。有些情況下,你也許想讓分類器給一個樣例輸出多個類別。在無人駕駛的應用中,在下圖識別出有車和指示牌,沒有交通燈和人。
我們不打算深入物體識別。先看一個簡單點的例子,僅僅是為了闡明「多標簽分類」的原理。在手寫數字的例子上,我們特意為每個數字設計了多標簽:
-
標簽 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。
有兩個估計器,每個對應一個標簽。
-
print( len(ova_ml.estimators_) )
-
ova_ml.estimators_
展示一下測試集上 100 張圖片。
第一張圖片是數字 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
: 多輸出分類
本節只關注多輸出分類。

MultiOutputClassifier
首先引入 MultiOutputClassifier 和 RandomForestClassifier。你看,這兩個都是元估計器,因此在 Sklearn 里面估計器可以隨意組合。
-
from sklearn.multioutput import MultiOutputClassifier
-
from sklearn.ensemble import RandomForestClassifier
在手寫數字的例子上,我們也為特意每個數字設計了多標簽而且每個標簽的類別都大於二。
-
標簽 1 - 小於等於 4,4 和 7 之間,大於等於 7 (三類)
-
標簽 2 - 數字本身 (十類)
代碼如下:
用含有 100 棵決策樹的隨機森林來解決這個多輸入分類問題。
看看這個模型在測試集前五張照片上的預測。
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
: 用交叉驗證從一組隨機超參數搜索出最佳超參數。
本小節關注調節超參數的兩個估計器,即上面列出的最后兩個。它們都要用到交叉驗證,先來了解一下這個知識點。
接下來我們來看這兩個調參的估計器,網格追蹤和隨機追蹤。
網格追蹤:參數 1 在 [1, 10, 100, 1000] 中取值,參數 2 在 [0.01, 0.1, 1 10] 中取值,注意並不是等間距取值。模型在所有 16 個組合跑,選取一對對應最小交叉驗證誤差的參數。
隨機追蹤:根據指定分布隨機搜索,可以選擇獨立於參數個數,比如 log(參數 1) 服從 0 到 3 的均勻分布, log(參數 2) 服從 -2 到 1 的均勻分布。此外,會設定一個預算參數。
原理講清楚了,看代碼吧。
解釋一下代碼:
-
前 10 行就是引入各種包,並准備好 X 和 y,創建一個含 20 個決策樹的隨機森林模型,那么它有超參數最大樹深、最多特征數、最小可分裂樣本數、和分裂標准。
-
第 12-27 行是運行隨機追蹤,關鍵點是建立了一個參數分布 (param_dist),而且還預先設定了個迭代組數 n_iter_search=20。
-
第 22-42 行是運行網格追蹤,關鍵點是建立了一個參數網格 (param_grid)。
解釋一下運行結果:
-
第一行輸出每種追蹤法運行的多少次和花的時間。
-
第二行輸出最佳超參數的組合。
-
第三行輸出最高得分。
由上面結果可知,隨機追蹤比網格追蹤用更短時間內找到一組超參數,獲得了更高的得分。
4.5
Pipeline 估計器
Pipeline 估計器又叫流水線,把各種估計器串聯 (Pipeline) 或並聯 (FeatureUnion) 的方式組成一條龍服務。用好了它真的能大大提高效率。

Pipeline
Pipeline 將若干個估計器按順序連在一起,比如
特征提取 -> 降維 -> 擬合 -> 預測
在整個 Pipeline 中,它的屬性永遠和最后一個估計器屬性一樣
-
如果最后一個估計器是預測器,那么 Pipeline 是預測器
-
如果最后一個估計器是轉換器,那么 Pipeline 是轉換器
下面用一個簡單例子來說明如果用 Pipeline 來做「先填補缺失值-再標准化」這兩步的。先生成含缺失值 NaN 的數據 X。
首先引入 Pipeline,再引入
-
處理缺失值的轉換器 SimpleImputer
-
做規划化的轉換器 MinMaxScaler
第 4-7 行創建了流水線,范式非常簡單,就在 Pipeline() 里一個輸入「含名稱的估計器的列表」。SimpleImputer 起名叫 impute,MinMaxScaler起名叫 normalize。
由於最后一個估計器是轉換器,因此 pipe 也是個轉換器。寫好了就可以那它來做「先填補缺失值-再標准化」的重復工作了。
看看運行結果,值都被填滿了,而且兩列也被標准化了。
來驗證上面流水線的參數,我們可以按順序來運行這兩個轉換器,結果是一樣的。

FeatureUnion
如果我們想在一個節點同時運行幾個估計器,我們可用 FeatureUnion。下例首先建立一個 DataFrame,
-
前兩列智力 IQ 和脾氣 temper 都是分類型變量
-
后兩列收入 income 和身高 height 都是數值型變量
-
每列中都有缺失值
我們現在按下列步驟來清洗數據。
-
對分類型變量:獲取 -> 中位數填充 -> 獨熱編碼
-
對數值型變量:獲取 -> 均值填充 -> 標准化
上面兩步是平行進行的。
首先我們自己定義一個從 DataFrame 里面獲取每列的類,起名叫 DataFrameSelector
。
接下來建立一個流水線 full_pipe,它並聯着兩個流水線
-
categorical_pipe 處理分類型變量
-
DataFrameSelector 用來獲取
-
SimpleImputer 用出現最多的值來填充 None
-
OneHotEncoder 來編碼返回非稀疏矩陣
-
numeric_pipe 處理數值型變量
-
DataFrameSelector 用來獲取
-
SimpleImputer 用均值來填充 NaN
-
normalize 來規范化數值
下面代碼非常漂亮。
將結果打印出來,齊活!
-
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. ]]
Sklearn 里面設計 API 遵循五大原則。
所有對象的接口一致且簡單,在「估計器」中
-
創建: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)
所有估計器里設置的超參數和學到的參數都可以通過實例的變量直接訪問來檢驗其值,區別是超參數的名稱最后沒有下划線 _,而參數的名稱最后有下划線 _。舉例如下:
-
通例:model.hyperparameter
-
特例:SVC.kernel
-
通例:model.parameter_
-
特例:SVC.support_vectors_
Sklearn 模型接受的數據集的格式只能是「Numpy 數組」和「Scipy 稀疏矩陣」。超參數的格式只能是「字符」和「數值」。
不接受其他的類!
模塊都能重復「連在一起」或「並在一起」使用,比如兩種形式流水線 (pipeline)
-
任意轉換器序列
-
任意轉換器序列 + 估計器
Sklearn 給大多超參數提供了合理的默認值,大大降低了建模的難度。
結合本帖講的總結一套機器學習的初級框架:
確定任務:是「有監督」的分類或回歸?還是「無監督」的聚類或降維?確定好后基本就能知道用 Sklearn 里哪些模型了。
數據預處理:這步最繁瑣,要處理缺失值、異常值;要編碼分類型變量;要正規化或標准化數值型變量,等等。但是有了 Pipeline 神器一切變得簡單高效。
訓練和評估:這步最簡單,訓練用估計器 fit() 先擬合,評估用預測器 predict() 來評估。
選擇模型:啟動 Model Selection 估計器里的 GridSearchCV 和 RandomizedSearchCV,選擇得分最高的那組超參數 (即模型)。
本帖講的東西有點抽象,但最核心的東西就是弄懂估計器以及元估計器的原理。剩下的就是 1) 了解各種模型,2) 知道模型中每個參數的含義,3) 查閱 Sklearn 官方文檔。非深度的機器學習不像深度學習有那么多調參技巧 (tuning trick),按照上面那套框架足夠了。