邏輯斯蒂迴歸分類算法[sklearn.linear_model/LogisticRegression/最大似然/梯度下降]

【關鍵詞】Logistics函數,最大似然估計,梯度下降法

1、Logistics迴歸的原理

利用Logistics迴歸進行分類的主要思想是:根據現有數據對分類邊界線建立迴歸公式,以此進行分類。這裏的“迴歸” 一詞源於最佳擬合,表示要找到最佳擬合參數集。

訓練分類器時的做法就是尋找最佳擬合參數,使用的是最優化算法。接下來介紹這個二值型輸出分類器的數學原理

Logistic Regression和Linear Regression的原理是相似的,可以簡單的描述爲這樣的過程:

(1)找一個合適的預測函數,一般表示爲h函數,該函數就是我們需要找的分類函數,它用來預測輸入數據的判斷結果。這個過程是非常關鍵的,需要對數據有一定的瞭解或分析,知道或者猜測預測函數的“大概”形式,比如是線性函數還是非線性函數。

(2)構造一個Cost函數(損失函數),該函數表示預測的輸出(h)與訓練數據類別(y)之間的偏差,可以是二者之間的差(h-y)或者是其他的形式。綜合考慮所有訓練數據的“損失”,將Cost求和或者求平均,記爲J(θ)函數,表示所有訓練數據預測值與實際類別的偏差。

(3)顯然,J(θ)函數的值越小表示預測函數越準確(即h函數越準確),所以這一步需要做的是找到J(θ)函數的最小值。找函數的最小值有不同的方法,Logistic Regression實現時有梯度下降法(Gradient Descent)。

1) 構造預測函數

Logistic Regression雖然名字裏帶“迴歸”,但是它實際上是一種分類方法,用於兩分類問題(即輸出只有兩種)。首先需要先找到一個預測函數(h),顯然,該函數的輸出必須是兩類值(分別代表兩個類別),所以利用了Logistic函數(或稱爲Sigmoid函數),函數形式爲:

該函數形狀爲:

預測函數可以寫爲:

2)構造損失函數

Cost函數和J(θ)函數是基於最大似然估計推導得到的。

每個樣本屬於其真實標記的概率,即似然函數,可以寫成:

所有樣本都屬於其真實標記的概率爲

對數似然函數爲

最大似然估計就是要求得使l(θ)取最大值時的θ,其實這裏可以使用梯度上升法求解,求得的θ就是要求的最佳參數

3) 梯度下降法求J(θ)的最小值

求J(θ)的最小值可以使用梯度下降法,根據梯度下降法可得θ的更新過程:

式中爲α學習步長,下面來求偏導:

上式求解過程中用到如下的公式:

因此,θ的更新過程可以寫成:

因爲式中α本來爲一常量,所以1/m一般將省略,所以最終的θ更新過程爲:

Type Markdown and LaTeX: α2α2

2、實戰

參數:

sklearn.linear_model.LogisticRegression(
    penalty='l2', dual=False, tol=0.0001, C=1.0, fit_intercept=True, 
    intercept_scaling=1, class_weight=None, random_state=None, solver='liblinear',     
    max_iter=100, multi_class='ovr', verbose=0, warm_start=False, n_jobs=1)

solver參數的選擇:

  • “liblinear”:小數量級的數據集
  • “lbfgs”, “sag” or “newton-cg”:大數量級的數據集以及多分類問題
  • “sag”:極大的數據集

LogisticRegression,一共有14個參數,參數說明:參考詳細說明 ↓↓↓↓

  • penalty:懲罰項,str類型,可選參數爲l1和l2,默認爲l2。用於指定懲罰項中使用的規範。newton-cg、sag和lbfgs求解算法只支持L2規範。L1G規範假設的是模型的參數滿足拉普拉斯分佈,L2假設的模型參數滿足高斯分佈,所謂的範式就是加上對參數的約束,使得模型更不會過擬合(overfit),但是如果要說是不是加了約束就會好,這個沒有人能回答,只能說,加約束的情況下,理論上應該可以獲得泛化能力更強的結果。
  • dual:對偶或原始方法,bool類型,默認爲False。對偶方法只用在求解線性多核(liblinear)的L2懲罰項上。當樣本數量>樣本特徵的時候,dual通常設置爲False。
  • tol:停止求解的標準,float類型,默認爲1e-4。就是求解到多少的時候,停止,認爲已經求出最優解。
  • c:正則化係數λ的倒數,float類型,默認爲1.0。必須是正浮點型數。像SVM一樣,越小的數值表示越強的正則化。
  • fit_intercept:是否存在截距或偏差,bool類型,默認爲True。
  • intercept_scaling:僅在正則化項爲”liblinear”,且fit_intercept設置爲True時有用。float類型,默認爲1。
  • class_weight:用於標示分類模型中各種類型的權重,可以是一個字典或者’balanced’字符串,默認爲不輸入,也就是不考慮權重,即爲None。如果選擇輸入的話,可以選擇balanced讓類庫自己計算類型權重,或者自己輸入各個類型的權重。舉個例子,比如對於0,1的二元模型,我們可以定義class_weight={0:0.9,1:0.1},這樣類型0的權重爲90%,而類型1的權重爲10%。如果class_weight選擇balanced,那麼類庫會根據訓練樣本量來計算權重。某種類型樣本量越多,則權重越低,樣本量越少,則權重越高。當class_weight爲balanced時,類權重計算方法如下:n_samples / (n_classes * np.bincount(y))。n_samples爲樣本數,n_classes爲類別數量,np.bincount(y)會輸出每個類的樣本數,例如y=[1,0,0,1,1],則np.bincount(y)=[2,3]。 
    • 那麼class_weight有什麼作用呢? 
      • 在分類模型中,我們經常會遇到兩類問題:
      • 第一種是誤分類的代價很高。比如對合法用戶和非法用戶進行分類,將非法用戶分類爲合法用戶的代價很高,我們寧願將合法用戶分類爲非法用戶,這時可以人工再甄別,但是卻不願將非法用戶分類爲合法用戶。這時,我們可以適當提高非法用戶的權重。
      • 第二種是樣本是高度失衡的,比如我們有合法用戶和非法用戶的二元樣本數據10000條,裏面合法用戶有9995條,非法用戶只有5條,如果我們不考慮權重,則我們可以將所有的測試集都預測爲合法用戶,這樣預測準確率理論上有99.95%,但是卻沒有任何意義。這時,我們可以選擇balanced,讓類庫自動提高非法用戶樣本的權重。提高了某種分類的權重,相比不考慮權重,會有更多的樣本分類劃分到高權重的類別,從而可以解決上面兩類問題。
  • random_state:隨機數種子,int類型,可選參數,默認爲無,僅在正則化優化算法爲sag,liblinear時有用。
  • solver:優化算法選擇參數,只有五個可選參數,即newton-cg,lbfgs,liblinear,sag,saga。默認爲liblinear。solver參數決定了我們對邏輯迴歸損失函數的優化方法,有四種算法可以選擇,分別是: 
    • liblinear:使用了開源的liblinear庫實現,內部使用了座標軸下降法來迭代優化損失函數。
    • lbfgs:擬牛頓法的一種,利用損失函數二階導數矩陣即海森矩陣來迭代優化損失函數。
    • newton-cg:也是牛頓法家族的一種,利用損失函數二階導數矩陣即海森矩陣來迭代優化損失函數。
    • sag:即隨機平均梯度下降,是梯度下降法的變種,和普通梯度下降法的區別是每次迭代僅僅用一部分的樣本來計算梯度,適合於樣本數據多的時候。
    • saga:線性收斂的隨機優化算法的的變重。
    • 總結: 
      • liblinear適用於小數據集,而sag和saga適用於大數據集因爲速度更快。
      • 對於多分類問題,只有newton-cg,sag,saga和lbfgs能夠處理多項損失,而liblinear受限於一對剩餘(OvR)。啥意思,就是用liblinear的時候,如果是多分類問題,得先把一種類別作爲一個類別,剩餘的所有類別作爲另外一個類別。一次類推,遍歷所有類別,進行分類。
      • newton-cg,sag和lbfgs這三種優化算法時都需要損失函數的一階或者二階連續導數,因此不能用於沒有連續導數的L1正則化,只能用於L2正則化。而liblinear和saga通吃L1正則化和L2正則化。
      • 同時,sag每次僅僅使用了部分樣本進行梯度迭代,所以當樣本量少的時候不要選擇它,而如果樣本量非常大,比如大於10萬,sag是第一選擇。但是sag不能用於L1正則化,所以當你有大量的樣本,又需要L1正則化的話就要自己做取捨了。要麼通過對樣本採樣來降低樣本量,要麼回到L2正則化。
      • 從上面的描述,大家可能覺得,既然newton-cg, lbfgs和sag這麼多限制,如果不是大樣本,我們選擇liblinear不就行了嘛!錯,因爲liblinear也有自己的弱點!我們知道,邏輯迴歸有二元邏輯迴歸和多元邏輯迴歸。對於多元邏輯迴歸常見的有one-vs-rest(OvR)和many-vs-many(MvM)兩種。而MvM一般比OvR分類相對準確一些。鬱悶的是liblinear只支持OvR,不支持MvM,這樣如果我們需要相對精確的多元邏輯迴歸時,就不能選擇liblinear了。也意味着如果我們需要相對精確的多元邏輯迴歸不能使用L1正則化了。
  • max_iter:算法收斂最大迭代次數,int類型,默認爲10。僅在正則化優化算法爲newton-cg, sag和lbfgs纔有用,算法收斂的最大迭代次數。
  • multi_class:分類方式選擇參數,str類型,可選參數爲ovr和multinomial,默認爲ovr。ovr即前面提到的one-vs-rest(OvR),而multinomial即前面提到的many-vs-many(MvM)。如果是二元邏輯迴歸,ovr和multinomial並沒有任何區別,區別主要在多元邏輯迴歸上。 
    • OvR和MvM有什麼不同*?* 
      • OvR的思想很簡單,無論你是多少元邏輯迴歸,我們都可以看做二元邏輯迴歸。具體做法是,對於第K類的分類決策,我們把所有第K類的樣本作爲正例,除了第K類樣本以外的所有樣本都作爲負例,然後在上面做二元邏輯迴歸,得到第K類的分類模型。其他類的分類模型獲得以此類推。
      • MvM則相對複雜,這裏舉MvM的特例one-vs-one(OvO)作講解。如果模型有T類,我們每次在所有的T類樣本里面選擇兩類樣本出來,不妨記爲T1類和T2類,把所有的輸出爲T1和T2的樣本放在一起,把T1作爲正例,T2作爲負例,進行二元邏輯迴歸,得到模型參數。我們一共需要T(T-1)/2次分類。
      • 可以看出OvR相對簡單,但分類效果相對略差(這裏指大多數樣本分佈情況,某些樣本分佈下OvR可能更好)。而MvM分類相對精確,但是分類速度沒有OvR快。如果選擇了ovr,則4種損失函數的優化方法liblinear,newton-cg,lbfgs和sag都可以選擇。但是如果選擇了multinomial,則只能選擇newton-cg, lbfgs和sag了。
  • verbose:日誌冗長度,int類型。默認爲0。就是不輸出訓練過程,1的時候偶爾輸出結果,大於1,對於每個子模型都輸出。
  • warm_start:熱啓動參數,bool類型。默認爲False。如果爲True,則下一次訓練是以追加樹的形式進行(重新使用上一次的調用作爲初始化)。
  • n_jobs:並行數。int類型,默認爲1。1的時候,用CPU的一個內核運行程序,2的時候,用CPU的2個內核運行程序。爲-1的時候,用所有CPU的內核運行程序。

總結:

  • 優點:實現簡單,易於理解和實現;計算代價不高,速度很快,存儲資源低。
  • 缺點:容易欠擬合,分類精度可能不高。
  • 其他: 
    • Logistic迴歸的目的是尋找一個非線性函數Sigmoid的最佳擬合參數,求解過程可以由最優化算法完成。
    • 改進的一些最優化算法,比如sag。它可以在新數據到來時就完成參數更新,而不需要重新讀取整個數據集來進行批量處理。
    • 機器學習的一個重要問題就是如何處理缺失數據。這個問題沒有標準答案,取決於實際應用中的需求。現有一些解決方案,每種方案都各有優缺點。
    • 我們需要根據數據的情況,這是Sklearn的參數,以期達到更好的分類效果。

-------------------------------------------------------------------------------------

1) 手寫數字數據集的分類

使用KNN與Logistic迴歸兩種方法

導包:

from sklearn.neighbors import KNeighborsClassifier

from sklearn.linear_model import LogisticRegression

import sklearn.datasets as datasets
from sklearn.model_selection import train_test_split

創建數據集:

iris = datasets.load_iris()

X = iris['data']
y = iris['target']

X.shape
Out: (150,4)


X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.1)
X_test.shape
Out: (15,4)

創建模型,訓練和預測

①knn

knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train,y_train)

# 評估
knn.score(X_test,y_test)
Out: 1.0

# 算法,新數據
knn.score(X_train,y_train)
Out:0.9703703703703703

knn.score(X,y)
Out:0.9666666666666667

②logistics

logistic = LogisticRegression()

# 學習
logistic.fit(X_train,y_train)

logistic.score(X_test,y_test)
Out:1.0

# 算法,運算
logistic.score(X_train,y_train)
Out:0.9703703703703703

logistic.score(X,y)
Out:0.96
'''一招邏輯斯蒂打天下'''

y_ = logistic.predict(X_test)
display(y_test,y_)
Out:
array([1, 1, 1, 2, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2])
array([1, 1, 1, 2, 0, 0, 2, 2, 1, 2, 2, 0, 0, 0, 2])

驗證出手算樣本概率準確率較低

# 鳶尾花3分類
y
Out :
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

''' 
鳶尾花的屬性4個所以:求得的線性迴歸的數據係數4
3行 鳶尾花分成了3類:0,1,2 
'''
coef_ = logistic.coef_
coef_
Out:
array([[ 0.38587596,  1.43118091, -2.20018994, -0.99472477],
       [ 0.44170356, -1.51578807,  0.5087426 , -1.28678969],
       [-1.65593131, -1.57118279,  2.41128425,  2.4580315 ]])

logistic.intercept_
Out:
array([ 0.25119738,  0.98252054, -1.06417625])

z1 = np.dot(X_test[1],coef_[0]) + logistic.intercept_[0]
z2 = np.dot(X_test[1],coef_[1]) + logistic.intercept_[1]
z3 = np.dot(X_test[1],coef_[2]) + logistic.intercept_[2]

p1 = 1/(1 + np.e**-z1) 
p1
Out: 0.026307743509197783

p2 = 1/(1 + np.e**-z2) 
p2
Out: 0.5276282740359096

p3 = 1/(1 + np.e**-z3) 
p3
Out: 0.1104520286545805

y_proba_ = logistic.predict_proba(X_test)
# 每一類(0,1,2)代表着屬於真實值的概率
y_proba_
Out:
array([[6.27212078e-02, 8.03917363e-01, 1.33361429e-01],
       [3.95969549e-02, 7.94156784e-01, 1.66246261e-01],
       [2.13879880e-02, 7.29729879e-01, 2.48882133e-01],
       [8.08742006e-04, 3.33471948e-01, 6.65719310e-01],
       [8.34217842e-01, 1.65683268e-01, 9.88907075e-05],
       [8.18934236e-01, 1.80985882e-01, 7.98825637e-05],
       [2.44679646e-03, 2.65004499e-01, 7.32548704e-01],
       [4.51933705e-02, 4.26488524e-01, 5.28318106e-01],
       [4.28481928e-02, 8.34261996e-01, 1.22889811e-01],
       [1.17617015e-02, 3.32177684e-01, 6.56060614e-01],
       [2.04906038e-04, 4.32615723e-01, 5.67179371e-01],
       [8.70778305e-01, 1.29143433e-01, 7.82620023e-05],
       [9.14259009e-01, 8.57188187e-02, 2.21724636e-05],
       [8.66382774e-01, 1.33609493e-01, 7.73260487e-06],
       [8.32789306e-04, 2.96653463e-01, 7.02513748e-01]])

y_proba_.argmax(axis = 1)
Out:
array([1, 1, 1, 2, 0, 0, 2, 2, 1, 2, 2, 0, 0, 0, 2], dtype=int64)

logistic.predict(X_test)
Out:
array([0, 2, 1, 0, 0, 2, 0, 0, 2, 2, 0, 1, 0, 2, 1])

y_test
Out:
array([0, 2, 1, 0, 0, 2, 0, 0, 2, 2, 0, 1, 0, 2, 1])


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章