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

 

 golang知识图谱NLP实战第一节——整体思路

golang知识图谱NLP实战第二节——解析依存句法分析结果

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

 

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

1.数据结构和背景知识

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  当前词语与中心词的依存关系
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——为分词结果中的每个分词,配上依存句法分析结果……(这样讲其实也不准确,就是为了方便下面的逻辑分析出三元体)

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

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

/**
     * @param parser 句法依存分析
     * @param dict 词语依存字典
     * @param i 词语索引
     * @return 三元组列表
     */
    private static Set extract(List parser,
                                       List>> dict,
                                       int i) {
        CoNLLWord word = parser.get(i);
        Map> dic = dict.get(i);
        Set 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);
            }
        }

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

刘小绪,洗,衣服
刘小绪,洗干净了,衣服

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

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的结果为: 

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

构造的dict是这样子的:

[{[{1 刘小绪 刘小绪 nh nr _ 2 主谓关系}] [{5 衣服 衣服 n n _ 2 动宾关系}] [] [] [] [] [] [{3 干净 干净 a a _ 2 动补结构}] [] [] [] [{4 了 了 u u _ 2 右附加关系}] [] []}]

 

你可能感兴趣的:(golang,beego,水利信息化,水利项目管理,水利工程管理,知识图谱,图数据库,人工智能)