维基百科预料下载和处理
我们使用一下项目下载并处理中文维基百科语料得到csv
https://github.com/mattzheng/ChineseWikigithub.com得到的txt我们再用DrQA的document retriever的流程进行下去:
参见:
facebookresearch/DrQAgithub.com用下面的代码转换一下,注意到包含】的就是我们的词条,由于bert的文档长度限制以及让模型跟准确,我这里判断了一个条件只要文档长度大于450我们就算一个文档了,特别是我们不希望我们的回答会从百科后面的段落去选择,前面的段落才是百科的重点部分。
import pandas as pd
df = pd.read_csv(r"wiki.csv")
df.columns = ['context','level']
df = df.drop(df[df['context']=='== 参考文献 ==n'].index)
df = df.drop(df[df['context']=='n'].index)
words = df[df['level']==1]['context']
import json
from tqdm import tqdm
import re
sec = []
# total_sec = []
num_count = filename = 0
for num,data in enumerate(tqdm(zip(df['level'],df['context']))):
level,context = data[0],data[1]
if len(sec) == 0:
if level == 1:
sec = [context]
elif len(sec)!=0:
sec.append(context)
if len(sec)>=4:
# print('yes')
sec_dict = {'id':re.sub('n|】|【|=','',''.join(sec[0]))+'|||'+str(num_count),'text':re.sub('n|】|【|=','',''.join(sec[1:]))}
if len(sec_dict['text'])>450 or level==1:
if level==1:
sec = sec[:-1]
sec_dict = {'id': re.sub('n|】|【|=', '', ''.join(sec[0])) + '|||' + str(num_count),
'text': re.sub('n|】|【|=', '', ''.join(sec[1:]))}
num_count += 1
# total_sec.append(sec_dict)
# if filename>20:
with open(f'data//{filename}.txt','a', encoding='utf-8') as f:
f.write(json.dumps(sec_dict, ensure_ascii=False) + 'n')
# if len(total_sec)==10:
# break
sec = []
if num_count in range(0,1000000000,20000):
filename +=1
else:
pass
然后我们再用DrQA的document retriever的流程先制作一个sqlite数据库,字段就是id和text
python build_db.py /mnt/c/users/nocoo/desktop/chinesewiki-master/data /mnt/c/users/nocoo/desktop/chinesewiki-master/output/output.db
ES引擎搭建
因为drqa用的是英文分词方法,我们需要追踪到分词方法的位置,通过修改源码实现中文分词,这里我使用了结巴,并且把词条名作为我们的词汇表,结巴将不会对这些词分词。
我们通过代码定位drqa的路径
import drqa
print(drqa.__path__)
修改drqa/tokenizers/simple_tokenizer.py,这个是drqa默认的分词方法,添加代码
import jieba
jieba.load_userdict(r'/mnt/c/Users/nocoo/Desktop/ChineseWiki-master/entity.txt')
with open(r'/mnt/d/work/conda-jupyter/stop.txt',encoding = 'utf-8') as f:
stop = f.read().split()
#对每个text
text = list(set([i for i in jieba.cut(text)])-set(stop))
text = ' '.join(text)
matches = [m for m in self._regexp.finditer(text)]
这样我们就不用大改源码,只要增加一个空格假装自己是英文。
然后再构造向量:
python build_tfidf.py /mnt/c/users/nocoo/desktop/chinesewiki-master/output/output.db /mnt/c/users/nocoo/desktop/chinesewiki-master/output
引擎启动测试
python interactive.py --model /mnt/c/users/nocoo/desktop/chinesewiki-master/output/output-tfidf-ngram=2-hash=16777216-tokenizer=simple.npz
引擎测试
process('question answering', k=5)
如果搜索专业名词效果还可以。但是搜索人名往往出现问题不能对应到人名的词条,因为人对应的事件会更频繁出现人名。
下面这个代码让我们可以通过drqa得到应该查询的文档id
from drqa import retriever
ranker = retriever.get_class('tfidf')(tfidf_path=r'/mnt/c/users/nocoo/desktop/chinesewiki-master/output/output-tfidf-ngram=2-hash=16777216-tokenizer=simple.npz')
doc_name= ranker.closest_docs('蜘蛛侠', 1)[0][0]
drqa只需要2G内存,BM25需要4G内存,我们还是用drqa用ES做的搜索吧
这里我增加了一个功能,如果问题中出现了实体,则优先查询实体,如果没有出现实体则使用es文档搜索。方法是使用我们的实体表(每个维基百科的百科词条名),看问题的每个分词是否在实体表中,其中分词我们已经使用了我们的实体表作为分词表了。
系统肯定是慢慢优化的,我们先上线系统吧。
我们需要把代码对接上torchserve的handler
cp -r /mnt/d/work/conda-jupyter/torchserve_package/cmrc2018 /home/nocoolsandwich/work/cmrc2018torchserve/cmrc2018
cd cmrc2018
torch-model-archiver --model-name CMRC2018 --version 1.0 --serialized-file ./checkpoint_score_f1-86.233_em-66.853.pth --handler ./Transformer_handler_generalized.py --extra-files "./setup_config.json,./output-tfidf-ngram=2-hash=16777216-tokenizer=simple.npz,./inference.py,./output.db,./official_tokenization.py,./entity.txt"
mkdir model_store
mv CMRC2018.mar model_store/
torchserve --start --model-store model_store --models my_tc=CMRC2018.mar --no-config-snapshots
模型过大我们要限制一下woker
curl -v -X PUT "http://localhost:8081/models/my_tc?min_worker=1"
测试一下
效果还行吧,接下来我们对接上这个为DrQA前端设计的项目,用的是flask。
cp -r /mnt/c/users/nocoo/desktop/drqa-webui-master /home/nocoolsandwich/work/cmrc2018torchserve/drqa-webui-master
遇到gunicorn 再次无法再次重启的解决方案
gunicorn --timeout 300 index:app
gunicorn index:app --preload
当当~,那么我们的中文DrQA就打造成功。
等周末我再整理整理,先发布文章啦。