PaddleNLP 实战——信息抽取(InfoExtraction )
[ ⽂章⽬录 ]
1. 信息抽取任务是什么?
在NLP任务中,通常当我们拿到⼀段⽂本时,我们希望机器去理解这段⽂本描述的是什么内容,进⽽完成⼀些特定的任务。例如,现在有这么⼀句话:
今⽇,在玩家们的期待中,王者荣耀终于上架了李⽩的新⽪肤——凤求凰。
这句话中具体描述了什么事件呢?⼈类⼀看就知道,哦,王者出了⼀款李⽩的新⽪肤。
没错,这句话的核⼼就是「王者」「出了」「李⽩」「新⽪肤」。
⾄于「…在玩家们的…」这些⽂字⼤概率直接被读者⾃动给过滤掉了,这就是⼈类天然存在的超强信息提取的能⼒。
扩展:⼈类除了在阅读⽂字上有着超强的信息抽取能⼒,在听觉上同样如此,⼤部分⼈都能在多⼈同时说话的场景下清楚的听取想听的那个⼈说话的内容。同样的,学者们也尝试让机器具备同样的能⼒,Sp
eech Separation的其中⼀个研究⽅向便是如此。为此,我们希望机器也能具备⼈类⼀样的信息抽取能⼒,能够在⼀段⽂字中⾃动抽出⼀组⼀组成对的关键信息。
我们再来回顾⼀下刚才那句话,句⼦中包含两个主要信息:
1. 王者出了⼀款新⽪肤。
2. 这款新⽪肤是李⽩的。
这两个主要信息都可以⽤基本的三元组的结构来表⽰:
1. 王者(主语) - 出了(谓语) - 新⽪肤(宾语)
2. 新⽪肤(主语) - 属于(谓语) - 李⽩(宾语)
不难看出,不管多么复杂的句⼦和多么复杂的关系,最后都是可以被细分为若⼲个三元组,即:
**实体(Entity)**是NLP领域中的常⽤语,⽤于描述某⼀个「东西」,这个「东西」通常⽤于描述⼀种词语类别.
⽐如「游戏」就可以被定义为⼀种实体,举例来说,有如下句⼦:王者荣耀和和平精英都是当下⽐较
流⾏的游戏。
该句⼦中就包含了2个「游戏」实体:王者荣耀(GameEntity1)和和平精英(GameEntity2)都是当下⽐较流⾏的游戏。此外,在信息提取(InfoExtraction)领域中,我们需要将关系(Relation)前后的两个实体稍稍区分⼀下:
关系(Relation)前⾯的实体(Entity1)我们称之为头实体(Subject);
关系(Relation)后⾯的实体(Entity2)我们称之为尾实体(Object);
(Entity ,Relation ,Entity )
12
这就是我们常说的SPO表⽰法。S-头实体,O-尾实体,P-Predicate,即「关系(Relation)」更专业的叫法。
王者荣耀(S)−出了(Predicate)−凤求凰(O)
凤求凰(S)−属于(Predicate)−李⽩(O)
⾄此,我们已经明⽩了什么是信息抽取(抽取句⼦中的SPO结构体),以及什么是SPO结构体(描述实体关系的最⼩单位),那么接下来就开始进⾏实战吧。
2. 基于PaddleNLP的信息抽取任务
这篇⽂章中将选择使⽤PaddleNLP作为辅助来完成信息抽取任务,所⽤的数据集/⽰例代码均能在PaddleNLP官⽹上到,并且很⽅便的为我们提供了运⾏环境,不⽤⾃⼰⼿搭环境、下载数据集。接下来的内容只是在官⽅资料的基础上加上⼀些⾃⼰的理解,有兴趣⼩伙伴可以参考下⾯的官⽅资料:
官⽅视频资料:aistudio.baidu/aistudio/education/lessonvideo/2016982
官⽅⽰例代码:aistudio.baidu/aistudio/projectdetail/3190877?forkThirdPart=1
2.1 训练任务概览
本次实战的数据集来⾃于,是⼀个很全⾯的中⽂开源数据集合,这次我们将选取其中「信息抽取」任务相关的数据集。
我们先从训练数据集中随机挑出⼀条样本来看看:
{
"text":"2013年,哈密地区户籍⼈⼝约58万⼈",
"spo_list":[
{
"predicate":"⼈⼝数量",
"object_type":{"@value":"Number"},
"subject_type":"⾏政区",
"object":{"@value":"58万"},
"subject":"哈密"
}
]
}
分析⼀下这⼀条数据,输⼊的是⼀句话:「2013年,哈密地区户籍⼈⼝约58万⼈」。
我们需要提取出该句⼦中全部的SPO关系(1个,spo_list中表⽰):
哈密(S)−⼈⼝数量(P)−58万(O)
其中,“subject”、“predicate”、"object"分别代表SPO三个值;
“subject_type” 和 "object_type"代表头/尾实体的类型,这个我们将放在后⾯讲。
⾄此我们已经明⽩了数据集中样本长什么样,我们的任务⽬标就是将⼀个句⼦(text)当中所有的SPO结构体(spo_list)给提取出来。2.2 Predicate列表
在明确了提取SPO的任务⽬标后,我们⾸先要做的事情就是对所有可能的Predicate进⾏枚举。
Predicate的本质是定义了两个实体之间的「关系」,因此,我们需要告诉模型,模型需要提取哪些「关系」。
例如,有如下句⼦:
张裕妃,顺天府涿州⼈,⽗张世登,母段⽒
森本龙太郎吸烟这个句⼦中就存在两种实体关系:
1. 张裕妃(S)- ⽗亲(P1)- 张世登(O1)
2. 张裕妃(S)- 母亲(P2)- 段⽒(O2)
其中,⽗亲、母亲就是两种不同的实体关系(即不同的Predicate)。
因此,我们⾸先需要先对数据集进⾏分析,枚举出所有实体之间可能存在的实体关系,并记录下来。这个「实体关系列表」就是项⽬⽂件下的 “predicate2id.json” ⽂件所表达的意思,我们将该⽂件打印出来看看:
{
"O": 0,
"I": 1,
"注册资本": 2,
"作者": 3,
"所属专辑": 4,
"歌⼿": 5,
...
"上映时间_@value": 8,
"上映时间_inArea": 9,
...
}
这个json⽂件中定义了所有可能的实体关系(Predicate),其中O和I和BIO(Begin,Intermediate,Other)标记法中的O、I完全⼀致,这⾥不再赘述,除了O、I以外,剩下的就是数据集中所有可能的实体关系了。
例如,
「作者」就代表了⼀种实体关系:
[+]《道学的病理》是2007年商务印书馆出版的图书,作者是韩东育 《道学的病理》的作者是韩东育
「歌⼿」也代表了⼀种实体关系:
[+] 写历史作业时,林俊杰的《曹操》和周杰伦的《爱在西元前》混搭,绝配 《曹操》的歌⼿是林俊杰
注意,实体关系(Predicate)不⼀定必须出现在原⽂本中。如「作者」出现在了第⼀句中;但「歌⼿」没有出现在第⼆句⽂本中。
这⾥可能有同学注意到了8号&9号实体关系有些奇怪,同样都是「上映时间」为什么后缀不同,⼀个是_@value,⼀个是_inArea,这个我们放到下⼀节讲。
2.3 SPO 列表
在我们了解完实体关系(Predicate)列表后,我们已经明⽩数据集当中有哪些实体关系了,现在我们需要关注数据集当中有哪些实体了。还记得实体是什么吗?
我们之前提到过,实体关系是⽤来连接⾸、尾实体的,因此,这⾥的实体应当枚举数据集中所有存在的实体类型。
那什么是实体类型呢?实体类型的实质就是对所有的实体按类别进⾏归类后,得到的类别标签。
举例来讲,
[+] 杨幂出演了《绣春⼑》⾥的北斋。
[+] 胡歌出演了《琅琊榜》中的梅长苏。
这两句话中,「杨幂」和「胡歌」是两个实体,但这两个实体都对应同⼀种实体类型——演员。
黄美英尼坤实体类型是可以按照⾃⼰的需求来⾃⼰定义的,这取决于要做什么任务。
→→
这很好理解:
边塞诗鉴赏如果我们要做⼀个针对影视作品的信息提取模型,那我们的实体类型就可能是:影视作品、演员、导演…等等。
但如果我们要做⼀个游戏内容的提取模型,那我们的实体类型就有可能是:英雄、技能、⽪肤…等等。
在SPO列表中,对于头实体(S)和尾实体(O)的类型进⾏了分开表⽰,即,分为头实体类型(subject_type)和尾实体类型
(object_type)。
类型列表在项⽬⽬录下 “id2spo.json” ⽂件中存储,我们打印该⽂件看看:
{
"predicate":["empty","empty","注册资本","作者","所属专辑",...],
"subject_type":["empty","empty","企业","图书作品","歌曲",...],
"object_type":["empty","empty","Number","⼈物","⾳乐专辑",...]
}
可以看到,该json⽂件下存放了三个list,分别代表了SPO的对应type类型。
其中,每⼀个列表中对应的索引是可以构成⼀个合法三元组的,我们将相同索引的各列表中的列表组合打印出来,结果如下:
----------------------
S | P | O
----------------------
empty | empty | empty
empty | empty | empty
企业 | 注册资本 | Number
图书作品 | 作者 | ⼈物
歌曲 | 所属专辑 | ⾳乐专辑
歌曲 | 歌⼿ | ⼈物
⾏政区 | | Text
影视作品 | 主演 | ⼈物
影视作品 | 上映时间 | Date_@value
影视作品 | 上映时间 | 地点_inArea
娱乐⼈物 | 饰演 | ⼈物_@value
娱乐⼈物 | 饰演 | 影视作品_inWork
...
其中,前2个empty是为了O和I标签留的,因为之前定义的predicate列表中的前2个标签分别为O、I,这两个标签不会起到连接⾸尾实体的作⽤,因此需要置为empty。
注意看这两⾏:
...
影视作品 | 上映时间 | Date_@value
影视作品 | 上映时间 | 地点_inArea
...
还记得我们在1.2.2节中提到的奇怪的问题吗,为什么同⼀个实体关系「上映时间」会有两个不同的后缀——上映时间_@value和上映时间_inArea?
想必你已经在这个表中到答案了吧?
原因就是,同⼀个「头实体(S) - 实体关系(P)- ?」可能会对应多个不同的尾实体(O)。
举例来讲,
[+]《⼤⽿朵图图之美⾷狂想曲》的动画电影将于2017年7⽉28⽇在中国上映
这句话当中,《⼤⽿朵图图之美⾷狂想曲》(S)- 上映(P)- ?,同⼀个SP可以对应两个不同的O:
1. 《⼤⽿朵图图之美⾷狂想曲》(S)- 上映(P)- 2017年7⽉28⽇(O1)
2. 《⼤⽿朵图图之美⾷狂想曲》(S)- 上映(P)- 中国(O2)
第⼀个SPO代表电影-上映-上映时间;
第⼆个SPO代表电影-上映-上映地点;
由此我们可以看出,对于同⼀个S-P,句⼦中是可能存在多个不同的合法O的,那我们就需要使⽤两个或多个不同的S-P来对应这些不同的O。
⼀种最常见的⽅法就是对P再进⾏细分,尾实体O不是既有可能是上映时间,也有可能是上映地点吗。那我就直接分别设定两个不同的P(上映时间)标签就好了,即:
1. 上映时间_@value:⽤于连接实体类型为「时间」的尾实体。
2. 上映时间_inArea:⽤于连接实体类型为「地点」的尾实体。
这就是我们对应在尾实体列表(object_type)中看到存在 […, Date_@value, 地点_inArea, …] ,在predicate2id⽂件中看到 […, “上映时间_@value”, “上映时间_inArea”, …] 的原因。
这⾥顺带再提⼀句,我们看看在训练数据集中这种关系是如何表⽰的:
{
阿sa门事件...
"spo_list":[
{
"predicate":"上映时间",
"object_type":{"inArea":"地点","@value":"Date"},
"subject_type":"影视作品",
"object":{"inArea":"中国","@value":"4⽉14⽇"},
"subject":"垫底辣妹"
}
...
}
汽车行业分析报告可以看到,训练数据集中,“object_type” 和 “object” 字段对应的都是⼀个字典(可以包含多个值),⽽SP对应的都是⼀个唯⼀的值。
这就印证了我们刚才的说法,确定⼀组S-P,可以对应多个不同的尾实体O。
但是,这⾥和prediacte2id.json中不同,训练集中的predicate的值并不包含标签后缀,不是「上映时间_@value」,⽽直接就是「上映时间」。
这样将原来的predicate后缀变化到 "object"字段中的key中,其实更符合⼈们的认知,毕竟关系不应该存在区别,区别的应该是实体类型。
2.4 代码解析
在该任务中,我们将实体关系抽取问题建模为Token Classification的问题。
我们知道,在命名实体识别(Named Entity Recognition,NER)问题中,通常会⽤Token Classification的⽅式来进⾏任务求解,即判断每个字符(token)属于哪个类别(某个实体?⾮实体?)。
在NER任务中使⽤字符分类的⽅式我们⾮常容易就理解,但是,我们怎么通过对字符分类的⽅式来提取出不同实体之间的关系呢?
答案就是:在字符类别中添加⼊「关系标签」,即该字符是否能和这句话当中的其他字符产⽣关联关系。
这个想法相当简单暴⼒,也相当符合神经⽹络「硬train⼀发」的核⼼原则。
六级分数分配模型结构图如下所⽰:
发布评论