并行与分布式系统中的并行K-最近邻(KNN)分类:基于距离矩阵计算的并行化方法
题目描述
在机器学习和数据挖掘中,K-最近邻(KNN)是一种基本的分类与回归算法。其核心思想是:对于一个待分类的测试样本,在训练集中找到与其距离最近的K个邻居,然后根据这K个邻居的类别标签(多数投票)来预测测试样本的类别。在大型数据集上,KNN的计算开销主要来自距离计算(例如欧氏距离),因为需要计算测试样本与所有训练样本之间的距离。本题目要求设计一个并行化的KNN算法,该方法特别关注距离矩阵的高效并行计算,通过将训练数据和测试数据划分到多个处理器(或计算节点)上,并行地计算所有测试样本与所有训练样本之间的距离矩阵,然后并行地找出每个测试样本的K个最近邻,最终实现分类加速。
解题过程循序渐进讲解
步骤1:问题分析与串行KNN回顾
串行KNN算法通常包含以下步骤:
- 输入:训练集 \(X_{\text{train}}\)(\(n\)个样本,每个样本为\(d\)维特征向量),测试集 \(X_{\text{test}}\)(\(m\)个样本),参数K(邻居数)。
- 对每个测试样本 \(x_{\text{test}}^{(i)}\):
a. 计算它与所有训练样本 \(x_{\text{train}}^{(j)}\) 之间的距离(例如欧氏距离:\(\| x_{\text{test}}^{(i)} - x_{\text{train}}^{(j)} \|_2\))。
b. 从距离列表中选出最小的K个距离,并记录对应的训练样本标签。
c. 通过投票(多数表决)确定测试样本的预测标签。
计算复杂度为 \(O(m \cdot n \cdot d)\),当 \(m, n, d\) 都很大时,计算非常耗时。
我们的并行化目标:将距离矩阵计算和K个最近邻搜索并行化。
步骤2:并行化策略选择
我们采用数据并行策略,将训练集和/或测试集划分到多个处理器上。常用划分方式有:
- 按测试样本划分(行划分):每个处理器分配一部分测试样本,但需要访问全部训练数据。适合训练集可放入每个处理器内存的情况,通信少,但训练集需广播或复制。
- 按训练样本划分(列划分):每个处理器分配一部分训练样本,但需要访问全部测试数据。类似地,测试集需广播。
- 二维块划分:将距离矩阵视为一个 \(m \times n\) 的矩阵,按二维网格划分到处理器网格上。每个处理器计算局部距离块,然后通过归约收集全局结果。
这里我们选择按测试样本划分,因为它更简单直观,且适合许多实际场景(训练集可复制)。假设有 \(p\) 个处理器。
步骤3:数据划分与分布
- 将测试集 \(X_{\text{test}}\) 均匀划分为 \(p\) 块,每个处理器 \(P_i\) 获得 \(m/p\) 个测试样本(假设 \(m\) 可被 \(p\) 整除)。
- 将整个训练集 \(X_{\text{train}}\) 广播到所有处理器(每个处理器都有一份完整的训练集副本)。
- 这样,每个处理器可以独立计算自己所分配的测试样本与所有训练样本之间的距离,无需在计算过程中通信。
步骤4:并行距离矩阵计算
在每个处理器 \(P_i\) 上:
- 对于分配到的每个测试样本 \(x_{\text{test}}^{(k)}\):
- 计算该测试样本与每个训练样本 \(x_{\text{train}}^{(j)}\) 的欧氏距离(或其它距离度量)。
- 得到距离向量 \(D_k = [d_{k1}, d_{k2}, ..., d_{kn}]\),其中 \(d_{kj} = \text{distance}(x_{\text{test}}^{(k)}, x_{\text{train}}^{(j)})\)。
- 对每个距离向量 \(D_k\),找出最小的K个距离对应的索引(即最近的K个训练样本的序号)。
- 可以使用选择算法(例如基于快速选择的Partition)或维护一个大小为K的最大堆。
- 最大堆方法:遍历距离向量,将距离插入最大堆(堆大小为K),当堆满时,若新距离小于堆顶,则替换堆顶并调整堆。遍历完成后,堆中即为K个最小距离。
- 根据这K个索引,从训练标签中取出对应标签,进行多数投票,得到该测试样本的预测标签。
至此,每个处理器已独立完成所分配测试样本的分类。所有处理器并行执行,总计算量从 \(O(mnd)\) 降至 \(O((m/p)nd)\),理想加速比为 \(p\)。
步骤5:结果收集(可选)
如果需要在单个处理器(如主节点)上收集所有预测结果,则每个处理器将本地预测标签发送给主处理器。这是一个简单的聚集(Gather)操作,通信量为 \(m\) 个标签,通常很小。
步骤6:处理边界情况与优化
- 负载均衡:测试集划分应尽量均匀,若 \(m\) 不能被 \(p\) 整除,可让部分处理器多分一个样本。
- 距离计算优化:
- 使用矩阵运算库(如BLAS)批量计算距离,避免逐样本循环。
- 对于欧氏距离,可利用展开公式 \(\|a-b\|^2 = \|a\|^2 + \|b\|^2 - 2a\cdot b\),预先计算训练样本的范数 \(\|x_{\text{train}}^{(j)}\|^2\),减少计算量。
- K个最近邻搜索优化:
- 当 \(n\) 很大时,使用最大堆(大小K)比全排序更快,复杂度为 \(O(n \log K)\)。
- 也可使用近似最近邻搜索(如基于局部敏感哈希LSH)进一步加速,但会损失精度。
- 内存考虑:训练集需在每个处理器存一份副本,若训练集很大,可能内存不足。此时可采用按训练样本划分或二维划分,但会增加通信。
- 二维划分示例:将测试集分为 \(p_r\) 块,训练集分为 \(p_c\) 块,分配到 \(p_r \times p_c\) 处理器网格。每个处理器计算局部距离子矩阵,然后通过按行归约(对测试样本维度)找出每个测试样本的K个最近邻。通信量较大,但可处理超大规模数据。
步骤7:算法总结与复杂度
- 时间复杂度:并行距离计算 \(O((m/p) n d)\),并行K近邻选择 \(O((m/p) n \log K)\)。
- 空间复杂度:每个处理器存储整个训练集 \(O(nd)\) 和局部测试集 \(O((m/p)d)\),以及距离向量 \(O(n)\)(可流式计算避免存储全部距离)。
- 通信复杂度:仅结果收集时有一次Gather,通信量 \(O(m)\);若使用二维划分,则需归约操作,通信量增加。
该方法通过并行化距离矩阵计算,有效加速了KNN中最耗时的部分,适用于测试集很大、训练集可复制的场景。