
简介DICTOL-master 是一份面向机器学习、图像处理研究者与高年级学生的低秩字典学习开源实现重点提供 FDDLFast Dictionary Learning算法的 MATLAB 代码适用于图像分类、稀疏表示及高维结构化数据建模等任务。压缩包共 446 个文件约 57.61MB其中 238 个 .m 脚本为算法主体21 个 C 文件与 11 个头文件展示底层实现57 个 mexw64 和 47 个 mexa64 为已编译的跨平台 MEX 文件另有 PDF 与 tex 格式的文档、mat 数据、示例图片等便于直接运行、调试与二次开发。目前已有 190 人下载学习资源覆盖字典学习的完整流程——数据预处理、字典初始化、交替最小化、稀疏编码与低秩约束并附有在 MNIST、CIFAR-10 等标准数据集上的实验代码与性能对比读者可借此深入理解低秩字典学习的核心思想并复现 FDDL 相关实验。适合具备一定 MATLAB 基础、希望系统性掌握低秩字典学习方法的研究者或工程师。1. 字典学习与 DICTOL-master低秩约束到底解决什么问题手头一批高光谱图像几百个波段之间相关性极高用传统字典学习学出来的原子又密又冗余同一组基换个角度反复出现。低秩字典学习正是冲着这个痛点来的在稀疏表示之外再给字典或编码矩阵加一个秩约束强迫表示结构落在低维子空间里而不是让每个原子各自为政。DICTOL-master 是 MATLAB 生态里用得比较顺手的字典学习工具包把稀疏编码、字典更新、低秩正则串成可调用模块适合做图像去噪、人脸遮挡移除、高光谱解混和视频背景建模。下面先把低秩项该加在哪讲清楚再给一套能直接落到脚本里的流程。2. 从稀疏编码到低秩字典学习为什么 DICTOL 里要加低秩项2.1 传统字典学习K-SVD 与在线字典学习的核心循环字典学习要解的数学问题可以写成给定信号集合 $Y \in \mathbb{R}^{d \times N}$找一个字典 $D \in \mathbb{R}^{d \times K}$ 和一个编码矩阵 $Z \in \mathbb{R}^{K \times N}$使得重构误差 $|Y - DZ|_F^2$ 尽量小同时每个编码列 $z_i$ 的非零元素个数不超过 $L$。$K$ 是字典原子数$L$ 是稀疏度这两个数字从第一轮迭代起就决定了模型容量。稀疏性在这里不是额外惩罚而是核心先验每个信号只由少量原子线性组合而成。最经典的求解框架是 K-SVD。它把整个优化拆成两个阶段交替推进第一步固定字典 $D$用 OMP正交匹配追踪逐列求稀疏编码 $Z$第二步固定 $Z$更新字典。K-SVD 这个名字的来源在第二步——它不是对整个字典做一次最小二乘更新而是逐原子处理对第 $j$ 个原子找出所有用到它的样本计算去掉该原子后的误差矩阵对这个误差矩阵做 SVD然后用第一个左奇异向量替换旧原子。这样做的好处是每个原子都被“定向校准”到最能降低残差的方向。在线字典学习则是另一个分支解决的是大规模数据进不了内存的问题。它把目标函数改写成对样本分布的期望每一轮随机抽一批样本用带遗忘因子的交替更新逼近最优解。在线做法的优势是收敛曲线平滑、内存占用固定DICTOL-master 这类工具包之所以好用就是把这些交替更新和加速策略封装成了黑匣子调用方只需要喂数据和调参。但这两条路线有一个共同前提原子之间是独立的。K-SVD 每次用一个 SVD 分量替换原子天然不含“多个原子共享某个低维子空间”的约束。于是当数据本身具有强全局结构时学出来的字典会出现严重的原子冗余几个原子之间的余弦相似度经常超过 0.9。低秩字典学习就是在这一步把约束改了要么让字典本身低秩要么让编码矩阵低秩从根源上控制原子族的结构。2.2 低秩项加在哪个矩阵上字典低秩与编码低秩低秩字典学习没有唯一版本差别集中在约束对象和惩罚方式上。常见做法有两种落地之前必须分清楚因为后续所有参数含义完全不同。第一种是对字典 $D \in \mathbb{R}^{d \times K}$ 做低秩约束要求它的秩不超过某个值 $r$。直观含义是所有字典原子都躺在一个 $r$ 维线性子空间里。高光谱解混是典型受益场景——端元数量远少于波段数光谱曲线共享一个低维线性组合空间给字典加低秩约束学出来的原子更干净不会把两个端元的混合体当成“新原子”存下来。第二种是对编码矩阵 $Z$ 做低秩约束写成 $|Z|* \le \tau$这里的 $|Z|*$ 是核范数也就是 $Z$ 所有奇异值之和。核范数是矩阵秩函数的凸松弛把它放进目标函数后优化过程会在每次更新完编码矩阵后做一次奇异值软阈值对 $Z$ 做 SVD把所有奇异值整体向零方向收缩。这个操作在数学上等价于在低秩约束下做投影也是求解器里最常见的可闭式更新步骤。为什么要用核范数而不是直接把秩写进约束因为矩阵的秩函数非凸直接约束会把问题变成 NP 难核范数是奇异值向量的 $\ell_1$ 范数凸且可以用软阈值闭式求解。DICTOL-master 里的低秩模块无论包在哪个求解器里底层基本都是在重复“SVD、阈值、重建”这个循环。编码低秩的物理意义比字典低秩更容易感知。当一批样本来自同一个低维子空间比如同一个人的多张人脸、同一场景的连续视频帧、同一个物体的多次观测编码矩阵的奇异值会快速衰减。给 $Z$ 加低秩约束等于强调“样本集合整体共享结构”这和稀疏表示的“单样本局部结构”正好互补。稀疏负责挑选原子低秩负责让被选出来的原子们协同工作。2.3 怎么选型什么时候低秩字典学习值得用该不该给字典学习加低秩项不能只看论文标题。我一般会先做一个小实验对学到的普通字典或编码矩阵做一次奇异值分解看奇异值衰减曲线。如果前 20% 的奇异值占据了 90% 以上的能量说明数据本身高度低秩低秩字典学习值得上如果奇异值衰减平缓原子之间正交性强硬加低秩只会把信息压进一个窄子空间重构和下游任务都会受损。下表给出几个典型场景的选择参考高光谱解混普通字典原子冗余、端元混叠低秩字典原子更干净、端元可解释性强 视频背景建模普通字典逐帧独立编码、背景前景难分低秩编码把背景吸入共同子空间前景变成稀疏残差 人脸批量去遮挡普通字典对遮挡过拟合低秩编码压制身份子空间遮挡呈稀疏异常 纯随机噪声去噪普通字典效果好且收敛快低秩提升有限参数更敏感选型的核心判断就一句话数据和基向量是否天然共享低维子空间。是就值得上低秩不是别硬加。DICTOL-master 里两类求解器都齐先各跑一小轮看奇异值谱再决定最终投入哪条路线。3. 在 MATLAB 里把 DICTOL-master 跑起来从目录到最小复现3.1 落地前准备路径、mex 编译与数据格式约定DICTOL-master 解压出来通常是一组 MATLAB 函数加 mex 源码mex 目录里是 C 语言加速实现。第零步是把整个目录挂到 MATLAB 搜索路径上。genpath会把子目录全部展开避免漏掉工具函数挂载后运行自带的编译脚本把 mex 文件生成到对应目录。编译脚本的命名在不同版本里略有差异常见的是make.m或setup.m打开 mex 目录看一眼就知道。% 解压到任意路径后在 MATLAB 里挂载整个工具包 addpath(genpath(DICTOL-master)); % 进入 mex 源码目录执行编译脚本 cd DICTOL-master/mex; make; % 如果脚本名不是 make以目录里实际文件名为准逻辑说明addpath(genpath(...))把 DICTOL-master 下所有递归子目录里带.m的文件加入搜索路径这一步不做后面调用求解器会直接报Undefined function。make的作用是调用 MATLAB 的mex命令把 C 源码编译成平台相关的二进制编译输出在 mex 目录下生成.mexw64或.mexa64文件。如果不编译工具包退化成纯 MATLAB 模式小数据还能跑patch 数量一上来就慢得不可接受。编译失败的九成原因都是 C 编译器版本和 MATLAB 不匹配。Windows 上要用与 MATLAB 版本对应的 Microsoft Visual C 编译器Linux 用 gccmacOS 用 Xcode Command Line Tools。先执行mex -setup查看当前选的编译器再运行make能少踩一半坑。数据格式这一关同样关键。DICTOL 系列求解器对输入有个约定信号按列排列每列是一个样本数值为 double 精度。如果你直接把一张 uint8 的灰度图塞进去字典学出来的是像素坐标和强度值的混合体没有任何意义。图像类实验需要先把图像滑窗切块再按列堆放。我习惯的做法是% 从灰度图里提取重叠 patch组成训练矩阵 Y im double(imread(demo.png)) / 255; ps 8; % patch 边长 step 4; % 采样步长越小样本越多 [rows, cols] size(im); Y zeros(ps^2, 0); for i 1:step:rows-ps1 for j 1:step:cols-ps1 patch im(i:ips-1, j:jps-1); Y [Y, patch(:)]; % 每列是一个 d 64 维样本 end end逻辑说明滑窗采样的目的是把图像空间结构拆成局部块每个块拉成向量后字典学到的原子就是“块基元”比如边缘、纹理、平滑区域。ps决定基元的尺度step决定训练样本密度。step越小样本越重叠字典越稳但训练越慢。这里用 double 归一化是必须的uint8 直接进字典学习会让梯度计算和距离度量全部失真。数据准备好之后还要看一眼样本里有没有 NaN 或 inf。低秩字典学习里有一个很隐蔽的翻车点只要样本矩阵里混入一个 NaNSVD 就会把整个奇异值分解结果变成 NaN且这个错误会在后续迭代中迅速传染。正式实验前用一个assert(~any(isnan(Y(:))))拦住它。3.2 低秩字典学习最小循环不依赖私有函数也能跑DICTOL-master 里现成的低秩求解器可以直接调用但它的输入输出和参数名在不同版本里并不完全一致。为了让你不被黑匣子卡住我给出一个不依赖任何私有函数的低秩字典学习最小循环。这个循环覆盖了完整流程初始化、稀疏编码、字典更新、低秩投影、归一化。看懂它再看工具包里的现成求解器会顺畅得多。function [D, Z, S] lrdl_min(Y, K, r, L, iters) % 低秩字典学习最小循环MATLAB R2018 % Y: d x N 训练信号按列排列 % K: 字典原子个数 % r: 字典低秩秩取奇异值能量 90% 左右对应的下标 % L: OMP 稀疏度每个样本最多用 L 个原子 % iters: 迭代轮数 % 输出 D: d x K 低秩字典Z: K x N 编码S: 最终字典奇异值 % 初始化从样本里随机抽 K 列当字典比随机数收敛稳 rng(0); idx randperm(size(Y, 2), K); D Y(:, idx); D D ./ sqrt(sum(D.^2, 1)); % 原子归一化老版本 MATLAB 没有 vecnorm for t 1:iters % 1) 稀疏编码固定字典解 min ||Y - D Z||_F^2, ||z_i||_0 L % 这一步通常调用 mex 加速的 OMP这里用函数占位 Z sparse_coding_omp(D, Y, L); % 替换为你本地的 OMP 实现 % 2) 字典更新最小二乘闭式解D_new Y*Z / (Z*Z) % 加 1e-6 单位阵防止 Z*Z 奇异导致除零 D_new Y * Z / (Z * Z 1e-6 * eye(K)); % 3) 低秩投影对字典做 SVD只保留前 r 个奇异值 [U, Sv, V] svd(D_new, econ); Sv(r1:end, :) 0; % 硬阈值后 K-r 个奇异值清零 D U * Sv * V; % 4) 归一化防止原子尺度漂移导致迭代震荡 D D ./ sqrt(sum(D.^2, 1)); end S svd(D); % 返回最终字典奇异值用于观察低秩度 end逻辑说明第 1 步用 OMP 求稀疏编码这一步把每个样本投影到当前字典的 $L$ 个原子上。第 2 步是标准的字典最小二乘更新闭式解来自 $\min_D |Y - DZ|_F^2$ 的求导置零1e-6是防止Z*Z不可逆的微正则项。第 3 步是这个循环的灵魂对更新完的字典做 SVD然后把从 $r1$ 开始的奇异值全部置零。这一步强行让字典原子落在 $r$ 维子空间里普通 K-SVD 里对应位置的“逐原子 SVD”被整体低秩投影替换。第 4 步的归一化不是可选项没有它字典原子的尺度会不断漂移编码系数跟着震荡收敛曲线像锯齿。参数怎么设K一般取样本维度的 2 到 4 倍图像 patch 场景下 256 到 1024 是常见区间L取 3 到 8过小重构残差大过大稀疏性消失r是这里最敏感的参数先对普通字典做一次 SVD 看奇异值谱取能量占比 90% 到 95% 对应的下标迭代几次后再微调。实际工程里你不会长期用这个裸循环DICTOL-master 里的现成求解器做了更好的收敛控制和边界处理。但这个最小循环能让你清楚回答一个问题低秩投影到底改在哪一步答案是在字典更新之后、归一化之前格式就是 SVD 加奇异值截断。4. 低秩字典学习的必调参数与避坑排查翻车现场到稳定收敛4.1 决定成败的四个参数范围、作用与调试信号低秩字典学习的参数比普通字典学习多一层每个参数都对结果有可感知的影响。我实际调过的项目里下面四个参数的改动对结果影响最大字典原子数 K、稀疏度 L、低秩秩 r、迭代轮数。它们之间并不是独立关系低秩约束一旦过强稀疏度 L 带来的自由度会被抵消。参数常见取值控制什么调小表现调大表现字典原子数 Kd 的 2~4 倍或 256~1024表示粒度和容量重构残差大细节丢失原子重叠字典冗余稀疏度 L3~8单样本使用的原子数重构质量低结构模糊系数稠密低秩约束失效低秩秩 r由奇异值能量 90%~95% 决定字典子空间维数退化为普通字典低秩无意义原子挤压到同一方向表达退化迭代轮数20~50是否收敛到稳定点字典未成熟结果随机算力浪费容易过拟合噪声K 的选择要看信号本身的复杂度。灰度图像 patch 维度 64 时K 取 256 就能覆盖常见纹理如果是高光谱解混端元数量只有十几个K 反而不能太大否则低秩投影会把大量冗余原子压缩到同一方向。L 和 K 必须联动K 越大每个样本可选择的原子越多L 保持小值才能维持稀疏性K 小的时候 L 可以稍微放大否则重构自由度不够。r 是最需要花时间观察的参数。我的习惯是先跑一轮普通字典学习把字典的奇异值谱画出来看能量拐点。比如奇异值在第 18 个附近出现明显拐点前 18 个占了总能量 93%那 r 就从 18 附近开始试。每轮迭代后监控重构误差和字典奇异值谱的变化如果第 r1 个奇异值仍然显著大于 0说明 r 取小了信息被硬挤掉如果第 r 个和第 r1 个差距很小说明 r 取大了低秩约束形同虚设。迭代轮数不是越大越好。低秩投影本身是一个强先验迭代到后期字典更新会在低秩子空间内缓慢漂移这时候接踵而来的是过拟合训练集噪声。我在迭代过程中会在验证集上算重构误差一旦验证误差开始上升说明拐点到了再迭代 5 轮左右就该停。4.2 五条高频踩坑记录现象、原因与解决第一条Undefined function mexOMP。现象是调用 OMP 编码时报未定义函数但字典更新部分正常。原因是编译脚本没有执行或者执行了但生成路径和当前路径不一致。解决方法是先cd DICTOL-master/mex执行make看到.mexw64文件生成后再回到工作目录。如果 make 报错先mex -setup检查编译器不要绕过编译直接跑。第二条字典输出全是 NaN。现象是迭代几轮后 D 和 Z 全变成 NaN且没有报错。原因按优先级排查训练样本里有 NaN 或 infZ*Z矩阵奇异低秩投影时 SVD 的 U 或 V 包含 NaN。解决方法是数据入口加检查assert(~any(isnan(Y(:))))字典更新加正则项1e-6 * eye(K)每轮归一化时用一个极小值兜底denom max(sqrt(sum(D.^2,1)), 1e-8)。这三步同时做基本能止血。第三条低秩约束加上了但结果和普通字典学习一模一样。现象是字典奇异值谱没有任何截断痕迹所有 K 个奇异值都非零。原因是 r 取到了 K 或接近 K低秩投影形同虚设。解决方法是把 r 调成 K 的 10% 到 30%再做一轮观察。血泪经验别用“秩占比”这种偷懒设定一定要看当前数据本身的奇异值拐点。第四条重构 PSNR 很高但下游分类精度崩了。现象是字典学完稀疏编码重构图像非常干净拿去做人脸识别却不如普通字典。原因是低秩约束太强所有原子被压进同一个窄子空间损失了类间差异信息。低秩保护了共性牺牲了辨别性。解决方法是适当地把 r 放大或者在目标函数里加一个判别项比如类间散布矩阵的迹。更简单的做法是在低秩投影前先对原子做一次白化让子空间内保留类间方向。第五条内存爆炸MATLAB 直接卡死。现象是K1024、N500000时Z*Z的中间变量把内存吃满。原因是字典更新的最小二乘解需要计算 $Z Z$这个矩阵是稠密的 $K \times K$K 到 1024 时看起来不大但 OMP 中间变量的稀疏结构被强制稠密化后内存和计算量同时失控。解决方法是缩小 patch 采样步长或者把样本分批处理每次取 5000 列做一次字典更新累积多次小批量更新替代一次性全量更新。DICTOL 里的在线字典学习路线就是为了解决这个场景别拿着小批量模式的代码硬撑大数据该切换求解器就切换。5. 怎么验证低秩字典学习没有白做奇异值谱、重构指标与下游任务一套低秩字典学习流程跑完不能只看训练集重构误差。我用三个指标做验证按顺序推进缺一个都可能被表面的 PSNR 骗住。第一看字典奇异值谱是否真的出现拐点。这是判断低秩约束有没有生效的最直接证据。普通字典的奇异值衰减平缓低秩字典的奇异值谱会在第 r 个位置出现明显断层。画谱线的代码很简单D trained_dictionary; % 学到的低秩字典 S svd(D); % 按降序排列的奇异值 energy cumsum(S.^2) / sum(S.^2); % 累计能量占比 plot(S, o-); grid on; hold on; idx90 find(energy 0.9, 1); % 能量达到 90% 的位置 plot(idx90, S(idx90), rx, MarkerSize, 10); xlabel(奇异值下标); ylabel(奇异值大小);逻辑说明cumsum(S.^2)/sum(S.^2)计算前 k 个奇异值占总能量的比例find(..., 1)找到第一个超过 90% 的下标。如果这个下标远小于 K比如 K512 时下标才 30说明字典有效自由度很低低秩约束确实起作用了。如果下标接近 K说明你的数据本身就不低秩或者 r 设得太大。第二算验证集的重构信噪比 RSNR。注意这里必须用训练时没见过的数据。低秩字典学习过拟合的速度比普通字典快因为低秩子空间一旦确定后续迭代只是在子空间内微调验证集 RSNR 通常会先升后降。我一般记录每轮迭代的验证 RSNR画出曲线取最高点对应的迭代轮数作为最终模型。第三回到任务本身。高光谱解混就看端元光谱与真实地物光谱的相关系数人脸识别就看分类准确率视频背景建模就看前景 mask 的 F1 分数。这一步是最诚实的检验重构指标再漂亮下游任务不涨就是白做。我做过一次高光谱解混r 取大了字典原子被压到几乎同一个方向重构残差低得骗人解混精度却崩了。后来养成习惯先看奇异值谱拐点再用下游任务校验 r才把这个方向校回来。低秩字典学习不是万灵药它是让稀疏表示兼顾全局结构的一把尺子DICTOL-master 把尺子封装成了能跑的代码剩下的参数判断只能在你的数据上一点点验。希望帮到你。本文还有配套的精品资源点击获取