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

如何用AutoModelForSequenceClassification进行NER微调?解决张量形状错误

解决方案:用AutoModelForSequenceClassification适配NER任务

一、先解决输入序列长度报错

你遇到的716 vs 512尺寸不匹配,是因为输入文本长度超过了BERT默认的512最大上下文窗口限制,处理方式有两种:

  • 截断/分段长文本:直接将超过512的文本截断至最大长度;或者分成多个重叠/不重叠的片段(比如每段取512个token,相邻段重叠100个token),确保所有输入序列长度≤512;
  • 修改模型配置(谨慎使用):如果模型允许,修改max_position_embeddings参数并重新初始化位置嵌入,但这种方式会破坏预训练的位置编码效果,仅在必要时尝试。

二、适配SequenceClassification模型到NER任务

AutoModelForSequenceClassification默认只输出[CLS] token的预测结果,而NER需要逐token的分类输出,需从模型结构和前向逻辑两方面改造:

1. 替换分类头

加载模型后,把原本针对[CLS]的序列分类头,改成支持逐token预测的线性层:

from transformers import AutoModelForSequenceClassification, AutoConfig
import torch.nn as nn

# 加载目标模型和配置
model_name = "你的预训练模型名称"
config = AutoConfig.from_pretrained(model_name)
# 修改配置为你的NER标签数量
config.num_labels = len(你的NER标签列表)
# 加载模型(用新配置覆盖原分类头设置)
model = AutoModelForSequenceClassification.from_pretrained(model_name, config=config)

# 替换分类层:原classifier是单维度输出,改为适配所有token的线性层
del model.classifier
model.classifier = nn.Linear(config.hidden_size, config.num_labels)

2. 重写前向传播逻辑

让模型返回所有token的logits,而非仅[CLS]的结果,并适配NER的损失计算:

def custom_forward(input_ids, attention_mask=None, token_type_ids=None, labels=None):
    # 获取BERT主体的所有token隐藏状态
    bert_outputs = model.bert(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
    last_hidden_state = bert_outputs.last_hidden_state  # 形状:(batch_size, seq_len, hidden_size)
    # 生成逐token的预测logits
    logits = model.classifier(last_hidden_state)  # 形状:(batch_size, seq_len, num_labels)

    loss = None
    if labels is not None:
        # 用CrossEntropyLoss计算损失,忽略padding部分的标签(设为-100)
        loss_fct = nn.CrossEntropyLoss(ignore_index=-100)
        loss = loss_fct(logits.view(-1, config.num_labels), labels.view(-1))
    
    return (loss, logits) if loss is not None else logits

# 替换模型默认的forward方法
model.forward = custom_forward

3. 数据处理适配

确保标签序列和输入序列长度对齐,且padding部分的标签设为-100(让损失函数忽略):

import torch
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained(model_name)

def preprocess_ner_data(examples):
    tokenized_inputs = tokenizer(
        examples["text"],
        truncation=True,
        max_length=512,
        padding="max_length",
        return_tensors="pt"
    )
    labels = []
    for i, label in enumerate(examples["labels"]):
        word_ids = tokenized_inputs.word_ids(batch_index=i)
        label_ids = []
        prev_word_idx = None
        for word_idx in word_ids:
            if word_idx is None:
                # padding部分标签设为-100
                label_ids.append(-100)
            elif word_idx != prev_word_idx:
                # 每个词的第一个token用原标签
                label_ids.append(label[word_idx])
            else:
                # 子词处理:IOB格式下可继承父词的I-标签,或设为-100,根据任务调整
                label_ids.append(label[word_idx] if label[word_idx].startswith("I-") else -100)
            prev_word_idx = word_idx
        labels.append(label_ids)
    tokenized_inputs["labels"] = torch.tensor(labels)
    return tokenized_inputs

三、训练注意事项

  • 损失计算必须忽略padding标签,否则会引入无效损失影响训练;
  • 由于该预训练模型原本是针对序列分类任务的,微调NER时可能需要更多训练数据和epochs,抵消预训练任务的偏差;
  • 验证阶段需将子词的预测结果合并回原词,比如取每个词的第一个token的预测作为该词的最终标签。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:33:15