第四章 機器學習
4.1 機器學習簡介
機器學習是用數據科學的計算能力和算法能力去彌補統計學的不足。
基本統計學概念:偏差(bias)、方差(variance)、過擬合(overfitting)和欠擬合(underfitting)
4.1.1 機器學習分類
機器學習一般分為:有監督學習和無監督學習。
有監督學習分為:分類和回歸任務,分類任務中,標簽都是離散值;回歸任務中標簽都是連續值。
無監督學習分為:聚類和降維任何。聚類算法將數據分成不同的組別;降維算法追求更簡潔的方式表現數據。
半監督學習,通常在標簽數據不完整時使用。
4.2 Scikit-Learn簡介
4.2.1 Scikit-Learn的數據表示
機器學習是從數據創建模型的學問,因此你首先需要了解怎樣表示數據才能讓計算機理解。sklearn認為數據表示最好的方法就是用數據表的形式。
1.數據表
# 基本的數據表示二維網格數據,其中每一行表示數據集的樣本,而列表示構成每個樣本的相關特征。
import seaborn as sns
iris = sns.load_dataset('iris')
iris.head()
sepal_length | sepal_width | petal_length | petal_width | 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 |
3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
2.特征矩陣
# 這個表格通過二維數組或矩陣的形式將信息清晰底表達出來,我們記此類矩陣為特征矩陣。特征矩陣通常被簡記為X。
# 它是維度為[n_samples, n_features]的二維矩陣。samples--樣本(行數),features--特征數(列數)
# 一、樣本(即每一行)通常是指數據集中的每一個對象。
# 二、特征(即每一列)通常是指每個樣本都具有的某種量化觀測值。一般,特征都是實數值,但有時也可能是布爾值或離散值。
3.目標數組
# 除了特征矩陣x之外,還需要一個標簽或標簽數組,記為y。目標數組一般是一維數組,
# 長度就是樣本總數n_samples,通常用一維的NumPy數組或者Pandas的Series表示。
# 目標數組可以是連續的數值類型,也可以是離散的類型/標簽。
%matplotlib inline
sns.set()
sns.pairplot(iris,hue='species',size=1.5)
# 從iris數據集中抽取特征矩陣和目標數組
X_iris = iris.drop('species',axis=1)
print("X_iris:",X_iris.shape)
y_iris = iris['species']
print("y_iris:",y_iris.shape)
X_iris: (150, 4)
y_iris: (150,)
4.2.2 Scikit-Learn的評估器API
sklearn api主要遵循以下設計原則:
統一性:所有對象使用共同接口連接一組方法和統一的文檔。
內省:所有參數都是公共屬性。
限制對象層級:只有算法才能用Python類表示。數據集都用標准的數據類型(NumPy數組、Pandas DataFrame、SciPy稀疏矩陣)表示,參數名稱用標准的Python字符串。
函數組合:許多機器學習任務都可以用一串基本算法實現,sklearn盡量支持這種可能。
明智的默認值:當模型需要用戶設置參數時,sklearn預先設置合適的默認值。
sklearn中的所有機器學習算法都是用過評估器API實現的,它為機器學習應用提供統一的接口
1.API基礎知識
sklearn評估器API的常用步驟如下:
(1)通過從sklearn中導入適當的評估器類,選擇模型類。
(2)用合適的數值對模型類進行實例化,配置模型超參數。
(3)整理數據,獲取特征矩陣和目標數組。
(4)調用模型實例的fit()方法對數據進行擬合。
(5)對新數據應用模型:
在有監督學習模型中,通常使用predict()方法預測新數據的標簽;
在無監督學習模型中,通常使用transform()或predict()方法轉換或推斷數據的性質。
2.有監督學習示例:簡單線性回歸
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.RandomState(42)
x = 10 * rng.rand(50)
y = 2 * x - 1 + rng.randn(50)
# a.選擇模型類
from sklearn.linear_model import LinearRegression
# b.選擇模型超參數
# 問題:
# 我們想要擬合偏移量(即直線的截距)嗎?
# 我們需要對模型進行歸一化處理嗎?
# 我們需要對特征進行預處理以提高模型靈活性嗎?
# 我們打算在模型中使用哪種正則化類型?
# 我們打算使用多少模型組件?
# 有一些重要的參數必須在選擇模型類時確定好。這些參數通常被稱為超參數,即在模型擬合之前必須確定下來的參數。
model = LinearRegression(fit_intercept=True) # 擬合直線截距
print("模型超參數:\n",model)
# c.抽取特征矩陣和目標數據
X = x[:,np.newaxis]
print("特征矩陣X:\n",X)
print("目標數組y:\n",y)
# d.擬合數據
model.fit(X,y)
# fit()方法會在模型內部進行大量運算,運算結果將存儲在模型屬性中,供用戶使用。
# 在sklearn中,所有通過fit()方法獲得的模型參數都帶一條下划線。如:線性模型中的模型參數如下:
print("模型的斜率:",model.coef_)
print("模型的截距:",model.intercept_)
# 模型訓練效果評價:
# 原數據由y = 2 * x - 1 + rng.randn(50)獲得,從中看出直線斜率為 2,截距為 1
# 擬合模型的直線斜率為 1.9776566,截距為 -0.903310725531 誤差很小。
# e.預測新數據的標簽
xfit = np.linspace(-1,11)
Xfit = xfit[:,np.newaxis]
yfit = model.predict(Xfit)
plt.scatter(x,y) # 散點圖
plt.plot(xfit,yfit)
模型超參數:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
特征矩陣X:
[[ 3.74540119]
[ 9.50714306]
[ 7.31993942]
[ 5.98658484]
[ 1.5601864 ]
[ 1.5599452 ]
[ 0.58083612]
[ 8.66176146]
[ 6.01115012]
[ 7.08072578]
[ 0.20584494]
[ 9.69909852]
[ 8.32442641]
[ 2.12339111]
[ 1.81824967]
[ 1.8340451 ]
[ 3.04242243]
[ 5.24756432]
[ 4.31945019]
[ 2.9122914 ]
[ 6.11852895]
[ 1.39493861]
[ 2.92144649]
[ 3.66361843]
[ 4.56069984]
[ 7.85175961]
[ 1.99673782]
[ 5.14234438]
[ 5.92414569]
[ 0.46450413]
[ 6.07544852]
[ 1.70524124]
[ 0.65051593]
[ 9.48885537]
[ 9.65632033]
[ 8.08397348]
[ 3.04613769]
[ 0.97672114]
[ 6.84233027]
[ 4.40152494]
[ 1.22038235]
[ 4.9517691 ]
[ 0.34388521]
[ 9.09320402]
[ 2.58779982]
[ 6.62522284]
[ 3.11711076]
[ 5.20068021]
[ 5.46710279]
[ 1.84854456]]
目標數組y:
[ 7.22926896 18.18565441 13.52423055 10.67206599 0.64185082
1.4000462 -0.29896653 17.38064514 11.36591852 11.3984114
-0.26422614 18.01311476 14.97193082 3.8584585 3.66749887
3.59937032 4.24562734 9.18591626 7.9701638 5.80012793
10.75788366 1.60421824 3.736558 5.13103024 8.93392551
16.05975926 2.92146552 10.28822167 11.2099274 -0.7161115
11.51229264 3.94851904 0.26520582 19.5423544 15.69289556
15.98984947 5.17932245 0.65443493 12.77642131 5.81548096
1.22109281 9.26065077 1.16566447 16.66813782 3.36710603
11.74868864 6.14962364 9.73011153 9.40444538 3.21035654]
模型的斜率: [ 1.9776566]
模型的截距: -0.903310725531
[<matplotlib.lines.Line2D at 0x1e13980d1d0>]
3.有監督學習示例:鳶尾花數據分類
# 我們將使用高斯朴素貝葉斯方法來訓練鳶尾花數據集,這個方法假設每個特征中屬於每一類的觀測值都符合高斯分布。
# 因為高斯朴素貝葉斯方法速度很快,而且不需要選擇超參數,所以通常適合作為初步分類手段,在借助復雜模型進行優化之前使用。
from sklearn.naive_bayes import GaussianNB # 1.選擇模型類
model = GaussianNB() # 2.初始化模型
from sklearn.cross_validation import train_test_split
Xtrain,Xtest,ytrain,ytest = train_test_split(X_iris,y_iris) # 3.分割數據集
model.fit(Xtrain,ytrain) # 4.擬合數據集
ypredict = model.predict(Xtest) # 5.預測數據集
from sklearn.metrics import accuracy_score
print(accuracy_score(ypredict,ytest)) # 6.模型准確率評估
print(accuracy_score(ytest,ypredict))
# 准確率高達97%
0.947368421053
0.947368421053
4.無監督學習示例:鳶尾花數據降維
# 對鳶尾花數據集進行降維,以便能更方便地對數據進行可視化
# 降維的任務是要找到一個可以保留數據本質特征的低維矩陣來表示高維數據。
# 降維通常用於輔助數據可視化的工作,畢竟二維數據畫圖比多維甚至更高維的數據畫圖更方便。
# 主成分分析(principal component analysis,PCA)方法,這一種快速線性降維技術。
from sklearn.decomposition import PCA # 1.選擇模型類
model = PCA(n_components=2) # 2.設置超參數,初始化模型
model.fit(X_iris) # 3.擬合模型
x_2D = model.transform(X_iris) # 4.將數據轉換為二維
iris['PCA1'] = x_2D[:,0]
iris['PCA2'] = x_2D[:,1]
sns.lmplot("PCA1","PCA2",hue='species',data=iris,fit_reg=False)
# 從二維數據標示圖可以看出,雖然PCA算法根本不知道花的種類標簽,但不同種類的花還是
# 被清晰地區分開來! 這表明用一種比較簡單的分類方法就能夠有效地學習這份數據集。
<seaborn.axisgrid.FacetGrid at 0x1e139168198>
5.無監督學習示例:鳶尾花數據聚類
# 聚類方法是要對沒有標簽的數據集進行分組。
# 使用一個強大的聚類方法----高斯混合模型(Guassian 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_predict = model.predict(X_iris) # 4.預測簇標簽
iris['cluster'] = y_predict
sns.lmplot("PCA1","PCA2",data=iris,hue='species',col='cluster',fit_reg=False)
# 根據簇數量對數據進行分割,就會清晰地看出GaussianMixture算法的訓練效果:setosa類的花在簇0中
# 被完美底區分出來,唯一的遺憾是第二幅圖中的versicolor和virginical還有一點混淆。
# 這就說明,即使沒有專家告訴完美每朵花的具體類型,但由於每種花的特征差異很大,
# 因此完美也可以通過簡單的聚類算法自動識別出不同種類的花!
# 這種算法還可以幫助專家們探索觀察樣本之間的關聯性。
<seaborn.axisgrid.FacetGrid at 0x1e13c455630>
4.2.3 應用:手寫數字探索
1.加載並可視化手寫數字
from sklearn.datasets import load_digits
digits = load_digits()
digits.images.shape
# 這份圖像數據是一個三維矩陣:共有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.1,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')
# 為了在sklearn中使用數據,需要一個維度為[n_samples, n_features]的二維特征矩陣
# 可以將每個樣本圖像的所有像素都作為特征,也就是將每個數字的8 × 8像素平鋪成長度為64的一維數組。
# 另外,還需要一個目標數組,用來表示每個數字的真實值(標簽)。這兩份數據已經放在手寫數字數據集
# 的data與target屬性中,直接使用即可:
X = digits.data
y = digits.target
print("X的形狀:",X.shape,"\n",X)
print("y的形狀:",y.shape,"\n",y)
# 從上面可以看出,一共有1797個樣本和64個特征。
X的形狀: (1797, 64)
[[ 0. 0. 5. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 10. 0. 0.]
[ 0. 0. 0. ..., 16. 9. 0.]
...,
[ 0. 0. 1. ..., 6. 0. 0.]
[ 0. 0. 2. ..., 12. 0. 0.]
[ 0. 0. 10. ..., 12. 1. 0.]]
y的形狀: (1797,)
[0 1 2 ..., 8 9 8]
2.無監督學習:降維
# 雖然我們想對具有64維參數控件的樣本進行可視化,但是咋如此高緯度的控件中進行可視化十分困難。
# 因此,我們需要借助無監督學習方法將維度降到二維。
# 這次試試流行學學習算法中的isomap算法對數據域進行降維:
from sklearn.manifold import Isomap # 1.選擇模型類
model = Isomap(n_components=2) # 2.設置超參數,模型初始化
model.fit(X) # 3.擬合模型
data_projected = model.transform(X) # 4.將數據轉為二維
print(data_projected.shape)
# 現在數據已經投影到二維。把數據畫出來,看看從結構中能發現什么:
plt.scatter(data_projected[:,0],data_projected[:,1],c=y,
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好像有點兒混淆---也許有人寫數字1時喜歡在上面加個“帽子”,因此看起來像數字4.
# 雖然有些瑕癖,但從總體上看,各個數字在參數空間中的分離程度還是令人滿意的。
# 這其實告訴完美;用一個非常簡單的有監督分類算法就可以完成任務。
(1797, 2)
4.有監督學習:數字分類
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_predict = model.predict(Xtest)
from sklearn.metrics import accuracy_score
print(accuracy_score(y_predict,ytest))
# 可以看出,通過一個非常簡單的模型,數字識別率就可以達到80%以上!但僅依靠這個指標,
# 我們無法知道模型哪里做的不好,解決這個問題的辦法就是用混淆矩陣。
# 可以用sklearn計算混淆矩陣,然后用seaborn畫出來:
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest,y_predict)
sns.heatmap(mat,square=True,annot=True,cbar=False)
plt.xlabel('predicted value')
plt.ylabel('true value')
# 從圖中可以看出,誤判的主要原因是許多數字2被誤判成了數字1和數字8.
0.833333333333
<matplotlib.text.Text at 0x1e13b0dccc0>
# 另一種顯示模型特征的直觀方式是將樣本畫出來,然后把預測標簽放在左下角,用綠色表示預測正確,用紅色表示預測錯誤:
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_predict[i]),
transform=ax.transAxes,
color='green' if (ytest[i] == y_predict[i]) else 'red')
# 通過觀察這部分樣本數據,我們能自動模型哪里的學習不夠好。如果希望分類准確率達到90%以上
# 可能需要借助更加復雜的算法,例如支持向量機、隨機森林,或者其他分類算法。