损失函数定义

logits = outputs.logits  # logits 的形状是 (batch_size, sequence_length, vocab_size)
# 获取标签
labels = inputs['input_ids'][:, 1:]  # 标签的形状是 (batch_size, sequence_length - 1)
loss = criterion(logits[:, :-1, :].reshape(-1, logits.size(-1)), labels.reshape(-1)) 

1. logits = outputs.logits # logits 的形状是 (batch_size, sequence_length, vocab_size)
  • outputs 是语言模型的输出对象,一般包含了模型的预测结果。
  • outputs.logits 是模型的原始预测分数,没有经过 softmax 函数转换为概率。
  • logits 的形状是 (batch_size, sequence_length, vocab_size),其中:
    • batch_size 表示一次处理的样本数量。
    • sequence_length 表示每个样本的序列长度。
    • vocab_size 表示词汇表的大小,即模型可能预测的所有词的数量。
2. labels = inputs['input_ids'][:, 1:] # 标签的形状是 (batch_size, sequence_length - 1)
  • inputs 是模型的输入字典,inputs['input_ids'] 是输入文本的词索引序列。
  • [:, 1:] 是切片操作,意思是取每个样本从第二个位置开始到最后的部分。这样做是因为在语言模型里,我们通常是根据前面的词来预测下一个词,所以标签是输入序列去掉第一个词。
  • labels 的形状是 (batch_size, sequence_length - 1),即每个样本的标签序列长度比输入序列长度少 1。
3. loss = criterion(logits[:, :-1, :].reshape(-1, logits.size(-1)), labels.reshape(-1))
  • criterion 是损失函数,例如交叉熵损失函数 nn.CrossEntropyLoss()
  • logits[:, :-1, :] 是对 logits 进行切片操作,取每个样本从第一个位置到倒数第二个位置的预测结果。这是因为我们要让模型预测的结果与标签对应,所以预测序列的长度要和标签序列长度一致。
  • reshape(-1, logits.size(-1)) 是将切片后的 logits 张量进行形状重塑,将其转换为二维张量,形状为 (batch_size * (sequence_length - 1), vocab_size)
  • labels.reshape(-1) 是将 labels 张量进行形状重塑,将其转换为一维张量,形状为 (batch_size * (sequence_length - 1),)
  • 最后,将重塑后的 logits 和 labels 传入损失函数 criterion 中计算损失值。

总结

这段代码的核心是计算语言模型预测结果与真实标签之间的损失,通过对 logits 和 labels 进行适当的切片和形状重塑,使其能够匹配损失函数的输入要求。

数据tokenizer

inputs = self.tokenizer(text, return_tensors='pt', max_length=self.max_length, padding='max_length', truncation=True) return {key: val.squeeze(0) for key, val in inputs.items()}

代码功能概述

 

这段代码的主要功能是对输入文本 text 进行分词处理,接着把分词结果转换为 PyTorch 张量,最后将这些张量进行压缩(去除维度为 1 的维度)。

key 和 val 的含义

 
  • key:它代表字典 inputs 里的键。inputs 是 self.tokenizer 方法返回的字典,此字典包含了分词后的各类信息,像 input_idsattention_mask 等。
  • val:它代表字典 inputs 里对应键的值。这些值一般是 PyTorch 张量,用于存储分词后的具体数据。

解码

# 对输入文本进行编码
inputs = tokenizer(test_input, return_tensors="pt").to(device)

# 生成文本(设置最大生成长度为100)
#output = model.generate(**inputs, max_length=100)
output = model.generate(
    **inputs,
    max_length=100,  # 生成文本的最大长度
    num_beams=5,  # 束搜索的束宽
    no_repeat_ngram_size=2,  # 避免重复的 n-gram 大小
    early_stopping=True  # 是否提前停止生成
)

# 将生成的ID解码为文本
generated_text = tokenizer.decode(output[0], skip_special_tokens=True)

在使用 model.generate 方法生成文本时,下面代码生成的内容与预训练的文本语料更一致,而上面简单使用 max_length 参数生成的内容不一致,可能是由以下几个方面的因素导致的:

1. 束搜索(num_beams 参数)

 
  • 原理:在第一个代码片段中,只是简单地使用 max_length 进行生成,模型默认使用贪心搜索(greedy search),即每一步都选择概率最大的词元(token)作为输出。而在第二个代码片段中,设置了 num_beams = 5,这意味着使用了束搜索(beam search)。束搜索会在每一步保留概率最高的 num_beams 个候选序列,然后综合考虑后续的可能性来选择最优的生成路径。
  • 影响:束搜索能够更全面地探索可能的生成路径,相比贪心搜索,它有更大的机会找到与预训练语料中模式更匹配的生成结果。因为预训练语料通常具有一定的语言模式和上下文依赖关系,束搜索可以更好地捕捉这些信息,从而生成与语料更相似的文本。

2. 避免重复(no_repeat_ngram_size 参数)

 
  • 原理:在第一个代码片段中没有设置避免重复的参数,模型在生成过程中可能会出现重复的 n-gram(连续的词元序列)。而第二个代码片段中设置了 no_repeat_ngram_size = 2,这会禁止模型生成连续重复的两个词元的序列。
  • 影响:预训练语料中的文本通常具有一定的多样性和流畅性,避免重复的 n-gram 可以使生成的文本更符合自然语言的表达习惯,与预训练语料的风格更加一致。如果生成的文本中存在大量重复的内容,就会显得不自然,与预训练语料的差异较大。

3. 提前停止(early_stopping 参数)

 
  • 原理:第一个代码片段没有设置提前停止的条件,模型会一直生成直到达到 max_length。而第二个代码片段中设置了 early_stopping = True,当模型生成的序列达到一定的条件(如生成了结束标记或者某个特定的概率阈值)时,会提前停止生成。
  • 影响:在预训练语料中,文本通常有一定的完整性和逻辑性。提前停止可以避免模型生成一些不必要的、与语料风格不符的内容,使得生成的文本更紧凑、更符合预期,从而与预训练语料更加一致。
 

综上所述,通过设置 num_beamsno_repeat_ngram_size 和 early_stopping 等参数,第二个代码片段能够更好地控制生成过程,使生成的文本更符合预训练语料的模式、风格和逻辑,从而与预训练语料更加一致。

检验生成代码模块

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# 设置设备(自动检测是否有GPU可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 模型名称(这里以Llama-2为例,你可以替换为其他模型名称)
model_name = "./model"

# 加载模型和分词器
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name).to(device)

# 测试输入文本
test_input = "Beyond Single-Event Extraction:"

# 对输入文本进行编码
inputs = tokenizer(test_input, return_tensors="pt").to(device)

# 生成文本(设置最大生成长度为100)
#output = model.generate(**inputs, max_length=100)
output = model.generate(
    **inputs,
    max_length=100,  # 生成文本的最大长度
    num_beams=5,  # 束搜索的束宽
    no_repeat_ngram_size=2,  # 避免重复的 n-gram 大小
    early_stopping=True  # 是否提前停止生成
)

# 将生成的ID解码为文本
generated_text = tokenizer.decode(output[0], skip_special_tokens=True)

# 打印结果
print("Generated Text:")
print(generated_text)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐