回归和分类
回归和分类是机器学习可以解决两大主要问题,从预测值的类型上来区分,连续变量的预测称为回归,离散变量的预测称为分类。例如:预测房价的价格是一个回归任务;预测一张图片是猫还是狗的图片是分类任务。
线性回归
在一维特征空间,线性回归是通过学习一条直线
- 解决回归问题
- 连续的变量
- 符合信息关系
- 直观表达变量关系
逻辑回归
逻辑回归(Logistic Regression)主要解决二分类问题,用来表示某件事发生的可能性。
- 这张图片是不是狗(是、不是)
- 这封邮件是不是垃圾邮件(是、不是)
- 当前脑CT是不是包含并病灶(是、不是)
如何基于回归进行二分类?在多维空间表示上,线性回归表示为:
- 解决分类问题
- 离散的变量
- 可以不符合线性关系
- 无法直观表达变量关系
现在有
极大似然估计
上面两个式子用下面的式子表示:
这个式子可以理解为,我们将数据
这样就能做到,不断更新
对于
作为目标函数时,有乘法常常会使用 log 函数将其转化成加法。我们目标是使得最大似然函数越来越大,而作为目标函数时,常把这看作为一个损失,我们期望不断优化模型参数,使得损失越来越小。也就是使的目标函数越来越小,所以逻辑回归的目标函数是最大对数似然函数的相反数。
目标函数:
而这个目标函数也可以从交叉熵的角度来理解,常常使用交叉熵函数作为二分类的损失,相比于二次代价函数有一定优势。具体可见下文,下文来自博客:https://blog.youkuaiyun.com/u014313009/article/details/51043064
交叉熵代价函数和二次代价函数对比
交叉熵代价函数(Cross-entropy cost function)是用来衡量两个概率分布之间差异的函数。与二次代价函数相比,它能更有效地促进神经网络的训练。在介绍交叉熵代价函数之前,本文先简要介绍二次代价函数,以及其存在的不足。
二次代价函数的不足
神经网络的设计目的之一是为了使机器可以像人一样学习知识。人在学习分析新事物时,当发现自己犯的错误越大时,改正的力度就越大。比如投篮:当运动员发现自己的投篮方向离正确方向越远,那么他调整的投篮角度就应该越大,篮球就更容易投进篮筐。同理,**我们希望:神经网络在训练时,如果预测值与实际值的误差越大,那么在反向传播训练的过程中,各种参数调整的幅度就要更大,从而使训练更快收敛。**然而,如果使用二次代价函数训练神经网络,看到的实际效果是,如果误差越大,参数调整的幅度可能更小,训练更缓慢。
以一个神经元的二类分类训练为例,进行两次实验(神经网络常用的激活函数为 sigmoid 函数,该实验也采用该函数):输入一个相同的样本数据

实验1:第一次输出值为0.82

实验2:第一次输出值为0.98
在实验1中,随机初始化参数,使得第一次输出值为 0.82(该样本对应的实际值为 0 );经过 300 次迭代训练后,输出值由 0.82 降到0.09,逼近实际值。而在实验 2 中,第一次输出值为 0.98,同样经过 300 迭代训练,输出值只降到了 0.20。
从两次实验的代价曲线中可以看出:实验1的代价随着训练次数增加而快速降低,但实验2的代价在一开始下降得非常缓慢;直观上看,初始的误差越大,收敛得越缓慢。
其实,误差大导致训练缓慢的原因在于使用了二次代价函数。二次代价函数的公式如下:
其中,
目前训练神经网络最有效的算法是反向传播算法。简而言之,训练神经网络就是通过反向传播代价,以减少代价为导向,调整参数。参数主要有:神经元之间的连接权重
其中,

如图所示,**实验2的初始输出值(0.98)对应的梯度明显小于实验1的输出值(0.82),因此实验2的参数梯度下降得比实验1慢。这就是初始的代价(误差)越大,导致训练越慢的原因。**与我们的期望不符,即:不能像人一样,错误越大,改正的幅度越大,从而学习得越快。
可能有人会说,那就选择一个梯度不变化或变化不明显的激活函数不就解决问题了吗?图样图森破,那样虽然简单粗暴地解决了这个问题,但可能会引起其他更多更麻烦的问题。而且,类似 sigmoid 这样的函数(比如 tanh 函数)有很多优点,非常适合用来做激活函数,具体请自行google之。
交叉熵代价函数
换个思路,我们不换激活函数,而是换掉二次代价函数,改用交叉熵代价函数:
其中,x表示样本,n表示样本的总数。那么,重新计算参数w的梯度:
因此,
实际情况证明,交叉熵代价函数带来的训练效果往往比二次代价函数要好。
PyTorch中二分类交叉熵损失函数的实现
PyTorch提供了两个类来计算二分类交叉熵(Binary Cross Entropy),分别是BCELoss() 和BCEWithLogitsLoss()
- torch.nn.BCELoss()
类定义如下
torch.nn.BCELoss(
weight=None,
size_average=None,
reduction="mean",
)
举个例子:
import torch
import torch.nn as nn
model = nn.Sequential(
nn.Linear(10, 1),
nn.Sigmoid()
)
criterion = nn.BCELoss()
x = torch.randn(16, 10) # (16, 10)
y = torch.empty(16).random_(2) # shape=(16, ) 其中每个元素值为0或1
out = model(x) # (16, 1)
out = out.squeeze(dim=-1) # (16, )
loss = criterion(out, y)
- torch.nn.BCEWithLogitsLoss()
类定义如下:
torch.nn.BCEWithLogitsLoss(
weight=None,
size_average=None,
reduction="mean",
pos_weight=None,
)
用
这个类将 Sigmoid() 和 BCELoss() 整合起来,比 纯粹使用 BCELoss() + Sigmoid() 更数值稳定。
This loss combines a Sigmoid layer and the BCELoss in one single class. This version is more numerically stable than using a plain Sigmoid followed by a BCELoss as, by combining the operations into one layer, we take advantage of the log-sum-exp trick for numerical stability.
举个例子
import torch
import torch.nn as nn
model = nn.Linear(10, 1)
criterion = nn.BCEWithLogitsLoss()
x = torch.randn(16, 10)
y = torch.empty(16).random_(2) # (16, )
out = model(x) # (16, 1)
out = out.squeeze(dim=-1) # (16, )
loss = criterion(out, y)
梯度下降法更新参数
sigmoid导数:
目标函数:
梯度下降法更新公式:
对目标函数求偏导数:
逻辑回归的优缺点
- 优点
- 实现简单,广泛应用于工业问题;
- 分类时计算量非常小,速度很快,存储资源低;
- 便利的观测样本概率分数;
- 计算代价不高,易于理解和实现;
- 缺点
- 当特征空间很大时,逻辑回归的性能不是很好;
- 容易欠拟合,一般准确度不是太高;
- 不能很好的处理大量多类特征或者变量;
参考链接
[1] https://blog.youkuaiyun.com/u014313009/article/details/51043064
[2] https://zhuanlan.zhihu.com/p/59800597