逻辑回归的定义
简单来说, 逻辑回归(Logistic Regression)是一种用于解决二分类(0 or 1)问题的机器学习方法,用于估计某种事物的可能性。比如某用户购买某商品的可能性,某病人患有某种疾病的可能性,以及某广告被用户点击的可能性等。 注意,这里用的是“可能性”,而非数学上的“概率”,logisitc回归的结果并非数学定义中的概率值,不可以直接当做概率值来用。该结果往往用于和其他特征值加权求和,而非直接相乘。
那么逻辑回归与线性回归是什么关系呢?
逻辑回归(Logistic Regression)与线性回归(Linear Regression)都是一种广义线性模型(generalized linear model)。逻辑回归假设因变量 y 服从伯努利分布,而线性回归假设因变量 y 服从高斯分布。 因此与线性回归有很多相同之处,去除Sigmoid映射函数的话,逻辑回归算法就是一个线性回归。可以说,逻辑回归是以线性回归为理论支持的,但是逻辑回归通过Sigmoid函数引入了非线性因素,因此可以轻松处理0/1分类问题。
我们利用线性回归的办法来拟合然后设置阈值的办法容易受到离群值的影响,sigmod函数可以有效的帮助我们解决这一个问题,所以我们只要在拟合的时候把即y = 换成即可,其中
z=,也就是说g(z) = . 同时,因为g(z)函数的特性,它输出的结果也不再是预测结果,而是一个值预测为正例的概率,预测为负例的概率就是1-g(z).
函数形式表达:
P(y=0|w,x) = 1 – g(z)
P(y=1|w,x) = g(z)
P(正确) = * 为某一条样本的预测值,取值范围为0或者1.
到这里,我们得到一个回归函数,它不再像y=wT * x一样受离群值影响,他的输出结果是样本预测为正例的概率(0到1之间的小数).我们接下来解决第二个问题:选定一个阈值.
五:选定阈值
选定阈值的意思就是,当我选阈值为0.5,那么小于0.5的一定是负例,哪怕他是0.49.此时我们判断一个样本为负例一定是准确的吗?其实不一定,因为它还是有49%的概率为正利的.但是即便他是正例的概率为0.1,我们随机选择1w个样本来做预测,还是会有接近100个预测它是负例结果它实际是正例的误差.无论怎么选,误差都是存在的.所以我们选定阈值的时候就是在选择可以接受误差的程度.
我们现在知道了sigmod函数预测结果为一个0到1之间的小数,选定阈值的第一反应,大多都是选0.5,其实实际工作中并不一定是0.5,阈值的设定往往是根据实际情况来判断的.本小节我们只举例让大家理解为什么不完全是0.5,并不会有一个万能的答案,都是根据实际工作情况来定的.
0到1之间的数阈值选作0.5当然是看着最舒服的,可是假设此时我们的业务是像前边的例子一样,做一个肿瘤的良性恶性判断.选定阈值为0.5就意味着,如果一个患者得恶性肿瘤的概率为0.49,模型依旧认为他没有患恶性肿瘤,结果就是造成了严重的医疗事故.此类情况我们应该将阈值设置的小一些.阈值设置的小,加入0.3,一个人患恶性肿瘤的概率超过0.3我们的算法就会报警,造成的结果就是这个人做一个全面检查,比起医疗事故来讲,显然这个更容易接受.
第二种情况,加入我们用来识别验证码,输出的概率为这个验证码识别正确的概率.此时我们大可以将概率设置的高一些.因为即便识别错了又能如何,造成的结果就是在一个session时间段内重试一次.机器识别验证码就是一个不断尝试的过程,错误率本身就很高.
以上两个例子可能不大准确,只做意会,你懂了就好. [此时我的表情无法描述]
到这里,逻辑回归的由来我们就基本理清楚了,现在我们知道了逻辑回归的判别函数就是,z=.休息两分钟,我们下面看如何求解逻辑回归,也就是如何找到一组可以让全都预测正确的概率最大的W.
六.最大似然估计
此时我们想要找到一组w,使函数正确的概率最大.而我们在上面的推理过程中已经得到每个单条样本预测正确概率的公式:
P(正确) = *
若想让预测出的结果全部正确的概率最大,根据最大似然估计(多元线性回归推理中有讲过,此处不再赘述),就是所有样本预测正确的概率相乘得到的P(总体正确)最大,此时我们让 ,数学表达形式如下:
上述公式最大时公式中W的值就是我们要的最好的W.下面对公式进行求解.
我们知道,一个连乘的函数是不好计算的,我们可以通过两边同事取log的形式让其变成连加.
得到的这个函数越大,证明我们得到的W就越好.因为在函数最优化的时候习惯让一个函数越小越好,所以我们在前边加一个负号.得到公式如下:
这个函数就是我们逻辑回归(logistics regression)的损失函数,我们叫它交叉熵损失函数.
七.求解交叉熵损失函数
求解损失函数的办法我们还是使用梯度下降,同样在批量梯度下降与随机梯度下降一节有详细写到,此处我们只做简要概括.
求解步骤如下:
1-随机一组W.
2-将W带入交叉熵损失函数,让得到的点沿着负梯度的方向移动.
3-循环第二步.
求解梯度部分同样是对损失函数求偏导,过程如下:
交叉熵损失函数的梯度和最小二乘的梯度形式上完全相同,区别在于,此时的,而最小二乘的.
以下为python代码
#!/user/bin/env python3
# -*- coding:utf-8 -*-
import numpy as np
import random
class Logistic_Regression():
def __init__(self):
pass
#推理
def inference(self, w, b, x):
h = w * x + b
pred_y = 1 / (1 + np.exp(-h))
return pred_y
#损失
def eval_loss(self, x_list, gt_y_list, w, b):
eval_loss = 0.0
#loop
# for i in range(len(x_list)):
# h = w * x_list[i] + b
# pred_y = 1 / (1 + np.exp(-h))
# eval_loss += -1 * gt_y_list[i] * np.log(pred_y) - (1 - gt_y_list[i]) * np.log(1 - pred_y)
#python way
H = w * np.array(x_list) + b
pred_y_list = 1 / (1 + np.exp(-H))
eval_loss = np.sum(-1 * gt_y_list * np.log(pred_y_list) - (1 - gt_y_list) * np.log(1 - pred_y_list))
eval_loss /= len(gt_y_list)
return eval_loss
#参数更新量(每个样本)
def gradient(self, pred_y, gt_y, x):
diff = pred_y - gt_y
dw = x * diff
db = diff
return dw, db
#参数更新
def cal_step_gradient(self, x_batch, gt_y_batch, w, b, lr):
avg_dw, avg_db = 0, 0
batch_size = len(gt_y_batch)
for i in range(batch_size):
pred_y = self.inference(w, b, x_batch[i])
dw, db = self.gradient(pred_y, gt_y_batch[i], x_batch[i])
avg_dw += dw
avg_db += db
avg_dw /= batch_size
avg_db /= batch_size
w -= lr * avg_dw
b -= lr * avg_db
return w, b
#训练
def train(self, x_list, gt_y_list, batch_size, lr, max_iter):
w, b = 0, 0
num_samples = len(gt_y_list)
for i in range(max_iter):
batch_idxs = np.random.choice(len(x_list), batch_size)
x_batch = [x_list[i] for i in batch_idxs]
y_batch = [gt_y_list[i] for i in batch_idxs]
w, b = self.cal_step_gradient(x_batch, y_batch, w, b, lr)
print('w:{0}, b:{1}'.format(w, b))
print('loss is {0}'.format(self.eval_loss(x_list, gt_y_list, w, b)))
#数据生成
def gen_data(self):
w = random.randint(0, 10) + random.random()
b = random.randint(0, 5) + random.random()
num_samples = 100
#loop
# x_list = []
# gt_y_list = []
# for i in range(num_samples):
# x = random.randint(0, 100) * random.random()
# y = 1 if x > 50 else 0
# x_list.append(x)
# gt_y_list.append(y)
# gt_y_list = np.array(gt_y_list) #这里要更改为数组,以便在self.eval_loss中计算
#python way
x_list = np.random.randint(0, 100, num_samples) * random.random()
gt_y_list = x_list.copy()
gt_y_list[x_list > 50] = 1
gt_y_list[x_list <= 50] = 0
return x_list, gt_y_list, w, b
#运行
def run(self):
x_list, gt_y_list, w, b = self.gen_data()
batch_size = 50
lr = 0.01
max_iter = 10000
self.train(x_list, gt_y_list, batch_size, lr, max_iter)
if __name__ == '__main__':
LR = Logistic_Regression()
LR.run()