pytorch自带网络_Pytorch 中交叉熵 Loss 趣解

16f26aeeba981bc68102c0009b212a21.png

背景

最近一直在总结Pytorch中Loss的各种用法,交叉熵是深度学习中最常用的计算方法,写这个稿子把交叉熵的来龙去脉做一个总结。

什么是交叉熵

信息量

引用百度百科中信息量的例子来看,

在日常生活中,极少发生的事件一旦发生是容易引起人们关注的,而司空见惯的事不会引起注意,也就是说,极少见的事件所带来的信息量多。如果用统计学的术语来描述,就是出现概率小的事件信息量多。因此,事件出现得概率越小,信息量愈大。即信息量的多少是与事件发生频繁(即概率大小)成反比。

故越小概率的事情发生的事件本身具有的信息量就越大。例如在去年夏天,小卡拒了湖人投奔快船还捎带打劫雷霆了一个泡椒,这种闷声大发财的事情就有很大的信息量。

ac13810c814bcec91e068c4e90800395.png

信息量的计算公式为:

信息熵

理解了信息量之后,信息熵的理解也就不再困难了。熵原本是热力学中的一个概念,是用来衡量混乱程度的物理量。信息熵则是借用热力学的概念,衡量在事件发生前对于产生信息量的期望。即信息量是确定的具体事件发生后的信息的度量,信息熵是事件发生前预估的期望。

信息熵的计算公式为:

可以看到信息熵是一个求和的函数,是求得信息量的期望。还是以小卡为例,小卡转会前,假设去湖人的概率是0.4,去其他30支球队的概率分别为(计算方便),猛龙概率为0(心疼...),那么小卡转会的信息熵为

所以小卡转会这个事件预计的信息量为,但是实际小卡去了快船,实际的信息量为。因为这是一个非常轰动的事件,所以实际的信息量大于了估计所得的期望。

dc6cde9fe74d6ab24a73419d5b2c5ef5.png

KL散度与交叉熵

理解了信息量和信息熵之后,接下来就是交叉熵的概念了。介绍交叉熵之前,Loss是绕不开的。Loss的通俗解释就是预测值和真实值的差异,然后有各种各样的方法来衡量这个差异有多大,本文所介绍的交叉熵也是一种衡量Loss的方法。

KL散度

在讲交叉熵之前,有一个类似的东西叫KL散度,KL散度是用来衡量两个分布之间差异的指标,计算公式为

公式里面是真实值,是预测值,如果与相同时,即两者之间没有差异。

交叉熵

现在我们将KL散度的公式进行变形,

其中是真实值的信息熵,第二项

就是多分类的交叉熵。因此KL散度也被成为相对熵。对于二分类而言,交叉熵为

二分类交叉熵

Pytorch总共提供了两种二分类交叉熵,一种是nn.BCELoss,另一种是nn.BCEWithLogitsLoss,这两个的差别非常细微,nn.BCEWithLogitsLoss=nn.Sigmoid+nn.BCELoss。这里结合Pytorch的代码做一下验证,首先先验证nn.BCELoss

m = nn.Sigmoid()loss = nn.BCELoss()input = torch.randn(3, requires_grad=True)# tensor([1.5051, 2.5170, 0.7961], requires_grad=True)target = torch.empty(3).random_(2)# tensor([1., 1., 1.])m(input)# tensor([0.8183, 0.9253, 0.6891], grad_fn=)output = loss(m(input), target)# tensor(0.2168, grad_fn=)

对于nn.BCEWithLogitsLoss而言,使用的代码为

# tensor([1.5051, 2.5170, 0.7961], requires_grad=True)loss1 = nn.BCEWithLogitsLoss()output1 = loss1(input, target)# tensor(0.2168, grad_fn=)

可以看到两者的输入完全相同,输出nn.BCEWithLogitsLoss完全等于nn.BCELoss加上nn.Sigmoid

多分类交叉熵

对于多分类交叉熵函数而言,一般使用nn.CrossEntropyLoss,该函数的计算流程为:

  1. 在输入值上施加nn.Softmax函数
  2. 对于第一步所得结果使用log函数,将较为耗时的乘法运算改为加法运算,并将其归一化到之间
  3. 将第二步所得输出输入nn.NLLLoss函数中,nn.NNLLLoss的作用就是接受负对数似然值,然后对其求平均。

具体的案例在下一节的CIFAR-10的分类问题中。

实际应用

分类问题

这里我们使用Pytorch自带的CIFAR-10的数据集进行分类,训练的网络为

import torch.nn as nnimport torch.nn.functional as Fclass Net(nn.Module):    def __init__(self):        super(Net, self).__init__()        self.conv1 = nn.Conv2d(3, 6, 5)        self.pool = nn.MaxPool2d(2, 2)        self.conv2 = nn.Conv2d(6, 16, 5)        self.fc1 = nn.Linear(16 * 5 * 5, 120)        self.fc2 = nn.Linear(120, 84)        self.fc3 = nn.Linear(84, 10)    def forward(self, x):        x = self.pool(F.relu(self.conv1(x)))        x = self.pool(F.relu(self.conv2(x)))        x = x.view(-1, 16 * 5 * 5)        x = F.relu(self.fc1(x))        x = F.relu(self.fc2(x))        x = self.fc3(x)        return x

网络结构如下图所示:

5f4e98a1794f42dc90101c4b70e5f6a6.png

使用的loss为nn.CrossEntropyLoss,使用的优化器为SGD优化器,batch size为4,分类的图片类别为10类。

网络的输入为:

52928e3d212b8b71304ce218286a8a0d.png

网络的输出为

tensor([[ 0.1788,  2.7710, -1.7293, -0.7374, -0.2494, -1.8283,  3.9494, -2.5115,         -3.8941,  3.0328],        [-2.4240, -2.4709,  2.3784,  2.9654,  1.3043,  2.0047,  0.7166,  1.0876,         -1.9398, -1.9151],        [ 2.8583, -2.6383,  1.5032,  0.3617,  1.3289, -1.2958,  0.0279, -2.3972,          2.3778, -2.0852],        [-2.8961, -2.9959,  2.6880,  2.5834,  2.5803,  2.3840,  2.4317,  0.9671,         -3.0246, -2.5225]], grad_fn=)

输出是一个4×10的矩阵,对应的label为,

label = tensor([9, 5, 8, 6]) #truck, dog, ship, frog

将网络直接的输出输入到nn.softmax可得,并验证加和结果为

softmax_func=nn.Softmax(dim=1)# tensor([[1.3065e-02, 1.7453e-01, 1.9384e-03, 5.2261e-03, 8.5139e-03, 1.7555e-03,#         5.6710e-01, 8.8657e-04, 2.2246e-04, 2.2676e-01],#         [1.8934e-03, 1.8066e-03, 2.3061e-01, 4.1479e-01, 7.8778e-02, 1.5870e-01,#          4.3770e-02, 6.3433e-02, 3.0727e-03, 3.1494e-03],#         [4.4119e-01, 1.8093e-03, 1.1379e-01, 3.6341e-02, 9.5598e-02, 6.9270e-03,#          2.6028e-02, 2.3025e-03, 2.7287e-01, 3.1457e-03],#         [8.3395e-04, 7.5479e-04, 2.2197e-01, 1.9993e-01, 1.9930e-01, 1.6378e-01,#          1.7178e-01, 3.9713e-02, 7.3344e-04, 1.2118e-03]],#       grad_fn=)softmax_func(outputs).sum(1)# tensor([1.0000, 1.0000, 1.0000, 1.0000], grad_fn=)

接下来,将nn.softmax的输出输入torch.log可以得到

log_outputs=torch.log(soft_output)# tensor([[-4.3379, -1.7457, -6.2459, -5.2541, -4.7661, -6.3450, -0.5672, -7.0281,#         -8.4108, -1.4839],#        [-6.2694, -6.3163, -1.4670, -0.8800, -2.5411, -1.8407, -3.1288, -2.7578,#         -5.7852, -5.7605],#        [-0.8183, -6.3148, -2.1734, -3.3148, -2.3476, -4.9723, -3.6486, -6.0738,#         -1.2988, -5.7617],#        [-7.0893, -7.1891, -1.5052, -1.6098, -1.6130, -1.8093, -1.7616, -3.2261,#         -7.2178, -6.7157]], grad_fn=)

最后将log_outputs通过nn.NLLLoss并与nn.CrossEntropy对比

nllloss_func=nn.NLLLoss()nlloss_output=nllloss_func(log_outputs,labels)# tensor(1.5962, grad_fn=)criterion = nn.CrossEntropyLoss()criterion(outputs, labels)# tensor(1.5962, grad_fn=)

可以发现经过组合的loss和直接用nn.CrossEntroyLoss得到的loss是一样的。

下一次推送我们将会解析一下Kaiming大神的Focal Loss。

参考文献

[1] https://gombru.github.io/2018/05/23/cross_entropy_loss/

[2]https://www.baidu.com/link?url=mm7cnRyOERSRY_TPjZ8WbzU3im5Hq1JstcfLngNj4y0P5H4gC9lAhGLWnTBAgoucSnBu-Ek_fwM-RuyWSOfPxv4Idbxr0hm-udxOVd3Yz4rFgPymoQpsOb8_UsSmub-I&wd=&eqid=b0181d0f0002cd51000000045eccec89

[3] https://pytorch.org/docs/master/generated/torch.nn.BCELoss.html

[4] https://pytorch.org/docs/master/generated/torch.nn.NLLLoss.html

推荐阅读

(点击标题可跳转阅读)

干货 | 公众号历史文章精选

我的深度学习入门路线

我的机器学习入门路线图

重磅

AI有道年度技术文章电子版PDF来啦!

a1aab60bb346cb9eafde611c4acdc936.png

扫描下方二维码,添加 AI有道小助手微信,可申请入群,并获得2020完整技术文章合集PDF(一定要备注:入群 + 地点 + 学校/公司。例如:入群+上海+复旦。 

2d9dcb00bdd85aa89b3f96003c78fa7a.png

长按扫码,申请入群

(添加人数较多,请耐心等待)

最新 AI 干货,我在看 fe7a6750f103a348435eef84f6118670.png

好的,以下是使用 PyTorch 搭建 Transformer 时间序列预测网络的代码。 首先,需要导入必要的库: ```python import torch import torch.nn as nn import torch.optim as optim import numpy as np ``` 然后,我们定义一个 Transformer 模型类,其包括了编码器、解码器和最终的输出层。 ```python class TransformerTimeSeries(nn.Module): def __init__(self, input_size, output_size, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout): super(TransformerTimeSeries, self).__init__() self.input_size = input_size self.output_size = output_size self.d_model = d_model self.nhead = nhead self.num_encoder_layers = num_encoder_layers self.num_decoder_layers = num_decoder_layers self.dim_feedforward = dim_feedforward self.dropout = dropout self.encoder = nn.TransformerEncoder( nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead, dim_feedforward=dim_feedforward, dropout=dropout), num_layers=num_encoder_layers ) self.decoder = nn.TransformerDecoder( nn.TransformerDecoderLayer(d_model=d_model, nhead=nhead, dim_feedforward=dim_feedforward, dropout=dropout), num_layers=num_decoder_layers ) self.linear = nn.Linear(d_model, output_size) def forward(self, src, tgt): src_mask = self.generate_square_subsequent_mask(src.size(0)).to(src.device) tgt_mask = self.generate_square_subsequent_mask(tgt.size(0)).to(tgt.device) memory = self.encoder(src, src_mask) output = self.decoder(tgt, memory, tgt_mask=tgt_mask) output = self.linear(output) return output def generate_square_subsequent_mask(self, sz): mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1) mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0)) return mask ``` 在这个模型,我们使用了 PyTorch 自带的 Transformer 编码器和解码器,并且定义了一个线性层作为最终的输出层。在 `forward` 函数,我们通过编码器得到了输入序列的表示 `memory`,然后将其传递给解码器,解码器再根据目标序列生成预测结果。 接下来,我们定义一个函数来训练这个模型。 ```python def train(model, optimizer, criterion, train_loader, num_epochs): for epoch in range(num_epochs): train_loss = 0.0 for i, (inputs, targets) in enumerate(train_loader): optimizer.zero_grad() outputs = model(inputs, targets[:-1]) loss = criterion(outputs.reshape(-1, outputs.size(-1)), targets[1:].reshape(-1)) loss.backward() optimizer.step() train_loss += loss.item() print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, train_loss/len(train_loader))) ``` 在这个函数,我们使用交叉熵损失函数,并且在训练过程使用了梯度下降优化器。每个 epoch ,我们遍历训练数据集,计算损失并更新模型参数,最后输出平均损失。 最后,我们可以使用定义好的模型和训练函数来训练我们的时间序列预测网络了。 ```python # 定义超参数 input_size = 1 output_size = 1 d_model = 16 nhead = 4 num_encoder_layers = 2 num_decoder_layers = 2 dim_feedforward = 64 dropout = 0.1 num_epochs = 100 learning_rate = 0.001 # 准备数据 train_data = np.sin(np.linspace(0, 100, 1000)) train_data = torch.from_numpy(train_data).float().unsqueeze(-1) train_loader = torch.utils.data.DataLoader(train_data, batch_size=32) # 定义模型和优化器 model = TransformerTimeSeries(input_size, output_size, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate) # 训练模型 train(model, optimizer, criterion, train_loader, num_epochs) ``` 在这个例子,我们使用了一个简单的正弦函数来作为训练数据集。你可以根据你的需求替换成其他的时间序列数据集。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值