最大熵模型(maximum entropy model)可以用于二分类,也可以用于多分类。其是由最大熵原理推导实现的,所以讲最大熵模型时,绕不开最大熵原理。
什么是最大熵原理?
最大熵原理认为,学习概率模型时,在所有可能的概率模型(概率分布)中,熵最大的模型就是最好的模型。最大熵原理通常表述为在满足约束条件的模型集合中选取熵最大的模型。
假设离散随机变量 X X X的概率分布是 P ( X ) P(X) P(X),则其熵为:
H ( P ) = − ∑ x P ( x ) log P ( x ) H(P)=-\sum_{x}P(x)\log P(x) H(P)=−x∑P(x)logP(x)
且满足下列不等式:
0 ≤ H ( P ) ≤ log ∣ X ∣ {0 \leq H(P) \leq \log \left|X \right|} 0≤H(P)≤log∣X∣
上式中, ∣ X ∣ \left|X\right| ∣X∣是 X X X的取值个数,右边等号成立的条件是 X X X的分布是均匀分布。这就说明当 X X X服从均匀分布时,熵能取得最大值。
最大熵模型是由以下条件概率分布表示的分类模型:
P w ( y ∣ x ) = 1 Z w ( x ) exp ( ∑ i = 1 n w i f i ( x , y ) ) {P_w(y|x)=\frac{1}{Z_{w}(x)}\exp \left(\sum_{i=1}^{n} w_i f_i(x,y)\right)} Pw(y∣x)=Zw(x)1exp(i=1∑nwifi(x,y))
Z w ( x ) = ∑ y exp ( ∑ i = 1 n w i f i ( x . y ) ) {Z_w(x)=\sum_{y}\exp \left(\sum_{i=1}^{n} w_i f_i(x.y)\right)} Zw(x)=y∑exp(i=1∑nwifi(x.y))
其中, Z w ( x ) Z_w(x) Zw(x)是规范化因子, f i f_i fi为特征函数, w i w_i wi为特征的权值。
最大熵原理应用到分类模型学习中,由以下约束最优化问题:
min − H ( P ) = ∑ x , y P ~ ( x ) P ( y ∣ x ) log P ( y ∣ x ) {\min -H(P)=\sum{x,y}\tilde{P}(x)P(y|x)\log P(y|x)} min−H(P)=∑x,yP~(x)P(y∣x)logP(y∣x)
s . t P ( f i ) − ( ~ P ) ( f i ) = 0 , i = 1 , 2 , . . . , n {s.t \quad P(f_i)- \tilde(P)(f_i)=0,\quad i=1,2,...,n} s.tP(fi)−(~P)(fi)=0,i=1,2,...,n
∑ y P ( y ∣ x ) = 1 {\sum_{y}P(y|x)=1} y∑P(y∣x)=1
求解上述最优化问题的对偶问题便可得到最大熵模型。
案例代码已上传:Github地址
import math
from copy import deepcopy
第一步:定义最大熵模型
class MaxEntropy:
def __init__(self, EPS=0.005):
self._samples = []
self._label_y = set() # 标签集合,相当去去重后的y
self._numXY = {} # key为(x,y),value为出现次数
self._samples_num = 0 # 样本数
self._Ep_ = [] # 样本分布的特征期望值
self._xyID = {} # key记录(x,y),value记录id号
self._xy_num = 0 # 特征键值(x,y)的个数
self._max_feature_num = 0 # 最大特征数
self._IDxy = {} # key为(x,y),value为对应的id号
self._weights = []
self._EPS = EPS # 收敛条件
self._last_weights = [] # 上一次w参数值
def loadData(self, dataset):
self._samples = deepcopy(dataset)
for items in self._samples:
y = items[0]
X = items[1:]
self._label_y.add(y) # 集合中y若已存在则会自动忽略
for x in X:
if (x, y) in self._numXY:
self._numXY[(x, y)] += 1
else:
self._numXY[(x, y)] = 1
self._samples_num = len(self._samples)
self._xy_num = len(self._numXY)
self._max_feature_num = max([len(sample) - 1 for sample in self._samples])
self._weights = [0] * self._xy_num
self._last_weights = self._weights[:]
self._Ep_ = [0] * self._xy_num
for i, xy in enumerate(self._numXY): # 计算特征函数fi关于经验分布的期望
self._Ep_[i] = self._numXY[xy] / self._samples_num
self._xyID[xy] = i
self._IDxy[i] = xy
# 计算每个Z(x)值
def _calc_zx(self, X):
zx = 0
for y in self._label_y:
temp = 0
for x in X:
if (x, y) in self._numXY:
temp += self._weights[self._xyID[(x, y)]]
zx += math.exp(temp)
return zx
# 计算每个P(y|x)
def _calu_model_pyx(self, y, X):
zx = self._calc_zx(X)
temp = 0
for x in X:
if (x, y) in self._numXY:
temp += self._weights[self._xyID[(x, y)]]
pyx = math.exp(temp) / zx
return pyx
# 计算特征函数fi关于模型的期望
def _calc_model_ep(self, index):
x, y = self._IDxy[index]
ep = 0
for sample in self._samples:
if x not in sample:
continue
pyx = self._calu_model_pyx(y, sample)
ep += pyx / self._samples_num
return ep
# 判断是否全部收敛
def _convergence(self):
for last, now in zip(self._last_weights, self._weights):
if abs(last - now) >= self._EPS:
return False
return True
#计算预测概率
def predict(self, X):
Z = self._calc_zx(X)
result = {}
for y in self._label_y:
ss = 0
for x in X:
if (x, y) in self._numXY:
ss += self._weights[self._xyID[(x, y)]]
pyx = math.exp(ss) / Z
result[y] = pyx
return result
#训练
def train(self, maxiter=1000):
for loop in range(maxiter):
print("迭代次数:%d" % loop)
self._last_weights = self._weights[:]
for i in range(self._xy_num):
ep = self._calc_model_ep(i) # 计算第i个特征的模型期望
self._weights[i] += math.log(self._Ep_[i] / ep) / self._max_feature_num # 更新参数
print("权值:", self._weights)
if self._convergence(): # 判断是否收敛
break
第二步:构建数据
dataset = [['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']]
第三步:训练
maxent = MaxEntropy()
x = ['overcast', 'mild', 'high', 'FALSE']
maxent.loadData(dataset)
#设置迭代次数 100
maxent.train(maxiter=100)
print('预测结果:', maxent.predict(x))
预测结果: {‘no’: 0.0015600198042872487, ‘yes’: 0.9984399801957127}
案例代码已上传:Github地址
参考资料:
[1] 《统计学习方法》
[2]: fuqiuai实现的最大熵原理
Github地址https://github.com/Vambooo/lihang-dl
更多技术干货在公众号:深度学习学研社