基于HuggingFace微调rebel-large模型:仅更新最后一层权重的方法
自定义HuggingFace模型微调:以rebel-large为例
为什么训练和微调代码看起来没区别?
HuggingFace的Trainer API封装了通用训练逻辑,全参数训练与部分参数微调的核心差异不在代码结构,而在参数的可训练性设置——全参数训练时所有模型参数都允许更新,而微调时会冻结预训练主体的大部分参数,只让任务相关层或指定层参与更新。
实现仅更新最后一层权重的两种常见场景
针对rebel-large模型(基于BERT-large扩展的关系抽取模型),分两种需求实现:
场景1:仅训练任务头(关系/实体预测层)
rebel-large的预训练主体是model.bert,任务相关的"最后一层"是实体预测器(model.entity_predictor)和关系预测器(model.relation_predictor)。只需冻结预训练主体,保留任务头参数可训练:
from transformers import RebelModel, RebelTokenizer, Trainer, TrainingArguments # 加载模型和分词器 model_name = "Babelscape/rebel-large" tokenizer = RebelTokenizer.from_pretrained(model_name) model = RebelModel.from_pretrained(model_name) # 冻结预训练BERT主体的所有参数 for param in model.bert.parameters(): param.requires_grad = False # 验证可训练参数(可选) trainable_params = [p for p in model.parameters() if p.requires_grad] print(f"可训练参数总数: {sum(p.numel() for p in trainable_params)}") # 后续训练逻辑(与全参数训练代码结构一致) training_args = TrainingArguments( output_dir="./rebel-finetuned", per_device_train_batch_size=8, num_train_epochs=3, logging_dir='./logs', ) # 假设已处理好自有数据集dataset trainer = Trainer( model=model, args=training_args, train_dataset=dataset["train"], eval_dataset=dataset["eval"], ) trainer.train()
场景2:仅训练BERT最后一层Transformer层+任务头
如果需求是保留BERT的最后一层Transformer层可训练,冻结前面的所有BERT层,代码如下:
# 加载模型和分词器(同上) model_name = "Babelscape/rebel-large" tokenizer = RebelTokenizer.from_pretrained(model_name) model = RebelModel.from_pretrained(model_name) # 冻结BERT前11层(rebel-large的BERT共12层),仅保留最后一层可训练 for layer in model.bert.encoder.layer[:-1]: for param in layer.parameters(): param.requires_grad = False # 任务头默认保持可训练,无需额外设置
自定义微调的核心逻辑
- 拆分模型结构:先通过
print(model)查看模型结构,明确预训练主体与任务相关层的边界 - 冻结参数:通过
param.requires_grad = False标记不需要更新的参数 - 训练执行:
Trainer会自动识别requires_grad=True的参数,仅对这些参数进行梯度更新
内容的提问来源于stack exchange,提问作者Joekr
相关产品推荐
相关产品推荐

