(十三)T5是如何计算损失的
一、概述
T5 使用常规交叉熵损失(与任何语言模型一样)。
假设您正在微调 T5 以进行翻译,并且您有以下训练示例:
* source sentence: "hello how are you"
* target sentence: "salut comment ça-va"
首先,需要使用 对模型的句子进行标记。假设每个单词都被标记化为一个标记,并且我们还添加了 T5 的特殊标记(即 </s> - 它表示序列的结束),我们为模型提供以下输入:T5Tokenizer
* input tokens = [hello, how, are, you, </s>]
* label tokens = [salut, comment, ça, -, va, </s>]
当然,我们不会将这些标记作为文本提供给模型,而是以整数 ID 的形式提供,这些 ID 指的是嵌入矩阵中的行索引,因此实际输入将如下所示:
* input_ids = [21820, 149, 33, 25, 1]
* labels = [20239, 1670, 3664, 18, 900, 1]
在这种情况下,您首先向 T5 的编码器提供,该编码器会将其转换为形状的张量。接下来,T5 的解码器将为目标序列的每个标记预测正确的下一个标记。具体如下:input_ids
(batch_size, seq_len, hidden_size)
salut comment ça - va </s> => label tokens
20239 1670 3664 18 900 1 => labels
----------------------------------------------------------------------------------------------
DECODER
----------------------------------------------------------------------------------------------
0 20239 1670 3664 18 900 => decoder_input_ids
decoder_start_token salut comment ça - va => decoder input tokens
换句话说,我们用一个特殊的标记(解码器开始标记 - 对于 T5 来说是填充标记,索引为 0)在解码器输入前面,然后解码器需要(并行)预测:
- 解码器启动令牌后面的令牌是“Salut”。在这里,我们计算模型预测与目标标记(即“致敬”)之间的交叉熵损失。
- “Salut”后面的标记是“comment”。在这里,我们计算模型预测和目标标记(即“注释”)之间的交叉熵损失。
- “comment”后面的标记是“ça”。在这里,我们计算模型预测和目标标记(即“ça”)之间的交叉熵损失。
- 等。
- “va”后面的令牌是“</s>”(意思是序列末尾或EOS令牌)。在这里,我们计算模型预测与目标标记(“</s>”)之间的交叉熵损失。
在法典 389,这是一次性完成的,即通过将模型的对数(形状为 batch_size、seq_len、vocab_size))与地面真值标签进行比较:
loss = loss_fct(lm_logits.view(-1, lm_logits.size(-1)), labels.view(-1))
引用: What is loss function for T5 - Models - Hugging Face Forums
二、详细讲解
import torch from torch import nn from transformers import T5ForConditionalGeneration, T5Tokenizer # 修改后的自定义损失函数 class CustomLoss(nn.Module): def __init__(self): super(CustomLoss, self).__init__() self.loss_fn = nn.CrossEntropyLoss(reduction='none') def forward(self, outputs, labels): logits = outputs.logits batch_size, seq_len, vocab_size = logits.size() # 获取每个标签序列的实际长度(去掉pad) label_lengths = (labels != tokenizer.pad_token_id).sum(dim=1) # 计算权重 weights = torch.zeros_like(labels, dtype=torch.float) for i, length in enumerate(label_lengths): length = length.item() weights[i, :length] = torch.arange(length + 1, 1, -1, dtype=torch.float) # 计算损失 loss = self.loss_fn(logits.view(-1, vocab_size), labels.view(-1)) loss = loss.view(batch_size, seq_len) # 应用权重 weighted_loss = loss * weights # 计算平均损失 weighted_loss = weighted_loss.sum() / weights.sum() return weighted_loss # 加载模型和分词器 model_name = 't5-small' model = T5ForConditionalGeneration.from_pretrained(model_name) tokenizer = T5Tokenizer.from_pretrained(model_name) # 示例训练数据 train_data = [ {'input_text': 'translate English to French: Hello, how are you?', 'target_text': 'Bonjour, comment ça va?'}, # 添加更多训练数据... ] # 训练函数 def train(model, tokenizer, custom_loss_fn, train_data, epochs=3, lr=1e-4): optimizer = torch.optim.Adam(model.parameters(), lr=lr) for epoch in range(epochs): model.train() total_loss = 0 for batch in train_data: inputs = tokenizer(batch['input_text'], return_tensors='pt', padding=True, truncation=True).input_ids labels = tokenizer(batch['target_text'], return_tensors='pt', padding=True, truncation=True).input_ids outputs = model(input_ids=inputs, labels=labels) loss = custom_loss_fn(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() total_loss += loss.item() avg_loss = total_loss / len(train_data) print(f'Epoch {epoch + 1}, Loss: {avg_loss}') # 初始化自定义损失函数 custom_loss_fn = CustomLoss() # 训练模型 train(model, tokenizer, custom_loss_fn, train_data)