Как синхронизировать обучающие и тестовые кодовые книги в Accord.Net

#c# #tree #random-forest #accord.net

#c# #дерево #случайный лес #accord.net

Вопрос:

Вопрос: Существует ли пример случайного леса, который разделяет обучающий и тестовый наборы? Текущий пример, который я нашел в тестовом проекте Accord-Net ML, использует те же самые данные для обучения и тестирования.

По-видимому, проблема, с которой я сталкиваюсь, заключается в синхронизации сгенерированных меток (целых чисел) в наборах тестов и поездов. Я генерирую метки поездов как таковые:

 int[] trainOutputs = trainCodebook.Translate("Output", trainLabels);

And the test labels similarly:

int[] testOutputs = testCodebook.Translate("Output", testLabels);

Finally I train with the train data and test with the test data:

var forest = teacher.Learn(trainVectors, trainOutputs);

int[] predicted = forest.Decide(testVectors);
  

Если первые три строки не совпадают как в обучающем, так и в тестовом наборах, метки разные, и, соответственно, это приводит к очень высокому уровню ошибок.

Я попытался просто создать свою кодовую книгу вручную с помощью троичных строк:

 new Codification("-1","0","1");
  

К сожалению, это приводит к ошибке времени выполнения, указывающей, что данного ключа не было в словаре. Я уверен, что есть способ синхронизировать генерацию ключей в двух отдельных кодовых книгах. Я могу заставить это работать с приведенным ниже кодом, ЕСЛИ добавлю три строки моих обучающих данных, содержащих все три ключа, в начало моих тестовых данных. Не мое предпочтительное решение; =)

Вот весь тест, который я запускаю:

  [Test]
 public void test_learn()
 {
 Accord.Math.Random.Generator.Seed = 1;

    /////////// TRAINING SET ///////////
    // First, let's load the TRAINING set into an array of text that we can process
    string[][] text = Resources.train.Split(new[] { "rn" },
        StringSplitOptions.RemoveEmptyEntries).Apply(x => x.Split(','));

    int length = text[0].Length;
    List<int> columns = new List<int>();
    for (int i = 1; i < length; i  )
    {
        columns.Add(i);
    }
    double[][] trainVectors = text.GetColumns(columns.ToArray()).To<double[][]>();

    // The first column contains the expected ternary category (i.e. -1, 0, or 1)
    string[] trainLabels = text.GetColumn(0);
    var trainCodebook = new Codification("Output", trainLabels);
    int[] trainOutputs = trainCodebook.Translate("Output", trainLabels);

    ////////// TEST SET ////////////

    text = Resources.test.Split(new[] { "rn" },
        StringSplitOptions.RemoveEmptyEntries).Apply(x => x.Split(','));

    double[][] testVectors = text.GetColumns(columns.ToArray()).To<double[][]>();
    string[] testLabels = text.GetColumn(0);
    var testCodebook = new Codification("Output", testLabels);
    int[] testOutputs = testCodebook.Translate("Output", testLabels);

    var teacher = new RandomForestLearning()
    {
        NumberOfTrees = 10,
    };

    var forest = teacher.Learn(trainVectors, trainOutputs);
    int[] predicted = forest.Decide(testVectors);

    int lineNum = 1;
    foreach (int prediction in predicted)
    {
        Console.WriteLine("Prediction "   lineNum   ": " 
          trainCodebook.Translate("Output", prediction));
        lineNum  ;
    }
    // I'm using the test vectors to calculate the error rate
    double error = new ZeroOneLoss(testOutputs).Loss(forest.Decide(testVectors));

    Console.WriteLine("Error term is "   error);

    Assert.IsTrue(error < 0.20); // humble expectations ;-)
}
  

Комментарии:

1. На самом деле у вас должна быть только одна кодовая книга, созданная из обучающего набора, и вы должны использовать ее для предварительной обработки данных как из обучающего , так и из тестового набора.

Ответ №1:

Хорошо, я разобрался. Смотрите код ниже:

Ладно, я думаю, что смог это исправить. Проблема заключалась в ошибочной реализации сериализации в DecisionTree. К счастью, у нас есть код — смотрите исправление ниже:

 namespace Accord.MachineLearning.DecisionTrees
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  using System.Data;
  using System.Runtime.Serialization;
  using System.Runtime.Serialization.Formatters.Binary;
  using System.IO;
  using Accord.Statistics.Filters;
  using Accord.Math;
  using AForge;
  using Accord.Statistics;
  using System.Threading;


/// <summary>
///   Random Forest.
/// </summary>
/// 
/// <remarks>
/// <para>
///   Represents a random forest of <see cref="DecisionTree"/>s. For 
///   sample usage and example of learning, please see the documentation
///   page for <see cref="RandomForestLearning"/>.</para>
/// </remarks>
/// 
/// <seealso cref="DecisionTree"/>
/// <seealso cref="RandomForestLearning"/>
/// 
[Serializable]
public class RandomForest : MulticlassClassifierBase, IParallel
{
    private DecisionTree[] trees;
    **[NonSerialized]
    private ParallelOptions parallelOptions;**


    /// <summary>
    ///   Gets the trees in the random forest.
    /// </summary>
    /// 
    public DecisionTree[] Trees
    {
        get { return trees; }
    }

    /// <summary>
    ///   Gets the number of classes that can be recognized
    ///   by this random forest.
    /// </summary>
    /// 
    [Obsolete("Please use NumberOfOutputs instead.")]
    public int Classes { get { return NumberOfOutputs; } }

    /// <summary>
    ///   Gets or sets the parallelization options for this algorithm.
    /// </summary>
    ///
    **public ParallelOptions ParallelOptions { get { return parallelOptions; } set { parallelOptions = value; } }**

    /// <summary>
    /// Gets or sets a cancellation token that can be used
    /// to cancel the algorithm while it is running.
    /// </summary>
    /// 
    public CancellationToken Token
    {
        get { return ParallelOptions.CancellationToken; }
        set { ParallelOptions.CancellationToken = value; }
    }

    /// <summary>
    ///   Creates a new random forest.
    /// </summary>
    /// 
    /// <param name="trees">The number of trees in the forest.</param>
    /// <param name="classes">The number of classes in the classification problem.</param>
    /// 
    public RandomForest(int trees, int classes)
    {
        this.trees = new DecisionTree[trees];
        this.NumberOfOutputs = classes;
        this.ParallelOptions = new ParallelOptions();
    }

    /// <summary>
    ///   Computes the decision output for a given input vector.
    /// </summary>
    /// 
    /// <param name="data">The input vector.</param>
    /// 
    /// <returns>The forest decision for the given vector.</returns>
    /// 
    [Obsolete("Please use Decide() instead.")]
    public int Compute(double[] data)
    {
        return Decide(data);
    }


    /// <summary>
    /// Computes a class-label decision for a given <paramref name="input" />.
    /// </summary>
    /// <param name="input">The input vector that should be classified into
    /// one of the <see cref="ITransform.NumberOfOutputs" /> possible classes.</param>
    /// <returns>A class-label that best described <paramref name="input" /> according
    /// to this classifier.</returns>
    public override int Decide(double[] input)
    {
        int[] responses = new int[NumberOfOutputs];
        Parallel.For(0, trees.Length, ParallelOptions, i =>
        {
            int j = trees[i].Decide(input);
            Interlocked.Increment(ref responses[j]);
        });

        return responses.ArgMax();
    }

   [OnDeserializing()]
    internal void OnDeserializingMethod(StreamingContext context)
    {
        this.ParallelOptions = new ParallelOptions();
    }
}
}