語(yǔ)言模型:使用NLTK訓(xùn)練并計(jì)算困惑度和文本熵
Author: Sixing Yan
這一部分主要記錄我在閱讀NLTK的兩種語(yǔ)言模型源碼時(shí)滑燃,一些遇到的問(wèn)題和理解。
1. NLTK中訓(xùn)練語(yǔ)言模型MLE和Lidstone有什么不同
NLTK 中兩種準(zhǔn)備ngram
語(yǔ)言模型的方式:最大似然估計(jì)MLE
和平滑Lidstone
忱详。兩者的實(shí)現(xiàn)方式都是基于統(tǒng)計(jì)信息,而沒(méi)有進(jìn)行又梯度更新的估計(jì)。
(1)使用語(yǔ)言模型計(jì)算困惑度或文本熵
使用NLTK構(gòu)建了一個(gè)語(yǔ)言模型后聊浅,一般用于計(jì)算給定輸入的困惑度 perplexity 或者文本熵 entropy 暑椰。這兩個(gè)打分函數(shù)都調(diào)用了它們(MLE
和Lidstone
)父類(LanguageModel
)的score
函數(shù)霍转。
外部調(diào)用score
的時(shí)候,其實(shí)是通過(guò)各自的unmasked_score
函數(shù)計(jì)算的一汽,MLE
和Lid
有不同的unmasked_score
函數(shù)
def score(self, word, context=None):
"""Masks out of vocab (OOV) words and computes their model score.
For model-specific logic of calculating scores, see the `unmasked_score`
method.
"""
return self.unmasked_score(
self.vocab.lookup(word), self.vocab.lookup(context) if context else None
)
(2)lm.MLE
vs. lm.Lidstone
的區(qū)別和聯(lián)系
最大似然估計(jì)的核心方法
class MLE(LanguageModel):
"""Class for providing MLE ngram model scores.
Inherits initialization from BaseNgramModel.
"""
def unmasked_score(self, word, context=None):
"""Returns the MLE score for a word given a context.
Args:
- word is expcected to be a string
- context is expected to be something reasonably convertible to a tuple
"""
return self.context_counts(context).freq(word)
加k
平滑的核心方法
def unmasked_score(self, word, context=None):
"""Add-one smoothing: Lidstone or Laplace.
To see what kind, look at `gamma` attribute on the class.
"""
counts = self.context_counts(context)
word_count = counts[word]
norm_count = counts.N()
return (word_count + self.gamma) / (norm_count + len(self.vocab) * self.gamma)
這兩個(gè)模型都是用了LanguageModel.context_counts()
函數(shù)避消。
(3)現(xiàn)在探究一下兩者unmasked_score
函數(shù)都調(diào)用的context_counts
函數(shù)。
def context_counts(self, context):
"""Helper method for retrieving counts for a given context.
Assumes context has been checked and oov words in it masked.
:type context: tuple(str) or None
"""
return (
self.counts[len(context) + 1][context] if context else self.counts.unigrams
)
如果沒(méi)指定上下文的時(shí)候召夹,這個(gè)函數(shù)返回的是self.counts.unigrams
對(duì)象
那么再看self.counts
是什么:默認(rèn)情況下岩喷,是一個(gè)NgramCounter()
對(duì)象
self.counts = NgramCounter() if counter is None else counter
(4)所以我希望知道self.counts.unigrams.freq(word)
計(jì)算的是什么
self._counts[1] = self.unigrams = FreqDist()
可知,FreqDist().freq(word)
對(duì)應(yīng)是word
的頻率
word_count = counts[word]
的作用也是調(diào)取word
的頻率监憎,所以和FreqDist().freq(word)
的作用一樣纱意。
(5)結(jié)論
lm.MLE
是沒(méi)加平滑的 lm.Lidstone
方法
2. 另一個(gè)問(wèn)題:如何使用context
這個(gè)功能呢
從context_counts()
可知有context
的時(shí)候會(huì)返回 self.counts[len(context) + 1][context]
。
然后看self.counts = NgramCounter()
的[len(context) + 1][context]
調(diào)用鲸阔,它會(huì)使用self._counts
這個(gè)成員:
def __getitem__(self, item):
"""User-friendly access to ngram counts."""
if isinstance(item, int):
return self._counts[item]
elif isinstance(item, string_types):
return self._counts.__getitem__(1)[item]
elif isinstance(item, Sequence):
return self._counts.__getitem__(len(item) + 1)[tuple(item)]
self._counts = defaultdict(ConditionalFreqDist)
由此初始化偷霉,由第7行[len(word)][word]
取到同長(zhǎng)度下的該word
的頻數(shù)迄委。
而從counts = self.context_counts(context);word_count = counts[word]
這部分看,它返回一個(gè)Key-value型的數(shù)據(jù)類型类少,類似于如果context=(word1, word2);len(content)=2
叙身,那么應(yīng)該返回trigrams
的類實(shí)例(這里用了len(item)+1
做索引)。于是乎就是考慮了前面兩個(gè)詞硫狞,然后計(jì)量(word1, word2, word)
這個(gè)組合信轿。
(1)結(jié)論
使用context
不用改變?cè)蓄惖某跏蓟苯邮褂眉纯伞?/p>
NgramCounter
會(huì)根據(jù)輸入的List[Tuple]
的tuple
長(zhǎng)度len(tuple)
來(lái)設(shè)置ngram_order
的值(具體可看update()
函數(shù))妓忍。所以如果fit( )
的時(shí)候用的是ngrams(2)
的列表虏两,那么context
輸入的長(zhǎng)度就必須是1。
問(wèn)題是世剖,self.context_counts(context)
返回int
值定罢,它是怎么可以使用word_count = counts[word]
這種調(diào)用呢?因?yàn)樵谟?jì)算 entropy 的時(shí)候旁瘫,它會(huì)放入context
內(nèi)容祖凫。
具體實(shí)現(xiàn)細(xì)節(jié),參考
http://www.nltk.org/_modules/nltk/lm/models.html#Lidstone
http://www.nltk.org/_modules/nltk/lm/api.html#LanguageModel
http://www.nltk.org/_modules/nltk/lm/counter.html#NgramCounter
http://www.nltk.org/_modules/nltk/probability.html#FreqDist.freq