Подтвердить что ты не робот

Как правильно обучить мою нейронную сеть

Я пытаюсь научить нейронную сеть решать, куда идти, исходя из введенного уровня жизни. Нейронная сеть всегда будет получать три входа [x, y, life]. Если life => 0.2, он должен вывести угол от [x, y] до (1, 1). Если life < 0.2, он должен вывести угол от [x, y] до (0, 0).

Поскольку входы и выходы нейронов должны быть между 0 и 1, я делю угол на 2 *Math.PI.

Вот код:

var network = new synaptic.Architect.Perceptron(3,4,1);

for(var i = 0; i < 50000; i++){
  var x = Math.random();
  var y = Math.random();
  var angle1 = angleToPoint(x, y, 0, 0) / (2 * Math.PI);
  var angle2 = angleToPoint(x, y, 1, 1) / (2 * Math.PI);
  for(var j = 0; j < 100; j++){
    network.activate([x,y,j/100]);
    if(j < 20){
      network.propagate(0.3, [angle1]);
    } else {
      network.propagate(0.3, [angle2]);
    }
  }
}

Попробуйте здесь: jsfiddle

Поэтому, когда я ввожу следующий вход [0, 1, 0.19], я ожидаю, что нейронная сеть выведет что-то близкое к [0.75] (1.5PI / 2PI). Но мои результаты полностью противоречивы и не показывают никакой корреляции с каким-либо введенным данным.

Какую ошибку я принимаю в обучении моей нейронной сети?

Мне удалось научить нейронную сеть выводить 1 при вводе [a, b, c] с c => 0.2 и 0 при вводе [a, b, c] с c < 0.2. Мне также удалось научить его выводить угол в определенное место на основе ввода [x, y], однако Я не могу совместить их.


В соответствии с запросом, я написал код, который использует 2 Neural Networks для получения желаемого результата. Первая нейронная сеть преобразует уровень жизни в 0 или 1, а вторая нейронная сеть выдает угол в зависимости от 0 или 1, полученных из первой нейронной сети. Это код:

// This network outputs 1 when life => 0.2, otherwise 0
var network1 = new synaptic.Architect.Perceptron(3,3,1);
// This network outputs the angle to a certain point based on life
var network2 = new synaptic.Architect.Perceptron(3,3,1);

for (var i = 0; i < 50000; i++){
  var x = Math.random();
  var y = Math.random();
  var angle1 = angleToPoint(x, y, 0, 0) / (2 * Math.PI);
  var angle2 = angleToPoint(x, y, 1, 1) / (2 * Math.PI);

  for(var j = 0; j < 100; j++){
    network1.activate([x,y,j/100]);
    if(j < 20){
      network1.propagate(0.1, [0]);
    } else {
      network1.propagate(0.1, [1]);
    }
     network2.activate([x,y,0]);
    network2.propagate(0.1, [angle1]);
    network2.activate([x,y,1]);
    network2.propagate(0.1, [angle2]);
  }
}

Попробуйте здесь: jsfiddle

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

4b9b3361

Ответ 1

Наблюдения

  • Скорректированное распределение, выбранное как набор для обучения

    Ваш обучающий набор выбирает параметр life внутри for(var j = 0; j < 100; j++), который сильно смещен в сторону j>20 и, следовательно, life>0.2. Он имеет в 4 раза больше данных обучения для этого подмножества, что делает приоритет вашей тренировочной функции.

  • Неперетасованные данные обучения

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

    Это будет складываться с предыдущей точкой, потому что вы снова уделяете больше внимания некоторому подмножеству значений life.

  • Вы также должны измерить свою производительность обучения

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

    Можно сказать, что у вас есть два класса точек данных: те, у которых life>0.2, а другие нет. Но поскольку вы ввели разрыв в функции angleToPoint, я бы рекомендовал вам разделить три класса: сохранить класс для life<0.2 (потому что функция ведет себя непрерывно) и split life>0.2 in "выше (1,1 )" и "ниже (1,1)."

  • Сложность сети

    Вы можете успешно организовать сеть для каждой задачи отдельно. Теперь вы хотите стек их. Это и есть цель глубокого обучения: каждый слой опирается на концепции, воспринимаемые предыдущим слоем, тем самым увеличивая сложность понятий, которые он может изучить.

    Поэтому вместо того, чтобы использовать 20 узлов в одном слое, я бы рекомендовал вам использовать 2 слоя из 10 узлов. Это соответствует иерархии классов, о которой я упомянул в предыдущей точке.

Код

Запустив этот код, у меня была ошибка обучения/тестирования 0.0004/0.0002.

https://jsfiddle.net/hekqj5jq/11/

var network = new synaptic.Architect.Perceptron(3,10,10,1);
var trainer = new synaptic.Trainer(network);
var trainingSet = [];

for(var i = 0; i < 50000; i++){
  // 1st category: above vector (1,1), measure against (1,1)
  var x = getRandom(0.0, 1.0);
  var y = getRandom(x, 1.0);
  var z = getRandom(0.2, 1);
  var angle = angleToPoint(x, y, 1, 1) / (2 * Math.PI);
  trainingSet.push({input: [x,y,z], output: [angle]});
  // 2nd category: below vector (1,1), measure against (1,1)
  var x = getRandom(0.0, 1.0);
  var y = getRandom(0.0, x);
  var z = getRandom(0.2, 1);
  var angle = angleToPoint(x, y, 1, 1) / (2 * Math.PI);
  trainingSet.push({input: [x,y,z], output: [angle]});
  // 3rd category: above/below vector (1,1), measure against (0,0)
  var x = getRandom(0.0, 1.0);
  var y = getRandom(0.0, 1.0);
  var z = getRandom(0.0, 0.2);
  var angle = angleToPoint(x, y, 0, 0) / (2 * Math.PI);
  trainingSet.push({input: [x,y,z], output: [angle]});
}

trainer.train(trainingSet, {
    rate: 0.1,
    error: 0.0001,
    iterations: 50,
    shuffle: true,
    log: 1,
    cost: synaptic.Trainer.cost.MSE
});

testSet = [
    {input: [0,1,0.25], output: [angleToPoint(0, 1, 1, 1) / (2 * Math.PI)]},
    {input: [1,0,0.35], output: [angleToPoint(1, 0, 1, 1) / (2 * Math.PI)]},
    {input: [0,1,0.10], output: [angleToPoint(0, 1, 0, 0) / (2 * Math.PI)]},
    {input: [1,0,0.15], output: [angleToPoint(1, 0, 0, 0) / (2 * Math.PI)]}
];

$('html').append('<p>Train:</p> ' + JSON.stringify(trainer.test(trainingSet)));
$('html').append('<p>Tests:</p> ' + JSON.stringify(trainer.test(testSet)));

$('html').append('<p>1st:</p> ')
$('html').append('<p>Expect:</p> ' + angleToPoint(0, 1, 1, 1) / (2 * Math.PI));
$('html').append('<p>Received: </p> ' + network.activate([0, 1, 0.25]));

$('html').append('<p>2nd:</p> ')
$('html').append('<p>Expect:</p> ' + angleToPoint(1, 0, 1, 1) / (2 * Math.PI));
$('html').append('<p>Received: </p> ' + network.activate([1, 0, 0.25]));

$('html').append('<p>3rd:</p> ')
$('html').append('<p>Expect:</p> ' + angleToPoint(0, 1, 0, 0) / (2 * Math.PI));
$('html').append('<p>Received: </p> ' + network.activate([0, 1, 0.15]));

$('html').append('<p>4th:</p> ')
$('html').append('<p>Expect:</p> ' + angleToPoint(1, 0, 0, 0) / (2 * Math.PI));
$('html').append('<p>Received: </p> ' + network.activate([1, 0, 0.15]));

function angleToPoint(x1, y1, x2, y2){
  var angle = Math.atan2(y2 - y1, x2 - x1);
  if(angle < 0){
    angle += 2 * Math.PI;
  }
  return angle;
}

function getRandom (min, max) {
    return Math.random() * (max - min) + min;
}

Дальнейшие замечания

Как я упоминал в комментариях и в чате, нет такой вещи, как "угол между (x, y) и (0,0)", поскольку понятие угла между векторами обычно принимается за разность между их направления и (0,0) не имеют направления.

Ваша функция angleToPoint(p1, p2) возвращает вместо этого направление (p1-p2). Для p2 = (0,0) это означает, что угол между p1 и осью x в порядке. Но для p1 = (1,1) и p2 = (1,0) он не вернется на 45 градусов. Для p1 = p2 оно undefined вместо нуля.