NLLLoss
class torch.nn.NLLLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction=‘elementwise_mean’)
功能: 不好用言语描述其功能!请看计算公式:loss(input, class) = -input[class]。举个例,三分类任务,input=[-1.233, 2.657, 0.534], 真实标签为2(class=2),则loss为-0.534。就是对应类别上的输出,取一个负号!感觉被NLLLoss的名字欺骗了。 实际应用: 常用于多分类任务,但是input在输入NLLLoss()之前,需要对input进行log_softmax函数激活,即将input转换成概率分布的形式,并且取对数。其实这些步骤在CrossEntropyLoss中就有,如果不想让网络的最后一层是log_softmax层的话,就可以采用CrossEntropyLoss完全代替此函数。
参数: weight(Tensor)- 为每个类别的loss设置权值,常用于类别不均衡问题。weight必须是float类型的tensor,其长度要于类别C一致,即每一个类别都要设置有weight。 size_average(bool)- 当reduce=True时有效。为True时,返回的loss为除以权重之和的平均值;为False时,返回的各样本的loss之和。
reduce(bool)- 返回值是否为标量,默认为True。
ignore_index(int)- 忽略某一类别,不计算其loss,其loss会为0,并且,在采用size_average时,不会计算那一类的loss,除的时候的分母也不会统计那一类的样本。
实例: /Code/3_optimizer/3_1_lossFunction/4_NLLLoss.py
特别注意: 当带上权值,reduce = True, size_average = True, 其计算公式为:

例如当input为[[0.6, 0.2, 0.2], [0.4, 1.2, 0.4]],target= [0, 1], weight = [0.6, 0.2, 0.2] l1 = - 0.60.6 = - 0.36 l2 = - 1.20.2 = - 0.24 loss = -0.36/(0.6+0.2) + -0.24/(0.6+0.2) = -0.75
import torch
input=torch.randn(3,3)
soft_input = torch.nn.Softmax(dim=0)
soft_input(input)
Out[20]:
tensor([[0.7284, 0.7364, 0.3343],
[0.1565, 0.0365, 0.0408],
[0.1150, 0.2270, 0.6250]])
#对softmax结果取log
torch.log(soft_input(input))
Out[21]:
tensor([[-0.3168, -0.3059, -1.0958],
[-1.8546, -3.3093, -3.1995],
[-2.1625, -1.4827, -0.4701]])
假设标签是[0,1,2],第一行取第0个元素,第二行取第1个,第三行取第2个,去掉负号,即[0.3168,3.3093,0.4701],求平均值,就可以得到损失值。
nn.NLLLoss
官方文档中介绍称: nn.NLLLoss输入是一个对数概率向量和一个目标标签,它与nn.CrossEntropyLoss的关系可以描述为:softmax(x)+log(x)+nn.NLLLoss====>nn.CrossEntropyLoss
def prepare_train_input(insts, bos_id, eos_id, pad_id):
# Add eos token id and bos token id.
src = [[bos_id] + inst + [eos_id] for inst in insts]
trg = [inst[:-1] for inst in insts]
label = [inst[1:] for inst in insts]
# Pad sequence using eos id.
src, src_length = Pad(pad_val=pad_id, ret_length=True, dtype="int64")(
[ids for ids in src])
trg, trg_length = Pad(pad_val=pad_id, ret_length=True, dtype="int64")(
[ids for ids in trg])
label, _ = Pad(pad_val=pad_id, ret_length=True, dtype="int64")(
[ids for ids in label])
label = np.array(label)
label = label.reshape((label.shape[0], label.shape[1], 1))
return src, src_length, trg, trg_length, label
src = [[bos_id] + inst + [eos_id] for inst in insts]
trg = [inst[:-1] for inst in insts]
label = [inst[1:] for inst in insts]
输入都是一个batch,一个batch内的没个样本都做处理,已经添加开始符号和结束符号了,解码部分真实输入就是去掉结束符号,保留开始符号,解码部分真实的标签就是输入去掉起始符号。刚好错开一位。
记住编码阶段有输入输出,解码阶段有输入输出,但是训练是一起训练的,同时输入输出的。
class CrossEntropyWithKL(nn.Layer):
"""
backward_loss = kl_loss * kl_weight + cross_entropy_loss
"""
def __init__(self, base_kl_weight, anneal_r):
super(CrossEntropyWithKL, self).__init__()
self.kl_weight = base_kl_weight
self.anneal_r = anneal_r
self.loss = 0.0
self.kl_loss = 0.0
self.rec_loss = 0.0
def update_kl_weight(self):
self.kl_weight = min(1.0, self.kl_weight + self.anneal_r)
def forward(self, kl_loss, dec_output, trg_mask, label):
self.update_kl_weight()
self.kl_loss = kl_loss
rec_loss = F.cross_entropy(
input=dec_output, label=label, reduction='none', soft_label=False)
rec_loss = paddle.squeeze(rec_loss, axis=[2])
rec_loss = rec_loss * trg_mask
rec_loss = paddle.mean(rec_loss, axis=[0])
rec_loss = paddle.sum(rec_loss)
self.rec_loss = rec_loss
self.loss = self.kl_loss * self.kl_weight + self.rec_loss
return self.loss
这个就是两个loss输出的loss和分布的loss
# Encoder
_, enc_final_state = self.encoder(src, src_length)
虽然做了填充,但是实际的长度是要知道的,填充部分不能参与运算
outputs, _ = nn.dynamic_decode(
decoder,
inits=dec_initial_states,
max_step_num=self.max_out_len,
latent_z=latent_z)
return outputs
动态LSTM编码,在函数里面直接把循环做了,一次性得到结果。
def infer(args):
print(args)
device = paddle.set_device(args.device)
_, _, _, vocab, bos_id, eos_id, _ = create_data_loader(args)
net = VAESeq2SeqInferModel(args.embed_dim, args.hidden_size,
args.latent_size, len(vocab) + 2)
model = paddle.Model(net)
model.prepare()
model.load(args.init_from_ckpt)
infer_output = paddle.ones((args.batch_size, 1), dtype='int64') * bos_id
space_token = ' '
line_token = '\n'
with io.open(args.infer_output_file, 'w', encoding='utf-8') as out_file:
predict_lines = model.predict_batch(infer_output)[0]
for line in predict_lines:
end_id = -1
if eos_id in line:
end_id = np.where(line == eos_id)[0][0]
new_line = [vocab.to_tokens(e[0]) for e in line[:end_id]]
out_file.write(space_token.join(new_line))
out_file.write(line_token)
应用预测生产文本阶段,只用了解码部分,动态编码,在在内部解码循环一次性全部生成相应的输出。
本文详细介绍了PyTorch中NLLLoss的功能和使用方法,指出它常用于多分类任务,且输入需要先通过log_softmax转化为概率分布。NLLLoss计算涉及对数似然损失,结合weight、size_average等参数能处理类别不平衡问题。同时,文章对比了NLLLoss与CrossEntropyLoss的关系,强调后者已包含softmax和负对数似然损失。此外,还展示了代码示例来帮助理解。

1170

被折叠的 条评论
为什么被折叠?



