超完备”基向量来更高效地表示样本数据。稀疏编码算法的目的就是找到一组基向量 

稀疏编码python 稀疏编码器的作用_迭代

 ,使得我们能将输入向量 

稀疏编码python 稀疏编码器的作用_迭代_02

 表示为这些基向量的线性组合:


稀疏编码python 稀疏编码器的作用_稀疏编码_03


虽然形如主成分分析技术(PCA)能使我们方便地找到一组“完备”基向量,但是这里我们想要做的是找到一组“超完备”基向量来表示输入向量 

稀疏编码python 稀疏编码器的作用_稀疏编码_04

 (也就是说,k > n)。超完备基的好处是它们能更有效地找出隐含在输入数据内部的结构与模式。然而,对于超完备基来说,系数 ai不再由输入向量 

稀疏编码python 稀疏编码器的作用_迭代_02

 唯一确定。因此,在稀疏编码算法中,我们另加了一个评判标准“稀疏性”来解决因超完备而导致的退化(degeneracy)问题。要求系数 ai可以被表示成少量基本元素的叠加,在图像中这些基本元素可以是面或者线。

x 供我们进行特征学习。特别是,学习一个用于表示样本数据的稀疏特征集 s, 和一个将特征集从特征空间转换到样本数据空间的基向量 A, 我们可以构建如下目标函数:


稀疏编码python 稀疏编码器的作用_最小化_06


稀疏编码python 稀疏编码器的作用_最小化_07

是x的Lk范数,等价于 

稀疏编码python 稀疏编码器的作用_迭代_08

。L2 范数即大家熟知的欧几里得范数,L1 范数是向量元素的绝对值之和)


上式前第一部分是利用基向量将特征集重构为样本数据所产生的误差,第二部分为稀疏性惩罚项(sparsity penalty term),用于保证特征集的稀疏性。


但是,如目标函数所示,它的约束性并不强――按常数比例缩放A的同时再按这个常数的倒数缩放 s,结果不会改变误差大小,却会减少稀疏代价(表达式第二项)的值。因此,需要为 A 中每项 Aj 增加额外约束 

稀疏编码python 稀疏编码器的作用_稀疏编码python_09

。问题变为:

稀疏编码python 稀疏编码器的作用_稀疏编码_10


遗憾的是,因为目标函数并不是一个凸函数,所以不能用梯度方法解决这个优化问题。但是,在给定 A 的情况下,最小化 J(A,s) 求解 s 是凸的。同理,给定 s最小化 J(A,s) 求解 A。这表明,可以通过交替固定 s和 A 分别求解 As。实践表明,这一策略取得的效果非常好。


但是,以上表达式带来了另一个难题:不能用简单的梯度方法来实现约束条件 

稀疏编码python 稀疏编码器的作用_稀疏编码python_11

。因此在实际问题中,此约束条件还不足以成为“权重衰变”("weight decay")项以保证 A 的每一项值够小。这样我们就得到一个新的目标函数:

稀疏编码python 稀疏编码器的作用_稀疏编码python_12

(注意上式中第三项, 

稀疏编码python 稀疏编码器的作用_最小化_13

等价于

稀疏编码python 稀疏编码器的作用_稀疏编码python_14

,是A各项的平方和)

这一目标函数带来了最后一个问题,即 L1 范数在 0 点处不可微影响了梯度方法的应用。尽管可以通过其他非梯度下降方法避开这一问题,但是本文通过使用近似值“平滑” L1 范数的方法解决此难题。使用 

稀疏编码python 稀疏编码器的作用_迭代_15

 代替 

稀疏编码python 稀疏编码器的作用_最小化_16

, 对 L1 范数进行平滑,其中 ε 是“平滑参数”("smoothing parameter")或者“稀疏参数”("sparsity parameter") (如果 ε远大于x, 则 x + ε 的值由 ε 主导,其平方根近似于ε)。在下文提及拓扑稀疏编码时,“平滑”会派上用场。


因此,最终的目标函数是:


稀疏编码python 稀疏编码器的作用_迭代_17

( 

稀疏编码python 稀疏编码器的作用_稀疏编码_18

 是 

稀疏编码python 稀疏编码器的作用_稀疏编码_19

 的简写)


该目标函数可以通过以下过程迭代优化:

  1. 随机初始化A
  2. 重复以下步骤直至收敛:
  1. 根据上一步给定的A,求解能够最小化J(A,s)的s
  2. 根据上一步得到的s,,求解能够最小化J(A,s)的A


观察修改后的目标函数 J(A,s),给定 s 的条件下,目标函数可以简化为 

稀疏编码python 稀疏编码器的作用_稀疏编码python_20

(因为 s 的 L1 范式不是 A 的函数,所以可以忽略)。简化后的目标函数是一个关于 A 的简单二次项式,因此对 A 求导是很容易的。这种求导的一种快捷方法是矩阵微积分( 相关链接部分列出了跟矩阵演算有关的内容)。遗憾的是,在给定 A


理论上,通过上述迭代方法求解目标函数的最优化问题最终得到的特征集(A 的基向量)与通过稀疏自编码学习得到的特征集是差不多的。但是实际上,为了获得更好的算法收敛性需要使用一些小技巧,后面的 稀疏编码实践 稀疏编码实践章节会详细介绍这些技巧。用梯度下降方法求解目标函数也略需技巧,另外使用矩阵演算或 反向传播算法则有助于解决此类问题。

稀疏编码实践

如上所述,虽然稀疏编码背后的理论十分简单,但是要写出准确无误的实现代码并能快速又恰到好处地收敛到最优值,则需要一定的技巧。


回顾一下之前提到的简单迭代算法:

  1. 随机初始化A
  2. 重复以下步骤直至收敛到最优值:
  1. 根据上一步给定的A,求解能够最小化J(A,s)的s
  2. 根据上一步得到的s,求解能够最小化J(A,s)的A


这样信手拈来地执行这个算法,结果并不会令人满意,即使确实得到了某些结果。以下是两种更快更优化的收敛技巧:

  1. 将样本分批为“迷你块”
  2. 良好的s初始值

将样本分批为“迷你块”

如果你一次性在大规模数据集(比如,有10000 个patch)上执行简单的迭代算法,你会发现每次迭代都要花很长时间,也因此这算法要花好长时间才能达到收敛结果。为了提高收敛速度,可以选择在迷你块上运行该算法。每次迭代的时候,不是在所有的 10000 个 patchs 上执行该算法,而是使用迷你块,即从 10000 个 patch 中随机选出 2000 个 patch,再在这个迷你块上执行这个算法。这样就可以做到一石二鸟――第一,提高了每次迭代的速度,因为现在每次迭代只在 2000 个 patch 上执行而不是 10000个;第二,也是更重要的,它提高了收敛的速度(原因见TODO)。

良好的s初始值

A 的条件下,根据目标函数使用梯度下降(或其他方法)求解 s 之前找到良好的特征矩阵 s 的初始值。实际上,除非在优化 A 的最优值前已找到一个最佳矩阵 s,不然每次迭代过程中随机初始化 s 值会导致很差的收敛效果。下面给出一个初始化 s

  1. 令 (x
  2. s中的每个特征(s的每一列),除以其在A中对应基向量的范数。即,如果sr,c表示第c个样本的第r个特征,则Ac表示A中的第c个基向量,则令 


无疑,这样的初始化有助于算法的改进,因为上述的第一步希望找到满足 

稀疏编码python 稀疏编码器的作用_稀疏编码python_21

 的矩阵 s;第二步对 s 作规范化处理是为了保持较小的稀疏惩罚值。这也表明,只采用上述步骤的某一步而不是两步对 s 做初始化处理将严重影响算法性能。(TODO: 此链接将会对为什么这样的初始化能改进算法作出更详细的解释)

可运行算法

有了以上两种技巧,稀疏编码算法修改如下:

  1. 随机初始化A
  2. 重复以下步骤直至收敛
  1. 随机选取一个有2000个patches的迷你块
  2. 如上所述,初始化s
  3. 根据上一步给定的A,求解能够最小化J(A,s)的s
  4. 根据上一步得到的s,求解能够最小化J(A,s)的A

通过上述方法,可以相对快速的得到局部最优解。



稀疏编码概率解释 [基于1996年Olshausen与Field的理论]

到目前为止,我们所考虑的稀疏编码,是为了寻找到一个稀疏的、超完备基向量集,来覆盖我们的输入数据空间。现在换一种方式,我们可以从概率的角度出发,将稀疏编码算法当作一种“生成模型”。


我们将自然图像建模问题看成是一种线性叠加,叠加元素包括 k 个独立的源特征 

稀疏编码python 稀疏编码器的作用_迭代

 以及加性噪声 ν

稀疏编码python 稀疏编码器的作用_稀疏编码_23


我们的目标是找到一组特征基向量 

稀疏编码python 稀疏编码器的作用_最小化_24

 ,它使得图像的分布函数 

稀疏编码python 稀疏编码器的作用_稀疏编码python_25

 尽可能地近似于输入数据的经验分布函数 

稀疏编码python 稀疏编码器的作用_稀疏编码python_26

 。一种实现方式是,最小化 

稀疏编码python 稀疏编码器的作用_稀疏编码python_26

 与

稀疏编码python 稀疏编码器的作用_稀疏编码python_25

 之间的 KL 散度,此 KL 散度表示如下:

稀疏编码python 稀疏编码器的作用_稀疏编码_29


因为无论我们如何选择 

稀疏编码python 稀疏编码器的作用_最小化_24

 ,经验分布函数 

稀疏编码python 稀疏编码器的作用_稀疏编码python_26

 都是常量,也就是说我们只需要最大化对数似然函数 

稀疏编码python 稀疏编码器的作用_稀疏编码python_25

 。 假设 ν 是具有方差 σ2

稀疏编码python 稀疏编码器的作用_稀疏编码_33


为了确定分布 

稀疏编码python 稀疏编码器的作用_稀疏编码python_25

 ,我们需要指定先验分布 

稀疏编码python 稀疏编码器的作用_迭代_35

 。假定我们的特征变量是独立的,我们就可以将先验概率分解为:

稀疏编码python 稀疏编码器的作用_迭代_36


此时,我们将“稀疏”假设加入进来——假设任何一幅图像都是由相对较少的一些源特征组合起来的。因此,我们希望 ai


稀疏编码python 稀疏编码器的作用_最小化_37


这里 S(ai)


当定义了 

稀疏编码python 稀疏编码器的作用_最小化_38

 和 

稀疏编码python 稀疏编码器的作用_迭代_39

 后,我们就可以写出在由 

稀疏编码python 稀疏编码器的作用_最小化_24

 定义的模型之下的数据 

稀疏编码python 稀疏编码器的作用_迭代_02

 的概率分布:

稀疏编码python 稀疏编码器的作用_稀疏编码python_42


那么,我们的问题就简化为寻找:


\begin{align}\mathbf{\phi}^*=\text{argmax}_{\mathbf{\phi}} < \log(P(\mathbf{x} \mid \mathbf{\phi})) >\end{align}


这里 < . > 表示的是输入数据的期望值。


不幸的是,通过对 

稀疏编码python 稀疏编码器的作用_稀疏编码python_44

 的积分计算 

稀疏编码python 稀疏编码器的作用_最小化_45

 通常是难以实现的。虽然如此,我们注意到如果 

稀疏编码python 稀疏编码器的作用_最小化_45

 的分布(对于相应的 

稀疏编码python 稀疏编码器的作用_稀疏编码python_44

 )足够陡峭的话,我们就可以用 

稀疏编码python 稀疏编码器的作用_最小化_45

 的最大值来估算以上积分。估算方法如下:

稀疏编码python 稀疏编码器的作用_最小化_49\end{align}" title="\begin{align}\mathbf{\phi}^{*'}=\text{argmax}_{\mathbf{\phi}} < \max_{\mathbf{a}} \log(P(\mathbf{x} \mid \mathbf{\phi})) >\end{align}" style="width: 318px; visibility: visible;" data-type="block">


跟之前一样,我们可以通过减小 ai 或增大 

稀疏编码python 稀疏编码器的作用_最小化_24

 来增加概率的估算值(因为 P(ai) 在零值附近陡升)。因此我们要对特征向量 

稀疏编码python 稀疏编码器的作用_最小化_24

 加一个限制以防止这种情况发生。

最后,我们可以定义一种线性生成模型的能量函数,从而将原先的代价函数重新表述为:


稀疏编码python 稀疏编码器的作用_稀疏编码_52


其中 λ = 2σ2β


稀疏编码python 稀疏编码器的作用_最小化_53


使用概率理论来分析,我们可以发现,选择 L1 惩罚和 

稀疏编码python 稀疏编码器的作用_最小化_54

 惩罚作为函数 S(.) ,分别对应于使用了拉普拉斯概率 

稀疏编码python 稀疏编码器的作用_稀疏编码_55

 和柯西先验概率 

稀疏编码python 稀疏编码器的作用_稀疏编码_56

 。

学习算法

使用稀疏编码算法学习基向量集的方法,是由两个独立的优化过程组合起来的。第一个是逐个使用训练样本 

稀疏编码python 稀疏编码器的作用_迭代_02

 来优化系数 ai ,第二个是一次性处理多个样本对基向量 

稀疏编码python 稀疏编码器的作用_最小化_24

 进行优化。

如果使用 L1 范式作为稀疏惩罚函数,对 

稀疏编码python 稀疏编码器的作用_稀疏编码_59

 的学习过程就简化为求解 由 L1 范式正则化的最小二乘法问题,这个问题函数在域 

稀疏编码python 稀疏编码器的作用_稀疏编码_59

 内为凸,已经有很多技术方法来解决这个问题(诸如CVX之类的凸优化软件可以用来解决L1正则化的最小二乘法问题)。如果 S(.)

用 L2 范式约束来学习基向量,同样可以简化为一个带有二次约束的最小二乘问题,其问题函数在域 

稀疏编码python 稀疏编码器的作用_最小化_24

 内也为凸。标准的凸优化软件(如CVX)或其它迭代方法就可以用来求解 

稀疏编码python 稀疏编码器的作用_最小化_24

,虽然已经有了更有效的方法,比如求解拉格朗日对偶函数(Lagrange dual)。


根据前面的的描述,稀疏编码是有一个明显的局限性的,这就是即使已经学习得到一组基向量,如果为了对新的数据样本进行“编码”,我们必须再次执行优化过程来得到所需的系数。这个显著的“实时”消耗意味着,即使是在测试中,实现稀疏编码也需要高昂的计算成本,尤其是与典型的前馈结构算法相比。