在数据准备阶段,确保您有一个高质量的问答对数据集非常重要。这个数据集应该包含问题和对应的答案,以及答案在上下文中的位置(起始和结束索引)。
预处理数据时,清洗数据、去除无用信息、分词、转换为小写等操作可以帮助提高模型的性能。
在选择模型架构阶段,使用预训练的BERT模型作为基础是一个很好的选择。在BERT模型之上添加一个问答头部,通常是两个线性层,一个用于预测答案的起始位置,另一个用于预测答案的结束位置。
设置训练环境时,选择合适的损失函数和优化器非常重要。交叉熵损失通常用于训练起始和结束位置的预测,而Adam优化器是一种常用的优化器。学习率和学习率调度器也需要进行适当的设置。
在训练模型阶段,使用训练集数据训练模型,并在每个epoch结束后使用验证集评估模型性能。
使用早停来避免过拟合是一个好的做法。
在评估和测试阶段,使用测试集来评估模型性能,并使用评价指标如精确度、召回率、F1分数等来衡量模型的性能。
根据评估结果调整模型架构或超参数,可能需要进行更多的数据清洗或增强。
最后,将训练好的模型部署到生产环境中,以回答实际的问题。
import tensorflow as tf
import tensorflow_hub as hub
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import SparseCategoricalCrossentropy
# 加载预训练的BERT模型
# trainable=True 表示在训练过程中BERT模型的参数也会被更新
bert_layer = hub.KerasLayer("https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3",
trainable=True)
# 构建问答模型
# 定义模型的输入,这里需要三个输入:
# input_word_ids: 输入序列的token ids
# input_mask: 用于区分真实token和填充token的掩码
# segment_ids: 用于区分第一个句子和第二个句子的标记(对于问答任务通常不需要)
input_word_ids = tf.keras.layers.Input(shape=(None,), dtype=tf.int32, name='input_word_ids')
input_mask = tf.keras.layers.Input(shape=(None,), dtype=tf.int32, name='input_mask')
segment_ids = tf.keras.layers.Input(shape=(None,), dtype=tf.int32, name='segment_ids')
# 将输入传递给BERT模型
pooled_output, sequence_output = bert_layer([input_word_ids, input_mask, segment_ids])
# 添加问答头部
# 这里使用两个独立的线性层来预测答案的起始和结束位置
start_logits = tf.keras.layers.Dense(1, name="start_logit", use_bias=False)(sequence_output)
start_logits = tf.keras.layers.Flatten()(start_logits)
end_logits = tf.keras.layers.Dense(1, name="end_logit", use_bias=False)(sequence_output)
end_logits = tf.keras.layers.Flatten()(end_logits)
# 使用softmax激活函数将线性层的输出转换为概率分布
start_probs = tf.keras.layers.Activation(tf.keras.activations.softmax)(start_logits)
end_probs = tf.keras.layers.Activation(tf.keras.activations.softmax)(end_logits)
# 定义最终的模型
model = tf.keras.Model(inputs=[input_word_ids, input_mask, segment_ids], outputs=[start_probs, end_probs])
# 设置训练参数
# 使用Adam优化器和交叉熵损失函数
optimizer = Adam(learning_rate=5e-5)
loss = SparseCategoricalCrossentropy(from_logits=False)
model.compile(optimizer=optimizer, loss=[loss, loss])
# 使用两个损失函数,一个用于起始位置,一个用于结束位置
# 准备数据
# 假设有一个数据集,其中包含编码后的输入ids、注意力掩码、起始位置和结束位置
# 这里没有提供数据集的具体实现,因为它将依赖于您的数据格式和预处理步骤
train_dataset = ... # 包含编码后的输入ids、注意力掩码、起始位置和结束位置
# 训练模型
# 使用fit方法进行训练,指定训练集、训练轮数和批处理大小
model.fit(train_dataset, epochs=3, batch_size=8)
# 保存模型
# 训练完成后,保存模型以便将来使用或部署
model.save('./my_qa_model')
在这个例子中,我们使用TensorFlow和TensorFlow Hub来构建和训练一个问答模型。我们从TensorFlow Hub加载了一个预训练的BERT模型,并在其基础上添加了一个简单的问答头部,该头部由两个线性层组成,用于预测答案的起始和结束位置。
然后,我们编译模型,指定优化器和损失函数,并在准备好的数据集上进行训练。最后,我们保存了训练好的模型。
这个例子假设您已经有了一个适当格式的训练数据集,其中包含了编码后的输入ids、注意力掩码、以及答案的起始和结束位置。
在实际应用中,需要根据自己的数据集来实现数据加载和预处理的逻辑。建议参考下面的公开数据集。
问答(QA)模型通常使用的数据集包含了一系列的问题、上下文(问题的答案所在的文本段落)以及答案在上下文中的确切位置。一个典型的QA数据集条目可能包括以下字段:
context : 一个文本段落,其中包含了一个或多个问题的答案。
question : 针对上下文的一个问题。
answer : 上下文中的一个文本片段,是对问题的答案。
start_position : 答案在上下文中的起始字符索引。
end_position : 答案在上下文中的结束字符索引。
以下是一个简化的QA数据集的模拟例子:
[
{
"context": "TensorFlow is an open source software library for high performance numerical computation.",
"question": "What is TensorFlow?",
"answer": "an open source software library for high performance numerical computation",
"start_position": 13,
"end_position": 80
},
{
"context": "Paris is the capital and most populous city of France.",
"question": "What is the capital of France?",
"answer": "Paris",
"start_position": 0,
"end_position": 5
},
{
"context": "The mitochondrion is the powerhouse of the cell.",
"question": "What is referred to as the powerhouse of the cell?",
"answer": "The mitochondrion",
"start_position": 0,
"end_position": 17
}
]
在实际的训练过程中,您需要将这些数据转换为模型可以理解的格式。这通常涉及以下步骤:
使用BERT的tokenizer将 context 和 question 文本转换为token ids。
创建 attention_mask 以区分真实的token和填充的token。
创建 token_type_ids (也称为 segment_ids )以区分问题和上下文。
将答案的 start_position 和 end_position 转换为token级别的索引。
Hugging Face的Transformers库来准备数据的示例代码:
from transformers import BertTokenizer
# 初始化tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 模拟的数据集
dataset = [
{
"context": "TensorFlow is an open source software library for high performance numerical computation.",
"question": "What is TensorFlow?",
"answer": "an open source software library for high performance numerical computation",
"start_position": 13,
"end_position": 80
},
# ... 其他数据条目
]
# 准备模型输入
inputs = []
for item in dataset:
# 对context和question进行编码
encoded = tokenizer.encode_plus(
item['question'],
item['context'],
max_length=512,
truncation=True,
padding='max_length',
return_tensors='tf'
)
# 找到答案的token级别的start和end位置
start_position =
encoded.char_to_token(item['start_position'])
end_position = encoded.char_to_token(item['end_position'] - 1) # -1因为end_position是包含的
# 如果答案的某部分被截断了,那么我们需要处理这种情况
if start_position is None or end_position is None:
start_position = tokenizer.model_max_length
end_position = tokenizer.model_max_length
inputs.append({
'input_ids': encoded['input_ids'],
'attention_mask': encoded['attention_mask'],
'token_type_ids': encoded['token_type_ids'],
'start_position': start_position,
'end_position': end_position
})
# 将输入转换为TensorFlow的Dataset对象
def map_example_to_dict(input_ids, attention_mask, token_type_ids, start_position, end_position):
return {
"input_word_ids": input_ids,
"input_mask": attention_mask,
"segment_ids": token_type_ids,
}, {
"start_positions": start_position,
"end_positions": end_position
}
# 创建tf.data.Dataset
train_data = tf.data.Dataset.from_generator(
lambda: inputs,
output_types=({'input_ids': tf.int32, 'attention_mask': tf.int32, 'token_type_ids': tf.int32,
'start_position': tf.int32, 'end_position': tf.int32}),
output_shapes=({'input_ids': (None,), 'attention_mask': (None,), 'token_type_ids': (None,),
'start_position': (), 'end_position': ()})
)
train_data = train_data.map(
lambda x: map_example_to_dict(x['input_ids'], x['attention_mask'], x['token_type_ids'],
x['start_position'], x['end_position'])
)
在这个例子中,首先使用tokenizer将文本转换为模型可以理解的格式,然后创建一个 tf.data.Dataset 对象,它可以被用来训练模型。
注意,需要处理可能由于截断而导致答案不完整的情况。
在这种情况下,将答案的位置设置为最大长度,这意味着答案不在输入中。
在实际训练时,需要确保这种情况得到妥善处理。
SQuAD (Stanford Question Answering Dataset): 由斯坦福大学发布的英文问答数据集,包含10万个问题-答案对,这些问题是基于维基百科文章提出的。
CoQA (Conversational Question Answering Challenge): 一个面向会话式问答的数据集,包含12万个问题-答案对,这些对话是基于多种文本来源构建的。
MS MARCO (Microsoft Machine Reading Comprehension): 微软发布的大规模问答数据集,旨在提供真实世界的机器阅读理解训练数据。
Natural Questions (NQ): 由谷歌发布的问答数据集,包含自然提出的问题和维基百科页面作为上下文的答案。
HotpotQA: 一个问答数据集,要求模型进行多跳推理,即从多个文档中收集信息以回答问题。
TriviaQA: 包含大量的问答对,这些问题是基于Trivia知识提出的,答案需要从相关的文档中找到。
DuReader: 是一个中文问答数据集,由百度发布,包含了大量的真实用户提出的问题和长文本答案。
CMRC (Chinese Machine Reading Comprehension): 中文阅读理解数据集,包含了大量的问答对,旨在评估机器对中文文本的理解能力。
XQuAD (Cross-lingual Question Answering Dataset): 一个跨语言问答数据集,包含多种语言的翻译版本,旨在评估跨语言问答模型的性能。
MLQA (Multilingual Question Answering): 一个多语言问答数据集,支持问答任务的跨语言评估。
1. **OMGEval**: 这是一个多语言开放式问答数据集,由北京语言大学、清华大学等高校组成的团队共同发布。它支持生成式评估基准测试,适用于基础模型的评价。数据集已在GitHub上开源,可供研究人员和开发者使用。
2. **XQA**: 针对跨语言OpenQA(开放领域问答),这个数据集专门设计来处理问题与文档之间的关系以及对整个文档的理解。它旨在填补现有数据集中对跨语言理解的空白。
3. **百度知道知识类数据集**: 这个数据集包含大约147万个样本,是百度知道平台上的知识类问答对,适合用来训练和测试中文问答系统。
4. **webtext2019zh知识类数据集**: 这个数据集拥有约425万个社区问答对,是一个大规模的知识类数据集,同样适用于中文问答系统的开发。
开放的数据集很多,多留意开源社区,这里提供一种极度方便的方案,那就是直接用谷歌的TensorFlow
TensorFlow Datasets 提供了一个简单的API来下载和准备数据集,使得加载数据变得非常容易。
这些数据集涵盖了多种机器学习任务,包括图像分类、文本分类、翻译、问答等。
import tensorflow_datasets as tfds
# 加载数据集,例如SQuAD
dataset, info = tfds.load('squad', with_info=True)
# dataset是一个tf.data.Dataset对象,可以用于迭代和训练模型
train_dataset = dataset['train']
validation_dataset = dataset['validation']
# 例如,迭代训练数据集中的条目
for example in train_dataset.take(1):
print(example)
TensorFlow Datasets 中的数据集通常已经预处理成了一种标准格式,但可以查看 info 对象来获取数据集的详细信息,包括特征描述、数据集大小、类别标签等。
查看 TensorFlow Datasets 中都有哪些可用的问答数据集,可以访问 TensorFlow Datasets 的官方目录:
https://www.tensorflow.org/datasets/catalog/overview