简介:这份资源是凸优化理论学习的心得笔记配套代码包,面向正在入门或希望系统梳理凸优化知识体系的机器学习、数据分析方向学习者。内容围绕陆吾生、方述诚教授课程的学习路径展开,推荐《Optimization Model》作为入门读物,并细致解读Boyd《凸优化》的章节结构与核心内容,同时强调线性代数与微积分基础的重要性,附上MIT《线性代数》等公开课程资源,帮助读者建立从数学基础到凸优化应用的完整认知。资源包共3个文件,以html页面、inscode配置与gitignore为主,压缩后约5KB,轻量易读,适合作为学习索引与资料导航使用。目前已有71人学习下载,可帮助读者快速定位优质教程、理解凸优化在机器学习与信号处理中的实际价值,并借鉴作者的学习节奏与心态建议。
1. 凸优化学习心得:从「看得懂公式」到「跑得动代码」的那道坎
很多人学凸优化,卡住的地方不是数学,而是从数学到代码的那一步。教材上写「目标函数是凸的,局部最优即全局最优」,你点头;可真到要写一个求解器、调一个正则项、判断某个约束能不能松弛的时候,手就停了。这个标题——凸优化学习心得,配上一份项目代码——讲的正是这件事:把凸优化的概念、判定、建模、求解,用可运行的代码串起来,让你不只是「看懂」,而是能自己搭一个最小可用的凸优化求解流程,并知道每一步的参数为什么这么设。
它适合两类人:一类是刚学完线性代数和微积分、想动手做机器学习或信号处理里优化问题的学生;另一类是做工程落地、被「非凸模型调不动」折磨过、想回头补凸优化这块地基的从业者。核心价值不在于背下多少定理,而在于建立一套判断链路:这个问题是不是凸的、能不能转成凸的、转不了该用什么替代、转成了又该怎么选求解器和参数。下面我按自己踩过的顺序,把这条链路拆开讲。
2. 先分清凸与非凸:判定链路和三个必查条件
凸优化最容易被低估的一步,是「判定」。很多人拿到问题直接上求解器,跑不出结果才回头怀疑模型,这时候已经浪费了大量时间。我的习惯是:动手写代码之前,先用纸笔或符号计算把凸性判定走一遍,判定不过,后面全是白费。
2.1 凸集、凸函数与凸优化问题的定义边界
一个优化问题要称为凸优化问题,需要同时满足三件事:目标函数是凸函数、不等式约束函数是凸函数、等式约束函数是仿射函数。注意最后一条——等式约束必须是仿射的,也就是形如 $a^T x = b$ 的线性形式。如果等式约束里出现 $x^2$ 或者 $xy$ 这种项,哪怕目标函数再凸,整个问题也不再是凸优化问题。
凸集的定义是:集合内任意两点的连线仍在集合内。凸函数则是定义域为凸集、且函数图像上任意两点连线不低于函数曲线。这两个定义看起来抽象,但落到代码里,判定往往靠的是几个可操作的充分条件,而不是硬套定义。
常见的可操作判定手段有三类:一是看函数是否属于已知凸函数族(范数、二次型、指数、负熵、log-sum-exp 等);二是用保凸运算组合(非负加权和、仿射复合、逐点最大值、部分最小化);三是查二阶条件,海森矩阵半正定。前两类在建模阶段最实用,第三类适合小规模问题做验证。
2.2 用代码验证凸性的最小流程
光靠眼睛看容易翻车,尤其是复合函数。我一般会写一段数值验证:在定义域内随机采样,检查函数值是否满足凸性不等式 $f(\theta x + (1-\theta) y) \le \theta f(x) + (1-\theta) f(y)$。这不是严格证明,但能快速筛掉明显非凸的候选。
import numpy as np def check_convexity(f, x, y, n_samples=1000, tol=1e-6): """ 数值检验 f 在 x, y 连线上的凸性。 f: 目标函数,输入一维或二维 ndarray x, y: 定义域内两个点 tol: 允许的数值误差 返回: True 表示未发现违反凸性的采样点 """ for t in np.linspace(0, 1, n_samples): z = t * x + (1 - t) * y lhs = f(z) rhs = t * f(x) + (1 - t) * f(y) if lhs > rhs + tol: print(f"凸性违反: t={t:.4f}, f(z)={lhs:.6f}, 插值={rhs:.6f}") return False return True # 示例:二次函数 f(x) = x^T A x,A 半正定 A = np.array([[2.0, 0.5], [0.5, 1.0]]) f = lambda v: v @ A @ v x0 = np.array([1.0, 0.0]) y0 = np.array([0.0, 1.0]) print(check_convexity(f, x0, y0)) # 应输出 True这段代码的逻辑很直接:沿两点连线均匀采样,逐点比较函数值与线性插值。参数n_samples控制采样密度,太小会漏掉局部违反,太大则浪费时间,一般 1000 足够。tol是数值容差,浮点运算下严格相等几乎不可能,留一点余量避免误报。需要提醒的是,这个检验只能证伪不能证明——它没报错不代表函数一定凸,只代表在这条线上没发现问题。要更可靠,得换多组随机点重复跑。
2.3 三个必查条件与常见误判
实际建模时,我总结出三个必查条件,按顺序过一遍能挡掉大部分错误。
第一,目标函数里有没有出现变量的乘积项或变量的非线性函数嵌套。比如 $x_1 x_2$ 这种双线性项,单独看非凸,但如果问题结构允许,可以通过变量替换或松弛转成凸的。第二,约束里的等式是不是仿射的。很多人在建模时顺手写了个 $x^2 + y^2 = 1$ 的等式约束,这直接把问题踢出凸优化范畴,正确做法是改成不等式约束 $x^2 + y^2 \le 1$,再配合目标函数方向处理。第三,定义域是不是凸集。如果目标函数里含 $\log$ 或 $1/x$,定义域天然被限制在正半轴,这个隐式约束必须显式写进模型,否则求解器会在非法区域试探导致报错。
常见误判里最典型的是「把非凸当凸」。比如稀疏正则里的 $L_0$ 范数,很多人以为它和 $L_1$ 差不多,实际上 $L_0$ 是非凸的,$L_1$ 才是它的凸松弛。另一个误判是「把凸当非凸」,比如某些看起来复杂的范数组合,其实用保凸运算一拆就是凸的,白白放弃了全局最优的保证。判定这关过不去,后面选求解器、调参数都是空中楼阁。
3. 从数学到代码:把凸问题写成求解器能吃的形式
判定通过之后,下一步是建模。凸优化建模的核心思路是:把问题整理成求解器认识的标准形式,通常是 $\min f_0(x)$,约束 $f_i(x) \le 0$,$Ax = b$。这一步的难点不在数学,而在「翻译」——把业务语言翻译成标准形式,同时保持凸性不被破坏。
3.1 标准形式转换与变量替换技巧
标准形式要求目标函数和不等式约束都是凸的,等式约束是仿射的。很多原始问题不长这样,需要做变量替换。常见的替换有几类:一是把最大化问题取负号转成最小化;二是把 $x > 0$ 的隐式约束显式写成 $-x \le 0$;三是用对数变换或平方变换把某些非凸表达式转凸。
举个典型例子:原始问题是 $\max \prod_{i} x_i$,约束 $x_i > 0$。直接看是非凸的,但取对数后变成 $\max \sum_i \log x_i$,$\log$ 是凹函数,最大化凹函数等价于最小化凸函数 $-\sum_i \log x_i$,这就转成了凸问题。这个技巧在资源分配、概率建模里非常常见。
变量替换的另一个作用是改善数值条件。比如变量取值范围跨越好几个数量级时,直接求解容易遇到病态矩阵,这时对变量做缩放,让各维度量级接近,求解器收敛会稳很多。我一般会在建模阶段就检查各变量的典型量级,差三个数量级以上就考虑缩放。
3.2 用 CVXPY 搭一个可复现的凸优化模型
CVXPY 是凸优化建模里比较顺手的工具,它把「判定凸性」和「调用求解器」两件事都包了。下面用一个带 $L_2$ 正则的最小二乘问题做示例,这是最经典的凸问题之一。
import cvxpy as cp import numpy as np np.random.seed(0) m, n = 50, 20 A = np.random.randn(m, n) b = np.random.randn(m) lambda_reg = 0.1 # 正则化强度,控制拟合与稀疏的权衡 x = cp.Variable(n) # 目标:最小二乘 + L2 正则,整体是凸的 objective = cp.Minimize(cp.sum_squares(A @ x - b) + lambda_reg * cp.sum_squares(x)) # 约束:变量非负,显式写出保证定义域合法 constraints = [x >= 0] problem = cp.Problem(objective, constraints) # 求解,指定求解器;不指定则自动选择 problem.solve(solver=cp.OSQP, verbose=True) print("最优值:", problem.value) print("最优解前五个分量:", x.value[:5]) print("求解状态:", problem.status)这段代码里,cp.Variable(n)声明优化变量,cp.Minimize包住目标函数,constraints列表放约束。关键参数是lambda_reg,它控制正则项权重:调大解更平滑但拟合变差,调小拟合更好但可能过拟合,实际中常用交叉验证选。solver=cp.OSQP指定用 OSQP 求解,它适合二次规划类问题;如果不指定,CVXPY 会按问题类型自动挑,但自动挑有时会选到较慢的通用求解器,明确指定能省时间。verbose=True会打印迭代过程,排查不收敛时很有用。
求解完一定要看problem.status,它可能是optimal、infeasible、unbounded或solver_error。很多人只看problem.value,结果拿到一个不可行解还在那调参,这是典型的血泪经验。
3.3 求解器选择与参数初值设置
求解器选择没有万能答案,但有几条经验规则。问题规模小、精度要求高,用内点法类求解器,收敛稳但每步开销大;问题规模大、结构稀疏,用一阶方法类求解器,每步便宜但迭代多。二次规划优先考虑专用求解器,锥规划看具体锥类型。
参数初值方面,凸问题理论上对初值不敏感,因为局部最优即全局最优,但数值实现里初值仍会影响收敛速度甚至是否收敛。我的习惯是:如果问题有物理意义,用物理上的合理估计做初值;如果没有,用零向量或小随机向量。另外,求解器的容差参数不要一上来就设得极严,先用默认值跑通,再逐步收紧,否则容易在数值噪声上卡住。
4. 避坑与排查:凸优化落地时最容易翻车的五个地方
这一章是我自己踩过的坑,按「现象 → 原因 → 解决」写,每条都对应真实会遇到的场景。
4.1 求解器报 infeasible 但模型看起来没问题
现象:模型写得好好的,求解器返回infeasible,检查约束也没写错。
原因:多半是约束之间隐含矛盾,或者定义域没写全。比如变量实际要求为正,但没显式写 $x \ge 0$,求解器在负半轴试探时触发目标函数里的 $\log$ 或开方,数值上表现为不可行。
解决:把所有隐式定义域约束显式写出来,尤其是分母、对数、开方涉及的变量。再用一个小规模可行点代入验证约束是否真的满足。
4.2 迭代不收敛,残差来回震荡
现象:求解器跑了很多轮,残差曲线上下震荡,不下降。
原因:问题尺度差异太大,或者正则化参数设得让问题接近病态。也可能是求解器类型和问题结构不匹配。
解决:先做变量缩放,让各维度量级接近;再检查正则项系数,必要时用网格搜索找合理范围;最后换一个求解器对比,如果换了就收敛,说明是求解器匹配问题。
4.3 解出来了但业务上不可用
现象:status是optimal,但解的值在业务上没意义,比如出现极端值或全零。
原因:目标函数和约束没完整表达业务需求,或者正则化过强把解压平了。
解决:回头检查建模阶段是否漏了约束,比如上下界、整数性(注意整数约束会破坏凸性,需要特殊处理);再调正则化系数,从 0 开始逐步增大,观察解的变化。
4.4 数值精度导致判定凸性时误判
现象:数值检验凸性时偶尔报违反,但理论上函数是凸的。
原因:浮点误差在函数值接近时被放大,尤其是函数值本身很小的时候。
解决:放宽容差tol,或者对函数做尺度归一化后再检验。如果只是个别点报违反且幅度在容差附近,可以忽略。
4.5 大规模问题内存爆掉
现象:问题规模上去后,构造矩阵时内存不足。
原因:稠密矩阵存储和运算,没有利用稀疏结构。
解决:用稀疏矩阵格式存储,检查约束和目标里哪些项是稀疏的;必要时用矩阵-free 方法,只提供矩阵向量乘积而不显式构造矩阵。
5. 进阶技巧:用对偶和热启动把求解速度提上去
凸优化的进阶用法里,对偶和热启动是两个性价比很高的方向。对偶的价值在于:有些原问题约束多、变量少,转成对偶后变量多、约束少,求解反而更快;而且对偶变量本身往往有物理解释,比如影子价格。热启动则是在需要反复求解相似问题时,用上一次的解做初值,能显著减少迭代次数。
对偶的构造有固定套路:拉格朗日函数、对偶函数、对偶问题三步走。以带等式约束的二次规划为例,原问题 $\min \frac{1}{2}x^T Q x + c^T x$,约束 $Ax = b$,对偶问题可以写成关于对偶变量的无约束二次问题,规模通常更小。代码上,CVXPY 不直接暴露对偶变量,但求解后可以通过constraint.dual_value拿到,这对做灵敏度分析很有用。
热启动在 CVXPY 里不是所有求解器都支持,但 OSQP 这类一阶方法支持传入初值。做法是把上一次的x.value作为warm_start参数传进去。实测在参数缓慢变化的序列问题上,迭代次数能降一半以上。不过要注意,热启动对初值质量敏感,如果两次问题差异太大,反而可能拖慢收敛,所以我的习惯是只在问题结构相同、参数小幅变化时用。
验证求解结果是否可信,除了看status,我还会做两件事:一是用对偶间隙判断,原问题值和对偶问题值差距在容差内才可信;二是把解代回原始约束,逐条检查违反程度。这两步做完,基本能排除大部分数值陷阱。
最后说个我自己的习惯:每次调完参数,把配置和结果记一行日志,包括问题规模、求解器、关键参数、迭代次数、最终残差。凸优化调参不像深度学习那样玄学,但参数和结果之间的对应关系如果不记,过两周就忘了,回头复现等于重来。这个习惯帮我省了很多后悔药。希望帮到你。
本文还有配套的精品资源,点击获取