簡介:分類是指利用資料的特性將其分成若干型別的過程。
監督學習分類器就是用帶標記的訓練資料建立一個模型,然後對未知資料進行分類。
一、簡單分類器
首先,用numpy建立一些基本的資料,我們建立了8個點;
檢視程式碼
X = np.array([[3, 1], [2, 5], [1, 8], [6, 4], [5, 2], [3, 5], [4, 7], [4, -1]])
給這8個點的資料賦予預設的分類標籤
檢視程式碼
y = [0, 1, 1, 0, 0, 1, 1, 0]
class_0 = np.array([X[i] for i in range(len(X)) if y[i] == 0])
class_1 = np.array([X[i] for i in range(len(X)) if y[i] == 1])
我們將這些資料畫出來看看
檢視程式碼
plt.figure()
# 畫散點圖 (scatterplot)
plt.scatter(class_0[:, 0], class_0[:, 1], color='black', marker='s')
plt.scatter(class_1[:, 0], class_1[:, 1], color='black', marker='x')
plt.show()
如果我們要對資料加以區分,怎麼做呢?讓我們增加一條直線,我們用數學公式y=x畫出一條直線,構成我們的簡單分類器;
檢視程式碼
line_x = range(10)
line_y = line_x
plt.plot(line_x, line_y, color='black', linewidth=3)
plt.show()
二、邏輯迴歸分類器
邏輯迴歸雖然名字叫回歸,但是其實是一種分類方法,常用於二分類。
邏輯迴歸利用Sigmoid函式做了分類轉換,將結果轉換成0和1兩類,利用這個性質實現了分類的功能。
Sigmoid函式是一個S型的函式,當自變數z趨近正無窮時,因變數g(z)趨近於1,而當z趨近負無窮時,g(z)趨近於0,它能夠將任何實數對映到(0,1)區間,使其可用於將任意值函式轉換為更適合二分類的函式。
下面總結如何用python實現邏輯迴歸。
首先匯入需要的包,包括numpy(計算),matplotlib(畫圖), sklearn(建模)
檢視程式碼
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import datasets
from sklearn import linear_model
獲取鳶尾花資料集(sklarn中自帶資料集),指定特徵x和目標y,檢視資料的前10行
檢視程式碼
iris = datasets.load_iris()
x = iris.data[:, :2]
y = iris.target
print('x前10行:\n', x[:10])
print('y前10行:\n', y[:10])
畫圖檢視資料的分佈情況,c=y表示使用顏色的順序,用y中的不同個數來定義不同顏色的數量,這裡y總共有3類,所以有3種不同的顏色。
檢視程式碼
plt.figure()
plt.scatter(x[:, 0], x[:, 1], c=y)
plt.show()
初始化邏輯迴歸分類器,用sklearn中的LogisticRegression模型(簡稱LR)。其中的重要引數包括, solver 設定求解系統方程的演算法型別, C表示正則化強度,值越小正則化強度越高
檢視程式碼
clf = linear_model.LogisticRegression(solver='liblinear', C=1000)
訓練分類器,直接用fit方法,傳入特徵x和目標y
檢視程式碼
clf.fit(X, y)
畫出資料的邊界。首先定義圖形的取值範圍,通常是從最小值到最大值,增加了一些餘量(buffer),如程式碼中最小值-1,最大值+1。
畫邊界的時候用到了網格(grid)資料求解方程的值,然後把邊界畫出來。
np.c_方法是按行連線兩個矩陣,要求兩個矩陣的行數相等。(擴充套件一下,同理,np.r_ 方法就是按列連線兩個矩陣,要求兩個矩陣的列數相等)
檢視程式碼
x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0
y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0
print('x_min:', x_min, 'x_max:', x_max)
print('y_min:', y_min, 'y_max:', y_max)
# 設定網格步長
step_size = 0.05
# 定義網格
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size))
# 展平,連線
x_, y_ = np.c_[x_values.ravel(), y_values.ravel()][:, 0], np.c_[x_values.ravel(), y_values.ravel()][:, 1]
print('x_: \n', x_)
print('y_: \n', y_)
檢視x_min, x_max 和 y_min, y_max的分佈情況:
檢視x_ 和 y_ 的資料:
用分類器預測所有點的分類結果
檢視程式碼
y_pred = clf.predict(np.c_[x_.ravel(), y_.ravel()]).reshape(x_.shape)
print(y_pred)
檢視預測結果:
用matplotlib畫出各個型別的邊界:
檢視程式碼
cmap_light = ListedColormap(['#AAAAFF','#AAFFAA','#FFAAAA'])
plt.figure()
plt.pcolormesh(x_, y_, y_pred, cmap=cmap_light)
plt.xlim(x_.min(), x_.max())
plt.ylim(y_.min(), y_.max())
plt.show()
更多的顏色選擇可以從顏色清單中找到:https://matplotlib.org/2.0.2/examples/pylab_examples/colours.html
再把訓練的資料點也畫到圖上:
檢視程式碼
plt.scatter(x[:, 0], x[:, 1], c=y)
我們把引數C(對錯誤的懲罰值)調整一下,設定成1,看看效果
檢視程式碼
clf = linear_model.LogisticRegression(solver='liblinear', C=1)
可以看到,分類的效果沒有之前的那麼好,很多的綠色區域的分類都錯誤了。
我們可以總結,隨著引數C的不斷增大,分類錯誤的懲罰值越高,因此,各個型別的邊界更優。
三、樸素貝葉斯分類器
樸素貝葉斯是基於概率論的分類器,利用先驗概率推匯出後驗概率,通過概率值的閾值設定來區分類別。比如將概率>=0.5的定義為類別1,概率<0.5的定義為類別0,這樣就通過概率的計算方式實現了分類目的。
樸素貝葉斯分為高斯貝葉斯,伯努利貝葉斯,多項式貝葉斯。不同的貝葉斯基於資料的分佈不同進行選擇。
高斯貝葉斯用於正式分佈的資料,適用於連續資料,例如溫度,高度。
伯努利貝葉斯用於二項分佈的資料(例如,拋硬幣),二項分佈又叫做伯努利分佈。
多項式貝葉斯用於多項分佈的資料(例如,擲骰子)。
下面,我們開始總結用python實現樸素貝葉斯的方法。
首先,匯入需要用的包。主要是sklearn中的一些類,包括建模用到的包,構造資料用到的包,資料集劃分,交叉驗證等。
檢視程式碼
from sklearn.naive_bayes import GaussianNB
from utils.views import plot_classifier, plot_confusion_matrix
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import confusion_matrix, classification_report
我們開始匯入資料,使用的是sklarn自帶的構造資料的方法(make_classification)。
檢視程式碼
X, y = make_classification(n_samples=2000, n_features=2, n_redundant=0, n_classes=4, n_clusters_per_class=1, random_state=0)
print('X前10行資料: \n', X[: 10])
print('y前10行資料, \n', y[:10])
介紹一下,裡面用到的常用引數,包括:
n_samples: 2000個樣本
n_features:2個特徵
n_redundant:冗餘特徵數0個
n_classes: 4個類別
n_clusters_per_class:每個簇1個類
random_state: 隨機數種子,隨便定義,確定隨機數種子後,多次反覆執行該語句,生成的資料結果是一樣的。如果不確定的話,每次生成的資料隨機。
檢視資料的前10行情況:
檢視一下資料的分佈情況:
檢視程式碼
plt.Figure()
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.show()
用train_test_split對資料集進行訓練集和測試集的劃分。
檢視程式碼
X_train, X_test, y_tran, y_test = train_test_split(X, y, test_size=0.25)
其中,test_size = 0.25表示測試集資料佔25%,訓練集資料佔75%。
開始建立樸素貝葉斯模型。
檢視程式碼
clf = GaussianNB()
模型訓練,傳入特徵x和目標值y,這裡用的資料都是訓練集
檢視程式碼
clf.fit(X_train, y_tran)
預測結果,傳入測試集:
檢視程式碼
y_pred = clf.predict(X_test)
將資料劃分的結果視覺化
檢視程式碼
plot_classifier(clf, X_test, y_test)
def plot_classifier(clf, X, y):
# 定義圖形取值範圍
x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0
y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0
print('x_min:', round(x_min, 2), 'x_max:', round(x_max, 2))
print('y_min:', round(y_min, 2), 'y_max:', round(y_max, 2))
# 網格(grid) 資料求解方程的值,畫出邊界
# 設定網格步長
step_size = 0.01
# 定義網格
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size))
# 展平,連線
x_, y_ = np.c_[x_values.ravel(), y_values.ravel()][:, 0], np.c_[x_values.ravel(), y_values.ravel()][:, 1]
# 預測結果
mesh_output = clf.predict(np.c_[x_values.ravel(), y_values.ravel()])
# 陣列維度變形
mesh_output = mesh_output.reshape(x_values.shape)
plt.figure()
# 選擇配色方案‘
plt.pcolormesh(x_values, y_values, mesh_output, cmap=plt.cm.gray)
plt.scatter(X[:, 0], X[:, 1], c=y, s=80, edgecolors='black', linewidths=1) # cmap=plt.cm.Paired
# 設定圖形的取值範圍
plt.xlim(x_values.min(), x_values.max())
plt.ylim(y_values.min(), y_values.max())
# 設定x軸與y軸
plt.xticks((np.arange(int(min(X[:, 0]) - 1), int(max(X[:, 0]) + 1), 1.0)))
plt.yticks((np.arange(int(min(X[:, 1]) - 1), int(max(X[:, 1]) + 1), 1.0)))
plt.show()
和邏輯迴歸時畫圖的方法一樣,藉助於網格來確定資料的界限,這裡,直接把這個過程提取成一個plot_classifier方法,每次傳入模型clf和x,y的值即可。
用肉眼檢視,感覺分類結果還不錯,那麼具體結果值是多少呢?我們檢視一下準確率,用預測結果和測試集(即真實結果)進行比對
檢視程式碼
accuracy = clf.score(X_test, y_test)
print('accuracy:---', accuracy)
可以看到準確率有92%,還是不錯的。當然這只是訓練一次的結果,可能存在一定的偶然性,如果想讓結果更具說服力,減少資料切分帶來的偶然性,那麼,我們可以使用十折交叉驗證。
十折交叉驗證即每次取訓練集中的一份做驗證集,其餘9份做訓練集,然後取最後的結果平均值,作為最終結果的輸出。
檢視程式碼
accuracy_cv = cross_val_score(clf, X, y, scoring='accuracy', cv=10)
print('accuracy_cv:---', round(accuracy_cv.mean(), 2))
f1 = cross_val_score(clf, X, y, scoring='f1_weighted', cv=10)
print('f1:', round(f1.mean(), 4))
precision = cross_val_score(clf, X, y, scoring='precision_weighted', cv=10)
print('precision:', round(precision.mean(), 4))
recall = cross_val_score(clf, X, y, scoring='recall_weighted', cv=10)
print('recall:', round(recall.mean(), 4))
其中cv=10,表示交叉驗證10次。scoring='accuracy' 表示輸出的結果是準確率,其他的引數還有,f1_weighted(f1分數),precision_weighted(精準率), recall_weighted(召回率)。
可以看到十次交叉驗證的結果準確率也能達到92%,精準率,召回率,f1-score也都在92%左右,表現還是不錯的。
我們可以通過混淆矩陣進一步檢視,在哪些類別上出錯的多一些。
檢視程式碼
confusion_mat = confusion_matrix(y_test, y_pred)
print('confusion_mat: \n', confusion_mat)
plot_confusion_matrix(confusion_mat)
從矩陣中可以看出,第3類和第4類被誤判的結果稍多一些,這和從視覺化的圖上看到的結果是一致的。
對於混淆矩陣,還可以進行視覺化
檢視程式碼plt.imshow(confusion_mat, interpolation='nearest', cmap='gray') # 亮色: cmap=plt.cm.Paired
plt.title('Confusion matrix')
plt.colorbar()
tick_marks = np.arange(4)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
從圖中,我們可以看出,對角線的顏色很亮,我們希望的是對角線的顏色越亮越好,在非對角線的區域如果全部是黑色,表示沒有錯誤;如果有灰色區域,那麼表示分類錯誤的樣本量。從混淆矩陣的視覺化圖中,我們可以看到下標2(即第3類)和下標3(即第4類)存在灰色區域,說明第3類和第4類存在分類錯誤的情況。
sklearn類還內建了效能報告,我們可以直接用classification_report方法進行提取檢視演算法的分類效果。
檢視程式碼target_names = ['Class-0', 'Class-1', 'Class-2', 'Class-3']
report = classification_report(y_test, y_pred, target_names=target_names)
print(report)
報告中最後一列support表示的是樣本數,總的樣本數為2000個,我們設定了0.25比例的訓練集,那麼訓練數就有500個,132,122,120,126則表示每一類的樣本數,加起來總共也是500個。
以上,用的是高斯葉斯分類器的訓練和預測結果,我們也可以用伯努利貝葉斯看看結果如何。
我們可以看到,對於這個資料集,第1類分錯的情況變多了,從混淆矩陣的視覺化圖中,看到有灰色的矩陣出現,從報告中看出precision從93%降低到了92%,第4類分類錯誤也變多,由92%降低到了89%。這樣導致整體的平均precision由92%降低到了90%,不過對於第2類的分類準確率是提高了,92%提高到了98%。
四、分類器案例:根據汽車特徵評估質量
需求分析:根據汽車的特徵進行訓練,得到訓練模型,用模型預測具體某輛汽車的質量情況。
資料分析:
目標:「汽車質量」,(unacc,ACC,good,vgood)分別代表(不可接受,可接受,好,非常好)
6個屬性變數分別為:
「買入價」buying:取值範圍是vhigh、high、med、low
「維護費」maint:取值範圍是vhigh,high,med,low
「車門數」doors:取值範圍 2,3,4,5more
「可容納人數」persons:取值範圍2,4, more
「後備箱大小」lug_boot: 取值範圍 small,med,big
「安全性」safety:取值範圍low,med,high
值得一提的是6個屬性變數全部是有序類別變數,比如「可容納人數」值可為「2,4,more」,「安全性」值可為「low, med, high」
檢視資料分佈情況:
匯入必要的包,包括sklearn(建模,交叉驗證,學習曲線), numpy(計算), matplotlib(畫圖):
檢視程式碼import matplotlib.pyplot as plt
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, validation_curve
import numpy as np
from utils.views import plot_curve
import pandas as pd
載入資料:
檢視程式碼input_file = 'data/car.data.txt'
df = pd.read_table(input_file, header=None, sep=',')
df.rename(columns={0:'buying', 1:'maint', 2:'doors', 3:'persons', 4:'lug_boot', 5:'safety', 6:'quality'}, inplace=True)
df.head(10)
將字串轉換成數值:
檢視程式碼label_encoder = []
for i in range(df.shape[1]):
label_encoder.append(preprocessing.LabelEncoder())
df.iloc[:, i] = label_encoder[-1].fit_transform(df.iloc[:, i])
df.head(10)
提取特徵X和目標值y
檢視程式碼X = df.iloc[:, :-1]
y = df.iloc[:, -1]
接下來訓練分類器,這裡我們使用隨機森林分類器
檢視程式碼params = {
'n_estimators': 200,
'max_depth': 8,
'random_state': 7
}
clf = RandomForestClassifier(**params)
clf.fit(X, y)
接下來開始驗證模型的效果,採用十折交叉驗證。注意,用十折交叉驗證的時候就不需要做資料集的劃分,直接用全量資料集即可。
檢視程式碼accuracy = cross_val_score(clf, X, y, scoring='accuracy', cv=10)
print('accuracy:', round(accuracy.mean(), 3))
建立分類器的目的就是對孤立的未知資料進行分類,下面對單一資料點進行分類。
檢視程式碼input_data = ['low', 'vhigh', '2', '2', 'small', 'low']
input_data_encoded = [-1] * len(input_data)
for i, item in enumerate(input_data):
input_data_encoded[i] = int(label_encoder[i].transform([input_data[i]]))
input_data_encoded = np.array(input_data_encoded)
print(input_data_encoded)
將單一資料由字串型別轉換成數值型別:
預測資料點的輸出型別:
檢視程式碼output_class = clf.predict(input_data_encoded.reshape(1, -1))
print('output class:', label_encoder[-1].inverse_transform(output_class)[0])
用predict進行預測輸出,輸出的是數值編碼,顯然是看不懂具體的含義的,需要用inverse_transform對標記編碼進行解碼,轉換成原來的形式。
引數調優
通過生成驗證曲線,網格搜尋進行引數的調優。
我們對 n_estimators(弱學習器的個數) 這個引數,太小容易欠擬合,太大容易過擬合。
檢視程式碼
parameter_grid = np.linspace(25, 200, 8).astype(int)
train_scores, validation_scores = validation_curve(clf, X, y, param_name='n_estimators',
param_range=parameter_grid, cv=5)
print('\n ##### VALIDATION CURVES #####')
print('\nParam: n_estimators \n Training scores: \n', train_scores)
print('\nParam: n_estimators \n Validation scores:\n', validation_scores)
驗證曲線畫圖:
檢視程式碼plt.figure()
plt.plot(parameter_grid, 100 * np.average(train_scores, axis=1), color='black')
plt.title('Training curve')
plt.xlabel( 'Number of estimators')
plt.ylabel('Accuracy')
plt.show()
由圖可以看出,estimate在100附近,達到最大的準確率。
同理對max_depth生成驗證曲線。
檢視程式碼max_depth_grid = np.linspace(2, 10, 5).astype(int)
train_scores, validation_scores = validation_curve(clf, X, y, param_name='max_depth',
param_range=max_depth_grid, cv=5)
plot_curve(max_depth_grid, train_scores, 'Validation curve', 'Maximum depth of the tree')
可以看出,max_depth在10附近,準確率達到最大值。
生成學習曲線
學習曲線可以幫助我們理解訓練資料集的大小對機器學習模型的影響。當計算能力限制的時候,這點非常有用。下面改變訓練資料集的大小,繪製學習曲線。
檢視程式碼parameter_grid = np.array([200, 500, 800, 1100])
train_size, train_scores, validation_scores = learning_curve(clf, X, y, train_sizes=parameter_grid, cv=10)
print('\n ##### LEARNING CURVES #####')
print('\n Training scores: \n', train_scores)
print('\n Validation scores:\n', validation_scores)
plot_curve(parameter_grid, train_scores, 'Learning curve', 'Number of training samples')
可以看到訓練的資料集規模越小,訓練的準確率越高。
但是,這樣也會容易造成一個問題,那就是過擬合。如果選擇規模較大的資料集,會消耗更多的資源,所以訓練集的規模選擇是一個結合計算能力需要綜合考慮的問題。
以上用到資料集下載:
car.data.txt: https://url87.ctfile.com/f/21704187-595799592-6f0749?p=7287 (訪問密碼: 7287)