
最大熵模型( Maximum Entropy Model, MaxEnt ) ,一种常用的判别式分类模型。
核心思想
在已知一些约束(特征在训练数据上的期望)时,在所有满足约束的分布里,选 熵最大 的那个------即在符合已知事实的前提下,对未知部分保持最大不确定性(不过度假设)。
直观上:约束用训练数据"钉住",其余尽量均匀、不过拟合。
模型形式
对输入
、类别
,条件概率为指数族形式:

:特征函数(代码里用特征是否出现等指示)
:特征权重(self.w)
:归一化因子,保证对
求和为 1
对应 notebook 的 _pw:对激活特征加权求和 → exp → 除以
得到各类概率。
训练在做什么
使特征在 经验分布 上的期望,等于在 模型 上的期望:
| 量 | 代码 | 含义 |
|---|---|---|
![]() |
_EP_hat_f | 特征对经验联合分布 的期望 |
![]() |
_EP_f | 特征对模型 的期望 |
用 改进的迭代尺度法 IIS 更新权重(fit 中的循环):

直到两类期望接近。
预测
对测试样本算各类
,取概率最大的类(predict 中 argmax)。
示例用鸢尾花多分类。
和相关模型的关系
| 对比 | 说明 |
|---|---|
| vs 朴素贝叶斯 | MaxEnt 是判别式 ;NB 是生成式 |
| vs 逻辑回归 | 二分类时 MaxEnt 与 LR 本质相近;MaxEnt 更强调特征函数与最大熵约束,易扩展到多分类/NLP 特征 |
一句话: 最大熵模型在满足特征约束下选最"均匀"的条件分布,用指数形式
建模,本 notebook 用 IIS 学权重并做多类分类。
应用场景
最大熵模型( MaxEnt ) 主要用在需要把多种特征融合成条件概率
的判别式分类场景,尤其在 NLP 里很常见:
| 领域 | 典型应用 |
|---|---|
| 自然语言处理 | 词性标注、命名实体识别、分词、句法分析中的分类决策 |
| 文本分类 | 情感分析、主题/意图分类、垃圾邮件判定 |
| 信息抽取 | 关系抽取、事件抽取中的类别判断 |
| 语音 / 多模态 | 作为声学或语言模型中的判别组件(经典系统中) |
| 一般机器学习 | 多分类、特征可灵活设计的结构化预测(与 CRF 等结合) |
适合的原因:
- 可灵活加入 n-gram、词典、上下文等特征函数
- 不强制特征独立(相对朴素贝叶斯更灵活)
- 二分类时与逻辑回归本质接近,易扩展到多类
现在: 很多 NLP 任务更多用神经网络 / Transformer;但 MaxEnt 仍是理解判别式模型与特征工程的经典方法,在特征明确、需可解释概率输出的场景仍可用。
** **代码** **
import pandas as pd
import numpy as np
from collections import defaultdict
class MaxEnt:
def __init__(self, max_iter=100):
# 训练输入
self.X_ = None
# 训练标签
self.y_ = None
# 标签类别数量
self.m = None
# 特征数量
self.n = None
# 训练样本量
self.N = None
# 常数特征取值
self.M = None
# 权重系数
self.w = None
# 标签名称
self.labels = defaultdict(int)
# 特征名称
self.features = defaultdict(int)
# 最大迭代次数
self.max_iter = max_iter
### 计算特征函数关于经验联合分布P(X,Y)的期望
def _EP_hat_f(self, x, y):
self.Pxy = np.zeros((self.m, self.n))
self.Px = np.zeros(self.n)
for x_, y_ in zip(x, y):
# 遍历每个样本
for x__ in set(x_):
self.Pxy[self.labels[y_], self.features[x__]] += 1
self.Px[self.features[x__]] += 1
self.EP_hat_f = self.Pxy/self.N
### 计算特征函数关于模型P(Y|X)与经验分布P(X)的期望
def _EP_f(self):
self.EPf = np.zeros((self.m, self.n))
for X in self.X_:
pw = self._pw(X)
pw = pw.reshape(self.m, 1)
px = self.Px.reshape(1, self.n)
self.EP_f += pw*px / self.N
### 最大熵模型P(y|x)
def _pw(self, x):
mask = np.zeros(self.n+1)
for ix in x:
mask[self.features[ix]] = 1
tmp = self.w * mask[1:]
pw = np.exp(np.sum(tmp, axis=1))
Z = np.sum(pw)
pw = pw/Z
return pw
### 熵模型拟合
### 基于改进的迭代尺度方法IIS
def fit(self, x, y):
# 训练输入
self.X_ = x
# 训练输出
self.y_ = list(set(y))
# 输入数据展平后集合
tmp = set(self.X_.flatten())
# 特征命名
self.features = defaultdict(int, zip(tmp, range(1, len(tmp)+1)))
# 标签命名
self.labels = dict(zip(self.y_, range(len(self.y_))))
# 特征数
self.n = len(self.features)+1
# 标签类别数量
self.m = len(self.labels)
# 训练样本量
self.N = len(x)
# 计算EP_hat_f
self._EP_hat_f(x, y)
# 初始化系数矩阵
self.w = np.zeros((self.m, self.n))
# 循环迭代
i = 0
while i <= self.max_iter:
# 计算EPf
self._EP_f()
# 令常数特征函数为M
self.M = 100
# IIS算法步骤(3)
tmp = np.true_divide(self.EP_hat_f, self.EP_f)
tmp[tmp == np.inf] = 0
tmp = np.nan_to_num(tmp)
sigma = np.where(tmp != 0, 1/self.M*np.log(tmp), 0)
# 更新系数:IIS步骤(4)
self.w = self.w + sigma
i += 1
print('training done.')
return self
# 定义最大熵模型预测函数
def predict(self, x):
res = np.zeros(len(x), dtype=np.int64)
for ix, x_ in enumerate(x):
tmp = self._pw(x_)
print(tmp, np.argmax(tmp), self.labels)
res[ix] = self.labels[self.y_[np.argmax(tmp)]]
return np.array([self.y_[ix] for ix in res])
1. 整体角色
实现一个多分类最大熵模型:
- 学权重
,得到 
- 训练用 IIS (改进的迭代尺度法)
- 预测取概率最大的类
2. init :成员变量
| 变量 | 含义 |
|---|---|
| X_, y_ | 训练特征、标签集合 |
| m | 类别数 |
| n | 特征维数(含占位) |
| N | 样本量 |
| M | IIS 里特征函数和的上界常数(这里写死 100) |
| w | 权重矩阵,形状约 (类别数, 特征数) |
| labels | 标签名 → 下标 |
| features | 特征值 → 下标 |
| max_iter | 最大迭代次数 |
3. _EP_hat_f :经验期望 
self.Pxyself.labels\[y_, self.featuresx__] += 1
self.Pxself.features\[x__] += 1
self.EP_hat_f = self.Pxy / self.N
- 遍历每个样本
,统计「某类别 + 某特征」共现次数 - EP_hat_f = 计数 / N:特征函数在经验联合分布
上的期望
这是最大熵约束的「数据侧」:模型期望要对齐到这个值。
4. _pw :模型 
mask... = 1 # 当前样本出现的特征置 1
tmp = self.w * mask1:
pw = exp(sum(tmp)) / Z
对应:

- 对每个类别算未归一化分数 → exp → 除以
得各类概率 - 返回长度为 m 的概率向量
5. _EP_f :模型期望 
pw = self._pw(X) # P(y|x)
self.EP_f += pw * px / N
用当前模型的
和经验
(这里用 Px)估计特征在模型下的期望。
训练目标是让 EP_f 逼近 EP_hat_f。
6. fit : IIS 训练
流程:
- 建词典:特征值、标签映射到下标
- 算 EP_hat_f(只算一次)
- 初始化 w = 0
- 迭代 (最多 max_iter 次):
- 算当前 EP_f
- IIS 更新:

- 代码:sigma = (1/M)*log(EP_hat_f / EP_f),再 w += sigma
- 对 inf/nan 做了保护(分母为 0 等)
直观:某特征在数据里出现多、模型里偏少 →
→
→ 增大对应权重。
7. predict (你选中片段的开头)
对每个测试样本:
- _pw(x) 得各类概率
- argmax 取最大类
- 映射回原始标签名
(完整实现在后续几行,逻辑就是「最大后验类」。)
8. 数据流小结
训练数据 (x,y)
↓
建 features / labels 索引
↓
算 EP_hat_f(经验期望)
↓
循环:EP_f ← 当前 w → IIS 更新 w
↓
预测:P(y|x)=_pw(x) → argmax
9. 使用注意(结合该实现)
- 这里把样本行里的取值当「离散特征 ID」用(set(x_)),更适合离散/符号化特征;鸢尾花连续值被直接当地名用,是简化演示。
- M=100 固定,偏启发式。
- EP_f 里用全局 Px 与逐样本 pw 相乘,是教材式简化实现,和严格公式可能略有出入,但体现的是 IIS「对齐两类期望」的思想。
一句话: 这段代码用经验特征期望约束模型,用 IIS 迭代学
,再用指数族形式算
做分类。
** **代码** **
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
raw_data = load_iris()
X, labels = raw_data.data, raw_data.target
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.3, random_state=43)
print(X_train.shape, y_train.shape)
** **输出** **
(105, 4) (105,)
** **代码** **
labels[-5:]
** **输出** **
array(2, 2, 2, 2, 2)
** **代码** **
from sklearn.metrics import accuracy_score
maxent = MaxEnt()
maxent.fit(X_train, y_train)
y_pred = maxent.predict(X_test)
print(accuracy_score(y_test, y_pred))
** **输出** **
D:\Installation\anaconda\install\lib\site-packages\ipykernel_launcher.py:90: RuntimeWarning: invalid value encountered in true_divide
D:\Installation\anaconda\install\lib\site-packages\ipykernel_launcher.py:93: RuntimeWarning: divide by zero encountered in log
** **输出** **
training done.
0.87116843 0.04683368 0.08199789 0 {0: 0, 1: 1, 2: 2}
0.00261138 0.49573305 0.50165557 2 {0: 0, 1: 1, 2: 2}
0.12626693 0.017157 0.85657607 2 {0: 0, 1: 1, 2: 2}
1.55221378e-04 4.45985560e-05 9.99800180e-01 2 {0: 0, 1: 1, 2: 2}
7.29970746e-03 9.92687370e-01 1.29226740e-05 1 {0: 0, 1: 1, 2: 2}
0.01343943 0.01247887 0.9740817 2 {0: 0, 1: 1, 2: 2}
0.85166079 0.05241898 0.09592023 0 {0: 0, 1: 1, 2: 2}
0.00371481 0.00896982 0.98731537 2 {0: 0, 1: 1, 2: 2}
2.69340079e-04 9.78392776e-01 2.13378835e-02 1 {0: 0, 1: 1, 2: 2}
0.01224702 0.02294254 0.96481044 2 {0: 0, 1: 1, 2: 2}
0.00323508 0.98724246 0.00952246 1 {0: 0, 1: 1, 2: 2}
0.00196548 0.01681989 0.98121463 2 {0: 0, 1: 1, 2: 2}
0.00480966 0.00345107 0.99173927 2 {0: 0, 1: 1, 2: 2}
0.00221101 0.01888735 0.97890163 2 {0: 0, 1: 1, 2: 2}
9.87528545e-01 3.25313387e-04 1.21461416e-02 0 {0: 0, 1: 1, 2: 2}
3.84153917e-05 5.25603786e-01 4.74357798e-01 1 {0: 0, 1: 1, 2: 2}
0.91969448 0.00730851 0.07299701 0 {0: 0, 1: 1, 2: 2}
3.48493252e-03 9.96377722e-01 1.37345863e-04 1 {0: 0, 1: 1, 2: 2}
0.00597935 0.02540794 0.96861271 2 {0: 0, 1: 1, 2: 2}
0.96593729 0.01606867 0.01799404 0 {0: 0, 1: 1, 2: 2}
7.07324443e-01 2.92672257e-01 3.29961259e-06 0 {0: 0, 1: 1, 2: 2}
0.96122092 0.03604362 0.00273547 0 {0: 0, 1: 1, 2: 2}
9.92671813e-01 7.31265179e-03 1.55352641e-05 0 {0: 0, 1: 1, 2: 2}
9.99997290e-01 2.58555077e-06 1.24081335e-07 0 {0: 0, 1: 1, 2: 2}
1.77991802e-05 4.62006560e-04 9.99520194e-01 2 {0: 0, 1: 1, 2: 2}
9.99995176e-01 3.85240188e-06 9.72067357e-07 0 {0: 0, 1: 1, 2: 2}
0.15306343 0.21405142 0.63288515 2 {0: 0, 1: 1, 2: 2}
0.25817329 0.28818997 0.45363674 2 {0: 0, 1: 1, 2: 2}
2.43530473e-04 4.07929999e-01 5.91826471e-01 2 {0: 0, 1: 1, 2: 2}
0.71160155 0.27290911 0.01548934 0 {0: 0, 1: 1, 2: 2}
2.94976826e-06 2.51510534e-02 9.74845997e-01 2 {0: 0, 1: 1, 2: 2}
0.97629163 0.00331591 0.02039245 0 {0: 0, 1: 1, 2: 2}
0.04513811 0.01484173 0.94002015 2 {0: 0, 1: 1, 2: 2}
0.61382753 0.38321073 0.00296174 0 {0: 0, 1: 1, 2: 2}
9.65538451e-01 3.86322918e-06 3.44576854e-02 0 {0: 0, 1: 1, 2: 2}
0.00924088 0.01731108 0.97344804 2 {0: 0, 1: 1, 2: 2}
0.02511142 0.93818613 0.03670245 1 {0: 0, 1: 1, 2: 2}
9.99127831e-01 3.29723254e-04 5.42445518e-04 0 {0: 0, 1: 1, 2: 2}
0.05081665 0.0038204 0.94536295 2 {0: 0, 1: 1, 2: 2}
9.99985376e-01 6.85280694e-06 7.77081022e-06 0 {0: 0, 1: 1, 2: 2}
9.99791732e-01 2.06536005e-04 1.73191035e-06 0 {0: 0, 1: 1, 2: 2}
2.72323181e-04 2.99692548e-03 9.96730751e-01 2 {0: 0, 1: 1, 2: 2}
0.02005139 0.97151852 0.00843009 1 {0: 0, 1: 1, 2: 2}
0.95642409 0.02485912 0.01871679 0 {0: 0, 1: 1, 2: 2}
0.00297317 0.01261126 0.98441558 2 {0: 0, 1: 1, 2: 2}
0.37777777777777777

的期望
的期望
;NB 是生成式