大模型预训练核心代码分析
这段代码的核心是计算语言模型预测结果与真实标签之间的损失,通过对logits和labels进行适当的切片和形状重塑,使其能够匹配损失函数的输入要求。数据tokenizer这段代码的主要功能是对输入文本text进行分词处理,接着把分词结果转换为 PyTorch 张量,最后将这些张量进行压缩(去除维度为 1 的维度)。key。
·
损失函数定义
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_ids
、attention_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_beams
、no_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)
更多推荐
所有评论(0)