这是一篇独立的作业文章。
本作业迁移自李宏毅老师 2023 年机器学习春的 HW07,你可以尝试加入 Kaggle “刷榜”(衔接 BERT 和引入 Kaggle 是引入该作业的原因)。原 Colab 链接:ML2023_HW7_Question_Answering | 课程主页:Machine Learning 2023 Spring | 作业 PPT。
可以使用以下任一代码链接完成作业(内容一致)。
代码文件下载:Code
相关文章:微调 LLM:抽取式问答 | BERT 论文精读
请根据提示完成相应的代码修改,从而达到学习的目的。
作业文章不会给出答案,如果在闲暇时间进行学习,预计时间为 2-3 天。
如果理解了每个提示到底需要做什么,那么恭喜你已经完成了学习。
搜索代码文件中的 TODO 可以快速索引到需要修改的地方。
- Simple baseline
- 直接运行样例代码文件
- Medium baseline
- 使用学习率调度器:线性衰减,warmup 等,选择一个即可
- Strong baseline
- 修改模型的预处理过程 (TODO: Preprocessing)
- 尝试替换为其他预训练模型,在 Hugging Face 中进行选择
- Boss baseline
- 修改后处理部分 (TODO: Postprocessing)
- 尝试梯度累积
访问 Kaggle 加入链接,点击右上角的 Late Submission
,然后点击 I Understand and Accept
同意:
样例代码运行后会生成文件:result.csv
,如果想要提交查看分数,再次点击 Late Submission
,上传后点击提交:
你可以在 Submissions
中看到自己的提交成绩:
Note
在评估效果时最好仅参考 Public Score,Private Score 是测试集上的分数,因为比赛已经过了Deadline,所以显示了出来,比赛进行的过程中这个分数是隐藏的。
点击 Leaderboard
你将能够看到曾经参与者的成绩:
学习如何使用 transformers 微调预训练模型,以完成下游抽取式问答任务。
-
任务描述: 通过 fine-tune BERT 模型,使其能够从给定的段落中抽取出问题的具体答案。
-
TODO
- 微调预训练的中文 BERT 模型
- 调整超参数(如
doc_stride
) - 应用线性学习率衰减策略
- 尝试其他预训练模型
- 改进数据的预处理(preprocessing)
- 改进模型的后处理(postprocessing)
-
训练技巧
- 自动混合精度(Automatic Mixed Precision)
- 梯度累积(Gradient Accumulation)
- 集成方法(Ensemble)
-
预计训练时间(在 Tesla T4 上启用自动混合精度时的运行时间):
- Simple baseline:8 分钟
- Medium baseline:8 分钟
- Strong baseline:25 分钟
- Boss baseline:2 小时
- 准确率 (Exact Match): 该指标用于衡量模型的预测答案与真实答案完全一致的比例。
其实不需要手动下载,这里只是给出配置方法。
To use the Kaggle API, sign up for a Kaggle account at https://www.kaggle.com. Then go to the 'Account' tab of your user profile (
https://www.kaggle.com/<username>/account
) and select 'Create API Token'. This will trigger the download ofkaggle.json
, a file containing your API credentials. Place this file in the location~/.kaggle/kaggle.json
(on Windows in the locationC:\Users\<Windows-username>\.kaggle\kaggle.json
- you can check the exact location, sans drive, withecho %HOMEPATH%
). You can define a shell environment variableKAGGLE_CONFIG_DIR
to change this location to$KAGGLE_CONFIG_DIR/kaggle.json
(on Windows it will be%KAGGLE_CONFIG_DIR%\kaggle.json
).
替换<username>为你自己的用户名,https://www.kaggle.com/<username>/account
,然后点击 Create New API Token
,将下载下来的文件放去应该放的位置:
- Mac 和 Linux 放在
~/.kaggle
- Windows 放在
C:\Users\<Windows-username>\.kaggle
pip install kaggle
# 你需要先在 Kaggle -> Account -> Create New API Token 中下载 kaggle.json
# mv kaggle.json ~/.kaggle/kaggle.json
kaggle competitions download -c ml2023spring-hw7
unzip ml2023spring-hw7.zip
- 训练集(DRCD + DRCD-backtrans):包含 15,329 个段落和 26,918 个问题。一个段落可能对应多个问题。
- 开发集(DRCD + DRCD-backtrans):包含 1,255 个段落和 2,863 个问题。用于验证。
- 测试集(DRCD + ODSQA):包含 1,606 个段落和 3,504 个问题。测试集的段落没有提供答案,需要模型进行预测。
所有数据集的格式相同:
id
:问题编号paragraph_id
:段落编号question_text
:问题文本answer_text
:答案文本answer_start
:答案在段落中的起始字符位置answer_end
:答案在段落中的结束字符位置
这里解释一下QA_Dataset,如果觉得太长,可以只查看重点部分。
-
初始化(
__init__
):split
决定数据集的类型(训练、验证或测试)。questions
,tokenized_questions
, 和tokenized_paragraphs
是原问题和 tokenized 后的问题和段落。max_question_len
和max_paragraph_len
分别设定了问题和段落的最大长度。self.doc_stride
:段落的窗口滑动步长(决定每个窗口之间的重叠部分)。Sample code
中将其设置为 150,和max_paragraph_len
一样,意味着窗口之间完全不重叠。
self.max_seq_len
:定义了整个输入序列的最大长度(包含问题和段落)。
-
__getitem__
:- 针对给定的索引
idx
,获取对应问题和段落数据,返回模型需要的输入。 - 训练集:定位答案的起始和结束位置,将包含答案的段落部分截取为一个窗口(中心在答案位置附近)。然后将问题和段落合并为一个输入序列,并进行填充。
- 验证/测试集:将段落分成多个窗口,每个窗口之间的步长由
self.doc_stride
决定,然后将每个窗口作为模型的输入。验证和测试时不需要答案位置,因此只需生成多个窗口作为输入。
- 针对给定的索引
-
填充(
padding
):- 输入序列可能比最大序列长度短,填充部分用 0 表示。对于问题部分和段落部分,
token_type_ids
被用来区分它们(0 表示问题,1 表示段落)。attention_mask
用于标记有效的输入部分,防止模型对填充部分进行注意力计算。
- 输入序列可能比最大序列长度短,填充部分用 0 表示。对于问题部分和段落部分,
self.doc_stride
通过控制窗口之间的滑动步长,确保即使答案位于窗口边缘,模型也能通过多个窗口重叠的方式找到答案。- 训练阶段不需要使用
doc_stride
,因为训练时我们已经知道答案的位置,可以直接截取包含答案的窗口。但在验证和测试阶段,由于模型并不知道答案的位置,doc_stride
保证每个窗口之间有足够的重叠,减少遗漏答案。 - 所以这里存在一个问题,训练过程中模型可能学习到:答案就在中间这一模式。这是我们在 Strong baseline 中需要解决的。
微调(Finetune)和提示(Prompt)之间存在一些差异。上下文学习(In-context Learning)可以让预训练模型在不进行梯度下降的情况下,通过少量示例给出许多下游任务的正确预测。请回答以下问题:
a. Encoder-only 模型(如 BERT 系列)如何在抽取式问答任务中确定答案?
b. Decoder-only 模型(如 GPT 系列)如何在抽取式问答任务中确定答案?
尝试不同的 Prompt 并观察 fine-tuning 和 in-context learning 的区别。
代码所下载的模型是 facebook/xglm-1.7B,实际上你也可以直接去 GPT 或者其他 AI 平台提问,这里的目的是去调整自己的 prompt,从而使模型不经过微调也能获取到正确答案。
对于“老手”来说,完全可以跳过这个问题。
- Prompt 示例 1: "根据文章找出问题的答案:{问题}"。
- Prompt 示例 2: "请阅读文章并回答以下问题:{问题}"。
- Prompt 示例 3: "请根据文章信息回答下列问题:{问题}"。
- 中英对比
- 不同 prompt 对比
样例 prompt:
請從最後一篇的文章中找出最後一個問題的答案:
文章:<文章1 內容>
問題:<問題1 敘述>
答案:<答案1>
...
文章:<文章n 內容>
問題:<問題n 敘述>
答案: