当前开源的分词组件进行了对比,其中存在一个典型的未登录词识别问题,即对于一些未登录词或者领域词的识别时,会出现切分过细或者切分不合法的问题。

为了解决以上两个问题,短语提取逐步成为了当前NLP的一个重要任务,常见于文本分词、特征挖掘、领域实体发现、概念挖掘等场景当中。

解决这种情况,引申出来了多种方式:

当我们对一个文本进行分词,或者分字,并将所有长度不超过n的子串都当作潜在的词,然后组成ngram后,会得到很多候选的短语集合。

而为了得到真正有实际意义的短语,通常需要设计评判规则,并使用相应的统计手段加以实现。通过整理调研相关资料,一个高质量的短语应该满足高频率、一致性、信息量以及完整性等几个特性。

不过,上述方法遇到一个很大的问题,即需要统计的上下文语料,在语料缺失的情况下,就需要使用一些固定的构词规则【基于词法的或基于句法的】进行无监督抽取,或者使用有监督标注和训练的方法进行抽取

本文主要围绕NLP中的短语抽取问题,展开论述,介绍基于高频性与TFIDF判定、一致性与PMI点间互信息判定、完整性及左右信息熵判定三种统计短语抽取方法,基于词性与构词模板规则生成的方法以及基于有监督识别的三种方法,供大家一起参考。

一、高频性与TFIDF的统计判定

1)思想

高频率认为,一个n-gram出现的频率越高,那么它是高质量的文章的可能性就越高,频率很低的组合成词的可能性很小。

2)原理

在实现上,可以使用TF算法计算候选短语出现的频次加以过滤,也可以在此基础上使用TF-IDF、TextRank算法或者词性过滤规则,过滤文中出现的频率高但是重要的词,如代词,副词,介词,助词等。

3)实现

   def build_wordsdict(self, text):
        word_dict = {}
        candi_words = []
        candi_dict = {}
        for word in pseg.cut(text):
            if word.flag[0] in ['n''v''a'] and len(word.word) > 1:
                candi_words.append(word.word)
            if word.word not in word_dict:
                word_dict[word.word] = 1
            else:
                word_dict[word.word] += 1
        count_total = sum(word_dict.values())
        for word, word_count in word_dict.items():
            if word in candi_words:
                candi_dict[word] = word_count/count_total
            else:
                continue

        return candi_dict

    def extract_keywords(self, text, num_keywords):
        keywords_dict = {}
        candi_dict = self.build_wordsdict(text)
        for word, word_tf in candi_dict.items():
            word_idf = self.idf_dict.get(word, self.common_idf)
            word_tfidf = word_idf * word_tf
            keywords_dict[word] = word_tfidf
        keywords_dict = sorted(keywords_dict.items(), key=lambda asd:asd[1], reverse=True)

        return keywords_dict[:num_keywords]

开源实现:https://github.com/liuhuanyong/KeyInfoExtraction

二、一致性与PMI点间互信息的统计判定

1)思想

一致性,用于评估候选短语n-gram中的单词的搭配是否合理,与此相对应的是凝固度,即一个字组合片段里面字与字之间的紧密程度,例如“琉璃”、“榴莲”这样的词的凝固度就非常高,电影院的凝固度和合理性要比“的电影”要高。

不过,该方法存在一定的缺点,如果只看一致性,即凝固程度的话,会倾向于找出“巧克”、“俄罗”、“颜六色”、“柴可夫”等实际上是“半个词”的片段。

2)原理

在具体实现上,可以使用PMI点间互信息值来衡量,一个ngran短语的PMI点间互信息值越大成词的可能性越大。

设定p(x)为文本片段x在整个语料中出现的概率,那么:

“电影院”的凝合程度就是p(电影院)与p(电)*p(影院)比值和p(电影院)与p(电影)*p(院)的比值中的较小值

“的电影”的凝合程度则是p(的电影)分别除以p(的)*p(电影)和p(的电)*p(影)所得的商的较小值。

其中,有个实现的细节,即针对一个ngram,需要对其进行切分组合,然后分别计算组合中的PMI值,然后排序,例如,针对 ngram ’abcd’:

首先,把字组合切分成不同的组合对,拆成(‘a’, ‘bcd’), (‘ab’, ‘cd’), (‘abc’, ‘d’)

然后,计算每个组合对的凝固度:*D(s1, s2)=P(s1s2)/(P(s1)P(s2))

最后,取这些组合对凝固度中最小的那个为整个字组合的凝固度,通常,我们会对概率取对数处理。

3)实现

  def compute_mi(p1, p2, p12):
      return math.log2(p12) - math.log2(p1) - math.log2(p2)

开源实现:https://github.com/zhanzecheng/Chinese_segment_augment

三、完整性及左右信息熵的统计判定

1)思想

高质量的短语需要是一个完整的语义单元,如“巧克力“与“巧克”相比,完整性要更高。一般来说,一个短语如果完整,那么其能够被独立运用的概率就越高,其独立使用时出现的前后上下文就越丰富。

与此相对应的概念叫做自由度,即一个字组合片段能独立自由运用的程度。 比如“巧克力”里面的“巧克”的凝固度就很高,和“巧克力”一样高,但是它自由运用的程度几乎为零,所以“巧克”不能单独成词。

2)原理

在具体实现上,我们可以使用左右信息熵来进行实现,左右邻熵,描绘的是一个短语左右搭配的丰富度,一个好的短语左右搭配应该是非常丰富的,分别计算它的左邻居信息熵和右邻居信息熵,取其中较小的为该组合的自由度,自由度越大成词的可能性越大。

例如,援引文献5的论述,对于“吃葡萄不吐葡萄皮不吃葡萄倒吐葡萄皮”,“葡萄”一词出现了四次,其中左邻字分别为{吃, 吐, 吃, 吐} ,右邻字分别为 {不, 皮, 倒, 皮} 。

H(x) = E[I(xi)] = E[log(2,1/P(xi))] = -∑P(xi)log(2,P(xi)) (i=1,2,..n)

其中,x表示随机变量,与之相对应的是所有可能输出的集合,定义为符号集,随机变量的输出用x表示,P(x)表示输出概率函数。变量的不确定性越大,熵也就越大,把它搞清楚所需要的信息量也就越大。

根据上述公式计算,

“葡萄”一词的左邻字的信息熵为:

–(1/2)·log(1/2)–(1/2)·log(1/2)≈0.693,

“葡萄”一词的右邻字的信息熵为:

–(1/2)·log(1/2)–(1/4)·log(1/4)–(1/4)·log(1/4)≈1.04 

通过计算,可以发现“葡萄”一词的右邻字更加丰富一些。

3)实现

def compute_entropy(neighbours):
    if neighbours:
        right_sum = sum(neighbours)
        #TODO 计算改词的右领字熵,可以怎么优化呢?
        right_prob = map(lambda x:x/right_sum, neighbours)
        right_entropy = sum(map(lambda x:-(x)*math.log(x), right_prob))
        return right_entropy
    else:
        return 0

与一致性类似,如果只看完整性即自由程度的话,则会把 “吃了一顿”、“看了一遍”、“睡了一晚”、“去了一趟”中的“了一”提取出来,因为它的左右邻字都太丰富了。

因此,在最终实现的时候,通常都会综合考虑上面三个因素,然后分别设定阈值或者做得分的加和,得到一个整体的值,再进行阈值过滤,最终输出。

开源实现:https://github.com/zhanzecheng/Chinese_segment_augment

四、词性与构词模板的规则判定

上面列举的三种方法,都需要大量的文本进行统计,而且语料库越大,效果越好,但对于一些query或者短文本情况下,则并不奏效,因此,通常可以采用短语构词模板进行判定,将满足这种词性组合模式的短语过滤出来。

1)思想

词性模板规则判定的方法,其思想在于短语通常满足n+v,或者n+n,或a+n的模式,通过文本分词和词性标注,进行组合匹配,就可以输出。

2)原理与实现

通过jieba等开源工具进行分词和词性标注,可以得到一些有实际意义的词,但这些词分的粒度太细,可以进一步组合,例如,将连续的名词组合成新的NP短语。

IP = "(([NERMQ]*P*[ABDP]*)*([ABDV]{1,})*([NERMQ]*)*([VDAB]$)?([NERMQ]*)*([VDAB]$)?)*"
IP = "([NER]*([PMBQADP]*[NER]*)*([VPDA]{1,}[NEBRVMQDA]*)*)"
MQ = '[DP]*M{1,}[Q]*([VN]$)?'
VNP = 'V*N{1,}'
NP = '[NER]{1,}'
REN = 'R{2,}'
VP = 'P?(V|A$|D$){1,}'
PP = 'P?[NERMQ]{1,}'
SPO_n = "n{1,}"
SPO_v = "v{1,}"
def extract_ngram(pos_seq, self.NP):
    ss = self.transfer_tags(pos_seq)
    def gen():
        for s in range(len(ss)):
            for n in range(self.minlen, 1 + min(self.maxlen, len(ss) - s)):
                e = s + n
                substr = ss[s:e]
                if re.match(regex + "$", substr):
                    yield (s, e)
    return list(gen())

也可以使用standfordparser/corenlp进行浅层句子成分分析,得到相应结果。

from stanfordcorenlp import StanfordCoreNLP
nlp_ch = StanfordCoreNLP(r'stanford-corenlp-full-2018-10-05', lang='zh')
print (nlp_ch.parse('浙江大学有七个校区。'))

结果:
(ROOT
  (IP
    (NP (NR 浙江) (NN 大学))
    (VP (VE 有)
      (NP
        (QP (CD 七)
          (CLP (M 个)))
        (NP (NN 校区))))
    (PU 。)))

通过提取NP标签,可以直接提取出“浙江大学”、“七个校区”两个NP短语。

开源实现:https://github.com/liuhuanyong/EventTriplesExtraction

五、基于有监督标注分类评分的短语判定

在基于有监督的短语挖掘工作上,韩家炜老师团队逐步产出了系列具有代表性的工作,包括TopMine,SegPhrase、AutoPhrase,很具有启发性.

1、TopMine:频率模式挖掘+统计分析

TopMine,发表于《Scalable Topical Phrase Mining from Text Corpora》一文。

1)主要思想

该工作主要是对语料库文本的Topic进行挖掘,不同于以往采用Uni-gram的方法,而是将Topic 挖掘分成了两个步骤:通过Phrase Mining对文本进行分割;随后进行基于Phrase约束的Topic模型。

2)原理

首先,先进行Phrase挖掘,用到phrase本身的统计量以及phrase上下文信息。在统计量熵,直接使用频次作为过滤条件,生成所有的phrase候选,在上下文信息上,构造了一个类似PMI的指标,用来衡量合并之后的得分。

3)开源实现

https://github.com/anirudyd/topmine

2、SegPhrase:弱监督、高质量的 Phrase Mining

SegPhrase,发表于《Mining Quality Phrases from Massive Text Corpora》一文

1)思想

TopMine方法通常会产生低质量的主题短语,或者在中等大小的数据集上也会出现可伸缩性差的问题。SegPhrase是一种新的短语挖掘框架,将文档分为单词短语和多词短语,以及一个新的主题模型,该模型基于诱导文档划分,从而产生高质量的短语集合。

2)原理

根据用户需求生成常用短语候选,然后根据有关一致性和信息要求的特征来估计短语质量,并通过短语分段估计校正的频率,并将基于整流频率的基于细分的特征添加到短语质量分类器的特征集中,最后过滤低整流频率的短语,以满足后处理步骤的完整性要求。

3)开源实现

https://github.com/shangjingbo1226/SegPhrase

3、AutoPhrase:自动的 Phrase Mining

AutoPhrase, 发表于《Automated Phrase Mining from Massive Text Corpora》一文。

1)思想

该工作认为,一个理想的自动短语挖掘方法应该是独立于不同领域,并且只需要最少的人力或语言解析,因此提出了AutoPhrase框架,更深层次的避免了人工标注的问题。

2)原理

AutoPhrase框架选用了Robust Positive-Only Distant Training(强健正面的远程训练)以及POS-Guided Phrasal Segmentation(POS-Guided短语分割)两种策略。AutoPhrase会根据正负池对质量短语进行两次评估,分别是在短语分割的前和后,也就是说,POS-Guided短语分割需要一组初始的质量短语分数,预先根据原始频率估计分数,然后一旦特征值被纠正,就要重新估计分数。

上图展示了整个流程:

AutoPhrase的第一部分(上图中的左部分)候选短语集合包含所有超过最小阈值的n-grams。这里的阈值指的是通过字符串匹配计算出的n-grams的原始频率。

首先,在Robust Positive-Only Distant Training环节, 利用已有的知识库(Wikipedia)做远程监督训练,以避免手工标注,具体的:

构造正样本:从一般知识库中构建正样本,如维基百科中获取的高质量短语;

构造负样本:给定语料库抽取出的候选短语集合构建负例,如果不能匹配到公共知识库,那么就作为负例;

训练分类器:训练多个分类器,对于每个基分类器,分别从正池和负池中随机抽取K个候选短语,并进行替换,所有的候选短语中的2K大小的子集称之为扰动训练集,最终将这些分类器的预测结果进行集成。

其次,POS-Guided Phrasal Segmentation方面, 利用词性信息来增加抽取的准确性,在文档集合的语言中加入一个预先训练的词性标记,以进一步提高性能。

3)开源实现

https://github.com/shangjingbo1226/AutoPhrase

总结

本文主要围绕NLP中的短语抽取问题,展开论述,介绍基于高频性与TFIDF判定、一致性与PMI点间互信息判定、完整性及左右信息熵判定三种统计短语抽取方法以及基于词性与构词模板规则生成的方法以及基于有监督识别的三种方法。

从中,我们可以发现,围绕特定领域进行短语提取,其核心在于评估短语的成立性和评价标准,如文中提到的高频性、一致性和完整性,关于这一点,文献5做了很生动的说明。有了这个评价标准,我们就可以找对应的建模方式完成逼近。

不过,很多人将这种短语抽取的过程说成新词发现,感觉上是逻辑不对的,新词,是有时间先后对比的,必须有上一时间段中的短语作为参照系,才能这么说。

内容中包含的图片若涉及版权问题,请及时与我们联系删除