困惑
假设我们有一个模型,该模型将一个英语句子作为输入,并给出一个概率分数,该分数对应于它是有效英语句子的可能性。我们想确定这个模型有多好。一个好的模型应该给有效的英语句子打高分,给无效的英语句子打低分。困惑度是一种常用的衡量标准,用于量化这种模型的“好”程度。如果一个句子 s 包含 n 个单词,则表示困惑
建模概率分布p(建立模型)
可以使用概率链规则进行扩展
所以给定一些数据(称为训练数据),我们可以计算出上述条件概率。然而,实际上这是不可能的,因为它需要大量的训练数据。然后我们假设计算
假设:所有单词都是独立的(unigram)
假设:一阶马尔可夫假设(二元组)
下一个词只取决于上一个词
假设:n阶马尔可夫假设(ngram)
下一个单词仅取决于前面的 n 个单词
MLE 估计概率
最大似然估计(MLE)是估计个体概率的一种方法
一元组
在哪里
比格拉姆
在哪里
count(w_{i-1}, w_i) 是单词 w_{i-1}, w_i 在训练数据中以相同序列(二元组)一起出现的次数
count(w_{i-1}) 是单词 w_{i-1} 在训练数据中出现的次数。 w_{i-1} 称为上下文。
计算困惑度
正如我们在上面看到的,$p(s)$ 是通过将许多小数相乘来计算的,因此由于计算机上浮点数的精度有限,它在数值上不稳定。让我们使用 log 的好属性来简单化它。我们知道
示例:Unigram 模型
训练数据 ["an apple", "an orange"]
词汇:[an, apple, orange, UNK]
MLE 估计
对于测试句“一个苹果”
l = (np.log2(0.5) + np.log2(0.25))/2 = -1.5
np.power(2, -l) = 2.8284271247461903
对于测试句“an ant”
l = (np.log2(0.5) + np.log2(0))/2 = inf
代码
import nltk
from nltk.lm.preprocessing import padded_everygram_pipeline
from nltk.lm import MLE
train_sentences = ['an apple', 'an orange']
tokenized_text = [list(map(str.lower, nltk.tokenize.word_tokenize(sent)))
for sent in train_sentences]
n = 1
train_data, padded_vocab = padded_everygram_pipeline(n, tokenized_text)
model = MLE(n)
model.fit(train_data, padded_vocab)
test_sentences = ['an apple', 'an ant']
tokenized_text = [list(map(str.lower, nltk.tokenize.word_tokenize(sent)))
for sent in test_sentences]
test_data, _ = padded_everygram_pipeline(n, tokenized_text)
for test in test_data:
print ("MLE Estimates:", [((ngram[-1], ngram[:-1]),model.score(ngram[-1], ngram[:-1])) for ngram in test])
test_data, _ = padded_everygram_pipeline(n, tokenized_text)
for i, test in enumerate(test_data):
print("PP({0}):{1}".format(test_sentences[i], model.perplexity(test)))
示例:Bigram 模型
火车数据:“一个苹果”、“一个橙子”
填充训练数据:“(s) an apple (/s)”、“(s) an orange (/s)”
词汇 : (s), (/s) an, apple, orange, UNK
MLE 估计
对于测试句“an apple” 填充:“(s) an apple (/s)”
l = (np.log2(p(an|<s> ) + np.log2(p(apple|an) + np.log2(p(</s>|apple))/3 =
(np.log2(1) + np.log2(0.5) + np.log2(1))/3 = -0.3333
np.power(2, -l) = 1.
对于测试句“an ant” 填充:“(s) an ant (/s)”
l = (np.log2(p(an|<s> ) + np.log2(p(ant|an) + np.log2(p(</s>|ant))/3 = inf
代码
import nltk
from nltk.lm.preprocessing import padded_everygram_pipeline
from nltk.lm import MLE
from nltk.lm import Vocabulary
train_sentences = ['an apple', 'an orange']
tokenized_text = [list(map(str.lower, nltk.tokenize.word_tokenize(sent))) for sent in train_sentences]
n = 2
train_data = [nltk.bigrams(t, pad_right=True, pad_left=True, left_pad_symbol="<s>", right_pad_symbol="</s>") for t in tokenized_text]
words = [word for sent in tokenized_text for word in sent]
words.extend(["<s>", "</s>"])
padded_vocab = Vocabulary(words)
model = MLE(n)
model.fit(train_data, padded_vocab)
test_sentences = ['an apple', 'an ant']
tokenized_text = [list(map(str.lower, nltk.tokenize.word_tokenize(sent))) for sent in test_sentences]
test_data = [nltk.bigrams(t, pad_right=True, pad_left=True, left_pad_symbol="<s>", right_pad_symbol="</s>") for t in tokenized_text]
for test in test_data:
print ("MLE Estimates:", [((ngram[-1], ngram[:-1]),model.score(ngram[-1], ngram[:-1])) for ngram in test])
test_data = [nltk.bigrams(t, pad_right=True, pad_left=True, left_pad_symbol="<s>", right_pad_symbol="</s>") for t in tokenized_text]
for i, test in enumerate(test_data):
print("PP({0}):{1}".format(test_sentences[i], model.perplexity(test)))