You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CUDA中定义曼哈顿范数以实现大矩阵GPU并行距离计算?

如何用Numba CUDA实现曼哈顿距离矩阵计算?

我是StackOverflow的新用户,很高兴发布我的第一个问题。我刚接触编程领域,但具备一些基础知识,目前正在用Python解决一个分类问题。我拥有一个大型数据集(实际场景中n=60326),每个向量约有416个维度。我需要计算这些向量之间的曼哈顿距离,以便基于相似度对数据集进行分类(选取随机参考向量,合并距离在0到1之间的最近向量)。我已了解Kmeans及基础机器学习聚类算法……

当前的问题是,我需要使用GPU(CUDA)来加速分类前的距离矩阵计算,该矩阵大小为n²(60326×60326,因是对称矩阵可简化为n²/2)。我已通过Anaconda安装了CUDA包。

我最初尝试CPU并行处理,但出现了内存错误:

import pandas as pd 
import time 
import numpy as np 
import random 
from scipy.spatial import distance 
from sklearn.metrics.pairwise import pairwise_distances 

signatures = pd.read_csv(r"C:\Users\YMH1\Documents\Reduce Java code\BOBST.txt", sep=' ', header=None, usecols=[*range(2,417)])
PartName = pd.read_csv(r'C:\Users\YMH1\Documents\Reduce Java code\misumi_new.txt', sep=' ', header=None, usecols=[*range(0,1)])

signatures_vector = np.array(signatures)
PartName_vector = np.array(PartName)

D = pairwise_distances(X = signatures_vector, metric = 'manhattan', n_jobs = -1)
print(D)

现在我尝试用CUDA来提速,编写了如下代码:

from __future__ import division 
from numba import cuda, float32 
import pandas as pd 
import math 

signatures = pd.read_csv(r"C:\Users\YMH1\Documents\Reduce Java code\BOBST.txt", sep=';', header=None, usecols=[*range(2,418)])
signatures_vector = np.array(signatures)

@cuda.jit 
def manhattan(an_array):
    x, y = cuda.grid(2)
    "Here we define the Manhattan distance"
    return an_array 

data = signatures_vector 
threadsperblock = (16, 16)
blockspergrid_x = math.ceil(data.shape[0] / threadsperblock[0])
blockspergrid_y = math.ceil(data.shape[1] / threadsperblock[1])
blockspergrid = (blockspergrid_x, blockspergrid_y)

manhattan[blockspergrid, threadsperblock](data)
print(data)

我的问题是:在使用CUDA时如何定义曼哈顿范数,需要对manhattan函数内部做哪些修改?非常感谢。


你的CUDA代码问题分析与修正方案

首先,你的现有CUDA代码有几个关键问题:

  • 核函数没有接收输出矩阵的参数,也没有实际计算曼哈顿距离,只是返回了输入数组
  • 网格和线程块的维度设置错误:你需要计算的是n×n的距离矩阵,所以应该基于输入向量的数量(data.shape[0])来设置,而不是向量的维度(data.shape[1])
  • 没有处理GPU内存的传输,直接用CPU数组传入核函数会导致效率低下甚至错误

下面是修正后的完整实现,我会一步步解释:

1. 完整的Numba CUDA曼哈顿距离计算代码

from __future__ import division 
from numba import cuda, float32 
import pandas as pd 
import numpy as np 
import math 

# 读取数据(注意用原始字符串避免转义问题)
signatures = pd.read_csv(r"C:\Users\YMH1\Documents\Reduce Java code\BOBST.txt", sep=';', header=None, usecols=[*range(2,418)])
signatures_vector = signatures.values.astype(np.float32)  # 转为float32节省GPU内存
n_samples = signatures_vector.shape[0]
n_features = signatures_vector.shape[1]

# 定义CUDA核函数:计算所有向量对的曼哈顿距离
@cuda.jit
def manhattan_distance_kernel(input_matrix, output_matrix):
    # 获取当前线程的全局索引:i对应第i个向量,j对应第j个向量
    i, j = cuda.grid(2)
    
    # 检查索引是否在有效范围内(避免越界访问)
    if i < input_matrix.shape[0] and j < input_matrix.shape[0]:
        distance = 0.0
        # 遍历所有维度,计算曼哈顿距离:sum(|x_i - x_j|)
        for k in range(input_matrix.shape[1]):
            distance += abs(input_matrix[i, k] - input_matrix[j, k])
        # 将结果写入输出矩阵
        output_matrix[i, j] = distance

# 设置线程块和网格大小(通常选择32x32或16x16的线程块,根据GPU性能调整)
threads_per_block = (16, 16)
blocks_per_grid_x = math.ceil(n_samples / threads_per_block[0])
blocks_per_grid_y = math.ceil(n_samples / threads_per_block[1])
blocks_per_grid = (blocks_per_grid_x, blocks_per_grid_y)

# 分配GPU内存:输入数据传到GPU,输出矩阵在GPU上初始化
d_input = cuda.to_device(signatures_vector)
d_output = cuda.device_array((n_samples, n_samples), dtype=np.float32)

# 启动核函数
manhattan_distance_kernel[blocks_per_grid, threads_per_block](d_input, d_output)

# 将结果从GPU拷贝回CPU(注意:60326x60326的矩阵非常大,约14GB,可能需要考虑分批处理)
output_matrix = d_output.copy_to_host()

# 如果你只需要对称矩阵的上三角/下三角,可以在核函数里只计算i >= j的部分,节省一半计算量

2. 关键修改点说明

  • 核函数参数:新增了output_matrix参数,用于存储计算出的距离矩阵
  • 线程索引:每个线程负责计算一对向量(i,j)的距离,索引对应输出矩阵的位置
  • 曼哈顿距离计算:通过循环遍历所有维度,累加每个维度差值的绝对值
  • 内存管理:使用cuda.to_device将CPU数据传到GPU,cuda.device_array在GPU上创建输出数组,最后用copy_to_host取回结果
  • 越界检查:必须确保线程索引i和j不超过样本数量,避免数组越界错误

3. 针对大型数据集的优化建议

你的数据集有60326个样本,生成的距离矩阵非常大(约14GB的float32数据),可能超出GPU的显存容量,这里有几个优化方向:

  • 利用对称矩阵特性:只计算i >= j(或i <= j)的部分,减少一半的计算量和内存占用,核函数里可以修改为if i <= j,然后拷贝时填充对称部分
  • 分块处理:将数据集分成多个小批次,每次计算一个批次内的距离矩阵,或者计算批次与批次之间的距离,避免一次性占用过多显存
  • 共享内存加速:对于每个线程块,把需要用到的向量片段加载到共享内存中,减少全局内存的访问次数,进一步提升计算速度(适合有CUDA优化经验后尝试)

内容的提问来源于stack exchange,提问作者Mrabah Yassine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:15:20