参考:
1.稀疏表示:https://www.cnblogs.com/yifdu25/p/8128028.html
2.K-SVD : https://blog.csdn.net/chlele0105/article/details/16886795
3.OMP : https://www.cnblogs.com/yifdu25/p/8385204.html
4.https://www.cnblogs.com/yifdu25/p/8128028.html
5.MP的实例 https://korediantousman.staff.telkomuniversity.ac.id/files/2017/08/main.pdf
6.K-SVD https://www.cnblogs.com/salan668/p/3555871.html
7.OMP与MP算法: https://blog.csdn.net/scucj/article/details/7467955
稀疏表示(sparse representation)假设自然信号可以被一个字典的原子(基)稀疏的线性组合近似表示。即y≈Dα,这里 y ∈ R n y∈R^{n} y∈Rn表示信号, α ∈ R K α∈R^{K} α∈RK表示稀疏系数, D ∈ R n × K D∈R^{n×K} D∈Rn×K表示是过完备字典。向量α只具有很少的非0值,这便是稀疏的含义。之所以需要字典,是因为一般的自然信号x本身并不是稀疏的,往往需要在某种稀疏基上才可以进行稀疏表示。所以稀疏表示的关键是在某种基(字典)下的系数稀疏,而这些字典与图像本身密切相关,可以将字典看做是组成图像的基本信息。也就是说在稀疏表示中我们把字典当成一种变换域,在该变换中信号的表示是稀疏的。该问题用公式表述为:
其中 Y ∈ R n × M Y∈R^{n×M} Y∈Rn×M表示样本矩阵,每一列代表一个patch,M个样本, D = { d 1 , d 2 , . . . d K } ∈ R n × K D=\{d_{1},d_{2},...d_{K}\}∈R^{n×K} D={d1,d2,...dK}∈Rn×K表示字典,每一列代表一个原子,K个原子, X ∈ R K × M X∈R^{K×M} X∈RK×M表示系数矩阵,每一列 x i x_{i} xi对应一个样本在该字典上的系数,ε表示重构误差。对于表示一个信号使用字典稀疏表示的情况如下。
在这里,稀疏性表现在 x i x_{i} xi的系数大部分都是0,而只有很少的非0值。
由于求解0范数是非凸的问题,而在一定条件下,0范数问题可以转化为1范数求解,所以(1)式也可以转化为:
通常有2种生成字典的方式,一种基于分析模型(pre-constructed),比如利用傅立叶基,小波基(WT),离散余弦变换(DCT)基,Gabor基,Contourlet基,等等。依靠这种方法生成的字典结构良好,数值计算快速。但是这种字典具有局限性,只能表示某种类型的信号,过度依赖于图像的几何特征,并且只有少数图像块可以用稀疏的原子组合来表示。另一种基于学习模型,将图像分成一个个小patch,通过训练样本得到字典。由这种方法得到的字典具有很好的适应性,能够捕捉图像的几何特征。相较于第一种往往表现更好。
而从字典的形式上看,通俗的说就是从字典的长和宽的对比,字典又可以分成3种:过完备字典(n
从上面的讲解来看稀疏表示其实就是在求解式(1),但由于其中只有Y是已知的,而D和X都是未知的,这里就涉及到2个问题,1)如何对信号进行稀疏表示,即如何求稀疏系数 x i x_{i} xi?2)如何根据样本生成字典,即如何求解D?概括地说,我们可以使用任何追踪算法来求解稀疏系数 x i x_{i} xi,例如MP,OMP(正交匹配追踪算法),BasisPursuit (BP),FocalUnderdetermined System Solver (FOCUSS)。而更新字典最常用的就是K-SVD算法。
这里借用[3]的博客的内容来讲解MP算法。假设有这样一个问题:要使用OB,OC,OD来表示OA。这里OB,OC,OD就相当于字典,OA相当于要稀疏表示的信号。那么应该如何求解才能达到使用最少的向量近似表示OA呢?答案是每次将OA投影到各个向量上,选择OA投影最大的那个向量表示原信号会在当前情况下保存信号最多的信息。
1)如图1,将OA向OB,OC,OD分别进行投影,发现OA在OB上的投影最大,这样OA就被分成投影向量OM和残差向量MA。假设 O M O B = a \frac{OM} {OB} =a OBOM=a,那么OA=a OB+MA;2)然后继续对残差MA进行分解,和第一步一样将MA向各个向量上投影,如图2,发现MA在OC向量上的投影最大,则选择OC来表示MA,并且假设 O N O C = b \frac{ON} {OC} =b OCON=b,此时 M A = b ∗ O C + N A ′ MA =b*OC+N A^{'} MA=b∗OC+NA′,3)假设此时会有2种情况, [1]发现残差仍然大于阈值ε,那么就按照第一步继续分解,直到残差小于阈值。[2]发现残差已经小于阈值了,那么此时 O A = a ∗ O B + b ∗ O C + N A ′ OA = a*OB +b*OC +NA^{'} OA=a∗OB+b∗OC+NA′。
以上就是追踪算法MP的思想。稀疏表示记为OA ≈ aOB +b*OC 。a,b就相当于系数向量里面的元素。可以看出如果残差值可以忽略,则信号OA就是这些原子的线性组合。
那如何使用代码来实现投影最大呢?也就是如何用代码寻找最接近原子?又该如何计算残差呢?
算法流程总结如下,需要说明的是这里使用x表示信号,α表示系数向量。与上面的符号表示略有差异,主要是因为直接摘抄别人的东西。
OMP算法即正交的MP算法。MP算法的次最优性来源其残差只与当前投影方向垂直,这样在接下来的投影中,因为残差与已选到的原子不是线性无关,所以残差很有可能会再次投影到原来的方向。于是,在投影时,如果使得残差 R k + 1 R_{k+1} Rk+1与 x 1 到 x k + 1 x_{1}到x_{k+1} x1到xk+1(已选到的原子)的所有向量垂直,则可以克服这个问题。这正是OMP算法改进的地方,OMP算法在分解的每一步对所选择的全部原子进行正交化处理,这使得在精度要求相同的情况下,OMP算法的收敛速度更快。更多信息请参考https://blog.csdn.net/scucj/article/details/7467955
在进行稀疏表示的时候,我们往往会先初始化一个字典,但是它并不是最优的。常用K-SVD算法对字典进行更新。但是K-SVD算法并不是独立的,它需要和上面讲述的追踪算法结合在一起使用才能对字典进行更新。需要注意的是,这里
首先要初始化一个字典D,使用该字典对数据进行稀疏表示,得到系数矩阵X。然后把DX看做是D中每列 d i d_{i} di与X中每行 x i x_{i} xi(在这里表示X的一行,前面该符号表示X的一列)的乘积,即将DX的结果分片。如前所述 D = { d 1 , d 2 , . . . d K } ∈ R n × K D=\{d_{1},d_{2},...d_{K}\}∈R^{n×K} D={d1,d2,...dK}∈Rn×K表示字典,每一列 d i ∈ R n × 1 d_{i}∈R^{n×1} di∈Rn×1代表一个原子,有K个原子, X ∈ R K × M X∈R^{K×M} X∈RK×M表示系数矩阵,每一行 x i ∈ R 1 × M x_{i}∈R^{1×M} xi∈R1×M。 d i d_{i} di与 x i x_{i} xi相乘的结果也是一个n×M的矩阵。表示该原子在整个重构矩阵中的贡献。DX分片的结果如下,
由于初始的字典往往并不是最优的,所以需要对初始字典进行更新。而更新的动力就来源于残差矩阵。K-SVD采用逐列更新字典的方法,通过K次迭代完成一次字典更新。之所以需要K次主要是因为字典D有K个原子。而小写k表示每次更新字典所正在操作的原子。具体来说每次都提取出一个原子 d k d_{k} dk对整个重构矩阵DX的影响,因为缺少了 d k d_{k} dk,上述表达式会产生一个"空洞"。K-SVD算法的目的就是如何选择一个新的原子填补空洞,使得结果更加逼近Y,减小整体误差。提取出一个原子 d k d_{k} dk的影响之后,当前误差矩阵如下。
误差值为 E n = ∣ ∣ E ∣ ∣ F 2 E_{n} =||E||_{F}^{2} En=∣∣E∣∣F2
正如3.1所述,在开始更新字典之前就已经得到了字典D和其对应的系数矩阵X。言下之意,在更新字典的时候,D,X都是固定的,但因为重构的结果不好所以要更新。假设此时将要更新的是系数矩阵的 x k x_{k} xk行和字典的 d k d_{k} dk列,则有,
上式中的 E k E_{k} Ek是误差矩阵,对 E k E_{k} Ek做SVD分解,得到 E k = U Λ V T E_{k}=UΛV^{T} Ek=UΛVT,其中U和V的列矢量均是正交基,Λ是对角矩阵。若Λ的对角元素从大到小排列,则表示 E k E_{k} Ek的能量分量主轴在相应几个正交方向上由大到小分配,如此取U的第一个列向量来表示 d i d_{i} di,取V的第一个列向量与Λ的第一个元素的乘积表示 x i x_{i} xi,这样就完成了字典一个条目的更新。
值得注意的是,如果直接用上面的方法来更新 x k x_{k} xk和 d k d_{k} dk,则会导致 x k x_{k} xk不稀疏,出现"发散"。换句话说,更新之后的 x k n e w x_{knew} xknew与更新 d k d_{k} dk前的 x k x_{k} xk的非零元素所处位置和value不一样。
那应该如何更新 x k x_{k} xk和 d k d_{k} dk?
处理方法是在进行SVD分解之前,我们使用如下公式对 E k E_{k} Ek和 x k T x_{k}^{T} xkT进行变换。即只保留系数中的非零值,再进行SVD分解就不会出现这种现象了。
其中 Ω ∈ R M × W Ω∈R^{M×W} Ω∈RM×W,W是 x k T x_{k}^{T} xkT中的非零元素个数,M是样本个数。其实质就是保留 x k T x_{k}^{T} xkT中的非零元素, E k E_{k} Ek中只保留 d k d_{k} dk和 x k T x_{k}^{T} xkT中非零元素乘积后的那些项的贡献,形成 E k R E_{k}^{R} EkR。换句话说,乘以新的矩阵Ω之后的结果就是 E k E_{k} Ek, x k T x_{k}^{T} xkT去掉0输入之后的收缩结果。
那如何生成矩阵Ω?定义集合 w k = { i ∣ 1 ≤ i ≤ M , x k T ( i ) ≠ 0 } w_{k}=\{i|1≤i≤M,x_{k}^{T}(i)≠0\} wk={i∣1≤i≤M,xkT(i)̸=0}表示 x k T ( i ) ≠ 0 x_{k}^{T}(i)≠0 xkT(i)̸=0的点的索引。定义Ω为 M × ∣ w k ∣ M×|w_{k}| M×∣wk∣矩阵,它在 ( w k ( i ) , i ) (w_{k}(i),i) (wk(i),i)处值为1,其余都是0。
此时对 E k R E_{k}^{R} EkR做SVD分解得到 E k R = U Δ V T E_{k}^{R}=UΔV^{T} EkR=UΔVT,按照上面的思路使用U的第一列更新 d k d_{k} dk,使用V的第一列和Δ(1,1)的乘积更新 x k T x_{k}^{T} xkT,这样更新字典就正确了。
总得来说,整个字典学习的流程如下。它分为2个阶段,1)对数据使用OMP算法(也可以使用其他算法)进行稀疏表示,得到对应字典的系数矩阵X。2) 依据得到系数矩阵X,根据K-SVD算法对字典逐列进行更新,同时也对系数矩阵进行更新,经过K次迭代完成一次字典更新。直到算法收敛。下面是K-SVD算法的流程。