为什么需要稀疏编码及解释

参考:(1) UFLDL

    (2) Why sparse coding works



稀疏编码算法是一种无监督学习方法,它用来寻找一组“超完备”基向量来更高效地表示样本数据。稀疏编码算法的目的就是找到一组基向量 \mathbf{\phi}_i ,使得我们能将输入向量 \mathbf{x} 表示为这些基向量的线性组合:

\begin{align}\mathbf{x} = \sum_{i=1}^k a_i \mathbf{\phi}_{i} \end{align}


虽然形如主成分分析技术(PCA)能使我们方便地找到一组“完备”基向量,但是这里我们想要做的是找到一组“超完备”基向量来表示输入向量 \mathbf{x}\in\mathbb{R}^n (也就是说,k > n)。超完备基的好处是它们能更有效地找出隐含在输入数据内部的结构与模式。然而,对于超完备基来说,系数 ai 不再由输入向量 \mathbf{x} 唯一确定。因此,在稀疏编码算法中,我们另加了一个评判标准“稀疏性”来解决因超完备而导致的退化(degeneracy)问题。要求系数 ai 是稀疏的意思就是说:对于一组输入向量,我们只想有尽可能少的几个系数远大于零。选择使用具有稀疏性的分量来表示我们的输入数据是有原因的,因为绝大多数的感官数据,比如自然图像,可以被表示成少量基本元素的叠加,在图像中这些基本元素可以是面或者线。

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

J(A, s) = \lVert As - x \rVert_2^2 + \lambda \lVert s \rVert_1

\lVert x \rVert_k是x的Lk范数,等价于 \left( \sum{ \left| x_i^k \right| } \right) ^{\frac{1}{k}}。L2 范数即大家熟知的欧几里得范数,L1 范数是向量元素的绝对值之和)


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


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

\begin{array}{rcl}     {\rm minimize} & \lVert As - x \rVert_2^2 + \lambda \lVert s \rVert_1 \\     {\rm s.t.}     &    A_j^TA_j \le 1 \; \forall j \\\end{array}


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


但是,以上表达式带来了另一个难题:不能用简单的梯度方法来实现约束条件 A_j^TA_j \le 1 \; \forall j。因此在实际问题中,此约束条件还不足以成为“权重衰变”("weight decay")项以保证 A 的每一项值够小。这样我们就得到一个新的目标函数:

J(A, s) = \lVert As - x \rVert_2^2 + \lambda \lVert s \rVert_1 + \gamma \lVert A \rVert_2^2

(注意上式中第三项, \lVert A \rVert_2^2等价于\sum_r{\sum_c{A_{rc}^2}},是A各项的平方和)


这一目标函数带来了最后一个问题,即 L1 范数在 0 点处不可微影响了梯度方法的应用。尽管可以通过其他非梯度下降方法避开这一问题,但是本文通过使用近似值“平滑” L1 范数的方法解决此难题。使用 \sqrt{x^2 + \epsilon} 代替 \left| x \right|, 对 L1 范数进行平滑,其中 ε 是“平滑参数”("smoothing parameter")或者“稀疏参数”("sparsity parameter") (如果 ε远大于x, 则 x + ε 的值由 ε 主导,其平方根近似于ε)。在下文提及拓扑稀疏编码时,“平滑”会派上用场。


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

J(A, s) = \lVert As - x \rVert_2^2 + \lambda \sqrt{s^2 + \epsilon} + \gamma \lVert A \rVert_2^2

( \sqrt{s^2 + \epsilon} 是 \sum_k{\sqrt{s_k^2 + \epsilon}} 的简写)


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

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


观察修改后的目标函数 J(A,s),给定 s 的条件下,目标函数可以简化为 J(A; s) = \lVert As - x \rVert_2^2 + \gamma \lVert A \rVert_2^2(因为 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. s \leftarrow W^Tx (x 是迷你块中patches的矩阵表示)
  2. s中的每个特征(s的每一列),除以其在A中对应基向量的范数。即,如果sr,c表示第c个样本的第r个特征,则Ac表示A中的第c个基向量,则令 s_{r, c} \leftarrow \frac{ s_{r, c} } { \lVert A_c \rVert }.


无疑,这样的初始化有助于算法的改进,因为上述的第一步希望找到满足 Ws \approx x 的矩阵 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 个独立的源特征 \mathbf{\phi}_i 以及加性噪声 ν :

\begin{align}\mathbf{x} = \sum_{i=1}^k a_i \mathbf{\phi}_{i} + \nu(\mathbf{x})\end{align}


我们的目标是找到一组特征基向量 \mathbf{\phi} ,它使得图像的分布函数 P(\mathbf{x}\mid\mathbf{\phi}) 尽可能地近似于输入数据的经验分布函数 P^*(\mathbf{x}) 。一种实现方式是,最小化 P^*(\mathbf{x}) 与P(\mathbf{x}\mid\mathbf{\phi}) 之间的 KL 散度,此 KL 散度表示如下:

\begin{align}D(P^*(\mathbf{x})||P(\mathbf{x}\mid\mathbf{\phi})) = \int P^*(\mathbf{x}) \log \left(\frac{P^*(\mathbf{x})}{P(\mathbf{x}\mid\mathbf{\phi})}\right)d\mathbf{x}\end{align}


因为无论我们如何选择 \mathbf{\phi} ,经验分布函数 P^*(\mathbf{x}) 都是常量,也就是说我们只需要最大化对数似然函数 P(\mathbf{x}\mid\mathbf{\phi}) 。 假设 ν 是具有方差 σ2 的高斯白噪音,则有下式:

\begin{align}P(\mathbf{x} \mid \mathbf{a}, \mathbf{\phi}) = \frac{1}{Z} \exp\left(- \frac{(\mathbf{x}-\sum^{k}_{i=1} a_i \mathbf{\phi}_{i})^2}{2\sigma^2}\right)\end{align}


为了确定分布 P(\mathbf{x}\mid\mathbf{\phi}) ,我们需要指定先验分布 P(\mathbf{a}) 。假定我们的特征变量是独立的,我们就可以将先验概率分解为:

\begin{align}P(\mathbf{a}) = \prod_{i=1}^{k} P(a_i)\end{align}


此时,我们将“稀疏”假设加入进来——假设任何一幅图像都是由相对较少的一些源特征组合起来的。因此,我们希望 ai 的概率分布在零值附近是凸起的,而且峰值很高。一个方便的参数化先验分布就是:

\begin{align}P(a_i) = \frac{1}{Z}\exp(-\beta S(a_i))\end{align}


这里 S(ai) 是决定先验分布的形状的函数。


当定义了 P(\mathbf{x} \mid \mathbf{a} , \mathbf{\phi}) 和  P(\mathbf{a}) 后,我们就可以写出在由 \mathbf{\phi} 定义的模型之下的数据 \mathbf{x} 的概率分布:

\begin{align}P(\mathbf{x} \mid \mathbf{\phi}) = \int P(\mathbf{x} \mid \mathbf{a}, \mathbf{\phi}) P(\mathbf{a}) d\mathbf{a}\end{align}


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

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


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


不幸的是,通过对 \mathbf{a} 的积分计算 P(\mathbf{x} \mid \mathbf{\phi}) 通常是难以实现的。虽然如此,我们注意到如果 P(\mathbf{x} \mid \mathbf{\phi}) 的分布(对于相应的 \mathbf{a} )足够陡峭的话,我们就可以用 P(\mathbf{x} \mid \mathbf{\phi}) 的最大值来估算以上积分。估算方法如下:

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


跟之前一样,我们可以通过减小 ai 或增大 \mathbf{\phi} 来增加概率的估算值(因为 P(ai) 在零值附近陡升)。因此我们要对特征向量 \mathbf{\phi} 加一个限制以防止这种情况发生。

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

\begin{array}{rl}E\left( \mathbf{x} , \mathbf{a} \mid \mathbf{\phi} \right) & := -\log \left( P(\mathbf{x}\mid \mathbf{\phi},\mathbf{a}\right)P(\mathbf{a})) \\ &= \sum_{j=1}^{m} \left|\left| \mathbf{x}^{(j)} - \sum_{i=1}^k a^{(j)}_i \mathbf{\phi}_{i}\right|\right|^{2} + \lambda \sum_{i=1}^{k}S(a^{(j)}_i) \end{array}


其中 λ = 2σ2β ,并且关系不大的常量已被隐藏起来。因为最大化对数似然函数等同于最小化能量函数,我们就可以将原先的优化问题重新表述为:

\begin{align}\mathbf{\phi}^{*},\mathbf{a}^{*}=\text{argmin}_{\mathbf{\phi},\mathbf{a}} \sum_{j=1}^{m} \left|\left| \mathbf{x}^{(j)} - \sum_{i=1}^k a^{(j)}_i \mathbf{\phi}_{i}\right|\right|^{2} + \lambda \sum_{i=1}^{k}S(a^{(j)}_i) \end{align}


使用概率理论来分析,我们可以发现,选择 L1 惩罚和 \log(1+a_i^2) 惩罚作为函数 S(.) ,分别对应于使用了拉普拉斯概率 P(a_i) \propto \exp\left(-\beta|a_i|\right) 和柯西先验概率 P(a_i) \propto \frac{\beta}{1+a_i^2} 。


学习算法

使用稀疏编码算法学习基向量集的方法,是由两个独立的优化过程组合起来的。第一个是逐个使用训练样本 \mathbf{x} 来优化系数 ai ,第二个是一次性处理多个样本对基向量 \mathbf{\phi} 进行优化。


如果使用 L1 范式作为稀疏惩罚函数,对 a^{(j)}_i 的学习过程就简化为求解 由 L1 范式正则化的最小二乘法问题,这个问题函数在域 a^{(j)}_i 内为凸,已经有很多技术方法来解决这个问题(诸如CVX之类的凸优化软件可以用来解决L1正则化的最小二乘法问题)。如果 S(.) 是可微的,比如是对数惩罚函数,则可以采用基于梯度算法的方法,如共轭梯度法。


用 L2 范式约束来学习基向量,同样可以简化为一个带有二次约束的最小二乘问题,其问题函数在域 \mathbf{\phi} 内也为凸。标准的凸优化软件(如CVX)或其它迭代方法就可以用来求解 \mathbf{\phi},虽然已经有了更有效的方法,比如求解拉格朗日对偶函数(Lagrange dual)。


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



  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值