专栏首页AI机器学习与深度学习算法使用 HanLP 统计二元语法中的频次

使用 HanLP 统计二元语法中的频次

计算句子概率值的工具就是语言模型,但是随着句子长度的逐渐增大,语言模型会遇到下面两个问题:

  1. 数据稀疏。长度越长的句子在语料库中出现的次数就越小,甚至很多时候极有可能在语料库中统计不到长句子的频次,导致很多长句子的概率值为0;
  2. 计算代价大。假设有一个词汇量为的语料库,对于一个长度为的的句子,需要保存个参数。越大,需要存储的参数也就越多;

为了解决这两个问题,可以使用马尔科夫假设来简化语言模型,这就是 n-gram 语言模型,n-gram 语言模型简单来说就是当前单词出现的概率只和它的前 个单词有关系。随着 的取值越大,n-gram 语言模型在理论上越精确,但是模型也越复杂,需要的计算量和训练语料数据量也就越大,并且精度提升的不够明显,所以在实际的任务中很少使用 的语言模型。

如果使用 的 bigram 语言模型计算 句子的概率值 ,则有:(滑动移动公式)

其中,(Begin OF Sentence, 有时也用<s>),(End OF Sentence, 有时也用</s>),它们是用来标记句子首尾的两个特殊"单词"。计算出这些条件概率值,然后将这些概率值相乘之后就可以计算出 。

我们可以使用极大似然估计(Maximum Likelihood Estimation, MLE)来计算这些条件概率值,比如对于 :

其中, 表示 的计数(count)。

如果想要使用 bigram 语言模型计算句子的概率值,需要统计出一个单词的频次(分母)以及两个单词连续且共同出现的频次(分子)。HanLP 为我们提供了封装好的工具能够轻松的统计出一个单词和两个单词连续且共同出现的频次。接下来使用 HanLP 来统计这些一个单词和两个单词连续且共同出现的频次。

加载语料库

我们没有办法枚举出这一门语言的所有句子,因此只能采样一个小型的样本空间,称为语料库,因此这些统计的频次都是基于给定的语料库计数统计的,所以首先需要加载语料库。

假设现在有一个已经用空格分隔的分词语料库,将其命名为 my_corpus.txt,当然这里只是一个简单的小例子,实际的语料库要复杂的多。

商品 和 服务
商品 和服 物美价廉
服务 和 货币

对于空格分隔的语料库来讲,可以利用 HanLP 提供的 CorpusLoader.convert2SentenceList 来加载。

from pyhanlp import *

# 通过 SafeJClass 取得 HanLP 中的 CorpusLoader 类
CorpusLoader = SafeJClass('com.hankcs.hanlp.corpus.document.CorpusLoader')

def load_corpus(corpus_path):
    '''
    通过语料库的路径加载语料库
    :param corpus_path:
    :return: 
    '''
    return CorpusLoader.convert2SentenceList(corpus_path)

sents = load_corpus("my_corpus.txt")

>>> print(type(sents))
<class 'jpype._jclass.java.util.LinkedList'>
>>> print(sents)
[[商品, 和, 服务], [商品, 和服, 物美价廉], [服务, 和, 货币]]

统计一元语法和二元语法

有一些语料库中含有人工标注的词性,因此词典格式最好还要支持词性,所以在进行一元语法的频次统计时,可以考虑为语料库中的每个单词设置词性,这里为了简单统一设置为名词,当然在实际中即使是相同的单词在不同的上下文中也可能表示不同的词性。

from pyhanlp import *

# 通过 SafeJClass 取得 HanLP 中的 CorpusLoader 类
CorpusLoader = SafeJClass('com.hankcs.hanlp.corpus.document.CorpusLoader')

def load_corpus(corpus_path):
    '''
    通过语料库的路径加载语料库
    :param corpus_path:
    :return:
    '''
    return CorpusLoader.convert2SentenceList(corpus_path)

# my_corpus.txt在当前路径下
sents = load_corpus("my_corpus.txt")  
for sent in sents:
    for word in sent:
        # 为了兼容 HanLP 的词典格式,为每个单词赋予一个虚拟的名词词性
        # 此时的词性仅用作占位符,不起实际的作用
        word.setLabel("n")  

>>> print(type(sents))
<class 'jpype._jclass.java.util.LinkedList'>
>>> print(sents)
[[商品/n, 和/n, 服务/n], [商品/n, 和服/n, 物美价廉/n], [服务/n, 和/n, 货币/n]]

在 HanLP 中,统计单个单词词频的功能由 DictionaryMaker 提供,统计两个单词的词频的功能由 NGramDictionaryMaker 提供,篇幅限制,下面给出简单的执行代码,具体可以按照下面代码进行测试。

from pyhanlp import *

# 通过 SafeJClass 取得 HanLP 中的 DictionaryMaker 类
DictionaryMaker = SafeJClass('com.hankcs.hanlp.corpus.dictionary.DictionaryMaker')
# 通过 SafeJClass 取得 HanLP 中的 NGramDictionaryMaker 类
NGramDictionaryMaker = SafeJClass('com.hankcs.hanlp.corpus.dictionary.NGramDictionaryMaker')

maker = DictionaryMaker()  # 创建对象
ngramMaker = NGramDictionaryMaker()  # 创建对象

# 每次添加语料库中单个单词
maker.add(word)  
# 每次添加语料库中两个单词
ngramMaker.addPair(first word, second word)  

# 保存文件到 output_path 路径下
maker.saveTxtTo(output_path)
# 保存文件到 output_path 路径下
ngramMaker.saveTxtTo(output_path)  

可以看到 DictionaryMaker 和 NGramDictionaryMaker 都是根据语料库中的单词为单位进行统计,其中的和是句子的起始标识符,显然 DictionaryMaker 和 NGramDictionaryMaker 是统计不到的。

bigram 语言模型的计算是由单个单词词频和两个单词连续且共同出现的频次,以及这些句子的起始标识符出现的次数所决定的。HanLP 提供了能够同时统计与 bigram 语言模型相关频次的包装类 NatureDictionaryMaker。

from pyhanlp import *

# 通过 SafeJClass 取得 HanLP 中的 CorpusLoader 类
CorpusLoader = SafeJClass('com.hankcs.hanlp.corpus.document.CorpusLoader')
# 通过 SafeJClass 取得 HanLP 中的 NatureDictionaryMaker 类
NatureDictionaryMaker = SafeJClass('com.hankcs.hanlp.corpus.dictionary.NatureDictionaryMaker')


def statistical_single_word(corpus_path, output_path):
    """
    统计语料库中的单个单词的词频
    :param corpus_path: 语料库路径
    :param output_path: 输出保存单个单词词频的路径
    :return:
    """
    sents = CorpusLoader.convert2SentenceList(corpus_path)
    maker = NatureDictionaryMaker()  # 创建对象
    for sent in sents:
        for word in sent:
            # 为了兼容 HanLP 的词典格式,为每个单词赋予一个虚拟的名词词性
            # 此时的词性仅用作占位符,不起实际的作用
            word.setLabel("n")
    # 处理预料
    maker.compute(sents)  
    # 保存文件到 output_path 路径下
    maker.saveTxtTo(output_path)  

# 调用函数
statistical_single_word("cws_corpus.txt", "my_cws")

当前路径下会多出以 my_cws. 开头的三个文件:

其中 my_cws.txt 统计的是单个单词和句子的起始标识符的频次,my_cws.txt 文件内容如下所示:

和 n 2
和服 n 1
商品 n 2
始##始 begin 3
服务 n 2
末##末 end 3
物美价廉 n 1
货币 n 1

这也是标准的 HanLP 词典格式,每行分别为单词 词性 词性的频次。其中始##始代表句子的开头,末##末代表句子的结尾,和英文中的 和作用是相同的。

其中 my_cws.ngram.txt 统计的是两个单词连续且共同出现的频次和句子的起始标识符的频次,my_cws.ngram.txt 文件内容如下所示:

和@服务 1
和@货币 1
和服@物美价廉 1
商品@和 1
商品@和服 1
始##始@商品 2
始##始@服务 1
服务@和 1
服务@末##末 1
物美价廉@末##末 1
货币@末##末 1

这里@符号分割开两个单词,空格后面是对应的频次。my_cws.tr.txt 与词性标注有关,这里可以暂时忽略。

本文分享自微信公众号 - AI机器学习与深度学习算法(AI-KangChen),作者:Chenkc

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 数据结构与算法 1-6 Python列表类型不同操作的时间效率

    本系列是我在学习《基于Python的数据结构》时候的笔记。本小节首先回顾一下timeit代码执行时间测量模块,然后通过此模块测算Python中list列表一些操...

    触摸壹缕阳光
  • Numpy中常用随机函数的总结

    Numpy中的常用随机函数常常用于按照某种概率统计规则来产生随机数,在机器学习和深度学习中,我们常常需要使用随机函数对一些参数进行初始化,而且在一些深度学习框架...

    触摸壹缕阳光
  • 机器学习入门 11-1 什么是SVM

    本系列是《玩转机器学习教程》一个整理的视频笔记。本小节主要介绍支撑向量机的核心思想,最终将支撑向量机的思想转换成最优化问题。针对线性可分的问题可以使用Hard ...

    触摸壹缕阳光
  • python模块之hashlib

    什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

    菲宇
  • 腾讯云 AI 安全与互联网金融黑产暗战

    腾讯云安全
  • Android studio 生成带Kotlin文档的实现方式

    首先才项目的build.gradle 加入classpath ‘org.jetbrains.dokka:dokka-android-gradle-plugin:...

    砸漏
  • PP-YOLO何许模型?竟然超越了YOLOv4

    PP-YOLO评估指标显示出比现有的最新对象检测模型YOLOv4更高的性能。但是,提出者百度却谦虚的声明:

    小白学视觉
  • 对某单位的 APT 攻击样本分析

    在六月份的某单位HW行动中,知道创宇HW安全团队通过创宇云图APT威胁感知系统并结合腾讯御点终端安全管理系统成功处置了一起APT攻击事件。

    知道创宇云安全
  • 对某单位的 APT 攻击样本分析

    在六月份的某单位HW行动中,知道创宇HW安全团队通过创宇云图APT威胁感知系统并结合腾讯御点终端安全管理系统成功处置了一起APT攻击事件。

    Seebug漏洞平台
  • LiSSS (Literary Spanish Sentences Sentiment):用于情感检测的语料库(CS CL)

    在本文中,我们提出了一个新的创新计算(CC)领域小型语料库,名为Literary Spanish Sentences Sentiment (LISSS),通过对...

    Elva

扫码关注云+社区

领取腾讯云代金券