Categories

Calendar

July 2017
M T W T F S S
« Jun    
 12
3456789
10111213141516
17181920212223
24252627282930
31  

求最小的几个特征值

在机器学习中经常都会遇到特征值问题,例如 Laplacian Eigenmaps 或者一大堆的 KernelPCA 派的降维方法,或者谱聚类之类的。通常都是对于一个很大并且比较稀疏的矩阵,求最大或者最小的几个特征值以及对应的特征向量。在 Matlab 里,eig 函数可以用来求得一个矩阵的全部特征值和特征向量,然而,如果我们只需要其中最大或者最小的那几个,用 eig 来求就显得杀鸡用牛刀了,费力还不讨好。而且,eig 还不能处理稀疏矩阵的情况,所以,这个时候通常就需要 eigs 出场了。

eigs 可以只求矩阵的几个最小的或者最大的(或者最接近某一个数值的)特征值和对应的特征向量,并且能够处理稀疏矩阵(如果矩阵规模很大而且稀疏程度有很高的话,使用稀疏矩阵速度会快很多)。当然它使用的算法和 eig 不一样,是迭代的方式(具体的细节我也不是很清楚,记得数值分析课上讲过 power method ,不过也忘得精光了 -.-)。这也造成了一个小缺点,就是当矩阵是 singular 或者 near-singular 的时候,如果求最小的几个特征值,就会有问题。例如下面这个极端的例子,我们造一个 rank 为 1 的矩阵:

a = rand(3,1);
A = a*a';
eigs(A, 2, ‘SM’);

Matlab 就会在这里出错。让人很痛苦,因为有时候会在数据中碰到矩阵 near-singular 的情况。一开始我找不到解决办法,因为 eig 是可以处理 singular 矩阵的,所以干脆在遇到 eigs 出错的时候转由 eig 来计算,然后手工选出最小的几个特征值和特征向量。

巨大的 Matlab 存储数据

好吧,我的标题越来越土了,本来想取个“XX之迷”或者“走进科学”啥的,不过其实是个小问题。总之,最近我在用 matlab 跑一些实验的时候,保存的结果比较大,其实也不算太大,就几百兆,但是由于有很多个这样的文件,所以占用空间还是比较大的,而且几百兆的 .mat 文件每次 load 进来画个图之类的也要等半天,非常不方便。于是我就想能不能有方法处理一下。

一开始我以为是因为我存储的数据里有一些位图,所以才会比较大,于是我写了一个工具来将位图压缩了一下,发现单张图片即使是无损 JPEG 压缩也可以变得比原来小很多,于是赶紧用来处理原来存储的文件,结果发现处理之后基本上没有任何改观。由于很久没有遇到这么神奇的“bug”了,我比较兴奋,把代码相关的地方都检查了一遍,发现其中有一个中间计算结果作为 cache 放在一个 struct 的 field 里,而这个 cache 理论上是可以丢掉的(虽然要再算一遍很耗时,但是基本上不会再重跑这个实验了),于是我果断把它咔嚓掉。再一运行,保存……结果发现居然一丁点改观都没有!

更为诡异地是,我用 whos 看 matlab 里那个 struct 占用的空间,得到的结果其实是很小,但是当我 save 它到文件的时候,它就莫名其妙地占用了大量空间。 -.-bb 我用一个赶紧的 matlab workspace 把这个文件加载进来,逐一删掉其中的变量做测试。

其实一开始我也比较怀疑其中的一个 function_handle 的 field ,我在想不会 matlab 聪明到为了保证 function handle 下次加载进来能够运行,把所有相关的代码和引用到的代码也一应打包了吧?-.-bb 但是 Matlab 的文档说得很清楚就是在其他地方 load function handle 的时候,如果原来的代码文件不存在或者被修改过的话,就会发生 unexpected 结果。但是呢,当我删去 struct 的那个 function handle 的时候,竟然真的就瞬间从几百兆变成几兆了。

虽然是大跌眼镜,但是我突然一下子又想明白了。原来那个 function handle […]

Generate Recursive Images

在上一篇 blog 中我提到了递归图片,还给了一个有趣的例子,这次还说递归图片,再给另一个例子:

不过这次的例子是我自己生成的,而篇 blog 就是要讲如何来生成这样一张递归图片。其实方法很简单,类推一下,多花一些功夫的话,之前给的那个“二次递归”的例子也是可以“轻松”做出来的。

解决 Matlab MEX 编译时 GCC 版本的问题

在 Matlab 里使用 mex 来编译 C/C++ 代码失败,这个问题算是困扰了我好几个月了,主要是我的环境比较恶劣:系统是 Arch Linux ,感觉这个系统比较喜欢追求最新版本,比较无视老版本软件的兼容性问题吧。再加上系统是 64 位的,出现各种兼容性问题似乎也觉得是理所当然的,然后像 Matlab 这样的软件通常使用较老版本的 GCC ,而 GCC 更新的时候又做了一些大改动,总之结果是我每次试图编译的时候都得到类似这样的错误:

/usr/lib/gcc/x86_64-unknown-linux-gnu/4.4.2/cc1: /opt/matlab/sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.11′ not found (required by /usr/lib/libgmpxx.so.4)

因为 GCC 新版本里把 GLIBCXX_3.4.11 这个符号去掉了,所以挂掉了。搞得我每次需要运行 mex 编译的代码都要放到另外一台 Windows 机器上跑。也 Google 了很多次都没有找到解决方案。今天一怒之下用了最暴力的方法:/opt/matlab/sys/os/glnxa64/libstdc++.so.6 其实是指向该目录下的另一个文件 libstdc++.so.6.0.9 的软连接,我将他改成指向系统里的新版本的 libstdc++:/usr/lib/libstdc++.so.6.0.13 ,结果问题解决了。那个目录下还有一个 README ,说了这几个文件是从 GCC distribution 里拷贝过来的。于是这样用新版本覆盖应该不会有太大的问题吧? 至少目前还没有出什么问题的。

Matlab vs. scipy & numpy

实际上关于 Matlab 和 Python 的争论和比较已经非常非常多了。Matlab 本来是在相关领域可以算作“标准”的平台和工具了,然而 Python 有了 numpy ,又有了 scipy ,似乎相当有潜力和 Matlab 一较高下。在 scipy 的网站上有一个页面专门讲 Matlab 中常见的功能和函数在 numpy 中如何实现:NumPy for Matlab Users。看起来似乎还挺不错的。所以我之前都有想在 scipy/numpy 的基础上搭建相关的平台的想法,并且我在“漫谈 Clustering 系列”的前面几篇文章中也使用了 Python 代码作为示例。但是在我写系列中的第 4 篇(关于 Gaussian Mixture Model )的时候,还是直接用了 Matlab 代码。因为在那个时候我已经暂时放弃了工作在 scipy/numpy 里的想法。下面我还是从我自己的角度来谈一谈我对两者比较吧。

要比较 Matlab 和 Python ,不得不说的就是 license 了,前者是非常典型的昂贵的商业软件,而后者是开源软件的典范。如果说其他方面差不多的话,对于个人、科研或者教育方面使用来说,应当更优先选择 Python 了。但是情况显然不是这么简单,而且若仅仅是考虑 Matlab 的那些特性的话,也有一个开源的 clone Octave 可供选择。而 Python 的魅力似乎更多地在于 Matlab […]

在 VIM 中打开加号开头的文件

我在上一篇文章中提到 Matlab 以加号开头的目录来表示一个 package ,但是文件名开头的加号对于 VIM 来说有特殊的意义,每次打开 +foo/bar.m 这样的文件的时候都要用 ./+foo/bar.m 这样的方式,非常麻烦。

虽然 VIM 那个脚本语言甚至比 Matlab 的脚本语言还要丑陋许多,但是至少也是号称扩展性超强的编辑器,我觉得这样的任务至少应该能自动化一下。Google 了一下,很快就找到了一个类似的帖子:vim: Modifying filename in bufreadpre? ,似乎和我这里遇到的问题如出一辙,问问题的人是想要用 : 来代替 $TMPDIR ,每次打开 :/foo 的时候能自动变成 $TMPDIR/foo 。

放弃在 Matlab 中 OOP 了

当然,做 Research ,或者说做实验,目的就是要跑出结果来,代码写成什么样子其实无关紧要,而且通常代码的生存时间(比起一般的项目代码来说)很短,大可以不必理会下面这个 tip :

不过,太乱的代码自己多少也有些看不顺眼,而且养成一个习惯毕竟也是好事,其实做实验的一些代码也是经常要重用的。特别是我现在这种状态,经常要作为苦力把各种算法拿过来跑一跑,做一个对比。其实除非是做那种完全纯理论的研究,一般都是需要实验来验证的,而做实验就必然要做对比,何老师经常说:“做 Research ,目的不是你的结果要有多好,关键的地方在于你要做得比所有现在已知的方法都要好。”其实也挺有道理的。不过这样一来,会有一些经常需要用来做比较的方法,这些代码在各个实验中其实是差不多的,也总会想着能够尽量地重用,而不是每次都重新写一遍或者 copy & paste (通常是诡异的 bug 滋生的温床)。