计算句子概率值的工具就是语言模型,但是随着句子长度的逐渐增大,语言模型会遇到下面两个问题:
为了解决这两个问题,可以使用马尔科夫假设来简化语言模型,这就是 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机器学习与深度学习算法 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!