Получение метки класса с помощью DynamicTimeWarping с помощью Accord.NET

Я разрабатываю проект, в котором мне нужно распознавать жесты. После поиска способа сделать это я наткнулся на динамическую деформацию времени. Чтобы попробовать эту идею, а так как мой проект на C#, я решил попробовать Accord.NET. Прежде чем я попробовал это в своем проекте, я создал чистый проект и изменил пример в документации Accord.NET, который можно найти здесь:

http://accord-framework.net/docs/html/T_Accord_Statistics_Kernels_DynamicTimeWarping.htm

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

В документации не очень ясно, как это сделать, поскольку метод Decide, использованный в примере, возвращает только логическое значение. Я искал в документации способ определить правильный класс, но безрезультатно.

Единственное, что я нашел, это следующую строку, но она возвращает целочисленное значение 0 или 1 вместо логического значения:

var res1 = ((IClassifier)svm.ToMulticlass()).Решить(последовательности[0]);

Может ли кто-нибудь указать мне в правильном направлении, как правильно идентифицировать жест? Это моя первая попытка машинного обучения и Accord.NET, так что все это для меня абсолютно ново. Пример кода можно найти ниже.

namespace DynamicTimeWarpingExample
{
    public class Program
    {
        public static void Main(string[] args)
        {

            double[][][] sequences =
            {
                new double[][] // Swipe left
                {
                    new double[] { 1, 1, 1 }, 
                    new double[] { 1, 2, 1 }, 
                    new double[] { 1, 2, 2 }, 
                    new double[] { 2, 2, 2 }, 
                },

                new double[][] // Swipe right
                {
                    new double[] { 1, 10, 6 }, 
                    new double[] { 1, 5, 6 }, 
                    new double[] { 6, 7, 1 }, 
                },

                new double[][] // Double tap
                {
                    new double[] { 8, 2, 5 }, 
                    new double[] { 1, 50, 4 }, 
                }
            };


            int[] outputs =
            {
                    0,  // Swipe left
                    1,  // Swipe right
                    2  // Double tap
            };

            var smo = new SequentialMinimalOptimization<DynamicTimeWarping, double[][]>()
            {
                Complexity = 1.5,

                Kernel = new DynamicTimeWarping(alpha: 1, degree: 1)
            };

            var svm = smo.Learn(sequences, outputs);

            bool[] predicted = svm.Decide(sequences);

            double error = new ZeroOneLoss(outputs).Loss(predicted); // error will be 0.0

            var res1 = ((IClassifier<double[][], int>)svm.ToMulticlass()).Decide(sequences[0]); // returns 0

            var res2 = ((IClassifier<double[][], int>)svm.ToMulticlass()).Decide(sequences[1]); // returns 1

            var res3 = ((IClassifier<double[][], int>)svm.ToMulticlass()).Decide(sequences[2]); // returns 1
        }
    }
}

***************** Новая версия *****************

public static void Main(string[] args)
{
    double[][][] sequences =
    {
        new double[][] // Swipe left
        {
            new double[] { 1, 1, 1 },
            new double[] { 1, 2, 1 },
            new double[] { 1, 2, 2 },
            new double[] { 2, 2, 2 },
        },

        new double[][] // Swipe right
        {
            new double[] { 1, 10, 6 },
            new double[] { 1, 5, 6 },
            new double[] { 6, 7, 1 },
        },

        new double[][] // Double tap
        {
            new double[] { 8, 2, 5 },
            new double[] { 1, 50, 4 },
        }
    };


    int[] outputs =
    {
            0,  // Swipe left
            1,  // Swipe right
            2  // Double tap
    };

    var teacher = new MulticlassSupportVectorLearning<DynamicTimeWarping, double[][]>()
    {
        // Configure the learning algorithm to use SMO to train the
        //  underlying SVMs in each of the binary class subproblems.
        Learner = (param) => new SequentialMinimalOptimization<DynamicTimeWarping, double[][]>
        {
            Complexity = 1.5,
            Kernel = new DynamicTimeWarping(alpha: 1, degree: 1),
            //UseKernelEstimation = true
        }
    };

    // Learn a machine
    var machine = teacher.Learn(sequences, outputs);

    // Create the multi-class learning algorithm for the machine
    var calibration = new MulticlassSupportVectorLearning<DynamicTimeWarping, double[][]>()
    {
        Model = machine, // We will start with an existing machine

        // Configure the learning algorithm to use Platt's calibration
        Learner = (param) => new ProbabilisticOutputCalibration<DynamicTimeWarping, double[][]>()
        {
            Model = param.Model // Start with an existing machine
        }
    };

    // Configure parallel execution options
    calibration.ParallelOptions.MaxDegreeOfParallelism = 1;

    // Learn a machine
    calibration.Learn(sequences, outputs);

    double decision1, decision2, decision3, decision4, decision5, decision6;


    var res1 = machine.Probability(sequences[0], out decision1); // decision 0 - Probability 0.78698604216159851 - Score 1
    var res2 = machine.Probability(sequences[1], out decision2); // decision 1 - Probability 0.67246889837875257 - Score 1
    var res3 = machine.Probability(sequences[2], out decision3); // decision 2 - Probability 0.78698604216159851 - Score 1


    var newGesture1 = new double[][]
    {
        new double[] { 1, 1, 1 },
        new double[] { 1, 2, 1 },
        new double[] { 1, 2, 2 },
        new double[] { 2, 2, 2 },
    };

    var newGesture2 = new double[][]
    {
        new double[] { 1, 10, 6 },
        new double[] { 1, 5, 6 },
        new double[] { 6, 7, 1 },
    };

    var newGesture3 = new double[][]
    {
        new double[] { 8, 2, 5 },
        new double[] { 1, 50, 4 },
    };

    var res5 = machine.Score(newGesture1, out decision5); // decision 0 - Probability 0.35577588944247057 - Score 0.051251948605637254
    var res6 = machine.Score(newGesture2, out decision6); // decision 1 - Probability 0.40733908994050544 - Score 0.19912250476931792
    var res4 = machine.Score(newGesture3, out decision4); // decision 2 - Probability 0.71853321355842836 - Score 0.816934034911964
}

person Andre    schedule 06.04.2017    source источник
comment
Взгляните на ЭТО .   -  person jsanalytics    schedule 10.04.2017


Ответы (1)


Проблема в том, что вы создаете бинарный классификатор для задачи, которая на самом деле включает в себя несколько классов.

В вашем случае вместо того, чтобы делать:

var smo = new SequentialMinimalOptimization<DynamicTimeWarping, double[][]>()      
{
    Complexity = 1.5,
    Kernel = new DynamicTimeWarping(alpha: 1, degree: 1)
};

var svm = smo.Learn(sequences, outputs);

Вы хотели бы обернуть эту проблему бинарного обучения в многоклассовое обучение, используя

// Create the multi-class learning algorithm for the machine
var teacher = new MulticlassSupportVectorLearning<DynamicTimeWarping, double[][]>()
{
    // Configure the learning algorithm to use SMO to train the
    //  underlying SVMs in each of the binary class subproblems.
    Learner = (param) => new SequentialMinimalOptimization<DynamicTimeWarping, double[][]>
    {
        Complexity = 1.5,
        Kernel = new DynamicTimeWarping(alpha: 1, degree: 1)
    };
}

// Learn a  machine
var svm = teacher.Learn(inputs, outputs);
person Cesar    schedule 08.07.2017
comment
Спасибо, Цезарь, ответ от самого создателя Accord.Net :) Это как нельзя кстати. Я попробую на этой неделе и отмечу его как принятый ответ, если он сработает. - person Andre; 10.07.2017
comment
Это правильное решение. Еще одна строка, которую необходимо изменить, выглядит следующим образом: bool[] предсказано = svm.Decide(sequences); к этому: int [] предсказано = svm.Decide (последовательности); - person Andre; 11.07.2017
comment
Еще раз привет Ceaser, просто быстрый вопрос. Допустим, у меня есть сенсорная панель, и я хочу отклонить случайные жесты. Есть ли способ узнать определенность решения? Например, в классификаторе kNN это можно получить, передав переменную в метод Compute: var calculatedExerciseClass = knn.Compute(inputs, out Certainty); Используя аналогичный подход, я мог бы принять результат метода Decide, если уверенность была выше определенного порога. - person Andre; 13.07.2017
comment
Вы можете использовать методы .Probability или .Score SVM. Если вы используете SequentialMinimalOptimization, вам необходимо сначала откалибровать компьютер с помощью ProbabilisticOutputCalibration ( см. второй пример на связанной странице). После этого вы сможете получить и решение, и вероятность этого решения по методу Probability(input_vector, out decided_class). - person Cesar; 13.07.2017
comment
Поскольку ответ слишком большой, мне придется разбить его на несколько сообщений. Я пытался использовать второй пример на вашей странице документации, но если я использую UseKernelEstimation = true, я получаю исключение Тип ядра не поддерживает оценку. на var машина = учитель. Учиться (последовательности, результаты); Я использую ядро ​​DynamicTimeWarping. Если я удалю эту строку, исключение исчезнет, ​​дело в том, что я получу некоторые странные результаты, которые, как мне кажется, могут быть не связаны с этим. Чтобы помочь мне лучше объяснить, что происходит, я отредактировал свой исходный пост и добавил новую версию кода внизу. - person Andre; 13.07.2017
comment
Допустим, я вычисляю вероятность первого жеста, используя multiclassSVM.Probability(sequences[0], out solution1); Это дает мне правильное решение, хотя вероятность составляет всего около 0,78. Если после этого я снова попытаюсь оценить sequences[0], я получу тот же результат, что и ожидалось. Теперь начинается странная часть: если я попытаюсь создать новый экземпляр последовательности и использовать те же точные значения, что и последовательности [0], вероятность упадет примерно до 0,36. - person Andre; 13.07.2017
comment
Если я использую Score вместо Probability, я получаю похожее поведение. Если я оцениваю sequences[0] несколько раз, я всегда получаю значение 1, но если я создаю новый экземпляр с точно такими же значениями, я получаю оценку 0,05. Я добавил точные значения в качестве комментариев к коду, поскольку вы можете видеть, что экземпляры с именем newGestureX получают разные значения. У вас есть идея, почему это происходит? - person Andre; 13.07.2017
comment
Андре, большое спасибо за пример, могу ли я попросить вас открыть новую проблему в системе отслеживания проблем проекта с кодом, который вы используете. Это может быть проблема с классом ядра DynamicTimeWarp. Кстати, вам не нужно устанавливать UseKernelEstimation, если вы этого не хотите. Второй пример должен был показать, как класс ProbabilisticOutputCalibration может/должен использоваться после обучения с помощью SMO, но установка свойства UseKernelEstimation на самом деле совершенно необязательна. - person Cesar; 14.07.2017