使用回归的思想进行分类-逻辑回归
逻辑回归是属于机器学习里面的监督学习,它是以回归的思想来解决分类问题的一种非常经典的二分类分类器。由于其训练后的参数有较强的可解释性,在诸多领域中,逻辑回归通常用作 baseline模型,以方便后期更好的挖掘业务相关信息或提升模型性能。
逻辑回归大体思想
什么是逻辑回归
当一看到“回归”这两个字,可能会认为逻辑回归是一种解决回归问题的算法,然而逻辑回归是通过回归的思想来解决二分类问题的算法。
那么问题来了,回归的算法怎样解决分类问题呢?其实很简单,逻辑回归是将样本特征和样本所属类别的概率联系在一起,假设现在已经训练好了一个逻辑回归的模型为,模型的输出是样本的标签是的概率,则该模型可以表示成。若得到了样本属于标签的概率后,很自然的就能想到当时属于标签,否则属于标签 。所以就有(其中为样本 根据模型预测出的标签结果,标签 和标签 所代表的含义是根据业务决定的,比如在癌细胞识别中可以使 代表良性肿瘤, 代表恶性肿瘤)。
由于概率是 到 的实数,所以逻辑回归若只需要计算出样本所属标签的概率就是一种回归算法,若需要计算出样本所属标签,则就是一种二分类算法。
那么逻辑回归中样本所属标签的概率怎样计算呢?其实和线性回归有关系,学习了线性回归的话肯定知道线性回归就是训练出一组参数 和 来拟合样本数据,线性回归的输出为 。不过的值域是,如果能够将值域为 的实数转换成 的概率值的话问题就解决了。要解决这个问题很自然地就能想到将线性回归的输出作为输入,输入到另一个函数中,这个函数能够进行转换工作,假设函数为 ,转换后的概率为 ,则逻辑回归在预测时可以看成 。 其实就是接下来要介绍的函数。
sigmoid函数
函数的公式为:。函数图像如下图所示:
从函数的图像可以看出当趋近于时函数值趋近于,当趋近于时函数值趋近于。可见函数的值域是,满足我们要将的实数转换成的概率值的需求。因此逻辑回归在预测时可以看成,如果时预测为一种类别,否则预测为另一种类别。
逻辑回归的损失函数
在预测样本属于哪个类别时取决于算出来的。从另外一个角度来说,假设现在有一个样本的真实类别为 ,模型预测样本为类别 的概率为 的话,就意味着这个模型认为当前样本的类别有 的可能性为 ,有 的可能性为 。所以从这个角度来看,逻辑回归的损失函数与 有关。
当然逻辑回归的损失函数不仅仅与 有关,它还与真实类别有关。假设现在有两种情况,情况 A :现在有个样本的真实类别是 ,但是模型预测出来该样本是类别 的概率是 (也就是说类别 的概率为 );情况 B :现在有个样本的真实类别是 ,但是模型预测出来该样本是类别 的概率是 (也就是说类别 的概率为 );请您思考 秒钟,AB 两种情况哪种情况的误差更大?很显然,情况 A 的误差更大!因为情况 A 中模型认为样本是类别 的可能性只有 ,而 情况 B 有 。
假设现在又有两种情况,情况A: 现在有个样本的真实类别是 ,但是模型预测出来该样本是类别 的概率是 (也就是说类别 的概率为 );情况B:现在有个样本的真实类别是 ,但是模型预测出来该样本是类别 的概率是 (也就是说类别 的概率为 );请您再思考 秒钟,AB两种情况哪种情况的误差更大?很显然,一样大!
所以逻辑回归的损失函数如下,其中 表示损失函数的值,$y$ 表示样本的真实类别:
知道了逻辑回归的损失函数之后,逻辑回归的训练流程就很明显了,就是寻找一组合适的 和 ,使得损失值最小。找到这组参数后模型就确定下来了。怎么找?很明显,用梯度下降,而且不难算出梯度为:。
所以逻辑回归梯度下降的代码如下:
#loss
def J(theta, X_b, y):
y_hat = self._sigmoid(X_b.dot(theta))
try:
return -np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat)) / len(y)
except:
return float('inf')
# 算theta对loss的偏导
def dJ(theta, X_b, y):
return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(y)
# 批量梯度下降
def gradient_descent(X_b, y, initial_theta, leraning_rate, n_iters=1e4, epsilon=1e-8):
theta = initial_theta
cur_iter = 0
while cur_iter < n_iters:
gradient = dJ(theta, X_b, y)
last_theta = theta
theta = theta - leraning_rate * gradient
if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
break
cur_iter += 1
return theta