使用openmp和分段错误的并行K最近邻居
问题描述:
我正在尝试做“dat”中数据点的k最近邻居(KNN),所以我的第一步是构建每个点和所有其他点之间的距离矩阵点,然后为每个点找到K最近的邻居。下面的代码在没有openmp的情况下可以很好地工作。但是,当我使用openmp时,会出现分段错误。我认为这个错误与我如何更新包含k个最小元素索引的最小值有关。我认为可能是我需要使用矢量最小的“缩小”,但我不知道如何使用它,或者它是对还是错,所以对如何克服这种分割错误的帮助真的很感激。使用openmp和分段错误的并行K最近邻居
vector<vector<double> > dist(dat.size(), vector<double>(dat.size()));
size_t p,j;
ptrdiff_t i;
vector<double> sumKnn;
vector<vector<int > > smallest(dat.size(), vector<int>(k));
#pragma omp parallel for private(p,j,i) default(shared)
for(p=0;p<dat.size();++p)
{
int mycont=0;
for (j = p+1; j < dat.size(); ++j)
{
double ecl = 0.0;
for (i = 0; i < c; ++i)
{
ecl += (dat[p][i] - dat[j][i]) * (dat[p][i] - dat[j][i]);
}
ecl = sqrt(ecl);
dist[p][j] = ecl;
dist[j][p] = ecl;
int index=0;
if(mycont<k && j!=p)
{
smallest[p][j-p-1]=j;
mycont++;
}
else
{
double max=0.0;
int index=0;
for(int i=0;i<smallest[p].size();i++)
{
if(max < dist[p][smallest[p][i]])
{
index=i;
max=dist[p][smallest[p][i]];
}
}
if(max>dist[p][j])
{
smallest[p].erase(smallest[p].begin()+index);
smallest[p].push_back(j);
}
}
}
double sum=0.0;
for(int r=0;r<k;r++)
sum+= dist[p][smallest[p][r]];
sumKnn.push_back(sum);
}
答
你可以用 “挑剔” 的指令:
#pragma omp critical
{
smallest[p].erase(smallest[p].begin()+index);
smallest[p].push_back(j);
}
和
#pragma omp critical
sumKnn.push_back(sum);
但我同意,更好是使用kd树或K均值的树istead并行化。您可以下载FLANN库http://www.cs.ubc.ca/~mariusm/index.php/FLANN/FLANN。
答
所以我同意@izomorphius是并行算法(其中所有的距离计算)可能不会比使用更快的基于树的算法,特别是对于非常大数目的点是一个加速。
不过,你可以很容易地做到这一点。问题是你不能有多个线程同时处理共享向量上的push_back()和erase()。无论如何坦率地说,载体看起来像是错误的方法来使用这些东西;既然你知道这些东西的大小,只是使用数组可能是一种方式。
无论如何,通过在最小的[] []数组中手动移动东西,而不是使用擦除和回推,并且只需为sumKnn写入静态数组而不是使用push_back(),就可以实现上班。
#include <cmath>
#include <cstdlib>
#include <vector>
using namespace std;
int main(int argc, char **argv) {
const int size = 25; // number of pts
const int k = 2; // number of neighbours
const int c = 2; // number of dimensions
vector<vector<double> > dat(size, vector<double>(c));
for (int i=0; i<size; i++) {
vector<double> pt(c);
for (int d=0; d<c; d++) {
pt.push_back(rand()*1./RAND_MAX);
}
dat.push_back(pt);
}
vector<vector<double> > dist(size, vector<double>(size));
double sumKnn[size];
vector<vector<int > > smallest(size, vector<int>(k));
#pragma omp parallel for default(none) shared(dat, dist, smallest, sumKnn)
for(size_t p=0;p<size;++p)
{
int mycont=0;
for (size_t j = p+1; j < size; ++j)
{
double ecl = 0.0;
for (ptrdiff_t i = 0; i < c; ++i)
{
ecl += (dat[p][i] - dat[j][i]) * (dat[p][i] - dat[j][i]);
}
ecl = sqrt(ecl);
dist[p][j] = ecl;
dist[j][p] = ecl;
int index=0;
if(mycont<k && j!=p)
{
smallest[p][j-p-1]=j;
mycont++;
}
else
{
double max=0.0;
int index=0;
for(int i=0;i<k;i++)
{
if(max < dist[p][smallest[p][i]])
{
index=i;
max=dist[p][smallest[p][i]];
}
}
if(max>dist[p][j])
{
for (int ii=index; ii<k-1; ii++)
smallest[p][ii] = smallest[p][ii+1];
smallest[p][k-1] = j;
}
}
}
double sum=0.0;
for(int r=0;r<k;r++)
sum+= dist[p][smallest[p][r]];
sumKnn[p] = sum;
}
return 0;
}
“k最近邻KNN”和普通KNN有什么区别? – 2012-03-03 14:03:18
这是相同的,只是我想使它平行 – DOSMarter 2012-03-03 14:21:34
你有没有考虑过使用kd-tree而不是对算法进行parellizing? – 2012-03-03 16:34:43