正在加载中...

展开本页目录
算法教程SPE-随机邻近嵌入

SPE-随机邻近嵌入

No.132 · 在线教程

随机邻近嵌入(Stochastic Proximity Embedding, SPE)是一类距离保持型降维方法。它的基本思想不是一次性求解完整矩阵分解,而是在迭代过程中反复随机抽取样本点对,根据低维距离与高维目标距离之间的偏差,对嵌入坐标做局部修正,逐步逼近原空间的距离结构。

SPE-随机邻近嵌入

1. 方法概述

随机邻近嵌入(Stochastic Proximity Embedding, SPE)是一类距离保持型降维方法。它的基本思想不是一次性求解完整矩阵分解,而是在迭代过程中反复随机抽取样本点对,根据低维距离与高维目标距离之间的偏差,对嵌入坐标做局部修正,逐步逼近原空间的距离结构。

本项目中的 SPE-随机邻近嵌入 模块并不是调用现成的 sklearn SPE 接口,而是自行实现了随机点对更新流程。根据当前代码,它具有以下特点:

  1. 固定要求第 1 列为样本 ID,后续列为数值特征;
  2. 支持 nonestandardminmax 三种特征预处理方式;
  3. 支持 randompca 两种初始化;
  4. 每轮随机抽取一批点对进行更新;
  5. 学习率按迭代次数线性衰减,并设置最小下限;
  6. 输出低维嵌入结果、stress_msestress_mae 与二维散点图。

设共有 \(n\) 个样本、\(p\) 个被选中的特征,记原始特征矩阵为

$$ X=(x_{ij})_{n\times p},\quad i=1,2,\ldots,n,\ j=1,2,\ldots,p \tag{1} $$

程序默认第 1 列是样本名称列,不参与距离计算。

2. 数据要求与预处理

2.1 数据约束

根据 upload_widget.pyindicators_widget.pydata_validator.pycore/spe_calculator.py 的实现,模块对输入数据的真实要求如下:

  1. 数据不能为空;
  2. 至少包含两列,即第 1 列对象名称和至少 1 列特征;
  3. 第 1 列对象名称不能重复;
  4. 第 2 列及以后必须是数值型列;
  5. 特征列不能含空值;
  6. 特征列不能是常数列;
  7. 至少选择 1 个特征列;
  8. 真正开始计算时,样本量还必须满足 \(n\ge 2\)。

当前模块没有单独的 label_col 选择逻辑,首列就是固定的样本标识列。

2.2 Z-score 标准化

normalization="standard" 时,代码调用 StandardScaler() 执行

$$ z_{ij}=\frac{x_{ij}-\mu_j}{\sigma_j} \tag{2} $$

2.3 Min-Max 归一化

normalization="minmax" 时,代码调用 MinMaxScaler() 执行

$$ z_{ij}=\frac{x_{ij}-x_j^{\min}}{x_j^{\max}-x_j^{\min}} \tag{3} $$

normalization="none",则直接令

$$ z_{ij}=x_{ij} \tag{4} $$

记用于构造目标距离矩阵的预处理后特征矩阵为

$$ Z=(z_{ij})_{n\times p} \tag{5} $$

2.4 符号说明

符号 含义
\(n\) 样本数量
\(p\) 特征维数
\(q\) 目标嵌入维数 n_components
\(z_i\) 第 \(i\) 个样本的预处理后特征向量
\(y_i\) 第 \(i\) 个样本的低维嵌入坐标
\(\delta_{ij}\) 原空间目标距离
\(d_{ij}\) 当前低维距离
\(T\) 最大迭代次数
\(s\) 每轮采样点对数 sample_size
\(\eta_t\) 第 \(t\) 次迭代学习率

3. 核心数学模型

3.1 目标距离矩阵

代码先基于预处理后的矩阵 \(Z\) 计算欧氏距离矩阵:

$$ \delta_{ij}=\|z_i-z_j\|_2 \tag{6} $$

这一步通过 pairwise_distances(X, metric="euclidean") 完成,因此后续 SPE 更新想要逼近的是真正经过预处理后的高维距离关系。

3.2 低维嵌入表示

设低维嵌入维数为 \(q\),则每个样本对应一个坐标向量

$$ y_i\in\mathbb{R}^{q},\quad i=1,2,\ldots,n \tag{7} $$

对任意一对样本 \((i,j)\),当前低维距离为

$$ d_{ij}=\|y_i-y_j\|_2 \tag{8} $$

3.3 线性衰减学习率

在第 \(t\) 次迭代时,代码把初始学习率 learning_rate 按线性方式衰减为

$$ \eta_t=\eta_0\left(1-\frac{t}{T}\right) \tag{9} $$

若该值不再为正,则代码会强制设为

$$ \eta_t\leftarrow 10^{-6} \tag{10} $$

3.4 随机点对更新

每轮迭代中,程序随机生成一批样本索引对 \((i,j)\),并剔除掉 \(i=j\) 的情况。对保留下来的点对,先计算误差

$$ e_{ij}=d_{ij}-\delta_{ij} \tag{11} $$

再构造更新系数

$$ \alpha_{ij}=\eta_t\frac{e_{ij}}{d_{ij}} \tag{12} $$

并按如下方式同步修正两个端点的低维坐标:

$$ y_i\leftarrow y_i-\alpha_{ij}(y_i-y_j),\qquad y_j\leftarrow y_j+\alpha_{ij}(y_i-y_j) \tag{13} $$

这就是当前实现中的 SPE 核心更新机制。

需要注意,代码注释写的是“无重复组合”,但实际实现只是分别随机生成 idx_iidx_j 并过滤 i==j,并没有真正去重,因此同一轮内点对仍可能重复出现。

4. 初始化与参数口径

4.1 random 初始化

init="random" 时,代码用

  1. rng.normal(scale=0.01, size=(n_samples, n_components))

生成初始低维坐标,因此初始嵌入是一个小方差随机正态云。

4.2 pca 初始化

init="pca"

$$ q\le p \tag{14} $$

时,程序会尝试调用 PCA(n_components=q).fit_transform(X) 作为初始坐标。

但需要特别注意两个代码细节:

  1. PCA 计算失败,会自动回退到随机正态初始化;
  2. 若 \(q>p\),代码也不会报错,而是直接跳过 PCA 初始化,退回到随机初始化。

4.3 sample_size 的实际使用

代码会把每轮采样点对数裁剪为

$$ s=\min\!\left(\texttt{sample\_size},\ \frac{n(n-1)}{2}\right) \tag{15} $$

但由于后续是随机生成索引并过滤 \(i=j\),所以最终真正参与更新的点对数量可能少于这个值。

5. 评价指标

5.1 stress_mse

拟合完成后,代码会重新计算低维距离矩阵 \(D=(d_{ij})\),并与目标距离矩阵 \(\Delta=(\delta_{ij})\) 比较。均方误差定义为

$$ \mathrm{stress\_mse}=\frac{1}{n^2}\sum_{i=1}^{n}\sum_{j=1}^{n}(d_{ij}-\delta_{ij})^2 \tag{16} $$

5.2 stress_mae

平均绝对误差定义为

$$ \mathrm{stress\_mae}=\frac{1}{n^2}\sum_{i=1}^{n}\sum_{j=1}^{n}|d_{ij}-\delta_{ij}| \tag{17} $$

这里的计算直接基于完整距离矩阵,因此对角线上的 0 也包含在平均中。

5.3 统计表字段

当前 Stats 工作表中固定包含:

  1. stress_mse
  2. stress_mae
  3. max_iter
  4. sample_size
  5. learning_rate
  6. n_components

6. 代码实现细节

6.1 结果表的口径

导出的 Embedded 工作表会保留首列样本 ID,并输出

  1. Dim1
  2. Dim2
  3. ...
  4. Dimq

其中 \(q=\texttt{n\_components}\)。

6.2 图表生成条件

当前散点图不是任何情况下都会生成。代码要求

  1. embedded.shape[1] >= 3

才会保存 scatter.png。由于 embedded 的第一列是样本 ID,这实际上等价于:

  1. 嵌入维度至少为 2;
  2. 只有 Dim1Dim2 会被画出来。

n_components=1,则不会生成散点图。

6.3 Features 工作表不是预处理后的数据

这是当前模块一个很容易写错的地方。导出的 Features 工作表保存的是

  1. self.features

也就是用户选中的原始特征值,而不是标准化或 Min-Max 变换后的矩阵。真正参与目标距离计算的预处理矩阵只存在于内存中,没有单独导出为 Excel 工作表。

7. 算法流程

结合当前项目代码,SPE-随机邻近嵌入 的实际执行流程如下:

  1. 读取 .xlsx.xls.csv 文件,并校验首列样本 ID 与后续特征列。
  2. 在“特征选择”页勾选参与嵌入的特征列。
  3. 在“方法与参数”页设置 n_componentsmax_itersample_sizelearning_rateinitnormalizationrandom_state
  4. 对选中特征按 nonestandardminmax 进行预处理。
  5. 计算预处理后高维样本之间的欧氏距离矩阵,作为 SPE 的目标距离。
  6. randompca 方式初始化低维坐标。
  7. 在每一轮中随机抽取样本点对,按当前距离误差更新两端点坐标,并使学习率线性衰减。
  8. 迭代结束后计算 stress_msestress_mae
  9. 当嵌入维度不少于 2 时,生成 scatter.png
  10. 导出 spe_results_<时间戳>.xlsx,并在同级目录下生成 spe_plots_<时间戳>/ 图表目录。

8. 关键参数说明

8.1 n_components

目标嵌入维度,界面范围为 150,默认值为 2。当取 2 时最方便直接查看散点图结果。

8.2 max_iter

最大迭代次数,默认值为 300。该值越大,通常越有机会让低维距离逐步逼近目标距离,但计算时间也会增加。

8.3 sample_size

每轮更新时抽取的样本点对数量。该值越大,单轮更新覆盖的距离关系越多,但每轮计算量也更大。

8.4 learning_rate

初始学习率,必须大于 0。代码会按线性规则逐步衰减,并设置下限 1e-6

8.5 init

当前支持:

  1. random
  2. pca

其中 pca 更适合把初始位置放在一个相对稳定的低维结构上,但并不保证一定成功,失败时会自动回退到随机初始化。

8.6 normalization

当前支持:

  1. none
  2. standard
  3. minmax

由于目标距离矩阵直接基于预处理后的特征计算,不同预处理方式会显著改变嵌入结果。

9. 输出结果与导出说明

9.1 Excel 工作表

根据 save_to_excel() 的实现,导出的 Excel 文件包含:

  1. RawData
  2. Features
  3. Embedded
  4. Stats
  5. Parameters
  6. Charts

其中:

  1. RawData 保存原始完整数据;
  2. Features 保存选中的原始特征列;
  3. Embedded 保存低维嵌入结果;
  4. Charts 记录图表路径,同时保留中英文字段列名。

9.2 结果文件与图表目录

当前结果目录固定形如:

  1. results/SPE-随机邻近嵌入分析结果_<时间戳>/

其中导出文件命名为:

  1. spe_results_<时间戳>.xlsx

若生成散点图,则图表目录命名为:

  1. spe_plots_<时间戳>/scatter.png

10. 论文写作建议

10.1 方法描述模板

“本文采用随机邻近嵌入(SPE)方法对高维样本进行降维表示。该方法首先计算样本在预处理特征空间中的目标距离矩阵,然后在低维空间中随机抽取样本点对,根据当前低维距离与目标距离的偏差反复修正样本坐标,从而逐步保持原始距离结构。本文使用 stress 均方误差和平均绝对误差评价嵌入质量。”

10.2 结果解释模板

结果部分可写为:Embedded 表中的 Dim1Dim2 等是最终低维坐标。若 stress_msestress_mae 越小,通常说明低维空间更好地保持了原空间距离关系;若使用了 standardminmax,则应在论文中明确说明目标距离是基于预处理后的特征计算的。

10.3 图表题注模板

  • 图 1 SPE 二维嵌入散点图。
  • 表 1 SPE 低维嵌入坐标结果。
  • 表 2 SPE 嵌入误差指标与参数设置。

11. 实现说明与注意事项

  1. 当前模块只支持“首列样本名,后续列为数值特征”的数据结构。
  2. pca 初始化并不总是成功,失败后会自动回退到随机初始化。
  3. sample_size 经过裁剪后仍不代表每轮一定有那么多个不重复点对参与更新。
  4. Features 工作表保存的是原始选中特征,而不是预处理后的特征矩阵。
  5. n_components=1 时不会生成二维散点图,但嵌入结果仍会正常导出。

12. 单篇终审补充

12.1 图题与表题对齐建议

当前 SPE 模块的真实工作簿包含:

  • RawData
  • Features
  • Embedded
  • Stats
  • Parameters
  • Charts

其中 Embedded 是论文中最适合作为核心结果表引用的页;Stats 负责承接 stress_msestress_mae 等嵌入误差指标。不要把 Features 误写成“预处理特征矩阵”,因为当前程序并未把预处理后的矩阵单独导出到 Excel。

图文件保存在结果目录下的 spe_plots_<suffix>/ 子目录,当前真实图名为:

  • scatter.png

Charts 表中的图表名写的是 embedding_scatter。因此论文图题建议写成“SPE 二维嵌入散点图”;正文中可以说明图表索引名为 embedding_scatter,但不需要把文件名 scatter.png 原样写进标题。

12.2 终审说明

这篇文档最需要避免的误写,是把 SPE 的目标距离理解为来自 Features 工作表。当前实现先对特征做可能的标准化/归一化,再基于预处理后的矩阵构造 target_dist,但 Excel 里导出的 Features 仍是原始选中特征。因此“导出表”和“内部优化距离空间”并不是同一口径。

另外,当前 repro 脚本已经使用 repro_inputs/... 相对路径,结果目录中可见如 repro_inputs/spe_随机邻近嵌入_repro_data.csv 的输入引用。这篇在可复现性表述上可以按新框架写。

12.3 全量强化补充

本篇终审补充绑定的真实算法目录为 具体的算法3/聚类与降维/SPE-随机邻近嵌入,本次采用的代表性结果目录为 具体的算法3/聚类与降维/SPE-随机邻近嵌入/results/SPE-随机邻近嵌入分析结果_20260322_043459_enhanced

当前目录中真实存在两份工作簿:

  • spe_export.xlsx
  • spe_repro.xlsx

两者实测工作表一致,均为:

  • RawData
  • Features
  • Embedded
  • Stats
  • Parameters
  • Charts

因此这一轮应把 spe_export.xlsx 视为主结果,把 spe_repro.xlsx 视为 repro 再生产物。两者结构相同,但不能在文档中写成“单个结果文件”。

当前目录中的真实图文件也分成两套:

  • 具体的算法3/聚类与降维/SPE-随机邻近嵌入/results/SPE-随机邻近嵌入分析结果_20260322_043459_enhanced/spe_plots_20260322_043459/scatter.png
  • 具体的算法3/聚类与降维/SPE-随机邻近嵌入/results/SPE-随机邻近嵌入分析结果_20260322_043459_enhanced/spe_plots_20260322_043500/scatter.png

这说明当前磁盘同时保留了主结果散点图与 repro 重跑散点图。它们图义一致,都是嵌入散点图;区别在于来源批次,而不是图类型差异。

复现实物方面,该目录实际包含:

  • 具体的算法3/聚类与降维/SPE-随机邻近嵌入/results/SPE-随机邻近嵌入分析结果_20260322_043459_enhanced/repro_spe_随机邻近嵌入.py
  • 具体的算法3/聚类与降维/SPE-随机邻近嵌入/results/SPE-随机邻近嵌入分析结果_20260322_043459_enhanced/repro_inputs/spe_随机邻近嵌入_repro_data.csv

脚本中明确写成 INPUT_FILE = 'repro_inputs/spe_随机邻近嵌入_repro_data.csv'OUTPUT_DIR = '.'OUTPUT_FILE = 'spe_repro.xlsx'。因此这一轮的真实可复现口径是“结果目录内部输入副本 + 同目录 repro 结果工作簿与图目录”,而不是重新依赖外部 uploads/ 文件。

11. 软件实现核查补充(2026-07)

  • 当前主结果目录应写作 具体的算法3/聚类与降维/SPE-随机邻近嵌入/results/SPE-随机邻近嵌入分析结果_20260322_043459_enhanced
  • 正文应围绕 RawDataProcessedDataEmbeddingStatsParametersCharts 来写。
  • 图证应对应两轮 scatter.png,并把主结果与 repro 再生产物区分开。
  • 复现脚本应按 repro_spe_随机邻近嵌入.py + repro_inputs/spe_随机邻近嵌入_repro_data.csv 的口径说明。