使用Neftune微调asafaya/kanarya-750m时训练损失恒为0的求助
尝试用Neftune微调土耳其语模型asafaya/kanarya-750m时,训练全程损失保持为0,但用完全相同的流程微调ytu-ce-cosmos/turkish-gpt2-large一切正常,怀疑问题出在模型本身,以下是排查和解决思路:
排查步骤与解决方案
1. 手动验证模型前向传播的真实损失值
先跳过SFTTrainer,用单条数据直接测试模型的损失是否真的为0,确认是模型计算逻辑问题还是Trainer的日志显示问题:
import torch from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("asafaya/kanarya-750m") model = AutoModelForCausalLM.from_pretrained("asafaya/kanarya-750m") # 取一条格式化后的训练样本 sample_text = dataset["text"][0] inputs = tokenizer(sample_text, return_tensors="pt") labels = inputs.input_ids.clone() # 前向传播计算损失 outputs = model(**inputs, labels=labels) print(f"手动计算的损失值: {outputs.loss.item()}")
如果手动计算的损失也是0,说明模型本身的损失计算存在问题;如果损失正常,那问题出在SFTTrainer的配置逻辑上。
2. 检查模型标签的忽略设置
部分模型会默认将padding token对应的标签设为-100(损失计算时会忽略),但如果所有标签都被标记为-100,损失会直接为0。可以查看模型的pad_token配置,并检查标签情况:
print(f"模型的pad_token_id: {model.config.pad_token_id}") print(f"样本标签中-100的数量: {(labels == -100).sum().item()}") print(f"样本总token数: {labels.size(1)}")
如果pad_token_id未正确设置,或者标签全部被标记为-100,需要手动修正:
# 确保pad_token正确设置 if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token model.config.pad_token_id = tokenizer.eos_token_id
3. 暂时关闭Neftune验证训练流程
虽然另一个模型适配Neftune正常,但kanarya-750m可能对Neftune的噪声处理存在兼容性问题。修改SFTTrainer配置,关闭Neftune后重新训练:
trainer_2 = SFTTrainer( model=model, train_dataset=dataset, dataset_text_field="text", max_seq_length=512, neftune_noise_alpha=0, # 关闭Neftune packing=False, args = TrainingArguments( per_device_train_batch_size = 1, gradient_accumulation_steps = 2, warmup_steps = 5, max_steps = 80, learning_rate = 2e-4, fp16 = False, bf16 = torch.cuda.is_bf16_supported(), logging_steps = 1, optim = "adamw_8bit", weight_decay = 0.01, lr_scheduler_type = "linear", seed = 3407, output_dir = "outputs", ), ) trainer_2.train()
如果关闭Neftune后损失恢复正常,说明需要调整Neftune的噪声参数(比如将neftune_noise_alpha降低到1-2),或者检查模型Embedding层是否支持Neftune的噪声注入逻辑。
4. 替换为普通Trainer测试训练流程
SFTTrainer对部分非标准GPT架构的模型可能存在适配问题,尝试用普通Trainer手动构建训练流程:
from transformers import Trainer, TrainingArguments def tokenize_function(examples): tokenized = tokenizer(examples["text"], truncation=True, max_length=512, padding="max_length") tokenized["labels"] = tokenized["input_ids"].copy() return tokenized tokenized_dataset = dataset.map(tokenize_function, batched=True) trainer = Trainer( model=model, args=TrainingArguments( per_device_train_batch_size = 1, gradient_accumulation_steps = 2, warmup_steps = 5, max_steps = 80, learning_rate = 2e-4, fp16 = False, bf16 = torch.cuda.is_bf16_supported(), logging_steps = 1, optim = "adamw_8bit", weight_decay = 0.01, lr_scheduler_type = "linear", seed = 3407, output_dir = "outputs", ), train_dataset=tokenized_dataset, ) trainer.train()
如果普通Trainer训练时损失正常,说明问题出在SFTTrainer与kanarya-750m的适配性上,可以考虑用普通Trainer结合手动注入Neftune噪声的方式完成训练。
内容的提问来源于stack exchange,提问作者Zephyrus

