最大熵模型

一、最大熵模型
1、模型
根据最大熵定义,我们可以将最大熵模型的学习看成约束最优化问题
给定训练数据 T=(x1,y1),.....,(xn,yn) ,以及特征函数(就是一个个特征,不要想得太复杂) fi(x,y)
特征函数定义f(x,y)
f(x,y)={1   x,yx:sunny,y:badf(sunny,bad)=10   x,yx:sunny,y:badf(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。
{pP|Ep⃗ (fi)=Ep(fi)}
模型的构建思路来源于最大熵的原理。

  1. 熵的定义
    假设离散随机变量X的概率分布是P(X),则其熵是
    H(P)=xP(x)logP(x)
    由上式可知 0H(P)logX ,即当X分布式均匀分布式右边等号成立。这就是说,当X服从均匀分布时,熵最大。
  2. 最大熵模型的构建
    假设满足所有约束条件的模型集合为 C{pP|Ep⃗ (fi)=Ep(fi)} 定义在条件概率分布P(Y|X)上的条件熵为 H(P)=x,yp⃗ (x)p(y|x)logP(y|x) 则模型集合C中条件熵H(P)最大的模型就是最大熵模型。

  3. 最大熵模型的学习
    maxpC 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. 最优化原始问题 minPCmaxwL(P,w) 变为对偶问题 maxwminPCL(P,w) 。即我先推导出满足集合C的模型,再根据训练数据学习出模型的参数
    3. Ψ(w)=minPC 是无约束问题,直接求偏导,可以得到模型 Pw=argminpCL(P,w)=P(y|x)=exp(...)
    4. 然后在求解对偶问题外部极大化问题 maxwΨ(w) 。即 w=argmaxwΨ(w)

  4. 有意思的一点
    对偶形式和最大似然的出的目标函数一样。
    似然函数 Lp⃗ (Pw)=logx,yp(y|x)p⃗ (x,y) Ψ(w) 一样。所有求对偶外部极大化问题可以转换成对上述求得的模型最大似然估计(代码也就好写了)
    对目标函数优化,常用方法:IIS,随机梯度,BFGS, LBFGS等

  5. 代码:

#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"]) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值