信息熵与信息增益:从信息量到决策树特征选择的完整指南
2026/9/18 15:45:41 网站建设 项目流程

1. 信息量与“越意外越有料”的反直觉规律

聊信息论之前,先把一个最容易被忽略的概念拎出来:信息量。很多人第一次接触这三个字,都觉得“信息量”不就是“数据量”“内容多少”嘛。其实信息论里的信息量,跟“多少”没关系,它衡量的是“惊讶程度”——一件事发生得越意外,携带的信息量越大。

拿最常见的例子:假如你住的城市明天太阳照常升起,这事几乎百分之百确定,有人专门跑来告诉你“明天太阳还会升起”,你会觉得这是一句废话。信息量接近0。但如果有人告诉你“明天凌晨有月全食”,这个事件的概率比较低,你就觉得“有点东西”,信息量就上来了。再极端一点,如果有人说“明天市区地震”,平时概率极低,这条消息就炸了,信息量极大。

信息论对信息量的定义正是这样:一个事件x的信息量 I(x) = -log₂ P(x),P(x)是事件发生的概率。取负数的原因是概率在0到1之间,对数算出来是负的,取负号才能变成正值;底数取2,单位就是bit(比特),也便于和计算机里的二进制对齐。

从这个公式能推导出一个很实用的规律:概率每降低一半,信息量增加1 bit。比如某事件概率是1/2,信息量就是1 bit;概率是1/4,信息量就是2 bit;概率是1/8,信息量就是3 bit。想象一枚正常硬币,抛出来正面朝上,概率1/2,给你带来的信息量就是1 bit;但如果是一枚做了手脚的硬币,正面概率99%,偶然出了一次反面,这一次反转带来的信息量大约是 log₂(1/0.01) ≈ 6.64 bit,相当惊人。

这正是信息论和直觉不一样的地方:我们日常觉得“信息量大”的文章、视频,往往是篇幅长、细节多,但信息论里的信息量纯粹由“不确定性消除”的程度决定。一篇全是套话、车轱辘话来回说的长文,信息量几乎为0;而一句“这条路上有抓拍”的点拨,信息量可能顶得上一整篇驾驶教程。建立这个认知很重要,因为后面说信息熵和信息增益,都是站在这条公式的地基上的。

提示:在机器学习里,决策树分裂特征时用的“信息增益”,本质逻辑就是“这条特征帮我消除了多少不确定性”。所以先别急着套公式,把“信息量=意外程度”这个直觉刻进脑子里,后面全通了。

2. 信息熵:一个系统到底有多“乱”

理解了单个事件的信息量,信息熵就是它的“全家桶”版本。

一个系统里可能有好几种结果,每种结果有各自的概率。信息熵H(X)的定义是:把所有可能结果的“信息量”按概率加权求平均。公式长这样:

H(X) = -Σ P(x) log₂ P(x)

说白了,信息熵就是“这个系统平均每条消息带来的信息量”,或者说“在知道结果之前,系统整体有多不确定”。越不确定,熵越大;越确定,熵越小。

我常用一个比喻:信息熵像一个房间的混乱程度。房间里的东西摆放得越随机、越无法预测,你去收拾的时候要获取的信息就越多,熵就高;如果所有东西都在固定位置,你一进门就知道哪拿哪放,熵就低。极端情况有两种,一是所有东西都整整齐齐,也就是某个事件概率是1其余全是0,这时候熵等于0,完全没有不确定性;二是所有位置都乱七八糟且概率均等,比如抽屉里10件东西每件出现的概率都是1/10,这时候熵达到最大值 log₂10 ≈ 3.32 bit。

把公式拆开看,有几个立即能用的结论:

  • 概率越均匀,熵越大。一个天气系统如果晴雨各半,熵是1 bit;如果晴天概率90%,熵就只有约0.47 bit。均匀分布是最“没有倾向性”的状态,也是最难预测的状态。
  • 状态越多,熵越大。一个只有两种状态的系统,最高熵是1 bit;一个有8种等可能状态的系统,最高熵是3 bit。因为你要用更多二进制位才能区分这么多状态。
  • 熵是非负的。概率都在0到1之间,负对数非负,加权平均后依然非负,最小值是0。

在机器学习里,信息熵最常见的用途是当“杂质度”用。决策树划分数据集之前,会先算一下当前数据集的熵。举个例子,一个训练集里有100个样本,其中50个是“买”,50个是“不买”,类别分布最均匀,此时熵是1 bit,说明数据非常“纠结”,非常不纯;如果样本里99个“买”、1个“不买”,熵大约是0.08 bit,数据几乎一边倒,很纯。决策树算法的核心目标,就是找到一个特征,把数据从“纠结”切分成“不纠结”的子集。

这里有个初学者特别容易混淆的点:信息熵描述的是随机变量的不确定性,不是某个具体样本的属性。不能说“这个样本的熵是多少”,只能说“这堆样本的类分布熵是多少”。你按某个特征把所有样本分成两组,每组各自算一个熵,再按样本量加权平均,这样才能得到“划分之后的总体纯度”。这个加权平均的过程,正好是下一步信息增益的铺垫。

3. 信息增益:决策树“找茬”的核心指标

假设你手里有一堆样本,每个样本有若干特征,每个特征都有一些取值,你要做的就是让数据从“最混乱”变成“最有条理”。什么叫有条例?就是按类别分得越开越好,同一堆里最好全是一个类别。

决策树做这件事的方式很“笨”也很有用:挨个拿特征出来试,算算“如果按这个特征划分,混乱程度能降多少”。这个降低的量就是信息增益

公式长这样:

Gain(D, A) = H(D) - H(D|A)

其中H(D)是划分前数据集D的熵,H(D|A)是已知特征A的取值后,D的条件熵,也就是按A的所有取值划分成多个子集后,各子集熵的加权平均。

用大白话说:信息增益=原来的不确定度-知道这个特征之后还剩下的不确定度。差值越大,说明这个特征越能“一句话问到点子上”,对消除混乱贡献越大。

为什么这么设计?我换个场景你就明白了。假设你要判断一个陌生人是不是篮球运动员,面前摆着两个特征:“身高”和“是否戴眼镜”。如果按“是否戴眼镜”划分,你会发现两个子集里运动员比例差不多,熵几乎没降,增益接近0;如果按“身高”分成高和矮,高的那组里运动员比例明显偏高,熵大幅下降,增益就大。决策树在每一步都会挑增益最大的特征来分裂,这本质上是在做“最有效的提问”——每次都用最能缩小答案范围的问题来缩小答案范围。

这里有一个决策树实现里的关键点:算条件熵H(D|A)时,不是简单把所有子集熵加起来,而是“按子集大小加权”。为什么?因为决策树照顾的是整体纯度,如果某个特征把数据切成一堆小碎块,每块都特别纯,但每块就一两个样本,这样的划分对后续分类没意义。加权平均能压制这种“过度切碎”的倾向,让大子集的贡献更突出。

信息增益还有一个非常直观的几何解释:它衡量的是“划分前后类别分布的变化幅度”。划分前是一个分布,划分后是多个分布的加权混合,信息增益就是这两个状态之间的“距离”。分类任务里,这个距离越大,说明特征和标签之间的关联越强。所以很多人用信息增益做特征选择,原理也在这里:先给每个特征算一遍信息增益,排个序,选增益大的特征子集,能有效降低维度,减少过拟合。

注意:信息增益有一个隐藏缺点——它天然偏向取值多的特征。比如“用户ID”这种特征,每个样本一个值,按它划分后每个子集只有一个样本,子集熵全为0,信息增益会直接拉满。但这样的特征完全没有泛化能力。这也是为什么后来的算法(比如C4.5)要用增益率来修正这个偏好。后面我会专门展开讲这个坑。

4. 完整手算:用“要不要发录用通知”把三概念串起来

公式背得再多,不如完整推一遍。我给你设计一个非常贴近工作的案例:一个团队要根据候选人的3个特征决定“要不要发录用通知”。

原始数据集有14个候选人,特征分别是“学历”(本科/硕士/博士)、“工作经验”(少于3年/3到5年/超过5年)、“笔试成绩”(高/中/低),标签是“录用”(是/否)。样本分布如下:

编号学历工作经验笔试成绩录用
1本科少于3年
2本科少于3年
3硕士少于3年
4博士3到5年
5本科超过5年
6硕士3到5年
7博士少于3年
8硕士超过5年
9本科少于3年
10博士3到5年
11硕士3到5年
12博士超过5年
13硕士超过5年
14本科3到5年

第一步,算划分前的信息熵。14个人里,录用6人,不录用8人(数一下:是的有3、4、5、6、8、10、11、12、13,9个?不对,我重新数:否:1、2、7、9、14,5个;是:3、4、5、6、8、10、11、12、13,9个。等一下,14个样本里明明是9个是和5个否)。H(D) = - (9/14) log₂(9/14) - (5/14) log₂(5/14) ≈ 0.940 bit。

第二步,按“学历”划分。学历有3个取值:

  • 本科:样本1、2、5、9、14,其中录用1个(5),不录用4个,熵H(本科) = - (1/5) log₂(1/5) - (4/5) log₂(4/5) ≈ 0.722 bit。
  • 硕士:样本3、6、8、11、13,录用5个,不录用0个,熵H(硕士) = 0。
  • 博士:样本4、7、10、12,录用3个(4、10、12),不录用1个(7),熵H(博士) = - (3/4) log₂(3/4) - (1/4) log₂(1/4) ≈ 0.811 bit。

条件熵H(D|学历) = (5/14)×0.722 + (5/14)×0 + (4/14)×0.811 ≈ 0.258 + 0 + 0.232 ≈ 0.490 bit。

信息增益Gain(D, 学历) = 0.940 - 0.490 = 0.450 bit。

第三步,按“工作经验”划分。工作经验有3个取值:

  • 少于3年:样本1、2、3、7、9,录用2个(3、7?不对,7是不录用,我重新数:1否、2否、3是、7否、9否,录用1个),熵H(短) = - (1/5) log₂(1/5) - (4/5) log₂(4/5) ≈ 0.722 bit。等一下,样本3的录用是“是”,所以短工作经验里录用1个、不录用4个。
  • 3到5年:样本4、6、10、11、14,录用4个(4、6、10、11),不录用1个(14),熵H(中) = - (4/5) log₂(4/5) - (1/5) log₂(1/5) ≈ 0.722 bit。
  • 超过5年:样本5、8、12、13,录用4个,不录用0个,熵H(长) = 0。

条件熵H(D|工作经验) = (5/14)×0.722 + (5/14)×0.722 + (4/14)×0 = 0.516 bit。

信息增益Gain(D, 工作经验) = 0.940 - 0.516 = 0.424 bit。

第四步,按“笔试成绩”划分。笔试成绩有3个取值:

  • 低:样本3、6、7、11、12,录用4个(3、6、11、12),不录用1个(7),熵H(低) = - (4/5) log₂(4/5) - (1/5) log₂(1/5) ≈ 0.722 bit。
  • 中:样本1、4、8、10、14,录用3个(4、8、10),不录用2个(1、14),熵H(中) = - (3/5) log₂(3/5) - (2/5) log₂(2/5) ≈ 0.971 bit。
  • 高:样本2、5、9、13,录用2个(5、13),不录用2个(2、9),熵H(高) = 1 bit。

条件熵H(D|笔试成绩) = (5/14)×0.722 + (5/14)×0.971 + (4/14)×1 ≈ 0.258 + 0.347 + 0.286 ≈ 0.891 bit。

信息增益Gain(D, 笔试成绩) = 0.940 - 0.891 = 0.049 bit。

三个特征增益排个序:学历0.450 > 工作经验0.424 > 笔试成绩0.049。所以决策树第一步会优先用“学历”做根节点的划分。这个结果也符合直觉:在这个小样本里,硕士学历的员工全员录用,说明学历和工作录用的相关性最强;笔试成绩几乎不提供额外区分度,增益很低。

注意:我这里的样本是我自己构造的,为了讲清楚算法故意让“硕士全录用”特别极端。真实数据集里极少有这么整齐的分布,但计算逻辑完全一样。你完全可以拿这个表去Excel或者纸上自己验算一遍,我建议你亲手算一次,真的比看十遍公式都管用。

5. 代码实操:Python和MATLAB怎么算信息熵

很多读到这里的人会问一个问题:我知道公式了,但实际处理一两千行数据,总不能拿笔算吧?确实,工程场景里我们都是用代码来算。搜“matlab中怎么计算一维数据信息熵”的人不少,说明大家对这块有实际需求。

先说Python。计算信息熵最顺手的做法是结合numpy。核心就四步:统计每个类别的数量、转成概率、代入熵公式、求和。给你一段可以直接跑的代码:

import numpy as np def entropy(labels): # 输入:一维数组,比如['yes','no','yes',...] # 输出:该集合的信息熵(单位bit) _, counts = np.unique(labels, return_counts=True) probs = counts / counts.sum() return -np.sum(probs * np.log2(probs)) # 试一下:14个人里9个录用、5个不录用 labels = ['yes'] * 9 + ['no'] * 5 print(entropy(labels)) # 约0.940

如果你在算决策树的信息增益,可以用pandas按特征分组聚合:

import pandas as pd def conditional_entropy(data, feature, label): # data: DataFrame,feature: 特征列名,label: 标签列名 total = len(data) result = 0.0 for _, subset in data.groupby(feature): w = len(subset) / total result += w * entropy(subset[label].values) return result def info_gain(data, feature, label): base = entropy(data[label].values) cond = conditional_entropy(data, feature, label) return base - cond

这段代码是教科书公式的直接翻译,逻辑很清晰。实际做项目时,如果特征列特别多,几十上百个特征,用循环逐个算也能扛住;但如果数据量上百万行,建议直接用sklearn里的mutual_info_classifDecisionTreeClassifier,底层用C实现,速度不在一个量级。

再说MATLAB。MATLAB里没有内置一个叫entropy的函数——这点很坑,因为很多刚入门的人上来就搜“matlab熵函数”,结果发现根本没有。其实一行就能写出来:

function H = shannon_entropy(labels) % labels可以是数值向量或字符串元胞数组 tab = tabulate(labels); probs = tab(:, 3) / 100; % tabulate的第三列是百分比 probs = probs(probs > 0); H = -sum(probs .* log2(probs)); end

如果你手头数据已经是频数,还可以更简单。比如你统计出某特征三个取值分别对应5、5、4个样本,那熵就是:

counts = [5, 5, 4]; probs = counts / sum(counts); H = -sum(probs .* log2(probs)); % 0.xxx

用MATLAB算一维数据信息熵,最容易踩的坑是tabulate在遇到单类别数据时百分比是100,熵算出来是0,这个没问题;但如果你直接用histcounts,它默认的bins可能把一个离散类别切成多段,导致熵偏大。处理离散类别,记住一个原则:先unique去重,再数频数,别直接丢给直方图函数。

我在实际写代码时有一个习惯:算完熵先做个“空值测试”和“纯值测试”。空值测试就是传一个所有类别都不同的数组进去,熵应该等于log₂(n);纯值测试就是传一个全是同一类别的数组,熵应该是0。这两个边界值对了,公式基本没写错。很多次我写完函数,直接拿这两个用例一测,就能立刻发现符号写反或者log底数写错的问题。

6. 信息增益在决策树里的真实工作方式与“偏好取值多”的坑

现在你已经能自己算信息增益了,接下来要看它在完整决策树算法里是怎么工作的。以ID3算法为例,流程是:

  1. 计算当前数据集的熵H(D)。
  2. 对每个候选特征A,计算按A划分后的条件熵,再算增益。
  3. 选增益最大的特征作为当前节点的分裂特征。
  4. 按该特征的每个取值把数据切成子集,对每个子集递归重复1到3步。
  5. 如果某个子集的类别已经全部一致,或者没有特征可用,就停止分裂,把这个子集标记为数量最多的那个类别。

这个流程看起来很顺,但实际跑起来会遇到一个经典问题:信息增益偏爱取值多的特征

为什么?回看条件熵公式:按特征A划分后的熵是子集熵的加权平均。特征取值越多,子集就越多,每个子集里的样本越少,就越容易“纯”,加权平均熵自然偏低,增益虚高。极端情况就是我前面提过的“ID列”:每个样本一个唯一ID,切出来的每个子集只有一行,熵全是0,增益直接等于H(D),达到上限。但这样的特征对分类毫无意义。

怎么解决?有两条路。

一条是改用C4.5算法里的增益率(Gain Ratio)。增益率在信息增益基础上加了一个“惩罚项”:

GainRatio(D, A) = Gain(D, A) / H_A(D)

其中 H_A(D) 是特征A本身取值的熵,相当于“这个特征自己有多分散”。取值越多、分布越均匀的ID类特征,H_A(D)越大,分母越大,增益率就被压下来了。这是对信息增益偏好的直接修正。

另一条路更工程化:过滤式特征选择。在建树之前,先给每个特征算信息增益,设个阈值,把增益特别低的排掉。我个人的经验是,文本分类场景里特征维度动辄上万,如果直接丢给决策树,不仅慢,而且容易被大量低增益特征干扰。先按信息增益做一轮初筛,保留前几百个特征,效果通常会稳很多。

除了这个经典坑,还有一个容易忽略的细节:连续特征没法直接用信息增益分裂。信息增益的公式针对的是离散取值的特征,如果特征是连续值(比如“笔试成绩”是0到100的整数),直接把每个数值当一类,切出来的子集会碎成渣。C4.5的处理方式是二分法:先把连续值排序,然后遍历所有相邻值的中间点,把数据切成“小于等于阈值”和“大于阈值”两份,分别算信息增益,取增益最大的那个切分点。我在实际项目里验证过,这种做法在数据量上万时效果很不错,但也正是这个机制让C4.5建树的速度比ID3慢不少。

7. 几个高频疑问和我的个人经验

聊到这里,我觉得有必要把平时被问得最多的问题集中解答一下。这些问题光看教科书不会想那么细,但真上手就会碰到。

疑问一:信息增益和信息熵,到底哪个是“纯度”,哪个是“提升”?

信息熵本身就是纯度/不纯度的度量。熵越大,越不纯;熵越小,越纯。信息增益则是“划分前后纯度的提升量”,它是决策树做特征选择时的决策依据。你可以把信息熵理解为“体检指标”,信息增益理解为“治疗方案的有效性评估”。

疑问二:为什么公式里有的地方用log₂,有的用自然对数ln?

取决于底数设定。用log₂,单位是bit;用ln,单位是nat;用log₁₀,单位是dit。信息的物理含义不受底数影响,只差一个常数倍数。机器学习里绝大多数资料用log₂,因为决策树和编码理论都建立在二进制基础上;但有些统计教材和sklearn内部的mutual_info计算默认用的是自然对数,所以你看文档时要注意看它写的单位。用Python算的时候,我建议显式写np.log2,避免混用ln导致结果对不上。

疑问三:信息增益是越大越好还是越小越好?

这要分场景。做决策树分裂时,选增益最大的特征;做特征筛选时,保留增益大的特征,扔掉增益小的。但如果你在评估一个模型有没有过拟合,增益过大反而要警惕——它可能说明特征跟标签在训练集里产生了“虚假相关”。比如极端情况下,拿“候选人的邮件ID”当特征,增益爆表,但这就是过拟合的铁证。

疑问四:信息增益是负的怎么办?

正常情况不会。划分后的加权熵不会大于划分前的熵,信息增益始终大于等于0。如果你算出负值,大概率是代码写错了,概率没归一化,或者分组时把空组当成0处理出了bug。我的检查顺序是:先看概率和是否为1,再看空组的权重是否漏掉,最后看log的底数是否统一。

我个人的实操体会是:单纯背公式很难真正理解这套东西,最重要的还是亲手算一遍、写一遍代码。我当初刚开始学决策树时,花了整整一个下午用Excel手算一个20条数据的小案例,把每个特征的熵、条件熵、增益全部列出来,然后才真正明白加权平均的意义。之后再去理解C4.5的增益率、CART的基尼系数,就成了一件水到渠成的事。

另外,如果你想在项目里感受信息增益的实际效果,可以用sklearnmutual_info_classif对真实数据集跑一遍特征排序,对比一下你手算的结果。你会发现:信息增益高的特征,往往就是业务上“最能一锤定音”的那些字段;信息增益接近0的特征,要么是无关字段,要么是取值分布太极端。这个规律在招聘评估、风控评分、营销响应预测里都屡试不爽。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询