前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang知识图谱NLP实战第三节——实体三元组关系抽取

golang知识图谱NLP实战第三节——实体三元组关系抽取

作者头像
hotqin888
发布2018-09-11 15:32:27
1.9K0
发布2018-09-11 15:32:27
举报
文章被收录于专栏:hotqin888的专栏hotqin888的专栏

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1334586

参考上两篇文章的开源代码,这里不重复贴出了,感谢他们开源精神。

1.数据结构和背景知识

代码语言:javascript
复制
CONLL标注格式包含10列,分别为:
———————————————————————————
ID   FORM    LEMMA   CPOSTAG POSTAG  FEATS   HEAD    DEPREL  PHEAD   PDEPREL
———————————————————————————


只用到前8列,其含义分别为:

1    ID      当前词在句子中的序号,1开始.
2    FORM    当前词语或标点
3    LEMMA   当前词语(或标点)的原型或词干,在中文中,此列与FORM相同
4    CPOSTAG 当前词语的词性(粗粒度)
5    POSTAG  当前词语的词性(细粒度)
6    FEATS   句法特征,在本次评测中,此列未被使用,全部以下划线代替。
7    HEAD    当前词语的中心词
8    DEPREL  当前词语与中心词的依存关系
代码语言:javascript
复制
1 主谓关系  SBV subject-verb  我送她一束花 (我 <-- 送)
2 动宾关系  VOB 直接宾语,verb-object  我送她一束花 (送 --> 花)
3 间宾关系  IOB 间接宾语,indirect-object  我送她一束花 (送 --> 她)
4 前置宾语  FOB 前置宾语,fronting-object  他什么书都读 (书 <-- 读)
5 兼语  DBL double  他请我吃饭 (请 --> 我)
6 定中关系  ATT attribute 红苹果 (红 <-- 苹果)
7 状中结构  ADV adverbial 非常美丽 (非常 <-- 美丽)
8 动补结构  CMP complement  做完了作业 (做 --> 完)
9 并列关系  COO coordinate  大山和大海 (大山 --> 大海)
10  介宾关系  POB preposition-object  在贸易区内 (在 --> 内)
11  左附加关系 LAD left adjunct  大山和大海 (和 <-- 大海)
12  右附加关系 RAD right adjunct 孩子们 (孩子 --> 们)
13  独立结构  IS  independent structure 两个单句在结构上彼此独立
14  核心关系  HED head  指整个句子的核心

开源的代码都是java的,我要将它们读懂——写出思路——尝试用go编出来

刘小绪的代码思路是:将hanlp输出来的依存句法分析结果转成下列格式的map,称之为dict——为分词结果中的每个分词,配上依存句法分析结果……(这样讲其实也不准确,就是为了方便下面的逻辑分析出三元体)

代码语言:javascript
复制
[{}, 
  {主谓关系=[1 刘小绪 刘小绪 nh  nr  _ 2 主谓关系  _ _], 
  动宾关系=[3 四川  四川  ns  ns  _ 2 动宾关系  _ _]}, 
  {}]

然后用extract函数进行逻辑分析出实体三元组。extract函数输入分词结果,dict和循环i,对于每个dict的一行dic来进行逻辑分析,来瞅一下:

代码语言:javascript
复制
/**
     * @param parser 句法依存分析
     * @param dict 词语依存字典
     * @param i 词语索引
     * @return 三元组列表
     */
    private static Set<String> extract(List<CoNLLWord> parser,
                                       List<Map<String, List<CoNLLWord>>> dict,
                                       int i) {
        CoNLLWord word = parser.get(i);
        Map<String, List<CoNLLWord>> dic = dict.get(i);
        Set<String> result = new HashSet<>();
        // 主谓宾关系:刘小绪生于四川
        if (dic.containsKey("主谓关系") && dic.containsKey("动宾关系")){
            CoNLLWord entity1 = dic.get("主谓关系").get(0);
            // 排除:刘小绪和李华是朋友
            // entity1.ID-1 即主语在依存字典中的索引
            if (dict.get(entity1.ID-1).containsKey("并列关系")){
                String relation = dic.get("动宾关系").get(0).LEMMA;
                CoNLLWord entity2 = dict.get(entity1.ID-1).get("并列关系").get(0);
                result.add(entity1.LEMMA + "," + relation + "," + entity2.LEMMA);
            }else {
                CoNLLWord entity2 = dic.get("动宾关系").get(0);
                String relation = word.LEMMA;
                result.add(entity1.LEMMA + "," + relation + "," + entity2.LEMMA);
            }
        }

        // 动补结构:刘小绪洗干净了衣服
        if (dic.containsKey("动补结构") && dic.containsKey("主谓关系") && dic.containsKey("动宾关系")){
            CoNLLWord entity1 = dic.get("主谓关系").get(0);
            CoNLLWord complement = dic.get("动补结构").get(0);
            CoNLLWord entity2 = dic.get("动宾关系").get(0);
            if (dic.containsKey("右附加关系")){
                CoNLLWord subjoin = dic.get("右附加关系").get(0);
                String relation = word.LEMMA + complement.LEMMA + subjoin.LEMMA;
                result.add(entity1.LEMMA + "," + relation + "," + entity2.LEMMA);
            }else {
                String relation = word.LEMMA + complement.LEMMA;
                result.add(entity1.LEMMA + "," + relation + "," + entity2.LEMMA);
            }
        }

这一段就能得出这样的结果了:

代码语言:javascript
复制
刘小绪,洗,衣服
刘小绪,洗干净了,衣服

我用go进行仿照,因为他用了java语言的map数组,而go似乎没找到map数组,我只好用struct数组。首先将依存句法结果解析成struct(代码中的Hanlp),然后构造类似java的dict(代码中的Dict),然后循环dict,进行逻辑分析,就得到三元组结果了。

代码语言:javascript
复制
package main

import (
	"fmt"
	"strings"
)

type Dict struct {
	SBV []Hanlp
	VOB []Hanlp
	IOB []Hanlp
	FOB []Hanlp
	DBL []Hanlp
	ATT []Hanlp
	ADV []Hanlp
	CMP []Hanlp
	COO []Hanlp
	POB []Hanlp
	LAD []Hanlp
	RAD []Hanlp
	IS  []Hanlp
	HED []Hanlp
}

type Hanlp struct {
	ID      string `json:"id"`
	FORM    string `json:"form"`
	LEMMA   string `json:"lemma"`
	CPOSTAG string `json:"cpostag"`
	POSTAG  string `json:"postag"`
	FEATS   string `json:"feats"`
	HEAD    string `json:"head"`
	DEPREL  string `json:"deprel"`
}

type Ltp2 struct {
	Ltptwo []Ltp1
}

type Ltp1 struct { //这个办法不行!
	Ltpone []Ltp
}

type Ltp struct {
	Id        int64  `json:"id"`
	Cont      string `json:"cont"`
	Pos       string `json:"pos"`
	Ne        string `json:"ne"`
	Parent    int64  `json:"parent"`
	Relate    string `json:"relate"`
	Semparent int64  `json:"semparent"`
	Semrelate string `json:"semrelate"`
	Arg       []Arg1 `json:"arg"`
	Sem       []Sem1 `json:"sem"`
}

type Sem1 struct {
	Id     int64  `json:"id"`
	Parent int64  `json:"parent"`
	Relate string `json:"relate"`
}

type Arg1 struct {
	Id   int64  `json:"id"`
	Type string `json:"type"`
	Beg  int64  `json:"beg"`
	End  int64  `json:"end"`
}

func main() {
	//	jsonHanlpStr := `1 房顶  房顶  n n _ 2 定中关系  _ _
	//2 上 上 nd  f _ 3 状中结构  _ _
	//3 站 站 v v _ 0 核心关系  _ _
	//4 着 着 u u _ 3 右附加关系 _ _
	//5 一 一 m m _ 6 定中关系  _ _
	//6 只 只 q q _ 7 定中关系  _ _
	//7 小鸟  小鸟  n n _ 3 动宾关系  _ _`

	//	jsonHanlpStr := `1 刘海 刘海 nh nr _ 2 主谓关系 _ _
	//2 打扫 打扫 v v _ 0 核心关系 _ _
	//3 房间 房间 n n _ 4 定中关系 _ _
	//4 卫生 卫生 a an _ 2 动宾关系 _ _`

	jsonHanlpStr := `1 刘小绪 刘小绪 nh nr _ 2 主谓关系 _ _
2 洗 洗 v v _ 0 核心关系 _ _
3 干净 干净 a a _ 2 动补结构 _ _
4 了 了 u u _ 2 右附加关系 _ _
5 衣服 衣服 n n _ 2 动宾关系 _ _`

	hanlp := make([]Hanlp, 0)
	aa := make([]Hanlp, 1)
	array := strings.Split(jsonHanlpStr, "\n")
	for _, v := range array {
		array1 := strings.Split(v, " ")
		//for _, w := range array1 {
		aa[0].ID = array1[0]
		aa[0].FORM = array1[1]
		aa[0].LEMMA = array1[2]
		aa[0].CPOSTAG = array1[3]
		aa[0].POSTAG = array1[4]
		aa[0].FEATS = array1[5]
		aa[0].HEAD = array1[6]
		aa[0].DEPREL = array1[7]
		hanlp = append(hanlp, aa...)
	}
	//	fmt.Println(hanlp)
	//刘小绪 --(主谓关系)--> 生于
	//生于 --(核心关系)--> ##核心##
	//四川 --(动宾关系)--> 生于

	//	[{},
	//  {主谓关系=[1 刘小绪 刘小绪 nh  nr  _ 2 主谓关系  _ _],
	//  动宾关系=[3 四川  四川  ns  ns  _ 2 动宾关系  _ _]},
	//  {}]

	for _, w := range hanlp {

		dict := make([]Dict, 1)

		for _, x := range hanlp {
			//找出x.head.lemma
			//hanlp[x.HEAD].LEMMA

			bb := make([]Hanlp, 1)
			for _, i := range hanlp {
				//fmt.Println(i.ID)
				if x.HEAD == i.ID {
					//temp = i.LEMMA
					//temp[0] = i
					bb[0] = i
					//fmt.Println(i)
				}
			}
			if w.LEMMA == bb[0].LEMMA { 

				list1 := make([]Hanlp, 1)
				list1[0] = x

				switch x.DEPREL {
				case "主谓关系":
					dict[0].SBV = list1
				case "动宾关系":
					dict[0].VOB = list1
				case "间宾关系":
					dict[0].IOB = list1
				case "前置宾语":
					dict[0].FOB = list1
				case "兼语":
					dict[0].DBL = list1
				case "定中关系":
					dict[0].ATT = list1
				case "状中结构":
					dict[0].ADV = list1
				case "动补结构":
					dict[0].CMP = list1
				case "并列关系":
					dict[0].COO = list1
				case "介宾关系":
					dict[0].POB = list1
				case "左附加关系":
					dict[0].LAD = list1
				case "右附加关系":
					dict[0].RAD = list1
				case "独立结构":
					dict[0].IS = list1
				case "核心关系":
					dict[0].HED = list1

				}

				bb = make([]Hanlp, 0)

			}
		}
		//		fmt.Println(dict)
		//[]
		//[{[{1 刘海 刘海 nh nr _ 2 主谓关系}] [] [] [] [] [] [] [] [] [] [] [] [] []} {[] [{4 卫生 卫生 a an _ 2 动宾关系}] [] [] [] [] [] [] [] [] [] [] [] []}]
		//[]
		//[{[] [] [] [] [] [{3 房间 房间 n n _ 4 定中关系}] [] [] [] [] [] [] [] []}]

		for _, ww := range dict {

			//主谓宾关系:刘小绪生于四川
			if len(ww.SBV) > 0 && len(ww.VOB) > 0 {

				entity1 := ww.SBV[0].LEMMA
				entity2 := ww.VOB[0].LEMMA
				relation := w.LEMMA
				fmt.Println(entity1 + "," + relation + "," + entity2)

			}
			// 动补结构:刘小绪洗干净了衣服
			//			_, ok = maps["主谓关系"]
			//			_, ok1 = maps["动宾关系"]
			//			_, ok2 := maps["动补结构"]
			if len(ww.SBV) > 0 && len(ww.VOB) > 0 && len(ww.CMP) > 0 {
				entity1 := ww.SBV[0].LEMMA
				complement := ww.CMP[0].LEMMA
				entity2 := ww.VOB[0].LEMMA
				if len(ww.RAD) > 0 { //右附加关系
					subjoin := ww.RAD[0].LEMMA
					relation := w.LEMMA + complement + subjoin
					fmt.Println(entity1 + "," + relation + "," + entity2)
				} else {
					relation := w.LEMMA + complement
					fmt.Println(entity1 + "," + relation + "," + entity2)
				}
			}
		}
}

解析成struct的结果为: 

代码语言:javascript
复制
[{1 刘小绪 刘小绪 nh nr _ 2 主谓关系} {2 洗 洗 v v _ 0 核心关系} {3 干净 干净 a a _ 2 动补结构} {4 了 了 u u _ 2 右附加关系} {5 衣服 衣服 n n _ 2 动宾关系}]

构造的dict是这样子的:

代码语言:javascript
复制
[{[{1 刘小绪 刘小绪 nh nr _ 2 主谓关系}] [{5 衣服 衣服 n n _ 2 动宾关系}] [] [] [] [] [] [{3 干净 干净 a a _ 2 动补结构}] [] [] [] [{4 了 了 u u _ 2 右附加关系}] [] []}]
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年07月30日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.数据结构和背景知识
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档