如何正确训练我的神经网络

时间:2017-02-02 15:22:37

标签: javascript neural-network backpropagation

我试图根据输入的生命水平来教导神经网络决定去哪里。神经网络将始终接收三个输入[x, y, life]。如果life => 0.2,则应输出从[x, y](1, 1)的角度。如果life < 0.2,则应输出从[x, y](0, 0)的角度。

由于神经元的输入和输出应在01之间,我将角度除以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个神经网络来获得所需的输出。第一神经网络将生命水平转换为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

正如您在此示例中所看到的那样。它设法非常接近地达到所需的输出,通过添加更多的迭代,它将更接近。

1 个答案:

答案 0 :(得分:9)

观察

  1. 倾斜分布采样为训练集

    您的训练集选择life内的for(var j = 0; j < 100; j++)参数,该参数高度偏向j>20,因此life>0.2。它为该子集提供了4倍以上的训练数据,这使您的训练功能优先。

  2. 非改组训练数据

    您正在按照life参数顺序进行训练,这可能有害。您的网络最终会更多地关注较大的j,因为它是网络传播的最新原因。你应该改变你的训练集以避免这种偏见。

    这将与前一点叠加,因为您再次关注life值的某些子集。

  3. 您还应该衡量您的训练表现

    尽管以前有过观察,你的网络并不是那么糟糕。您的训练错误不如您的测试那么大。这种差异通常意味着您需要对不同的样本分布进行培训和测试。

    你可以说你有两类数据点:life>0.2而其他数据点没有。但是因为你在angleToPoint函数中引入了一个不连续性,我建议你分成三个类:为life<0.2保留一个类(因为函数连续运行)并拆分{{1}在&#34;以上(1,1)&#34;和&#34;低于(1,1)。&#34;

  4. 网络复杂性

    您可以分别为每项任务成功培训网络。现在你要堆叠它们。这是深度学习的目的:每一层都建立在前一层感知的概念之上,因此增加了它可以学习的概念的复杂性。

    因此,我建议您使用2层10个节点,而不是在单个图层中使用20个节点。这与我在前一点中提到的类层次结构相匹配。

  5. 代码

    运行此代码我的培训/测试错误为life>0.2 / 0.0004

      

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

    0.0002

    进一步说明

    正如我在评论和聊天中所提到的那样,在(x,y)和(0,0)&#34;之间没有&#34;角度这样的东西,因为矢量之间的角度通常被视为它们的方向之间的差异,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; } 没有方向。

    您的函数(0,0)返回(p1-p2)的方向。对于p2 = angleToPoint(p1, p2),这意味着p1和(0,0)轴之间的角度正常。但是对于p1 = x和p2 = (1,1),它将不会返回45度。对于p1 = p2,它是未定义的而不是零。