【发布时间】:2014-08-28 06:44:37
【问题描述】:
我正在 C# 中实现 K-最近邻分类算法,用于训练和测试集,每个样本约有 20,000 个样本和 25 个维度。
在我的实现中只有两个类,分别用“0”和“1”表示。目前,我有以下简单的实现:
// testSamples and trainSamples consists of about 20k vectors each with 25 dimensions
// trainClasses contains 0 or 1 signifying the corresponding class for each sample in trainSamples
static int[] TestKnnCase(IList<double[]> trainSamples, IList<double[]> testSamples, IList<int[]> trainClasses, int K)
{
Console.WriteLine("Performing KNN with K = "+K);
var testResults = new int[testSamples.Count()];
var testNumber = testSamples.Count();
var trainNumber = trainSamples.Count();
// Declaring these here so that I don't have to 'new' them over and over again in the main loop,
// just to save some overhead
var distances = new double[trainNumber][];
for (var i = 0; i < trainNumber; i++)
{
distances[i] = new double[2]; // Will store both distance and index in here
}
// Performing KNN ...
for (var tst = 0; tst < testNumber; tst++)
{
// For every test sample, calculate distance from every training sample
Parallel.For(0, trainNumber, trn =>
{
var dist = GetDistance(testSamples[tst], trainSamples[trn]);
// Storing distance as well as index
distances[trn][0] = dist;
distances[trn][1] = trn;
});
// Sort distances and take top K (?What happens in case of multiple points at the same distance?)
var votingDistances = distances.AsParallel().OrderBy(t => t[0]).Take(K);
// Do a 'majority vote' to classify test sample
var yea = 0.0;
var nay = 0.0;
foreach (var voter in votingDistances)
{
if (trainClasses[(int)voter[1]] == 1)
yea++;
else
nay++;
}
if (yea > nay)
testResults[tst] = 1;
else
testResults[tst] = 0;
}
return testResults;
}
// Calculates and returns square of Euclidean distance between two vectors
static double GetDistance(IList<double> sample1, IList<double> sample2)
{
var distance = 0.0;
// assume sample1 and sample2 are valid i.e. same length
for (var i = 0; i < sample1.Count; i++)
{
var temp = sample1[i] - sample2[i];
distance += temp * temp;
}
return distance;
}
这需要相当长的时间来执行。在我的系统上,大约需要 80 秒才能完成。我该如何优化它,同时确保它也可以扩展到更多的数据样本?如您所见,我尝试使用 PLINQ 和并行 for 循环,这确实有帮助(没有这些,大约需要 120 秒)。我还能做什么?
我读过关于 KD-trees 一般对 KNN 有效,但我读到的每个资料都表示它们对更高维度无效。
我也找到了this stackoverflow discussion,但这似乎已经有 3 年历史了,我希望现在有人知道这个问题的更好解决方案。
我查看了 C# 中的机器学习库,但由于各种原因,我不想从我的 C# 程序中调用 R 或 C 代码,而且我看到的其他一些库并不比我看到的代码更有效书面。现在我只是想弄清楚如何自己编写最优化的代码。
编辑添加 - 我不能使用 PCA 或其他东西减少维度的数量。对于这个特定模型,需要 25 个维度。
【问题讨论】:
-
您的代码目前似乎可以运行,并且您正在寻求改进它。一般来说,这些问题对于本网站来说过于固执己见,但您可能会在CodeReview.SE 找到更好的运气。记得阅读their requirements,因为他们比这个网站更严格。
-
我不知道,谢谢@gunr2171,我也去那里试试。但是我仍然认为这对于这个网站来说也是一个有效的问题,因为我希望就可能使用不同的数据结构(如 KD-trees)来解决这个问题进行讨论,就像在我链接的 stackoverflow 帖子中一样。跨度>
-
programmers.stackexchange.com 可能会更好。寻找替代算法对于 SO 来说是“太宽泛”的边界。查看相关问题 - 有时其他语言的解决方案已经存在。
-
也会尝试@AlexeiLevenkov,谢谢。我仍在寻找关于此的最新讨论。
-
我目前正在开发一个 C# 模块,以优化高维问题(10 到 1000 维)中的 K-最近邻搜索。我使用希尔伯特曲线取得了巨大的成功。对于 K=50 个邻居、200 个维度、10,000 个点,我的线性扫描速度提高了 40 倍。将 n-D 点映射到 1-D Hilbert 索引,执行二进制搜索,然后使用距离函数对较小的列表进行排序。请参阅这篇文章:J. Shepherd、X. Zhu 和 N. Megiddo。 “一种用于多维最近邻搜索的快速索引方法”。
标签: c# optimization classification knn