Project : 利用信息抽取技术搭建知识库
本项目的目的是结合命名实体识别、依存语法分析、实体消歧、实体统一对网站开放语料抓取的数据建立小型知识图谱。
Part1:开发句法结构分析工具
1.1 开发工具(15分)
使用CYK算法,根据所提供的:非终结符集合、终结符集合、规则集,对以下句子计算句法结构。
“the boy saw the dog with a telescope"
非终结符集合:N={S, NP, VP, PP, DT, Vi, Vt, NN, IN}
终结符集合:{sleeps, saw, boy, girl, dog, telescope, the, with, in}
规则集: R={
- (1) S-->NP VP 1.0
- (2) VP-->VI 0.3
- (3) VP-->Vt NP 0.4
- (4) VP-->VP PP 0.3
- (5) NP-->DT NN 0.8
- (6) NP-->NP PP 0.2
- (7) PP-->IN NP 1.0
- (8) Vi-->sleeps 1.0
- (9) Vt-->saw 1.0
- (10) NN-->boy 0.1
- (11) NN-->girl 0.1
- (12) NN-->telescope 0.3
- (13) NN-->dog 0.5
- (14) DT-->the 0.5
- (15) DT-->a 0.5
- (16) IN-->with 0.6
- (17) IN-->in 0.4
}
# 分数(15) class my_CYK(object): # TODO: 初始化函数 def __init__(self, non_ternimal, terminal, rules_prob, start_prob): # TODO: 返回句子的句法结构,并以树状结构打印出来 def parse_sentence(self, sentence): def main(sentence): """ 主函数,句法结构分析需要的材料如下 """ non_terminal = {'S', 'NP', 'VP', 'PP', 'DT', 'Vi', 'Vt', 'NN', 'IN'} start_symbol = 'S' terminal = {'sleeps', 'saw', 'boy', 'girl', 'dog', 'telescope', 'the', 'with', 'in'} rules_prob = {'S': {('NP', 'VP'): 1.0}, 'VP': {('Vt', 'NP'): 0.8, ('VP', 'PP'): 0.2}, 'NP': {('DT', 'NN'): 0.8, ('NP', 'PP'): 0.2}, 'PP': {('IN', 'NP'): 1.0}, 'Vi': {('sleeps',): 1.0}, 'Vt': {('saw',): 1.0}, 'NN': {('boy',): 0.1, ('girl',): 0.1,('telescope',): 0.3,('dog',): 0.5}, 'DT': {('the',): 1.0}, 'IN': {('with',): 0.6, ('in',): 0.4}, } cyk = my_CYK(non_terminal, terminal, rules_prob, start_symbol) cyk.parse_sentence(sentence) # TODO: 对该测试用例进行测试 if __name__ == "__main__": sentence = "the boy saw the dog with the telescope" main(sentence)
1.2 计算算法复杂度(3分)
计算上一节开发的算法所对应的时间复杂度和空间复杂度。
# 分数(3) # 上面所写的算法的时间复杂度和空间复杂度分别是多少? # TODO 时间复杂度=O(), 空间复杂度=O()
Part2 : 抽取企业股权交易关系,并建立知识库
2.1 练习实体消歧(15分)
将句中识别的实体与知识库中实体进行匹配,解决实体歧义问题。 可利用上下文本相似度进行识别。
在data/entity_disambiguation目录中,entity_list.csv是50个实体,valid_data.csv是需要消歧的语句。
答案提交在submit目录中,命名为entity_disambiguation_submit.csv。格式为:第一列是需要消歧的语句序号,第二列为多个“实体起始位坐标-实体结束位坐标:实体序号”以“|”分隔的字符串。
*成绩以实体识别准确率以及召回率综合的F1-score
import jieba import pandas as pd # TODO:将entity_list.csv中已知实体的名称导入分词词典 entity_data = pd.read_csv('../data/entity_disambiguation/entity_list.csv', encoding = 'utf-8') # TODO:对每句句子识别并匹配实体 valid_data = pd.read_csv('../data/entity_disambiguation/valid_data.csv', encoding = 'gb18030')
# TODO:将计算结果存入文件 """ 格式为第一列是需要消歧的语句序号,第二列为多个“实体起始位坐标-实体结束位坐标:实体序号”以“|”分隔的字符串。 样例如下: [ [0, '3-6:1008|109-112:1008|187-190:1008'], ... ] """ pd.DataFrame(result_data).to_csv('../submit/entity_disambiguation_submit.csv', index=False)
2.2 实体识别(10分)
借助开源工具,对实体进行识别。
将每句句子中实体识别出,存入实体词典,并用特殊符号替换语句。
# code # 首先尝试利用开源工具分出实体 import fool # foolnltk是基于深度学习的开源分词工具,参考https://github.com/rockyzhengwu/FoolNLTK,也可以使用LTP等开源工具 import pandas as pd from copy import copy sample_data = pd.read_csv('../data/info_extract/train_data.csv', encoding = 'utf-8', header=0) y = sample_data.loc[:,['tag']] train_num = len(sample_data) test_data = pd.read_csv('../data/info_extract/test_data.csv', encoding = 'utf-8', header=0) sample_data = pd.concat([sample_data.loc[:, ['id', 'sentence']], test_data]) sample_data['ner'] = None # TODO: 将提取的实体以合适的方式在‘ner’列中,便于后续使用
2.3 实体统一(15分)
对同一实体具有多个名称的情况进行统一,将多种称谓统一到一个实体上,并体现在实体的属性中(可以给实体建立“别称”属性)
比如:“河北银行股份有限公司”和“河北银行”可以统一成一个实体。
公司名称有其特点,例如后缀可以省略、上市公司的地名可以省略等等。在data/dict目录中提供了几个词典,可供实体统一使用。
- company_suffix.txt是公司的通用后缀词典
- company_business_scope.txt是公司经营范围常用词典
- co_Province_Dim.txt是省份词典
- co_City_Dim.txt是城市词典
- stopwords.txt是可供参考的停用词
import jieba import jieba.posseg as pseg import re import datetime #提示:可以分析公司全称的组成方式,将“地名”、“公司主体部分”、“公司后缀”区分开,并制定一些启发式的规则 # TODO:建立main_extract,当输入公司名,返回会被统一的简称 def main_extract(company_name,stop_word,d_4_delete,d_city_province): """ company_name 输入的公司名 stop_word 停用词 d_city_province 地区 """
# 简单测试实体统一代码 stop_word,d_city_province = my_initial() company_name = "河北银行股份有限公司" # 对公司名提取主体部分,将包含相同主体部分的公司统一为一个实体 company_name = main_extract(company_name,stop_word,d_city_province) print(company_name)
河北银行
# TODO:在实体识别中运用统一实体的功能 import fool import pandas as pd from copy import copy sample_data = pd.read_csv('../data/info_extract/train_data.csv', encoding = 'utf-8', header=0) y = sample_data.loc[:,['tag']] train_num = len(sample_data) test_data = pd.read_csv('../data/info_extract/test_data.csv', encoding = 'utf-8', header=0) sample_data = pd.concat([sample_data.loc[:, ['id', 'sentence']], test_data]) sample_data['ner'] = None # TODO: 将提取的实体以合适的方式在‘ner’列中并统一编号,便于后续使用
2.4 关系抽取(37分)
目标:借助句法分析工具,和实体识别的结果,以及文本特征,基于训练数据抽取关系。
本次要求抽取股权交易关系,关系为有向边,由投资方指向被投资方。
模板建立可以使用“正则表达式”、“实体间距离”、“实体上下文”、“依存句法”等。
答案提交在submit目录中,命名为info_extract_submit.csv和info_extract_entity.csv。
- info_extract_entity.csv格式为:第一列是实体编号,第二列是实体名(多个实体名用“|”分隔)
- info_extract_submit.csv格式为:第一列是一方实体编号,第二列为另一方实体编号。
*成绩以抽取的关系准确率以及召回率综合的F1-score
# 提取文本tf-idf特征 # 去除停用词,并转换成tfidf向量。 # 可以使用LTP分词工具,参考:https://ltp.readthedocs.io/zh_CN/latest/ from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer from pyltp import Segmentor def get_tfidf_feature(): segmentor = Segmentor() # 初始化实例 segmentor.load_with_lexicon('/ltp_data_v3.4.0/cws.model', '../data/user_dict.txt') # 加载模型 return tfidf_feature
提取句法特征
参考特征:
1、企业实体间距离
2、企业实体间句法距离
3、企业实体分别和关键触发词的距离
4、实体的依存关系类别
# -*- coding: utf-8 -*- from pyltp import Parser from pyltp import Segmentor from pyltp import Postagger import networkx as nx import pylab import re # 投资关系关键词 # 提示:可以结合投资关系的触发词建立有效特征 key_words = ["收购","竞拍","转让","扩张","并购","注资","整合","并入","竞购","竞买","支付","收购价","收购价格","承购","购得","购进", "购入","买进","买入","赎买","购销","议购","函购","函售","抛售","售卖","销售","转售"] postagger = Postagger() # 初始化实例 postagger.load_with_lexicon('/ltp_data_v3.4.0/pos.model', '../data/user_dict.txt') # 加载模型 segmentor = Segmentor() # 初始化实例 segmentor.load_with_lexicon('/ltp_data_v3.4.0/cws.model', '../data/user_dict.txt') # 加载模型 parser = Parser() # 初始化实例 parser.load('/ltp_data_v3.4.0/parser.model') # 加载模型 def get_parse_feature(s): """ 对语句进行句法分析,并返回句法结果 parse_result:依存句法解析结果 source:企业实体的词序号 target:另一个企业实体的词序号 keyword_pos:关键词词序号列表 source_dep:企业实体依存句法类型 target_dep:另一个企业实体依存句法类型 ... (可自己建立新特征,提高分类准确率) """ return features # LTP中的依存句法类型如下:['SBV', 'VOB', 'IOB', 'FOB', 'DBL', 'ATT', 'ADV', 'CMP', 'COO', 'POB', 'LAD', 'RAD', 'IS', 'HED']
# 汇总词频特征和句法特征 from sklearn.preprocessing import OneHotEncoder whole_feature = pd.concat([tfidf_feature, parse_feature]) # TODO: 将字符型变量转换为onehot形式 train_x = whole_feature.iloc[:, :train_num] test_x = whole_feature.iloc[:, train_num:]
# 建立分类器进行分类,使用sklearn中的分类器,不限于LR、SVM、决策树等 from sklearn.ensemble import RandomForestClassifier from sklearn import preprocessing from sklearn.model_selection import train_test_split class RF: def __init__(self): def train(self, train_x, train_y): return model def predict(self, model, test_x) return predict, predict_prob rf = RF() model = rf.train(train_x, y) predict, predict_prob = rf.predict(model, test_x) # TODO: 存储提取的投资关系实体对,本次关系抽取不要求确定投资方和被投资方,仅确定实体对具有投资关系即可 """ 以如下形式存储,转为dataframe后写入csv文件: [ [九州通,江中药业股份有限公司], ... ] """
2.5 存储进图数据库(5分)
本次作业我们使用neo4j作为图数据库,neo4j需要java环境,请先配置好环境。
from py2neo import Node, Relationship, Graph graph = Graph( "http://localhost:7474", username="neo4j", password="person" ) for v in relation_list: a = Node('Company', name=v[0]) b = Node('Company', name=v[1]) # 本次不区分投资方和被投资方 r = Relationship(a, 'INVEST', b) s = a | b | r graph.create(s) r = Relationship(b, 'INVEST', a) s = a | b | r graph.create(s)
# TODO:查询某节点的3层投资关系