邏輯迴歸(分類算法)

轉載自:【菜菜的sklearn】05 邏輯迴歸

1 概述

1.1 邏輯迴歸(分類算法)(理論)

線性迴歸模型
Y=β0+β1X++βnXn Y=\beta _0+\beta _1X+\cdots +\beta _nX_n

通常是處理因變量 Y 是連續變量的問題,

如果因變量是定性變量 (比如:yes 或 no,0 或 1,好或壞,輸或贏),線性迴歸模型就不再適用了,需採用邏輯迴歸模型解決。
在這裏插入圖片描述

邏輯迴歸算法:雖然名字中帶有迴歸兩個字,但它卻不是迴歸算法,而是一個經典的二分類算法

換句話說,邏輯迴歸就是用類似迴歸的思路來解決分類問題,本質是由線性迴歸變化而來的,一種廣泛使用於分類問題中的廣義迴歸算法,即:

y=g1(wTx+b) y=g^{-1}\left( w^Tx+b \right)

主要解決二分類問題,用來表示某件事情發生的可能性


設 研究分類觀察結果爲(Y);影響因素爲(X)

因變量Y:二分類或多分類
自變量X:可以是連續的,也可以是分類的。

邏輯迴歸在處理分類問題時,其分類邊界不一定都是線性的,也可以是非線性的。


考慮二分類任務,其輸出標記 y∈{0,1},而線性迴歸模型產生的預測值 z=β0+β1x1++βnxnz=\beta _0+\beta _1x_1+\cdots +\beta _nx_n 是實值,於是,需要將實值 z 轉換爲 0/1 值。最理想的是“單位階躍函數”:

y={0z<00.5z=01z>0 y=\left\{ \begin{array}{l} 0\text{,}z<0\\ \\ 0.5\text{,}z=0\\ \\ 1\text{,}z>0\\ \end{array} \right.

但是階躍函數不是連續的,因此考慮用另一函數代替階躍函數,即sigmoid函數:

引入聯繫函數(link function),將線性迴歸方程 z 變換爲 y(z),並且令 y(z) 的值分佈在(0,1)之間,且當y(z)接近0時樣本的標籤爲類別0,當y(z)接近1時樣本的標籤爲類別1,這樣就得到了一個分類模型。而這個聯繫函數對於邏輯迴歸來說,就是Sigmoid函數

具體:
對於線性迴歸模型
z=β0+β1x1++βnxn z=\beta _0+\beta _1x_1+\cdots +\beta _nx_n

z 爲實值,需要將實值z轉換爲0/1值:

若將 y 視爲樣本 x 作爲正例的可能性,則 1−y 是其反例的可能性,兩者的比值稱爲“機率“(odds),反映了 x 作爲正例的相對可能性。對機率取對數則得到對數機率(邏輯迴歸也被稱爲”對數機率迴歸“):


z=log(y/(1y)) z=log\left( y/\left( 1-y \right) \right)
log(y1y)=β0+β1x1++βnxn \log \left( \frac{y}{1-y} \right) =\beta _0+\beta _1x_1+\cdots +\beta _nx_n

其中 y 是[0,1]之間的數值,可以認爲是某件事情發生的可能性


注:邏輯迴歸返回概率這個命題,這種說法嚴謹嗎?

事實上,雖然 y + (1-y) =1 ,但是 y 不是概率,因爲 y 的概率密度在(,+)\left( -\infty ,+\infty \right)上面的積分(面積)不等於1,實際上積分值是正無窮。

但目前多數人都是以”返回概率“的方式來理解邏輯迴歸,並且這樣使用它的性質


變換後得到:

在這裏插入圖片描述

在這裏插入圖片描述


Sigmoid函數的公式和性質
Sigmoid函數是一個S型的函數,當自變量z趨近正無窮時,因變量y(z)趨近於1,而當z趨近負無窮時,y(z)趨近於0,它能夠將任何實數映射到(0,1)區間,使其可用於將任意值函數轉換爲更適合二分類的函數。

因爲這個性質,Sigmoid函數也被當作是歸一化的一種方法,與之前的MinMaxSclaer同理,是屬於數據預處理中的“縮放”功能,可以將數據壓縮到[0,1]之內。區別在於,MinMaxScaler歸一化之後,是可以取到0和1的(最大值歸一化後就是1,最小值歸一化後就是0),但Sigmoid函數只是無限趨近於0和1。


將對數機率log(y/(1y))\log \left( y/\left( 1-y \right) \right)寫爲概率形式:

logP(y=1x)1P(y=1x)=logP(y=1x)P(y=0x)=β0+β1x1++βnxn \log \frac{P\left( y=1|x \right)}{1-P\left( y=1|x \right)}=\log \frac{P\left( y=1|x \right)}{P\left( y=0|x \right)}=\beta _0+\beta _1x_1+\cdots +\beta _nx_n

顯然有
P(y=1x)=eβ0+β1x1++βnxn1+eβ0+β1x1++βnxn   P\left( y=1|x \right) =\frac{e^{\beta _0+\beta _1x_1+\cdots +\beta _nx_n}}{1+e^{\beta _0+\beta _1x_1+\cdots +\beta _nx_n}}\,\,

P(y=0x)=11+eβ0+β1x1++βnxn   P\left( y=0|x \right) =\frac{1}{1+e^{\beta _0+\beta _1x_1+\cdots +\beta _nx_n}}\,\,

1.2 爲什麼需要邏輯迴歸

  1. 邏輯迴歸對線性關係的擬合效果好,比如邏輯迴歸應用在金融領域
  2. 邏輯迴歸計算快:對於線性數據,邏輯迴歸的擬合和計算都非常快,計算效率優於SVM和隨機森林
  3. 邏輯迴歸返回的分類結果不是固定的 0或1,而是以小數形式呈現的類概率數字:因此可以把邏輯迴歸返回的結果當成連續型數據來利用
  4. 邏輯迴歸抗噪能力強,在小數據集上表現更好

邏輯迴歸數學目的是求解能夠讓模型對數據擬合程度最高的參數的值,以此構建預測函數 y(x),然後將特徵矩陣輸入預測函數來計算出邏輯迴歸的結果y。注意,雖然我們熟悉的邏輯迴歸通常被用於處理二分類問題,但邏輯迴歸也可以做多分類。

1.3 sklearn中的邏輯迴歸

在這裏插入圖片描述

2 linear_model.LogisticRegression

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='warn', 
				max_iter=100,
				multi_class='warn', 
				verbose=0, 
				warm_start=False, 
				n_jobs=None)

2.1 二元邏輯迴歸的損失函數

2.1.1 損失函數的概念與解惑

邏輯迴歸有着基於訓練數據求解參數 β\beta 的需求,並且希望訓練出來的模型能夠儘可能地擬合訓練數據,即模型在訓練集上的預測準確率越靠近100%越好。

因此,使用”損失函數“這個評估指標,來衡量參數爲 β\beta 的模型擬合訓練集時產生的信息損失的大小,並以此衡量參數 β\beta 的優劣

在求解參數 β\beta 時,追求損失函數最小,讓模型在訓練數據上的擬合效果最優,即預測準確率儘量靠近100%。

關鍵概念:損失函數

  • 衡量參數 β\beta 的優劣的評估指標,用來求解最優參數的工具
  • 損失函數小,模型在訓練集上表現優異,擬合充分,參數優秀
  • 損失函數大,模型在訓練集上表現差勁,擬合不足,參數糟糕
  • 追求能夠讓損失函數最小化的參數組合

注意:沒有”求解參數“需求的模型沒有損失函數,比如KNN,決策樹

邏輯迴歸的損失函數是由極大似然估計推導出來的,具體結果可以寫作:
J(β)=i=1m(yilog(yβ(xi))+(1yi)log(1yβ(xi))) J\left( \beta \right) =-\sum_{i=1}^m{\left( y_i*\log \left( y_{\beta}\left( x_i \right) \right) +\left( 1-y_i \right) *\log \left( 1-y_{\beta}\left( x_i \right) \right) \right)}
其中,
β\beta 表示求解出來的一組參數,m是樣本的個數,
yiy_i 是樣本 i 上真實的標籤,
yβ(xi)y_{\beta}\left( x_i \right) 是樣本 i 上,基於參數β\beta 計算出來的邏輯迴歸返回值,
xix_i 是樣本 i 各個特徵的取值(行)

我們的目標是求解出使損失函數 J(β)J\left( \beta \right) 最小的 β\beta 取值。注意,在邏輯迴歸的本質函數y(x)裏,特徵矩陣x是自變量,參數是 β\beta 。但在損失函數中,參數β\beta 是損失函數的自變量,x 和 y 都是已知的特徵矩陣和標籤,相當於是損失函數的參數。

2.2 重要參數 penalty & C

2.2.1 正則化

如果模型在訓練集上表示優秀,卻在測試集上表現糟糕,模型就會過擬合。
對邏輯迴歸中過擬合的控制,通過正則化來實現

正則化是用來防止模型過擬合的過程,常用的有L1正則化L2正則化兩種選項,分別通過在損失函數後加上參數向量 的L1範式和L2範式的倍數來實現。這個增加的範式,被稱爲“正則項”,也被稱爲"懲罰項"。

損失函數改變,基於損失函數的最優化來求解的參數取值必然改變,以此來調節模型擬合的程度。其中

  • L1範式表現爲參數向量中的每個參數的絕對值之和,L1正則化爲J(β)L1=CJ(β)+j=1nβj (j1) J\left( \beta \right) _{L1}=C*J\left( \beta \right) +\sum_{j=1}^n{\left| \beta _j \right|}\ \left( j\ge 1 \right)

  • L2範數表現爲參數向量中的每個參數的平方和的開方值,L2正則化爲J(β)L2=CJ(β)+j=1n(βj)2 (j1) J\left( \beta \right) _{L2}=C*J\left( \beta \right) +\sqrt{\sum_{j=1}^n{\left( \beta _j \right) ^2}}\ \left( j\ge 1 \right)

其中 J(β)J\left( \beta \right) 是之前提過的損失函數,C是用來控制正則化程度的超參數,n是方程中特徵的總數,也是方程中參數的總數,j 代表每個參數。在這裏,j 要大於等於1, 是因爲我們的參數向量 β\boldsymbol{\beta } 中,第一個參數是 β0\beta _0 ,是截距,它通常是不參與正則化的。

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='warn', 
				max_iter=100,
				multi_class='warn', 
				verbose=0, 
				warm_start=False, 
				n_jobs=None)
參數 說明
penalty 可以輸入"l1"或"l2"來指定使用哪一種正則化方式,不填寫默認"l2"。注意,若選擇"l1"正則化,參數solver僅能夠使用求解方式”liblinear"和"saga“,若使用“l2”正則化,參數solver中所有的求解方式都可以使用。
C C正則化強度的倒數,必須是一個大於0的浮點數,不填寫默認1.0,即默認正則項與損失函數的比值是1:1。C越小,損失函數會越小,模型對損失函數的懲罰越重,正則化的效力越強,參數會逐漸被壓縮得越來越小。

L1正則化和L2正則化雖然都可以控制過擬合,但它們的效果並不相同。當正則化強度逐漸增大(即C逐漸變小),參數 β\beta 的取值會逐漸變小,但L1正則化會將參數壓縮爲0,L2正則化只會讓參數儘量小,不會取到0。

在L1正則化在逐漸加強的過程中,攜帶信息量小的、對模型貢獻不大的特徵的參數,會比攜帶大量信息的、對模型有巨大貢獻的特徵的參數更快地變成0,所以L1正則化本質是一個特徵選擇的過程,掌管了參數的“稀疏性”。L1正則化越強,參數向量中就越多的參數爲0,參數就越稀疏,選出來的特徵就越少,以此來防止過擬合。因此,如果特徵量很大,數據維度很高,傾向於使用L1正則化。由於L1正則化的這個性質,邏輯迴歸的特徵選擇可以由Embedded嵌入法來完成。

L2正則化在加強的過程中,會盡量讓每個特徵對模型都有一些小的貢獻,但攜帶信息少,對模型貢獻不大的特徵的參數會非常接近於0。通常來說,如果我們的主要目的只是爲了防止過擬合,選擇L2正則化就足夠了。但是如果選擇L2正則化後還是過擬合,模型在未知數據集上的效果表現很差,就可以考慮L1正則化。

而兩種正則化下C的取值,都可以通過學習曲線來進行調整。

下面建兩個邏輯迴歸模型,分別使用L1正則化和L2正則化,進行對比

from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_breast_cancer
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 加載乳腺癌數據,判斷是否患乳腺癌
data = load_breast_cancer()
data.data.shape  # (569, 30) 569個樣本,30個特徵
data.feature_names
'''
array(['mean radius', 'mean texture',..,'worst fractal dimension'], dtype='<U23')
'''

X = data.data # 特徵矩陣
y = data.target
'''
array([0, 0, 0, 0, 0, 0,... 0, 0, 0, 0, 0, 0, 1])
'''

# 實例化 分別使用L1正則化和L2正則化 ,C相同
lrl1 = LR(penalty="l1",solver="liblinear",C=0.5,max_iter=1000)
lrl2 = LR(penalty="l2",solver="liblinear",C=0.5,max_iter=1000)

#邏輯迴歸的重要屬性coef_,查看每個特徵所對應的參數
# 訓練
lrl1 = lrl1.fit(X,y) 
lrl1.coef_  # 30個係數,大部分爲0,相當於進行特徵選擇
'''
array([[ 4.01042427,  0.03193774, -0.13858089, -0.01622997,  0.        ,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.50477939,  0.        , -0.07125383,  0.        ,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        , -0.24588618, -0.128632  , -0.01440303,  0.        ,
         0.        , -2.03173397,  0.        ,  0.        ,  0.        ]])
'''

(lrl1.coef_ != 0).sum(axis=1)  #  array([10])

lrl2 = lrl2.fit(X,y)  
lrl2.coef_ # 均不爲0 ,L2正則化則是對所有的特徵都給出了參數
'''
array([[ 1.61543234e+00,  1.02284415e-01,  4.78483684e-02,
        -4.43927107e-03, -9.42247882e-02, -3.01420673e-01,
        -4.56065677e-01, -2.22346063e-01, -1.35660484e-01,
        -1.93917198e-02,  1.61646580e-02,  8.84531037e-01,
         1.20301273e-01, -9.47422278e-02, -9.81687769e-03,
        -2.37399092e-02, -5.71846204e-02, -2.70190106e-02,
        -2.77563737e-02,  1.98122260e-04,  1.26394730e+00,
        -3.01762592e-01, -1.72784162e-01, -2.21786411e-02,
        -1.73339657e-01, -8.79070550e-01, -1.16325561e+00,
        -4.27661014e-01, -4.20612369e-01, -8.69820058e-02]])
'''

究竟哪個正則化的效果更好呢?還是都差不多?

畫參數 C 的學習曲線

l1 = []
l2 = []
l1test = []
l2test = []
# 劃分訓練集,測試集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)

for i in np.linspace(0.05,1,19):
    # 實例化
    lrl1 = LR(penalty="l1",solver="liblinear",C=i,max_iter=1000)
    lrl2 = LR(penalty="l2",solver="liblinear",C=i,max_iter=1000)
    # 訓練
    lrl1 = lrl1.fit(Xtrain,Ytrain)
    # accuracy_score 準確率
    # lrl1.predict(Xtrain)模型預測的訓練Y
    l1.append(accuracy_score(lrl1.predict(Xtrain),Ytrain))
    # lrl1.predict(Xtest) 模型預測的測試Y
    l1test.append(accuracy_score(lrl1.predict(Xtest),Ytest))
    
    lrl2 = lrl2.fit(Xtrain,Ytrain)
    l2.append(accuracy_score(lrl2.predict(Xtrain),Ytrain))
    l2test.append(accuracy_score(lrl2.predict(Xtest),Ytest))
    
graph = [l1,l2,l1test,l2test]
color = ["#FF0000","#000000","#00bc12","#44cef6"]
label = ["L1","L2","L1test","L2test"]

plt.figure(figsize=(6,6)) # 畫布大小
for i in range(len(graph)):
    plt.plot(np.linspace(0.05,1,19),graph[i],color[i],label=label[i])
    plt.legend(loc=4) #圖例的位置在哪裏?4表示,右下角
    plt.show()

在這裏插入圖片描述
有輕微的過擬合

可見,至少在乳腺癌數據集下,兩種正則化的結果區別不大。但隨着C的逐漸變大,正則化的強度越來越小,模型在訓練集和測試集上的表現都呈上升趨勢,直到C=0.8左右,訓練集上的表現依然在走高,但模型在未知數據集上的表現開始下跌,這時候就是出現了過擬合。我們可以認爲,C設定爲0.8會比較好。在實際使用時,基本就默認使用l2正則化,如果感覺到模型的效果不好,那就換L1試試看。

2.2.2 邏輯迴歸中的特徵工程

當特徵的數量很多的時候,出於業務考慮,也出於計算量的考慮,希望對邏輯迴歸進行特徵選擇來降維。

  • 業務選擇
    說到降維和特徵選擇,首先要想到的是利用自己的業務能力進行選擇,肉眼可見明顯和標籤有關的特徵就是需要留下的。

  • PCA和SVD一般不用
    這兩種方法大多數時候不適用於邏輯迴歸,因爲PCA和SVD的降維結果是不可解釋的,因此一旦降維後,我們就無法解釋特徵和標籤之間的關係了。當然,在不需要探究特徵與標籤之間關係的線性數據上,降維算法PCA和SVD也是可以使用的。

  • 統計方法可以使用,但不是非常必要
    邏輯迴歸對數據的總體分佈和方差沒有要求,也不需要排除特徵之間的共線性

  • 高效的嵌入法embedded
    由於L1正則化會使得部分特徵對應的參數爲0,因此L1正則化可以用來做特徵選擇,結合嵌入法的模塊SelectFromModel,我們可以很容易就篩選出讓模型十分高效的特徵。注意,此時我們的目的是,儘量保留原數據上的信息,讓模型在降維後的數據上的擬合效果保持優秀,因此我們不考慮訓練集測試集的問題,把所有的數據都放入模型進行降維。

from sklearn.linear_model import LogisticRegression as LR # 邏輯迴歸
from sklearn.datasets import load_breast_cancer # 乳腺癌數據集
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score # 交叉驗證
from sklearn.feature_selection import SelectFromModel # 特徵選擇

#導入數據
data = load_breast_cancer()
data.data.shape  # (569, 30)
# 實例化 默認penalty="l1"  這裏C=0.9
LR_ = LR(solver="liblinear",C=0.9,random_state=420)
# 降維前 10折 交叉驗證
cross_val_score(LR_,data.data,data.target,cv=10).mean()
 # 0.9490601503759398 效果不錯

# 特徵選擇  不考慮訓練集測試集的問題,把所有的數據都放入模型進行降維
# norm_order=1 指的是使用L1範式來進行篩選
X_embedded = SelectFromModel(LR_,norm_order=1).fit_transform(data.data,data.target)
X_embedded.shape  # (569, 9) 特徵由30個 降到 9個
# 降維後 交叉驗證
cross_val_score(LR_,X_embedded,data.target,cv=10).mean()
# 0.9368107769423559  模型效果略微下降

能否讓模型的擬合效果更好呢?在這裏,有兩種調整方式:

1)調節SelectFromModel這個類中的參數threshold

這是嵌入法的閾值,表示刪除所有參數的絕對值低於這個閾值的特徵。現在threshold默認爲None,所以SelectFromModel只根據L1正則化的結果來選擇了特徵,即選擇了所有L1正則化後參數不爲0的特徵。

此時,只要調整threshold的值(畫出threshold的學習曲線),就可以觀察不同的threshold下模型的效果如何變化。一旦調整threshold,就不是在使用L1正則化選擇特徵,而是使用模型的屬性.coef_中生成的各個特徵的係數來選擇。coef_雖然返回的是特徵的係數,但是係數的大小和決策樹中的feature_ importances_以及降維算法中的可解釋性方差explained_vairance_概念相似,其實都是衡量特徵的重要程度和貢獻度的,因此SelectFromModel中的參數threshold可以設置爲coef_的閾值,即可以剔除係數小於
threshold中輸入的數字的所有特徵。

fullx = []
fsx = []
threshold = np.linspace(0,abs((LR_.fit(data.data,data.target).coef_)).max(),20)
k=0
for i in threshold:
  X_embedded = SelectFromModel(LR_,threshold=i).fit_transform(data.data,data.target)
  fullx.append(cross_val_score(LR_,data.data,data.target,cv=5).mean())
  fsx.append(cross_val_score(LR_,X_embedded,data.target,cv=5).mean())
  print((threshold[k],X_embedded.shape[1]))
  k+=1
 
plt.figure(figsize=(20,5))
plt.plot(threshold,fullx,label="full")
plt.plot(threshold,fsx,label="feature selection")
plt.xticks(threshold)
plt.legend()
plt.show()
(0.0, 30)
(0.10897274787330495, 17)
(0.2179454957466099, 12)
(0.32691824361991484, 10)
(0.4358909914932198, 8)
(0.5448637393665248, 8)
(0.6538364872398297, 5)
(0.7628092351131347, 5)
(0.8717819829864396, 5)
(0.9807547308597445, 5)
(1.0897274787330495, 5)
(1.1987002266063544, 4)
(1.3076729744796594, 3)
(1.4166457223529643, 2)
(1.5256184702262694, 2)
(1.6345912180995743, 1)
(1.7435639659728792, 1)
(1.8525367138461841, 1)
(1.961509461719489, 1)
(2.070482209592794, 1)

在這裏插入圖片描述
當threshold越來越大,被刪除的特徵越來越多,模型的效果也越來越差,模型效果最好的情況下需要保證有17個以上的特徵。

2)第二種調整方法,是調邏輯迴歸的類LR_,通過畫C的學習曲線來實現:

fullx = []
fsx = []
C=np.arange(0.01,10.01,0.5) # 調C
for i in C:
    LR_ = LR(solver="liblinear",C=i,random_state=420) # 默認使用L1正則化
    # 特徵選擇前
    fullx.append(cross_val_score(LR_,data.data,data.target,cv=10).mean())
    X_embedded = SelectFromModel(LR_,norm_order=1).fit_transform(data.data,data.target)
    # 特徵選擇後
    fsx.append(cross_val_score(LR_,X_embedded,data.target,cv=10).mean())
    print(max(fsx),C[fsx.index(max(fsx))])
    
plt.figure(figsize=(10,5))
plt.plot(C,fullx,label="full")
plt.plot(C,fsx,label="feature selection")
plt.xticks(C)
plt.legend()
plt.show()

0.8822994987468672 0.01
0.9244987468671677 0.51
0.9368107769423559 1.01
0.9438283208020051 1.51
0.9473370927318296 2.01
0.9473370927318296 2.01
0.9490914786967419 3.01
0.9508458646616542 3.51
0.9543546365914788 4.01
0.9543546365914788 4.01
0.9543546365914788 4.01
0.9543546365914788 4.01
0.9543546365914788 4.01
0.9543546365914788 4.01
0.9561090225563911 7.01
0.9561090225563911 7.01
0.9561090225563911 7.01
0.9561090225563911 7.01
0.9561090225563911 7.01
0.9561090225563911 7.01

在這裏插入圖片描述

  • 比較麻煩的係數累加法
  • 簡單快速的包裝法

2.3 梯度下降:重要參數max_iter

邏輯迴歸的數學目的求解能夠讓損失函數 最小化的參數 β\boldsymbol{\beta } 值。對於二元邏輯迴歸來說,有多種方法可以用來求解參數 ,最常見的有梯度下降法(Gradient Descent),座標下降法,牛頓法等,其中又以梯度下降法最爲著名。每種方法都涉及複雜的數學原理,但這些計算在執行的任務其實是類似的。

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='warn', 
				max_iter=100,
				multi_class='warn', 
				verbose=0, 
				warm_start=False, 
				n_jobs=None)

2.3.1 梯度下降求解邏輯迴歸

梯度下降是迭代法的一種,可以用於求解最小二乘問題(線性和非線性都可以)。

在求解機器學習算法的模型參數,即無約束優化問題時,梯度下降(Gradient Descent)是最常採用的方法之一,另一種常用的方法是最小二乘法。

在求解損失函數的最小值時,可以通過梯度下降法來一步步的迭代求解,得到最小化的損失函數和模型參數值。

反過來,如果我們需要求解損失函數的最大值,這時就需要用梯度上升法來迭代了

在機器學習中,基於基本的梯度下降法發展了兩種梯度下降方法,分別爲隨機梯度下降法和批量梯度下降法。


現在有一個帶兩個特徵並且沒有截距的邏輯迴歸 y(β1,β2),兩個特徵所對應的參數分別爲β1和β2 。下面這個華麗的平面就是我們的損失函數 J(β1,β2)J\left( \beta _1,\beta _2 \right) 在以 β1\beta _1β2\beta _2 ,J 爲座標軸的三維立體座標系上的圖像。現在,尋求的是損失函數的最小值,也就是圖像的最低點。

在這裏插入圖片描述

在這個圖像上隨機放一個小球,這個小球就會順着這個華麗的平面滾落,直到滾到深藍色的區域——損失函數的最低點。爲了嚴格監控這個小球的行爲,讓小球每次滾動的距離有限,不讓它一次性滾到最低點,並且最多隻允許它滾動100步,還要記下它每次滾動的方向,直到它滾到圖像上的最低點。

可以看見,小球從高處滑落,在深藍色的區域中來回震盪,最終停留在了圖像凹陷處的某個點上

小球每次滾動的方向都是不同的。

小球不一定滾到了圖像的最低處。

但無論如何,小球停下的就是在現有狀況下可以獲得的唯一點了。如果夠幸運,這個點就是圖像的最低點,那隻要找到這個點的對應座標 (β1,β2,Jmin)\left( \beta _1^*,\beta _2^*,J_{\min} \right),就可以獲取能夠讓損失函數最小的參數取值 了。如此,梯度下降的過程就已經完成。


在這個過程中,小球其實就是一組組的座標點 (β1,β2,J)\left( \beta _1,\beta _2,J \right) ;小球每次滾動的方向就是那一個座標點的梯度向量的方向,因爲每滾動一步,小球所在的位置都發生變化,座標點和座標點對應的梯度向量都發生了變化,所以每次滾動的方向也都不一樣;人爲設置的100次滾動限制,就是sklearn中邏輯迴歸的參數max_iter(最大迭代次數),代表着能走的最大步數,即最大迭代次數。

所以,梯度下降其實就是在衆多(β1,β2)\left( \beta _1,\beta _2 \right) 可能的值中遍歷,一次次求解座標點的梯度向量,不斷讓損失函數的取值 J 逐漸逼近最小值,再返回這個最小值對應的參數取值(β1,β2)\left( \beta _1^*,\beta _2^* \right) 的過程。

2.3.2 梯度下降的概念與解惑

百度百科解釋
梯度的本意是一個向量(矢量),表示某一函數在該點處的方向導數沿着該方向取得最大值,即函數在該點處沿着該方向(此梯度的方向)變化最快,變化率最大(爲該梯度的模)。

對於可微的數量場f(x,y,z)f\left( x,y,z \right) ,以(fx,fy,fz)\left( \frac{\partial f}{\partial x},\frac{\partial f}{\partial y},\frac{\partial f}{\partial z} \right)爲分量的向量場稱爲 f 的梯度或斜量。(注意:這裏的 x y z 與上面邏輯迴歸中的x y z 不是同一個,不要混淆)


核心誤區:到底在哪個函數上,求什麼的偏導數?

一個多元函數的梯度,是對其自變量求偏導的結果,不是對其參數求偏導的結果。

在多元函數(損失函數)上對自變量(邏輯迴歸的預測函數y(x)的參數)求偏導
即是說:
求解梯度,是在損失函數 J(β1,β2)J\left( \beta _1,\beta _2 \right) 上對損失函數自身的自變量 β1\beta _1β2\beta _2 求偏導,而這兩個自變量,剛好是邏輯迴歸的預測函數 y(x)=11+eβTxy\left( x \right) =\frac{1}{1+e^{-\boldsymbol{\beta }^{\boldsymbol{T}}\boldsymbol{x}}} 的參數。


梯度是一個向量,因此它有大小也有方向。它的大小,就是偏導數組成的向量的大小,又叫做向量的模,記作 d 。它的方向,幾何上來說,就是損失函數 J(β)\boldsymbol{J}\left( \boldsymbol{\beta } \right) 的值增加最快的方向,就是小球每次滾動的方向的反方向。只要沿着梯度向量的反方向移動座標,損失函數 J(β)\boldsymbol{J}\left( \boldsymbol{\beta } \right) 的取值就會減少得最快,也就最容易找到損失函數的最小值。

在邏輯迴歸中,損失函數如下所示:

J(β)=i=1m(yilog(yβ(xi))+(1yi)log(1yβ(xi))) J\left( \beta \right) =-\sum_{i=1}^m{\left( y_i*\log \left( y_{\beta}\left( x_i \right) \right) +\left( 1-y_i \right) *\log \left( 1-y_{\beta}\left( x_i \right) \right) \right)}
其中,
β\beta 表示求解出來的一組參數,m是樣本的個數,
yiy_i 是樣本 i 上真實的標籤,
yβ(xi)y_{\beta}\left( x_i \right) 是樣本 i 上,基於參數β\beta 計算出來的邏輯迴歸返回值,
xix_i 是樣本 i 各個特徵的取值(行)

對這個函數上的自變量 β\beta 求偏導,就可以得到梯度向量在第 j 組 β\beta 的座標點上的表示形式:
βjJ(β)=dj=i=1m(yβ(xi)yi)xij \frac{\partial}{\partial \beta _j}J\left( \beta \right) =d_j=\sum_{i=1}^m{\left( y_{\beta}\left( x_i \right) -y_i \right) x_{ij}}

在這個公式下,只要給定一組 β\beta 的取值 βj\beta _j ,再帶入特徵矩陣 x ,就可以求得這一組 β\beta 取值下的預測結果 yβ(xi)y_{\beta}\left( x_i \right),結合真實標籤向量 y ,就可以獲得這一組 β\beta 取值下的梯度向量,其大小表示爲 d。之前說過,我們的目的是在可能的 β\beta 取值上進行遍歷,一次次計算梯度向量,並在梯度向量的反方向上讓損失函數 J 下降至最小值。在這個過程中,β\beta 和梯度向量的大小 d 都會不斷改變,而遍歷 β\beta 的過程可以描述爲:

βj+1=βjαdj=βjαi=1m(yβ(xi)yi)xij \beta _{j+1}=\beta _j-\alpha *d_j=\beta _j-\alpha *\sum_{i=1}^m{\left( y_{\beta}\left( x_i \right) -y_i \right) x_{ij}}
其中 βj+1\beta _{j+1} 是第 j 次迭代後的參數向量,βj\beta _j 是第 j 次迭代是的參數向量, α\alpha 被稱爲步長,控制着每走一步(每迭代一次)後 β\beta 的變化,並以此來影響每次迭代後的梯度向量的大小和方向。

2.3.3 步長的概念與解惑

核心誤區:步長到底是什麼?
步長不是一個物理距離,不是我們所理解的長度或者距離

下面這一張二維平面的求導三角型圖。
在這裏插入圖片描述

圖中的拋物線就是我們的損失函數 J(θ)J\left( \theta \right) (注:這裏 θ\thetaβ\beta 一樣,符號表示不同而已 ) , A(θa,J(θa))A\left( \theta _a,J\left( \theta _a \right) \right) 就是小球最初在的位置, B(θb,J(θb))B\left( \theta _b,J\left( \theta _b \right) \right) 就是一次滾動後小球移動到的位置。從A到B的方向就是梯度向量的反方向,指向損失函數在A點下降最快的方向。而梯度向量的大小是點A在圖像上對 θ\theta 求導後的結果,也是點A切線方向的斜率橙色角的 tan 結果,記作 d

  • 梯度下降每走一步,損失函數減小的量,是損失函數在 θ\theta 變化之後的取值的變化,寫作 J(θa)J(θb)J\left( \theta _a \right) -J\left( \theta _b \right) ,這是二維平面的求導三角型中的“對邊”。

  • 梯度下降每走一步,參數向量的變化,寫作 θaθb\theta _a-\theta _b ,根據向量的迭代公式,就是 步長 * 梯度向量的大小,記作 αd\alpha *d ,這是二維平面的求倒三角形中的“鄰邊”。

  • 梯度下降中每走一步,下降的距離,是 (αd)2+(J(θa)J(θb))2\sqrt{\left( \alpha *d \right) ^2+\left( J\left( \theta _a \right) -J\left( \theta _b \right) \right) ^2} ,是對邊和鄰邊的根號下平方和,是二維平面的求導三角型中的”斜邊“。

所以,步長不是任何物理距離,它甚至不是梯度下降過程中任何距離的直接變化,它是梯度向量的大小 d 上的一個比例,影響着參數向量每次迭代後改變的部分。

步長可以調節損失函數下降的速率。在損失函數降低的方向上,

  • 步長越長,θ\boldsymbol{\theta } 的變動就越大。
  • 步長如果很短,θ\boldsymbol{\theta } 的每次變動就很小。

具體地說,如果步長太大,損失函數下降得就非常快,需要的迭代次數就很少,但梯度下降過程可能跳過損失函數的最低點,無法獲取最優值。而步長太小,雖然函數會逐漸逼近我們需要的最低點,但迭代的速度卻很緩慢,迭代次數就需要很多。

在這裏插入圖片描述


蒐集幾張圖,幫助理解
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

因此,在sklearn當中,通過設置參數 max_iter 最大迭代次數來代替步長,幫助我們控制模型的迭代速度並適時地讓模型停下

  • max_iter越大,代表步長越小,模型迭代時間越長,
  • max_iter越小,則代表步長設置很大,模型迭代時間很短。

迭代結束,獲取到 J(θ)\boldsymbol{J}\left( \boldsymbol{\theta } \right) 的最小值後,就可以找出這個最小值對應的參數向量 θ\boldsymbol{\theta } ,邏輯迴歸的預測函數也就可以根據這個參數向量 θ\boldsymbol{\theta } 來建立了。

max_iter的學習曲線:

# 導包
from sklearn.linear_model import LogisticRegression as LR # 邏輯迴歸
from sklearn.datasets import load_breast_cancer # 乳腺癌數據集
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score # 交叉驗證
from sklearn.feature_selection import SelectFromModel # 特徵選擇
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 加載乳腺癌數據,判斷是否患乳腺癌
data = load_breast_cancer()
data.data.shape  # (569, 30) 569個樣本,30個特徵
data.feature_names
'''
array(['mean radius', 'mean texture',..,'worst fractal dimension'], dtype='<U23')
'''

X = data.data # 特徵矩陣
y = data.target.data # 標籤

l2 = []  # 存放訓練集的擬合效果
l2test = []  # 存放測試集擬合效果
# 劃分訓練集 數據集
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)

for i in np.arange(1,201,10):
    lrl2 = LR(penalty="l2",solver="liblinear",C=0.9,max_iter=i)
    lrl2 = lrl2.fit(Xtrain,Ytrain)
    l2.append(accuracy_score(lrl2.predict(Xtrain),Ytrain))
    l2test.append(accuracy_score(lrl2.predict(Xtest),Ytest))
graph = [l2,l2test]
color = ["black","gray"]
label = ["L2","L2test"]
plt.figure(figsize=(20,5))

for i in range(len(graph)):
    plt.plot(np.arange(1,201,10),graph[i],color[i],label=label[i])
plt.legend(loc=4)
plt.xticks(np.arange(1,201,10))
plt.show()

#我們可以使用屬性.n_iter_來調用本次求解中真正實現的迭代次數
lr = LR(penalty="l2",solver="liblinear",C=0.9,max_iter=300).fit(Xtrain,Ytrain)
lr.n_iter_

當 max_iter 中限制的步數已經走完了,邏輯迴歸卻還沒有找到損失函數的最小值,參數 的值還沒有被收斂,sklearn就會彈出紅色警告

這是在提醒我們:參數沒有收斂,請增大max_iter中輸入的數字。但我們不一定要聽sklearn的。

max_iter很大,意味着步長小,模型運行得會更加緩慢。雖然在梯度下降中追求的是損失函數的最小值,但這也可能意味着我們的模型會過擬合(在訓練集上表現得太好,在測試集上卻不一定)

因此,如果在max_iter報紅條的情況下,模型的訓練和預測效果都已經不錯了,那我們就不需要再增大max_iter中的數目了,畢竟一切都以模型的預測效果爲基準——只要最終的預測效果好,運行又快,那就一切都好,無所謂是否報紅色警告了。

2.4 二元迴歸與多元迴歸:重要參數solver & multi_class

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='warn', 
				max_iter=100,
				multi_class='warn', 
				verbose=0, 
				warm_start=False, 
				n_jobs=None)

之前對邏輯迴歸的討論,都是針對二分類的邏輯迴歸展開,其實sklearn提供了多種可以使用邏輯迴歸處理多分類問題的選項。

比如說,

  • "一對多"(One-vs-rest),簡稱OvR,在sklearn中表示爲“ovr"
    把某種分類類型都看作1,其餘的分類類型都爲0值
  • ”多對多“(Many-vs-Many)的方法,簡稱MvM,在sklearn中表示爲"Multinominal"
    把好幾個分類類型劃爲1,剩下的幾個分類類型劃爲0值

每種方式都配合L1或L2正則項來使用。

在sklearn中,使用參數multi_class來告訴模型,我們的預測標籤是什麼樣的類型。
multi_class

  • ovr’:表示分類問題是二分類,或讓模型使用"一對多"的形式來處理多分類問題。默認是"ovr"。
  • multinomial’:表示處理多分類問題,這種輸入在參數solver是’liblinear’時不可用。
  • auto”:表示會根據數據的分類情況和其他參數來確定模型要處理的分類問題的類型。比如說,如果數據是二分類,或者solver的取值爲"liblinear",“auto"會默認選擇"ovr”。反之,則會選擇"nultinomial"。

注意:默認值將在0.22版本中從"ovr"更改爲"auto"。


sklearn 提供了不同的求解器來計算邏輯迴歸的參數(梯度下降法就是其中之一)

由參數"solver"控制,共有五種選擇。其中“liblinear”是二分類專用,也是現在的默認求解器。

在這裏插入圖片描述

multinomial 和 ovr 的區別怎麼樣:

from sklearn.linear_model import LogisticRegression as LR # 邏輯迴歸
from sklearn.datasets import load_iris
iris = load_iris()
for multi_class in ('multinomial', 'ovr'): 
    clf = LR(solver='sag', 
                             max_iter=100, 
                             random_state=42,
                             multi_class=multi_class).fit(iris.data, iris.target)
    print("training score : %.3f (%s)" % (clf.score(iris.data, iris.target),multi_class))

# training score : 0.987 (multinomial)
# training score : 0.960 (ovr)

如果是多分類問題,一般選擇 multi_class=multinomial

2.5 樣本不平衡與參數class_weight

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='warn', 
				max_iter=100,
				multi_class='warn', 
				verbose=0, 
				warm_start=False, 
				n_jobs=None)

樣本不平衡是指在一組數據集中,標籤的一類天生佔有很大的比例,或誤分類的代價很高,即我們想要捕捉出某種特定的分類的時候的狀況。

因此我們要使用參數class_weight對樣本標籤進行一定的均衡,給少量的標籤更多的權重,讓模型更偏向少數類,向捕獲少數類的方向建模。

該參數默認None,此模式表示自動給與數據集中的所有標籤相同的權重,即自動1:1。當誤分類的代價很高的時候,我們使用”balanced“模式,我們只是希望對標籤進行均衡的時候,什麼都不填就可以解決樣本不均衡問題。

但是,sklearn當中的參數class_weight變幻莫測,可以說是非常難用。

我們有着處理樣本不均衡的各種方法,其中主流的是採樣法,是通過重複樣本的方式來平衡標籤,可以進行上採樣(增加少數類的樣本),比如SMOTE,或者下采樣(減少多數類的樣本)。對於邏輯迴歸來說,上採樣是最好的辦法。

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