如何在C#中为大量维度最好地实现K-最近邻?

如何在C#中为大量维度最好地实现K-最近邻?,c#,optimization,classification,knn,C#,Optimization,Classification,Knn,我正在C#中实现K近邻分类算法,用于训练和测试集,每个样本约20000个,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

我正在C#中实现K近邻分类算法,用于训练和测试集,每个样本约20000个,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;
}
//testSamples和trainSamples由大约20k个向量组成,每个向量有25个维度
//trainClasses包含0或1,表示trainSamples中每个样本的对应类
静态int[]TestKnnCase(IList trainSamples、IList testSamples、IList TrainClass、int K)
{
Console.WriteLine(“使用K=“+K”执行KNN);
var testResults=newint[testSamples.Count()];
var testNumber=testSamples.Count();
var trainNumber=trainSamples.Count();
//在这里声明这些,这样我就不必在主循环中一次又一次地“新建”它们,
//只是为了节省一些开销
var距离=新的双[车次][];
对于(变量i=0;i
{
var dist=GetDistance(测试样本[tst],列车样本[trn]);
//存储距离和索引
距离[trn][0]=距离;
距离[trn][1]=trn;
});
//对距离进行排序并取顶K(?如果在同一距离上有多个点,会发生什么情况?)
var votingdistance=distance.AsParallel().OrderBy(t=>t[0]).Take(K);
//对测试样本进行“多数投票”分类
var yea=0.0;
var-nay=0.0;
foreach(以votingDistances为单位的var表决器)
{
if(列车类别[(int)表决器[1]]==1)
是++;
其他的
nay++;
}
如果(是>否)
测试结果[tst]=1;
其他的
测试结果[tst]=0;
}
返回测试结果;
}
//计算并返回两个向量之间欧氏距离的平方
静态双GetDistance(IList sample1、IList sample2)
{
var距离=0.0;
//假设样本1和样本2有效,即长度相同
对于(var i=0;i
这需要相当长的时间来执行。在我的系统上,大约需要80秒才能完成。我如何优化它,同时确保它也可以扩展到更多的数据样本?正如您所看到的,我尝试过使用PLINQ和并行for循环,这确实有帮助(如果没有这些,大约需要120秒)。我还能做什么

我读过关于KD树对于KNN一般来说是有效的,但是我读到的每一个资料都表明它们对于更高维度是无效的

我也发现了这一点,但这似乎是3岁,我希望有人会知道更好的解决方案,这个问题的现在

我看过C#中的机器学习库,但出于各种原因,我不想从我的C#程序中调用R或C代码,我看到的一些其他库的效率并不比我编写的代码高。现在我只是想弄清楚我自己如何才能为这个编写出最优化的代码


编辑为添加-我无法使用PCA或其他方法减少维度的数量。对于这个特定的模型,需要25个维度。

每当您试图提高代码的性能时,第一步就是分析当前的性能,以准确了解它在哪里花费时间。一个好的剖析器对此至关重要。在我以前的工作中,我能够很好地使用这个工具;VisualStudio也有一个。一个好的分析器会准确地告诉您代码在什么地方花费时间,一个方法一个方法,甚至一行一行

话虽如此,在阅读您的实现时,您会想到以下几点:

  • 您正在并行化一些内部循环。你能把外环并行化吗?委托调用(请参阅或)可能会在“Parallel.For”回调中遇到一个小但非零的开销

  • 类似地,通过使用IList接口的数组进行索引也会带来较小的性能损失。您可以考虑将数组参数声明为“GETExistAsHes()”。
  • 与训练数组的大小相比,K有多大?您正在对“距离”数组进行完全排序,并取最上面的K,但如果K远小于数组大小,则使用a/算法可能有意义,例如,当设置的大小超过K时,使用a并替换最小的元素


  • 看起来您的代码目前运行正常,您正在寻求改进。一般来说,这些问题对本网站来说过于固执己见,但你可能会在网站上找到更好的运气。记住要阅读,因为他们比这个网站更严格。我不知道,谢谢@gunr217,我也会去那里试试。然而,我仍然认为这是一个有效的问题,因为我希望得到一个关于可能使用不同的数据结构(如KD树)来解决这个问题的讨论,就像在我链接的stackoverflow帖子中一样。可能更好。寻找替代算法对于SO来说是一条“太宽”的界线。查看相关问题-有时其他语言已经有了解决方案。我也会试试@AlexeiLevenkov,谢谢。我仍在寻找关于这方面的最新讨论。我目前正在研究一个C#模块,以优化高维问题(10到1000维)中的K-最近邻搜索。我在使用希尔伯特曲线方面取得了巨大的成功。对于K=50个邻居,200个维度,10000个点,I