一、最大熵模型
1、模型
根据最大熵定义,我们可以将最大熵模型的学习看成约束最优化问题
给定训练数据
T=(x1,y1),.....,(xn,yn)
,以及特征函数(就是一个个特征,不要想得太复杂)
fi(x,y)
。
特征函数定义f(x,y)
f(x,y)={1 x,y不满足某事实。比如训练数据中出现x:sunny,y:bad,那么f(sunny,bad)=10 x,y不满足某事实。比如训练数据中没有出现x:sunny,y:bad,那么f(sunny,bad)=0
特征函数f(x,y)关于经验分布P(x,y)的期望值
Ep⃗ (f)=∑(x,y)p⃗ (x,y)f(x,y)
Ep(f)=∑(x,y)p⃗ (x)p(y|x)f(x,y)
这个p(y|x)是上一轮学到的模型。
我们需要保证
Ep⃗ (fi)=Ep(fi)
满足这一条件的模型p很多,设为集合P。
即
{p∈P|Ep⃗ (fi)=Ep(fi)}
模型的构建思路来源于最大熵的原理。
- 熵的定义
假设离散随机变量X的概率分布是P(X),则其熵是
H(P)=−∑xP(x)logP(x)
由上式可知 0≤H(P)≤log∥X∥ ,即当X分布式均匀分布式右边等号成立。这就是说,当X服从均匀分布时,熵最大。 最大熵模型的构建
假设满足所有约束条件的模型集合为 C≡{p∈P|Ep⃗ (fi)=Ep(fi)} 定义在条件概率分布P(Y|X)上的条件熵为 H(P)=−∑x,yp⃗ (x)p(y|x)logP(y|x) 则模型集合C中条件熵H(P)最大的模型就是最大熵模型。最大熵模型的学习
maxp∈C H(P)=−∑x,yp⃗ (x)p(y|x)logP(y|x)
s.t Ep⃗ (fi)=Ep(fi)
∑yP(y|x)=1
求解约束最优化,所得到的解就是最大熵模型学习的解。
推导大致流程(统计学习方法上有详细推导)
1. 首先引入拉格朗日乘子 w0,w1,..,wn ,定义拉格朗日函数L(P,w)=…
2. 最优化原始问题 minP∈CmaxwL(P,w) 变为对偶问题 maxwminP∈CL(P,w) 。即我先推导出满足集合C的模型,再根据训练数据学习出模型的参数
3. Ψ(w)=minP∈C 是无约束问题,直接求偏导,可以得到模型 Pw=argminp∈CL(P,w)=P(y|x)=exp(...)
4. 然后在求解对偶问题外部极大化问题 maxwΨ(w) 。即 w=argmaxwΨ(w)有意思的一点
对偶形式和最大似然的出的目标函数一样。
似然函数 Lp⃗ (Pw)=log∏x,yp(y|x)p⃗ (x,y) 和 Ψ(w) 一样。所有求对偶外部极大化问题可以转换成对上述求得的模型最大似然估计(代码也就好写了)
对目标函数优化,常用方法:IIS,随机梯度,BFGS, LBFGS等代码:
#coding=utf-8
'''
Created on 2016��1��12��
@author: qf
'''
from _collections import defaultdict
import math
from ensurepip import __main__
import codecs
class MaxEnt(object):
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
self.samples = []
self.labels = []
self.N = 0
self.M = 0 #特征数量
self.lambdas = []
self.last_lambdas = []
self.current_lambdas = []
self.C = 0
self.stepValues = [];
self._ep_ = [] #
self._ep = []
self.numXY = defaultdict(int)
self.featureId_map = {}
self.Y = []
def fit(self,trainX,trainY,iterNum=100):
self.samples = trainX
self.labels = trainY
self.Y = set(trainY)
self.N = len(trainY)
# self.M = len(trainX[0])
# self.getC()
self.C = max([len(sample) for sample in trainX])
for id,sample in enumerate(self.samples):
y = self.labels[id]
for x in set(sample):
self.numXY[(x,y)] += 1.0
self.M = len(self.numXY.keys())
self.train(iterNum)
def _EP_(self):
#self._ep_ = [xyCount/self.N for xyCount in self.numXY]
for id,xy in enumerate(self.numXY.keys()):
self._ep_.append(self.numXY[xy]/self.N)
self.featureId_map[xy] = id
print len(self._ep_)
def ZX(self,sample):
sumY = 0.0
for y in self.Y:
sum = 0.0
for x in sample:
if self.numXY.has_key((x,y)):
sum += self.current_lambdas[self.featureId_map[(x,y)]]
sumY += math.exp(sum)
return sumY
def pXY(self,sample):
pxy = 0.0
ZX_sum = self.ZX(sample)
result = []
for y in self.Y:
pxy_sum = 0.0
for x in sample:
if self.numXY.has_key((x,y)):
pxy_sum += self.current_lambdas[self.featureId_map[(x,y)]]
result.append((math.exp(pxy_sum)/ZX_sum,y))
return result
def _Ep(self):
self._ep = [0.0]*self.M
for sample in self.samples:
pxy = self.pXY(sample)
for p,y in pxy:
for x in sample:
if self.numXY.has_key((x,y)):
self._ep[self.featureId_map[(x,y)]] += p*1.0/self.N
def train(self,iterNum):
self.current_lambdas = [0.0]*self.M
print len(self.current_lambdas)
self._EP_()
for iter in range(iterNum):
#self.last_lambdas = self.current_lambdas
self._Ep()
for id,w in enumerate(self.current_lambdas):
# print id
self.current_lambdas[id] = w + 1.0/self.C*math.log(self._ep_[id]/self._ep[id])
print self.current_lambdas
def predict(self,testX):
X = testX
p = self.pXY(X)
print p
#def predict(self,testX):
def loadfile():
trainX = []
trainY = []
for line in codecs.open("./train",'r','utf-8').readlines():
trainY.append(line.strip().split("\t")[0])
trainX.append(line.strip().split("\t")[1:])
return trainX,trainY
if __name__ == "__main__":
maxEnt = MaxEnt()
trainX,trainY = loadfile()
maxEnt.fit(trainX,trainY,1000)
maxEnt.predict(["sunny", "hot", "high", "FALSE"])
maxEnt.predict(["sunny", "hot", "high", "True"])
maxEnt.predict(["overcast", "hot", "high", "FALSE"])###yes
maxEnt.predict(["sunny", "hot", "high", "FALSE"])
maxEnt.predict(["sunny", "hot", "high", "FALSE"])