You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用AutoModelForSequenceClassification做多分类时模型无学习效果的排查

问题分析与修复方案

你的模型出现完全不学习、所有样本预测为同一类的情况,主要是训练循环里的几个致命错误导致的,还有标签处理的小细节需要修正,我们逐个拆解:

1. 训练循环开头的无效优化步骤(最关键问题)

你在每个epoch的最开头就执行了:

optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()

这完全是错误的操作!此时你还没有处理任何batch、没有计算任何损失和梯度,就直接更新了模型参数——这不仅是无意义的参数变更,还会彻底打乱优化器和学习率调度器的状态,直接导致模型无法正常学习。必须删掉这三行代码。

2. 标签数据类型错误

你在预处理时把标签转成了普通的torch.Tensor:

encoding['labels'] = torch.Tensor(examples['labels'])

而nn.CrossEntropyLoss要求标签必须是整数类型的LongTensor(因为它要作为类别索引使用),但torch.Tensor默认生成的是FloatTensor。这种类型不匹配不会直接报错,但会导致梯度传递异常,模型无法学到有效信息。你需要改成:

encoding['labels'] = torch.LongTensor(examples['labels'])

3. 损失值的平均计算逻辑错误

你当前计算训练/验证损失的方式是:

training_loss.append(epoch_loss/num_training_steps)
validation_loss.append(epoch_val_loss/num_val_steps)

但num_training_steps是整个训练周期的总batch数(NUM_EPOCHS * len(train_dataloader)),而epoch_loss只是当前一个epoch的总损失。正确的做法是除以当前epoch的batch数量,也就是:

training_loss.append(epoch_loss / len(train_dataloader))
validation_loss.append(epoch_val_loss / len(valid_dataloader))

否则你的损失值会被错误地缩小几十倍(因为你设置了NUM_EPOCHS=30),导致你完全无法正确判断模型的学习状态。

4. 学习率调度器的执行问题

你在每个batch里已经正确执行了lr_scheduler.step(),但之前epoch开头的额外lr_scheduler.step()会导致调度器提前走完所有预设步数,学习率过早降到极低水平,模型无法继续更新。删掉开头的无效步骤后,这个问题就会解决。

另外补充一点:BERT-base模型的常规学习率范围是2e-5到5e-5,你设置的1e-4偏高一丢丢——小数据集可能没问题,但大数据集容易出现训练震荡,你可以尝试降到5e-5看看效果。

5. 关于独热编码的疑问:不需要!

你的任务是多分类(每个样本属于唯一类别),problem_type="multi_class_classification"搭配LabelEncoder生成的整数标签是完全正确的,CrossEntropyLoss就是专门处理这种场景的损失函数。只有当你的任务是**多标签分类(每个样本可以属于多个类别)**时,才需要使用独热编码和BCEWithLogitsLoss。

修正后的核心训练循环

把开头的无效步骤删掉,修正后的循环代码如下:

for epoch in tqdm(range(1, NUM_EPOCHS+1), total = NUM_EPOCHS, position = 0, leave = True):
    epoch_loss = 0
    epoch_val_loss = 0
    
    model.train()
    for batch in tqdm(train_dataloader, total = len(train_dataloader),position = 0, leave = True):
        batch = {k: v.to(device) for k, v in batch.items()}
        outputs = model(**batch)
        
        loss = criterion(outputs.logits, batch['labels'])
        loss.backward()
        
        epoch_loss += loss.item()
        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
    
    model.eval()
    with torch.no_grad():
        for batch in tqdm(valid_dataloader, total = len(valid_dataloader), position = 0, leave = True):
            batch = {k : v.to(device) for k, v in batch.items()}
            outputs = model(**batch)
            
            val_loss = criterion(outputs.logits, batch['labels'])
            epoch_val_loss += val_loss.item()
    
    # 修正损失平均逻辑
    training_loss.append(epoch_loss / len(train_dataloader))
    validation_loss.append(epoch_val_loss / len(valid_dataloader))
    
    pd.DataFrame({'train':training_loss,'valid':validation_loss}).to_csv("SMALL_BERT"+'_loss.csv')
    print(f'Epoch {epoch+0:03}: | Training Loss: {training_loss[-1]:.5f} | Validation Loss: {validation_loss[-1]:.5f}')

额外建议

  • 可以在训练/验证阶段加入准确率计算,比如在验证时统计模型预测类别与真实标签的匹配率,这样能更直观地观察模型是否在学习。
  • 检查一下当前数据集的类别分布,如果某个类别占比极高(比如超过90%),模型可能会倾向于预测这个类别,但你提到小数据集没问题,所以这个可能性较低,不过还是可以确认一下。

内容的提问来源于stack exchange,提问作者gankmeplease

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 16:50:16