上文结尾我们提到了关于模型损失的问题,一个模型的效果好坏如何衡量,如何去优化我们的模型,带着这些问题本文就和大家一起来学习一下模型的损失函数。😊
推荐回顾前文,以便新老朋友丝滑入戏
文章大纲
损失函数大致分为回归、二分类和多分类,之前的文章我们已经实现了上述三种神经网络,也就是实现了建模,建模之后的下一步就是确定损失函数,然后通过分析损失函数的特点确定对应的优化方法,即如何去最优化对应的损失函数,使其收敛到最小值,得到模型的参数。以上流程总结为一张图如下
以上流程环环相扣,是模型训练的全部流程,每个步骤都有具体的细节,都根据前一步的特点使用对应的方法。对于损失函数来讲,就是根据模型的不同确定不同的损失函数。神经网络的学习的最终目标就是得到让损失尽量小的参数,如此才可以说建立了一个准确的模型,使得神经网络输出的预测值和真实值相吻合。
01 线性回归模型的损失函数
我们还是从最简单的线性回归说起,假设我们得到了一些身高体重的数据,这些数据符合线性关系 ,其中x代表身高,y代表体重,a是权重,b是偏置,在数学中就是一元方程的系数。数据如下
索引 | 身高x | 体重y | y_hat |
1 | 175.0 | 65.2 | 175.0a+b |
2 | 180.5 | 71.0 | 180.5a+b |
3 | 179.2 | 70.5 | 179.2a+b |
... |
假设我们已经对上述数据拟合完成了一条直线
此时如果给出一个身高的数据 ,假设预测出来的值 和真实值 如图
可以看到图中预测值和真实值之间是有差距的,我们捕获到这个差距,用数学的式子表示出来,这个就是模型的损失
y
−
y
^
y-\hat{y}
y−y^ 这是一个点的损失值,考虑整个训练集的损失之和即为
∑
i
=
1
m
y
−
y
^
\sum ^{m}_{i=1} {y-\hat{y}}
i=1∑my−y^
在直线上方的点损失值为正,直线下方的损失值为负,为了避免损失值正负抵消,👾我们采用对损失值取平方的做法 S S E = ∑ i = 1 m ( y − y ^ ) 2 SSE = \sum ^{m}_{i=1} {\left ( {y-\hat{y}}\right)^{2}} SSE=i=1∑m(y−y^)2上式就是著名的回归类的损失函数——误差平方和(SSE,Sum of the Squared Errors),这个式子是把所有数据的损失加和,所以一般得到的数值都比较大,还有一种是求所有数据点的平均损失 M S E = 1 m ∑ i = 1 m ( y − y ^ ) 2 MSE = \frac {1} {m} \sum ^{m}_{i=1} {\left ( {y-\hat{y}}\right)^{2}} MSE=m1i=1∑m(y−y^)2 这种求数据点的平均损失的损失函数叫做——均方误差(MSE,mean-square error),一般我们常使用均方误差。
02 二分类损失函数
我们在实现二分类神经网络的时候,网络预测的输出结果 是0和1。在二分类神经网络中,我们使用了sigmoid函数,将加权求和后的值映射到0和1之间。我们可以将sigmoid函数的输出值看作概率,也就是分为两个类别的概率,那么自然就可以根据概率值的大小判断分为哪一个类别,如此一来就有了判断依据,比如说分为sigmoid值为0.8,我们就可以认为该样本被分为1类的概率比较大,考虑到并不是完全为1,因此这里面就会有分错的情况,这种情况就可以定义为损失。
二分类损失函数是由极大似然法推导出来的,所以我们先来简单了解一下极大似然法是什么。
2.1 极大似然估计
极大似然估计(Maximum Likelihood Estimate,MLE)是统计学中的一个概念,这里简单介绍一下,参考于B站UP主“王木头学科学”的一些解释。
(⚠️篇幅警告⚠️)
很多事件经过大量的重复实验后会得到一个理论值,比如说对于抛硬币这件事,我们都知道一个硬币抛起来后得到正反面的概率值分别为0.5,这是理论世界里的值。但是在现实世界中,假设我们抛10次硬币,并不一定能得到1:1的这种分布规律。
比如说抛了10次硬币,3正7反,那么这时候正反硬币的概率值就不是0.5:0.5了,而是0.3:0.7,这是一个概率模型,但是我们并不能简单的认为抛一次得到的正反概率值为0.3:0.7。因为还有可能这10次的结果为1正9反,这时候的概率值为0.1:0.9,还有可能是2正8反,这时候的概率值为0.2:0.8。
我们这样就得到了三种现实中的概率分布,这三种概率分布都有可能是真实的理论概率值,但是我们不能确定是哪一个,这时候我们可以去分别计算每一个概率模型下的概率值是多少,比如0.1:0.9的这种情况,此时我们抛得硬币结果为3正7反,概率值就可以表示为 C 10 3 0.1 3 0.9 7 {C}^{3}_{10}{0.1}^{3}{0.9}^{7} C1030.130.97 。同样的,其它两种概率模型下,其概率值分别表示为 C 10 3 0.3 3 0.7 7 {C}^{3}_{10}{0.3}^{3}{0.7}^{7} C1030.330.77 、 C 10 3 0.2 3 0.8 7 {C}^{3}_{10}{0.2}^{3}{0.8}^{7} C1030.230.87 。
这是三种概率模型下得到的概率值,这些值都是从现实中的例子得到的可能值,我们就可以称其为”似然值“,根据得到的概率值的大小,可以计算出0.3:0.7这种概率模型的值最大,当然这也符合我们理论世界中正反面0.5:0.5的这种情况。
有了似然值我们就可以用最大的似然值来当作我们的概率模型,虽然不一定和我们理论世界相同,但是相较而言其确定性是最大的,这也是极大似然法的关键思想。以上就是对极大似然法的一个简单解释,如果你还没有理解,推荐去B站搜索极大似然法,播放量最多的就是UP主”王木头学科学“的讲解。当然了,极大似然法毕竟是统计学中的知识,如果理解不了并不影响接下来的内容。
总结一下极大似然法的思想,其实这个方法简单来说就是求解让目标事件发生概率最大的模型,对于我们的神经网络而言,就是如果该类别为1,那我们就让预测值为1的概率最大化,如果该类别为0,我们就让预测值为0的概率最大化。进一步来讲就是求解其权重w,使得概率最大化。
2.2 二分类交叉熵推导
我们所实现的二分类神经网络,其输出值为0和1,服从伯努利分布(0-1分布),其概率模型为
在我们的二分类模型中,假设在给定一个样本x的情况下,预测结果为1类,我们将此概率称为 P1 ,即
其中 是 sigmoid函数预测的输出值,即预测出来的概率值。
因此同一样本对应的分类为0类的概率为
我们希望得到的结果是,当真实标签y为0时,预测值也为0,当真实标签y=1时,预测值也为1,如此其模型损失最小,效果最好。但是模型都会有损失,那么我们将上面的两个式子整合一下,得到一个能在y=1和y=0情况下都能衡量损失的式子 当真实值为y=1时,1-y=0,则 对应的概率值就被忽略掉,我们只考虑预测值为1时候的概率,这样就能衡量预测值和真实值之间的差距。同样的,当y=0时,也只关注预测值为0时候的概率。对于这两种情况我们希望不管关注于哪一个概率,如果其概率都最大就是最好的情况,此时的损失就是最小的。这个思想不就是极大似然法的核心思想吗,追求最大概率的情况。
我们将单个样本的情况扩展到多个样本,假设我们的训练集的样本是独立同分布的,则有 P ( Y ∣ X ) = ∏ i = 1 m P ( y ^ i ∣ x i ) = ∏ i = 1 m ( P 1 y i ∗ P 0 1 − y i ) = ∏ i = 1 m ( y ^ y i ∗ ( 1 − y ^ ) 1 − y i ) \begin{aligned} \boldsymbol{P(Y|X)} &=\prod_{i=1}^{m} P(\hat{y}_{i} \mid x_{i}) \\ &=\prod_{i=1}^{m}\left(P_{1}^{y_{i}} * P_{0}^{1-y_{i}}\right)\\ &=\prod_{i=1}^{m}\left(\hat{y}^{y_{i}} *\left(1-\hat{y}\right)^{1-y_{i}}\right) \end{aligned} P(Y∣X)=i=1∏mP(y^i∣xi)=i=1∏m(P1yi∗P01−yi)=i=1∏m(y^yi∗(1−y^)1−yi) 对上面式子两边取对数 ln P ( Y ∣ X ) = ln ∏ i = 1 m ( y ^ i y i ∗ ( 1 − y ^ i ) 1 − y i ) = ∑ i = 1 m ln ( y ^ i y i ∗ ( 1 − y ^ i ) 1 − y i ) = ∑ i = 1 m ( ln y ^ i y i + ln ( 1 − y ^ i ) 1 − y i ) = ∑ i = 1 m ( y i ∗ ln ( y ^ i ) + ( 1 − y i ) ∗ ln ( 1 − y ^ i ) ) \begin{aligned} \ln \boldsymbol{P(Y|X)} &=\ln \prod_{i=1}^{m}\left(\hat{y}_i^{y_{i}} *\left(1-\hat{y}_i\right)^{1-y_{i}}\right)\\ &=\sum_{i=1}^{m} \ln \left(\hat{y}_i^{y_{i}} *\left(1-\hat{y}_i\right)^{1-y_{i}}\right)\\ &=\sum_{i=1}^{m}\left(\ln \hat{y}_i^{y_{i}}+\ln \left(1-\hat{y}_i\right)^{1-y_{i}}\right) \\ &=\sum_{i=1}^{m}\left(y_{i} * \ln \left(\hat{y}_i\right)+\left(1-y_{i}\right) * \ln \left(1-\hat{y}_i\right)\right) \end{aligned} lnP(Y∣X)=lni=1∏m(y^iyi∗(1−y^i)1−yi)=i=1∑mln(y^iyi∗(1−y^i)1−yi)=i=1∑m(lny^iyi+ln(1−y^i)1−yi)=i=1∑m(yi∗ln(y^i)+(1−yi)∗ln(1−y^i))这样我们便推导出来了这个非常重要也很常见的二分类交叉熵损失函数(Binary Cross Entropy Loss),也叫做对数损失(log loss)。一般我们对损失函数是求最小值,所以在上面的式子前面加上负号就得到 l o s s = − ∑ i = 1 m ( y i ∗ ln ( y ^ i ) + ( 1 − y i ) ∗ ln ( 1 − y ^ i ) ) loss =-\sum_{i=1}^{m}\left(y_{i} * \ln \left(\hat{y}_i\right)+\left(1-y_{i}\right) * \ln \left(1-\hat{y}_i\right)\right) loss=−i=1∑m(yi∗ln(y^i)+(1−yi)∗ln(1−y^i)) 中含有权重w,将权重w视为我们损失函数的自变量,对 求导便可得到使得损失函数最小的一组权重w,如此便可以降低模型损失,提升模型的效果。
2.3 Pytorch中实现二分类交叉熵损失函数
上面推导那么多,在框架中只需要调用实现好的函数就可以了,其中一个是torch.nn下面的**BCELoss
**
import torch.nn as nn
def loss_fn(yhat, y):
loss = nn.BCELoss()(yahat,y)
return loss
这个BCELoss函数,需要输入我们的预测值和真实值,因为该函数内部并没有实现sigmoid层,也就是说必须先经过sigmoid函数输出后的yhat才能作为参数。
而另外一个函数则不同,BCEWithLogitsLoss函数内部实现了sigmoid函数,我们只需要输入加权求和后的z即可,调用方法也和上面一样,极其简单粗暴。
===
03 多分类损失函数
3.1 多分类概率表示
在损失函数上,多分类和二分类的思想是一样的,既然二分类是求分为0或1时的概率,那么多分类的损失函数求分为多个类时的概率就可。多分类模型使用的是softmax函数,其输出的预测值并不是0和1,此时就不能像二分类那样,将y和(1-y)作为概率的指数来统一成一个式子。但是softmax输出的结果我们也可以看作是一组概率,只不过其对应的类别增加了而已。比如对于一个K分类问题,softmax输出的结果就是一个
这么一来概率最大的对应的索引就是最大可能预测的类别。相应的,我们可以把训练集中的类别用one-hot编码来表示,比如对于一个样本
我们将训练集中的真实标签y都用one-hot编码表示出来,那么softmax预测的输出就与标签一一对应,这样就又回到了类似于二分类的样子。
3.2 多分类交叉熵推导
类比于二分类的概率表示,我们可以写成多分类的概率表示
P
(
y
^
i
∣
x
i
)
=
P
1
y
1
∗
P
2
y
2
∗
P
3
y
3
.
.
.
P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}\right)=P_{1}^{y_{1}} * P_{2}^{y_{2}} * P_{3}^{y_{3}} ...
P(y^i∣xi)=P1y1∗P2y2∗P3y3...
注意,这里的y_i表示的是经过我们one-hot编码后的真实标签值,比如上面的样本中,y_1为1,y_0为0,即各个类别对应的编码中的值,而p代表我们softmax输出的那一行概率值
P
(
y
^
i
∣
x
i
)
=
P
j
y
i
P\left(\hat{y}_{i} \mid \boldsymbol{x}_{\boldsymbol{i}}\right)=P_{j}^{y_{i}}
P(y^i∣xi)=Pjyi。 将该式推广到所有训练集的样本,可以得到
P
=
∏
i
=
1
m
P
(
y
^
i
∣
x
i
)
=
∏
i
=
1
m
P
j
y
i
=
∏
i
=
1
m
y
^
i
y
i
\begin{aligned} \boldsymbol{P} &=\prod_{i=1}^{m} P(\hat{y}_{i} \mid x_{i}) \\ &=\prod_{i=1}^{m} P_{j}^{y_{i}} \\ &=\prod_{i=1}^{m} \hat{y}_i^{y_{i}} \end{aligned}
P=i=1∏mP(y^i∣xi)=i=1∏mPjyi=i=1∏my^iyi 同样的,两边取对数,可得
ln
P
=
ln
∏
i
=
1
m
y
^
i
y
i
=
∑
i
=
1
m
ln
(
y
^
i
y
i
)
=
∑
i
=
1
m
y
i
ln
y
^
i
\begin{aligned} \ln \boldsymbol{P} &=\ln \prod_{i=1}^{m} \hat{y}_{i}^{y_{i}} \\ &=\sum_{i=1}^{m} \ln \left(\hat{y}_{i}^{y_{i}}\right) \\ &=\sum_{i=1}^{m} y_{i} \ln \hat{y}_{i} \end{aligned}
lnP=lni=1∏my^iyi=i=1∑mln(y^iyi)=i=1∑myilny^i对上式取负数,即可得到多分类的交叉熵损失函数
l
o
s
s
=
−
∑
i
=
1
m
y
i
ln
y
^
i
loss=-\sum_{i=1}^{m} y_{i} \ln \hat{y}_{i}
loss=−i=1∑myilny^i
可以看的出,二分类的交叉熵损失函数和多分类的交叉熵损失函数是特殊到一般的关系,二者有着相同的逻辑。
3.3 pytorch实现多分类交叉熵损失函数
在pytorch中直接调用CrossEntropyLoss即可
def loss_fn(yhat, y):
loss = nn.CrossEntropyLoss()(yhat, y.long())
return loss
在多分类中,我们直接调用CrossEntropyLoss即可实现,不管是在处理文本还是图像,多分类的问题还是非常常见的。
好了,以上就是本文的全部内容了,希望能给到你一点点的帮助。🐞
分享知识,记录生活,一起进步