流形学习 manifold learning--自学小结(2)之 Isomap

来源:互联网 发布:梧桐一叶而天下知秋 编辑:程序博客网 时间:2024/06/06 08:42

http://isomap.stanford.edu/     应有尽有

另外,中文blog:
http://www.cvchina.info/2010/05/31/manifold-learning/#more-1038

 Isomap 论文里的一个结果:

这里的图片来自同一张人脸(好吧,其实是人脸模型),每张图片是 64×64 的灰度图,如果把位图按照列(或行)拼起来,就可以得到一个 4096 维的向量,这样一来,每一张图片就可以看成是 4096 维欧氏空间中的一个点。很显然,并不是 4096 维空间中任意一个点都可以对应于一张人脸图片的,这就类似于球面的情形,我们可以假定所有可以是人脸的 4096 维向量实际上分布在一个 d 维 (d < 4096) 的子空间中。而特定到 Isomap 的人脸这个例子,实际上我们知道所有的 698 张图片是拍自同一个人脸(模型),不过是在不同的 pose 和光照下拍摄的,如果把 pose (上下和左右)当作两个自由度,而光照当作一个自由度,那么这些图片实际只有三个自由度,换句话说,存在一个类似于球面一样的参数方程(当然,解析式是没法写出来的),给定一组参数(也就是上下、左右的 pose 和光照这三个值),就可以生成出对应的 4096 维的坐标来。换句话说,这是一个嵌入在 4096 维欧氏空间中的一个 3 维流形。

实际上,上面的那张图就是 Isomap 将这个数据集从 4096维映射到 3 维空间中,并显示了其中 2 维的结果,图中的小点就是每个人脸在这个二维空间中对应的坐标位置,其中一些标红圈的点被选出来,并在旁边画上了该点对应的原始图片,可以很直观地看出这两个维度正好对应了 pose 的两个自由度平滑变化的结果。

就我目前所知,把流形引入到机器学习领域来主要有两种用途:一是将原来在欧氏空间中适用的算法加以改造,使得它工作在流形上,直接或间接地对流形的结构和性质加以利用;二是直接分析流形的结构,并试图将其映射到一个欧氏空间中,再在得到的结果上运用以前适用于欧氏空间的算法来进行学习。

这里 Isomap 正巧是一个非常典型的例子,因为它实际上是通过“改造一种原本适用于欧氏空间的算法”,达到了“将流形映射到一个欧氏空间”的目的。 :)

Isomap 所改造的这个方法叫做 Multidimensional Scaling (MDS) ,MDS 是一种降维方法,它的目的就是使得降维之后的点两两之间的距离尽量不变(也就是和在原是空间中对应的两个点之间的距离要差不多)。只是 MDS 是针对欧氏空间设计的,对于距离的计算也是使用欧氏距离来完成的。如果数据分布在一个流形上的话,欧氏距离就不适用了。

让我们再回到地球——这个在三维空间中的二维流形,假设我们要在三维空间中计算北极点和南极点的距离,这很容易,就是两点相连的线段的长度,可是,如果要在这个流形上算距离就不能这样子算了,我们总不能从北极打个洞钻到南极去吧?要沿着地球表面走才行,当然,如果我随便沿着什么路线走一遍,然后数出总共走了多少步作为距离,这是不成的,因为这样一来如果我沿着不同的路线走,岂不是会得到不同的距离值?总而言之,我们现在需要一个新的定义在地球表面(流形)上的距离度量,理论上来说,任意满足测度的 4 个条件的函数都可以被定义为距离,不过,为了和欧氏空间对应起来,这里选择一个直线距离的推广定义。

还记得初中学的“两点之间,线段最短”吗?现在,我们反过来说,把线段的概念推广一下,变成“两点之间最短的曲线是线段”,于是流形上的距离定义也就等同于欧氏空间了:流形上两个点之间的距离就是连接两个点的“线段”的长度。虽然只是置换了一个概念,但是现在两者统一起来了,不过,在流形上的线段大概就不一定是“直”的了(于是直线也变成不一定是“直”的了),通常又称作是“测地线”。对于球面这个简单的流形来说,任意一条线段必定是在一个“大圆”上的,于是球面上的直线其实都是一些大圆,也造成了球面这个流形上没有平行线等一系列尴尬的局面(任意两条直线均相交),如果你看过一些数学科普八卦类的书,应该会回忆起不少东西啦!

回到 Isomap ,它主要做了一件事情,就是把 MDS 中原始空间中距离的计算从欧氏距离换为了流形上的测地距离。当然,如果流形的结构事先不知道的话,这个距离是没法算的,于是 Isomap 通过讲数据点连接起来构成一个邻接 Graph 来离散地近似原来的流形,而测地距离也相应地通过 Graph 上的最短路径来近似了。如下图所示:

这个东西叫做 Swiss Roll ,姑且把它看作一块卷起来的布好了。图中两个标黑圈的点,如果通过外围欧氏空间中的欧氏距离来计算的话,会是挨得很近的点,可是在流形上它们实际上是距离很远的点:红色的线是 Isomap 求出来的流形上的距离。可以想像,如果是原始的 MDS 的话,降维之后肯定会是很暴力地直接把它投影到二维空间中,完全无视流形结构,而 Isomap 则可以成功地将流形“展开”之后再做投影。





实验:

ISOMAP人脸识别

两种经典的非线性降维(Nonlinear Dimensionality Reduction)方法:lle和isomap。问题是别人做过的,算法实现也基本都是现成的,我只是拿来"玩一玩"。实验有很多环节,最有趣的一个环节,是给你698张人脸的图像(64×64灰度),通过isomap降维方法将每张脸当做一个点映到二维平面上,使得横坐标恰好反映人脸左右看的程度,纵坐标反映人脸上下看的程度。

如果你也对这个实验感兴趣,就往下读吧,很简单的~

实验环境:Matlab6.5

实验步骤

步骤一:准备数据集和工具包

下载

人脸数据集:http://waldron.stanford.edu/~isomap/face_data.mat.Z

并解压缩

下载

isomap算法实现:http://waldron.stanford.edu/~isomap/code/

的所有代码

步骤二:

准备图片标记的人脸序号集:一共有698张人脸,都画在平面上太拥挤了,所以选了30个人脸(存入posesSelect.mat的ks向量),选取的准则是:30个人脸的姿态尽量不同,也就是希望画在平面上尽量分散。事实上,face_data.mat数据集中,poses是一个2行698列的矩阵,第j列就是第j张人脸的客观姿态。

绘制客观姿态分布图:

load face_data

load posesSelect

showFacesOnR2(images,poses,ks)

http://lh6.ggpht.com/_iMG9M3S-9Xo/SWXkfHPcWNI/AAAAAAAACBs/otRKwIQ6DMc/s800/image002.gif

步骤三:降维

用Isomap算法将4096维的人脸数据images降维到2维,并绘制在平面上

load face_data

load posesSelect

D=L2_distance(images,images,1);

options.dims = [2];

[Y, R, E] = IsomapII(D, 'k', 7, options); 

showFacesOnR2(images,Y.coords{1},ks);

http://lh4.ggpht.com/_iMG9M3S-9Xo/SWXkfu3IIOI/AAAAAAAACB0/nbFaLN3X1HM/s800/image004.gif

D是一个距离矩阵,i行j列值表示人脸i和人脸j的距离,这里把一个人脸图像数据当做一个向量,使用2范数定义距离。

IsomapII是高性能算法,先把D用k=7近邻打成稀疏矩阵,然后用基于斐波那契堆的Dijkstra算法计算最短路,Dijkstra算法用C实现使用并且编译成了.dll文件为了提高效率。计算结果对我们有用的是Y.coords{1},它保存了降维后的结果,是2行698列的矩阵。

观察计算结果发现,以中间那个正的人脸为中心,他左边的都在向左看,而且越是靠左的转动越明显。同理,他右面的都在向右看、上面的都在向下看、下面的在向上看。与客观姿态分布基本吻合。

实验细节:

showFacesOnR2.m

%把头像和姿态坐标画在平面上

function showFacesOnR2(images,poses,ks);

%normalize into 1:1

poses(1,:)=poses(1,:)/range(poses(1,:));

poses(2,:)=poses(2,:)/range(poses(2,:));

%draw all points

scatter(poses(1,:),poses(2,:),12,'o','filled');

xlabel('left-right pose');

ylabel('up-down pose'); 

hold on

%draw selected points

scatter(poses(1,ks),poses(2,ks),24,'ro');

hold on

%draw images on selected points

scale = 0.001;

x=zeros(64,64);

for p=1:size(ks,2)

    k=ks(p);

    for i=1:64

        x(:,i)=images((i-1)*64+1:i*64,k);

    end

    xc=poses(1,k);

    yc=poses(2,k);

    imshow(xc:scale:xc+64*scale,yc:-scale:yc-64*scale,x);

    hold on

end

return



转自:http://hi.baidu.com/bess_tang/item/f3f0d216cde2520d8fbde438