本文主要介紹邏輯回歸的基本概念,并結合實際案例說明邏輯回歸的應用。
從線性回歸到邏輯回歸
在【機器學習系列2——線性回歸】和【機器學習系列3——多項式回歸】中我們結合實際案例解釋了線性回歸的基本概念和應用。線性回歸解決的是連續型數值的預測問題,例如預測房價,產品銷量等。
但是生活中存在大量的分類問題,例如:判斷垃圾短息,垃圾郵件;銀行判斷是否給用戶放貸;電商判斷用戶是否會喜歡某類商品,從而確定是否要推薦給用戶等等;
解決以上這類問題,線性回歸不再適用,我們需要采用邏輯回歸(Logistic Regression)。
邏輯回歸解決的是分類問題,從分類數量上看,有二項分類和多項分類。
二項分類一般是0和1,或者True和False的問題,例如預測病人是否患某種疾病;
多項分類中,分類標簽比較多,例如根據歌詞將歌曲分類,可以有多種類別:民謠,搖滾,輕音樂,說唱等等。我們通常把構建的用于分類的邏輯回歸模型叫做分類器。
雖然機器學習算法中還有很多其他的分類算法,但邏輯回歸是最常用的,也是首選的分類算法,它的原理非常簡單,但是分類能力非常強大。一般我們拿到一個分類問題,都會先用邏輯回歸看一下效果,然后再去嘗試復雜的算法,看能否在邏輯回歸的效果基礎上有進一步的效果改進。
邏輯回歸基本概念
假設t為連續隨機變量,則t服從邏輯分布是指,它具有下列分布函數(Sigmoid函數):
該公式包含兩個關鍵的部分:
- 指數變換:將所有的數值轉換為正數,即e^t
- 歸一化:將所有數值轉換為0到1之間的數值,即1/(1+t)
所以邏輯函數輸出的,是0到1之間的數值,表示的是一個概率。
我們可以用Python看一下邏輯分布函數的圖形化表示。
由于 t = 6的時候,邏輯分布函數 f(t) 的值趨近于1,t = -6時,f(t) 的值趨近于0,因此我們觀察 t = {-6,6} 范圍內的邏輯分布函數圖形:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
x = np.arange(-6,6,0.01)
y = np.exp(x)/(1 + np.exp(x))
plt.figure()
plt.axis([-6,6,0,1])
plt.grid(True)
plt.plot(x, y)
plt.show()
一般化邏輯回歸模型
我們一般定義邏輯回歸模型的預測函數如下:
其中:
其實這就是我們的一元線性回歸模型;一元線性回歸的輸出結果取值范圍是負無窮到正無窮,把它的輸出結果,當做邏輯回歸模型的輸入,我們就把數值問題,映射為了(0,1)之間的數值,從而轉換成為了分類問題(0還是1),即求y=1的概率問題(一般概率大于0.5,分類為1,概率小于0.5,分類為0)。
二項邏輯回歸模型
所謂二項邏輯回歸,就是y的取值只有兩種可能:我們一般分別用0和1表示。
它的模型表示如下:
也即整合后的模型:
只要計算出θ參數的值,我們就能根據輸入x來分別計算y = 0和y=1的概率,根據概率大小,就可以判斷x應該歸類到0還是1。
一般我們可以簡化二項邏輯回歸模型,用基本的邏輯回歸模型表示:
案例
假設我們有一個樣本大小為50的GPA成績和錄取與否的樣本數據,我們希望通過邏輯回歸,根據GPA成績預測學生是否能被錄取。數據樣例如下所示,其中【gpa】是介于0到5之間的數值,表示學生的GPA成績,【Admission】0代表沒有錄取,1代表被錄取:
ID | gpa | Admission |
---|---|---|
1 | 3.177 | 0 |
2 | 3.412 | 0 |
3 | 2.728 | 0 |
4 | 4.523 | 1 |
5 | 3.781 | 1 |
數據基本信息探索:
我們使用Scikit-learn的linear_model中的LogisticRegression來做邏輯回歸。
from sklearn.linear_model import LogisticRegression
from sklearn.cross_validation import train_test_split, cross_val_score
#讀取數據
data = pd.read_csv('admissions.csv')
#拆分訓練集和測試集
xtrain, xtest, ytrain, ytest = train_test_split(data['gpa'], data['Admission'])
x_train = xtrain.reshape(len(xtrain),1)
y_train = ytrain.reshape(len(ytrain),1)
x_test = xtest.reshape(len(xtest),1)
y_test = ytest.reshape(len(ytest),1)
#初始化模型
lor = LogisticRegression()
#擬合
lor.fit(x_train, y_train)
#預測,其中predict預測函數返回標簽數值,即0或者1,predict_proba函數返回概率數值,即標簽為0的概率和標簽為1的概率,它是基于默認的0.5為閾值,概率大于0.5,標簽為1,概率小于0.5,標簽為0
pred_label = pd.DataFrame(lor.predict(x_test), columns=['label'])
pred_proba = pd.DataFrame(lor.predict_proba(x_test), columns=['proba_0', 'proba_1'])
#數據合并
gpa = pd.DataFrame(x_test, columns=['gpa'])
admission = pd.DataFrame(y_test, columns=['admission'])
pd.concat([gpa, admission, pred_label, pred_proba], axis = 1)
最終預測所得結果如下:
模型效果評估
二項分類模型效果的評估方法有很多,常用的有準確率(accuracy),精確率(precision),召回率(recall),ROC(Receiver Operating Characteristic),AUC(Area Under the Curve)。
準確率
準確率 = 正確預測數 / 測試集樣本數
在上面的栗子中,我們的測試集樣本數 = 13,其中正確預測的(admission = label)有8個,所以這里準確率 = 8 / 13 = 0.61538461538461542
我們也可以用Scikit-learn中自帶的方法來計算準確率:
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(admission, pred_label)
但是準確度只能用來評估模型在測試集上的準確度,無法評估非測試集的預測準確度。
精確率
首先來看一下,在二項分類問題中,我們可以將觀察值和預測值的關系用以下混淆矩陣表示出來:
精確率 = TP / (TP + FP)
在上面的栗子中,精確率指的是,在預測出來的被錄取學生人數中,有多少是真正被錄取的。所以測試集反映出的 精確率 = 7/12 = 0.5833333333333334
召回率
召回率在醫學領域也叫做靈敏度(Sensitivity),它反映的是在模型識別陽性結果(Positive Result)的效果。
召回率 = TP / (TP + FN)
在上面的栗子中指的是,在正真被錄取的學生人數中,被正確正確預測出來的人數。所以測試集反映的 召回率 = 7/7 = 1。
可以看出,精確率和召回率各自含有的信息都很少,它們對分類器效果的觀察角度不同。精確率和召回率都不能從表現差的一種分類器中區分出好的分類器。
Scikit-learn中提供了計算精確率和召回率的計算函數,我們介紹使用方法之前,順便介紹一下K折交叉驗證(K-Fold Cross Validation)。
我們在【機器學習系列1——機器學習概況】中介紹到,在數據集本身非常少,可能存在訓練集不夠而無法很好的構建模型的情 況下,可以使用交叉驗證方法。主體思想就是將數據集分成K等份,用其中K-1份做訓練,剩余1份做測試,然后依次迭代。如下圖所示,這里K=5。
在數據集非常大的情況下,數據集拆分不宜過多,否則迭代將耗費太多時間。
Scikit-learn提供了K折交叉驗證所需要的函數KFold,可以直接調用。
下面我們結合K折交叉驗證,來說明二項分類邏輯回歸中的精確率和召回率的計算。
from sklearn.cross_validation import KFold
from sklearn.cross_validation import cross_val_score
#shuffle = True表示打亂數據集順序,random_state = 1表示在shuffle設置為True的時候,隨機種子的值,相當于如果設置該值,每次執行代碼時,數據集的拆分結果都是固定的,如果不設置,每次數據集的拆分都是隨機的,計算所得結果也會有差異
kf = KFold(len(data), 5, shuffle = True,random_state = 1)
accuracy = cross_val_score(lor, data[['gpa']], data['Admission'], cv = kf, scoring = 'accuracy')
recall = cross_val_score(lor, data[['gpa']], data['Admission'], cv = kf, scoring = 'recall')
precision = cross_val_score(lor, data[['gpa']], data['Admission'], cv = kf, scoring = 'precision')
print('準確率:', accuracy.mean(), accuracy)
print('精確率:', precision.mean(), precision)
print("召回率:", recall.mean(), recall)
所得結果為:
準確率: 0.68 [ 1. 0.3 0.5 0.9 0.7]
精確率: 0.677777777778 [ 1. 0.22222222 0.5 0.83333333 0.83333333]
召回率: 0.942857142857 [ 1. 1. 1. 1. 0.71428571]
由于這里采用了交叉驗證,和之前計算的精確率結果稍有差異。
綜合評價指標(F-measure)
一般情況下,我們希望精確率和召回率都越高越好,但在一些極端情況下,兩者是矛盾的。這時候我們需要用綜合評價指標:精確率和召回率的調和均值(harmonic mean),或加權平均值,也稱為F-measure或F-score。
F-measure = 2 * Precision * Recall / (Precision + Recall)
F-measure 平衡了精確率和召回率。當F-measure較高的時候,說明模型效果比較好。
Scikit-learn中提供了直接計算F-measure的方法:
f1 = cross_val_score(lor, data[['gpa']], data['Admission'], cv = kf, scoring = 'f1')
綜合評價指標: 0.741724941725 [ 1. 0.36363636 0.66666667 0.90909091 0.76923077]
ROC & AUC
介紹ROC之前,需要先介紹假陽性概率(False Positive Rate),即所有陰性樣本中,分類器預測出的假陽性樣本的比率,也叫做誤警率,FPR = FP / (TN + FP)
ROC曲線(Receiver Operating Characteristic curve,ROC curve),是以假陽性概率(誤警率)為橫坐標,以真陽性概率(召回率)為縱坐標所畫的曲線。可以理解為它是一系列混淆矩陣的結果組合所畫出的曲線。一般情況下,我們得到一個回歸模型的混淆矩陣,都是在提前設定好一個閾值的前提下獲得的。例如上面案例中,我們以默認的閾值0.5為界限,得到混淆矩陣如下:
預測值 \ 觀察值 | Admitted (1) | Rejected (1) |
---|---|---|
Admitted (1) | 7 | 5 |
Rejected (1) | 0 | 1 |
但如果我們調整這個閾值,得到的混淆矩陣就不同了。在模型預測的所有概率中,每一個概率值都可以作為一個閾值,相應的,我們就能得到一系列的混淆矩陣。對于每個混淆矩陣,我們都能計算出相應的假陽性概率FPR和真陽性概率TPR。然后我們以這些FPR為橫坐標軸,以TPR為縱坐標軸,所得到的曲線就是ROC。
Scikit-learn中提供了ROC曲線對應的函數roc_curve,我們根據上面案例,畫出對應的ROC曲線如下:
from sklearn import metrics
probabilities =lor.predict_proba(x_test)
fpr, tpr, thresholds = metrics.roc_curve(y_test, probabilities[:,1])
plt.plot(fpr, tpr)
plt.show()
因為我們測試集樣本數量太少,畫出來的曲線不太好看。。。
理想的情況下,如果存在一個閾值,大于這個閾值的都是陽性的,低于這個閾值的都是陰性的,那么我們認為這個模型已經能夠非常完美的區分陽性和陰性了,對應到我們的案例中,就是我們找到一個回歸模型,能夠確定一個的閾值,高于這個閾值的都被錄取了,低于這個閾值的都沒有被錄取,那么這個模型就是完美的。此時對應的真陽性概率TPR為1,假陽性概率FPR為0,對應坐標軸上的(0,1)點,即左上角的點。所以如果ROC曲線經過該點,它就是完美的。但事實上,現實生活中不會有這么完美的模型。
AUC(Area Under the Curve)是指ROC曲線下面的面積。從AUC定義的角度來理解,AUC就是從所有標簽為1的樣本中隨機選取一個樣本, 從所有標簽為0的樣本中隨機選取一個樣本,然后根據分類器對兩個隨機樣本進行預測,假設標簽為1的樣本預測為1的概率為p1,標簽為0的樣本預測為1的概率為p0,那么p1>p0的概率就等于AUC。所以AUC反應的是分類器對樣本的排序能力。
一般情況下,AUC值介于0.5和1之間,AUC值越大,說明分類器效果越好,ROC曲線越接近于(0,1)點。AUC值越小,說明分類器效果越差,跟隨機分類沒什么差別,ROC曲線越趨近于下圖中的紅線。如果AUC值小于0.5,則可能是樣本數據標簽出了問題。
另外值得注意的是,AUC對樣本類別是否均衡并不敏感,這也是不均衡樣本通常用AUC評價分類器性能的一個原因。
Python中計算AUC值的方法如下:
auc_score = metrics.roc_auc_score(y_test, probabilities[:,1])
auc_score = 0.9285714285714286