逻辑回归(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著