原文出自https://chunqiu.blog.ustc.edu.cn/?p=570。
SPAMS (SPArse Modeling Software)是一个为解决各种稀疏估计问题的开源优化工具箱,其主页为http://spams-devel.gforge.inria.fr/index.html ,其可实现的功能如下:
由于这个工具箱给出的是c++代码,所以需要编译才能最终使用。也正因为如此,这个工具箱实现的算法在执行时是很快的,而且可以移植到各种平台使用。
这个工具箱网上很多人有提到,都说编译时出现问题,我的电脑也不例外,不过如果只是使用其几个特定函数的话,可以规避遇到的问题。下面稍微总结一下它的安装及使用情况。
对于安装,下载安装包,解压到一个文件夹,如下图,解压后有不少文件、文件夹,其中的doc_spams.pdf是一个详尽的使用说明,介绍了各个函数接口如何使用(每个函数都有一个测试文件供测试,在test_release文件夹中)。
对于安装,在HOW_TO_INSTALL.txt中有简单说明,啰嗦了半天就是说使用compile.m进行文件编译,对于不同的计算机,配置都在这个文件中调整,所以使用matlab打开compile.m文件,前130行是用来做配置的,作者也很贴心啊,第131、132行有如下提示,意思就是130行以后,不懂不要乱动。
%%%%%%%%%%%% END OF THE CONFIGURATION %%%%%%%%%%%%%%
% Do not touch what is below this line, unless you know what you are doing
对于配置,主要涉及以下几个部分:
对于各配置,文件中都有详细说明,根据自己的情况做调整就行。如对于我的电脑(win7 32bit、matlab2014a、vs2008,虽然工具箱主页里说vs编译器建议使用vs compiler 10及以后的,但我懒得装了,最后使用vs2008(compiler 9)编译的文件也能正常使用),我做的配置如下
%%%%%%%%%%%%% COMPILER CONFIGURATION %%%%%%%%%%%%%%%%
% set up the compiler you want to use. Possible choices are
% - 'mex' (default matlab compiler), this is the easy choice if your matlab
% is correctly configured. Note that this choice might not compatible
% with the option 'use_multithread=true'.
compiler='mex'; % 我只是简单使用,所以直接使用matlab默认编译器。可以使用mex -setup配置默认编译器,matlab自带的lcc编译器貌似编译不了cpp,所以配置为vs2008的编译器(这样其实就跟compiler='vs'一样了,只是如果配置成'vs',下面需要改vs编译器路径,使用'mex'相当于让matlab帮配置好了,后面的路径配置什么也不用管)
%%%%%%%%%%%% BLAS/LAPACK CONFIGURATION %%%%%%%%%%%%%%
% set up the blas/lapack library you want to use. Possible choices are
% - builtin: blas/lapack shipped with Matlab,
% same as mex: good choice if matlab is correctly configured.
blas='builtin'; % 直接使用matlab自带的数学函数库就行
%%%%%%%%%%%% MULTITHREADING CONFIGURATION %%%%%%%%%%%%%%
% set true if you want to use multi-threaded capabilities of the toolbox. You
% need an appropriate compiler for that (intel compiler, most recent gcc, or visual studio pro)
use_multithread=false; % (might not compatible with compiler=mex) 上面配置编译器时已经有提示,选择mex,会与'use_multithread=true'有冲突,所以直接配置为false
use_64bits_integers=false; % 我的电脑是32bit,所以false
% use this option if you have VERY large arrays/matrices
% this option allows such matrices, but may slightly reduce the speed of the computations.
% if you use the options 'mex' and 'builtin', you can proceed with the compilation by
% typing 'compile' in the matlab shell. Otherwise, you need to set up a few path below.
path_matlab=''; % 上面两行已经说得很清楚,使用mex编译器,直接跳过这里的两个配置即可
% path_matlab='/softs/bin/';
add_flag='';
%%%%%%%%%%%% PATH CONFIGURATION %%%%%%%%%%%%%%%%%%%%
% only if you do not use the options 'mex' and 'builtin'
% set up the path to the compiler libraries that you intend to use below
% 此后一律不动,只有如果使用了其他编译器、数学函数库,才需要改相应的路径
按上面的过程配置好后,运行执行编译。编译过程顺利完成,那恭喜你有一台好电脑;如果出错,也恭喜你,和我的情况一样。我的出错提示是说mexStochasticProx.cpp文件编译时失败,然后是给出失败的各种错误用法。对此网上找了半天没找到好的解决办法,所以折中的办法就是不编译这些不知道哪里出问题的文件,其实这些文件也不常用(因为对我而言,根本不知道这个函数是干啥的)。个人感觉这个工具箱好的地方也在此,一个文件对应一个函数接口,一个文件不编译,对其他函数接口无影响(貌似是这样)。
如何不编译呢?就是在compile.m中找到相应行注释掉,如我的电脑上mexStochasticProx.cpp编译失败,那就把它注释掉
% '-I./linalg/ -I./prox/ prox/mex/mexStochasticProx.cpp',
对于我的电脑,其他编译失败的文件还有
% '-I./linalg/ -I./prox/ prox/mex/mexGraphOfGroupStruct.cpp',
% '-I./linalg/ -I./prox/ prox/mex/mexGroupStructOfString.cpp',
% '-I./linalg/ -I./prox/ prox/mex/mexReadGroupStruct.cpp',
% '-I./linalg/ -I./prox/ prox/mex/mexSimpleGroupTree.cpp',
% '-I./linalg/ -I./prox/ prox/mex/mexTreeOfGroupStruct.cpp'
编译完没再出错,就可以使用了。按HOW_TO_INSTALL.txt中说的,使用前执行start_spams.m文件实现添加路径和一些配置工作。但start_spams.m添加路径(使用函数addpath)貌似只是临时的,如果想随时用,那就使用Set Path把build文件夹添加到matlab搜索路径就行了。
另外,真正使用时,调用工具箱函数可能会报错,说函数无法执行之类,这是因为,compile.m文件的最后把src_release文件夹中各函数接口的帮助文件(与函数同名,里面全是注释,作为帮助信息)也拷贝到了build文件夹中,由于.m文件和.mexw32同名,matlab可能去执行.m文件去了(一般是优先执行.mexw32文件的,可能跟matlab版本有关,执行优先级不同)。所以可以把build文件夹中与.mexw32同名的.m文件直接删掉。
最后,工具箱主页里有说:
To use SPAMS with matlab R2011a and later, do not forget to run the command setenv('MKL_NUM_THREADS','1') before using SPAMS.
就是在使用前先执行setenv('MKL_NUM_THREADS','1'),但实际上这个貌似不用管。
以上都做好了,一般就可以正常调用工具箱函数了,如何使用可以参考刚才提到的doc_spams.pdf文件,里面写得也真够详细,或者说啰嗦,因为99页的文档基本就是罗列函数接口帮助、贴函数接口测试文件代码。
这里对mexTrainDL函数接口做一个简单测试吧,代码来自test_release/test_TrainDL.m文件,原文件做了4个实验,这里只测试其中一个,代码如下(此文件放在test_release文件夹下)
% test_TrainDL_GJ.m:测试mexTrainDL函数
clc
clear
close all
I=double(imread('../data/lena.png'))/255;
I = imresize(I, 0.25); % 图片太大,运行时间过长,所以缩放一下
X=im2col(I,[8 8],'sliding'); % extract 8 x 8 patches
X=X-repmat(mean(X),[size(X,1) 1]); % 减均值
X=X ./ repmat(sqrt(sum(X.^2)),[size(X,1) 1]); % 标准化
param.K=256; % learns a dictionary with 100 elements
param.lambda=0.15;
param.numThreads=-1; % number of threads
param.batchsize=400;
param.verbose=false;
param.iter=100; % 迭代次数,原始是1000,效果好但时间过长,这里减为100
tic
D = mexTrainDL(X,param);
t=toc;
fprintf('time of computation for Dictionary Learning: %f\n',t);
fprintf('Evaluating cost function...\n');
alpha=mexLasso(X,D,param);
R=mean(0.5*sum((X-D*alpha).^2)+param.lambda*sum(abs(alpha)));
ImD=displayPatches(D);
imagesc(ImD);
colormap('gray');
fprintf('objective function: %f\n',R);
此测试又是对可怜的lena做处理,实现字典学习,结果如下
OK!Hello, Dictionary Learning! Hello, Sparse Representation!