资讯动态

近似梯度下降全解析:从原理到LASSO稀疏优化实战

发布时间:2026/10/14 15:51:16 来源:尧图企业网站定制
在机器学习、信号处理和统计建模里但凡遇到带稀疏性的问题比如LASSO回归、图像去噪、压缩感知重建背后几乎都站着同一个核心方法近似梯度下降。这个算法在中文资料里往往被包装成近端梯度下降Proximal Gradient Method这类有点劝退的名字但它本质上就一句话——对光滑部分做梯度下降对非光滑部分做近端算子两者交替进行。这篇内容就专门把这个方法从头到尾拆开讲清楚包括它到底解决什么问题、每一步在做什么、收敛性怎么保证以及我实际调参时踩过的一些坑。如果你在学最优化、在看深度学习之外的传统优化模型或者只是想把稀疏正则项做成一个可以稳定迭代求解的算法这篇文章应该能帮你把链路彻底打通。我默认你手上有基本的梯度下降和凸函数知识但即便你有点生疏我也会在关键地方把直觉捡回来。1. 从梯度下降的死穴说起1.1 光滑损失函数的天花板标准的梯度下降解决的是这样一个问题$$ \min_x ; f(x) $$其中 $f$ 是光滑凸函数比如平方损失 $\frac{1}{2}|Ax - b|^2$。这个函数的性质很好梯度处处存在且利普希茨连续于是一路沿着反梯度方向滑下去就能找到极小值点。收敛速度依赖于梯度映射的利普希茨常数步长只要取到 $L$ 以内就能保证函数值不上升。这套逻辑对纯光滑问题是完美的。但现实问题往往不是纯光滑的我拿一个最常见的例子来说明$$ \min_x ; \frac{1}{2}|Ax - b|^2 \lambda |x|_1 $$第一项是光滑的第二项 $\lambda|x|_1$ 在 $x_i 0$ 的位置不可导。梯度下降走到零点附近时这个非光滑项没有明确的梯度信号步长再小也会在零点附近来回抖动卡在稀疏解附近的锯齿区里。换句话说梯度下降在处理非光滑 光滑的复合目标时要么收敛极慢要么最终解的稀疏性完全达不到预期。1.2 撕开一个口子复合目标函数的结构与其被这种不光滑但结构简单的目标函数折磨不如接受一个事实很多非光滑项本身是好对付的。$|x|_1$ 虽然不可导但它的结构极其简单每个坐标互相独立我们可以直接写出它的闭式解映射。$|x|_2$ 同样如此球面约束或者范数约束都有简单的投影公式。于是就有了一个自然的拆分思路。把目标函数写成$$ F(x) f(x) g(x) $$其中 $f$ 光滑、凸、梯度利普希茨连续$g$ 非光滑但有简单结构。近似梯度下降就是针对这种夫妻档问题设计的光滑部分交给梯度下降非光滑部分交给近端算子。两种操作都是熟面孔合在一起却能把一大类实际问题解得很漂亮。提示判断一个非光滑项是否好对付核心标准是它的近端算子能不能高效求解。稀疏、低秩、投影约束这几类都可以这也是为什么近似梯度下降在稀疏学习和低秩恢复领域地位如此稳固。2. 近端算子的直觉与定义2.1 用一个生活类比理解近端算子近端算子的公式长这样$$ \text{prox}_{g}(y) \arg\min_x \left( g(x) \frac{1}{2}|x - y|^2 \right) $$第一次看到这个式子的人往往会懵这不是又构造了一个新的优化问题吗一个迭代算法内部还要做优化这不是套娃吗换个类比就清楚了。把 $y$ 想成一个刚从手里扔出去、落在泥地里的玻璃球它现在的位置 $y$ 可能不太规整。近端算子做的工作是在一个模型里面即过度惩罚偏离又照顾 $g$ 的偏好把玻璃球轻轻推回一个合规的位置。如果 $g$ 是 $0$近端算子啥也不干球留在原地$\text{prox}_g(y) y$。如果 $g$ 是某个约束集合的指示函数那近端算子就是向这个集合做欧氏投影。如果 $g$ 是 $|x|_1$球会被推回零轴附近并且在原点附近产生压扁效果这就是软阈值。系数 $\frac{1}{2}$ 的本质是控制回归原位的拉力和结构偏好的推力之间的平衡。$y$ 越远拉回性价比越高$g$ 越强越倾向于待在 $g$ 偏好的区域。2.2 常见近端算子的闭式解实战中用到的基本就四类我直接列出来第一类是 $\ell_1$ 范数的软阈值算子$$ \text{prox}_{\lambda|\cdot|_1}(y) \text{sign}(y_i) \cdot \max(|y_i| - \lambda, 0) $$这是最经典的公式稀疏问题的核心。它做两件事把绝对值小于 $\lambda$ 的坐标直接清零大于 $\lambda$ 的坐标向原点收缩 $\lambda$。第二类是 $\ell_2$ 范数的块软阈值$$ \text{prox}_{\lambda|\cdot|_2}(y) \left(1 - \frac{\lambda}{\max(|y|_2, \lambda)}\right) y $$整组坐标要么一起保留要么一起压缩到零不产生组内稀疏。第三类是二次项 $\frac{\mu}{2}|x - a|^2$ 的近端算子它等价于在 $y$ 和 $a$ 之间做一个加权平均本质是把先验信息组合进来。第四类是指示函数 $I_C(x)$当 $x$ 属于凸集 $C$ 时为 $0$否则为 $\infty$。它的近端算子就是对集合 $C$ 的投影比如非负象限、概率单纯形、球面约束。注意近端算子能成为实用工具就靠这些闭式解。如果一个非光滑项没有闭式近端那用它做近端梯度下降每次迭代都要内嵌一个复杂优化整体代价就失控了。3. 近似梯度下降算法推导3.1 从两步操作到迭代框架现在把 $f$ 和 $g$ 合起来。近似梯度下降的迭代式是$$ x_{k1} \text{prox}_{t_k g}\left( x_k - t_k \nabla f(x_k) \right) $$流程分两步。第一步先在光滑函数 $f$ 上走一个梯度下降步得到一个中间点$$ z_k x_k - t_k \nabla f(x_k) $$第二步对这个中间点做 $g$ 的近端操作把结果作为下一步的位置。从推导的角度看这个迭代式来自对目标函数的近端梯度近似$$ F(x) \approx f(x_k) \langle \nabla f(x_k), x - x_k \rangle \frac{1}{2t_k}|x - x_k|^2 g(x) $$去掉与 $x$ 无关的常数项等价于$$ x_{k1} \arg\min_x \left( \frac{1}{2t_k}\left|x - \left(x_k - t_k \nabla f(x_k)\right)\right|^2 g(x) \right) $$这个式子的几何含义非常清楚在 $x_k$ 附近用一个简单的二次函数来替代光滑部分 $f$再叠加上非光滑部分 $g$然后求解这个局部近似问题。$t_k$ 越大局部近似越宽松越信任当前梯度信息往前多走$t_k$ 越小越保守留在原地附近。3.2 步长选择与梯度利普希茨常数步长 $t_k$ 的选取直接决定收敛性。如果 $f$ 的梯度利普希茨常数为 $L$也就是$$ |\nabla f(x) - \nabla f(y)| \le L |x - y| $$那么当 $t_k \in (0, 1/L]$ 时迭代能够保证目标函数值单调不增这也是最稳妥的选择。实际使用里$L$ 可以显式计算比如平方损失 $\frac{1}{2}|Ax - b|^2$ 的梯度利普希茨常数就是矩阵 $A$ 最大奇异值的平方即 $L \sigma_{\max}(A)^2$也是 $A^T A$ 的最大特征值。如果 $L$ 算不准或者不方便算可以用回溯线搜索每轮从一个较大的步长尝试如果目标函数值相对近似模型下降得不够多就把步长缩小一半重复检查。这样做的好处是自适应性坏处是每轮可能多算几次目标函数代价在数据量大的时候需要权衡。实操心得我个人更推荐先算谱范数。用幂法迭代几十步就能拿到一个高精度估计比回溯干净利落迭代过程也稳定。回溯后面一般作为兜底方案处理那些 $L$ 动态变化或者没法方便估计的场景。4. 收敛性分析与不动点条件4.1 基本收敛速度在凸且 $L$ 光滑的假设下近似梯度下降的收敛速度与标准梯度下降相当都是函数值以 $O(1/k)$ 的速度趋近最优值$$ F(x_k) - F(x^) \le \frac{L|x_0 - x^|^2}{2k} $$这个速度对于一个一阶方法来说是标准档位不算慢但也不算惊艳。当目标函数强凸性更强时可以达到线性收敛速。所谓线性收敛是说误差每轮乘以一个固定比例因子像利息复利反过来一样衰减。那个比例因子由条件数决定条件数越大越慢。4.2 加速版本从 $O(1/k)$ 到 $O(1/k^2)$既然标准版是 $O(1/k)$那自然有人想到能不能做加速。经典的加速方案是这样做的在原有迭代的基础上引入一个外推点$$ y_k x_k \frac{\theta_k(1 - \theta_{k-1})}{\theta_{k-1}}(x_k - x_{k-1}) $$然后对 $y_k$ 执行近端梯度步再更新 $\theta$ 序列。这个加速虽然每一步只多了一次外推计算却能把收敛率提升到 $O(1/k^2)$在光滑凸优化里这是最优的一阶方法收敛速度理论上没有普通一阶方法能越过这个界限。加速版本对步长更敏感也更需要配合适当的重启策略。实操里我见过不少加速后震荡甚至发散的情况尤其当目标函数本身条件数很差时。一个常见的做法是设定一个自适应重启条件当新目标函数值大于上一轮时强制把外推序列重置到当前迭代点这种重启版既兼顾速度又不容易发散。4.3 不动点与最优性条件如何判断迭代是否已经收敛到了最优解关键在不动点条件$$ x^* \text{prox}_{t g}(x^* - t \nabla f(x^*)) $$这个条件与最优性条件等价。它刻画了这样一个事实在最优解处光滑部分想沿着负梯度方向移动非光滑部分想维持结构两者通过近端算子达成平衡。这个条件不仅仅是一个理论结论也可以直接用来做停机判断。每轮迭代后计算$$ \delta_k |x_k - \text{prox}_{t g}(x_k - t \nabla f(x_k))| $$$\delta_k$ 越小说明越接近不动点也就是越接近最优解。我实践中的习惯是把这个残差作为主停机准则而不是单纯看目标函数值的变化量因为在稀疏问题里目标函数值变化很小的时候残差可能还在明显移动过早停止会导致解不够稀疏。5. 实操案例用近似梯度下降解一个LASSO问题5.1 一个小规模示例的完整配置我做一个具体的例子。设观测矩阵 $A$ 是 $100 \times 250$ 的随机高斯矩阵真实信号 $x_{\text{true}}$ 是一个只有 10 个非零分量且其余为 0 的稀疏向量观测 $b A x_{\text{true}} \epsilon$其中 $\epsilon$ 是小幅高斯噪声。我们要解$$ \min_x \frac{1}{2}|A x - b|_2^2 \lambda |x|_1 $$此时 $f \frac{1}{2}|Ax - b|^2$梯度是 $A^T(Ax - b)$$L \sigma_{\max}(A)^2$。非光滑部分是 $\lambda |\cdot|_1$近端算子用软阈值。单次迭代的计算量主要是两个矩阵向量积一个算 $Ax$一个算 $A^T(Ax - b)$。对 $250$ 维的向量来说完全不是负担但把维度换成上万甚至百万这套流程依然成立因为代价永远线性增长。迭代代码如下import numpy as np def soft_threshold(z, threshold): return np.sign(z) * np.maximum(np.abs(z) - threshold, 0.0) def solve_lasso(A, b, lam, max_iter2000, tol1e-9): m, n A.shape x np.zeros(n) L max(np.linalg.eigvalsh(A.T A)) step 1.0 / L for k in range(max_iter): grad A.T (A x - b) z x - step * grad x_new soft_threshold(z, lam * step) residual np.linalg.norm(x_new - x) x x_new if residual tol: print(fconverged at iteration {k}) break return x这个实现是骨架级的真实项目里需要加的东西很多用幂法估算 $L$ 而不是直接eigvalsh大规模场景下这个调用是灾难、对梯度做预计算缓存、加加速和重启。但骨架已经包含了近端梯度方法的全部灵魂。5.2 正则化系数与稀疏率的影响$\lambda$ 的选择会极大影响最终解的行为。$\lambda$ 很小的时候软阈值几乎不发挥作用解偏向于最小二乘解但会充满小分量$\lambda$ 过大的时候所有坐标都被压到零解全部稀疏但严重偏离真实信号。实践中可以用一个简单的启发式来设定 $\lambda$ 的上界。从不动点条件看如果最优解是 $0$那就要求$$ |\text{prox}_{\lambda t |\cdot|_1}(-t \nabla f(0))|_2 0 $$即梯度在原点处的绝对值最大值不超过 $\lambda$。所以一个合理的 $\lambda_{\max}$ 就是 $|A^T b|\infty$ 乘以一个略小于 1 的系数。超过这个上界再配合一定步长就直接全部稀疏为零了。实际操作往往在这个 $\lambda{\max}$ 附近按等比数列扫描一组值看交叉验证误差选出一个合适的。5.3 停机判断与参数演进在迭代过程中把目标函数值、近端残差、非零分量数一起打印出来是最有效的监控方式。我经常遇到目标函数已经稳定到小数点后六位但近端残差还在 $10^{-3}$ 量级的场景这说明解还没完全收敛到稀疏图案上。再跑几十轮后残差掉到 $10^{-6}$ 量级非零分量数目才会真正稳定。只看目标函数定停机很容易得到一个差不多但不够干净的解。避坑提示软阈值操作天然会造成分量在一个比较早的阶段就归零此时如果把非零分量数当作唯一指标容易过早自信。非零分量收敛往往晚于目标函数收敛要以近端残差为核心参考。6. 常见问题与排查技巧实录6.1 迭代发散或震荡最常见的原因是步长超过了 $1/L$。排查方法很简单在迭代中监控目标函数值如果连续几轮上升优先把步长减半。另一个原因是目标函数本身非凸或条件数极差这种情况标准近似梯度下降会很挣扎得靠加速加重启或者预处理。矩阵预处理是个实用招数。对问题做变量替换 $x D^{-1}y$把光滑部分的 Hessian 逼近一个单位阵从而降低条件数。在稀疏恢复里这个操作可以大幅加快收敛但要注意正则项在替换后也要相应调整。6.2 近端残差降不下去这种问题一般出在步长选择太保守。步长小的时候迭代稳定但每一步走得很近近端残差每轮下降的幅度也很小。解决办法是检查 $L$ 估计是否偏大或者直接上回溯线搜索配合加速让算法自动把有效步长撑到边界。还有一种情况是数据本身的尺度差异太大某个特征的梯度量级天然偏大导致全局步长被这些强势特征牵制。特征归一化在这里比换算法更有效把每一列特征缩放到单位方差后情况会好很多。6.3 与ADMM和原始对偶方法的选型取舍不同问题适合不同的分解方式。近似梯度下降适合非光滑项结构简单、近端算子好算的场景。如果 $g$ 中包含两个都需要耦合、没法直接拆开的项比如同时有 $\ell_1$ 和线性约束那近似梯度下降的迭代内会冒出子问题不如直接上ADMM或者原始对偶方法。我把经验整理成一个速查表问题特征推荐方法理由光滑项 简单稀疏正则近似梯度下降每次迭代只有梯度矩阵向量积和软阈值代价极低两个非光滑项耦合ADMM每次剥一个算子分而治之强凸 光滑加速近似梯度下降配合重启能拿到线性收敛非光滑部分无近端闭式解子梯度法或原始对偶不要硬套近端框架大规模但精度要求高二阶或拟牛顿方法一阶方法的线性收敛在这种场景下不够用6.4 从一阶方法到实践体系的几点个人经验写到这里我给几个自己这几年反复验证过的建议。第一先用小规模数据把算法链路跑通再考虑优化和扩展。小规模场景下每一步算得清清楚楚你能直观感受到前几步梯度下降的猛冲和之后软阈值慢慢雕琢稀疏结构的过程。这种体感是看公式代替不了的。第二为真实项目封装迭代日志。记录每一轮的轮次、函数值、近端残差、非零占比、步长。这一步看起来很笨但排查问题时能节省几倍时间。第三把加速、重启、回溯线搜索做成可选模块而不是全部写死在主循环里。我在实际项目中通常默认组合是幂法估计 $L$ 固定步长 加速 自适应重启。只有在遇到具体问题时才逐个关掉模块做对比能迅速找到问题在哪一层。第四别迷信某一个方法。近似梯度下降虽然通用但遇到每个分量都有不同尺度的稀疏问题块坐标下降往往更好用。遇到约束复杂的目标ADMM更顺手。把近端这套算子思维内化好眼里就不再是某一个算法而是一个可以灵活组合的武器库。如果现在让我给一个初学最优化的人推荐一条学习路线我会说把标准梯度下降、近似梯度下降、加速版本、ADMM这四样吃透配合动手写软阈值和投影的代码就足以应付大部分现代数据科学里的凸优化问题。再往上走才轮到那些更精细的线搜索策略和二阶方法。近似梯度下降不是终点但它绝对是最值得先走稳的一座桥。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑