1

我有一个测试程序不能为 Accord.Net K-Means 提供一致的结果。

我附上了一个可在 Visual Studio 2013 中运行的可重现测试程序。

该程序是一个控制台应用程序,要重现您需要参考的结果:

  Accord.MachineLearning
  Accord.Statistics,

来自 Accord.Net 2.15 库。

当我多次运行该程序时,每次都会得到不同的结果。该程序使用经典的 Fisher Iris 数据集。数据集有 150 行,我将数据拆分为 120 行训练数据和 30 行测试数据。

当我运行该程序时,我可能会在 30 个中得到正确分类的 26 个。再次运行它可能会产生 30 次中的 2 次正确。

例如:

 Number correct: 2 out of 30
         FScore: NaN
      Precision: 0
 True Positives: 0
False Positives: 9
 True Negatives: 9
False Negatives: 12
       Accuracy: 0.3
 Standard Error: 0.107268513868515
       Variance: 0.0115065340675597

我想知道我是否正确使用了 Accord.Net。任何帮助将不胜感激。

我的程序是:

using System;
using System.IO;
using System.Net;

using Accord.MachineLearning;
using Accord.Statistics.Analysis;

namespace K_Keans {

  #region K_Means
  public static class K_Means {
    private static KMeans kmeans;

    #region DowloadIrisData
    private static void DowloadIrisData(out double[][] predictors, out int[] targets) {
      using (var fileDownloader = new WebClient()) {
        // http://www.math.uah.edu/stat/data/Fisher.html
        // The dataset gives Ronald Fisher's measurements of type, petal width (PW), petal length (PL),
        // sepal width (SW), and sepal length (SL) for a sample of 150 irises, measured in millimeters. 
        // Type 0 is Setosa; type 1 is Verginica; and type 2 is Versicolor.
        const string webLocation = @"http://www.math.uah.edu/stat/data/Fisher.csv";
        const string fileName = @"c:\Temp\iris.csv";
        fileDownloader.DownloadFile(webLocation, fileName);
        var s = File.ReadAllText(fileName);
        var sarray = s.Split('\n');
        var nrows = sarray.Length - 2;
        var ncols = sarray[0].Split(',').Length;
        predictors = new double[nrows][];
        targets = new int[nrows];
        for (var j=1; j<=nrows; j++) {
          predictors[j-1] = new double[ncols-1];
          var line = sarray[j].Split(',');
          for (var k = 1; k < ncols; k++) {
            targets[j-1] = Convert.ToInt32(line[0]);
            predictors[j-1][k-1] = Convert.ToDouble(line[k]);
          }
        }
      }
    }
    #endregion

    #region IrisData
    public static void IrisData(out double[][] trainingData, out int[] expectedTrainingTargets,
                                out double[][] testingData, out int[] expectedTestingTargets) {
      double[][] predictors;
      int[] targets;
      DowloadIrisData(out predictors, out targets);

      var nRows = predictors.Length;
      var nCols = predictors[0].Length;
      var nRowsTesting = Convert.ToInt32(0.2*nRows);
      var nRowsTraining = nRows - nRowsTesting;

      trainingData = new double[nRowsTraining][];
      expectedTrainingTargets = new int[nRowsTraining];
      for (var k = 0; k < nRowsTraining; k++) {
        trainingData[k] = new double[nCols];
        Array.Copy(predictors[k], trainingData[k], nCols);
        expectedTrainingTargets[k] = targets[k];
      }
      testingData = new double[nRowsTesting][];
      expectedTestingTargets = new int[nRowsTesting];
      for (var k = 0; k < nRowsTesting; k++) {
        testingData[k] = new double[nCols];
        Array.Copy(predictors[nRows-nRowsTesting+k], testingData[k], nCols);
        expectedTestingTargets[k] = targets[nRows-nRowsTesting+k];
      }
    }
    #endregion

    #region Train
    public static void Train(double[][] trainingData, out int[] predicted) {

      kmeans = new KMeans(3) {
        Tolerance = 1e-5,
        ComputeInformation = true
      };

      predicted = kmeans.Compute(trainingData);
    }
    #endregion

    #region Test
    public static void Test(double[][] testingData, out int[] predicted) {
      var nRowsTesting = testingData.Length;
      predicted = new int[nRowsTesting];
      for (var k = 0; k < nRowsTesting; k++) {
        predicted[k] = kmeans.Clusters.Nearest(testingData[k]);
      }
    }
    #endregion
  }
  #endregion

  class Program {
    static void Main(string[] args) {
      double[][] trainingData, testingData;
      int[] expectedTrainingTargets, expectedTestingTargets;

      K_Means.IrisData(out trainingData, out expectedTrainingTargets, out testingData, out expectedTestingTargets);

      int[] predictedTrainingTargets;
      K_Means.Train(trainingData, out predictedTrainingTargets);

      int[] predictedTestingTargets;
      K_Means.Test(testingData, out predictedTestingTargets);

      var confusionMatrix = new ConfusionMatrix(predictedTestingTargets, expectedTestingTargets);

      var nCorrect = 0;
      var nRows = expectedTestingTargets.Length;
      for (var k=0; k<nRows; k++) {
        if (predictedTestingTargets[k] == expectedTestingTargets[k]) { nCorrect++; }
      }

      Console.WriteLine(" Number correct: {0} out of {1}", nCorrect, nRows);
      Console.WriteLine("         FScore: {0}", confusionMatrix.FScore);
      Console.WriteLine("      Precision: {0}", confusionMatrix.Precision);
      Console.WriteLine(" True Positives: {0}", confusionMatrix.TruePositives);
      Console.WriteLine("False Positives: {0}", confusionMatrix.FalsePositives);
      Console.WriteLine(" True Negatives: {0}", confusionMatrix.TrueNegatives);
      Console.WriteLine("False Negatives: {0}", confusionMatrix.FalseNegatives);
      Console.WriteLine("       Accuracy: {0}", confusionMatrix.Accuracy);
      Console.WriteLine(" Standard Error: {0}", confusionMatrix.StandardError);
      Console.WriteLine("       Variance: {0}", confusionMatrix.Variance);
      Console.WriteLine(" ");
      Console.WriteLine("Hit enter to exit.");
      Console.ReadKey();
    }
  }
}
4

1 回答 1

3

K-means不是分类算法

但它是一种随机算法,因此每次得到不同的结果也就不足为奇了。

现在,由于它是随机的,k-means 使用的标签也是随机的。

因此,30 个正确的 2 个可能与 30 个正确的 28 个相同(只是标签打乱了)。

再次运行它,它可能会产生相同的集群,但“标签”都混在一起了。(事实上​​,它不知道鸢尾花的种类。它标记对象 0、1、2;而不是“鸢尾花”)

于 2015-05-07T21:23:20.880 回答