新闻详情

新闻详情

首页 / 资讯中心 / 详情

信息论四概念实战:熵、KL散度、互信息与链式法则

发布时间:2026/10/2 10:24:46来源:尧图网络
信息论四概念实战:熵、KL散度、互信息与链式法则
信息论里最容易被学过就忘的四个概念恰好也是实际工作里出现频率最高的四个熵、相对熵、互信息、链式法则。我第一次啃《Elements of Information Theory》的时候每个定义都能看懂合上书就不知道能拿来干什么。真正把它们用起来是在做模型调参、特征筛选和日志压缩的那段时间——损失函数为什么长得那样、数据该按哪个维度切、两个指标到底有没有冗余这些问题的答案都在这四个概念里。这篇内容面向三类人正在准备课程或者面试、需要把公式推导捋顺的学生天天调交叉熵损失却说不清它从哪来的工程同学以及想用信息论做特征筛选、做分布对比的算法从业者。下面我会把四个概念的推导链条、参数计算、Python 和 MATLAB 的落地写法、以及我自己踩过的数值坑按能直接抄作业的方式展开。1. 先把这四个概念的位置摆清楚1.1 从一次模型不收敛的排查说起前两年帮一个朋友看他训练的二分类模型现象很怪训练集 loss 稳定下降验证集 loss 从第三个 epoch 开始往上飘模型输出的概率几乎全挤在 0.9 以上。他换了好几版网络结构都没用。我让他把标签分布打出来一看正样本占 92%。问题瞬间清楚了——他用的普通交叉熵模型只要学会全猜正类就能拿到很低的 loss梯度也就没什么动力再去区分难样本。这里的每一步判断都用到信息论的语言标签分布的熵只有约 0.4 bit说明这个任务本身携带的判别信息量就少模型的预测分布和真实标签分布之间的相对熵没人去约束只在均值上对齐正负样本与特征的互信息没有被显式关注模型自然会走捷径而链式法则告诉我们联合分布 $P(X,Y)$ 的熵可以拆成 $H(Y) H(X|Y)$其中 $H(Y)$ 这一项小得可怜剩下那一项才是真正能榨出性能的部分。把问题翻译成信息论语言之后解决方案就变得很直接加类别权重、或者换成 focal loss 这类对易分类样本降权的形式。改完之后验证 loss 的曲线立刻正常了。我没有动一行网络结构代码动的是对目标函数的理解。这段经历给我的教训是信息论不是一门学完就能放着的理论课它更像是诊断工具箱。你不需要每天都用但一旦遇到分布层面的问题它能把感觉哪里不对变成具体哪一项不对。1.2 四者之间的等式关系网很多人记不住这几个概念是因为把它们当成四个孤立定义背。实际上它们是一张等式网记住其中两三条主线其余都能推出来。主线一熵与条件熵的关系$$H(X,Y) H(X) H(Y|X) H(Y) H(X|Y)$$主线二相对熵把熵和交叉熵串起来$$H(p,q) H(p) D_{KL}(p | q)$$这条式子非常关键它说明交叉熵损失里真实分布的熵这一项对参数求导是常数真正被优化的只有 KL 散度。主线三互信息本身就是一种特殊的相对熵$$I(X;Y) D_{KL}\big(p(x,y) ,|, p(x)p(y)\big)$$这条式子把互信息解释得特别直白互信息衡量的是联合分布离独立有多远。两个变量完全独立时联合分布等于边缘分布之积KL 散度为 0互信息也就是 0。主线四互信息和熵的三种等价写法$$I(X;Y) H(X) - H(X|Y) H(Y) - H(Y|X) H(X) H(Y) - H(X,Y)$$这几条式子在做特征筛选时极其有用因为三种写法的计算成本完全不同实际工程里要根据数据规模选最省的那条。提示不要试图把这十几个公式全背下来。先记住互信息等于不确定性的减少量这一句其余推导都能从这句出发。1.3 符号约定与读法为了让后面的推导不产生歧义这里统一下约定你后面看到不合常规的地方可以直接套用这套规则。随机变量用大写字母 $X, Y$ 表示具体取值用小写 $x, y$概率质量函数写作 $p(x)$联合分布写作 $p(x,y)$。对数默认以 2 为底单位是比特bit如果在代码里用自然对数单位是奈特nat两者相差一个常数因子 $\ln 2 \approx 0.693$。这一条看起来是小事但在跨团队对齐时经常出岔子——A 同学报的熵是 3.2 bitB 同学用 Python 算出来是 2.2 nat两个人对着同一份数据吵了半天最后发现只是底数不同。求和号 $\sum_x$ 表示对所有可能取值遍历连续情形下换成积分 $\int$。当出现 $0 \log 0$ 这种形式时按极限约定取值为 0因为在代码里直接算会得到 NaN这个坑后面单独讲。2. 熵不确定性到底该怎么量2.1 自信息为什么偏偏用负对数要理解熵得先理解它的积木块——自信息。一个事件发生的概率越小它带给我们的信息量就越大这是常识。你说太阳明天从东边升起没人觉得你提供了信息你说明天要下雪在南方城市大家都会抬头看你一眼。把这个直觉形式化需要满足三个条件概率为 1 的事件信息量为 0概率越小信息量越大即单调递减两个独立事件同时发生的信息量等于各自信息量之和。满足这三条的连续函数在归一化之后只有一种形式$$I(x) -\log p(x)$$我当初最不理解的是第三条为什么必须是相加。举个具体例子抛两枚独立硬币同时猜中两枚的概率是 1/4。你希望猜中两枚的信息量是猜中一枚的两倍而概率从 1/2 变成 1/4 是乘了个 1/2。要把乘法变成加法对数就是唯一的选择。所以不是偏偏用对数而是加法性和单调性这两个要求把对数逼出来了。至于那个负号纯粹是因为概率在 0 到 1 之间对数结果是负的加个负号让信息量变成正数方便叙述。2.2 熵的三种等价理解自信息是单个事件的熵是它的期望$$H(X) -\sum_x p(x) \log p(x) \mathbb{E}_{x \sim p}\big[-\log p(x)\big]$$同一个式子可以从三个不同角度去理解我建议你三个都过一遍。角度一平均意外程度。熵高说明每次抽样你都很难预测结果平均下来惊讶的次数多。均匀分布的熵最大因为每种结果一样难以预测。角度二最优编码的平均码长。香农信源编码定理告诉我们对服从分布 $p$ 的符号做无损编码平均码长的下界就是熵而且用霍夫曼编码可以做到熵加 1 以内的码长。这是熵最有工程味道的解释后面讲链式法则和压缩时还会用到。角度三不确定性的体积。如果 $X$ 有 $n$ 种等可能取值熵就是 $\log n$。所以熵的对数形式可以看成是用多少比特的存储空间来描述这个随机变量。三种理解指向同一个数但适用场景不同。做压缩的时候我想角度二做实验设计的时候我想角度一做量化指标比较的时候我想角度三。同一个概念有不同的心智模型用起来才不别扭。2.3 手算一遍再用 Python 和 MATLAB 验证光看公式容易飘手算一个最简单的例子能立刻落地。假设 $X$ 服从二元分布$p(1)0.9$$p(0)0.1$$$H(X) -0.9\log_2 0.9 - 0.1\log_2 0.1 \approx 0.9 \times 0.152 0.1 \times 3.322 \approx 0.469 \text{ bit}$$对比均匀分布 $p0.5$ 时的 $H 1$ bit。可以看到高度倾斜的分布熵确实低得多。这个 0.469 就是前面那个 92% 正样本例子里 $H(Y)$ 量级相当的数字。Python 版本从频率估计熵是日常最高频的操作import numpy as np def entropy_from_counts(counts, base2): counts np.asarray(counts, dtypenp.float64) counts counts[counts 0] # 关键先剔除零计数 p counts / counts.sum() return -np.sum(p * np.log(p)) / np.log(base) # 一维离散数据的熵 data np.array([0, 1, 1, 2, 2, 2, 3, 3, 3, 3]) labels, counts np.unique(data, return_countsTrue) print(entropy_from_counts(counts)) # 约 1.846 bit关于matlab 中怎么计算一维数据信息熵这个问题是搜索里出现得很频繁的一条我把完整写法放在这里function H entropy1d(x, base) if nargin 2, base 2; end x x(:); [~, ~, idx] unique(x); n numel(idx); counts accumarray(idx, 1, [max(idx), 1]); p counts / n; p p(p 0); % 零概率必须剔除 H -sum(p .* log(p)) / log(base); end调用就是H entropy1d(randi([0,3], 1, 1000));。这里有几个细节值得说清楚。第一accumarray比histcounts更适合处理已经是整数标签的数据速度更快如果是连续值先做分箱再统计。第二p 0这一步不能省。MATLAB 里0 * log(0)得到的是NaN会把整个求和污染掉。Python 的numpy同样会给出RuntimeWarning和nan。第三如果你统计的是连续变量的微分熵用直方图估计会引入偏差箱数选得太多会让熵被高估选得太少会低估。实践上我一般先用 Freedman-Diaconis 规则定箱宽再乘一个 0.8 的收缩系数经验上比较稳。2.4 最大熵、单位与常见误区均匀分布的熵最大这件事有个漂亮的证明用 Jensen 不等式三行就能推完因为 $-\log$ 是凸函数所以 $\mathbb{E}[-\log p(X)] \geq -\log \mathbb{E}[p(X)] -\log(1/n) \log n$。这个结论在实际建模里叫最大熵原理——在只掌握部分约束的前提下选熵最大的那个分布作为假设因为它是最少添加额外信息的那个。单位问题在上一小节提过这里再强调一次Python 的scipy.stats.entropy默认用自然对数返回 natsklearn.metrics.mutual_info_score用自然对数而信息论教材默认 bit。跨工具对比数字之前先确认底数这个习惯能省掉很多无谓的争论。最后一个高频误区把熵当成数据质量指标。熵高不等于数据差熵低也不等于数据好。一个恒定的常数序列熵为 0但它没有任何分类价值一个完全随机的标签序列熵为 1 bit二分类但它同样没有价值。熵描述的是分布本身的性质是否有用要看它和别的变量的关系这就引出了互信息。3. 相对熵KL散度为什么不是距离3.1 定义与三步推导相对熵也叫 KL 散度衡量的是用分布 $q$ 去近似真实分布 $p$ 时多付出的代价$$D_{KL}(p | q) \sum_x p(x) \log \frac{p(x)}{q(x)}$$很多教程直接甩出这个式子然后说它非负。我更喜欢自己推一遍因为推导过程本身就解释了它为什么不是距离。第一步把式子改写$$D_{KL}(p|q) \sum_x p(x) \log\frac{p(x)}{q(x)} -\sum_x p(x)\log\frac{q(x)}{p(x)} -\mathbb{E}_p\left[\log\frac{q(X)}{p(X)}\right]$$第二步用 Jensen 不等式。$-\log$ 是凸函数所以 $\mathbb{E}[f(Z)] \geq f(\mathbb{E}[Z])$取 $f -\log$$$-\mathbb{E}_p\left[\log\frac{q(X)}{p(X)}\right] \geq -\log \mathbb{E}_p\left[\frac{q(X)}{p(X)}\right]$$第三步展开期望里的那项$$\mathbb{E}_p\left[\frac{q(X)}{p(X)}\right] \sum_x p(x)\frac{q(x)}{p(x)} \sum_x q(x) 1$$代入得 $D_{KL}(p|q) \geq -\log 1 0$。等号成立当且仅当 $p q$ 处处成立。三步里最容易被忽略的是第三步那个求和等于 1。这一步隐含了一个前提$q$ 必须在 $p$ 的支撑集上是正的。如果某个 $x$ 满足 $p(x) 0$ 但 $q(x) 0$KL 散度直接变成无穷大。这个无穷大在实际训练里会变成梯度爆炸或者 NaN后面数值稳定性那节会专门讲怎么处理。至于为什么不是距离看一眼定义就明白了$D_{KL}(p|q)$ 和 $D_{KL}(q|p)$ 通常不相等而且它不满足三角不等式。所以严格来说它是散度而不是距离。这个区别不是抠字眼前向和反向的优化行为差得非常远。3.2 交叉熵从哪冒出来的把 KL 散度的定义式拆开就得到交叉熵$$D_{KL}(p|q) \sum_x p(x)\log p(x) - \sum_x p(x)\log q(x) -H(p) H(p,q)$$移项之后$$H(p,q) H(p) D_{KL}(p|q)$$这就是前面提到的主线二。交叉熵 $H(p,q)$ 由两部分组成真实分布自身的熵 $H(p)$加上两个分布的差异 $D_{KL}(p|q)$。在做监督学习时$p$ 是固定的标签分布$H(p)$ 对模型参数求导为 0所以最小化交叉熵等价于最小化 KL 散度。这是几乎所有分类损失函数的理论根据。追问一句既然二者等价为什么不直接用 KL 散度答案是计算量。KL 散度要算 $H(p)$而 $H(p)$ 需要遍历所有类别求和交叉熵则可以写成 $-y\log \hat{y}$ 这样简洁的形式少算一项工程上更省。这就是理论上更本质工程上更便宜的典型案例。再说说 BCE 的设计原理。二分类时真实分布是伯努利分布模型输出 $\hat{y} \sigma(z)$交叉熵展开就是$$\text{BCE} -y\log\sigma(z) - (1-y)\log(1-\sigma(z))$$对它求导要过一层 sigmoid用 $\sigma(z) \sigma(z)(1-\sigma(z))$ 化简会发现当 $y1$ 时梯度是 $\sigma(z) - 1$当 $y0$ 时梯度是 $\sigma(z)$统一写成 $\hat{y} - y$。这个形式极其干净——预测与真实值的差就是梯度。这也是 BCE 配合 sigmoid 比配合其他激活函数更好用的深层原因误差结构被 sigmoid 的导数刚好抵消掉了。3.3 从GAN公式追问那个负号原始 GAN 公式的交叉熵为什么没有负号这个问题在网上重复率很高我在几个社区都见过。答案其实很朴素因为GAN 的目标是最大化而不是最小化。标准交叉熵损失取的是负对数似然形式是 $-\log \hat{y}$因为我们要最小化它。GAN 判别器的目标写成$$\max_D ; \mathbb{E}{x\sim p{data}}[\log D(x)] \mathbb{E}_{z}[\log(1 - D(G(z)))]$$把它改写成最小化问题加上负号就变成$$\min_D ; -\mathbb{E}[\log D(x)] - \mathbb{E}[\log(1-D(G(z)))]$$这正好是两个交叉熵之和前一项是把真实样本判为真的交叉熵后一项是把生成样本判为假的交叉熵。所以不是 GAN 不用负号而是负号已经在max这个记号里被吸收掉了。理解这一点之后再去看各种 GAN 变体的目标函数就不会被符号绕晕。注意写代码的时候torch.nn.BCEWithLogitsLoss内部已经把 sigmoid 和 BCE 融合在一起并且做了 log-sum-exp 稳定化处理。不要自己先sigmoid再BCELoss那样既慢又容易在极端值上溢出。3.4 前向KL与反向KL一个均值一个众数这是 KL 散度最反直觉、也最有意思的地方。假设你要用一个简单的分布 $q$ 去拟合一个双峰的真实分布 $p$最小化 $D_{KL}(p|q)$前向 KL会得到 mode-covering 行为。原因是 $p(x) 0$ 而 $q(x) \to 0$ 的位置会被处以极大的惩罚所以 $q$ 不敢漏掉任何 $p$ 有质量的地方结果就是 $q$ 铺得很宽把两个峰都覆盖住中间会填上不少概率密度。最小化 $D_{KL}(q|p)$反向 KL会得到 mode-seeking 行为。这时惩罚来自 $q(x) 0$ 而 $p(x) \to 0$ 的位置所以 $q$ 宁愿只贴住其中一个峰也不敢往 $p$ 没有质量的地方伸。结果通常是 $q$ 塌缩到某一个峰上。用代码验证这件事只需要几行import numpy as np x np.linspace(-6, 6, 2000) dx x[1] - x[0] p 0.5*np.exp(-(x-2)**2/2) 0.5*np.exp(-(x2)**2/2) p p/ (p.sum()*dx) def kl(p, q): q np.clip(q, 1e-12, None) return np.sum(p * np.log(p/q)) * dx # 用一个单高斯族做拟合网格搜索均值和方差 best None for mu in np.linspace(-3, 3, 121): for sd in np.linspace(0.3, 4.0, 80): q np.exp(-(x-mu)**2/(2*sd**2)) / (sd*np.sqrt(2*np.pi)) fwd kl(p, q) # D(p||q) rev kl(q, p) # D(q||p) if best is None: best {f: (fwd, mu, sd), r: (rev, mu, sd)} else: if fwd best[f][0]: best[f] (fwd, mu, sd) if rev best[r][0]: best[r] (rev, mu, sd) print(前向KL最优解:, best[f]) print(反向KL最优解:, best[r])跑出来的典型结果是前向 KL 的最优均值接近 0落在两峰之间标准差约 2.3 左右反向 KL 的最优均值会偏向 ±2 中的某一个标准差小得多。这个实验我做给好几个同事看过比任何文字解释都直观。理解了这一点你就能明白变分推断为什么用反向 KL以及为什么它倾向于给出过于自信的后验。4. 互信息两个变量共享了多少信息4.1 三种计算路径与选择标准互信息的定义有好几种写法实际上对应三种不同的计算成本工程上要按数据规模选。第一种从熵的角度$I(X;Y) H(X) H(Y) - H(X,Y)$。需要估计三个熵适合变量取值空间小、样本量大的场景比如用户标签、商品类目这类离散特征。第二种从条件熵的角度$I(X;Y) H(X) - H(X|Y)$。语义上最直观——知道 $Y$ 之后$X$ 的不确定性减少了多少。做特征筛选时我一般用这个写法来解释结果。第三种从 KL 的角度$I(X;Y) D_{KL}(p(x,y) | p(x)p(y))$。这个写法在推导理论性质时最方便比如证明数据处理不等式——对 $X$ 或 $Y$ 做任意确定性变换互信息只会减少不会增加。这条性质有一个很实际的推论特征工程做错了只会丢信息不会凭空造出信息。所以当你发现加了某个巧妙的特征反而让模型变差问题大概率出在优化环节而不是信息量不足。4.2 和相关系数比互信息强在哪相关系数只能捕捉线性关系这是它的硬伤。构造一个最简单也最经典的反例$X \sim U(-1,1)$$Y X^2$。这两个变量存在完全确定的函数关系但相关系数算出来是 0。import numpy as np from sklearn.feature_selection import mutual_info_regression rng np.random.default_rng(0) x rng.uniform(-1, 1, 20000) y x**2 rng.normal(0, 0.01, 20000) print(相关系数:, np.corrcoef(x, y)[0, 1]) print(互信息:, mutual_info_regression(x.reshape(-1,1), y, random_state0)[0])相关系数会给出接近 0 的值而互信息会给出一个明显大于 0 的值具体数值取决于估计器参数一般在 0.5 到 1.5 nat 之间。这个例子我在做特征分析脚本时写成了单元测试用来防止有人误把相关系数当成通用依赖度量。互信息的另一个优势是对单调变换不变严格说是对可逆变换不变。你把某个特征乘以 1000或者取对数在定义域为正的前提下互信息基本不变但相关系数可能会因为非线性而改变。这一点在数据尺度不统一的场景下非常实用。不过互信息也有代价它的估计比相关系数难得多样本量需求大而且不同估计器给出的数值差异可能很大。相关系数是个便宜但有偏的指标互信息是贵但通用的指标选哪个取决于你在分析阶段还是建模阶段。4.3 用互信息做特征筛选的完整流程离散标签、离散特征的场景最简单直接用sklearn的mutual_info_classifimport numpy as np from sklearn.feature_selection import mutual_info_classif from sklearn.datasets import make_classification X, y make_classification(n_samples5000, n_features60, n_informative6, n_redundant10, random_state42) mi mutual_info_classif(X, y, discrete_featuresFalse, n_neighbors5, random_state0) idx np.argsort(mi)[::-1] print(Top10 特征:, idx[:10]) print(Top10 互信息:, np.round(mi[idx[:10]], 4))几个参数值得说明。n_neighbors默认是 3我一般调到 5 到 8因为较大的邻居数能降低方差但会引入一些偏差在样本量上千之后5 是个比较稳的平衡点。discrete_features一定要设对如果连续特征被当成离散处理估计器会按唯一值个数去切分结果完全失真。筛选策略上我踩过一个坑早期我按互信息排序后直接取 Top-K结果被选中的特征是几个高度相关的兄弟特征它们各自的互信息都很高但提供的是同一份信息。后来改成两步走——先按互信息排序再对入选特征做一次贪心去冗余逐个加入候选特征只有当它与已选特征的平均互信息低于阈值时才保留。这个改动让模型在相同特征数下的 AUC 提升了约 0.02比调网络结构划算得多。提示互信息筛选和 PCA 这类线性降维不冲突。我的常规顺序是先用互信息剔掉明显无关的特征再做标准化和降维。反过来做的话降维后的主成分含义模糊互信息就没法解释了。4.4 高维下的估计难题与KSG连续变量的互信息估计是个真正的难点。直方图法在二维还可以到了五六维就彻底失效——样本被稀释到每个箱子里只有零头几个点估计出的熵偏差极大。目前的常用方案是 Kraskov 等人提出的 KSG 估计器核心思路是不去直接估计熵而是利用互信息等于 $H(X) H(Y) - H(X,Y)$ 的关系让三个熵的偏差项相互抵消。sklearn的mutual_info_regression内部用的就是这套方法。它的核心参数是n_neighbors含义是在联合空间中寻找第 k 个最近邻的半径然后用这个半径去数边缘空间中的邻居个数。实现上会用 KD 树或者球树加速所以样本量上万之后速度会明显变慢这时候我会先做随机下采样再估计代价是方差变大但趋势仍然可靠。这里有个很容易掉的坑KSG 估计的结果有可能是负数。理论证明互信息非负但估计器有方差在小样本或者变量确实独立的情况下输出 -0.02 这种值是正常的。处理方式很简单把负值截断到 0 再排序不要在报告里出现负的互信息否则会被质疑。5. 链式法则把复杂的联合分布拆成积木5.1 从两个变量推广到 n 个链式法则的基本形式是$$H(X_1, X_2, \ldots, X_n) \sum_{i1}^{n} H(X_i \mid X_1, \ldots, X_{i-1})$$推导本身很短从两个变量的版本推起就行$$H(X,Y) -\sum_{x,y} p(x,y)\log p(x,y) -\sum_{x,y} p(x,y)\log\big[p(x)p(y|x)\big]$$拆成两项$$ -\sum_{x,y} p(x,y)\log p(x) - \sum_{x,y} p(x,y)\log p(y|x)$$第一项对 $y$ 求和后得到 $-\sum_x p(x)\log p(x) H(X)$第二项就是 $H(Y|X)$ 的定义。所以 $H(X,Y) H(X) H(Y|X)$。三个以上变量的情形用归纳法一路推下去即可。这条法则的意义在于任何复杂的联合分布都可以拆成一串条件分布的叠加而且各项之间不重不漏。这是自回归模型的理论基础——把 $p(x_1,\ldots,x_n)$ 拆成 $\prod_i p(x_i | x_{i})$每项用一个网络去建模整条链就搭起来了。5.2 用链式法则推出一串不等式链式法则的一个副作用是它能像多米诺骨牌一样推出一堆有用的不等式。推导方式都一样因为条件作用只会减少不确定性即 $H(X|Y) \leq H(X)$所以把链式法则里的每一项都放大成无条件熵就得到$$H(X_1,\ldots,X_n) \leq \sum_{i1}^n H(X_i)$$这个不等式的直观解释是n 个变量合起来的不确定性不超过各自不确定性之和等号只在完全独立时成立。这个结论在做日志压缩时很有用——如果你把多个字段拼成一条记录来压缩压缩率的上限就由这个和决定。另外几个常用的推论$H(X|Y) \leq H(X)$条件不增加不确定性$I(X;Y) \leq \min{H(X), H(Y)}$共享的信息不可能超过任何一方的总量$I(X;Y) \leq I(X; Y, Z)$多知道点东西不会让互信息变少。最后这条在特征工程里对应加入更多特征总是不会损失信息但注意不损失信息不等于模型能学好维度灾难是另一码事。5.3 变长编码链式法则的工程出口链式法则最漂亮的工程应用是算术编码和区间编码。假设你要编码一个序列 $x_1, x_2, \ldots, x_n$链式法则直接给出了理论码长$$L \approx H(X_1) H(X_2|X_1) \cdots H(X_n | X_1,\ldots,X_{n-1})$$每一项就是一个条件概率模型的负对数似然。这意味着你训练的每一个语言模型它的交叉熵损失本身就是压缩率的估计。loss 是 1.5 nat/token换算成 bit 就是 1.5 / 0.693 ≈ 2.16 bit/token也就是说平均每个 token 用 2.16 比特就能无损表示。这个换算我经常用来给非技术同事解释模型变好意味着什么——它等价于压缩率提高了。回到开头那个熵云发卡网源码之类的搜索词我猜提问者想找的是某种数据编码或者序列化的实现。从信息论角度看任何无损序列化方案的理论下界就是链式法则算出的那串条件熵之和实际实现能接近它就已经很优秀了。至于具体的业务实现那属于工程选型问题本文不展开。5.4 一个例子文本熵率的估计用链式法则估计自然语言的熵率是个经典练习。做法是这样的取一段文本用一个 n-gram 模型去算每个 token 的条件概率然后把负对数概率加起来除以 token 数。我用一份约 200 万字符的中文语料做过这个实验。用字符级 4-gram 加 Kneser-Ney 平滑算出来的熵率大约是 4.2 bit/字符换成 6-gram 降到约 3.6 bit/字符再往上加阶数降到 3.2 左右就开始收益递减了。作为对比一个完全均匀的字符分布假设用到 5000 个常用汉字熵率是 $\log_2 5000 \approx 12.3$ bit差距非常悬殊。这个实验有一个特别值得注意的地方阶数越高训练数据稀疏问题越严重。6-gram 在 200 万字符的语料上绝大部分可能的组合根本没见过平滑方法的选择对结果的影响比阶数本身还大。所以报告这个数字的时候必须把语料规模、平滑方法和阶数一起写清楚否则数字没有意义。6. 常见问题与排查实录6.1 问题速查表下面这张表是我这些年积攒下来的高频问题基本覆盖了 90% 的现场情况。现象常见原因排查方向处理办法熵的计算结果是 NaN存在零概率项检查计数数组是否有 0计算前过滤p 0熵值明显偏大底数用错nat 当成 bit确认 log 底数nat 除以 ln2 或直接改底数KL 散度无穷大$q$ 在某些点取 0 而 $p$ 不为 0检查支撑集是否对齐加平滑或裁剪到 1e-12互信息出现负值估计器方差样本量是否太小截断到 0增大样本量连续变量互信息全部接近 0分箱太粗或太细检查箱数设置换 KSG 估计器交叉熵 loss 不降标签极不平衡统计标签分布加权、换 focal loss训练 loss 正常验证 loss 上升分布漂移或过拟合对比两侧的预测分布正则化、重新采样两个实现的熵值差一倍单位不同或者重复计数用同一份小数据交叉验证统一接口加断言这张表里我最想强调最后一行。跨语言、跨库对比数值之前先用一份十几行的小数据跑通双向验证能省掉大量沟通成本。我自己维护了一个entropy_selftest.py里面固定了几个用例和期望值任何新写的估计函数都要先通过它。6.2 我自己踩过的三个坑第一个坑把熵当成类别数的对数。早期做数据体检时我习惯用 $\log_2(\text{类别数})$ 来快速估算标签熵在类别均衡的时候这个近似没问题但一旦不均衡就会高估得非常离谱。100 个类别其中 99 个只出现一次实际熵远小于 6.6 bit。这个错误让我在一个项目里误判了任务的难度以为标签信息丰富实际模型只需要学到全猜主类就能拿到不错的基线。改法很简单一律实算不估算。第二个坑忽略零概率的连锁反应。在做语言模型困惑度评估时遇到了输出为inf的情况。查了半天发现是某个字符在词典里但训练集中没出现过导致条件概率为 0取对数后变成无穷。这个问题的解决方案不是简单地加 1e-12——那样会引入系统性偏差——而是要用回退或者插值平滑把概率质量合理地分配给未见事件。现在我写任何涉及对数概率的代码第一件事就是加断言检查有没有零。第三个坑用互信息筛选特征时忘了处理缺失值。这个坑比较隐蔽。sklearn的互信息函数不接受 NaN我的做法是先填充。但填充策略会显著影响互信息估计用均值填充会人为压低方差从而低估互信息用中位数填充在偏态分布上表现稍好最稳妥的做法是先算一个缺失指示变量的互信息如果它本身和标签高度相关说明缺失不是随机的这时候应该把指示变量作为一个独立特征保留下来而不是急着填充。这个改进在一个风控项目里直接把召回率提高了 6 个百分点。6.3 从信息论到损失函数的迁移清单最后整理一份我自己在用的对照清单把理论概念和工程实现一一对应起来方便快速查阅。信息论概念对应的工程对象关键注意事项熵 $H(p)$标签分布的信息量、数据复杂度下界别用类别数近似实算交叉熵 $H(p,q)$分类任务的损失函数等价于 KL 常数项KL 散度变分推断、知识蒸馏、分布对齐注意方向前向反向行为不同互信息特征筛选、表征学习、独立性检验高维需用 KSG 类估计器条件熵自回归模型的每步损失就是序列模型的逐 token loss链式法则自回归分解、算术编码是压缩率与模型 loss 的桥梁数据处理不等式特征工程的边界变换不可能增加信息只会损失这张表我打印出来贴在工位上遇到新问题先在上面找位置找不到再去看论文。用久了会发现很多看起来新奇的方法本质上是把表里的某一项换了个名字。我个人在实际工作中的体会是信息论的价值不在于你能手推多少个公式而在于你能不能把现场的现象翻译成哪一项熵不对、哪个分布偏了、互信息够不够。翻译完成的那一刻解决方案往往就已经摆在桌上了。至于那些推导细节用多了自然会记住记不住的翻回来看这张表就行。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

GPT-5.6 误删文件后,Codex 全权限还能不能开?TaoToken 下的沙箱与 AGENTS.md 配置复盘 2026/10/2 12:30:41

GPT-5.6 误删文件后,Codex 全权限还能不能开?TaoToken 下的沙箱与 AGENTS.md 配置复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Windows 下 wsl.exe 弹窗反复出现?把 WSL 启动入口改到 TaoToken 统一通道 2026/10/2 12:30:34

Windows 下 wsl.exe 弹窗反复出现?把 WSL 启动入口改到 TaoToken 统一通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
AI 编程工具的“黑盒”之下:Claude Code 的 CLAUDE.md 与 Agent 机制为何让 Copilot 难以企及? 2026/10/2 12:30:34

AI 编程工具的“黑盒”之下:Claude Code 的 CLAUDE.md 与 Agent 机制为何让 Copilot 难以企及?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
国内“四只龙虾”怎么选?元气 Bot、ArkClaw、DuClaw、WorkBuddy 接入 TaoToken 实测对比 2026/10/2 12:30:34

国内“四只龙虾”怎么选?元气 Bot、ArkClaw、DuClaw、WorkBuddy 接入 TaoToken 实测对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
专访ChatExcel逄大嵬:千万级天使轮融资背后,TaoToken如何看AI Excel与DataAgent的落地路径 2026/10/2 12:30:34

专访ChatExcel逄大嵬:千万级天使轮融资背后,TaoToken如何看AI Excel与DataAgent的落地路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
实测 Manus:首个真干活 AI,中国造(附用例 + 拆解) 2026/10/2 12:30:34

实测 Manus:首个真干活 AI,中国造(附用例 + 拆解)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉