1. 邏輯迴歸
線性迴歸算法旨在通過訓練一定量的樣本來預測 連續型數值(Continuous Value)(即迴歸問題)。當然了,聰明的科學家們早就想好如何將線性迴歸運用到 離散型數值(Discrate value) 的預測上了(即分類問題)。我們將這種運用在分類問題上的線性迴歸模型稱之爲 邏輯迴歸(Logistic Regression)。
簡單的來說,邏輯迴歸僅僅是將線性迴歸的輸出值做了一定的處理,並設置一個閾值。當預測的輸出值大於或等於這個閾值時,將樣本分爲一類;當預測的輸出與之小於這個閾值時,將樣本分爲另一類。一個常見的例子就是給線性迴歸結果上加上 Sigmoid 函數(或稱 Logisitc 函數),如此一來,線性迴歸的輸出值就固定在 0 到 1 之間了。我們設置閾值爲 0.5,當預測值大於 0.5 時,認爲樣本爲正類;反之則爲負類。
上圖左邊展示了標準的 Sigmoid 函數,它將整個 x 軸平均分爲 2 部分,即 時樣本爲正,當 時樣本爲負,我們記這個函數爲 。因此,邏輯迴歸的函數可以寫爲,
其中, 是 n+1 維參數向量()。 表示單個樣本,也是 n+1 維向量()。對於每個樣本 ,我們通過計算其 的值來判斷它到底術語正類還是負類,其預測類別爲,
2. 決策邊界 Decision Boundary
線性迴歸和邏輯迴歸最大的不同在於預測值 的計算。即在線性迴歸中,。而在邏輯迴歸中,。若設置分類的閾值爲 0.5,那麼判斷 與 0 的關係,等價於判斷 與 0 的關係。換句話說,如果 大於等於 0,則 爲正類樣本,反之則爲負類樣本。因此, 構成了邏輯迴歸的決策邊界,具體的,
2.1 線性決策邊界
舉個例子,現需要針對下圖中的正類和負類樣本進行分類。每個樣本 都是一個二維向量。現在,假設我們訓練好的參數 ,那麼我們根據邏輯迴歸生成的決策邊界爲 ,
由於這個決策邊界是關於 的線性表達式,故這個決策邊界也成爲線性決策邊界。線性決策邊界是分類問題中最簡單最基礎的。
2.2 非線性決策邊界
另一個例子,假設正類和負類樣本的分佈如下圖所示,我們很難通過一條直線將它們分割開。這個時候我們需要一個非線性的決策邊界。這個時候簡單的 2 項不能很好的分類,我們需要增加到 2 次方的多項式,如 或 。那麼假設學習到的參數 ,那麼對應的決策邊界爲,
因此,只要將回歸方程中的項的次數升高,邏輯迴歸還是具有很強的非線性分類能力的。
3. 代價函數
邏輯迴歸的代價函數 比較特別,它與線性迴歸的代價函數不同,它更多的是一個分段函數形式呈現。也就是說,當樣本 是正類()或負類()時,計算代價的公式是不同的。【這麼做的原因是保持代價函數是凸函數,也即保持只有一個全局優點】
首先,當樣本本來是正類時,根據 可知:預測值越是接近 1,代價越小;當預測值越是接近 0,代價變得非常大(無窮大)。與之相反,當樣本本來是負類時,根據 可知:預測值越是接近 1,代價變得大(無窮大);當預測值越是接近 0,代價越小。這個設計不得不說十分巧妙。
通常,爲了簡化代價函數(將多行寫成一行的形式),我們在前面添加參數 和 ,這樣代價函數 x^{(i)} 的代價也可以寫成,
因此總的代價函數 可看做是所有樣本 的代價之和( 表示樣本個數),記爲,
4. 參數優化
在邏輯迴歸中,我們沿用之前介紹的梯度下降算法(Gradient Descent)。通過每次迭代中對參數的調整來實現代價函數最小化,邏輯迴歸同樣是求解出使得代價函數最小的參數集合 ,
同樣的,我們對代價函數求導,發現其梯度與線性迴歸的梯度一模一樣,
每一輪迭代,調整的參數也是類似,令 爲學習率,則參數項 調整過程爲,
我們將 j 推廣到一般情況,即每次對待看做是對 上的矩陣操作,令 和 分別表示特徵集合和標籤集合,我們得到每次迭代的新的 應該等於(等式同樣適用於線性迴歸),
可以說,參數調優過程跟線性迴歸一模一樣。但是注意,邏輯迴歸的 與線性迴歸的不同。
5. Sklearn 實現
Sklearn 同樣提供了邏輯迴歸算法的現成品,即 sklearn.linear_model.LogisticRegression 類1 。實例代碼如下,函數logistic_regression_example()
建立了一個邏輯迴歸模型的框架,函數 set_arguments()
列出了邏輯迴歸中比較重要的參數。
from sklearn.linear_model import LogisticRegression
def logistic_regression_example(args, train_x, train_y):
# fit the train_x, train_y
clf = LogisticRegression()
clf.set_params(**args)
clf.fit(train_x,train_y)
return clf
def set_arguments():
args = dict()
args['penalty']='elasticnet' # 正則化方法, {'l1','l2','elasticnet',none'}
args['l1_ratio']=0.5 # elasticnet 正則化情況下的 L1 正則化的比例
args['class_weight']='balanced' # 類權值, {'balanced',dict={0:0.5,1:0.5}}
args['max_iter']=800 # 迭代次數, default=100
args['tol']=1e-4
args['solver'] = 'saga' # 優化算法, {‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}
args['random_state']=0 # 隨機種子
return args
值得一提的是優化算法的參數 solver
,它對邏輯迴歸的預測結果影響重大。默認的選擇有 ‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’(具體見博文2)。不同的優化算法對應着不同的正則化的懲罰項 ,即penalty
參數。‘liblinear’ 在小數據集上效果不錯,此時 penalty
不能等於 ‘none’。其他的 ‘newton-cg’, ‘lbfgs’, ‘sag’ and ‘saga’ 算法,此時 penalty
必須等於 ‘l2’ 或者 ‘none’。支持 penalty
=‘slasticnet’ 的只有 ‘saga’ 算法。這個在設置參數時應該留意。
另外,這個 class_weight
表示類權重,也就是說分類器分錯不同類的樣本的代價是不同的,這個參數可以取 ‘balanced’,也可以取一個字典,裏面標記不同權重的比例,如 ‘{0:0.5,0:0.5}’。不同的權重會導致決策邊界向正類或負類的方向偏移,這個參數也是處理類不平衡問題的一個好辦法。
最後,不同的參數調節出來的分類效果也是不一樣的,如下面 4 個子圖所示。由此看來,機器學習的調參確實是很玄學。