Transformer实战(20)——微调Transformer语言模型进行问答任务 - 实践

news/2025/9/30 19:46:41/文章来源:https://www.cnblogs.com/tlnshuju/p/19121702

Transformer实战(20)——微调Transformer语言模型进行问答任务 - 实践

2025-09-30 19:43  tlnshuju  阅读(0)  评论(0)    收藏  举报

Transformer实战(20)——微调Transformer语言模型进行问答任务

    • 0. 前言
    • 1. 问答任务
    • 2. SQuAD 数据集
    • 3. 数据集加载与处理
    • 4. 模型微调
    • 5. 多任务问答
    • 小结
    • 系列链接

0. 前言

问答 (Question Answering, QA) 是一种自然语言处理 (Natural Language Processing, NPL) 任务,其目标是在给定上下文文本的前提下,自动定位并生成对用户提问的准确回答。与视觉问答 (Visual Question Answering, VQA) 需要结合图像信息不同,纯文本 QA 完全依赖于文本上下文。本文将使用 SQuAD v2 数据集,详细讲解如何使用 DistilBERT 完成从数据预处理、模型微调,到模型保存的完整流程。

1. 问答任务

问答任务通常定义为一种自然语言处理 (Natural Language Processing, NPL) 问题:给定一段文本和一个问题,从中找到答案。通常,答案可以从原始文本中找到,解决这一问题的方法多种多样。在视觉问答 (Visual Question Answering, VQA) 中,问题是关于视觉实体或视觉概念的,而不是文本,但问题本身仍以文本形式呈现。下图是一些 VQA 的示例:

VQA
大多数用于 VQA 的模型都是多模态模型,能够理解视觉上下文并结合问题生成合适的答案。而单模态的纯文本 QA 仅基于文本上下文和文本问题,并生成相应的文本答案。

2. SQuAD 数据集

SQuAD (Stanford Question Answering Dataset) 是问答领域中最经典的数据集之一。查看 SQUAD 的示例并进行分析:

from pprint import pprint
from datasets import load_dataset
squad = load_dataset("squad")
for item in squad["train"][1].items():
print(item[0])
pprint(item[1])
print("="*20)

输出结果如下所示:

输出结果
SQuAD 数据集有一个 SQuAD v2 版本,包含更多的训练样本。为了全面了解如何训练一个 QA 模型,我们将使用 SQuAD 数据集进行文本问答训练。

3. 数据集加载与处理

(1) 首先,加载 SQuAD v2

from datasets import load_dataset
squad = load_dataset("squad_v2")

(2) 加载 SQuAD 数据集后,查看数据集的详细信息:

print(squad)

输出结果如下所示,可以看到,数据集包含超过 130000 个训练样本和 11000 多个验证样本。

输出结果
(3) 与命名实体识别 (Named Entity Recognition, NER) 任务类似,我们需要对数据进行预处理,以确保数据具有正确的格式,便于模型使用。为此,首先需要加载分词器,由于使用的是预训练模型 distilBERT 并针对 QA 问题进行微调,因此需要使用预训练的分词器:

from transformers import AutoTokenizer
model = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model)

对于 SQuAD 示例,我们需要向模型输入多个文本,一个是问题,一个是上下文。因此,我们需要让分词器将它们并排放置,并用特殊的 [SEP] 词元分隔它们,因为 distilBERT 是基于 BERT 的模型。
在问答问题中,还存在另一个问题,即上下文长度。上下文的长度可能超过模型的输入大小,但我们不能将其截断为模型所接受的大小。对于某些特定的 NLP 任务,我们可以截断输入,但在 QA 中,有可能答案就包含在被截断的部分,我们将采用文档滑动窗口 (document stride) 来解决这个问题。

(4) 使用分词器 tokenizer 处理上下文长度问题:

max_length = 384
doc_stride = 128
example = squad["train"][173]
tokenized_example = tokenizer(
example["question"],
example["context"],
max_length=max_length,
truncation="only_second",
return_overflowing_tokens=True,
stride=doc_stride
)

这里的 stride 与文档滑动窗口 (document stride) 相同,用于返回第二部分(像窗口一样)的滑动步长,而 return_overflowing_tokens 参数则告诉模型是否应返回额外的词元。tokenized_example 的结果不仅仅是单个分词后的输出,而是包含两个输入 ID

len(tokenized_example['input_ids'])
# 2

可以通过运行以下 for 循环查看完整结果:

for input_ids in tokenized_example["input_ids"][:2]:
print(tokenizer.decode(input_ids))
print("-"*50)

运行结果如下所示,可以看到,使用 128 个词元的窗口,剩余的上下文会在第二个输入 ID 的输出中再次出现。

输出结果
另一个问题是结束跨度 (end span),在数据集中并没有给出这个值,而是给出了答案的起始跨度 (start span) 或起始字符。我们可以通过计算答案的长度并将其加到起始跨度上,就能自动得到结束跨度。

(5) 我们已经了解了数据集的相关细节及处理方式,可以将它们组合起来定义预处理函数,函数使用示例作为输入:

def prepare_train_features(examples):

接下来,对示例进行分词:

tokenized_examples = tokenizer(
examples["question" if pad_on_right else "context"],
examples["context" if pad_on_right else "question"],
truncation="only_second" if pad_on_right else "only_first",
max_length=max_length,
stride=doc_stride,
return_overflowing_tokens=True,
return_offsets_mapping=True,
padding="max_length",
)

将特征映射到其示例:

sample_mapping = tokenized_examples.pop("overflow_to_sample_mapping")
offset_mapping = tokenized_examples.pop("offset_mapping")
tokenized_examples["start_positions"] = []
tokenized_examples["end_positions"] = []

对于无法回答的示例,应该将其标记为 CLS,并为每个示例添加起始和结束词元:

for i, offsets in enumerate(offset_mapping):
input_ids = tokenized_examples["input_ids"][i]
cls_index = input_ids.index(tokenizer.cls_token_id)
sequence_ids = tokenized_examples.sequence_ids(i)
sample_index = sample_mapping[i]
answers = examples["answers"][sample_index]
if len(answers["answer_start"]) == 0:
tokenized_examples["start_positions"].append(cls_index)
tokenized_examples["end_positions"].append(cls_index)
else:
start_char = answers["answer_start"][0]
end_char = start_char + len(answers["text"][0])
token_start_index = 0
while sequence_ids[token_start_index] != (1 if pad_on_right else 0):
token_start_index += 1
token_end_index = len(input_ids) - 1
while sequence_ids[token_end_index] != (1 if pad_on_right else 0):
token_end_index -= 1
if not (offsets[token_start_index][0] <= start_char and offsets[token_end_index][1] >= end_char):tokenized_examples["start_positions"].append(cls_index)tokenized_examples["end_positions"].append(cls_index)else:while token_start_index < len(offsets) and offsets[token_start_index][0] <= start_char:token_start_index += 1tokenized_examples["start_positions"].append(token_start_index - 1)while offsets[token_end_index][1] >= end_char:token_end_index -= 1tokenized_examples["end_positions"].append(token_end_index + 1)return tokenized_examples

将这个函数应用到数据集上:

tokenized_datasets = squad.map(prepare_train_features, batched=True, remove_columns=squad["train"].column_names)

4. 模型微调

(1) 加载预训练模型进行微调:

from transformers import AutoModelForQuestionAnswering, TrainingArguments, Trainer
model = AutoModelForQuestionAnswering.from_pretrained(model)

(2) 接下来,创建训练参数:

args = TrainingArguments(
f"test-squad",
evaluation_strategy = "epoch",
learning_rate=2e-5,
per_device_train_batch_size=32,
per_device_eval_batch_size=32,
num_train_epochs=3,
weight_decay=0.01,
)

(3) 如果我们不打算使用数据合并器 (data collator),可以为模型训练器提供一个默认的数据合并器:

from transformers import default_data_collator
data_collator = default_data_collator

(4) 创建训练器 trainer

trainer = Trainer(
model,
args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
)

(5) 调用 trainertrain 方法:

trainer.train()

结果如下所示,可以看到模型经过了 3epoch 的训练,输出了验证和训练数据集的损失 (loss):

输出结果
(6) 使用 save_model() 函数保存模型:

trainer.save_model("distillBERT_SQUAD")

如果想要使用保存的模型或其它经过 QA 训练的模型,Transformers 库提供了一个易于使用的管道 (pipeline)。

(7) 通过使用 pipeline,可以使用任意模型,通过以下代码使用 QA 流水线的模型:

from transformers import pipeline
qa_model = pipeline('question-answering', model='distilbert-base-cased-distilled-squad', tokenizer='distilbert-base-cased')

pipeline 只需要两个输入来准备模型进行使用:模型和分词器,同时还需要提供管道 (pipeline) 类型,在以上代码中为 “question-answering” 即问答类型。

(8) 提供模型所需的输入,即上下文 context 和问题 question

question = squad["validation"][0]["question"]
context = squad["validation"][0]["context"]

查看问题和上下文:

print("Question:")
print(question)
print("Context:")
print(context)

输出结果

(9) 使用模型:

qa_model(question=question, context=context)

输出结果如下所示:

{'score': 0.9889376759529114, 'start': 159, 'end': 165, 'answer': 'France'}

我们已经学习了如何在所选用的数据集上训练问答任务模型,还学习了如何使用管道 (pipeline) 来使用训练好的模型。

5. 多任务问答

我们可以将多种不同的 NLP 任务简化为一个简单的范式——问答 (Question Answering, QA)。例如,对于情感分类任务,我们可以通过基于 QA 的方法来解决,而不是直接将输入分类为不同类别(正面、负面和中立)。我们可以通过以下方式重新定义输入:

Context: "I loved this movie!"
Question: "What best describes the sentiment of this text (Positive,
Negative, Neutral)?"
Answer: "Positive"

通过这种方式,不仅可以处理单个 NLP 任务,还可以将其他 NLP 任务与词元分类器结合使用。例如,可以使用不同的问题来处理不同的 NLP 任务,只需要一组问题及其对应的答案。但在本节场景中,并非所有答案都来自给定的上下文,答案也可以来自问题本身。
另一个例子是使用 QA 来解决代词解析问题。例如,使用以下格式,可以用 QA 来进行代词解析:

Context: "Meysam admired Savas. He was always fascinated about
his work."
Question: "What does He refer to in this text?"
Answer: "Meysam"

小结

在本节中,我们介绍了文本问答系统构建流程,首先加载并分析 SQuAD 数据,采用“文档滑动窗口”策略处理超长上下文,再通过 mapping 计算 answer 的起止 token 索引;随后使用 Transformers 中的 Trainer 结合 AutoModelForQuestionAnsweringDistilBERT 进行微调;训练完成后,保存模型并借助 pipeline("question-answering") 实现快速推理。最后,还介绍了如何将 QA 框架推广到情感分类、代词消解等多种 NLP 任务,实现多任务问答。

系列链接

Transformer实战(1)——词嵌入技术详解
Transformer实战(2)——循环神经网络详解
Transformer实战(3)——从词袋模型到Transformer:NLP技术演进
Transformer实战(4)——从零开始构建Transformer
Transformer实战(5)——Hugging Face环境配置与应用详解
Transformer实战(6)——Transformer模型性能评估
Transformer实战(7)——datasets库核心功能解析
Transformer实战(8)——BERT模型详解与实现
Transformer实战(9)——Transformer分词算法详解
Transformer实战(10)——生成式语言模型 (Generative Language Model, GLM)
Transformer实战(11)——从零开始构建GPT模型
Transformer实战(12)——基于Transformer的文本到文本模型
Transformer实战(13)——从零开始训练GPT-2语言模型
Transformer实战(14)——微调Transformer语言模型用于文本分类
Transformer实战(15)——使用PyTorch微调Transformer语言模型
Transformer实战(16)——微调Transformer语言模型用于多类别文本分类
Transformer实战(17)——微调Transformer语言模型进行多标签文本分类
Transformer实战(18)——微调Transformer语言模型进行回归分析
Transformer实战(19)——微调Transformer语言模型进行词元分类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/923200.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

题解:B4410 [GESP202509 一级] 金字塔

题解:B4410 [GESP202509 一级] 金字塔 题目传送门 题意 给定正整数 \(n\),求从 \(1\) 到 \(n\) 的立方和(\(\sum \limits _{i=1} ^{n} i^2\)) 数据规模与约定 \(1 \le n \le 50\) 算法 tag 模拟(循环结构),数学…

9.30总结

1.今天完善了程序加上了倒计时功能 2.计时功能不能实时播报,明天继续完善

pytorch基本运算-torch.normal()函数输出多维材料时,如何绘制正态分布函数图

pytorch基本运算-torch.normal()函数输出多维材料时,如何绘制正态分布函数图pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fam…

PyTorch 神经网络工具箱全解析:从核心组件到模型实战 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

做商城网站的项目背景电子上网站开发

输入一个字符串&#xff0c;打印出该字符串中字符的所有排列。 你可以以任意顺序返回这个字符串数组&#xff0c;但里面不能有重复元素。 示例: 输入&#xff1a;s "abc" 输出&#xff1a;["abc","acb","bac","bca","…

音乐网站开发结语申泽seo

awk是行处理器: 相比较屏幕处理的优点&#xff0c;在处理庞大文件时不会出现内存溢出或是处理缓慢的问题&#xff0c;通常用来格式化文本信息 awk处理过程: 依次对每一行进行处理&#xff0c;然后输出 awk命令形式: awk [-F|-f|-v] ‘BEGIN{} //{command1; command2} END{}’…

AT_agc035_c [AGC035C] Skolem XOR Tree

感觉每次遇到这种神秘构造都会跪下。 首先如果 \(n\) 为 \(2\) 的正整数次幂,由于第 \(n\) 位为 \(1\) 的只有一个数,显然会跪下。 然后我们通过构造证明除了这种情况都是有解的,分奇数和偶数考虑。 你考虑到一个性…

做网站的的需求文档成都公司网站设计

当我们在使用电脑时&#xff0c;时常可能会遇到各类系统提示的错误信息。"找不到mfc100.dll" 就是这些错误之一&#xff0c;该错误提示会妨碍我们执行一些应用程序或特定代码。为了帮助读者克服这个技术障碍&#xff0c;本篇文章将详尽阐明导致该问题的根本原因&…

网站开发人员职业分析做网站买好域名怎么办

Java 提供了一个操作 Set 、List 和 Map 等集合的工具类 &#xff1a;Collections&#xff0c;该工具类里提供了大量方法对集合元素进行排序、查询和修改等操作 转载于:https://www.cnblogs.com/szj-ang/p/7383027.html

2025.9.30总结 - A

今天上午上的工程实训,学习了一些铁道基础常识,收获颇丰,还体验了驾驶动车,收益良多

Harbor磁盘空间清理指南:如何安全清理半年前的镜像 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

详细介绍:第14章 AI Agent——构建自主智能助理

详细介绍:第14章 AI Agent——构建自主智能助理pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", …

网站建设宣传广告03340 网站建设与管理

响应式设计方法对开发者非常有用&#xff0c;因为它使我们的内容在各种设备上广为传播。不用保留几个独立版本的网站&#xff0c;也可以摒除诸如缩放和流式布局这些方法的弊端。 缩放、流式布局与响应式 这些术语容易造成混淆&#xff0c;设计师常常错误地交替互用。实际上&…

网站建设费用属于业务宣传费吗软件企业

写在前面的话&#xff1a; 总是在灾难发生后&#xff0c;才想起容灾的重要性&#xff1b; 总是在吃过亏后&#xff0c;才记得曾经有人提醒过。 核心军规 1、不在数据库做运算 cpu计算务必移至业务层 2、控制单表数据量 int型不超过1000w&#xff0c;含char则不超过500w&#xf…

Java入门级教程21——Java 缓存技术、RMI远程办法调用、多线程分割大档案

Java入门级教程21——Java 缓存技术、RMI远程办法调用、多线程分割大档案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family:…

如何建设网站 知乎aoc24g2色域

随着信息技术的迅猛发展&#xff0c;计算机和网络已经成为了我们日常生活中不可或缺的工具&#xff0c;用于办公、通信和协作。尽管这些信息系统提高了工作效率&#xff0c;但也引发了一系列与信息安全相关的问题&#xff0c;例如如何有效地保护存储在这些系统中的关键数据&…

PowerToys新工具Light Switch:让Windows自动切换明暗主题

微软PowerToys将新增Light Switch工具,可根据时间自动切换Windows明暗主题。用户可设置地理位置或特定时间,还能选择仅系统、仅应用或两者同时切换主题,提升视觉舒适度。PowerToys:在Windows上根据时间自动切换明暗…

丰县做淘宝网站网站聚合页面怎么做

1005.K次取反后最大化的数组和 给定一个整数数组 A&#xff0c;我们只能用以下方法修改该数组&#xff1a;我们选择某个索引 i 并将 A[i] 替换为 -A[i]&#xff0c;然后总共重复这个过程 K 次。&#xff08;我们可以多次选择同一个索引 i。&#xff09; 以这种方式修改数组后…

icoc.cc是哪个网站域名琼海网站制作

问题&#xff1a; 问题1磁盘满 1.原本是100G的大小&#xff0c;我们实际还没接入真正业务&#xff0c;昨日空间满了&#xff0c;需要帮忙看下是什么原因导致磁盘满的吗 数据库是每天备份一次&#xff0c;是不是备份的太频繁&#xff0c;还是数据量的问题导致&#xff0c;需要…