实现spaCy实体标注模型
命名实体识别是指对现实世界中某个对象的名称的识别。与词性标注一样,是自然语言处理的技术基础之一。它的作用主要是通过模型识别出文本中需要的实体,也可以推导出实体之间的关系(实体消歧)。
本文介绍的是运用Python从头训练一个spaCy模型来识别中标公告中中标公司的名字,现通过爬虫爬取了大约200篇中标公告(爬取过程省略),利用人工对其中的150篇训练集公告进行标注中标公司,使用spaCy训练一个实体抽取模型并进行本地保存,再调取训练好的模型对剩余的50篇公告进行测试,检验该模型对中标公司提取的准确率。
1、获取数据和数据清洗
首先,需要对爬取下来的中标公告文件数据进行清洗处理,分别对其进行去重和删除网络格式(比如 ),清洗前后对比如下:
2、标注实体
对于清洗后的数据集,需要把标注后的结果以下例格式进行储存(即文本+实体标注的索引+实体标注类别标签):
TRAIN_DATA = [ ('Who is Shaka Khan?', { 'entities': [(7, 17, 'PERSON')] ###实体标注的索引从0开始17是最后一字符的索引+1 }), ('I like London and Berlin.', { 'entities': [(7, 13, 'LOC'), (18, 24, 'LOC')] }) ]
也就是说,需要找出需要被标注实体的开始索引和结束索引。由于有204篇公告,每篇公告都都需要人工标注,鉴于数据量和寻找索引工作量都很大,所以通过编写Python程序且小组分工后每位成员人工标注中标公司名称,并把结果储存成上述格式。标注代码如下:
import pandas as pd import re def find_company_name(name,text): if re.search(name,text): tup = list(re.search(name,text).span()) tup.append("LOC") tup=tuple(tup) return (text,{'entities':tup}) return False
from IPython.display import clear_output as clear textall= [] data5 = pd.read_csv("home_work_clear.txt",encoding="utf-8",sep="\n",header=None) start=int(input("请输入第几行开始")) end=int(input("请输入第几行结束")) for line in data5[0][start:end]: clear()#清除输出 print(line) while True: panduan="not break" company_name = input("请输入公司名字") if find_company_name(company_name,line): print(find_company_name(company_name,line)) textall.append(find_company_name(company_name,line)) break elif company_name == "break": panduan='break' break else: print("输入公司名字可能有误,重新输入") print if panduan=="break": break f = open("result%d-%d.txt" % (start,end),"w+",encoding="gbk") f.write(str(textall)) f.close()
(1)首先输入需要标注的开始位置和结束位置:
(2)然后输入每份公告的中标公司名称:
(3)最后,把标注后的结果保存到txt中:
上图表示文本中索引121-133为中标公司。
3、划分训练集和测试集
经标注后,一共有204个数据集,其中设定训练集150篇公告,测试集为54篇公告:
4、spaCy模型训练
对于处理好的训练集,输入到spaCy模型中进行训练,并对训练后的模型进行保存,代码如下:
def main(model=None, output_dir = None, n_iter=100): ##参数意义,model:是否存在现有的模型,output_dir:模型存储位置,n_iter迭代次数 """Load the model, set up the pipeline and train the entity recognizer.""" if model is not None: nlp = spacy.load(model) # load existing spaCy model ###这里的作用是对现有的模型进行优化 *非常重要 print("Loaded model '%s'" % model) else: nlp = spacy.blank('zh') # create blank Language class print("Created blank 'zh' model") if 'ner' not in nlp.pipe_names: ner = nlp.create_pipe('ner') nlp.add_pipe('ner', last=True) # otherwise, get it so we can add labels else: ner = nlp.get_pipe('ner') # add labels for _, annotations in TRAIN_DATA: ##添加train data的标签 for ent in annotations.get('entities'): ner.add_label(ent[2]) other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner'] with nlp.disable_pipes(*other_pipes): # 仅训练我们标注的标签,假如没有则会对所有的标签训练, #建议不要对下载的spacy的模型进行训练可能导致下载的语言模型出错,训练一个空白语言模型就好 optimizer = nlp.begin_training() ##模型初始化 for itn in range(n_iter): random.shuffle(TRAIN_DATA) ##训练数据每次迭代打乱顺序 losses = {} ##定义损失函数 for text, annotations in TRAIN_DATA: example = Example.from_dict(nlp.make_doc(text), annotations) ##对数据进行整理成新模型需要的数据 print("example:",example) nlp.update( [example], # batch of annotations drop=0.5, # dropout - make it harder to memorise data sgd=optimizer, # 更新权重 losses=losses) print(losses) # 保存模型 if output_dir is not None: output_dir = Path(output_dir) if not output_dir.exists(): output_dir.mkdir() nlp.to_disk(output_dir) print("Saved model to", output_dir)
对上述训练过程进行计时,由于代码运行时间过久,测试得当训练5个数据集需要花费差不多1分钟的时间:
通过检查发现代码中的迭代次数为100,为了提高速度把100改为了50,训练150个训练集,花费时间为(约47分钟):
并把训练好的模型保存到本地,方便后续测试时可以直接加载已训练好的模型得出预测结果。
5、测试集测试模型
训练完spaCy模型后,导入测试数据进行测试,代码如下:
import spacy def load_model_test(path,text): nlp = spacy.load(path) print("Loading from", path) doc = nlp(text) for i in doc.ents: print(i.text,i.label_)
训练结果如下:
根据上图测试结果来看,总体预测结果良好,能准确找出中标公司名称。但是,也存在少许预测失败的数据,说明该模型还不是非常精确,后续可以在迭代次数和训练数据量两方面进行改进。