4. 加权和法详解:原理、权重选择、优缺点分析、Python代码实现
加权和法,说白了就是多目标优化里最朴素、最直接的一种思路。你想想看,我们面对一堆互相打架的目标——比如又要成本低,又要性能高,还要寿命长——怎么办?最简单的办法就是给每个目标打个分,然后按重要性加权求和,变成一个单目标问题来解。
我刚开始做工业项目时,第一个接触的就是这个方法。当时觉得它太简单了,甚至有点瞧不上。后来踩过几次坑才发现,简单不等于没用。很多实际问题,用加权和法反而比那些花里胡哨的算法更靠谱。
4.1 原理:把多目标变成单目标
加权和法的数学表达其实就一行公式:
min F(x) = w₁·f₁(x) + w₂·f₂(x) + ... + wₘ·fₘ(x)
其中 wᵢ 是第 i 个目标的权重,fᵢ(x) 是第 i 个目标函数。权重之和通常归一化为 1,即 ∑wᵢ = 1。
说白了,就是把多个目标函数线性组合成一个综合指标。然后你只需要用单目标优化的方法去求解就行了——梯度下降、遗传算法、粒子群,随便你。
核心思想:通过调整权重 wᵢ,你可以让优化器偏向不同的目标。权重越大,对应的目标就越被重视。
这里有个关键点:加权和法只能找到帕累托前沿上的凸部分。如果帕累托前沿是非凸的,那有些最优解你永远找不到。嗯,这个坑我后面会细说。
4.2 权重选择:没有银弹,但有经验
权重怎么选?这是加权和法最让人头疼的地方。我见过不少新手,上来就拍脑袋给个 0.5、0.3、0.2,结果跑出来的结果根本不能用。
我个人习惯用以下几种方法:
4.2.1 等权重法
最简单,所有目标权重一样。适合你对目标重要性完全没概念的时候。但说实话,实际项目中很少这么干——因为不同目标的量纲和数值范围往往差很多。
4.2.2 目标归一化 + 权重
先对每个目标做归一化处理,再乘以权重。这样能避免数值大的目标主导优化过程。公式如下:
f'_i(x) = (f_i(x) - f_i_min) / (f_i_max - f_i_min)
我在做汽车轻量化设计时,就吃过这个亏。当时车身重量是几百公斤,碰撞能量是几千焦耳,直接加权求和,重量目标完全被淹没了。后来做了归一化,结果才合理。
4.2.3 层次分析法
如果你有多个决策者,或者目标重要性需要主观判断,可以用 AHP。它通过两两比较构建判断矩阵,然后计算特征向量得到权重。我个人觉得这方法有点繁琐,但确实能给出相对合理的权重。
4.2.4 自适应权重
在优化过程中动态调整权重。比如一开始均匀探索,后期逐渐聚焦到某个区域。这个方法我在做多目标进化算法时用过,效果不错,但实现起来稍微复杂点。
我的建议:如果你刚开始用加权和法,先试试等权重 + 归一化。跑几轮看看结果,再根据实际情况调整。别一上来就搞复杂的权重策略。
4.3 优缺点分析:别被它的简单骗了
| 优点 | 缺点 |
|---|---|
| 实现简单,代码量少 | 只能找到凸帕累托前沿上的解 |
| 计算效率高,适合大规模问题 | 权重选择主观性强,影响结果 |
| 容易理解,非专业人士也能看懂 | 不同目标量纲不一致时需要归一化 |
| 可以灵活调整权重来探索不同偏好 | 无法处理非凸、不连续的帕累托前沿 |
| 与大多数单目标优化器兼容 | 权重变化与解的变化不一定线性 |
我曾经在一个项目中,用加权和法去优化一个非凸问题。跑了上百次,换了各种权重组合,始终找不到某个区域的解。后来换成 NSGA-II,一下子就找到了。嗯,这就是加权和法的局限性——它只擅长凸问题。
避坑指南:如果你的问题帕累托前沿可能是非凸的,别只用加权和法。可以先用加权和法快速探索,再用其他方法精细搜索。我曾经因为只用加权和法,差点错过一个关键的设计方案。
4.4 Python代码实现:从零开始写一个加权和优化器
下面我给出一个完整的 Python 实现。这个例子解决一个简单的双目标优化问题:
- 目标1:最小化 f₁(x) = x²
- 目标2:最小化 f₂(x) = (x-2)²
我们用加权和法,配合 scipy 的优化器来求解。
import numpy as np
from scipy.optimize import minimize
def weighted_sum_objective(x, weights, objectives):
"""
加权和目标函数
x: 决策变量
weights: 权重向量,如 [0.5, 0.5]
objectives: 目标函数列表,如 [f1, f2]
"""
# 计算每个目标的值
f_values = [obj(x) for obj in objectives]
# 加权求和
total = sum(w * f for w, f in zip(weights, f_values))
return total
# 定义两个目标函数
def f1(x):
return x[0]**2
def f2(x):
return (x[0] - 2)**2
# 目标函数列表
objectives = [f1, f2]
# 不同的权重组合
weight_sets = [
[1.0, 0.0], # 只优化目标1
[0.8, 0.2],
[0.6, 0.4],
[0.5, 0.5], # 等权重
[0.4, 0.6],
[0.2, 0.8],
[0.0, 1.0] # 只优化目标2
]
# 存储结果
results = []
for weights in weight_sets:
# 定义当前权重下的目标函数
def obj_func(x):
return weighted_sum_objective(x, weights, objectives)
# 使用SCIPY优化
res = minimize(obj_func, x0=[0.0], method='BFGS')
x_opt = res.x[0]
f1_opt = f1([x_opt])
f2_opt = f2([x_opt])
results.append({
'weights': weights,
'x': x_opt,
'f1': f1_opt,
'f2': f2_opt
})
print(f"权重 [{weights[0]:.1f}, {weights[1]:.1f}] -> "
f"x={x_opt:.4f}, f1={f1_opt:.4f}, f2={f2_opt:.4f}")
# 输出结果
print("\n=== 帕累托解集 ===")
for r in results:
print(f"f1={r['f1']:.4f}, f2={r['f2']:.4f}")
运行这段代码,你会看到不同权重下得到的解。当权重偏向 f1 时,x 接近 0;偏向 f2 时,x 接近 2。等权重时,x 在 1 附近——这是两个目标的折中方案。
实战技巧:在实际项目中,我通常不会只跑一组权重。我会生成 10-20 组均匀分布的权重,跑出对应的解集,然后让决策者从中挑选。这样既利用了加权和法的效率,又保留了多目标优化的灵活性。
4.5 知识体系结构图
下面我用一张 SVG 图来展示加权和法的核心逻辑和知识体系:
这张图把加权和法的四个核心模块串起来了。从原理出发,到权重选择策略,再到优缺点分析,最后落地到代码实现。你可以在实际项目中对照这个框架来使用。
好了,关于加权和法就聊这么多。它虽然简单,但确实是多目标优化工具箱里的一把好手。记住它的适用场景和局限性,你就能在项目中用好它。
公众号:蓝海资料掘金营,微信deep3321