我是stackoverflow的新手,我很高兴发布我的第一个问题。我实际上是编程世界的新手,但我还是有一些基本知识,我实际上是用Python来解决分类问题。我有一大组数据(在我的实际问题中n = 60326),每个向量大约有416维。我需要计算这些不同向量之间的曼哈顿距离,以便根据相似性对我的数据集进行分类(采用随机参考向量并将最接近的向量合并到0到1之间的距离范围)我已经熟悉了Kmeans和基本的ML聚类算法...我的实际问题是我需要使用GPU(CUDA)来加速时间,以便在开始分类之前初始计算距离矩阵,其大小为n²(60326 x 60326,我们可以将其减小到n²/ 2因为它是一个对称矩阵)所以我的实际问题是如何在这种情况下实现CUDA,我已经使用ANACONDA安装了CUDA包。
我从并行CPU的处理开始,它提供了内存错误
import pandas as pd
import time
import time
import numpy as np
import random
from scipy.spatial import distance
from sklearn.metrics.pairwise import pairwise_distances
signatures=pd.read_csv("C:\\Users\\YMH1\\Documents\\Reduce Java code\\BOBST.txt", sep=' ',header=None,usecols=[*range(2,417)])
PartName = pd.read_csv('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("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时定义曼哈顿标准,在曼哈顿城堡内进行哪些修改 非常感谢你
答案 0 :(得分:0)
我的问题是如何在我们使用CUDA时定义曼哈顿标准,在曼哈顿城堡内进行哪些修改
根据documentation,manhattan
距离度量是两个向量之间元素差异绝对值的总和。
您可能遇到的一个问题是内存空间问题。如果我们假设距离度量的输出(即矩阵元素)表示为普通的蟒蛇数量,则这可能占用8个字节的内存。对于规定的尺寸(60326),这意味着矩阵将占用60326 * 60326 * 8字节,几乎为30GB。即使我们假设您只存储了一半,即使我们假设32位的绝对差值总和,这仍然需要超过7GB的存储空间。
当我尝试使用sckit-learn方法运行这样的测试时,即使在具有128GB系统内存的计算机上也遇到了麻烦:
# cat t5.py
import numpy as np
import random
from scipy.spatial import distance
from sklearn.metrics.pairwise import pairwise_distances
vector_length = 416
num_signatures = 60000
sig_pattern = np.array([0,1,2,3], dtype=np.float32).reshape(4,1)
signatures = np.tile(sig_pattern,(num_signatures, vector_length//sig_pattern.shape[1]))
E = pairwise_distances(signatures, metric = 'manhattan', n_jobs = -1)
print(E[:8,:8])
# time python t5.py
Traceback (most recent call last):
File "t5.py", line 17, in <module>
E = pairwise_distances(signatures, metric = 'manhattan', n_jobs = -1)
File "/root/anaconda2/lib/python2.7/site-packages/sklearn/metrics/pairwise.py", line 1247, in pairwise_distances
return _parallel_pairwise(X, Y, func, n_jobs, **kwds)
File "/root/anaconda2/lib/python2.7/site-packages/sklearn/metrics/pairwise.py", line 1096, in _parallel_pairwise
for s in gen_even_slices(Y.shape[0], n_jobs))
File "/root/anaconda2/lib/python2.7/site-packages/sklearn/externals/joblib/parallel.py", line 789, in __call__
self.retrieve()
File "/root/anaconda2/lib/python2.7/site-packages/sklearn/externals/joblib/parallel.py", line 699, in retrieve
self._output.extend(job.get(timeout=self.timeout))
File "/root/anaconda2/lib/python2.7/multiprocessing/pool.py", line 572, in get
raise self._value
multiprocessing.pool.MaybeEncodingError: Error sending result:
'[array([[ 0., 416., 832., ..., 416., 832., 1248.],
[ 416., 0., 416., ..., 0., 416., 832.],
[ 832., 416., 0., ..., 416., 0., 416.],
...,
[ 416., 0., 416., ..., 0., 416., 832.],
[ 832., 416., 0., ..., 416., 0., 416.],
[1248., 832., 416., ..., 832., 416., 0.]])]'. Reason: 'OverflowError('cannot serialize a string larger than 2 GiB',)'
real 31m47.361s
user 405m28.155s
sys 8m19.851s
在那种情况下,在我的机器上计算大约需要30分钟。输出测试矩阵看起来大致正确,但python引发了错误,因为某些中间表示大于2GB。
内存大小问题也是在numba / cuda实现中需要考虑的重要问题之一。
然而,要执行的操作相对简单。根据我的测试,它可以比numpy / scikit-learn方法快得多。
这是一个有效的例子:
# cat t4.py
import numpy as np
import numba as nb
from numba import cuda,float32
vector_length = 416
num_vecs_per_block = 8
sm_size = vector_length*num_vecs_per_block
#num_sig must be divisible by num_vecs_per_block
@cuda.jit('void(float32[:,:], float32[:,:], int32, int32)')
def manhattan(signatures, distances, num_sig, vec_len):
sm = cuda.shared.array(sm_size, float32)
temp = cuda.local.array(num_vecs_per_block, dtype = float32)
bid = cuda.blockIdx.x
tid = cuda.threadIdx.x
bdim = cuda.blockDim.x
# load shared memory with vectors for comparison
if tid < num_vecs_per_block:
for i in range(vec_len):
sm[i*num_vecs_per_block+tid] = signatures[i, bid*num_vecs_per_block+tid];
cuda.syncthreads()
#block-stride loop through the vector array
# the addition below to tid results in only elements above the diagonal being computed
# since the output matrix is symmetric
svec = tid +(bid*num_vecs_per_block)
while svec < num_sig:
for i in range(num_vecs_per_block):
temp[i] = 0
for i in range(vec_len):
src = signatures[i,svec]
for j in range(num_vecs_per_block):
#elementwise difference
sdist = src - sm[i*num_vecs_per_block + j]
#absolute value
if sdist < 0:
sdist *= -1
#sum
temp[j] += sdist
for i in range(num_vecs_per_block):
distances[bid*num_vecs_per_block+i, svec] = temp[i]
svec += bdim
num_signatures = 60000
sig_pattern = np.array([0,1,2,3], dtype=np.float32)
signatures = np.tile(sig_pattern,(num_signatures//sig_pattern.shape[0], vector_length))
distances = np.zeros((num_signatures, num_signatures), dtype=np.float32)
threadsperblock = 1024
blockspergrid = num_signatures//num_vecs_per_block
d_signatures = cuda.to_device(signatures)
d_distances = cuda.device_array_like(distances)
manhattan[blockspergrid, threadsperblock](d_signatures, d_distances, num_signatures, vector_length)
d_distances.copy_to_host(distances)
print(distances[:16,:16])
# time python t4.py
[[ 0. 416. 832. 1248. 0. 416. 832. 1248. 0. 416. 832. 1248. 0. 416. 832. 1248.]
[ 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416. 832.]
[ 832. 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416.]
[1248. 832. 416. 0. 1248. 832. 416. 0. 1248. 832. 416. 0. 1248. 832. 416. 0.]
[ 0. 416. 832. 1248. 0. 416. 832. 1248. 0. 416. 832. 1248. 0. 416. 832. 1248.]
[ 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416. 832.]
[ 832. 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416. 832. 416. 0. 416.]
[1248. 832. 416. 0. 1248. 832. 416. 0. 1248. 832. 416. 0. 1248. 832. 416. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 416. 832. 1248. 0. 416. 832. 1248.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 416. 0. 416. 832. 416. 0. 416. 832.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 832. 416. 0. 416. 832. 416. 0. 416.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 1248. 832. 416. 0. 1248. 832. 416. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 416. 832. 1248. 0. 416. 832. 1248.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 416. 0. 416. 832. 416. 0. 416. 832.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 832. 416. 0. 416. 832. 416. 0. 416.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 1248. 832. 416. 0. 1248. 832. 416. 0.]]
real 0m11.580s
user 0m5.579s
sys 0m5.922s
#
在这种情况下,我将signatures
向量限制为60000,而不是60320,因为较大的数字导致输出矩阵太大而无法放入我的16GB Tesla P100 GPU的可用内存中。 如果您的GPU内存少于16GB,则此代码无法按原样运行。您需要将问题缩小到较少数量的签名向量。将矢量分成两组,并在两组之间进行距离计算,填写整个矩阵,应该相对简单。
然而,我的测试机器上的numba代码在大约11秒内运行,并且似乎为我打印的非常小的16x16输出矩阵产生了相同的结果。
如果我对这段代码进行了剖析,我实际上发现GPU内核在大约3秒内运行,从GPU到CPU的巨大输出矩阵的数据传输时间大约需要6秒,剩下的大约需要2秒钟。 python开销。
实际的GPU算法是面向块的。每个块负责将8个向量与整个向量数组进行比较。每个块首先将8个向量加载到共享内存中,然后遍历整个向量数组,计算与这8个向量中的每一个相对应的曼哈顿距离。该块使用块跨步循环来遍历整个阵列。在每次循环迭代结束时,对应于当前比较的8曼哈顿距离被写入输出数组。
此外,代码有一个细微的变化,因此只计算对角线以上的矩阵输出值。由于计算是按块完成的,因此不计算在对角线上方没有元素的块。这会将处理时间缩短一半左右,但完整输出矩阵仍在内存中。出于这个原因,我上面的16x16输出的左下象限都是零,因为8x8象限完全是&#34;低于&#34;由于问题中已经指出的相似性情况,因此跳过了对角线的处理。