知识库 下载(猜题知识库下载)全程干货
上千人已学的问答系统初级案例。
中秋佳节将至按照历来传统,又到了上(chi)才(yue)艺(bing)的时候啦!!!但是……光吃月饼…是不是有点单调?不如…化身气氛组,自己搞个灯谜玩玩?那么,问题来了如何对谜(wen)面(ti)进行推理和查询
从而得到正确答案呢?这篇【基于Bert实现知识库问答】案例也许能帮到你!
赛题名:基于Bert实现知识库问答难度指数:★学习人数:1063人实训类别知识问答》深度学习数据集版权说明本实践使用互联网开源数据集,请勿用于商业用途01问答系统初级教程概述任务描述本教程以基于知识库的问答来讨论问答系统的主题。
知识库问答也叫知识图谱问答,模型结合知识图谱,对输入的问题进行推理和查询从而得到正确答案的一项综合性任务知识图谱问答方法可分为两大类:基于信息检索的方式:不需要生成中间结果,直接得到问题答案,十分简洁,但是对复杂问题的处理能力有限;。
基于语义解析的方式:需要对输入的自然语言问题进行语义解析,再进行推理,具备解决复杂问题的能力本教程选用信息检索的方式进行讨论数据集本教程采用的数据集为开放式问答数据集WikiQAWikiQA使用Bing查询日志作为问题源,每个问题都链接到一个可能有答案的维基百科页面,页面的摘要部分提供了关于这个问题的重要信息,WikiQA使用其中的句子作为问题的候选答案。
数据集中共包括3047个问题和29258个句子数据集可以在公众号后台回复“开放域问答”获取原始数据存在一些问题没有答案,已进行初步清洗,数据存放在data/raw/文件夹下预训练模型本教程使用Bert作为模型,需要提前下载预训练。
模型和词向量以及预训练模型,放入./models/bert-pretrain文件夹中运行环境在Python3.7环境下测试了本教程代码需要的第三方模块和版本包括:pandas=0.23.0numpy=1.16.3。
csv=1.0pytorch=1.2.0tqdm==4.38.0可以使用pip命令安装上述模块并指定版本,pip install pandas=0.23.0 numpy=1.16.3 csv=1.0 torch=1.2.0 tqdm==4.38.0 matplotlib==2.2.2
方法概述本教程旨在介绍如何利用深度学习工具pytorch,利用Bert模型实现知识库问答系统,通过加载数据、预处理数据、构建模型、训练模型、测试用例依次实现一个知识库问答工具,在训练和预处理过程中通过可视化监督训练过程。
说明:本教程使用模型训练速度较慢,因此只训练了较少次数,效果不是最佳,可以自行修改迭代参数、训练设备等进行后续训练。模型流程图
本案例利用Bert模型实现一个基于检索式的知识库问答系统,同时还介绍了问答系统的相关背景知识及实现方式# 使用pip命令安装指定版本工具包# !pip install pandas=0.23.0 numpy=1.16.3 csv=1.0 pytorch=1.2.0 tqdm==4.38.0 matplotlib==2.2.2。
# 统一导入工具包import pandas as pdimport csvimport transformersimport torchfrom transformers import BertPreTrainedModel, BertModel
from torch import nnimport numpy as npimport osfrom tqdm.notebook import tqdmimport matplotlib.pyplot
as pltimport warningswarnings.filterwarnings(ignore)# Kagging debugkagging =True02数据准备WikiQA问答数据集可以用于问答系统的训练。
数据集中存放着问题的文本,每个问题对应的知识库数据,以及对应的答案因此我们要将数据读入内存中,将不同类型的数据分别处理,将数据结构化整体步骤如下: 数据说明 数据加载 数据标准化:向量化、对齐、掩码2.1 数据说明
数据为tsv格式,可以使用pandas加载查看每条数据有6个字段:QuestionID: 问题idQuestion: 问题文本DocumentID: 检索到的作为答案来源的文档IDDocument: 检索到的作为答案来源的文档标题
SentenceID: 对于文档摘要中的每个句子的idSentence: 文档中摘要中的句子label: 判断句子是否是答案的标签pd_table = pd.read_csv(./datasets/raw/WikiQA-train.tsv
,encoding="utf-8",sep = \t)pd_table2.2 数据加载数据结构:从1.1的数据可以看到,本数据集的知识库就是通过问题检索到的文档摘要,而摘要中的每一句话都作为候选答案因此我们可以将问答问题转化为两个句子之间的匹配问题。
为了后续模型的训练,我们将数据加载为这样的三元组如果answer是question的正确答案,则label为1,反之则为0.每一个三元组用一个字典来存储定义
load函数使用csv将文件读入,在csv.reader中指定\t作为分隔符(delimiter),将数据自动分割依次遍历每一行,将数据按照上述数据结构加载defload(filename): result = []。
withopen(filename, r,encoding=utf-8) as csvfile: spamreader = csv.reader(csvfile, delimiter=
\t, quotechar=")next(spamreader, None) # skip the headersfor row in spamreader: res = {}
res[question] = str(row[1]) res[answer] = str(row[5]) res[
label] = int(row[6]) result.append(res)return resulttrain_file = load(./datasets/raw/WikiQA-train.tsv
)valid_file = load(./datasets/raw/WikiQA-dev.tsv)test_file = load(./datasets/raw/WikiQA-test.tsv)print(
len(train_file))print(len(valid_file))print(len(test_file))# Kagging debugif kagging: train_file = train_file[:
int(len(train_file)*0.01)] valid_file = valid_file[:int(len(valid_file)*0.01)] test_file = test_file[:
int(len(test_file)*0.01)] print(len(train_file)) print(len(valid_file)) print(len(test_file))
查看加载的数据train_file[:10]2.3 数据标准化读入数据后,要对数据进行预处理,包括分词、将自然语言转化为one-hot向量,将文本对齐或截断为相同长度等同时由于本教程选用的模型为Bert,因此需要将数据处理为Bert要求的输入形式。
▌2.3.1 补齐由于数据每个批次输入序列长度是不一样的,我们要对输入序列进行对齐定义padding函数,对长度不足的序列,在右边补0,也可以改变pad_token自己定义用于补足的符号defpadding。
(sequence, max_length, pad_token=0):""" Pad sequence at the given max_length length. pad_token specifies which token should be used to pad.
pad_position can be right or left. """ padding_length = max_length - len(sequence)return sequence + [pad_token] * padding_length
▌2.3.2 Bert的标准输入接下来需要将数据处理为Bert的标准输入形式Bert的输入主要由input_ids,input_mask,token_type_ids三部分构成input_ids:Bert的输入通常需要两个句子,句子A前由
[CLS]开始,以[SEP]结束,后面再连接句子B的向量如[CLS] how are glacier caves formed [SEP] A partly submerged glacier cave on Perito Moreno Glacier [SEP]。
之后再将上述句子根据词表转化为one-hot向量,作为Bert的输入input_idsinput_mask: 由于不同批次的数据长度不同,因此会对数据进行补全但补全的信息对于网络是无用的,不应该对模型产生影响,因此通过掩码将其标注出来,便于训练。
token_type_ids:用于标记一个input_ids序列中哪些位置是第一句话,哪些位置是第二句话下面使用transformer中的BertTokenizer进行处理tokenizer需要指定预训练模型位置,读取使用的词表vocab用于文本转换。
使用tokenizer的encode_plus方法,除了可以对文本进行分词外,还可以将输入序列转换为上述的标准形式max_length指定序列的最长长度,如果输入的序列过长会进行截断add_special_tokens。
设为True就可以将句子转化成对应模型的输入形式model_path = ./datasets/models/bert-pretrain/tokenizer = transformers.BertTokenizer.from_pretrained(model_path,do_lower_case=。
True)# max_length = 512 max_length = 64# Kagging debugdevice = torch.device(cpu)# 如果使用gpu,则注释掉上面一行,使用下面的代码
#device = torch.device(cuda:0)deftokenize(data, max_length,tokenizer,device): res = []for triple
in data:# 将问题和答案两句话作为输入,使用[SEP]连接,同时转化为one-hot向量 inputs = tokenizer.encode_plus( triple[
question], triple[answer], add_special_tokens=True, max_length=max_length,
trunction=True# Kagging debug ) input_ids, token_type_ids = inputs[
"input_ids"], inputs["token_type_ids"]# input_mask.现在还没有padding,因此每个位置都是1 attention_mask = [
1] * len(input_ids)# 进行长度补全 input_ids = padding( input_ids, max_length,
pad_token=0 ) attention_mask = padding( attention_mask,
max_length, pad_token=0 ) token_type_ids = padding(
token_type_ids, max_length, pad_token=0 ) label = triple[
label] res.append( (input_ids, attention_mask, token_type_ids, label) )
all_input_ids = torch.tensor([x[0] for x in res], dtype=torch.int64, device=device) all_attention_mask = torch.tensor([x[
1] for x in res], dtype=torch.int64, device=device) all_token_type_ids = torch.tensor([x[2] for
x in res], dtype=torch.int64, device=device) all_labels = torch.tensor([x[3] for x in res], dtype=torch.int64, device=device)
return torch.utils.data.TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels)
查看标准化的数据test = tokenize(test_file,max_length,tokenizer,device)next(iter(test))03模型定义使用pytorch构建模型,步骤如下:
定义构建实例化模型查看模型3.1 模型概述使用Bert来计算两个语句之间的匹配度将问题和答案作为一个序列输入后,使用Bert进行特征提取,之后使用一个全连接层将输出的特征向量转化为一个1维的输出,作为是否匹配的标签。
同时使用交叉熵作为损失函数读取预训练好的模型参数 config = transformers.BertConfig.from_pretrained(model_path)模型定义为了加快训练,可以使用requires_grad冻结bert部分的参数,在Fine-tunning时只训练全连接层的参数。
classBertQA(BertPreTrainedModel):def__init__(self, config):super(BertQA, self).__init__(config) self.num_labels = config.num_labels
self.bert = BertModel(config)# 冻结bert参数,只fine-tuning后面层的参数# for p in self.parameters():
# p.requires_grad = False self.qa_outputs = nn.Linear(config.hidden_size, 2) self.loss_fn = nn.CrossEntropyLoss(reduction=
mean) self.init_weights()defforward(self, input_ids=None, attention_mask=None, token_type_ids=
None, position_ids=None, head_mask=None, inputs_embeds=None, labels=None):# 将数据输入Bert模型,得到Bert提取出的特征
outputs = self.bert(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, position_ids=position_ids, head_mask=head_mask, inputs_embeds=inputs_embeds) # 通过全连接网络,将特征转化为一个二维向量,可以看作标签0和1的得分情况 logits = self.qa_outputs(outputs[0][:, 0, :]).squeeze() # 选择得分大的标签作为预测值 predicted_labels = nn.functional.softmax(logits, dim=-1) # 如果输入数据中含有标准答案,就计算loss值(即训练过程) if labels is not None: loss = self.loss_fn(predicted_labels, labels) return loss, predicted_labels # 否则返回预测值(测试过程) else: return predicted_labels
模型实例化model = BertQA.from_pretrained(model_path,config=config)将模型转移到cpu或gpu上model.to(device)04模型训练定义数据生成器,并实现训练过程。
4.1 数据生成器Dataloader是pytorch的一个可迭代对象,可以加速生成batch数据它需要传入一个DataSet类型的数据集,和一个Sampler定义从数据集中取样本的策略在1.3.2部分处理数据时将标准化的数据使用了。
torch.utils.data.TensorDataset进行封装,因此只需要创建Sampler,就可以创建Dataloaderbatch_size = 8# Datasettrain_dataset = tokenize(train_file,max_length,tokenizer,device)
# 创建Samplertrain_sampler = torch.utils.data.RandomSampler(train_dataset)# 通过Dataset和Sampler创建dataloader
train_dataloader = torch.utils.data.DataLoader(train_dataset, sampler=train_sampler, batch_size=batch_size)
4.2 训练过程Bert模型十分巨大,因此从头开始训练是不现实的,通常采用在大规模数据集上预训练好的模型,并在此基础上进行微调(fine-tunning)的方式进行微调过程,可以冻结bert层的参数,只训练全连接层加速训练,也选择可以对bert层进一步训练。
由于训练较慢,因此只进行少量步骤的训练,在训练100步后中止训练参数epoch_num = 1learning_rate = 1e-5adam_epsilon = 1e-8# 模型路径# save_path = ./datasets/models/
save_path = ./temp/ifnot os.path.exists(save_path): os.makedirs(save_path)定义优化器,使用AdamW优化器使用filter选择模型中未冻结的层,就可以只训练全连接层,而冻结bert层。
optimizer = transformers.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=learning_rate, eps=adam_epsilon)
loss_list = []for epoch inrange(epoch_num): print("Training epoch {}".format(epoch+1)) pbar = tqdm(train_dataloader)
for step, batch inenumerate(pbar):# 仅作演示,提前结束训练if step == 5: torch.save(model.state_dict(),os.path.join(save_path,
best_param.bin)) print("Model Saved") print("Stopped Early")break model.train()
model.zero_grad() inputs = {input_ids: batch[0],attention_mask: batch[1],token_type_ids
: batch[2],labels: batch[3] } outputs = model(**inputs) loss, results = outputs
loss.backward() optimizer.step() loss_list.append(loss.item()) pbar.set_description(
[!]Batch loss{:.3f}.format(loss.item()))训练过程loss的图像plt.plot(loss_list)plt.show()
05模型测试读取模型参数,输入时不输入标签,将得到的值取softmax即为模型预测出的标签,与label比较求准确率同时由于该问题也可以看作信息检索问题,同时可以采用信息检索的指标如MAP、MRRMAP
:Mean Average Precision. MAP是AP的平均值AP指的是在各个召回率上的正确率的平均值MRR:是把标准答案在被评价系统给出结果中的排序取倒数作为它的准确度,再对所有的问题取平均评测指标定义
defAP(output, target): output = torch.tensor(output,dtype=torch.float) target = torch.tensor(target,dtype=torch.
float) _, indexes = torch.sort(output, descending=True) target = target[indexes].round() total =
0.for i inrange(len(output)): index = i+1if target[i]: total += target[:index].sum().item() / index
# 如果没有正确答案,target.sum = 0 出现异常return total/target.sum().item()# 没有正确答案返回 0defMAP(outputs, targets):assert
(len(outputs) == len(targets)) res = []for i inrange(len(outputs)): res.append(AP(outputs[i], targets[i]))
return np.mean(res)defRR(output, target): _, indexes = torch.sort(output, descending=True) best = target[indexes].nonzero().squeeze().
min().item()return1.0/(best+1)defMRR(outputs, targets):assert(len(outputs) == len(targets)) res = []
for i inrange(len(outputs)): res.append(RR(outputs[i], targets[i]))return np.mean(res)加载测试数据test_dataset = tokenize(test_file,max_length,tokenizer,device)
# 创建Samplertest_sampler = torch.utils.data.RandomSampler(test_dataset)# 通过Dataset和Sampler创建dataloader
test_dataloader = torch.utils.data.DataLoader(test_dataset, sampler=test_sampler, batch_size=batch_size)
加载模型参数# model.load_state_dict(torch.load(datasets/models/best_param.bin))model.load_state_dict(torch.load(
temp/best_param.bin))# 由于CPU运算速度较慢,只选取小部分数据作为测试展示tot_right = 0tot = 0y_hats = []y_gold = []# result_file = open(./result/result.txt,w,encoding=utf-8)
result_file = open(./work/result.txt,w,encoding=utf-8)for step, batch inenumerate(tqdm(test_dataloader)):
#检测label是否全是0,即无正确答案,这样的样例无法计算MAP和MRR all_zero = Truefor i in batch[3]:if i!=0: all_zero =
Falsebreakif all_zero == True:continue# 跳过这个样例 model.eval()with torch.no_grad():if step == 2: # 提前停止
break inputs = {input_ids: batch[0],attention_mask: batch[1],token_type_ids: batch[2] }
y_hat = model(**inputs) y_hat = torch.argmax(y_hat, dim=-1) y_wrt_list = [str(int
(yh))+\nfor yh in y_hat.data] result_file.writelines(y_wrt_list) tot_right += (y_hat == batch[
3]).sum() tot += len(batch[3]) y_gold.append(batch[3]) y_hats.append(y_hat)print(
"Accuracy is {}".format(float(tot_right) / tot))print("MAP is {}".format(MAP(y_hats,y_gold)))print("MRR is {}"
.format(MRR(y_hats,y_gold)))result_file.close() 说明 by Kagging因为运行时间较长,本案例采用了debug模式(kagging=Ture)
,所以学习中请重点关注案例的过程,结果优化可通过数据量增加(kagging=False)直接提升06优化数据可数据增强方式,提升数据量模型适当增加网络结构,如在Bert后加LSTM等获取更多语义特征猜你想要
DataFountain官网的实训案例和公众号上发的这种实训案例有什么不同吗?公众号的实训案例是从官网(DataFountain.cn)搬过来的,省略了执行输出的结果部分在官网上,你可以从这个案例直接跳转到赛题页、查看赛题详情
也可以快速跳转到data页面,直接获取或下载数据集还可以一键fork全部代码,直接在线运行,学后快速练习,比单纯在公众号上阅读更利于吸收和提升哦~明白,可以直接挂载数据集练习哈不去官网,可以获取数据集吗?。
后台回复“开放域问答”试试。明白。色鲁特!
No趴笨。
你可能错过的“数据汤”(上下滑动查看更多)回复【openLooKeng】获2020 CCF BDCI 华为赛题「openLooKeng性能优化」数据集回复【昆虫】获图像分类初级教程:基于PyTorch框架实现昆虫分类任务】数据集
回复【AI线下沙龙】获首期圈内人的交流会vivo杨昂博士「无线通信AI」精彩PPT回复【2019成果汇编】获《2019 CCF 大数据与计算智能大赛参赛团队成果汇编》电子版回复【投稿】参与技术干货类内容有偿变现
回复【实训导师】获创作分享激励计划变现路径回复【ccf-didi】获2020 CCF BDCI《路况状态时空预测》一等奖代码方案回复【YZY队】获2020首届无线通信AI大赛-YZY队竞赛获奖方案回复【
统计学习】获机器学习入门宝典《统计学习方法(第二版)》的代码实现文件回复【b站】获B站数据分析资源包回复【NLP入门】获NLP快速入门及提升资料回复【南瓜书】获机器学习入门“笔记”南瓜书PumpkinBook
回复【博士论文】获论文《基于深度学习的自然场景文字检测与识别方法研究》回复【王者荣耀】获王者荣耀英雄数据集及聚类实践代码回复【Word2Vec】获深度学习Word2Vec学习笔记及实战详解回复【公益课第四期
】获《用"规则"解决时间序列问题》课件及代码回复【公益课第三期】获第三期直播baseline模型参数及其他资料回复【公益课第二期】获第二期ppt、代码、Baseline模型等全部资料回复【公益课第一期】获《时序预测从朴素法到ARIMA的最优调参方法》课件及代码
回复【表白代码】获程序员的正确表白姿势回复【吃鸡】获《interesting-python: 有趣的Python爬虫和数据分析小项目》回复【烟花代码】获超炫烟花代码回复【桑基图】获Python绘制桑基图的方法及材料包
回复【走进高校】或【走进课堂】获2019CCF BDCI走进课堂文件回复【DL500】获《深度学习500问》回复【30nlp】获30nlptasks回复【DCIC直播】获2019数字中国大赛总决赛直播回放
回复【吴亦凡】获大众对吴亦凡作品《大碗宽面》的情感倾向分析代码回复【蔡徐坤】获用“大数据扒一扒蔡徐坤的真假流量粉”代码资料回复【北上广深】获北上广深租房图鉴大数据及代码分析资料回复【混凝土】获2019DCIC混凝土泵车砼活塞故障预警赛题baseline
回复【论文下载小程序】获“随心下”论文下载神器回复【pydroid】获整理手机图片的python小应用pydroid3回复【小姐姐】获Python自动下载抖音视频的GitHub源码
你们点点“分享”,给我充点儿电吧~
免责声明:本站所有信息均搜集自互联网,并不代表本站观点,本站不对其真实合法性负责。如有信息侵犯了您的权益,请告知,本站将立刻处理。联系QQ:1640731186