以上代码出现问题:(style_tune) C:\Users\28996\Desktop\AI\persona_contrastive_finetuning>python Contrastive_Training_LM.py C:\Users\28996\miniconda3\envs\style_tune\lib\site-packages\transformers\generation\configuration_utils.py:818: UserWarning: `return_dict_in_generate` is NOT set to `True`, but `output_hidden_states` is. When `return_dict_in_generate` is not `True`, `output_hidden_states` is ignored. warnings.warn( Generating train split: 1 examples [00:00, 6.57 examples/s] Generating train split: 1 examples [00:00, 142.77 examples/s] C:\Users\28996\Desktop\AI\persona_contrastive_finetuning\Contrastive_Training_LM.py:76: FutureWarning: `tokenizer` is deprecated and will be removed in version 5.0.0 for `ContrastiveTrainer.__init__`. Use `processing_class` instead. super().__init__(*args, **kwargs) Traceback (most recent call last): File "C:\Users\28996\Desktop\AI\persona_contrastive_finetuning\Contrastive_Training_LM.py", line 223, in <module> trainer.train() File "C:\Users\28996\miniconda3\envs\style_tune\lib\site-packages\transformers\trainer.py", line 2171, in train return inner_training_loop( File "C:\Users\28996\miniconda3\envs\style_tune\lib\site-packages\transformers\trainer.py", line 2200, in _inner_training_loop train_dataloader = self.get_train_dataloader() File "C:\Users\28996\miniconda3\envs\style_tune\lib\site-packages\transformers\trainer.py", line 1000, in get_train_dataloader train_dataset = self._remove_unused_columns(train_dataset, description="training") File "C:\Users\28996\miniconda3\envs\style_tune\lib\site-packages\transformers\trainer.py", line 926, in _remove_unused_columns raise ValueError( ValueError: No columns in the dataset match the model's forward method signature. The following columns have been ignored: [negative_input_ids, positive_input_ids, anchor_input_ids]. Please check the dataset and model. You may need to set `remove_unused_columns=False` in `TrainingArguments`. 请分析解决
时间: 2025-07-20 08:14:54 浏览: 8
<think>我们遇到了一个错误,提示在数据集中没有匹配模型前向方法签名的列,并且列出了三元组的列(negative_input_ids, positive_input_ids, anchor_input_ids)被忽略了。
错误信息建议我们在`TrainingArguments`中设置`remove_unused_columns=False`。
原因分析:
默认情况下,Hugging Face的Trainer会移除那些不在模型前向方法参数列表中的列。在我们的自定义训练器中,我们重写了`compute_loss`方法,并且使用了自定义的输入(即三元组的各个部分)。但是,在构建数据加载器时,Trainer会检查数据集的列是否与模型的前向方法参数匹配。由于我们的模型(`AutoModelForCausalLM`)的前向方法通常只需要`input_ids`、`attention_mask`等,而我们数据集中有`anchor_input_ids`、`positive_input_ids`、`negative_input_ids`等,所以这些列会被移除。
解决方案:
1. 在`TrainingArguments`中设置`remove_unused_columns=False`,这样就不会自动移除不匹配的列。
2. 确保在自定义训练器的`compute_loss`方法中,我们能够处理这些额外的列。
修改步骤:
在`TrainingArguments`中添加参数:`remove_unused_columns=False`
另外,注意在错误信息中还有一个关于`tokenizer`参数在`ContrastiveTrainer.__init__`中将被弃用的警告。根据警告,我们应该使用`processing_class`代替?但是,查看Hugging Face的文档,实际上在Trainer中,`tokenizer`参数并没有被弃用,而且警告信息中提到的替代参数是`processing_class`,这可能是误报。但是,为了安全起见,我们可以检查一下Trainer的初始化参数。
然而,在当前的Hugging Face Transformers版本中,Trainer的`__init__`方法仍然支持`tokenizer`参数。警告信息可能是由于我们自定义的Trainer类继承自Trainer,而在初始化时传递了`tokenizer`参数,而父类Trainer在未来的版本中可能会改变参数名称。但是,目前我们仍然需要传递`tokenizer`参数,因为我们在自定义的`ContrastiveDataCollator`中使用了它。
因此,我们忽略这个警告,或者根据警告建议,在自定义Trainer中不使用`tokenizer`参数?但是,我们在`compute_loss`方法中使用了`self.tokenizer`,所以必须设置它。
实际上,在Trainer中,如果我们传递了`tokenizer`,它会将其存储为`tokenizer`属性,这是官方支持的。所以警告可能是误报,或者是因为我们使用的transformers版本问题。我们可以尝试更新transformers库,或者忽略这个警告。
但是,为了确保代码的健壮性,我们可以查看一下当前transformers库中Trainer的源码。如果确实有变化,我们可以调整。不过,根据错误信息,它只是说在版本5.0.0中会移除,所以暂时可以忽略。
因此,我们主要解决错误问题:在`TrainingArguments`中设置`remove_unused_columns=False`。
修改代码:
在`TrainingArguments`中添加`remove_unused_columns=False`。
修改后的`TrainingArguments`初始化:
```python
training_args = TrainingArguments(
...,
remove_unused_columns=False, # 添加这一行
...
)
```
这样,Trainer就不会自动移除数据集中不匹配的列,我们的自定义列(三元组)就会保留,从而在`compute_loss`方法中被使用。
同时,我们还需要注意:在自定义训练器的`compute_loss`方法中,我们使用了一些特定的键(如`anchor_input_ids`等),所以数据集必须包含这些键。
修改后的代码将解决这个错误。
但是,我们还需要注意另一个问题:在`ContrastiveDataCollator`中,我们期望每个样本是一个字典,包含`anchor_input_ids`、`positive_input_ids`和`negative_input_ids`。因此,在数据集中,每个样本必须包含这些字段。
因此,我们修改主程序中的`TrainingArguments`,然后重新运行。
另外,注意在`ContrastiveTrainer`的初始化中,我们调用了`super().__init__(*args, **kwargs)`,并且传递了`tokenizer`参数,这是Trainer所支持的,所以警告可以暂时忽略。如果未来版本移除,我们可以通过其他方式设置tokenizer(例如,通过数据收集器)。
现在,我们修改代码并重新运行。
修改后的完整代码(仅修改TrainingArguments部分):</think>### 问题分析
错误信息表明 Hugging Face Trainer 在尝试移除未使用的数据集列时遇到了问题。核心问题在于:
1. **错误原因**:Trainer 默认会移除数据集中与模型前向方法不匹配的列(`remove_unused_columns=True`),但我们的自定义训练器需要 `anchor_input_ids`、`positive_input_ids` 和 `negative_input_ids` 这些特殊列
2. **警告提示**:`tokenizer` 参数在 `ContrastiveTrainer.__init__` 中将被弃用
3. **隐藏状态警告**:模型生成配置需要调整
### 解决方案
需要修改三处代码:
1. **在 TrainingArguments 中禁用列移除**
2. **更新 ContrastiveTrainer 初始化方式**
3. **修复模型生成配置**
以下是修复后的完整代码:
```python
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
Trainer,
PreTrainedTokenizerBase
)
from transformers.tokenization_utils_base import PreTrainedTokenizerBase
from transformers.utils import PaddingStrategy
from datasets import load_dataset
from typing import Any, Dict, List, Optional, Tuple, Union
import logging
from dataclasses import dataclass
# 设置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class ContrastiveDataCollator:
"""数据收集器,用于处理对比学习的三元组数据"""
tokenizer: PreTrainedTokenizerBase
padding: Union[bool, str, PaddingStrategy] = True
max_length: Optional[int] = None
pad_to_multiple_of: Optional[int] = None
return_tensors: str = "pt"
def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, torch.Tensor]:
# 分离出三元组的各个部分
anchor_features = [{"input_ids": f["anchor_input_ids"]} for f in features]
positive_features = [{"input_ids": f["positive_input_ids"]} for f in features]
negative_features = [{"input_ids": f["negative_input_ids"]} for f in features]
# 对每个部分分别进行填充
batch_anchor = self.tokenizer.pad(
anchor_features,
padding=self.padding,
max_length=self.max_length,
pad_to_multiple_of=self.pad_to_multiple_of,
return_tensors=self.return_tensors,
)
batch_positive = self.tokenizer.pad(
positive_features,
padding=self.padding,
max_length=self.max_length,
pad_to_multiple_of=self.pad_to_multiple_of,
return_tensors=self.return_tensors,
)
batch_negative = self.tokenizer.pad(
negative_features,
padding=self.padding,
max_length=self.max_length,
pad_to_multiple_of=self.pad_to_multiple_of,
return_tensors=self.return_tensors,
)
# 创建注意力掩码
def create_attention_mask(input_ids):
return (input_ids != self.tokenizer.pad_token_id).int()
return {
"anchor_input_ids": batch_anchor["input_ids"],
"anchor_attention_mask": create_attention_mask(batch_anchor["input_ids"]),
"positive_input_ids": batch_positive["input_ids"],
"positive_attention_mask": create_attention_mask(batch_positive["input_ids"]),
"negative_input_ids": batch_negative["input_ids"],
"negative_attention_mask": create_attention_mask(batch_negative["input_ids"]),
}
class ContrastiveTrainer(Trainer):
"""自定义训练器,实现对比学习损失"""
def __init__(self, *args, contrastive_config=None, **kwargs):
# 修复:使用新的参数名代替将被弃用的tokenizer参数
if "tokenizer" in kwargs:
kwargs["data_collator"] = kwargs.pop("tokenizer")
super().__init__(*args, **kwargs)
if contrastive_config is None:
contrastive_config = {}
# 设置默认值并验证配置
self.temperature = contrastive_config.get("temperature", 0.07)
self.margin = contrastive_config.get("margin", 0.3)
self.contrastive_weight = contrastive_config.get("weight", 0.8)
self.repr_layer = contrastive_config.get("repr_layer", -1)
# 验证必要参数
if not hasattr(self.model.config, "output_hidden_states") or not self.model.config.output_hidden_states:
raise ValueError("模型必须设置output_hidden_states=True以获取隐藏状态")
self.cross_entropy = nn.CrossEntropyLoss()
def compute_contrastive_loss(self, anchor_emb, pos_emb, neg_emb):
"""计算对比损失:结合InfoNCE和三元组损失"""
# 计算余弦相似度
pos_sim = F.cosine_similarity(anchor_emb, pos_emb)
neg_sim = F.cosine_similarity(anchor_emb, neg_emb)
# InfoNCE损失
numerator = torch.exp(pos_sim / self.temperature)
denominator = numerator + torch.exp(neg_sim / self.temperature)
info_nce_loss = -torch.log(numerator / (denominator + 1e-8)).mean()
# 三元组损失
triplet_loss = F.relu(neg_sim - pos_sim + self.margin).mean()
# 加权组合
return info_nce_loss + triplet_loss
def get_sequence_representation(self, outputs, attention_mask):
"""获取序列表示(取最后一个非填充token的隐藏状态)"""
# 获取指定层的隐藏状态
hidden_states = outputs.hidden_states[self.repr_layer]
# 获取每个序列的最后一个非填充token
seq_lengths = attention_mask.sum(dim=1) - 1
batch_indices = torch.arange(hidden_states.size(0))
return hidden_states[batch_indices, seq_lengths]
def compute_loss(self, model, inputs, return_outputs=False):
"""计算总损失:语言模型损失 + 对比损失"""
# 提取输入
anchor_ids = inputs["anchor_input_ids"]
anchor_mask = inputs["anchor_attention_mask"]
positive_ids = inputs["positive_input_ids"]
positive_mask = inputs["positive_attention_mask"]
negative_ids = inputs["negative_input_ids"]
negative_mask = inputs["negative_attention_mask"]
# 前向传播获取隐藏状态
def get_embeddings(input_ids, attention_mask):
outputs = model(
input_ids=input_ids,
attention_mask=attention_mask,
output_hidden_states=True,
return_dict=True
)
return self.get_sequence_representation(outputs, attention_mask)
# 获取三元组的嵌入表示
anchor_emb = get_embeddings(anchor_ids, anchor_mask)
pos_emb = get_embeddings(positive_ids, positive_mask)
neg_emb = get_embeddings(negative_ids, negative_mask)
# 计算对比损失
cl_loss = self.compute_contrastive_loss(anchor_emb, pos_emb, neg_emb)
cl_loss = cl_loss * self.contrastive_weight
# 计算语言建模损失(仅针对positive回复)
lm_labels = positive_ids.clone()
lm_labels[lm_labels == self.tokenizer.pad_token_id] = -100
lm_outputs = model(
input_ids=positive_ids,
attention_mask=positive_mask,
labels=lm_labels
)
lm_loss = lm_outputs.loss
# 总损失 = LM损失 + 对比损失
total_loss = lm_loss + cl_loss
# 记录自定义指标
self.log({"cl_loss": cl_loss.item(), "lm_loss": lm_loss.item()})
return (total_loss, lm_outputs) if return_outputs else total_loss
# ================ 主程序 ================ #
if __name__ == "__main__":
# 加载模型和分词器
model = AutoModelForCausalLM.from_pretrained(
"model/Qwen/Qwen1.5-1.8B",
output_hidden_states=True, # 必须设置以获取隐藏状态
return_dict_in_generate=True # 修复隐藏状态警告
)
tokenizer = AutoTokenizer.from_pretrained("model/Qwen/Qwen1.5-1.8B")
tokenizer.pad_token = tokenizer.eos_token # 设置填充token
# 加载数据集
train_dataset = load_dataset('json', data_files='data/processed/train_style_triplets.json')['train']
val_dataset = load_dataset('json', data_files='data/processed/val_style_triplets.json')['train']
# 训练参数配置 - 关键修复:禁用列移除
training_args = TrainingArguments(
output_dir="./model/lora_adapter",
per_device_train_batch_size=4,
num_train_epochs=3,
learning_rate=2e-4,
logging_steps=50,
save_steps=500,
fp16=True,
gradient_accumulation_steps=4,
report_to="none", # 禁用默认日志
remove_unused_columns=False, # 关键修复:保留自定义列
)
# 对比学习配置
contrastive_config = {
"temperature": 0.07, # 温度参数
"margin": 0.3, # 三元组损失边界
"weight": 0.8, # 对比损失权重
"repr_layer": -1 # 使用最后一层隐藏状态
}
# 初始化数据收集器
data_collator = ContrastiveDataCollator(
tokenizer=tokenizer,
max_length=512,
padding="max_length"
)
# 初始化训练器 - 修复tokenizer参数警告
trainer = ContrastiveTrainer(
model=model,
args=training_args,
data_collator=data_collator, # 直接传递数据收集器
train_dataset=train_dataset,
eval_dataset=val_dataset,
contrastive_config=contrastive_config
)
# 开始训练
trainer.train()
# 评估模型
eval_results = trainer.evaluate()
print("评估结果:", eval_results)
```
### 关键修复点解释
1. **禁用列移除**:
```python
TrainingArguments(remove_unused_columns=False)
```
- 阻止 Trainer 自动移除三元组列(anchor/positive/negative_input_ids)
- 确保这些列能传递给自定义的 `compute_loss` 方法
2. **ContrastiveTrainer 初始化修复**:
```python
if "tokenizer" in kwargs:
kwargs["data_collator"] = kwargs.pop("tokenizer")
```
- 解决 `tokenizer` 参数弃用警告
- 使用 `data_collator` 参数替代即将弃用的 `tokenizer` 参数
3. **模型生成配置修复**:
```python
model = AutoModelForCausalLM.from_pretrained(
...,
return_dict_in_generate=True # 修复隐藏状态警告
)
```
- 解决 `return_dict_in_generate` 警告
- 确保正确输出隐藏状态
### 其他改进建议
1. **数据集验证**:
```python
# 添加数据集验证
print("训练集样本示例:", train_dataset[0].keys())
print("验证集样本示例:", val_dataset[0].keys())
```
- 确保数据集包含必需的键:`anchor_input_ids`, `positive_input_ids`, `negative_input_ids`
2. **内存优化**:
```python
# 在TrainingArguments中添加
gradient_checkpointing=True,
optim="adafactor",
```
- 使用梯度检查点减少内存占用
- 使用Adafactor优化器替代AdamW
3. **混合精度训练**:
```python
# 如果使用NVIDIA Ampere架构GPU
bf16=True, # 替代fp16
```
- 在支持BF16的GPU上获得更好性能
阅读全文
相关推荐

















