knowledge_graph_20180107_ѧԱ-checkpoint.ipynb 17.1 KB

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

}

In [1]:
# 分数(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

In [12]:
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是可供参考的停用词
In [14]:
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 地区
    """
In [15]:
# 简单测试实体统一代码
stop_word,d_city_province = my_initial()
company_name = "河北银行股份有限公司"
# 对公司名提取主体部分,将包含相同主体部分的公司统一为一个实体
company_name = main_extract(company_name,stop_word,d_city_province)
print(company_name)
Out [15]:
河北银行
In [17]:
# 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

In [28]:
# 提取文本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、实体的依存关系类别

In [29]:
# -*- 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']
In [31]:
# 汇总词频特征和句法特征
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层投资关系