在学习最大熵模型时,令我最大的困惑点在于它一些公式的物理含义是什么!但发现,它在概率模型当中,除了一个最宏观的假设【无知信息最大熵】之外,没有发现任何有趣的现象。但更加神奇的一点在于,它的【特征函数】在某些特定的取值情况下,能够回归到逻辑斯蒂回归模型的一般表现形式上,这无形之中让我对最大熵的解产生了无比的好奇,似乎又能联系到一些什么。
本文重点在于自己对公式的理解,是我个人的学习笔记。是在我世界观中所形成的最大熵模型,因此由于知识局限性,如有不当,请指正。知识准备:需了解最大熵的概念、模型最优化方法、基本高等数学。
在《统计学习方法》中第六章第二节中,关于最大熵模型的阐述已经很明确了,此处不在重复,有兴趣的可以参考书本P80页的内容。在这里直接写出最大熵模型的核心公式。
假设随机变量X的概率分布是 P(X) ,则其熵是:
熵最大其实是现实生活中很常见的物理现象,原本是用来描述某一现象的不确定程度,但在此处却被用来作为【无知信息】的假设。对于熵的理解,可以参看【知乎-能否尽量通俗地解释什么叫做熵?】,以及纪录片【宇宙的奇迹:时间之箭】
直观地,最大熵原理认为要选择的概率模型首先必须满足已有的事实,即约束条件。在没有更多信息的情况下,那些不确定的部分是“等可能的”。最大熵原理通过熵的最大化来表示等可能性。“等可能”不容易操作,而熵则是一个可优化的数值指标。
这段话告诉我们一个什么道理?我们可以根据【已有信息】来明确某些现象的【和概率】,从而把收集到的数据分成【符合特殊条件的现象】和【不符合特殊条件的现象】,建立了约束条件之后,我们根据【无知信息最大化熵】的假设来分别对分类现象进行【等可能处理】,进一步得到所有现象出现的概率,从而能够用于预测分类。
书中例子:
假设随机变量X有5个取值{A,B,C,D,E},要估计取各个值的概率 P(A),P(B),P(C),P(D),P(E).
现在满足约束条件:
P(A)+P(B)=310
P(A)+P(B)+P(C)+P(D)+P(E)=1
满足这两个约束条件的概率分布仍然有无穷多个。在缺少其他信息的情况下,可以认为A与B是等概率的,C,D和E是等概率的,于是,
最大熵的原理已经阐述完毕了,A和B的和概率即为我们的已知信息,有了已知信息,自然就有了另外一部分的未知信息,但不管怎么样,我们都让已知信息中和未知信息中各类出现的概率相同,这就是我们的最大熵等可能性原理。
咱们再进一步!上述问题的求解是相当简单的,但有想过这两个约束条件是怎么得来的嘛?或者说如何从数据中抽取出来?这就需要一定的数学基础了。
好了,我们开始机器学习中最大熵模型的特征提取原理和预测分类原理的学习。既然是从【已有数据】中获取【约束信息】,那么我们首先定义我们的数据集样本。
假设分类模型是一个条件概率分布 P(Y|X),X∈X⊆Rn 表示输入, Y∈Y 表示输出, X和Y 分别是输入和输出的集合。这个模型表示的是对于给定的输入X,以条件概率 P(Y|X)输出Y 。
给定一个训练数据集
首先考虑模型应该满足的条件。给定训练数据集,可以确定联合分布 P(X,Y) 的经验分布和边缘分布 P(X) 的经验分布,分别以 P^(X,Y)和P^(X) 表示。这里,
其中, v(X=x,Y=y) 表示训练数据中样本 (x,y) 出现的频数, v(X=x) 表示训练数据中输入 x 出现的频数, N 表示训练样本容量。
从上述数据集中可以看出,如果随机变量 x和y 分别代表的是某个样例的 xn和yn ,那么它实际是没有做任何特征抽取工作的。比如说,在给定的数据样本中,输入集合存在唯一性,那么不存在某两个数据样例中的 xi=xj ,由此极端情况下可以推得:
那么所导致的结果就是对任何数据, P(X=xj,Y=yj)=1N ,所有唯一现象出现的概率是等可能的。这的确是最大熵的等可能性理论中最特殊的情况,那如果在给定数据样例中,有相同特征时,会出现什么情况呢?现在假设存在某两个或几个样例,随机变量 X和Y ,均为 xj和yj ,由此得:
看到这里我就想了,我去,这不就是求某个输入 x 与某个输出 y 在给定数据中出现次数最多情况下的 yj 么,这模型也太简单了吧,预测能力能上去么?显然这种预测模型是相当低级的,所以最大熵模型引入了一个【特征函数】的概念,有了这东西,这泛化能力就上去了。咱们进一步,看书中关于特征函数的介绍。
用特征函数 f(x,y) 描述输入x和输出y之间的某一个事实。其定义如下:
为什么引入了特征函数后,模型就有了很强的泛化能力?它对数据集做了什么样的【特征提取】导致它的预测能力显著上升?或者数学上对最大熵模型做了哪些约束?
这里就需要明确特征函数 f(x,y) 中 (x,y) 的含义是什么,这点非常关键。可以参看一篇知乎回答【关于最大熵模型的严重困惑:为什么没有解析解?】
拿情感分类举个栗子:
但事实上我们可以:
所以说,在给定数据集中,可以出现大量样例 (xi,yj) 符合特征函数 f(x,y) 的情况。假定只给定了一个特征函数,那么我们可以把数据样例分类成两类【符合特征函数的样例】和【不符合特征函数的样例】,由此得:
为了更好地描述经验分布与特征函数的关系,书中用 Ep^(f) 表示:
说白了,无非是用数据中的【经验信息】去拟合系统所假设的模型,但它并没有拿数据去拟合模型,而是用数据生成约束条件,最大熵模型根据约束条件求似然方程的解,从而估计出模型的参数。
定义:
最大熵模型
假设满足所有约束条件的模型集合为:
C≡{P∈P|Ep(fi)=Ep^(fi),i=1,2,...,n}
定义在条件概率分布 P(Y|X) 上的条件熵为:
H(P)=−∑x,yP^(x)P(y|x)logP(y|x)
则模型集合 C 中条件熵 H(P) 最大的模型成为最大熵模型。式中的对数为自然对数。
这是最大熵模型的终极表达式,个人觉得最大的亮点在于对特征函数的使用。它使得最大熵模型有了泛化能力,就好像一个筛子一样,不断地在数据集中划分为【已知信息】和【未知信息】两类,一个特征对应于一个筛子,当筛子足够多时,给定某个输入 x 时,所能筛选出的 y 将越精准。
最大熵的数学模型已经介绍完毕了,接下来是对其求极大值的推导过程,从理论上来实际操作一把。它的学习过程就是求解最大熵模型的过程,最大熵模型的学习可以形式化为约束最优化问题。
对于给定的训练数据集 T={(x1,y1),(x2,y2),...,(xn,yn)} 以及特征函数 fi(x,y),i=1,2,...,n, 最大熵模型的学习等价于约束最优化问题:
首先,引进拉格朗日乘子 w0,w1,w2,...,wn ,定义拉格朗日函数 L(P,w) :
之后,求解对偶问题外部的极大化问题
以下内容摘自博文【码农场-逻辑斯谛回归与最大熵模型】
常用的方法有改进的迭代尺度法、梯度下降法、牛顿法或拟牛顿法,牛顿法或拟牛顿法一般收敛速度更快。
GIS的算法流程如下:
1.初始化所有 wi 为任意值,一般可以设置为0,即:
w(0)i=0,i∈{1,2,3,...,n}
其中 w 的上标(t)表示第t轮迭代,下标 i 表示第 i 个特征,n是特征总数。
2.重复下面的权值更新直至收敛:
w(t+1)i=w(t)i+1ClogEp^(fi)Ep(n)(fi),i∈{1,2,...,n}
收敛的判断依据可以是 wi 前后两次的差足够小。其中C一般取所有样本数据中最大的特征数量。
最原始的最大熵模型的训练方法是一种称为通用迭代算法 GIS(generalized iterative scaling) 的迭代 算法。GIS 的原理并不复杂,大致可以概括为以下几个步骤:
假定第零次迭代的初始模型为等概率的均匀分布。
用第 N 次迭代的模型来估算每种信息特征在训练数据中的分布,如果超过了实际的,就把相应的模型参数变小;否则,将它们便大。
重复步骤 2 直到收敛。
一份简明的Python实现:
import sys
import math
from collections import defaultdict
class MaxEnt:
def __init__(self):
self._samples = []
self._Y = set([]) # 标签集合,相当于去重之后的y
self._numXY = defaultdict(int) # key是(xi,yi)对,value是count(xi,yi)
self._N = 0 #样本数量
self._n = 0 #特征对(xi,yi)总数量
self._xyID = {} # 对(x,y)对做的顺序编号(ID),key是(xi,yi)对,value是ID
self._C = 0 # 样本最大的特征数量,用于求参数的迭代,见IIS原理说明
self._ep_ = [] # 样本分布的特征期望值
self._ep = [] # 模型分布的特征期望值
self._w = [] # 对应n个特征的权值
self._lastw = [] # 上一轮迭代的权值
self._EPS = 0.01 # 判断是否收敛的阈值
def load_data(self,filename):
for line in open(filename,"r"):
sample = line.strip().split("\t")
if len(sample) < 2: #至少:标签+一个特征
continue
y = sample[0]
X = sample[1:]
self._samples.append(sample)
self._Y.add(y)
for x in set(X):
self._numXY[(x,y)] += 1
def _initparams(self):
self._N = len(self._samples)
self._n = len(self._numXY) # 没有做任何特征提取操作,直接操作特征
self._C = max([len(sample) -1 for sample in self._samples])
self._w = [0.0] * self._n
self._lastw = self._w[:]
self._sample_ep()
def _convergence(self):
for w,lw in zip(self._w,self._lastw):
if math.fabs(w-lw) >= self._EPS:
return False
return True
def _sample_ep(self):
self._ep_ = [0.0] * self._n
for i,xy in enumerate(self._numXY):
self._ep_[i] = self._numXY[xy] * 1.0 / self._N
self._xyID[xy] = i
def _zx(self,X):
# calculate Z(x)
ZX = 0.0
for y in self._Y:
sum = 0.0
for x in X:
if(x,y) in self._numXY:
sum += self._w[self._xyID[(x,y)]]
ZX += math.exp(sum)
return ZX
def _pyx(self,X):
ZX = self._zx(X)
results = []
for y in self._Y:
sum = 0.0
for x in X:
if(x,y) in self._numXY:
sum += self._w[self._xyID[(x,y)]]
pyx = 1.0 / ZX * math.exp(sum)
results.append((y,pyx))
return results
def _model_ep(self):
self._ep = [0.0] * self._n
for sample in self._samples:
X = sample[1:]
pyx = self._pyx(X)
for y,p in pyx:
for x in X:
if(x,y) in self._numXY:
self._ep[self._xyID[(x,y)]] += p * 1.0 / self._N
def train(self,maxiter = 1000):
self._initparams()
for i in range(0,maxiter):
print("Iter:%d...."%i)
self._lastw = self._w[:]
self._model_ep()
for i,w in enumerate(self._w):
self._w[i] += 1.0 / self._C * math.log(self._ep_[i] / self._ep[i])
print(self._w)
if self._convergence():
break
def predict(self,inp):
X = inp.strip().split("\t")
prob = self._pyx(X)
return prob
if __name__ == "__main__":
maxent = MaxEnt()
maxent.load_data('data.txt')
maxent.train()
print (maxent.predict("sunny\thot\thigh\tFALSE"))
print (maxent.predict("overcast\thot\thigh\tFALSE"))
print (maxent.predict("sunny\tcool\thigh\tTRUE"))
sys.exit(0)
其中测试数据为:
no sunny hot high FALSE
no sunny hot high TRUE
yes overcast hot high FALSE
yes rainy mild high FALSE
yes rainy cool normal FALSE
no rainy cool normal TRUE
yes overcast cool normal TRUE
no sunny mild high FALSE
yes sunny cool normal FALSE
yes rainy mild normal FALSE
yes sunny mild normal TRUE
yes overcast mild high TRUE
yes overcast hot normal FALSE
no rainy mild high TRUE
《统计学习方法》关于IIS的理论推导写了一大堆,在博文【码农场-逻辑斯谛回归与最大熵模型】也全部推导过一遍了,所以具体的细节就不再赘述了。主要阐述下IIS的基本思想,以及重点关注代码的编写。
首先,我们已经明确了最大熵模型的条件概率表达式 Pw(y|x) ,由此得整个模型的对数似然函数为:
于是IIS的一个想法是:既然我不知道原始参数如何变化,我就给你一个变化量 δ ,对于每个参数 wi 对应于一个变化量为 δi ,所以,上述式子就可以表示为:
所以该问题就变成了研究 A(δ|w) 的极值了,所以我们把 A(δ|w) 的表达式写出来吧,分别代入 w+δ和w 得:
将右端记为:
为什么是不等式,因为原式无法对它进一步求导分析,所以需要近似的下界,来缓解这种尴尬的情况。但当我们得到 A(δ|w) 时,对它继续求导试试,你会发现同样的问题,参数 δi 关联了其他参数 δ ,依然无法独立,所以咱们得想办法进一步降低下界,呵呵,书中引进了计数函数 f#(x,y)=∑ni=1fi(x,y) ,再利用Jensen不等式完美的解决了这个问题。于是,经过层层推导,下界再一次被刷新!即为:
你会发现一个神奇的现象,原本在 exp 中的所有参数累加,在最新的下界中消失不见了,没错,这就是神奇的jessen不等式功效,然后你再对 B(δ|w) 求偏导试试,呵呵,所以的参数能够独立计算了,于是我们得到一个迭代更新公式:
最大熵IIS训练算法的Java实现
Fork自https://github.com/tpeng/maxent ,经过实测,hankcs所给的数据训练准确率可达0.7619。
文初说到了,逻辑斯蒂回归模型其实是最大熵模型的特殊形式,这是怎么回事呢?我们可以从最大熵模型来推得逻辑斯蒂回归模型,我们只需要改变的是特征函数 f(x,y) ,详细的可以参看知乎回答【如何理解最大熵模型里面的特征?】。
但令我好奇的是,为什么最大熵模型对特征函数进行特殊化后便能够推出逻辑斯蒂回归模型呢?关于逻辑斯蒂回归模型的物理含义挺清楚,可最大熵模型是否对特征有着同样的物理含义呢?个人觉得还是得从特征函数入手,最大熵模型中 f(x,y)=1or 0 ,所代表的是 y 在 x 这样的条件下能否出现,而这种条件并不像逻辑斯蒂回归模型中的随机变量【连续时间】那样属于连续值,所以自然特征函数只能简单的以0和1划分条件成立与否。这点和逻辑斯蒂回归模型又有几分相像。但猜测毕竟是猜测,仅供参考。