动手学深度学习(九)——交叉熵代价函数原理及其在MNIST手写数字识别中的应用

pytorch学习(九)——交叉熵代价函数原理及其在MNIST手写数字识别中的应用

说明:这篇博客主要是介绍交叉熵代价函数的原理及其在pytorch中实现MNIST手写数字的识别,可以非常清晰地看清在pytorch之中进行模型训练的步骤和方式
相关代码下载:https://download.csdn.net/download/jerry_liufeng/13099072

一、二次代价函数(改变激活函数)

二次代价函数定义式
C = 1 2 n ∑ x 1 , . . . , x n ∣ ∣ y ( x ) − a L ( x ) ∣ ∣ 2 C = \frac{1}{2n}\sum_{x1,...,xn}||y(x)-a^L(x)||^2 C=2n1x1,...,xn∣∣y(x)aL(x)2

  • C——代价函数
  • x——样本
  • y——实际值
  • a——输出值
  • n——样本总数

二次代价函数简写为: E = 1 2 ( t − y ) 2 E = \frac{1}{2}(t-y)^2 E=21(ty)2
使用梯度下降法来进行更新权值:
∂ E ∂ ω = ( y − t ) f ′ ( z ) x \frac{\partial{E}}{\partial{\omega}} = (y-t)f^{'}(z)x ωE=(yt)f(z)x
z = W X z = WX z=WX

激活函数的梯度f’(z)越大,w的大小调整得越快,训练收敛就越快,激活函数的梯度f’(z)越小,w的调整就越慢,训练收敛就越慢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4NejKp1-1604892650611)(attachment:image.png)]

二、熵(Entropy)与交叉熵(Cross-Entropy)原理及推导

参考:https://zhuanlan.zhihu.com/p/149186719

1、熵

香农对熵的定义:无损编码事件信息的最小平均编码长度

如果熵比较大,表示这一信息有较多的可能状态,相应每个状态的可能性就比较低;因此每当来了一个新的信息,我们很难对其作出准去的预测,即有着较大的混乱程度/不确定性/不可预测性

编码信息的最小编码长度
l o g 2 1 N = − l o g 2 P log_2{\frac{1}{N}} = -log_2{P} log2N1=log2P

熵的计算公式:
E n t r o p y = − ∑ i P ( i ) l o g 2 P ( i ) Entropy = -\sum_i{P(i)log_2{P(i)}} Entropy=iP(i)log2P(i)

对于连续变量x的概率分布P(x),熵的计算公式:
E n t r o p y = − ∫ P ( x ) l o g 2 P ( x )   d x Entropy = -\int{P(x)log_2{P(x)}}\,dx Entropy=P(x)log2P(x)dx

在熵的计算公式之中,对于连续和离散的变量都计算了负的可能性的对数的期望,代表该事件理论上的平均最小编码长度
H ( P ) = E n t r o p y = E x − P [ − l o g P ( x ) ] H(P)= Entropy = E_{x-P}[-logP(x)] H(P)=Entropy=ExP[logP(x)]

  • 其中x-P表示使用概率分布P来计算期望

“熵是服从某一特定概率分布事件的理论最小平均编码长度”,只要我们知道了任何事件的概率分布,我们就可以计算它的熵;那如果我们不知道事件的概率分布,又想计算熵,该怎么做呢?那我们来对熵做一个估计吧,熵的估计的过程自然而然的引出了交叉熵

2、交叉熵

根据预估的概率分布Q我们估计概率分布,可以计算估计的熵:

E s t i m a t e d E n t r o p y = E x − Q [ − l o g Q ( x ) ] EstimatedEntropy = E_{x-Q}[-logQ(x)] EstimatedEntropy=ExQ[logQ(x)]

如果Q是真实的概率分布,根据上述公式,我们已经得到了编码的最小平均长度;然而估计的概率分布为我们的公式引入了两部分的不确定性:

  • 计算期望的概率分布是Q,与真实的概率分布P不同。
  • 计算最小编码长度的概率是 -logQ,与真实的最小编码长度 -logP 不同。

我们希望编码长度尽可能的短,所以我们需要对比我们的编码长度和理论上的最小编码长度(熵)。假设经过观测后,我们得到了真实概率分布P,可以使用P计算平均编码长度,实际编码长度基于Q计算,这个计算结果就是P和Q的交叉熵。

实际编码长度和理论最小编码长度的对比意义:
C r o s s E n t r o p y = E x − P [ − l o g Q ( x ) ] CrossEntropy = E_{x-P}[-logQ(x)] CrossEntropy=ExP[logQ(x)]

E n t r o p y = E x − P [ − l o g P ( x ) ] Entropy = E_{x-P}[-logP(x)] Entropy=ExP[logP(x)]

由此我们得出 交叉熵 > = 熵 交叉熵 >= 熵 交叉熵>=,交叉熵用H(P,Q)表示(使用P计算期望,使用Q计算编码长度),H(P,Q)并不一定等于H(Q,P)
H ( P , Q ) = E x − P [ − l o g Q ( x ) ] H(P,Q) = E_{x-P}[-logQ(x)] H(P,Q)=ExP[logQ(x)]

对于期望,我们使用真实概率分布P来计算;

对于编码长度,我们使用假设的概率分布Q来计算,因为它是预估用于编码信息的。

因为熵是理论上的平均最小编码长度,所以交叉熵只可能大于等于熵

换句话说,如果我们的估计是完美的,即Q=P,那么有H(P,Q) = H§,否则,H(P,Q) > H§。

3、交叉熵作为代价函数(改变代价函数)

一个图片数据集有5种植物,每张图片上有一种,每张图片的标签都是ont-hot编码

机器学习模型对一张图片做出了预测:

modelprediction
Q1[0.4,0.3,0.05,0.05,0.2]
Q2[0.98,0.01,0,0,0.01]

假设两个机器学习模型对一张真实标签为[1,0,0,0,0]的图片做出预测,其交叉熵分别为
H ( P 1 , Q 1 ) = − ∑ i P 1 ( i ) l o g 2 Q 1 ( i ) = − ( l o g ( 0.4 ) + 0 l o g 0.3 + 0 l o g 0.05 + 0 l o g 0.05 + 0 l o g 0.2 ) ≈ 0.916 H(P_1,Q_1) = -\sum_i{P_1(i)log_2{Q_1(i)}}\\ =-(log(0.4)+0log0.3+0log0.05+0log0.05+0log0.2)≈0.916 H(P1,Q1)=iP1(i)log2Q1(i)=(log(0.4)+0log0.3+0log0.05+0log0.05+0log0.2)0.916

H ( P 1 , Q 2 ) = − ∑ i P 1 ( i ) l o g 2 Q 2 ( i ) = − ( 1 l o g ( 0.98 ) + 0 l o g 0.01 + 0 l o g 0 + 0 l o g 0 + 0 l o g 0.01 ) ≈ 0.02 H(P_1,Q_2) = -\sum_i{P_1(i)log_2{Q_2(i)}}\\ =-(1log(0.98)+0log0.01+0log0+0log0+0log0.01)≈0.02 H(P1,Q2)=iP1(i)log2Q2(i)=(1log(0.98)+0log0.01+0log0+0log0+0log0.01)0.02

交叉熵对比了模型的预测结果和数据的真实标签,随着预测越来越准确,交叉熵的值越来越小,如果预测完全正确,交叉熵的值就为0。因此,训练分类模型时,可以使用交叉熵作为损失函数。

4、二分类交叉熵

在二分类模型中,标签只有是和否两种;这时,可以使用二分类交叉熵作为损失函数
H ( P , Q ) = − ∑ i = ( 1 , 2 ) P ( i ) l o g 2 Q ( i ) = − ( P ( 1 ) l o g 2 Q ( 1 ) + P ( 2 ) l o g 2 Q ( 2 ) ) H(P,Q) = -\sum_{i=(1,2)}{P(i)log_2{Q(i)}}\\ =-(P(1)log_2{Q(1)}+P(2)log_2{Q(2)}) H(P,Q)=i=(1,2)P(i)log2Q(i)=(P(1)log2Q(1)+P(2)log2Q(2))
在这里插入图片描述

回归用二次代价、分类用交叉熵

三、MNIST数据识别—交叉熵(含源代码)

# 导入函数库
import numpy as np
import torchvision
from torch import nn
from torch import optim
from torch.autograd import Variable
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

#载入数据
# 载入训练集
train_dataset = datasets.MNIST(root='./data/06_MNIST/', # 这个地址需要自己指定
                               train=True, # 载入训练集
                               transform=transforms.ToTensor(), # 转变为tensor数据
                               download=True)       # 下载数据
#载入测试集
test_dataset = datasets.MNIST(root='./data/06_MNIST/',
                               train=False, # 载入测试集
                               transform=transforms.ToTensor(), # 转变为tensor数据
                               download=True)       # 下载数据

#装载数据
# 设置批次大小(每次传入数据量)
batch_size = 64

# 装载数据集
train_loader = DataLoader(dataset=train_dataset,
                          batch_size=batch_size, #每批数据的大小
                          shuffle=True) # shuffle表示打乱数据
test_loader = DataLoader(dataset=test_dataset,
                          batch_size=batch_size, #每批数据的大小
                          shuffle=True) # shuffle表示打乱数据

#定义网络结构
class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.fc1 = nn.Linear(784,10)
        self.softmax = nn.Softmax(dim=1) #dim=1表示对第一个维度求概率(64,10)的数组,其第一个维度表示10
    
    def forward(self,x):
        # 得到的数据格式torch.Size([64, 1, 28, 28])需要转变为(64,784)
        x = x.view(x.size()[0],-1) # -1表示自动匹配
        x = self.fc1(x)
        x = self.softmax(x)
        return x
    
# 定义模型
model = Net()

#定义代价函数
CrossEntropy_loss = nn.CrossEntropyLoss()

#定义优化器
LR=0.5 #学习率
optimizer = optim.SGD(model.parameters(),lr=LR)


# 训练模型
def train_model():
    for i,data in enumerate(train_loader):
        # 循环一次获得一批次的数据与标签
        inputs, labels = data
        
        # 获得模型预测结果(64,10)
        out = model(inputs)
        
        #计算loss,交叉熵代价函数,out(batch,C),labels(batch),对于交叉熵out和labels的shape可以不一致
        loss = CrossEntropy_loss(out,labels)
        
        # 梯度清零
        optimizer.zero_grad()
        # 计算梯度
        loss.backward()
        # 修改权值
        optimizer.step()
        
def test_model():
    correct = 0
    for i,data in enumerate(test_loader):
        # 获取一批次的数据
        inputs, labels = data
        # 预测结果
        out = model(inputs)
        # 获得最大值即最大值所在的位置
        _,predicted = torch.max(out,1)
        # 对比预测结果与标签(累积预测正确的数量)
        correct += (predicted==labels).sum()   
    print("Test acc:{0}".format(correct.item()/len(test_dataset)))
    
if __name__=='__main__':
    for epoch in range(10):
        print('epoch:',epoch)
        train_model()
        test_model()

结果:
在这里插入图片描述
说明:
对比mse代价函数得到的结果和交叉熵代价函数得到的结果可见:使用交叉熵代价函数对于分类问题进行梯度下降得到的结果更优。
对比结果参考:pytorch学习(八)——MNIST手写数字识别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

留小星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值