如何正确训练我的神经网络
How to correctly train my Neural Network
我正在尝试教神经网络根据输入的 生命水平 决定去哪里。神经网络将始终接收三个输入 [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]);
}
}
}
Try it out here: jsfiddle
所以当我输入以下输入 [0, 1, 0.19]
时,我希望神经网络输出接近 [0.75]
(1.5PI / 2PI
) 的东西。但是我的结果完全不一致,并且与任何给定的输入都没有任何关联。
我在教授神经网络时犯了什么错误?
I have managed to teach a neural network to output 1
when input [a, b, c]
with c => 0.2
and 0
when input [a, b, c]
with c < 0.2
. I have also managed to teach it to output an angle to a certain location based on [x, y]
input, however I can't seem to combine them.
根据要求,我编写了一些代码,使用 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]);
}
}
Try it out here: jsfiddle
正如您在此示例中所见。它设法非常接近地达到所需的输出,通过添加更多的迭代,它会更接近。
观察结果
作为训练集采样的偏态分布
您的训练集正在选择 for(var j = 0; j < 100; j++)
内的 life
参数,它高度偏向 j>20
,因此 life>0.2
。它具有该子集的 4 倍多的训练数据,这使您的训练功能具有优先级。
Non-shuffled训练数据
您正在针对 life
参数进行顺序训练,这可能是有害的。您的网络最终会更加关注更大的 j
s,因为这是网络传播的最新原因。你应该洗牌你的训练集以避免这种偏见。
这将与前一点叠加,因为您再次更加关注 life
值的某些子集。
你也应该衡量你的训练表现
尽管之前有观察,但您的网络并没有那么糟糕。你的训练错误没有你的测试那么大。这种差异通常意味着您正在对不同的样本分布进行训练和测试。
你可以说你有两个 classes 数据点:一个有 life>0.2
而另一个没有。但是因为你在 angleToPoint
函数中引入了一个不连续性,我建议你分成三个 classes:为 life<0.2
保留一个 class (因为函数连续运行) 并将 life>0.2
拆分为 "above (1,1)" 和 "below (1,1)."
网络复杂度
您可以成功地分别为每个任务训练一个网络。现在您想 堆叠 它们。这正是 深度 学习的目的:每一层都建立在前一层感知到的概念之上,因此增加了它可以学习的概念的复杂性。
因此,与其在单层中使用 20 个节点,我建议您使用 2 层,每层 10 个节点。这与我在上一点中提到的 classes 层次结构相匹配。
代码
运行 这段代码我有一个 training/testing 错误 0.0004
/0.0002
。
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;
}
进一步说明
正如我在评论和聊天中提到的,没有 "angle between (x,y) and (0,0)" 这样的东西,因为向量之间的角度概念通常被视为它们的方向与 (0,0)
之间的差异没有方向
你的函数 angleToPoint(p1, p2)
return 代替了 (p1-p2) 的方向。对于 p2 = (0,0)
,这意味着 p1 和 x
轴之间的角度没问题。但是对于 p1=(1,1)
和 p2=(1,0)
它不会 return 45 度。对于 p1=p2,它是未定义的而不是零。
我正在尝试教神经网络根据输入的 生命水平 决定去哪里。神经网络将始终接收三个输入 [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]);
}
}
}
Try it out here: jsfiddle
所以当我输入以下输入 [0, 1, 0.19]
时,我希望神经网络输出接近 [0.75]
(1.5PI / 2PI
) 的东西。但是我的结果完全不一致,并且与任何给定的输入都没有任何关联。
我在教授神经网络时犯了什么错误?
I have managed to teach a neural network to output
1
when input[a, b, c]
withc => 0.2
and0
when input[a, b, c]
withc < 0.2
. I have also managed to teach it to output an angle to a certain location based on[x, y]
input, however I can't seem to combine them.
根据要求,我编写了一些代码,使用 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]);
}
}
Try it out here: jsfiddle
正如您在此示例中所见。它设法非常接近地达到所需的输出,通过添加更多的迭代,它会更接近。
观察结果
作为训练集采样的偏态分布
您的训练集正在选择
for(var j = 0; j < 100; j++)
内的life
参数,它高度偏向j>20
,因此life>0.2
。它具有该子集的 4 倍多的训练数据,这使您的训练功能具有优先级。Non-shuffled训练数据
您正在针对
life
参数进行顺序训练,这可能是有害的。您的网络最终会更加关注更大的j
s,因为这是网络传播的最新原因。你应该洗牌你的训练集以避免这种偏见。这将与前一点叠加,因为您再次更加关注
life
值的某些子集。你也应该衡量你的训练表现
尽管之前有观察,但您的网络并没有那么糟糕。你的训练错误没有你的测试那么大。这种差异通常意味着您正在对不同的样本分布进行训练和测试。
你可以说你有两个 classes 数据点:一个有
life>0.2
而另一个没有。但是因为你在angleToPoint
函数中引入了一个不连续性,我建议你分成三个 classes:为life<0.2
保留一个 class (因为函数连续运行) 并将life>0.2
拆分为 "above (1,1)" 和 "below (1,1)."网络复杂度
您可以成功地分别为每个任务训练一个网络。现在您想 堆叠 它们。这正是 深度 学习的目的:每一层都建立在前一层感知到的概念之上,因此增加了它可以学习的概念的复杂性。
因此,与其在单层中使用 20 个节点,我建议您使用 2 层,每层 10 个节点。这与我在上一点中提到的 classes 层次结构相匹配。
代码
运行 这段代码我有一个 training/testing 错误 0.0004
/0.0002
。
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;
}
进一步说明
正如我在评论和聊天中提到的,没有 "angle between (x,y) and (0,0)" 这样的东西,因为向量之间的角度概念通常被视为它们的方向与 (0,0)
之间的差异没有方向
你的函数 angleToPoint(p1, p2)
return 代替了 (p1-p2) 的方向。对于 p2 = (0,0)
,这意味着 p1 和 x
轴之间的角度没问题。但是对于 p1=(1,1)
和 p2=(1,0)
它不会 return 45 度。对于 p1=p2,它是未定义的而不是零。