C++,python,热爱算法和机器学习
全部博文(1214)
分类: 高性能计算
2017-12-13 18:24:43
上一篇博文,我们聊到了L0,L1和L2范数,这篇我们絮叨絮叨下核范数和规则项参数选择。知识有限,以下都是我一些浅显的看法,如果理解存在错误,希望大家不吝指正。谢谢。
三、核范数
核范数||W||*是指矩阵奇异值的和,英文称呼叫Nuclear Norm。这个相对于上面火热的L1和L2来说,可能大家就会陌生点。那它是干嘛用的呢?霸气登场:约束Low-Rank(低秩)。OK,OK,那我们得知道Low-Rank是啥?用来干啥的?
我们先来回忆下线性代数里面“秩”到底是啥?举个简单的例子吧:
对上面的线性方程组,第一个方程和第二个方程有不同的解,而第2个方程和第3个方程的解完全相同。从这个意义上说,第3个方程是“多余”的,因为它没有带来任何的信息量,把它去掉,所得的方程组与原来的方程组同解。为了从方程组中去掉多余的方程,自然就导出了“矩阵的秩”这一概念。
还记得我们怎么手工求矩阵的秩吗?为了求矩阵A的秩,我们是通过矩阵初等变换把A化为阶梯型矩阵,若该阶梯型矩阵有r个非零行,那A的秩rank(A)就等于r。从物理意义上讲,矩阵的秩度量的就是矩阵的行列之间的相关性。如果矩阵的各行或列是线性无关的,矩阵就是满秩的,也就是秩等于行数。回到上面线性方程组来说吧,因为线性方程组可以用矩阵描述嘛。秩就表示了有多少个有用的方程了。上面的方程组有3个方程,实际上只有2个是有用的,一个是多余的,所以对应的矩阵的秩就是2了。
OK。既然秩可以度量相关性,而矩阵的相关性实际上有带有了矩阵的结构信息。如果矩阵之间各行的相关性很强,那么就表示这个矩阵实际可以投影到更低维的线性子空间,也就是用几个向量就可以完全表达了,它就是低秩的。所以我们总结的一点就是:如果矩阵表达的是结构性信息,例如图像、用户-推荐表等等,那么这个矩阵各行之间存在这一定的相关性,那这个矩阵一般就是低秩的。
如果X是一个m行n列的数值矩阵,rank(X)是X的秩,假如rank (X)远小于m和n,则我们称X是低秩矩阵。低秩矩阵每行或每列都可以用其他的行或列线性表出,可见它包含大量的冗余信息。利用这种冗余信息,可以对缺失数据进行恢复,也可以对数据进行特征提取。
好了,低秩有了,那约束低秩只是约束rank(w)呀,和我们这节的核范数有什么关系呢?他们的关系和L0与L1的关系一样。因为rank()是非凸的,在优化问题里面很难求解,那么就需要寻找它的凸近似来近似它了。对,你没猜错,rank(w)的凸近似就是核范数||W||*。
好了,到这里,我也没什么好说的了,因为我也是稍微翻看了下这个东西,所以也还没有深入去看它。但我发现了这玩意还有很多很有意思的应用,下面我们举几个典型的吧。
1)矩阵填充(Matrix Completion):
我们首先说说矩阵填充用在哪。一个主流的应用是在推荐系统里面。我们知道,推荐系统有一种方法是通过分析用户的历史记录来给用户推荐的。例如我们在看一部电影的时候,如果喜欢看,就会给它打个分,例如3颗星。然后系统,例如Netflix等知名网站就会分析这些数据,看看到底每部影片的题材到底是怎样的?针对每个人,喜欢怎样的电影,然后会给对应的用户推荐相似题材的电影。但有一个问题是:我们的网站上面有非常多的用户,也有非常多的影片,不是所有的用户都看过说有的电影,不是所有看过某电影的用户都会给它评分。假设我们用一个“用户-影片”的矩阵来描述这些记录,例如下图,可以看到,会有很多空白的地方。如果这些空白的地方存在,我们是很难对这个矩阵进行分析的,所以在分析之前,一般需要先对其进行补全。也叫矩阵填充。
那到底怎么填呢?如何才能无中生有呢?每个空白的地方的信息是否蕴含在其他已有的信息之上了呢?如果有,怎么提取出来呢?Yeah,这就是低秩生效的地方了。这叫低秩矩阵重构问题,它可以用如下的模型表述:已知数据是一个给定的m*n矩阵A,如果其中一些元素因为某种原因丢失了,我们能否根据其他行和列的元素,将这些元素恢复?当然,如果没有其他的参考条件,想要确定这些数据很困难。但如果我们已知A的秩rank(A)<
2)鲁棒PCA:
主成分分析,这种方法可以有效的找出数据中最“主要"的元素和结构,去除噪音和冗余,将原有的复杂数据降维,揭示隐藏在复杂数据背后的简单结构。我们知道,最简单的主成分分析方法就是PCA了。从线性代数的角度看,PCA的目标就是使用另一组基去重新描述得到的数据空间。希望在这组新的基下,能尽量揭示原有的数据间的关系。这个维度即最重要的“主元"。PCA的目标就是找到这样的“主元”,最大程度的去除冗余和噪音的干扰。
鲁棒主成分分析(Robust PCA)考虑的是这样一个问题:一般我们的数据矩阵X会包含结构信息,也包含噪声。那么我们可以将这个矩阵分解为两个矩阵相加,一个是低秩的(由于内部有一定的结构信息,造成各行或列间是线性相关的),另一个是稀疏的(由于含有噪声,而噪声是稀疏的),则鲁棒主成分分析可以写成以下的优化问题:
与经典PCA问题一样,鲁棒PCA本质上也是寻找数据在低维空间上的最佳投影问题。对于低秩数据观测矩阵X,假如X受到随机(稀疏)噪声的影响,则X的低秩性就会破坏,使X变成满秩的。所以我们就需要将X分解成包含其真实结构的低秩矩阵和稀疏噪声矩阵之和。找到了低秩矩阵,实际上就找到了数据的本质低维空间。那有了PCA,为什么还有这个Robust PCA呢?Robust在哪?因为PCA假设我们的数据的噪声是高斯的,对于大的噪声或者严重的离群点,PCA会被它影响,导致无法正常工作。而Robust PCA则不存在这个假设。它只是假设它的噪声是稀疏的,而不管噪声的强弱如何。
由于rank和L0范数在优化上存在非凸和非光滑特性,所以我们一般将它转换成求解以下一个松弛的凸优化问题:
说个应用吧。考虑同一副人脸的多幅图像,如果将每一副人脸图像看成是一个行向量,并将这些向量组成一个矩阵的话,那么可以肯定,理论上,这个矩阵应当是低秩的。但是,由于在实际操作中,每幅图像会受到一定程度的影响,例如遮挡,噪声,光照变化,平移等。这些干扰因素的作用可以看做是一个噪声矩阵的作用。所以我们可以把我们的同一个人脸的多个不同情况下的图片各自拉长一列,然后摆成一个矩阵,对这个矩阵进行低秩和稀疏的分解,就可以得到干净的人脸图像(低秩矩阵)和噪声的矩阵了(稀疏矩阵),例如光照,遮挡等等。至于这个的用途,你懂得。
3)背景建模:
背景建模的最简单情形是从固定摄相机拍摄的视频中分离背景和前景。我们将视频图像序列的每一帧图像像素值拉成一个列向量,那么多个帧也就是多个列向量就组成了一个观测矩阵。由于背景比较稳定,图像序列帧与帧之间具有极大的相似性,所以仅由背景像素组成的矩阵具有低秩特性;同时由于前景是移动的物体,占据像素比例较低,故前景像素组成的矩阵具有稀疏特性。视频观测矩阵就是这两种特性矩阵的叠加,因此,可以说视频背景建模实现的过程就是低秩矩阵恢复的过程。
4)变换不变低秩纹理(TILT):
以上章节所介绍的针对图像的低秩逼近算法,仅仅考虑图像样本之间像素的相似性,却没有考虑到图像作为二维的像素集合,其本身所具有的规律性。事实上,对于未加旋转的图像,由于图像的对称性与自相似性,我们可以将其看做是一个带噪声的低秩矩阵。当图像由端正发生旋转时,图像的对称性和规律性就会被破坏,也就是说各行像素间的线性相关性被破坏,因此矩阵的秩就会增加。
低秩纹理映射算法(TransformInvariant Low-rank Textures,TILT)是一种用低秩性与噪声的稀疏性进行低秩纹理恢复的算法。它的思想是通过几何变换τ把D所代表的图像区域校正成正则的区域,如具有横平竖直、对称等特性,这些特性可以通过低秩性来进行刻画。
低秩的应用非常多,大家有兴趣的可以去找些资料深入了解下。
四、规则化参数的选择
现在我们回过头来看看我们的目标函数:
里面除了loss和规则项两块外,还有一个参数λ。它也有个霸气的名字,叫hyper-parameters(超参)。你不要看它势单力薄的,它非常重要。它的取值很大时候会决定我们的模型的性能,事关模型生死。它主要是平衡loss和规则项这两项的,λ越大,就表示规则项要比模型训练误差更重要,也就是相比于要模型拟合我们的数据,我们更希望我们的模型能满足我们约束的Ω(w)的特性。反之亦然。举个极端情况,例如λ=0时,就没有后面那一项,代价函数的最小化全部取决于第一项,也就是集全力使得输出和期待输出差别最小,那什么时候差别最小啊,当然是我们的函数或者曲线可以经过所有的点了,这时候误差就接近0,也就是过拟合了。它可以复杂的代表或者记忆所有这些样本,但对于一个新来的样本泛化能力就不行了。毕竟新的样本会和训练样本有差别的嘛。
那我们真正需要什么呢?我们希望我们的模型既可以拟合我们的数据,又具有我们约束它的特性。只有它们两者的完美结合,才能让我们的模型在我们的任务上发挥强大的性能。所以如何讨好它,是非常重要。在这点上,大家可能深有体会。还记得你复现了很多论文,然后复现出来的代码跑出来的准确率没有论文说的那么高,甚至还差之万里。这时候,你就会怀疑,到底是论文的问题,还是你实现的问题?实际上,除了这两个问题,我们还需要深入思考另一个问题:论文提出的模型是否具有hyper-parameters?论文给出了它们的实验取值了吗?经验取值还是经过交叉验证的取值?这个问题是逃不掉的,因为几乎任何一个问题或者模型都会具有hyper-parameters,只是有时候它是隐藏着的,你看不到而已,但一旦你发现了,证明你俩有缘,那请试着去修改下它吧,有可能有“奇迹”发生哦。
OK,回到问题本身。我们选择参数λ的目标是什么?我们希望模型的训练误差和泛化能力都很强。这时候,你有可能还反映过来,这不是说我们的泛化性能是我们的参数λ的函数吗?那我们为什么按优化那一套,选择能最大化泛化性能的λ呢?Oh,sorry to tell you that,因为泛化性能并不是λ的简单的函数!它具有很多的局部最大值!而且它的搜索空间很大。所以大家确定参数的时候,一是尝试很多的经验值,这和那些在这个领域摸爬打滚的大师是没得比的。当然了,对于某些模型,大师们也整理了些调参经验给我们。例如Hinton大哥的那篇A Practical Guide to Training RestrictedBoltzmann Machines等等。还有一种方法是通过分析我们的模型来选择。怎么做呢?就是在训练之前,我们大概计算下这时候的loss项的值是多少?Ω(w)的值是多少?然后针对他们的比例来确定我们的λ,这种启发式的方法会缩小我们的搜索空间。另外一种最常见的方法就是交叉验证Cross validation了。先把我们的训练数据库分成几份,然后取一部分做训练集,一部分做测试集,然后选择不同的λ用这个训练集来训练N个模型,然后用这个测试集来测试我们的模型,取N模型里面的测试误差最小对应的λ来作为我们最终的λ。如果我们的模型一次训练时间就很长了,那么很明显在有限的时间内,我们只能测试非常少的λ。例如假设我们的模型需要训练1天,这在深度学习里面是家常便饭了,然后我们有一个星期,那我们只能测试7个不同的λ。这就让你遇到最好的λ那是上辈子积下来的福气了。那有什么方法呢?两种:一是尽量测试7个比较靠谱的λ,或者说λ的搜索空间我们尽量广点,所以一般对λ的搜索空间的选择一般就是2的多少次方了,从-10到10啊什么的。但这种方法还是不大靠谱,最好的方法还是尽量让我们的模型训练的时间减少。例如假设我们优化了我们的模型训练,使得我们的训练时间减少到2个小时。那么一个星期我们就可以对模型训练7*24/2=84次,也就是说,我们可以在84个λ里面寻找最好的λ。这让你遇见最好的λ的概率就大多了吧。这就是为什么我们要选择优化也就是收敛速度快的算法,为什么要用GPU、多核、集群等来进行模型训练、为什么具有强大计算机资源的工业界能做很多学术界也做不了的事情(当然了,大数据也是一个原因)的原因了。
努力做个“调参”高手吧!祝愿大家都能“调得一手好参”!
五、参考资料
[1]
[2]
[3]
[4]