LLE 的思路很特别:不用坐标,只用「谁挨着谁」。 先给每个点找邻居、 算出一组「用邻居拼出自己」的权重,再到低维里找一组坐标,让这组权重依然成立。 Demo 拿瑞士卷试:PCA 的邻居保持率 0.5050,LLE 0.5833; 但换到一张本来就不弯的平面上,PCA 是 1.0000,LLE 只有 0.9167——它反而更差。
前面六个算法里,PCA、LSA、NMF、LDA 都是线性的换坐标, k-means 和混合高斯处理的则是「几团拼起来」的结构。 这一节的场景是第三种:数据躺在一张弯的面上。 瑞士卷就是标准例子——它本质上是一条带子, 二维的,但被卷成了三维的螺旋。PCA 只会旋转和投影,遇到弯的必然把不同层叠在一起。
一、线性方法的死穴
PCA 做的事是「旋转坐标系,然后丢掉几个方向」。 它的所有操作都是线性的:投影是线性变换,重建也是线性变换。线性的东西没法把弯的掰直。 想象一张卷起来的纸:纸面上相邻的两点,在三维空间里也相邻; 但如果卷了很多圈, 隔着两层的两个点在三维空间里可能贴得很近,而在纸面上离得很远。 这是「空间距离」和「面上距离」不一致。 PCA 只看空间距离,所以它会把这两层点当成邻居,压平之后它们就叠在一起了。 结果是一张糊掉的图,而且算法不会报错。 要解决这个问题,必须放弃「用坐标做线性变换」这条路。 LLE 的想法是:既然坐标不可靠,那就干脆不用坐标,只用「邻居关系」。 邻居关系是拓扑的, 卷起来也不会变。
二、第一步与第二步:只用距离,不用方向
第一步,给每个点找 k 个最近邻。 这一步用的还是欧氏距离,但只用来决定「谁是邻居」,不用来定位。
第二步,算一组权重。 对每个点 i,解一个小最小二乘问题:找一组数 w,
使得「邻居按 w 加权平均」尽量等于 i 自己,而且 w 加起来等于 1。
这个权重有个关键性质:它描述的是「局部的几何关系」,和坐标系无关。 你把整份数据旋转、平移、
缩放,权重都不会变。这就是 LLE 能对付弯曲的原因——它抓的东西本来就跟「弯不弯」无关。
为什么要求权重和为 1?因为不要求的话,把所有权重乘 2 能让误差变 4 倍,优化就没边了。
归一化之后,权重才是「比例」而不是「大小」。
求解时要加一点岭(代码里的 reg * np.trace(C))。
原因很实在:k 个邻居如果恰好落在一个低维子空间里(比如 3 维空间里的平面),矩阵 C 就奇异了,
直接求解会炸。这一点正则不是为了精度,是为了能算。
三、第三步:把权重搬到低维去
权重算完之后就固定了,再也不改。
现在反过来:已知这组权重,求一组低维坐标 y,使得「y_i 也约等于邻居的加权平均」。
这个目标可以写成「最小化 Σ |y_i − Σ w_ij y_j|²」。
把它写成矩阵形式,就是最小化 yᵀMy,其中 M = (I − W)ᵀ(I − W)。
如果只最小化这个,答案是「所有 y 都等于 0」——太平凡了。
所以要加约束:去掉平移(中心化为 0)、固定尺度(协方差为单位阵)。 加了这两个约束之后,
答案就是M 的最小的几个特征向量。
最小的那个特征向量是常数向量(全 1),它对应「所有点重合」的解,要丢掉。
取第 2 到第 d+1 个特征向量,就是 d 维的嵌入。 代码里 evec[:, 1:3] 就是这个意思。
这一步还有个隐含后果:M 的特征值大小反映了「这个方向上的局部关系有多难满足」。 如果第 4、
第 5 个特征值也很小,
说明数据其实还有更多维——这是判断「该降到几维」的一个天然指标,比 PCA 的解释率更贴近局部结构。
四、实测:赢了,但赢得不多
Demo 用一张标准瑞士卷(300 个点,卷 1.5 圈)。 评价指标是邻居保持率:在原始三维空间里最近的 10 个点,降到二维之后还有几个仍在最近的 10 个里。 结果:PCA 0.5050,LLE 0.5833。 涨了,但只涨了 8 个点。 这个数字可能比你想的低——很多教材上的 LLE 示意图漂亮得多。 为什么涨得不多?两个原因。 一是这份数据卷得不够狠,1.5 圈的螺旋在三维里投影后,很多局部关系本来就保住了。 二是 LLE 只保「权重」,不保「距离」。 它允许整体拉伸、弯折、扭曲,只要局部权重不变就算达标。 所以它摊开卷的同时,也会把别处拉歪。 最能说明问题的是那张对照表: 在一张本来就不弯的平面上,PCA 是 1.0000,LLE 只有 0.9167。 数据不需要「掰直」的时候, LLE 纯属给自己添乱。它是专用工具,不是通用升级版。
五、邻居数:唯一的旋钮,而且很难拧
LLE 只有一个超参数:k(邻居个数)。但这个参数特别敏感。 k 太小,邻居图会断成一截一截(某些点找不到足够的邻居形成连通区域),摊平之后带子会撕裂成几片。 k 太大,跨层的点也被连上了——本来隔着两圈的点成了邻居,卷起来的结构又被抹平。 Demo 里把 k 从 4 试到 12,邻居保持率是 0.5297、0.4807、0.5463、0.5833、0.5130、0.5300。 上上下下地晃,没有「越大越好」或「越小越好」的规律。 这个特性让 LLE 在实战里不太好用:调参成本高,而且调完也不知道是不是最优。 这也是它在可视化领域被 t-SNE 和 UMAP 挤到边缘的原因。 它仍然值得学,原因有两个: 一是它的思路(只用局部关系)非常干净,是理解「流形学习」这一整类方法最好的入口; 二是它的第三步那套「变成特征值问题」的推导,在谱聚类、图嵌入里会反复出现。
六、动手 Demo:把卷起来的带子摊平
300 个点铺成一张瑞士卷:一条长带子卷 1.5 圈,再往上拉高。 它的真实维度是 2,但摆在三维里,而且是弯的。 代码分三段:找邻居、解权重、求特征向量。 第三段是整个算法的核心——它把「保持局部权重」这个目标变成了一个特征值问题,不需要迭代。 另外单独做了一组对照:同一套参数跑在一张平面的数据上。
import numpy as np
nb = knn_idx(X, k) # 第一步:找 k 个邻居
W = np.zeros((n, n)) # 第二步:用邻居把自己拼出来
for i in range(n):
Z = X[nb[i]] - X[i]
C = Z @ Z.T
C = C + np.eye(k) * reg * np.trace(C) # 加一点岭,防奇异
w = np.linalg.solve(C, np.ones(k))
W[i, nb[i]] = w / w.sum() # 权重加起来等于 1
# 第三步:到低维去,把权重尽量保住 —— 归结成特征值问题
M = (np.eye(n) - W).T @ (np.eye(n) - W)
ev, evec = np.linalg.eigh(M)
Y = evec[:, 1:3] # 丢掉最小的那个(常数向量)
实跑输出:
--------------------------------------------------------------------
LLE:先把邻居关系记下来,再到低维里照着摆
--------------------------------------------------------------------
造 300 个点铺成一张「瑞士卷」:把一条长带子卷起来,再拉高一点。
**它真正的维度是 2(带子上的横纵坐标),但摆在了 3 维空间里,而且是弯的。**
【一】先看 PCA 会把它压成什么样
方法 降到 2 维后邻居还在的比例
----------------------------------
PCA 0.5050
LLE 0.5833
**这就是「线性」的代价。**
PCA 只会旋转、投影,它把卷起来的两层直接叠在了一起——本来隔着好几米的两层点,
投影后成了邻居。邻居保持率只有 0.51。
【二】LLE 的三步
**第一步,给每个点找 k 个最近的邻居**(这里是 8 个)。
**第二步,用邻居把自己「拼」出来。**
对每个点解一个小最小二乘:找一组权重,使得「邻居按这组权重加权平均」尽量等于它自己,
且权重加起来等于 1。**这一步只用到「谁挨着谁」,完全没用坐标轴**——所以卷起来也不影响。
**第三步,换到低维去,把这组权重尽量保住。**
权重已经固定了,现在反过来求低维坐标:让每个点在 2 维里也满足「我是邻居的加权平均」。
这一步归结成一个特征值问题,取最小的几个非零特征值对应的向量就是答案。
这一步做完,邻居保持率从 0.5050 升到 0.5833。**涨了,但涨得不多。**
【三】为什么只涨这么点:它保的是「权重」,不是「距离」
LLE 的第三步只要求「低维坐标也满足那组权重」,**它完全不管整体形状**。
摊平后的带子随便你拉伸、弯折、扭一下,只要局部权重不变,损失函数就一样大。
所以它会摊开卷着的部分,也会把本来平的地方拉歪。
数据 PCA LLE(k=8)
--------------------------------------------
平面(本来就不弯) 1.0000 0.9167
瑞士卷(弯的) 0.5050 0.5833
**这张表才是实话:数据本来不弯的时候,LLE 反而打不过 PCA。**
它是专门为「弯」准备的工具,用在平的数据上纯属给自己添乱。
【四】它的毛病:全靠「邻居数」这一个旋钮
邻居数 k 摊平后邻居保持率(仍按 10 个算)
----------------------------------------
4 0.5297
5 0.4807
6 0.5463
8 0.5833
10 0.5130
12 0.5300
数值上下晃,**没有「越大越好」或者「越小越好」的规律**,
最好的一档只比 PCA 高一点点。这也是 LLE 在实战里不如 t-SNE 常见的原因:调参成本高,
摊出来的形状还常常莫名其妙。
表里两行:PCA 0.5050、LLE 0.5833。 涨了,但不多。 这个数要如实看——它不是「LLE 把瑞士卷完美摊平了」,而是「局部关系比 PCA 多保住了 8 个点」。 为什么 PCA 也有 0.5050 而不是更差? 因为瑞士卷的螺旋本身有很强的径向结构,投影到主平面上时, 相邻圈之间虽然叠了,但同一圈内部的邻居大部分还是保住了。PCA 不是全错,它错在「跨圈」。 第三段的对照表才是这一节最该记住的东西。平面上 PCA 1.0000、LLE 0.9167。 数据本来不弯时,LLE 反而更差。 原因是那个岭正则会让权重偏离精确解,进而把平面轻微拉歪。 k 表六行数字上下晃,没有规律。0.48 到 0.58 之间跳。 这就是 LLE 的真实体验:调参靠试,试完也不知道对不对。 如果要做严肃的可视化, 直接用 t-SNE 或 UMAP 更省事。
30 秒实验
- 把瑞士卷的圈数从 1.5 改成 3(卷得更紧),PCA 和 LLE 的邻居保持率分别会怎么变?
- 把 k 加到 40(远大于真实邻域),LLE 的结果会退化成什么样?为什么?
- 把
reg从 1e-3 改成 1e-1(正则更重),平面上那个 0.9167 会变高还是变低?
常见误区
- 把 LLE 当成 PCA 的升级版。数据不弯的时候它更差——Demo 里平面上是 0.9167 对 1.0000。 先判断数据到底弯不弯,再决定要不要用它。
- 用 LLE 做「距离」相关的分析。它只保局部权重,整体距离和形状都可以任意扭曲。 要知道两团离多远,回原始空间去算。
- 不调 k 就用默认值。k 从 4 到 12,结果在 0.48 到 0.58 之间乱跳。 邻居数是这个算法唯一的旋钮,也是最不可靠的一环。
自测三问(点开对答案)
1. LLE 第二步求出来的权重,为什么和坐标系无关? —— 因为它只描述「这个点相对它邻居的位置」,是局部几何关系。 整份数据旋转、平移、缩放都不会改变这个比例关系。 2. 第三步为什么取「最小的第 2 到第 d+1 个」特征向量? —— 最小的那个特征向量是常数向量,对应「所有点重合」的平凡解,要丢掉。 接下来 d 个就是 d 维嵌入,它们满足权重约束又彼此正交。 3. 为什么 LLE 的邻居数 k 很难选? —— 太小会让邻居图断开、摊平后撕裂;太大又会让跨层的点连上,把卷起来的结构抹平。 中间没有稳定的最优区间,只能试。LLE 的做法是「先算权重再解特征向量」,两步都还挺讲究。 3.8 节的 t-SNE 走另一条路:直接把「邻居」写成概率,然后硬优化。
- ☐ (本节完,下一节 3.8 算法 17:t-SNE)