简介这是一份针对熵权法权重确定与目标函数构建的MATLAB源码资源面向需要开展多指标综合评价、决策分析或优化建模的研究者与工程人员。压缩包内包含shang.m与guiyi.m两个脚本文件整体大小仅1KB其中shang.m负责数据标准化与信息熵计算guiyi.m侧重熵权计算及归一化处理可配合使用完成从原始数据到最终权重的完整流程。资源已吸引约619人学习下载虽体量精简但函数逻辑清晰适合作为客观赋权方法的参考实现。通过运行脚本读者可直接替换评价数据获得各指标权重进而构建综合目标函数减少主观偏好对决策结果的影响提升多准则评估的可靠性与可复现性。1. 熵权法不是拍脑袋数据越乱越该用它定目标函数权重给一组待评价的方案定目标函数权重最让人头疼的往往不是打分而是权重本身怎么定。熵权法这条路线核心就一句话不靠专家拍脑袋靠数据本身的离散程度反推权重。某个指标下各方案的数值差异越大说明它携带的区分信息越多权重就该给得越高数值全都挤在一起说明它对最终排序没什么贡献权重自然低。这套思路在综合评价、多目标决策、算法评测里都很常见适合手头有完整评估矩阵、又不想让主观偏好污染权重确定过程的工程师。它不玄学但样本和预处理没做好照样翻车。2. 熵权法权重确定的三步推导信息熵、差异系数与权重之间的数学关系2.1 信息熵和权重的对应关系熵小权重大的直觉从哪来熵这个概念最早从热力学来香农把它借到信息论里定义了一个系统的混乱程度事件概率分布越均匀熵越大概率分布越集中熵越小。熵权法把这个逻辑反转过来用。把 n 个评价对象、m 个指标看成一张信息表对某个指标 j 来说如果所有对象在这个指标上的取值都差不多说明这个指标几乎不携带区分信息它的熵值接近 1给的权重就该小如果取值差异很大、有的高有的低说明这个指标能帮你把对象拉开差距熵值小权重就该大。这套直觉换成公式就是权重和熵值反向和差异系数正向。先算每个指标的熵值e_j算差异系数d_j 1 - e_j最后把d_j归一化就得到权重。整个过程没有引入任何主观评分所以被称为客观赋权法。实际做多目标决策时经常遇到 5 个甚至 10 个目标函数要合成一个综合指标AHP 那种两两比较的打分表在目标多的时候很容易自相矛盾熵权法只认数据反而能把权重确定这件事从“开会争论”变成“跑一遍脚本”。2.2 标准计算流程从原始矩阵到权重只需要五步熵权法的计算路径是固定的我一般按下面的顺序走每一步都对应一个可检查的中间结果。第一步构造原始数据矩阵X(n×m)。n 行是评价对象或者候选方案m 列是指标或者目标函数。第二步正向化处理。熵权法要求所有指标方向一致默认越大越好。成本型指标越小越好要先转换常见做法是取倒数或者用该列最大值减去当前值。第三步min-max 归一化到 [0,1] 区间消除量纲影响。第四步计算每个对象在某个指标下的比重p_ij x_ij / Σ x_ij本质上就是把该指标下所有对象的值“摊成概率”。第五步按熵公式计算熵值e_j -1 / ln(n) * Σ p_ij * ln(p_ij)前面的1/ln(n)是归一化系数保证e_j落在 0 到 1 之间。随后算差异系数d_j 1 - e_j最后权重w_j d_j / Σ d_j。这里面最容易出问题的不是公式本身而是 p_ij 取对数时遇到的零值。归一化后如果某个值恰好是 0ln(0) 直接报错所以几乎所有实现里都会加一个极小偏移量。这个偏移量是多少、放在哪个环节会直接影响权重结果后面专门讲。2.3 为什么它适合目标函数权重和 AHP、均权法的本质差别目标函数权重确定这件事本质上是在回答“多个优化目标哪个更重要”。均权法默认所有目标同等重要适合完全没有先验信息的起步阶段AHP 靠专家两两比较构造判断矩阵适合目标数量少、且决策者能给出稳定偏好的场景熵权法站在中间不预设偏好也不完全平均完全由候选方案之间的差异程度驱动。三种方法的适用边界差别很大我常用这张表来向团队解释选型理由。方法依据主观性适用场景主要短板均权法默认平等无没有先验信息的探索忽略指标重要程度差异AHP专家打分高目标数少、偏好明确目标多时判断矩阵一致性难保证熵权法数据离散度无有完整评估矩阵、样本量足够对数据质量和相关性敏感需要特别说明的是熵权法算出的权重描述的是“该指标在这个数据集里的区分力度”不是“该指标在业务上有多重要”。假如某项目标函数的取值在所有候选方案里几乎一样熵权法会给它接近 0 的权重但这不代表这个目标业务上不重要只是它在当前数据集里没有提供足够的排序信息。理解这一点后面做组合权重时才能知道它在干嘛。3. 用 Python 实现熵权法从原始矩阵到目标函数权重的完整代码3.1 最小可运行代码输入矩阵输出权重先说结论熵权法的 Python 实现不需要任何第三方库之外的依赖numpy 就够了。下面这段代码是最小可用版本我建议直接复制保存成entropy_weight.py后面的讨论都基于它。import numpy as np def entropy_weight(X): 熵权法计算指标权重 X: numpy array, shape (n_samples, n_features) 行是样本/方案列是指标/目标函数 返回: weights, shape (n_features,) # 1. min-max 归一化到 [0, 1] X_min X.min(axis0) X_max X.max(axis0) X_norm (X - X_min) / (X_max - X_min 1e-10) # 2. 非负平移避免后续 log(0) X_norm X_norm 1e-10 # 3. 计算每个方案在各指标下的比重 sum_col X_norm.sum(axis0) P X_norm / sum_col # 4. 计算熵值 n X.shape[0] k 1.0 / np.log(n) entropy -k * np.sum(P * np.log(P 1e-10), axis0) # 5. 差异系数归一化得到权重 d 1 - entropy weights d / d.sum() return weights if __name__ __main__: # 示例4 个候选方案3 个目标函数都是越大越好 X np.array([ [0.8, 0.6, 120], [0.9, 0.7, 95], [0.7, 0.8, 80], [0.6, 0.9, 60], ]) print(entropy_weight(X))这段代码的逻辑对应上一章的五步推导归一化、加偏移、算比重、算熵、算权重。要注意的三点是行和列的方向、偏移量加在哪里、以及系数k的意义。X的行是方案列是目标函数这个方向千万别搞反。如果习惯把指标放行axis0计算的逻辑会全乱。偏移量1e-10加在分母和取对数两个位置作用是双保险分母加偏移防止归一化时分子分母同时为 0 的情况取对数加偏移防止ln(0)报错。系数k用的是1/np.log(n)n 必须是样本数不是指标数写错的话熵值不会落在 [0,1] 区间里。3.2 归一化与正向化成本型目标怎么处理实际拿到的目标函数不全是越大越好。比如多目标优化里常见的“时延”“成本”“能耗”都是越小越好的成本型指标。这些数据不能直接喂给上面的函数必须先做正向化。我常用的做法是max - x而不是取倒数。def preprocess(X, cost_columns[]): 正向化 min-max 归一化 非负平移 cost_columns: 需要反向处理的列索引列表 X X.astype(float).copy() for j in cost_columns: X[:, j] X[:, j].max() - X[:, j] X_min X.min(axis0) X_max X.max(axis0) X_norm (X - X_min) / (X_max - X_min 1e-10) # 平移确保没有 0 或负值 X_shift X_norm 1e-6 return X_shift为什么不推荐取倒数因为倒数是非线性变换会放大数值小的样本之间的差异压缩数值大的样本之间的差异。一个离群值如果特别小取倒数后会变成极大的值直接主导整列的比重计算。max - x是线性变换能保留原始数据里的比例关系和分布形状对离群值的容忍度更高。归一化和平移的先后顺序也要固定先归一化再平移。如果先平移再归一化某些指标原本的范围差异会被平移量覆盖权重会整体偏向范围大的指标。1e-6这个量级是经验值足够避免零值又不至于把各样本的真实差异抹平。3.3 封装成标准函数适配多目标函数权重的接口设计上面的函数已经能直接用了但做目标函数权重时通常还要算加权综合得分所以我会把预处理和加权得分一起封装。def objective_weight_and_score(X, cost_columns[]): 熵权法确定目标函数权重并输出加权综合得分 X: shape (n_solutions, n_objectives) cost_columns: 成本型目标所在的列索引 返回: (weights, scores) X_proc preprocess(X, cost_columns) weights entropy_weight(X_proc) # 综合得分归一化后的矩阵与权重做矩阵乘法 scores X_proc weights return weights, scores这里有一个隐藏的细节entropy_weight内部对X_proc又做了一遍min-max归一化而preprocess已经把数据归一化过一次了。重复归一化不会改变列内样本的相对比例吗如果我们传入的矩阵所有列都已经在 [0,1] 区间且每列最小值都是 0、最大值都是 1那么二次归一化后数据不变结果是对的。如果某列归一化后最小值为 0.1、最大值为 0.9二次归一化会把它重新拉伸到 [0,1]相当于改变了列间的相对尺度这就会影响权重。所以正确的接口设计是preprocess只做正向化把归一化统一交给entropy_weight去做。或者反过来在objective_weight_and_score里用X_proc直接算得分把归一化逻辑收敛到一处。我倾向于后者代码里保持“只归一化一次”的原则避免这种隐蔽的二次拉伸。4. 熵权法避坑指南五个让权重翻车的真实场景4.1 离群值绑架权重一个 100 分把其他样本压成零头现象原始数据里混进一个异常大的值min-max 归一化后其他样本的值全部被压到 0.01 以下比重计算时这个离群值几乎独占整列熵值极低权重被它一家撑到离谱的高度。原因min-max 对离群值没有抵抗力最大值和最小值被极端数据控制整个归一化分布被拉伸。解决归一化之前先做分位数缩尾。我一般把超过 1% 和 99% 分位数的值拉回边界再用缩尾后的数据做熵权法。def winsorize(X, lower0.01, upper0.99): 分位数缩尾防止离群值主导权重 X_w X.copy() for j in range(X.shape[1]): lo np.percentile(X[:, j], lower * 100) hi np.percentile(X[:, j], upper * 100) X_w[:, j] np.clip(X[:, j], lo, hi) return X_w缩尾不是删除数据而是把极端值压到边界。它牺牲了离群值的绝对量换回的是权重对整体样本的代表性。对目标函数权重来说如果某个候选方案的某个目标值异常突出这个方案本来就该拿高分但它不该让其他目标函数的权重集体失真。4.2 强相关指标重复计权两个指标其实说的是一件事现象指标体系里有“完成数”和“完成率”两个指标高度相关熵权法算出来的这两个权重都偏高加起来几乎占了总量的一半导致其他指标被边缘化。原因熵权法把每个指标当成独立信息源完全不考虑指标间的相关性。两个强相关指标携带的信息几乎重合相当于同一份信息被计算了两次。解决先算相关系数矩阵把|r| 0.8的指标对找出来删掉其中一个再跑熵权法。这一步在目标函数权重场景下同样适用——如果两个目标函数本质上是一强相关保留一个就够了。这也是后文要讲的“熵权法critic”组合思路存在的根本原因critic 显式考虑了指标间的冲突性能惩罚这种重复计权。4.3 样本量太少三个方案算出来的权重没法信现象只有 3 个样本、6 个指标算出来的权重忽高忽低换一个样本权重排序就变了。原因熵值计算的归一化系数1/ln(n)在 n 很小时对概率分布的敏感度极高。3 个样本的熵值可能只有 0.3~0.7差异系数被整体放大权重看起来差异很大实际上都是噪声驱动的。解决熵权法的样本量不建议少于指标数的 3 倍。如果样本量实在不够可以做自助抽样看权重分布每次有放回抽 n 个样本重复 500 次观察每个指标权重的均值方差。如果方差大到权重排序不稳定就不要用熵权法作为唯一依据换 CRITIC 或者主观赋权。4.4 归一化方法换来换去同一份数据算出两套权重现象同事用 min-max 归一化你用 z-score 标准化同一个数据集跑出来的权重完全不一样开会时各执一词。原因z-score 会产生负值负值没法直接取对数必须平移。平移量一加原来的数据分布就被扭曲了熵值跟 min-max 的结果没有可比性。z-score 适合用于需要保留离群值影响的场景但它和熵权法天然不搭。解决锁定一套流程不要混用。我固定用正向化 → min-max → 平移 → 比重 → 熵值并且把预处理参数记录在代码配置里。任何人回跑结果时必须用同一套参数否则结论不可比。4.5 熵值接近 1权重约等于 0这个指标该不该删现象某个指标在所有样本上的取值几乎一致算出来的熵值 0.9999差异系数只有 0.0001归一化后权重接近 0。看起来该删但业务上这个指标明明很关键。原因熵权法度量的是“差异化信息量”不是“业务重要度”。数据分布越均匀权重越小是熵权法的正常行为不是 bug。解决不要直接删指标先确认数据是不是采集出了问题。如果数据确实没问题就在报告里说明该指标当前数据集未能提供足够的区分信息权重低不代表业务不重要。如果希望它参与决策可以用组合权重方案把熵权法结果和主观权重或 critic 权重做几何平均防止它被一票否决。5. 熵权法与 CRITIC 法怎么选目标函数权重确定的两个客观方案对比5.1 CRITIC 法的核心逻辑对比强度加上冲突性CRITIC 法做的是另一件事它用标准差衡量指标的对比强度标准差越大说明这个指标区分能力越强再用指标间的相关系数衡量冲突性相关系数越低说明这个指标和其他指标越不重复。综合成一个信息量指数C_j σ_j * Σ(1 - r_jk)其中σ_j是指标 j 的标准差r_jk是指标 j 与指标 k 的相关系数求和遍历所有其他指标。权重同样是C_j归一化后的结果。这个公式的价值在于它显式惩罚了强相关指标。如果一个指标和很多指标都高度相关Σ(1-r)会比较小就算它自己的标准差大最终权重也会被压低。这正是熵权法缺失的能力。5.2 两种方法的适用场景对比客观赋权领域熵权法和 CRITIC 是出场率最高的两个名字。它们都解决同一个问题——目标函数权重怎么确定但适用条件差别不小。维度熵权法CRITIC 法信息度量信息熵离散度标准差对比强度 相关系数冲突性指标相关性不考虑显式考虑数据量要求建议样本数 ≥ 3 倍指标数样本数太少时相关系数矩阵不稳定主要风险强相关指标重复计权相关系数估计受离群值影响典型场景指标独立或近似独立指标间存在明显关联实际项目里如果指标数量少、业务上能确认它们彼此独立我直接用熵权法。如果指标之间明显存在关联比如“时延”和“排队长度”“能耗”和“运行时长”我会换成 CRITIC。判断标准很简单先跑一个相关系数矩阵如果大部分相关系数的绝对值超过 0.6熵权法就要打个问号。5.3 组合权重落地熵权法critic 的几何平均方案热词“熵权法critic”对应的其实是一个很实际的诉求两种客观方法各有短板能不能合起来用。组合权重是我在高风险决策里常用的折中方案。线性加权组合是w α*w_熵 (1-α)*w_criticα 可以调几何平均组合是w sqrt(w_熵 * w_critic)。我更推荐几何平均原因是它保留了“一票否决”的属性。某个指标在一种方法下权重接近 0 时组合后也会被压得很低而线性加权会把它拉回来一部分。具体用哪种取决于业务上需不需要这种保护。下面是几何平均组合的代码。def critic_weight(X): CRITIC 法权重标准差 冲突性 std X.std(axis0) corr np.corrcoef(X.T) conflict (1 - corr).sum(axis0) info std * conflict return info / info.sum() def combined_entropy_critic(X): 熵权法 CRITIC 组合权重几何平均 w_entropy entropy_weight(X) w_critic critic_weight(X) w_comb np.sqrt(w_entropy * w_critic) return w_comb / w_comb.sum()注意这里X需要先完成正向化和归一化再传进去。组合权重的哲学是熵权法捕捉数据分布的离散度CRITIC 捕捉指标间的冲突性两者互补。但组合不是免费的它对数据质量的要求比单独使用更高——相关系数矩阵在样本量不足时本身就有噪声组合之后会把噪声也带进来。所以我的原则是样本量够≥ 3 倍指标数且指标关系复杂时才做组合样本量不足时宁可只跑熵权法。6. 拿到熵权法权重后先做这三项验证再投入使用6.1 敏感性分析给数据加一点噪声看权重稳不稳权重算出来只是第一步真正能不能投入决策要看权重对数据扰动是否敏感。我习惯的做法是对每个指标单独加 5% 的高斯噪声重新算一遍权重观察排名变化。np.random.seed(42) X_noise X * (1 0.05 * np.random.randn(*X.shape)) w_noise entropy_weight(X_noise) change np.abs(w_noise - w0) / w0如果某个指标的权重相对变化超过 20%说明这个权重被当前数据里的少数样本牵制需要回到数据清洗环节排查原因。6.2 与主观排序交叉验证解释不通的权重先别用我会把熵权法的权重排序拿给业务方看问一个问题排名前二的指标是不是业务上真正关心的。如果业务方认为最重要的指标在熵权法里垫底先别急着解释“这是客观算法”大概率是数据里有脏值或者指标放错了方向。用 AHP 或简单的主观打分做个交叉验证差异过大就回头查数据这是最便宜的质量检查。6.3 Bootstrap 稳定性检验留一法看权重排序是否翻转去掉一个样本重新计算权重重复 n 次。如果某指标的权重排序在多次留一验证里反复翻转说明它区分信息不足应该考虑和研究哪些指标真正驱动评价结果。这套流程走完我才敢把权重带到决策会上。我的习惯是权重必须能被业务逻辑解释通解释不通先查数据质量而不是急着调公式参数。希望帮到你。本文还有配套的精品资源点击获取