评估一个语言模型的好坏一般有两种方案:第一种是外部评估,即将语言模型嵌入到一个外部应用中去,然后观察该语言模型是否对那个应用的性能有所提升;第二种是内部评估,即寻找一个语料测试集,观察语言模型运用到此测试集上的建模效果如何。第一种方案耗费较大,这里我们只讲第二种内部评估方案。

 
那么如何去衡量一个训练好的语言模型在测试集上的建模效果呢?答案很简单,对于一个语言模型,我们认为当用它去计算一个测试集的概率,所得到的概率越大,就说明这个语言模型对该测试集的建模效果越好。

 
当然,如何选取测试集也是有讲究的,需要注意的主要有以下几点:

 
1.如前所述(关于防止过拟合的一些想法),测试集不能与训练集有交叉,如果测试集也参与了训练过程,那么就相当于人为地为语言模型对测试集赋予了更高的概率,这种测试结果是不准确的,会使得后面将要提到的perplexity变高;


2.我们知道一个语言模型(如N-gram)是依赖于训练语料库的,如果我们用来评价语言模型的测试集与原始的训练集没有一些共同的“基因”,那么这种测试也是没有价值的。例如,我们拿莎士比亚的作品集去训练一个语言模型,再去用华尔街日报上的文字来评估这个语言模型是不合理的,二者文风、用词显然有很大差别。


对于测试集选择好了,如何去用一个训练好的语言模型去建模它从而评估语言模型呢?直观上看,我们只用去拿这个语言模型去计算测试集的概率就好,但是一般不会用这个原始概率来评估,取而代之的是计算该语言模型在测试集上的perplexity变量值,简称PP。


从数学上看,一个语言模型在测试集上的perplexity是测试集概率的归一化倒数。假设对于一个包含N个单词的测试集T=w1w2…wN,那么PP(T)的计算公式如下:

PP(T)=P(w1w2…wn)^(-1/N)

借助概率公式的链式法则,上式可以化为:

PP(T)={P(w1)*P(w2|w1)*…*P(wN|w1w2…wN-1)}^(-1/N)

如果使用的是bigram模型,上式可以进一步化为:

PP(T)={P(w1)*P(w2|w1)*…*P(wN|wN-1)}^(-1/N)

 
观察上式可以发现,如果语言模型在测试集上得出的概率越大,那么其PP值就越小,因此,对于一个语言模型,使得概率最大化也就相当于让PP最小化。

 
我们可以换一个角度去看待PP值,PP值实际上是对于条件概率的一种加权平均,而对于一个语言来说,就是它的分支因子。通俗一点讲,对于一个语言中的任意一个单词,紧接它的下一个单词有多少种可能性。例如,设想有这样一种语言,它的所有单词是zero,one,two,three,four,five,six,seven,eight,nine并且每个单词出现概率是相同的,均为0.1,那么很容易计算出这个迷你语言的PP值为10;但是如果稍微改变一下它的分支因子,如果单词one出现的频率相比其他九个单词比较大,这个时候该语言的PP值就会降低了,因为大部分时候下一个单词很有可能都是one。

 
最后,我们再看一下PP值为什么会是概率的归一化倒数。前面提到过(决策树(Decision Tree)),熵是用来描述所含信息量的多少,并且我们介绍过熵的公式:

H(X)=-SUM[p(x)*log2_P(x)],其中x属于X集合

这里的对数实际上可以以任何数为底,不过这里我们取2为底,于是,熵的值就可以用比特去衡量。

 
在信息论中,熵可以被认为是信息最优编码方案中的比特数下限。例如,加入现在有1~8共八个数字,如果我们每次用计算机比特编码向别人传达一个数字,一种最简单的编码就是使用三位比特数去编码它们,分别是1用001,2用010,3用011,…,8用000,也就是说,每次传送均需要传送3个比特。

 
但是,假如我们已经知道了每个数字出现的先验概率分别是1/2,1/4,1/8,1/16,1/64,1/64,1/64,1/64,那么这个时候平均每次需要多少个比特呢?我们可以借助熵的公式来计算:

H(X)=-1/2*log2_(1/2)-…-1/64*log2_(1/64)=2

也就是说在知道符合上述的先验概率情况下,平均每次只要传送2比特,那么我们可以对于出现概率比较大的数字采取短的编码方案,而对于出现概率比较小的数字采取长的编码方案。例如对于数字1,我们可以用0去编码,剩下的数字使用10,110,1110,111100,111101,111110,111111去编码。


那么之前第一种方案为什么平均每次需要传送3个比特呢?这也很好计算,原因是我们不知道每个数字出现的先验概率,因此我们默认每个数字出现的概率均为1/8,根据熵的公式,我们可以计算此时需要的比特数为:

H(X)=8*[-1/8*log2_(1/8)]=3


前面我们只计算了单个变量的熵,但是在语言中我们要处理的往往是一段序列,对于一个语言,我们需要计算它的熵,假设它有单词集{w0,w1,…,wn},那么我们可以计算这个语言中的所有长度为n的随机序列:

H(w0,w1,…wn)=SUM[-p(w1_n)log(w1_n)],其中w1_n是属于该语言中的任一个长度为n序列。

我们也可以计算单词熵,即

1/n*H(w1_n)=-1/n* SUM[-p(w1_n)log(w1_n)]
 

但是要想衡量一个语言的熵,我们需要去考虑无限长度的序列。我们可以把语言当做一个随机过程L,其产生很多序列的单词,它的单词熵为:

H(L)=-lim(n->infinity){1/n*H(w1,w2,…,wn)}

=-lim(n->infinity){1/n*SUM[p(w1,w2,…,wn)*log(p(w1,w2,…,wn))]}


根据香农定理,如果一个过程(这里是指一门语言)是稳定的、各态历经性的,于是,我们可以计算一个足够长的序列而不用对所有可能的序列去求和,那么上式可以简化为:

H(L)= lim(n->infinity){1/n*log(p(w1,w2,…,wn))}

(语言的稳定性是指语言具有时移不变性,但是语言一般都不是稳定的,语言的变化与空间和时间均有关联,因此,这里我们的统计模型只能是一个方便的简化。)


因此,我们可以借助一个足够长的输出序列来对语言这个随机过程的熵进行近似。接下来我们引入交叉熵,交叉熵的意思是当我们遇到一堆数据而不知道数据背后的实际分布p是什么样的时候,我们可以寻找一个分布m去模拟它,那么m对于p的交叉熵公式为:

H(p,m)= lim(n->infinity){1/n*p(w1,w2,…,wn)*log(m(w1,w2,…,wn))]}

同样,根据香农定理,上式可以简化为:

H(p,m)= lim(n->infinity){1/n*log(m(w1,w2,…,wn)]}


交叉熵为什么有用呢?因为交叉熵是真实熵的一个近似,准确来说是它的一个上限逼近,即:

H(p)<=H(p,m)

也就是说我们可以使用一个简化的模型m去估计由p产生的一段序列的真实熵。如果所找寻的m越准确,那么交叉熵H(p,m)就越接近于真实熵H(p),换句话说,交叉熵H(p,m)的值就越小。


最后,我们来看一下交叉熵与语言的perplexity有什么关联。由于交叉熵中是一种极限状态,我们使用一段固定长度为N的序列去逼近交叉熵,即

H(W)=-1/N*log(P(w1,w2,…,wN))

对上式取以2为底的指数,则得到

Perplexity(W)=2^[H(W)]=P(w1,w2,…,wN)^(-1/N)=[P(w1)*P(w2|w1)*…*P(wN|w1w2…wN-1)]^(-1/N)

 
可以看到,一个语言模型的Perplexity值实际上是其交叉熵的指数形式。
 
 
 
 
 
来源:张泽旺 深度学习每日摘要
智造家