前面五篇博客主要探究了QG(Question Generation)任务的基本策略、评价指标;描述了我的初步探索;以及给出了数据处理的方法以及训练模型的构建方法;最后针对训练好的模型生成“问答对”:
- 博客1:基本策略
- 博客2:评价指标、初步探索
- 博客3:训练数据和预测数据预处理的部分
- 博客4:模型的构建
- 博客5:生成“问答对”
- 博客6:问答对有效性过滤(基于文本分类任务)
- 博客7:用问题库检索服务
一、过滤有效Q-A pairs
1.1 利用BERT模型评估生成问答对的有效性
在前面步骤中,使用问题生成器生成的系列问题可能存在很多无效的问题。
- 这可能是由训练数据的领域并不能完全覆盖预测数据的领域造成的。
另外,在抽取答案作为模型的输入时,我采取一种极为单纯的策略,即直接抽取实体,将其作为答案,这就导致有些被抽取的实体可能是不适合用于提问的。
归纳无效的Q-A对的基本特征,主要有下面三种:
- 情况一:答非所问。
eg. Question:…的概念是什么?
Answer:第二百 - 情况二:将问题局部语句作为答案。
- 情况三:生成的问题语义不通顺。
eg. Question:诉么时候中断的时间是什么时候?
Answer:从中断、有关程序终结时起
我在这里使用BERT搭建了一个文本分类器,对任意输入的问答对(均由前面NEZHA + UniLM模型生成),预测其是否为有效问题。
输入:
- [CLS] [SEP] [SEP]
输出:
- 0或1(其中1表示有效,0表示无效)
1.2 构造正样本和负样本
对这个文本分类器而言,训练数据中的“正类”和前面模型所用的训练数据完全一致,但是去除掉上下文部分(Q-A对的类别标签都为1)。
而“负类”则需要我自己重新构造。
结合了上文分析的“无效的Q-A对的基本特征”,我最终采取了下面三种方式构造出负类:
- 1)将Question和不同的Answer随机匹配。
- 2)将Question的局部提取出来,作为答案。
- 3)在Question或者答案中插入一些干扰的中文字符。
下面是训练数据基本构造方法:
import json
import random
data = [l["annotations"] for l in json.load(open('./train.json', 'r', encoding='utf-8'))]
all = []
for l in data:
all += l
label_T = 10000
label_F_extr = 5000
label_F_sh = 5000
index = [int(len(all) * random.random()) for i in range(label_T)]
index_1 = [int(len(all) * random.random()) for i in range(label_F_sh + label_F_extr)]
index_2 = [int(len(all) * random.random()) for i in range(label_F_sh)]
str_l_T = []
for i in index:
str1 = '[CLS]' + all[i]['Q'] + '[SEP]' + all[i]['A'].replace('\n', '') + '[SEP]'
str_l_T.append(str1)
str_l_F = []
for i, j in zip(index_1, index_2):
str1 = '[CLS]' + all[i]['Q'] + '[SEP]' + all[j]['A'].replace('\n', '') + '[SEP]'
str_l_F.append(str1)
for i in range(label_F_extr):
m = all[i + label_F_sh]['Q']
str1 = '[CLS]' + m + '[SEP]' + m[:int(