Question Comment former correctement mon réseau neuronal


J'essaie d'enseigner un réseau de neurones pour décider où aller en fonction de ses entrées niveau de vie. Le réseau neuronal recevra toujours trois entrées [x, y, life]. Si life => 0.2, il devrait sortir l'angle de [x, y] à (1, 1). Si life < 0.2, il devrait sortir l'angle de [x, y] à (0, 0).

Comme les entrées et les sorties des neurones doivent être entre 0 et 1, Je divise l'angle par 2 *Math.PI.

Voici le code:

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]);
    }
  }
}

Essayez-le ici: jsfiddle

Donc, quand j'entre dans l'entrée suivante [0, 1, 0.19], Je m'attends à ce que le réseau neuronal produise quelque chose proche de [0.75] (1.5PI / 2PI). Mais mes résultats sont totalement incohérents et ne montrent aucune corrélation avec aucune donnée donnée.

Quelle erreur fais-je en enseignant mon réseau de neurones?

J'ai réussi à enseigner un réseau neuronal à la sortie 1 en entrée [a, b, c] avec c => 0.2 et 0 en entrée [a, b, c] avec c < 0.2. J'ai également réussi à lui apprendre à générer un angle avec un certain emplacement en fonction de [x, y] entrée, cependant Je n'arrive pas à les combiner.


Comme demandé, j'ai écrit un code qui utilise 2 réseaux de neurones pour obtenir le résultat souhaité. Le premier réseau neuronal convertit le niveau de vie en 0 ou en 1, et le second réseau neuronal génère un angle dépendant du 0 ou du 1 qu'il a reçu du premier réseau neuronal. Ceci est le code:

// 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]);
  }
}

Essayez-le ici: jsfiddle

Comme vous pouvez le voir dans cet exemple. Il parvient à atteindre le résultat souhaité assez étroitement, en ajoutant plus d'itérations, il sera encore plus proche.


17
2018-02-02 15:22


origine


Réponses:


Observations

  1. Distribution asymétrique échantillonnée en tant que kit de formation

    Votre kit de formation choisit le life paramètre à l'intérieur for(var j = 0; j < 100; j++), qui est fortement biaisé vers j>20 et par conséquent life>0.2. Il dispose de 4 fois plus de données de formation pour ce sous-ensemble, ce qui rend votre fonction de formation prioritaire.

  2. Données d'entraînement non mélangées

    Vous vous entraînez séquentiellement contre le life paramètre, qui peut être nocif. Votre réseau finira par accorder plus d’attention au plus grand js puisque c'est la raison la plus récente des propagations de réseau. Vous devez mélanger votre jeu d’entraînement pour éviter ce biais.

    Cela s'empilera avec le point précédent, car vous accordez à nouveau plus d'attention à un sous-ensemble de life valeurs.

  3. Vous devriez également mesurer vos performances d'entraînement

    Votre réseau, malgré les observations précédentes, n’était pas si grave. Votre erreur d'entraînement n'était pas aussi importante que vos tests. Cet écart signifie généralement que vous vous formez et testez différentes distributions d’échantillons.

    Vous pourriez dire que vous avez deux classes de points de données: ceux avec life>0.2 et les autres pas. Mais parce que vous avez introduit une discontinuité dans le angleToPoint fonction, je vous recommande de séparer en trois classes: garder une classe pour life<0.2 (parce que la fonction se comporte continuellement) et divisée life>0.2 dans "ci-dessus (1,1)" et "ci-dessous (1,1)".

  4. Complexité du réseau

    Vous pourriez former avec succès un réseau pour chaque tâche séparément. Maintenant vous voulez empiler leur. C'est tout à fait le but de Profond apprentissage: chaque couche s'appuie sur les concepts perçus par la couche précédente, augmentant ainsi la complexité des concepts qu'elle peut apprendre.

    Donc, au lieu d'utiliser 20 nœuds dans une seule couche, je vous recommande d'utiliser 2 couches de 10 nœuds. Cela correspond à la hiérarchie de classes que j'ai mentionnée dans le point précédent.

Le code

En cours d'exécution de ce code, j'ai eu une erreur de formation / test de 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;
}

Remarques supplémentaires

Comme je l'ai mentionné dans les commentaires et dans le chat, il n'y a pas "angle entre (x, y) et (0,0)", car la notion d'angle entre les vecteurs est généralement prise comme la différence entre leurs directions et (0,0) n'a pas de direction.

Votre fonction angleToPoint(p1, p2) renvoie à la place la direction de (p1-p2). Pour p2 = (0,0), cela signifie l'angle entre p1 et le x axe bien. Mais pour p1 =(1,1) et p2 =(1,0) il ne retournera pas 45 degrés. Pour p1 = p2, il est indéfini au lieu de zéro.


9
2018-02-05 14:31