如何对loss进行mask
pytorch官方教程中有一个Chatbot教程,就是利用seq2seq和注意力机制实现的,感觉和机器翻译没什么不同啊,如果对话中一句话有下一句,那么就把这一对句子加入模型进行训练。其中在训练阶段,损失函数通常需要进行mask操作,因为一个batch中句子的长度通常是不一样的,一个batch中不足长度的位置需要进行填充(pad)补0,最后生成句子计算loss时需要忽略那些原本是pad的位置的值,即只保留mask中值为1位置的值,忽略值为0位置的值,具体演示如下:
import torch import torch.nn as nn import torch.nn.functional as F import itertools DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") PAD_token = 0
首先是pad函数和建立mask矩阵,矩阵的维度应该和目标一致。
def zeroPadding(l, fillvalue=PAD_token): # 输入:[[1, 1, 1], [2, 2], [3]] # 返回:[(1, 2, 3), (1, 2, 0), (1, 0, 0)] 返回已经是转置后的 [L, B] return list(itertools.zip_longest(*l, fillvalue=fillvalue)) def binaryMatrix(l): # 将targets里非pad部分标记为1,pad部分标记为0 m = [] for i, seq in enumerate(l): m.append([]) for token in seq: if token == PAD_token: m[i].append(0) else: m[i].append(1) return m
假设现在输入一个batch中有三个句子,我们按照长度从大到小排好序,LSTM或是GRU的输入和输出我们需要利用pack_padded_sequence和pad_packed_sequence进行打包和解包,感觉也是在进行mask操作。
inputs = [[1, 2, 3], [4, 5], [6]] # 输入句,一个batch,需要按照长度从大到小排好序 inputs_lengths = [3, 2, 1] targets = [[1, 2], [1, 2, 3], [1]] # 目标句,这里的长度是不确定的,mask是针对targets的 inputs_batch = torch.LongTensor(zeroPadding(inputs)) inputs_lengths = torch.LongTensor(inputs_lengths) targets_batch = torch.LongTensor(zeroPadding(targets)) targets_mask = torch.ByteTensor(binaryMatrix(zeroPadding(targets))) # 注意这里是ByteTensor print(inputs_batch) print(targets_batch) print(targets_mask)
打印后结果如下,可见维度统一变成了[L, B],并且mask和target长得一样。另外,seq2seq模型处理时for循环每次读取一行,预测下一行的值(即[B, L]时的一列预测下一列)。
tensor([[ 1, 4, 6], [ 2, 5, 0], [ 3, 0, 0]]) tensor([[ 1, 1, 1], [ 2, 2, 0], [ 0, 3, 0]]) tensor([[ 1, 1, 1], [ 1, 1, 0], [ 0, 1, 0]], dtype=torch.uint8)
现在假设我们将inputs输入模型后,模型读入sos后预测的第一行为outputs1, 维度为[B, vocab_size],即每个词在词汇表中的概率,模型输出之前需要softmax。
outputs1 = torch.FloatTensor([[0.2, 0.1, 0.7], [0.3, 0.6, 0.1], [0.4, 0.5, 0.1]]) print(outputs1)
tensor([[ 0.2000, 0.1000, 0.7000], [ 0.3000, 0.6000, 0.1000], [ 0.4000, 0.5000, 0.1000]])
先看看两个函数
torch.gather(input, dim, index, out=None)->Tensor
沿着某个轴,按照指定维度采集数据,对于3维数据,相当于进行如下操作:
out[i][j][k] = input[index[i][j][k]][j][k] # if dim == 0 out[i][j][k] = input[i][index[i][j][k]][k] # if dim == 1 out[i][j][k] = input[i][j][index[i][j][k]] # if dim == 2
比如在这里,在第1维,选第二个元素。
# 收集每行的第2个元素 temp = torch.gather(outputs1, 1, torch.LongTensor([[1], [1], [1]])) print(temp)
tensor([[ 0.1000], [ 0.6000], [ 0.5000]])
torch.masked_select(input, mask, out=None)->Tensor
根据mask(ByteTensor)选取对应位置的值,返回一维张量。
例如在这里我们选取temp大于等于0.5的值。
mask = temp.ge(0.5) # 大于等于0.5 print(mask) print(torch.masked_select(temp, temp.ge(0.5)))
tensor([[ 0], [ 1], [ 1]], dtype=torch.uint8) tensor([ 0.6000, 0.5000])
然后我们就可以计算loss了,这里是负对数损失函数,之前模型的输出要进行softmax。
# 计算一个batch内的平均负对数似然损失,即只考虑mask为1的元素 def maskNLLLoss(inp, target, mask): nTotal = mask.sum() # 收集目标词的概率,并取负对数 crossEntropy = -torch.log(torch.gather(inp, 1, target.view(-1, 1))) # 只保留mask中值为1的部分,并求均值 loss = crossEntropy.masked_select(mask).mean() loss = loss.to(DEVICE) return loss, nTotal.item()
这里我们计算第一行的平均损失。
# 计算预测的第一行和targets的第一行的loss maskNLLLoss(outputs1, targets_batch[0], targets_mask[0]) (tensor(1.1689, device='cuda:0'), 3)
最后进行最后把所有行的loss累加起来变为total_loss.backward()进行反向传播就可以了。
以上这篇pytorch实现seq2seq时对loss进行mask的方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]