邏輯迴歸(Logistic Regression)可以說是機器學習領域最基礎也是最常用的模型,邏輯迴歸的原理以及推導以及擴展應用幾乎是算法工程師必備技能。醫生的病理診斷、銀行個人行用評估、郵箱分類垃圾郵件等,無不體現邏輯迴歸精巧而廣泛的應用。
1. 邏輯迴歸基本原理
使用邏輯迴歸進行分類,就是要找到這樣的分類邊界,使其能夠儘可能地對樣本進行正確分類,也就是能夠儘可能地將兩種樣本分隔開來。
1.1激活函數
sigmoid 函數定義如下:
其函數圖像爲:
由函數圖像可以看出, sigmoid 函數可以很好地將 (−∞,∞)(−∞,∞) 內的數映射到 (0,1)(0,1) 上。於是我們可以將 g(z)≥0.5g(z)≥0.5 時分爲"1"類, g(z)<0.5g(z)<0.5 時分爲"0"類。
sigmoid 函數實際表達的是將樣本分爲“1”類的概率,這將在本文的最後一部分進行詳細解釋。
1.2. 邏輯迴歸模型函數
在瞭解了分類邊界:
以及 sigmoid 函數 :
我們可以構造出邏輯迴歸模型函數:
使得我們可以對於新樣本 進行輸入,得到函數值 hθ(xnew),根據 hθ(xnew)與0.5的比較來將新樣本進行分類。
1.3. 邏輯迴歸代價函數
回想在線性迴歸中,我們是利用均方誤差來作爲代價函數:
同樣的,假設我們仍舊使用均方誤差來作爲邏輯迴歸大家函數,會出現什麼效果呢?將 g(z)=11+e−zg(z)=11+e−z 帶入上式,我們會發現, J(θ)J(θ) 爲一個非凸函數,也就是說該函數存在許多局部最小值點,在求解參數的過程中很容易陷入局部最小值點,而無法求得真正的最小值點。
我們不妨換一個思路來求解這個問題。在上一節中提到:sigmoid 函數實際表達的是將樣本分爲“1”類的概率,也就是說,使用 sigmoidsigmoid 函數求解出來的值爲類1的後驗估計 p(y=1|x,θ)p(y=1|x,θ) ,故我們可以得到:
p(y=1|x,θ)=hθ(θTx)p(y=1|x,θ)=hθ(θTx)
則
p(y=0|x,θ)=1−hθ(θTx)p(y=0|x,θ)=1−hθ(θTx)
其中 p(y=1|x,θ) 表示樣本分類爲 y=1 的概率,而p(y=0|x,θ) 表示樣本分類爲 y=0y=0 的概率。針對以上二式,我們可將其整理爲:
我們可以得到其似然函數爲:
對數似然函數爲:
可以看到對數似然函數和交叉熵函數在二分類的情況下形式是幾乎一樣的,可以說最小化交叉熵的本質就是對數似然函數的最大化。對數似然函數的本質就是衡量在某個參數下,整體的估計和真實情況一樣的概率,越大代表越相近。而損失函數的本質就是衡量預測值和真實值之間的差距,越大代表越不相近。他們兩個是相反的一個關係,至於損失函數的懲罰程度,可以用參數修正,我們這裏不考慮。所以在對數似然前邊加一個負號代表相反,這樣就把對數似然轉化成了一個損失函數,
1.4. 優化算法
對於以上所求得的代價函數,我們採用梯度下降的方法來求得最優參數。
梯度下降法過程爲:
repeat {
}
其中 αα 爲學習率(learning rate),也就是每一次的“步長”; ΔJ(θ)ΔθjΔJ(θ)Δθj 是梯度,j=1,2,...,nj=1,2,...,n 。
接下來我們對梯度進行求解:
其中:
而又因爲:
則:
因此:
故:
由以上我們可以得到梯度下降過程爲:
repeat {
}
其中 i=1,2,...,m,表示樣本數;j=1,2,..,n,表示特徵數i=1,2,...,m,表示樣本數;j=1,2,..,n,表示特徵數
通過觀察我們可以發現,邏輯迴歸梯度下降中參數的更新公式同線性迴歸的一樣。
2.
import numpy as np
class LogisticRegression():
def __init__(self):
# seeding for random number generation
np.random.seed(1)
# converting weights to a 3 by 1 matrix with values from -1 to 1 and mean of 0
self.synaptic_weights = 2 * np.random.random((3, 1)) - 1
def sigmoid(self, x):
#applying the sigmoid function
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(self, x):
#computing derivative to the Sigmoid function
return x * (1 - x)
def train(self, training_inputs, training_outputs, training_iterations):
#training the model to make accurate predictions while adjusting weights continually
for iteration in range(training_iterations):
#siphon the training data via the neuron
output = self.forward(training_inputs)
#computing error rate for back-propagation
error = training_outputs - output
#performing weight adjustments
adjustments = np.dot(training_inputs.T, error * self.sigmoid_derivative(output))
self.synaptic_weights += adjustments
def forward(self, inputs):
#passing the inputs via the neuron to get output
#converting values to floats
inputs = inputs.astype(float)
output = self.sigmoid(np.dot(inputs, self.synaptic_weights))
return output
if __name__ == "__main__":
#initializing the neuron class
lr= LogisticRegression()()
print("Beginning Randomly Generated Weights: ")
print(lr.synaptic_weights)
#training data consisting of 4 examples--3 input values and 1 output
training_inputs = np.array([[0,0,1],
[1,1,1],
[1,0,1],
[0,1,1]])
training_outputs = np.array([[0,1,1,0]]).T
#training taking place
lr.train(training_inputs, training_outputs, 15000)
print("Ending Weights After Training: ")
print(lr.synaptic_weights)
user_input = [int(x) for x in input().split()]
print("Output data: ")
print(lr.forward(np.array(user_input)))
參考文獻:
[1]《統計學方法》李航著
[2]《機器學習實戰》Peter Harrington著
[3] https://blog.csdn.net/zjuPeco/article/details/77165974
[4] https://blog.csdn.net/ligang_csdn/article/details/53838743