【毕业设计】基于LTP的需求文本分析研究-2021.4.21

说明

本文作为个人向记录,内容为毕业设计期间学习的技术总结。

LTP内容均来源于官网:https://ltp.readthedocs.io/zh_CN/latest/appendix.html

技术版本

LTP模型:3.4.0
pyltp:0.2.1

LTP工具集

分句

对比之后,LTP分句效果比nltk更好

分词

分词部分使用能够调整词频的jieba分词代替

词性标注

LTP 使用的是863词性标注集,其各个词性含义如下表。

Tag Description Example Tag Description Example
a adjective 美丽 ni organization name 保险公司
b other noun-modifier 大型, 西式 nl location noun 城郊
c conjunction 和, 虽然 ns geographical name 北京
d adverb nt temporal noun 近日, 明代
e exclamation nz other proper noun 诺贝尔奖
g morpheme 茨, 甥 o onomatopoeia 哗啦
h prefix 阿, 伪 p preposition 在, 把
i idiom 百花齐放 q quantity
j abbreviation 公检法 r pronoun 我们
k suffix 界, 率 u auxiliary 的, 地
m number 一, 第一 v verb 跑, 学习
n general noun 苹果 wp punctuation ,。!
nd direction noun 右侧 ws foreign words CPU
nh person name 杜甫, 汤姆 x non-lexeme 萄, 翱
z descriptive words 瑟瑟,匆匆

命名实体识别

其中,wordspostags 分别为分词和词性标注的结果。同样支持Python原生的list类型。

LTP 采用 BIESO 标注体系。B 表示实体开始词,I表示实体中间词,E表示实体结束词,S表示单独成实体,O表示不构成命名实体。

LTP 提供的命名实体类型为:人名(Nh)、地名(Ns)、机构名(Ni)。

B、I、E、S位置标签和实体类型标签之间用一个横线 - 相连;O标签后没有类型标签。

LTP中的NE 模块识别三种NE,分别如下:

标记 含义
Nh 人名
Ni 机构名
Ns 地名

依存句法分析

arc.head 表示依存弧的父节点词的索引。ROOT节点的索引是0,第一个词开始的索引依次为1、2、3…

arc.relation 表示依存弧的关系。

arc.head 表示依存弧的父节点词的索引,arc.relation 表示依存弧的关系。

依存句法关系标注集:

关系类型 Tag Description Example
主谓关系 SBV subject-verb 我送她一束花 (我 <– 送)
动宾关系 VOB 直接宾语,verb-object 我送她一束花 (送 –> 花)
间宾关系 IOB 间接宾语,indirect-object 我送她一束花 (送 –> 她)
前置宾语 FOB 前置宾语,fronting-object 他什么书都读 (书 <– 读)
兼语 DBL double 他请我吃饭 (请 –> 我)
定中关系 ATT attribute 红苹果 (红 <– 苹果)
状中结构 ADV adverbial 非常美丽 (非常 <– 美丽)
动补结构 CMP complement 做完了作业 (做 –> 完)
并列关系 COO coordinate 大山和大海 (大山 –> 大海)
介宾关系 POB preposition-object 在贸易区内 (在 –> 内)
左附加关系 LAD left adjunct 大山和大海 (和 <– 大海)
右附加关系 RAD right adjunct 孩子们 (孩子 –> 们)
独立结构 IS independent structure 两个单句在结构上彼此独立
核心关系 HED head 指整个句子的核心

语义角色标注

第一个词开始的索引依次为0、1、2…

返回结果 roles 是关于多个谓词的语义角色分析的结果。由于一句话中可能不含有语义角色,所以结果可能为空。

role.index 代表谓词的索引, role.arguments 代表关于该谓词的若干语义角色。

arg.name 表示语义角色类型,arg.range.start 表示该语义角色起始词位置的索引,arg.range.end 表示该语义角色结束词位置的索引。

例如上面的例子,由于结果输出一行,所以“元芳你怎么看”有一组语义角色。 其谓词索引为3,即“看”。这个谓词有三个语义角色,范围分别是(0,0)即“元芳”,(1,1)即“你”,(2,2)即“怎么”,类型分别是A0、A0、ADV。

arg.name 表示语义角色关系,arg.range.start 表示起始词位置,arg.range.end 表示结束位置。

关系类型 Tag Description Example
ARG0 causers or experiencers 施事者、主体、触发者 [政府 ARG0]鼓励个人投资服务业。
ARG1 patient 受事者 政府鼓励[个人 ARG1]投资服务业。
ARG2 range 语义角色2 政府鼓励个人[投资服务业 ARG2]。
ARG3 starting point 语义角色3 巴基斯坦[对谈判前景 ARG3]表示悲观。
ARG4 end point 语义角色4 产检部门将产检时间缩短到[一至三天 ARG4]。
ADV adverbial 状语 我们[即将 ADV]迎来新年。
BNF beneficiary 受益人 义务[为学童及老师 BNF]做超音波检查 。
CND condition 条件 [如果早期发现 CND],便能提醒当事人注意血压的变化。
CRD coordinated arguments 并列 跟南韩、[跟美国 CRD]谋求和平关系的举动也更加积极。
DGR degree 程度 贫铀弹含有放射性比铀强[20万倍 DGR]。
DIR direction 方向 [从此处 DIR] 我们可以发现寇克斯报告的精髓。
DIS discourse marker 会话标记 警方上午针对目击者做笔录,[而 DIS]李士东仍然不见踪影。
EXT extent 范围 回归3年多[来 EXT] ,香港成为台商对大陆贸易的财务运作及资金调度中心。
FRQ frequency 频率 这类听证会在赖昌兴拘押期间每30天举行[一次 FRQ]。
LOC locative 地点 请听美国之音特邀记者康妮[在加拿大温哥华 LOC]发来的报道。
MNR manner 方式 以便他能继续[作为俄罗斯官员 MNR]从事他在一个特殊机构中的工作。
PRP purpose or reason 目的 执政党和在野党[为了应付这场攻守战 PRP]都发出了紧急动员令。
QTY quantity 数量 每年创汇[100万 QTY]美元。
TMP temporal 时间 [下星期 TMP]布什将提出一项周密计划。
TPC topic 话题 [这么大的事情 TPC],你怎么不和我说?
PRD predicate 谓语动词
PSR possessor 持有者
PSE possessee 被持有

运行记录

代码

LTP工具:

# 代码参考:https://www.itdaan.com/blog/2018/03/15/c324a4cfaf408bfcc5a98c6145f269eb.html
class LtpLanguageAnalysis(object):
    def __init__(self, model_dir=os.path.abspath("resource/ltp_data_v3.4.0")):
        self.segmentor = Segmentor()
        self.segmentor.load(os.path.join(model_dir, "cws.model"))
        self.postagger = Postagger()
        self.postagger.load(os.path.join(model_dir, "pos.model"))
        self.parser = Parser()
        self.parser.load(os.path.join(model_dir, "parser.model"))
        self.recognizer = NamedEntityRecognizer()
        self.recognizer.load(os.path.join(model_dir, "ner.model"))
        self.labeller = SementicRoleLabeller()
        self.labeller.load(os.path.join(model_dir, "pisrl_win.model"))

    def load_model(self, model_dir=os.path.abspath("resource/ltp_data_v3.4.0")):
        self.segmentor.load(os.path.join(model_dir, "cws.model"))
        self.postagger.load(os.path.join(model_dir, "pos.model"))
        self.parser.load(os.path.join(model_dir, "parser.model"))
        self.recognizer.load(os.path.join(model_dir, "ner.model"))
        self.labeller.load(os.path.join(model_dir, "pisrl_win.model"))

    def analyze(self, txt):
        # 分词
        words = self.segmentor.segment(txt)
        print('\t'.join(words))
        # 词性标注
        postags = self.postagger.postag(words)
        print('\t'.join(postags))
        # 命名实体识别
        netags = self.ner(words, postags)
        print("\t".join(netags))
        # 依存句法分析
        arcs = self.parse(words, postags)
        print("\t".join("%d:%s" % (arc.head, arc.relation) for arc in arcs))
        # 语义角色标注
        roles = self.srl(words, postags, arcs)
        for role in roles:
            print([
                role.index,
                "".join(["%s:(%d,%d)" % (arg.name, arg.range.start, arg.range.end) for arg in role.arguments])
            ])

    def parse(self, words, postags):
        # 依存句法分析
        arcs = self.parser.parse(words, postags)
        return arcs

    def ner(self, words, postags):
        # 命名实体识别
        netags = self.recognizer.recognize(words, postags)
        return netags

    def srl(self, words, postags, arcs):
        # 语义角色标注
        roles = self.labeller.label(words, postags, arcs)
        return roles

    def release_model(self):
        # 释放模型
        self.segmentor.release()
        self.postagger.release()
        self.parser.release()
        self.recognizer.release()
        self.labeller.release()

测试内容:

import jieba_nlp_util as jnu
from ltp_util import LtpLanguageAnalysis

def test_pyltp(txt):
    print("-----LTP分析:-----")
    # 导入ltp模型
    ltp = LtpLanguageAnalysis(os.path.abspath("../resource/ltp_data_v3.4.0"))
    # 文本分析
    # 分段
    paragraphs = txt.split('\n')
    for paragraph in paragraphs:
        # 使用nltk分句
        # sentences = nu.txt_split(paragraph)
        # 使用ltp分句
        sentences = lu.ltp_split(paragraph)
        for sentence in sentences:
            # 使用jieba分词
            signed_words = jnu.default_cut(sentence)
            words = []
            flags = []
            for word, flag in signed_words:
                if flag == 'x':
                    # 删去标点符号
                    continue
                words.append(word)
                flags.append(flag)
            print_words_and_flags(words, flags)
            arcs = ltp.parse(words, flags)
            netags = ltp.ner(words, flags)
            roles = ltp.srl(words, flags, arcs)
            print("\t".join(netags))
            print("\t".join("%d:%s" % (arc.head, arc.relation) for arc in arcs))
            for role in roles:
                print([
                    role.index,
                    " ".join(["%s:(%d,%d)" % (arg.name, arg.range.start, arg.range.end) for arg in role.arguments])
                ])
            print()
    # 释放模型
    ltp.release_model()

内容

未删去'x'标注词:
image

删去'x'标注词:
image

结论

  • 通过进行LTP语义角色标注,以是否删去'x'标注(包括标点符号等)为变量:
    • 不删去:语义标注可能会遗漏","等后面的受事者(A1)
    • 删去:如果一个标点前的受事者与这个标点后的施事者相同,可能导致两者被分到一个角色里面
    • 考虑:
      1. 可根据标点后(尤其是",")是否存在连词,来判断标点后的子句是否与标点前的有较强的内容联系,这种标点可以删去
      2. 可以根据标点后主语与标点前宾语是否相同,来决定这个逗号是不是应该当成句号处理(分句)
      3. 更多规则...
posted @ 2021-04-21 17:50  woodenhead  阅读(333)  评论(0编辑  收藏  举报