使用 fairseq 在 WSC 与 WinoGrande 数据上微调 RoBERTa:任务、损失函数与评估全解析
使用 fairseq 在 WSC 与 WinoGrande 数据上微调 RoBERTa任务、损失函数与评估全解析【免费下载链接】fairseqFacebook AI Research Sequence-to-Sequence Toolkit written in Python.项目地址: https://gitcode.com/gh_mirrors/fa/fairseq导读本文基于 fairseq 仓库中的 WSC 微调指南系统讲解如何在 SuperGLUE 提供的 Winograd Schema ChallengeWSC训练数据上微调 RoBERTa并给出配套的 WinoGrande 二选一场景的高效实现方案。读完本文你将掌握完整的fairseq-train微调命令与超参调优策略、--user-dir自定义任务Task与损失函数Criterion的加载机制、基于 spaCy 的名词短语候选挖掘原理以及用RobertaModel.disambiguate_pronoun()做零代码评估的实战方法。背景Winograd Schema 与候选挖掘式微调WSC 任务要求模型解决代词消歧问题给定一个句子和其中由[]标注的代词模型需要从句子中的候选名词短语里选出代词真正指代的对象。例如The trophy would not fit in the brown suitcase because [it] was too big.正确的答案是 The trophy。这类样本考验的是常识推理而非单纯的句法知识。fairseq 的 WSC 实现采用了一种特殊的微调范式将候选名词短语与代词分别做掩码mask处理比较两者的条件对数概率。其核心思路如下对句子中的每个候选名词短语candidate和代词query分别构造“其余部分完整、目标片段被mask替换”的输入用 RoBERTa 的 masked LM 头预测被掩码片段的平均对数概率若query的对数概率不低于所有candidate则判定模型“猜对了”代词指代对象。这种做法的关键特性是严格 pointwise候选是从单个输入句中独立挖掘出来的不依赖句子之间的交互或两两对比因此可以一次前向完成所有候选的打分。损失函数从 margin loss 到 cross entropy原版 RoBERTa arXiv 论文描述的是 Kocijan et al. (2019) 提出的 margin ranking lossloss -query_lprobs alpha * (cand_lprobs - query_lprobs beta).clamp(min0)fairseq 代码中通过--wsc-margin-alpha默认 1.0与--wsc-margin-beta默认 0.0两个超参数控制该损失详见 wsc_criterion.py 中的get_loss()。但本仓库文档指出官方在开发集上采用单一交叉熵损失项--wsc-cross-entropy获得了更好且更稳健的结果对query与全部挖掘出的candidate的对数概率做F.cross_entropy标签恒为 0即 query 一定正确。这一方案把需要调的超参数降到最少最佳单模型在开发集上达到92.3%准确率而 margin loss 约为 90%。两种损失实现在同一份 wsc_criterion.py 中def get_loss(self, query_lprobs, cand_lprobs): if self.args.wsc_cross_entropy: return F.cross_entropy( torch.cat([query_lprobs, cand_lprobs]).unsqueeze(0), query_lprobs.new([0]).long(), ) else: return ( -query_lprobs self.args.wsc_margin_alpha * (cand_lprobs - query_lprobs self.args.wsc_margin_beta).clamp(min0) ).sum()需要留意的是--wsc-cross-entropy是一个布尔开关actionstore_true不传则走 margin loss 分支训练 WSC 时若使用 margin 版本可通过--wsc-margin-alpha/--wsc-margin-beta调节边界惩罚。Criterion 层面的训练细节从 wsc_criterion.py 的forward()可以读出几个值得注意的实现事实只对正样本计算 loss代码中if label:才累加get_loss(...)负样本query 不是正确答案只参与准确率统计不产生梯度准确率即排序判定pred (query_lprobs cand_lprobs).all().item()即 query 分数必须同时不低于所有候选才算对日志输出支持--save-predictions FILE将每条的id、pred、label写入文件aggregate_logging_outputs()会额外聚合出accuracy指标与训练命令中的--best-checkpoint-metric accuracy相对应。第一步下载 WSC 数据并准备词典按照指南执行以下命令下载 SuperGLUE 的 WSC 数据并把 RoBERTa 使用的 GPT-2 BPE 词典复制到同一目录wget https://dl.fbaipublicfiles.com/glue/superglue/data/v2/WSC.zip unzip WSC.zip # we also need to copy the RoBERTa dictionary into the same directory wget -O WSC/dict.txt https://dl.fbaipublicfiles.com/fairseq/gpt2_bpe/dict.txt为什么需要dict.txt因为 wsc_task.py 中的load_dictionary()会从{args.data}/dict.txt加载词典并额外add_symbol(mask)保证掩码符号一定在词表内setup_task()中也会断言--criterionwsc必须被显式设置。第二步用 fairseq-train 微调 RoBERTa下面的命令在 4 张 GPU 上微调roberta_large。核心是通过--user-dir指向本目录从而动态加载注册在init.py 中的wsctask 与wsccriterionTOTAL_NUM_UPDATES2000 # Total number of training steps. WARMUP_UPDATES250 # Linearly increase LR over this many steps. LR2e-05 # Peak LR for polynomial LR scheduler. MAX_SENTENCES16 # Batch size per GPU. SEED1 # Random seed. ROBERTA_PATH/path/to/roberta/model.pt # we use the --user-dir option to load the task and criterion # from the examples/roberta/wsc directory: FAIRSEQ_PATH/path/to/fairseq FAIRSEQ_USER_DIR${FAIRSEQ_PATH}/examples/roberta/wsc CUDA_VISIBLE_DEVICES0,1,2,3 fairseq-train WSC/ \ --restore-file $ROBERTA_PATH \ --reset-optimizer --reset-dataloader --reset-meters \ --no-epoch-checkpoints --no-last-checkpoints --no-save-optimizer-state \ --best-checkpoint-metric accuracy --maximize-best-checkpoint-metric \ --valid-subset val \ --fp16 --ddp-backend legacy_ddp \ --user-dir $FAIRSEQ_USER_DIR \ --task wsc --criterion wsc --wsc-cross-entropy \ --arch roberta_large --bpe gpt2 --max-positions 512 \ --dropout 0.1 --attention-dropout 0.1 --weight-decay 0.01 \ --optimizer adam --adam-betas (0.9, 0.98) --adam-eps 1e-06 \ --lr-scheduler polynomial_decay --lr $LR \ --warmup-updates $WARMUP_UPDATES --total-num-update $TOTAL_NUM_UPDATES \ --batch-size $MAX_SENTENCES \ --max-update $TOTAL_NUM_UPDATES \ --log-format simple --log-interval 100 \ --seed $SEED关键参数逐项解读参数取值含义--restore-fileRoBERTa 预训练权重路径加载roberta_large预训练模型--reset-optimizer/--reset-dataloader/--reset-meters—丢弃 checkpoint 中记录的优化器状态从零开始微调--no-epoch-checkpoints/--no-last-checkpoints/--no-save-optimizer-state—减少 checkpoint 磁盘占用只保留最优模型--best-checkpoint-metric accuracy --maximize-best-checkpoint-metric—依据验证集准确率选择最优 checkpoint--valid-subset val—使用WSC/val.jsonl作为验证集--fp16 --ddp-backend legacy_ddp—半精度训练 legacy DDP 后端--task wsc --criterion wsc—使用本目录注册的 Task / Criterion--wsc-cross-entropy开关使用交叉熵损失替代 margin loss--arch roberta_large --bpe gpt2 --max-positions 512—模型架构、BPE 类型与最大序列长度--lr-scheduler polynomial_decay --lr 2e-05—多项式衰减学习率峰值 2e-5--warmup-updates 250 --total-num-update 2000—250 步线性 warmup总步数 2000该命令默认按 4 卡训练如果只有 1 张 GPU添加--update-freq4即可用梯度累积达到相同效果batch size 等效为 16×464。超参搜索经验为什么结果方差大指南明确提醒 WSC 微调结果方差很大。官方在 GLUE/SuperGLUE 提交时对以下维度做了网格搜索学习率1e-5, 2e-5, 3e-5batch size16, 32, 64总更新步数500, 1000, 2000, 3000随机种子多个在约 100 次运行中选出表现最好的 7 个模型做集成ensemble。这提示读者复现时应固定随机种子并多次运行取最优而非单次运行后直接比较数字。第三步评估微调后的模型微调完成后可用 Python 脚本加载最优 checkpoint 并逐条跑验证集。注意from examples.roberta.wsc import wsc_utils这行 import 会顺带注册wsctask 与 criterion因为init.py 导入了wsc_task与wsc_criterionfrom fairseq.models.roberta import RobertaModel from examples.roberta.wsc import wsc_utils # also loads WSC task and criterion roberta RobertaModel.from_pretrained(checkpoints, checkpoint_best.pt, WSC/) roberta.cuda() nsamples, ncorrect 0, 0 for sentence, label in wsc_utils.jsonl_iterator(WSC/val.jsonl, evalTrue): pred roberta.disambiguate_pronoun(sentence) nsamples 1 if pred label: ncorrect 1 print(Accuracy: str(ncorrect / float(nsamples))) # Accuracy: 0.9230769230769231评估背后的调用链roberta.disambiguate_pronoun(sentence)由 hub_interface.py 转发给 task 层的同名方法内部走一条完整的推理流水线格式化输入wsc_utils.py 的convert_sentence_to_json()把_query_与[pronoun]标记解析为 JSON 样本构造数据集wsc_task.py 的build_dataset_for_inference()用临时文件按单样本加载数据集打分disambiguate_pronoun()中分别对candidate_tokens和query_tokens做掩码并计算平均对数概率判定若 query 概率不低于所有候选则返回True否则返回得分最高的候选文本由 BPE 解码并strip()得到。值得注意的是评估时的jsonl_iterator(..., evalTrue)会输出带_与[]标记的句子见 wsc_utils.py这正是disambiguate_pronoun期望的输入格式。WinoGrande二选一场景的高效变体WSC 任务中候选数量不定需要逐一挖掘而 WinoGrande 这类数据集始终只有两个候选且恰有一个正确。为此仓库提供了专门的winograndetask 与 criterion实现更高效。它不再用 spaCy 挖掘候选而是直接读取 JSONL 中的option1/option2字段并按answer字段把正确项作为 query、错误项作为 candidate见 wsc_utils.py 的winogrande_jsonl_iterator()。WinoGrande 微调命令TOTAL_NUM_UPDATES23750 # Total number of training steps. WARMUP_UPDATES2375 # Linearly increase LR over this many steps. LR1e-05 # Peak LR for polynomial LR scheduler. MAX_SENTENCES32 # Batch size per GPU. SEED1 # Random seed. ROBERTA_PATH/path/to/roberta/model.pt # we use the --user-dir option to load the task and criterion # from the examples/roberta/wsc directory: FAIRSEQ_PATH/path/to/fairseq FAIRSEQ_USER_DIR${FAIRSEQ_PATH}/examples/roberta/wsc cd fairseq CUDA_VISIBLE_DEVICES0 fairseq-train winogrande_1.0/ \ --restore-file $ROBERTA_PATH \ --reset-optimizer --reset-dataloader --reset-meters \ --no-epoch-checkpoints --no-last-checkpoints --no-save-optimizer-state \ --best-checkpoint-metric accuracy --maximize-best-checkpoint-metric \ --valid-subset val \ --fp16 --ddp-backend legacy_ddp \ --user-dir $FAIRSEQ_USER_DIR \ --task winogrande --criterion winogrande \ --wsc-margin-alpha 5.0 --wsc-margin-beta 0.4 \ --arch roberta_large --bpe gpt2 --max-positions 512 \ --dropout 0.1 --attention-dropout 0.1 --weight-decay 0.01 \ --optimizer adam --adam-betas (0.9, 0.98) --adam-eps 1e-06 \ --lr-scheduler polynomial_decay --lr $LR \ --warmup-updates $WARMUP_UPDATES --total-num-update $TOTAL_NUM_UPDATES \ --batch-size $MAX_SENTENCES \ --max-update $TOTAL_NUM_UPDATES \ --log-format simple --log-interval 100注意与 WSC 命令的两处关键差异损失函数不同这里不传--wsc-cross-entropy而是显式使用 margin loss并设置--wsc-margin-alpha 5.0 --wsc-margin-beta 0.4与 criterion 默认值 1.0 / 0.0 不同需按数据集调参更新步数更长总步数 23750、warmup 2375学习率峰值 1e-5单卡 batch size 32。从源码看wsc_task.py 的WinograndeTask继承自WSCTask但其load_dataset()不再做候选 collate无需 padding 到等长而是用PadDataset分别打包 query 与 candidatewsc_criterion.py 的WinograndeCriterion也简化为向量化地一次性计算所有样本的query_lprobs cand_lprobs预测与整体 loss无需逐样本循环。这就是“对固定二选一子类更高效”的源码级体现。数据格式约定WSC 与 WinoGrande 的 JSONL 字段略有不同这是把数据接入本套代码前必须确认的前提WSCWSC/{split}.jsonl每行包含text与target{ text: ... sentence ..., target: { span1_index: 0, span1_text: trophy, span2_index: 8, span2_text: it }, label: true }WinoGrandewinogrande_1.0/{split}.jsonl每行包含sentence其中用_标记代词位置、option1、option2、answer{ sentence: The trophy did not fit in the brown suitcase because _ was too big., option1: trophy, option2: suitcase, answer: 1 }对 WSC 而言代词在句子中的下标偶尔会与分词结果错位wsc_utils.py 内置了1修正的 hack此外 query 若含换行或末尾带./,会被跳过或裁剪这些细节保证了数据清洗的健壮性。依赖与复现前提spaCy en_core_web_lgWSC 的候选挖掘依赖 spaCy 的noun_chunks与词性标注见 wsc_utils.py 的get_spacy_nlp()评估与训练前需安装该模型sacremosesMosesDetokenizer用于把按空格切分的 tokens 还原为自然文本wsc_utils.pyGPT-2 BPE--bpe gpt2要求词表为 GPT-2 风格即第一步下载的dict.txttask 内部会针对其“前导空格”特性设置leading_spaceTrue见 wsc_task.pyRoBERTa 预训练权重ROBERTA_PATH指向roberta_large的.pt文件微调时通过--restore-file恢复。小结本文完整还原了 fairseq 中 WSC 微调的三步走流程下载数据 →fairseq-train微调 →disambiguate_pronoun评估并剖析了背后的实现原理spaCy 候选挖掘、掩码平均对数概率打分、margin loss 与 cross entropy 两种损失的可切换实现以及 WinoGrande 二选一场景下的向量化高效变体。若需进一步深入可直接阅读 wsc_task.py、wsc_criterion.py 与 wsc_utils.py 三个源码文件以及 RoBERTa 总览文档 了解预训练模型下载与 BPE 配置的完整细节。【免费下载链接】fairseqFacebook AI Research Sequence-to-Sequence Toolkit written in Python.项目地址: https://gitcode.com/gh_mirrors/fa/fairseq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →