【火爐煉AI】機器學習008-簡單線性分類器解決二分類問題

煉丹老頑童發表於2018-08-06

【火爐煉AI】機器學習008-簡單線性分類器解決二分類問題

(本文所使用的Python庫和版本號: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

分類問題,就是將資料點按照不同的類別區分開來,所謂人以類聚,物以群分,就是這個道理。以前的【機器學習001-007】都是講解的迴歸問題,兩者的不同之處在於:迴歸輸出的結果是實數,並且一般是連續的實數值,而分類問題的輸出結果是離散的某一個類別或不同類別的概率。

最簡單的分類問題是二元分類,將整個樣本劃分為兩個類別,比如將整個人類分為男人和女人(泰國人妖不在考慮範圍內,呵呵)。稍微複雜一點的分類問題是多元分類,它將整個樣本劃分為多個(一般大於兩個)不同類別,比如將家禽資料集可以劃分為:雞,鴨,鵝等,將家畜樣本劃分為:狗,豬,牛,羊等等。

下面從一個最簡單的二元分類問題入手,看看二元分類器是如何構建的。


1. 準備資料集

由於二元分類問題比較簡單,此處我們自己構建了一些資料點,並將這些資料點按照不同類別放入不同變數中,比如把所有第0類別的資料點都放置到class_0中,把所有第1類別的資料點放入class_1中,如下所示。

# 首先準備資料集
# 特徵向量
X = np.array([[3,1], [2,5], [1,8], [6,4], [5,2], [3,5], [4,7], [4,-1]]) # 自定義的資料集
# 標記
y = [0, 1, 1, 0, 0, 1, 1, 0]

# 由於標記中只含有兩類,故而將特徵向量按照標記分割成兩部分
class_0=np.array([feature for (feature,label) in zip(X,y) if label==0])
print(class_0) # 確保沒有問題
class_1=np.array([feature for (feature,label) in zip(X,y) if label==1])
print(class_1)

# 劃分也可以採用如下方法:兩個列印後結果一樣
# class_0=np.array([X[i] for i in range(len(X)) if y[i]==0])
# print(class_0)
# class_1=np.array([X[i] for i in range(len(X)) if y[i]==1])
# print(class_1)
複製程式碼

-------------------------------------輸---------出--------------------------------

[[ 3 1] [ 6 4] [ 5 2] [ 4 -1]] [[2 5] [1 8] [3 5] [4 7]]

--------------------------------------------完-------------------------------------

上面雖然構建了資料點,但是難以直觀的看清這個二分類問題的資料點有什麼特點,所以為了有更加直觀的認識,一般會把資料點的散點圖畫出來,如下所示:

# 在圖中畫出這兩個不同類別的資料集,方便觀察不同類別資料的特點
plt.figure()
plt.scatter(class_0[:,0],class_0[:,1],marker='s',label='class_0')
plt.scatter(class_1[:,0],class_1[:,1],marker='x',label='class_1')
plt.legend()
複製程式碼

兩個類別資料集的分佈圖

########################小**********結###############################

1,本次研究的二分類問題是極其簡單的分類問題,故而構建了8個樣本的兩個類別的資料點,每個類別有四個點。

2,為了更加直觀的檢視資料點的分佈特點,一般我們要把資料點畫在平面上,對資料點的分佈情況有一個初步的瞭解,便於後面我們採用哪種分類器。

3,本次構建的資料集是由8行2列構成的特徵矩陣,即8個樣本,每個樣本有兩個features.

#################################################################


2. 構建簡單線性分類器

所謂線性可分問題,是指在平面上可以通過一條直線(或更高維度上的,一個平面)來將所有資料點劃分開來的問題,“可以用直線分開”是線性可分問題的本質。相對應的,“不可以用直線分開”便是線性不可分問題的本質,對於線性不可分問題,需要用曲線或曲面來將這些資料分開,對應的就是非線性問題。比如,上面自己定義的資料集可以用簡單的直線劃分開來,比如可以採用y=x這條直線分開,如下所示:

# 從上面圖中可以看出,可以畫一條直線輕鬆的將class_0和class_1兩個資料點分開
# 其實有很多直線可以起到分類器的效果,此處我們只用最簡單的y=x作為演示
plt.figure()
plt.scatter(class_0[:,0],class_0[:,1],marker='s',label='class_0')
plt.scatter(class_1[:,0],class_1[:,1],marker='x',label='class_1')
plt.plot(range(10),range(10),label='line_classifier') # 此處x=range(10), y=x
plt.legend()
複製程式碼

使用一條直線可以將這兩個資料集分開

實際上,可以採用非常多的直線來將本資料集的兩個類別區分開來,如下圖所示,這些直線是在斜率和截距上稍微調整而來。

可以分開的直線有很多條

那麼,這麼多直線都可以解決簡單分類問題,肯定會有一條最佳直線,能夠達到最佳的分類效果。下面,使用sklearn模組中的SGD分類器構建最佳直線分類器。如下程式碼:

# 上面雖然隨機的選擇了一條直線(y=x)作為分類器,但很多時候我們不知道分類
# 下面構建一個SGD分類器,它使用隨機梯度下降法來訓練
# 訓練之前需要對資料進行標準化,保證每個維度的特徵資料方差為1,均值為0,避免某個特徵值過大而成為影響分類的主因
from sklearn.preprocessing import StandardScaler
ss=StandardScaler()
X_train=ss.fit_transform(X) # 由於本專案資料集太少,故而全部用來train

# 構建SGD分類器進行訓練
from sklearn.linear_model import SGDClassifier
sgdClassifier=SGDClassifier(random_state=42)
sgdClassifier.fit(X_train,y) # y作為label已經是0,1形式,不需進一步處理

# 使用訓練好的SGD分類器對陌生資料進行分類
X_test=np.array([[3,2],[2,3],[2.5,2.4],[2.4,2.5],[5,8],[6.2,5.9]])
X_test=ss.fit_transform(X_test) # test set也要記過同樣的處理
test_predicted=sgdClassifier.predict(X_test)
print(test_predicted)
複製程式碼

-------------------------------------輸---------出--------------------------------

[0 1 1 1 1 0]

--------------------------------------------完-------------------------------------

########################小**********結###############################

1,使用sklearn中的SGDClassifier可以對資料集進行簡單的線性分類,達到比較好的分類效果。

2,在資料集的特徵上,貌似x>y時,資料屬於class_0, 而x<y時,資料屬於class_1,SGDClassifier模型在測試資料集上也基本能夠正確劃分,只有在x和y大體相等的關鍵點處容易出現錯誤判斷。

#################################################################


注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。

參考資料:

1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯

相關文章