资讯动态

稀疏编码全解析:从Lasso、字典学习到图像去噪与异常检测实战

发布时间:2026/10/14 3:47:38 来源:尧图企业网站定制
1. 为什么稀疏编码值得反复拿出来讲稀疏编码这个概念我第一次接触是在做图像压缩相关的小项目时。当时遇到一个很实际的问题一张图片里真正有用的信息其实很少大部分像素之间高度相关直接存储或处理非常浪费。后来发现稀疏编码这套思路本质上就是在回答一个问题——如何用尽可能少的“基元”去重建一个信号同时让这些基元的组合系数尽量稀疏。这个思路听起来简单但它的影响范围远超图像处理。从神经科学里对视觉皮层感受野的解释到信号压缩、特征提取、异常检测、推荐系统甚至到最近几年大模型里的稀疏激活、MoE架构背后都能看到稀疏编码的影子。可以说稀疏编码是连接“表示学习”和“最优化”的一座老桥但这座桥至今还在被不断翻新。这篇文章我打算按自己的理解把稀疏编码从动机、数学形式、求解方法、典型变体到实际落地完整地梳理一遍。适合已经有一点线性代数和最优化基础、想把这套东西真正用起来的人。如果你只是听说过Lasso、字典学习这些词但一直没搞明白它们之间的关系那这篇应该能帮你把脉络理清楚。2. 稀疏编码的核心思想与数学骨架2.1 从“表示”这件事说起任何信号处理问题第一步都是选一组基。傅里叶变换用的是正弦波小波变换用的是小波基它们都是固定基。固定基的好处是计算快、有快速算法但坏处也很明显基是事先定死的不管你的信号长什么样都得用同一套基去表示。遇到非平稳信号、纹理复杂的图像固定基往往需要很多非零系数才能表示好。稀疏编码换了个思路基不从天上掉下来而是从数据里学出来。给定一堆样本我们同时学一个字典一组基向量和每个样本在字典下的稀疏表示。用数学写出来就是给定数据矩阵 $X \in \mathbb{R}^{m \times n}$每列一个样本我们要找字典 $D \in \mathbb{R}^{m \times k}$ 和系数矩阵 $A \in \mathbb{R}^{k \times n}$使得 $X \approx DA$并且 $A$ 的每一列尽量稀疏。这里的“稀疏”通常用 $\ell_0$ 范数衡量也就是非零元素的个数。但 $\ell_0$ 范数不连续、不凸直接优化是NP难的。所以实际中几乎都用 $\ell_1$ 范数替代这就是著名的基追踪和Lasso的来源。2.2 目标函数到底长什么样最标准的稀疏编码目标函数可以写成$$\min_{D, A} |X - DA|F^2 \lambda \sum{i1}^{n} |a_i|_1$$约束条件是字典的每一列 $|d_j|_2 \leq 1$。这个约束非常关键不加的话字典列可以无限放大同时系数无限缩小稀疏性就失去意义了。为什么用 $\ell_1$ 而不是 $\ell_2$因为 $\ell_2$ 正则化得到的是稠密解每个系数都小但都不为零而 $\ell_1$ 的等高线是菱形和误差椭圆的切点往往落在坐标轴上自然产生零系数。这是稀疏性最直观的几何解释。注意$\ell_1$ 是凸的所以整个问题在固定其中一个变量时是凸的但联合优化 $D$ 和 $A$ 是非凸的。实践中通常交替优化虽然只能保证收敛到局部最优但效果通常足够好。2.3 和PCA、NMF的关系很多人会问稀疏编码和PCA有什么区别PCA也是学一组基但它的基是正交的系数是稠密的。稀疏编码的字典通常是过完备的$k m$基之间不正交但系数稀疏。过完备加上稀疏带来的是更强的表示灵活性。和NMF非负矩阵分解相比NMF强调非负性稀疏编码强调稀疏性两者可以叠加使用形成非负稀疏编码。实际做图像分解时如果物理意义要求非负加非负约束往往能让结果更可解释。3. 求解方法从交替优化到在线学习3.1 交替优化最朴素也最常用最直接的求解思路是固定一个、优化另一个反复迭代固定字典 $D$求系数 $A$这一步对每个样本独立求解一个Lasso问题。可以用坐标下降、LARS、近端梯度等方法。固定系数 $A$更新字典 $D$这一步是带约束的二次规划有闭式解通常用块坐标下降逐列更新。这个流程就是经典的字典学习算法框架。它的优点是实现简单、每步都有明确的最优子问题缺点是每次更新字典都要遍历所有样本数据量大时非常慢。3.2 坐标下降为什么在Lasso里这么好用Lasso的单样本问题是$$\min_a |x - Da|_2^2 \lambda |a|_1$$坐标下降每次只更新一个系数 $a_j$固定其他系数不变。对 $a_j$ 求次梯度并令其为零可以得到软阈值形式的更新$$a_j S_{\lambda / |d_j|2^2}\left( \frac{d_j^T (x - \sum{l \neq j} d_l a_l)}{|d_j|_2^2} \right)$$其中 $S_\tau(z) \text{sign}(z)\max(|z| - \tau, 0)$ 是软阈值算子。这个更新非常便宜而且可以配合活跃集技巧只更新非零系数速度提升明显。我实测下来对于中等规模问题几千维、几百个原子坐标下降配合热启动通常几十次扫描就能收敛到不错的精度。3.3 在线字典学习大数据场景的解法当样本量到百万级批量交替优化就不现实了。这时候可以用在线字典学习核心思想是每次只用一个或一小批样本用随机近似的方式更新字典。具体做法是对每个新样本先求它的稀疏编码然后计算当前字典在该样本上的重构误差的梯度用这个梯度去修正字典。同时维护一个累加的系数矩阵用来做字典列的归一化和“遗忘”旧数据。这个方法的妙处在于它把字典学习变成了一个在线随机优化问题内存占用和样本量无关只和字典大小有关。很多工业级稀疏编码系统用的都是这个套路。3.4 近端梯度与加速如果目标函数里还有别的光滑项比如逻辑回归的损失那就不能直接用坐标下降了。这时候近端梯度下降也叫ISTA是标准选择。它的每一步是先沿光滑部分做梯度下降再对结果做软阈值。加上Nesterov加速后变成FISTA收敛速度从 $O(1/t)$ 提升到 $O(1/t^2)$。实操心得FISTA的步长选择很关键步长取 $1/L$其中 $L$ 是光滑部分梯度的Lipschitz常数。如果 $L$ 估计不准要么收敛慢要么震荡。我一般先用线搜索估一个上界再固定使用。4. 典型变体与适用场景4.1 组稀疏当变量天然成组有些问题里系数不是单独稀疏而是成组稀疏。比如人脸识别里同一个人的不同表情、光照下的图像应该由同一组基表示。这时候用 $\ell_{2,1}$ 范数也就是先对组内求 $\ell_2$ 范数再对组间求 $\ell_1$ 范数就能让整组系数同时为零或同时非零。组稀疏的求解和普通Lasso类似只是软阈值变成了块软阈值对每个组如果组内 $\ell_2$ 范数小于阈值整组置零否则按比例缩放。4.2 结构化稀疏与图约束如果基之间还有图结构比如相邻的基应该被一起选中可以在正则项里加入图拉普拉斯项。这在脑电、基因表达数据里很常见因为通道之间、基因之间本身就有网络关系。4.3 卷积稀疏编码处理平移不变性标准稀疏编码把图像拉成向量丢失了空间结构。卷积稀疏编码把字典原子也变成滤波器表示变成特征图通过卷积操作重建图像。这样学出来的滤波器天然具有平移不变性而且参数量大大减少。代价是求解更复杂通常要在频域做或者用ADMM拆成几个子问题交替求解。我试过在小规模图像上去噪卷积稀疏编码的效果确实比块状处理更干净但调参也更麻烦。4.4 稀疏编码与自编码器的联系如果把稀疏编码的编码过程看成一个神经网络解码过程看成另一个那它其实就是一个线性自编码器加稀疏惩罚。这个视角很有用因为它把稀疏编码和深度学习打通了。后来很多工作把线性换成非线性就得到了稀疏自编码器再往后稀疏激活、稀疏注意力这些概念本质上都是同一个思想在不同尺度上的体现。5. 实操流程从数据到字典的完整走一遍5.1 数据预处理别跳过这一步稀疏编码对数据的尺度和中心非常敏感。标准流程是去均值每个特征减去均值让数据零中心。否则字典的第一列很容易变成全常数列浪费一个原子。归一化如果不同特征量纲差异大要标准化。但图像块通常只做去均值不做方差归一化因为像素值本身有物理意义。白化可选如果特征之间相关性极强可以先做ZCA白化让字典更容易学到方向性结构。但白化会放大噪声信噪比低时要慎用。注意字典学习前一定要把数据矩阵的列样本和行特征搞清楚。我见过有人把样本和特征转置了结果学出来的字典完全没法解释。5.2 字典初始化随机还是用数据字典初始化有两种常见做法随机初始化从数据里随机选 $k$ 个样本作为初始字典。简单但可能选到重复或异常样本。PCA初始化取前 $k$ 个主成分作为初始字典。这样初始字典已经覆盖了主要能量方向收敛更快。我一般用PCA初始化然后加一点随机扰动避免对称性问题。如果 $k$ 比特征维度大很多PCA不够用那就随机选加正交化。5.3 参数选择稀疏度怎么定$\lambda$ 控制稀疏度是最重要的超参数。$\lambda$ 越大系数越稀疏但重构误差越大。选择方法有几种交叉验证最稳妥但计算量大。L曲线法画重构误差和稀疏度的曲线找拐点。固定非零个数如果业务上要求每个样本最多用 $s$ 个原子那就把 $\lambda$ 调到满足这个约束。根据我的经验图像块比如16x16做稀疏编码时每个块用5到20个原子通常比较合理。太少重建差太多就失去稀疏的意义了。5.4 完整代码骨架下面是一个批量字典学习的核心循环用Python写依赖NumPy和SciPyimport numpy as np from scipy.optimize import nnls def soft_threshold(z, tau): return np.sign(z) * np.maximum(np.abs(z) - tau, 0) def lasso_coordinate_descent(x, D, lam, max_iter100, tol1e-4): k D.shape[1] a np.zeros(k) DTD D.T D DTx D.T x for _ in range(max_iter): a_old a.copy() for j in range(k): if DTD[j, j] 1e-12: continue residual DTx[j] - DTD[j] a DTD[j, j] * a[j] a[j] soft_threshold(residual, lam) / DTD[j, j] if np.max(np.abs(a - a_old)) tol: break return a def update_dictionary(X, A, D): # 逐列更新字典 k D.shape[1] for j in range(k): idx np.where(np.abs(A[j]) 1e-8)[0] if len(idx) 0: continue Ej X[:, idx] - D A[:, idx] np.outer(D[:, j], A[j, idx]) U, s, Vt np.linalg.svd(Ej, full_matricesFalse) D[:, j] U[:, 0] A[j, idx] s[0] * Vt[0] return D, A def dictionary_learning(X, k, lam, n_iter20): m, n X.shape # PCA初始化 U, s, Vt np.linalg.svd(X, full_matricesFalse) D U[:, :k] A np.zeros((k, n)) for it in range(n_iter): # 稀疏编码 for i in range(n): A[:, i] lasso_coordinate_descent(X[:, i], D, lam) # 字典更新 D, A update_dictionary(X, A, D) # 归一化 norms np.linalg.norm(D, axis0) D D / norms A A * norms[:, None] err np.linalg.norm(X - D A, fro) print(fiter {it}, reconstruction error: {err:.4f}) return D, A这段代码是教学版本清晰但慢。实际用的时候稀疏编码那一步可以用sklearn.linear_model.Lasso字典更新可以用矩阵运算批量做速度会快很多。5.5 评估指标怎么判断学得好不好光看重构误差不够还要看稀疏度平均非零系数个数。字典相干性字典列之间的最大内积。相干性太高说明原子冗余表示不稳定。重构误差在测试集上的表现防止过拟合。下游任务表现如果稀疏编码是中间步骤最终要看分类、聚类、去噪的效果。我一般会同时监控这几个指标如果相干性超过0.9就会考虑增大字典或加多样性约束。6. 常见问题与排查技巧实录6.1 字典学出来全是噪声怎么办这是最常见的问题。原因通常有三个数据没去均值、$\lambda$ 太小、字典太大。排查顺序是先检查数据是否零中心再把 $\lambda$ 调大试试最后考虑减小字典规模。如果数据本身信噪比低可以先做一次降噪预处理。6.2 系数全为零或全非零全零说明 $\lambda$ 太大软阈值把所有系数都压没了。全非零说明 $\lambda$ 太小正则化没起作用。解决办法是画一条 $\lambda$ 从大到小的路径观察非零系数个数的变化选在中间区域。6.3 训练慢得无法接受批量字典学习的时间复杂度大致是 $O(n \cdot k \cdot m \cdot T)$$T$ 是坐标下降的扫描次数。加速手段包括用活跃集只更新非零系数。用热启动上一次的解作为下一次的初始值。用在线学习每次只处理一小批。用GPU加速矩阵运算。6.4 字典原子重复如果多个原子几乎一样说明初始化不好或者缺少多样性约束。可以在字典更新后做一次去相干操作或者加一个惩罚项让 $D^T D$ 接近单位阵。6.5 问题速查表现象可能原因排查方向重构误差居高不下字典太小、$\lambda$ 太大增大 $k$减小 $\lambda$系数过于稠密$\lambda$ 太小增大 $\lambda$检查归一化字典原子像噪声数据未中心化、初始化差去均值换PCA初始化训练震荡不收敛步长太大、字典未归一化减小步长加列归一化约束测试集表现差过拟合增大 $\lambda$减少迭代次数独家避坑字典学习里最容易忽略的是列归一化。如果忘了归一化字典列会越来越大系数越来越小最后数值不稳定。我建议每次字典更新后立刻归一化并把缩放因子乘回系数保持重构不变。7. 稀疏编码在真实场景里的落地方式7.1 图像去噪稀疏先验的经典应用图像去噪是稀疏编码最成熟的应用之一。基本流程是把图像切成重叠小块对每个块做稀疏编码用字典重构再拼回去。因为噪声通常不稀疏而图像结构在字典下是稀疏的所以重构过程天然抑制了噪声。关键参数是块大小、重叠步长和 $\lambda$。块太小抓不住结构块太大计算慢。我一般用8x8或16x16重叠一半。$\lambda$ 根据噪声水平调噪声大就调大。7.2 特征提取给分类器喂稀疏特征在图像分类、文本分类里稀疏编码可以作为特征提取器。学一个字典把每个样本编码成稀疏向量然后把这个向量喂给SVM或逻辑回归。相比原始像素稀疏特征对光照、遮挡更鲁棒。这里有个技巧字典可以在无标签数据上学然后用有标签数据训练分类器。这样既利用了无标签数据又避免了过拟合。7.3 异常检测重构误差就是异常分数正常样本在字典下重构误差小异常样本重构误差大。所以可以直接用重构误差作为异常分数。这个方法在工业质检、网络入侵检测里都有应用。要注意的是字典必须只用正常样本训练。如果训练集里混了异常字典会去拟合异常检测效果就崩了。7.4 推荐系统稀疏编码做协同过滤用户-物品评分矩阵天然稀疏可以用稀疏编码的思路做矩阵补全。把用户和物品都映射到低维稀疏空间用重构误差做损失。这和矩阵分解很像但加了稀疏约束后表示更可解释冷启动表现也往往更好。8. 我踩过的坑和最后分享的几个技巧第一个坑是数据泄漏。做交叉验证时字典必须在训练折上学不能在全量数据上学完再切分。我早期犯过这个错结果测试误差好得离谱实际部署就崩了。第二个坑是字典大小拍脑袋。字典太小欠拟合太大过拟合且慢。我的经验是先从 $2m$ 到 $4m$ 试起$m$ 是特征维度然后根据重构误差和稀疏度调整。第三个坑是忽略数值稳定性。坐标下降里如果字典列范数接近零除法会炸。一定要加一个小的epsilon或者直接跳过接近零的列。最后分享一个实用技巧如果数据量很大可以先用随机投影降维再学字典。随机投影近似保持内积但维度降下来后字典学习速度会快一个数量级效果通常损失很小。这个技巧在高维文本数据上特别管用。稀疏编码这套东西理论漂亮落地也实在。关键是要理解它的假设——信号在某个字典下是稀疏的——然后根据具体场景去设计字典和正则项。不要指望一套参数打天下多试、多看指标、多分析学出来的原子慢慢就有感觉了。

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

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

免费获取报价 →
↑