LLM的finetune 中的 attention_mask 和input_ids

一。input_ids :

它是通过tokenizer分词器,把原来的context中的text进行进行转化,变为词元。 然后进行相应的id转化,通过查词表,记录该词在原始词表位置,即:索引位置 

【2,18,30,50,。。。】这个索引的数字就是该词在原词表中的第i位置中。 

接着就是embedding,映射成多少纬度。 

最后多个input_ids 通过np转化成matrix , 过程中我们会用一个常量来限制大小, 

if:这个len(matrix) 小于了常量,那么我们需要padding, padding的方式也是选择一个值(这个值一般是.eos的值),那么后续的decode过程中,我们不能把padding的值解码出来,这样是不遵循原始结构了,所以我们得用attention——mask进行处理。 

else if: 我们就得进行trunacation

  1. 序列太短需要padding
    • 如果序列长度小于所需长度,我们用特定值(如PAD token或EOS token)进行填充
    • 创建attention_mask,用1标记真实token,0标记padding位置
    • 解码时,通过attention_mask或设置skip_special_tokens=True来忽略这些padding token
  2. 序列太长需要截断(truncation)
    • 如果序列长度超过模型能处理的最大长度,需要截断
    • 通常会保留序列的开头或结合开头和结尾的部分,丢弃中间部分
    • 可能会保留特殊token如BOS(开始)和EOS(结束)标记

解码过程中,有几种方式处理特殊token:

  • 通过设置skip_special_tokens=True让tokenizer自动忽略如PAD、EOS等特殊token
  • 特殊token通常有预定义的ID,如BERT中PAD=0,解码时可以专门过滤掉它们
  • attention_mask本身不直接参与解码,但它在模型生成过程中确保模型不关注padding部分

所以padding和truncation是处理不同长度输入文本的两种互补方法,让批处理高效进行的同时保持模型表现。

Tokenizer自动处理padding和truncation:

inputs = tokenizer(
    ["短文本", "这是一个较长的文本需要被处理..."], 
    padding=True,  # 自动添加padding
    truncation=True,  # 自动截断过长文本
    max_length=512,  # 最大长度
    return_tensors="pt"  # 返回PyTorch张量
)
# 自动生成attention_mask
# inputs包含: input_ids, attention_mask

二.attention_mask的作用就是告诉模型哪些token是真实输入哪些只是padding 。 

attention_mask通常就是由0和1组成的序列,用于告诉模型哪些token是真实输入,哪些只是padding。

一个典型的attention_mask结构是这样的:

  • 1表示实际内容的token,模型需要关注这部分
  • 0表示padding token,模型应该忽略这部分

例如,如果我们有一个句子被编码为[101, 2054, 2003, 2026, 3793, 102],而最大长度是10,模型会进行padding:

  • input_ids: [101, 2054, 2003, 2026, 3793, 102, 0, 0, 0, 0]
  • attention_mask: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]

这个掩码告诉模型只考虑前6个token,忽略后面的padding token。这对模型非常重要,因为:

  1. 防止模型在无意义的padding token上浪费注意力计算
  2. 确保模型不会从padding token中学到错误的模式
  3. 对于BERT这样的双向模型尤其重要,防止一个词的表示被padding污染

当警告说"attention mask未设置"时,意味着模型无法区分真实输入和padding,可能导致不可预测的行为,尤其是当输入序列长度不一时。

三、LLM推理函数 

def inference(question, model, tokenizer, max_length=512, do_sample=True, temperature=0.7):
    """
    使用语言模型对输入问题进行推理并生成回答
    
    参数:
        question (str): 输入的问题文本
        model: 已加载的语言模型 (如从AutoModelForCausalLM加载的模型)
        tokenizer: 与模型匹配的分词器
        max_length (int): 生成文本的最大长度
        do_sample (bool): 是否使用采样策略生成文本
        temperature (float): 生成温度,越高越随机
        
    返回:
        str: 模型生成的回答文本
    """
    # 设置设备
    device = model.device
    
    # 编码输入文本
    inputs = tokenizer(question, return_tensors="pt").to(device)
    
    # 处理attention_mask警告
    if "attention_mask" not in inputs:
        print("The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior.")
        # 可以在这里添加默认的attention_mask
    
    # 设置pad_token_id (如果未设置)
    if tokenizer.pad_token_id is None:
        tokenizer.pad_token_id = tokenizer.eos_token_id
        print(f"Setting 'pad_token_id' to 'eos_token_id':{tokenizer.eos_token_id} for open-end generation.")
    
    # 生成文本
    with torch.no_grad():
        output_sequences = model.generate(
            input_ids=inputs["input_ids"],
            attention_mask=inputs.get("attention_mask", None),
            max_length=max_length,
            temperature=temperature,
            do_sample=do_sample,
            pad_token_id=tokenizer.pad_token_id,
            eos_token_id=tokenizer.eos_token_id,
        )
    
    # 解码生成的文本
    generated_text = tokenizer.decode(output_sequences[0], skip_special_tokens=True)
    
    # 从生成的文本中提取回答部分 (移除输入问题)
    # 有时输出会包含输入,所以需要处理
    if question in generated_text:
        answer = generated_text[len(question):].strip()
    else:
        answer = generated_text.strip()
    
    return answer

<think>好的,我现在要详细解释用户提供的这段代码。首先,我需要理解这段代码的功能结构。代码定义了一个名为`tokenize_texts`的函数,用于对文本进行分词处理,并将结果转换为数值格式。然后,它调用这个函数预处理训练数据,并提取标签。 首先,函数`tokenize_texts`接受三个参数:数据框`df`、分词器`tokenizer`最大长度`max_length`,默认是512。函数内部对数据框中的`response_a``response_b`两列分别进行分词处理,得到两个编码结果`encoding_a``encoding_b`。然后返回包含四个键的字典,分别是两个输入的ID注意力掩码。 接下来,代码调用这个函数处理训练数据,存储在`preprocessed_train`变量中。然后,从训练数据中提取标签,转化为PyTorch张量。 现在我需要详细解释每一部分。首先,函数的作用是将文本转换为模型可以处理的数值格式。使用Hugging Face的`tokenizer`来处理文本,应用了填充截断,确保所有输入长度一致。返回的字典结构可能用于后续模型的输入,比如双塔结构分别处理两个回答。 然后,标签部分提取了三个列:“winner_model_a”、“winner_model_b”、“winner_tie”,可能是多标签分类的情况,但通常偏好预测是二分类(选择A或B),或者三分类(包括平局)。这里用`dtype=torch.float32`可能意味着标签是多热的,比如每个样本可能属于多个类别,或者每个类别的概率。但更常见的是,对于三个类别,使用单一样本属于其中一类,所以可能需要检查数据的具体结构。 用户可能的疑问包括:为什么需要分别处理AB?如何处理标签?为什么要用这样的编码方式?另外,可能想知道这段代码在整个项目中的作用,比如如何与模型结合,是否有潜在的问题,比如位置偏差的处理是否在这里考虑。 可能需要注意的地方是,`tokenizer`的使用是否正确,比如是否在正确的列上应用,填充截断的设置是否合理。此外,标签的处理是否正确,比如是否应该使用单标签(通过argmax)还是多标签。另外,返回的字典结构是否适合后续模型的输入格式,比如是否模型需要同时接收AB的输入。 需要确保解释清楚每个参数的作用,比如`padding="max_length"``truncation=True`的作用,以及`return_tensors="pt"`的意义。同时,要指出可能的改进点,比如动态填充、处理长文本的策略,或者标签的转换方式是否需要调整。 另外,用户提供的代码可能存在潜在问题。例如,标签部分提取了三个列作为多标签,但实际任务可能是单标签分类(用户只能选择一个回答胜出或平局),这时候可能需要将这三个列转换为单一的类别标签,或者使用softmax处理。如果这三个列是互斥的,那么应该使用交叉熵损失,而标签应为类别的索引,而不是三个独立的二进制值。因此,需要指出这一点,并建议检查数据集的标签定义。 总结,我需要逐步分解代码的每个部分,解释其功能,指出潜在的问题或优化点,并关联到整个任务的目标,比如如何支持偏好模型的训练。</think>以下是对代码的详细解释,按功能模块分步说明: --- ### **一、函数定义:`tokenize_texts`** #### **功能** 将文本数据(两个回答`response_a``response_b`)转换为预训练语言模型(如BERT)所需的数值化输入格式。 #### **参数说明** - `df`: 包含文本数据的DataFrame,需有`response_a``response_b`两列。 - `tokenizer`: Hugging Face提供的分词器(如`BertTokenizer`)。 - `max_length`: 文本最大长度,默认512(大多数预训练模型的输入上限)。 #### **代码解析** 1. **对`response_a`分词** ```python encoding_a = tokenizer( df["response_a"].tolist(), # 提取回答A列表 padding="max_length", # 填充至max_length truncation=True, # 超长文本截断 max_length=max_length, # 统一文本长度 return_tensors="pt" # 返回PyTorch张量格式 ) ``` - **`padding="max_length"`**: 所有文本填充到固定长度`max_length`,不足补零。 - **`truncation=True`**: 超过`max_length`的文本从末尾截断。 - **`return_tensors="pt"`**: 返回PyTorch张量,而非Python列表。 2. **对`response_b`分词** 同上,处理`response_b`列。 3. **返回结果** 返回包含四个键的字典: ```python { "input_ids_a": encoding_a["input_ids"], # 回答A的单词ID序列 "attention_mask_a": encoding_a["attention_mask"], # 回答A的注意力掩码(0表示填充位置) "input_ids_b": encoding_b["input_ids"], # 回答B的单词ID序列 "attention_mask_b": encoding_b["attention_mask"] # 回答B的注意力掩码 } ``` --- ### **二、数据预处理流程** #### **调用函数** ```python print("Preprocessing training data...") preprocessed_train = tokenize_texts(train_data, tokenizer) ``` - 对训练数据`train_data`中的`response_a``response_b`进行分词,生成模型输入所需的张量。 #### **标签提取** ```python labels = torch.tensor( train_data[["winner_model_a", "winner_model_b", "winner_tie"]].values, dtype=torch.float32 ) ``` - **标签列说明**(需结合数据集定义): - `winner_model_a`: 二进制标签,用户是否选择回答A。 - `winner_model_b`: 二进制标签,用户是否选择回答B。 - `winner_tie`: 二进制标签,用户是否认为平局。 - **输出格式**: 将这三列转换为`float32`类型的PyTorch张量,形状为`(样本数, 3)`。 --- ### **三、关键细节与潜在问题** #### **1. 输入处理** - **双回答独立编码**:适合“双塔模型”结构(如SBERT),分别编码两个回答后计算相似度。 - **位置偏差风险**:若模型未显式处理位置信息,可能因`response_a``response_b`的顺序引入偏差。 **改进建议**:在训练时随机交换A/B位置,或添加位置标记(如`[A]``[B]`前缀)。 #### **2. 标签设计** - **多标签 vs 单标签**:当前代码假设允许用户同时选择多个标签(如AB胜出),但实际任务通常是**互斥选择**(仅A、仅B或平局)。 **问题**: 若标签列是互斥的,应使用`torch.LongTensor`存储类别索引(0,1,2),而非多热编码。 **验证方法**: 检查数据中这三列是否每行有且仅有一个值为1。 #### **3. 文本截断风险** - 若回答长度常超过512,可能丢失关键信息。 **改进建议**: - 动态调整`max_length`(如统计文本长度分布)。 - 使用支持长文本的模型(如Longformer)。 --- ### **四、代码在任务中的作用** 1. **模型输入准备** 将原始文本转换为模型可处理的数值化输入(`input_ids``attention_mask`)。 2. **标签格式适配** 将用户偏好标签转换为张量,用于监督训练(如交叉熵损失函数)。 3. **支持双回答对比** 通过独立编码两个回答,为后续计算偏好得分提供基础(如计算相似度或差异值)。 --- ### **五、完整流程示例** 假设训练数据如下: | response_a | response_b | winner_model_a | winner_model_b | winner_tie | |------------------|------------------|----------------|----------------|------------| | "答案是42" | "42是正确的" | 1 | 0 | 0 | | "需要更多数据" | "数据不足" | 0 | 0 | 1 | 1. **分词输出** - `input_ids_a`: 编码后的回答A(如`[101, 1234, 5678, 102]`) - `input_ids_b`: 编码后的回答B(如`[101, 5678, 2456, 102]`) 2. **标签输出** ```python labels = tensor([[1., 0., 0.], [0., 0., 1.]]) ``` --- ### **六、总结** 此代码是构建LLM偏好预测模型的关键预处理步骤,需结合任务需求检查标签设计输入处理方式。改进方向包括动态文本截断、位置偏差消除标签格式优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值