机器学习最大熵模型max_entropy_model

最大熵模型( 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 训练

流程:

  1. 建词典:特征值、标签映射到下标
  2. EP_hat_f(只算一次)
  3. 初始化 w = 0
  4. 迭代 (最多 max_iter 次):
    • 算当前 EP_f
    • IIS 更新:
    • 代码:sigma = (1/M)*log(EP_hat_f / EP_f),再 w += sigma
  5. 对 inf/nan 做了保护(分母为 0 等)

直观:某特征在数据里出现多、模型里偏少 → → 增大对应权重。

7. predict (你选中片段的开头)

对每个测试样本:

  1. _pw(x) 得各类概率
  2. argmax 取最大类
  3. 映射回原始标签名

(完整实现在后续几行,逻辑就是「最大后验类」。)

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

相关推荐
IT_陈寒1 小时前
Python的GIL让我多写了500行代码
前端·人工智能·后端
迪康Defender1 小时前
AI 重构终端安全运营:智能分析中枢 AI Insight 模块架构与落地场景深度解析
运维·网络·人工智能·其他·安全·重构·架构
IPHWT 零软网络1 小时前
技术方案分享|AI Agent 赋能 IVR 导航,解决传统语音呼叫系统交互瓶颈
人工智能·通信系统·rag·ivr·aiagent·智能语音·语音导航
leijiwen1 小时前
《花尖墨 · 果域宇宙》电影三部曲
人工智能·saas·paas
番茄炒鸡蛋加糖1 小时前
主流 AI 框架 + RAG 落地实战
人工智能·rag·springai
哈__1 小时前
面向AI智能体的数据库专业技能包:将DBA工程经验封装为可调用能力
数据库·人工智能·dba
leisoo80971 小时前
财报数据怎么排雷本地化Python构建财务异常预警系统
人工智能·python·算法
u0103055272 小时前
长株潭AI节能应用构建核心方案
人工智能
ACP广源盛139246256732 小时前
蚂蚁百灵 Ling‑3.0‑flash 开源 + 昇腾 0‑Day 原生适配@ACP#GSV9001E 在国产算力矩阵中的机会与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件