文章目錄
[ sklearn version: 0.23.0 ]
二、Scikit-Learn簡介
Scikit-Learn爲各種常用機器學習算法提供了高效版本。
(一)Scikit-Learn的數據表示
機器學習是從數據創建模型的學問,因此首先需要了解怎樣表示數據愛你讓計算機理解。Scikit-Learn認爲數據表示(data representation)最好的方法就是用數據表的形式。
1. 數據表
基本的數據表就是二維網格數據,其中的每一行表示數據集中的每個樣本(samples),而列表示構成每個樣本的相關特徵(features)。
# 鳶尾花數據集
import pandas as pd
iris = pd.read_csv('./seaborn-data-master/iris.csv')
iris.head()
- 每行數據表示每朵被觀察的鳶尾花,行數表示數據集中記錄的鳶尾花總述。一般會將這個矩陣的行稱爲樣本,行數記爲 n_samples,每列數據表示每個樣本某個特徵的量化值,列數記爲 n_features
- 數據集: mwaskom/seaborn-data - GitHub
2. 特徵矩陣
這個表格佈局通過二維數組或矩陣的形式將信息清晰地表達出來,通常把這類矩陣稱爲特徵矩陣(features matrix)。
特徵矩陣通常被簡記爲變量X
。它是維度爲[n_samples, n_features]
的二維矩陣,通常可以用NumPy數組或Pandas的DataFrame來表示,不過Scikit-Learn也支持SciPy的稀疏矩陣。
樣本(每一行)通常是指數據集中的每個對象(任何可以通過一組量化方法進行測量的實體)。
特徵(每一列)通常是指每個樣本都具有的某種量化觀測值。一般情況下,特徵都是實數,但有時也可能是布爾類型或離散值。
3. 目標數組
除了特徵矩陣 X 之外,還需要一個標籤或目標數組,通常簡記爲y
。
目標數組一般是一維數組,其長度就是樣本總數n_samples
,通常都用一維的NumPy數組或Pandas的Series表示。
目標數組可以是連續的數值類型,也可以是離散的類型/標籤。
有些Scikit-Learn的評估器可以處理具有多目標值的二維[n_samples, n_targets]目標數組,文中基本上只涉及常見一維目標數組問題。
如何區分目標數組的特徵與特徵矩陣中的特徵列,一直是個問題。目標數組的特徵通常是我們希望從數據中預測的量化結果,借用統計學的術語,y
就是因變量。
- 以鳶尾花數據集爲例,我們需要通過其他測量值來建立模型,預測花的品種(species),species列就可以看成是目標數組
%matplotlib inline
import seaborn as sns
sns.set()
sns.pairplot(iris, hue='species', height=1.5)
抽取特殊矩陣和目標數組
# 從DataFrame中抽取特徵矩陣和目標數組
X_iris = iris.drop('species', axis=1)
X_iris.shape # (150, 4)
X_iris
y_iris = iris['species']
y_iris.shape # (150,)
y_iris
(二)Scikit-Learn的評估器API
Scikit-Learn API主要遵照以下設計原則:
- 統一性:所有對象使用共同接口連接一組方法和統一的文檔
- 內省:所有參數值都是公共屬性
- 限制對象層級:只有算法可以用Python類表示。數據集都用標準數據類型(NumPy數組、Pandas DataFrame、SciPy稀疏矩陣)表示,參數名稱用標準的Python字符串
- 函數組合:許多機器學習任務都可以用一串基本算法實現,Scikit-Learn盡力支持這種可能
- 明智的默認值:當模型需要用戶設置參數時,Scikit-Learn預先定義適當的默認值
Scikit-Learn中的所有機器學習算法都是通過評估器API實現的,它爲各種機器學習應用提供了統一的接口
1. API基礎知識
Scikit-Learn評估器API的常用步驟如下:
- 通過從Scikit-Learn中導入適當的評估器類,選擇模型類
- 用合適的數值對模型類進行實例化,配置模型超參數(hyperparameter)
- 整理數據,獲取特徵矩陣和目標數組
- 調用模型實例的
fit()
方法對數據進行擬合 - 對新數據應用模型:
– 在有監督學習模型中,通常使用predict()
方法預測新數據的標籤
– 在無監督學習模型中,通常使用transform()
或predict()
方法轉換或推斷數據的性質
2. 有監督學習示例:簡單線性迴歸
演示一個簡單線性迴歸的建模步驟:最常見的任務就是爲散點數據集擬合一條直線
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
rng = np.random.RandomState(42)
x = 10 * rng.rand(50)
y = 2 * x - 1 + rng.randn(50)
plt.scatter(x, y)
(1) 選擇模型類
在Scikit-Learn中,每個模型類都是一個Python類。
# 計算一個簡單線性迴歸模型
from sklearn.linear_model import LinearRegression
(2) 選擇模型超參數
注意:模型類與模型實例不同
當我們選擇模型類之後,還有許多參數需要配置。根據不同模型的不同情況,你可能需要回答一下問題:
- 我們想要擬合偏移量(即直線的截距)嗎?
- 我們需要對模型進行歸一化處理嗎?
- 我們需要對特徵進行預處理以提高模型靈活性嗎?
- 我們打算在模型中使用哪種正則化類型?
- 我們打算使用多少模型組件(model component)?
有一些重要的參數必須在選擇模型類時確定好。這些參數通常被稱爲超參數,即在模型擬合數據之前必須被確定的參數。
在Scikit-Learn中,通常在模型初始化階段選擇超參數。
# 對於示例,可以實例化LinearRegression類
# 並用fit_intercept超參數設置是否想要擬合直線的截距
model = LinearRegression(fit_intercept=True)
model
# LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
注意:對模型進行實例化僅僅是存儲了超參數的值。我們還沒有將模型應用到數據上:Scikit-Learn的API對選擇模型和將模型應用到數據區別得很清晰。
(3) 將數據整理成特徵矩陣和目標數組
Scikit-Learn的數據表示方法(需要二維特徵矩陣和一維目標數組)。
雖然我們的目標數組已經有了y(長度爲n_samples的數組),但還需要將數據x整理成[n_samples, n_features]的形式。
# 示例中,可以對一維數組進行簡單的維度變換
X = x[:, np.newaxis]
X.shape # (50, 1)
(4) 用模型擬合數據
現在就可以將模型應用到數據上了,這一步通過模型的fit()
方法即可完成。
fit()
命令會在模型內部大量運算,運算結果將存儲在模型屬性中,供用戶使用。
model.fit(X, y)
# LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
在Scikit-Learn中,所有通過fit()
方法獲得的模型參數都帶一條下劃線。
# 例如,在線迴歸模型中,模型參數如下
model.coef_ # array([1.9776566])
model.intercept_ # -0.9033107255311164
model.coef_
和model.intercept_
兩個參數分別表示對樣本數據擬合直線的斜率和截距- 與前面樣本數據的定義(斜率2,截距-1)進行比對,發現擬合結果與樣本非常接近
模型參數的不確定性是機器學習經常遇到的問題。一般情況下,Scikit-Learn不會爲用戶提供直接從模型參數獲得結論的工具,與其將模型桉樹解釋爲機器學習問題,不如說它更像統計建模問題。機器學習的重點並不是模型的預見性。
(5) 預測新數據的標籤
模型預測出來後,有監督機器學習的主要任務就變成了對不屬於訓練集的新數據進行預測。
在Scikit-Learn中,用predict()
方法進行預測
# 新數據是特徵矩陣的x座標值,需要用模型預測出目標數組的y軸座標
xfit = np.linspace(-1, 11)
# 先將x值轉換成[n_samples, n_features]的特徵矩陣形式,之後將其輸入到模型中
Xfit = xfit[:, np.newaxis]
yfit = model.predict(Xfit)
# 最後把原始數據和擬合結果可視化
plt.scatter(x, y)
plt.plot(xfit, yfit)
3. 有監督學習示例:鳶尾花數據分類
示例問題:如何爲鳶尾花數據集建立模型,先用一部分數據進行㜕,再用模型預測出其他樣本的標籤?
我們將使用高斯樸素貝葉斯(Gaussian naive Bayes)方法完成。
這個方法假設每個特徵中屬於每一類的觀測值都符合高斯分佈。因爲高斯樸素貝葉斯分類高斯樸素貝葉斯方法速度很快,而且不需要選擇超參數,所以通常很適合作爲初步分類手段,在藉助更復雜的模型進行優化之前使用。
由於需要用模型之前沒有接觸過的數據評估它的訓練效果,因此得現將數據分割成訓練集(training set)和測試集(testing set)。使用train_test_split
函數
from sklearn.model_selection import train_test_split
Xtrain, Xtest, ytrain, ytest = train_test_split(X_iris, y_iris, random_state=1)
整理好數據後,用模型預測標籤
from sklearn.naive_bayes import GaussianNB # 1.選擇模型類
model = GaussianNB() # 2.初始化模型
model.fit(Xtrain, ytrain) # 3.用模型擬合數據
y_model = model.predict(Xtest) # 4.對新數據進行預測
最後用accuracy_score
工具驗證模型預測結果的準確率(預測的所有結果中,正確結果佔總預測樣本數的比例)
from sklearn.metrics import accuracy_score
accuracy_score(ytest, y_model) # 輸出:0.9736842105263158
- 準確率高達97%,表明即使是簡單的分類算法也可以有效學習這個數據集
4. 無監督學習示例:鳶尾花數據降維
無監督學習問題:對鳶尾花數據集進行降維,以便能更方便地對數據進行可視化。(鳶尾花數據集由四個維度構成,即每個樣本都有四個維度。)
降維的任務是要找到一個可以保留數據本質特徵的低維矩陣來表示高維數據。降維通常用於輔助數據可視化的工作(用二維數據畫圖比用四維甚至更高維的數據畫圖更方便)
以下將使用主成分分析(principle component analysis, PCA)方法,是一種快速線性降維技術。將用模型返回兩個主成分,也就是用二維數據表示鳶尾花的四維數據。
from sklearn.decomposition import PCA # 1.選擇模型類
model = PCA(n_components=2) # 2.設置超參數,初始化模型
model.fit(X_iris) # 3.擬合數據(注意這裏不用y變量)
X_2D = model.transform(X_iris) # 4.將數據轉換爲二維
可視化結果:快速處理方法就是先將二維數據插入到鳶尾花的DataFrame中,然後用Seaborn的lmplot
方法畫圖
iris['PCA1'] = X_2D[:, 0]
iris['PCA2'] = X_2D[:, 1]
sns.lmplot('PCA1', 'PCA2', hue='species', data=iris, fit_reg=False)
- 從二維數據表示圖可以看出,雖然PCA算法不知道花的種類標籤,但不同種類的花被很清晰區分開,這表明用一種比較簡單的分類方法就能有效學習這份數據集
5. 無監督學習示例:鳶尾花數據聚類
聚類算法是要對沒有任何標籤的數據集進行分組。
此處使用聚類方法之高斯混合模型(Gaussian mixture model, GMM),GMM模型試圖將數據構造成若干服從高斯分佈的概率密度函數簇。
擬合高斯混合模型:
from sklearn.mixture import GaussianMixture # 1.選擇模型類
model = GaussianMixture(n_components=3,
covariance_type='full') # 2.設置超參數,初始化模型
model.fit(X_iris) # 3.擬合數據(注意不需要y變量)
y_gmm = model.predict(X_iris) # 4.確定簇標籤
將簇標籤添加到鳶尾花的DataFrame中,並用Seaborn畫出結果
iris['cluster'] = y_gmm
sns.lmplot('PCA1', 'PCA2', data=iris, hue='species',
col='cluster', fit_reg=False)
- 根據簇數量對數據進行分割,就會清洗地看出GMM算法的訓練效果:setosa(山鳶尾花)類的花在簇0中被完美地區分出來,唯一的遺憾是第三幅圖中versicolor(變色鳶尾花)和virginical(維吉尼亞鳶尾花)還有一點混淆。這就說明,由於每種花的特徵差異很大,我們也可以通過簡單的聚類算法自動識別出不同種類的花。
- 這種算法還可以探索觀察樣本之間的關聯性
(三)應用:手寫數字探索
挑戰一個光學字符識別問題:手寫數字識別。這個問題包括圖像中字符的定位和識別兩部分。
1. 加載並可視化手寫數字
用Scikit-Learn的數據獲取接口加載數據並簡單統計:
from sklearn.datasets import load_digits
digits = load_digits()
digits.images.shape # 輸出:(1797, 8, 8)
# 圖像數據是一個三維矩陣:共有1797個樣本,每張圖像都是8像素*8像素。
# 對前100張圖進行可視化
import matplotlib.pyplot as plt
fig, axes = plt.subplots(10, 10, figsize=(8, 8),
subplot_kw={'xticks': [], 'yticks': []},
gridspec_kw=dict(hspace=0.2, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(digits.images[i], cmap='binary', interpolation='nearest')
ax.text(0.05, 0.05, str(digits.target[i]),
transform=ax.transAxes, color='green')
爲了在Scikit-Learn中使用數據,需要一個維度爲[n_samples, n_features]的二維特徵矩陣,可以將每個樣本圖像的所有像素都作爲特徵,也就是將每個數字的8像素*8像素平鋪成長度爲64的一維數組。另外還需要一個目標數組,用來表示每個數字的真實值(標籤)。
# 這兩份數據已經在手寫數字數據集的data與target屬性中,可直接使用
X = digits.data
X.shape # 輸出:(1797, 64)
y = digits.target
y.shape # 輸出:(1797,)
# 一共有1797個樣本和64個特徵
2. 無監督學習:降維
需要藉助無監督學習方法將維度降到二維,通過流形學習算法中的Isomap算法對數據進行降維:
from sklearn.manifold import Isomap
iso = Isomap(n_components=2)
iso.fit(digits.data)
data_projected = iso.transform(digits.data)
data_projected.shape # 輸出:(1797, 2)
# 數據已投影到二維,將二維數據可視化
plt.scatter(data_projected[:, 0], data_projected[:, 1], c=digits.target,
edgecolor='none', alpha=0.5,
cmap=plt.cm.get_cmap('nipy_spectral', 10))
plt.colorbar(label='digit label', ticks=range(10))
plt.clim(-0.5, 9.5)
- 圖像呈現了很直觀的效果,可以看出數字在64維空間中的分離(可識別)程度。
- 如,在參數空間中,數字0(黑色)和1(紫色)基本不會重疊。
- 從圖中發現,數字1、4、7有點混淆
3. 數字分類
需要找到一個分類算法,對手寫數字進行分類。
# 將數據分成訓練集和測試集
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, random_state=0)
# 用高斯樸素貝葉斯模型來擬合
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(Xtrain, ytrain)
y_model = model.predict(Xtest)
# 已完成模型預測,用模型在訓練集中的正確識別樣本量與總訓練樣本連進行對比,獲得模型的準確率
from sklearn.metrics import accuracy_score
accuracy_score(ytest, y_model) # 輸出:0.8333333333333334
- 可以看出,通過一個簡單的模型,數字識別率就可以高達80%,但僅依靠這個指標,無法知道模型哪裏做得不夠好,解決這個問題的辦法就是用混淆矩陣(confusion matrix)
# 用Scikit-Learn計算混淆矩陣,並用Seaborn可視化
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest, y_model)
sns.heatmap(mat, square=True, annot=True, cbar=True)
plt.xlabel('predicted value')
plt.ylabel('true value')
- 從圖中可以看出,誤判的主要原因在於許多數字2被誤判成數字1或8。
另一種顯示模型特徵的直觀方式是將樣本畫出來,然後把預測標籤放在左下角,用綠色表示預測正確,紅色表示預測錯誤
fig, axes = plt.subplots(10, 10, figsize=(8, 8),
subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
test_images = Xtest.reshape(-1, 8, 8)
for i, ax in enumerate(axes.flat):
ax.imshow(test_images[i], cmap='binary', interpolation='nearest')
ax.text(0.05, 0.05, str(y_model[i]),
transform=ax.transAxes,
color='green' if (ytest[i] == y_model[i]) else 'red')
- 通過觀察這部分樣本數據,能知道模型哪裏的學習不夠好。
- 如果希望分類準確率達到80%以上,可能需要藉助更加複雜的算法,如支持向量機、隨機森林或其他分類算法。
機器學習相關閱讀:
總結自《Python數據科學手冊》