redes programar neuronales inteligencia ejemplos artificial aprender javascript neural-network backpropagation

javascript - programar - redes neuronales ejemplos



Cómo entrenar correctamente mi red neuronal (1)

Estoy tratando de enseñar a una red neuronal a decidir dónde ir en función de su nivel de vida ingresado. La red neuronal siempre recibirá tres entradas [x, y, life] . Si life => 0.2 , debe mostrar el ángulo de [x, y] a (1, 1) . Si la life < 0.2 , debe mostrar el ángulo de [x, y] a (0, 0) .

Como las entradas y salidas de las neuronas deben estar entre 0 y 1 , 2 *Math.PI el ángulo entre 2 *Math.PI

Aquí está el código:

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

Pruébalo aquí: jsfiddle

Entonces, cuando ingreso la siguiente entrada [0, 1, 0.19] , espero que la red neuronal emita algo cercano a [0.75] ( 1.5PI / 2PI ). Pero mis resultados son completamente inconsistentes y no muestran ninguna correlación con ninguna entrada dada en absoluto.

¿Qué error estoy cometiendo en la enseñanza de mi red neuronal?

He logrado enseñar una red neuronal a la salida 1 cuando la entrada [a, b, c] con c => 0.2 y 0 cuando la entrada [a, b, c] con c < 0.2 . También me las he arreglado para enseñarle a emitir un ángulo a una determinada ubicación en función de la entrada [x, y] , sin embargo, parece que no puedo combinarlos .

Según lo solicitado, he escrito un código que utiliza 2 redes neuronales para obtener el resultado deseado. La primera red neuronal convierte el nivel de vida a 0 o 1, y la segunda red neuronal genera un ángulo que depende del 0 o 1 que obtuvo de la primera red neuronal. Este es el código:

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

Pruébalo aquí: jsfiddle

Como puedes ver en este ejemplo. Se las arregla para alcanzar la salida deseada bastante cerca, al agregar más iteraciones se acercará aún más.


Observaciones

  1. Distribución sesgada muestreada como conjunto de entrenamiento

    Su conjunto de entrenamiento está eligiendo el parámetro de life interior for(var j = 0; j < 100; j++) , que está muy orientado hacia j>20 y, en consecuencia, life>0.2 . Cuenta con 4 veces más datos de entrenamiento para ese subconjunto, lo que hace que su función de entrenamiento tenga prioridad.

  2. Datos de entrenamiento no barajados

    Estás entrenando secuencialmente contra el parámetro de life , que puede ser perjudicial. Su red terminará prestando más atención a las grandes j s, ya que es la razón más reciente para las propagaciones de la red. Debes barajar tu conjunto de entrenamiento para evitar este sesgo.

    Esto se acumulará con el punto anterior, porque nuevamente estás prestando más atención a algunos subconjuntos de valores de life .

  3. Debes medir tu rendimiento de entrenamiento también

    Tu red, a pesar de las observaciones anteriores, no era realmente tan mala. Tu error de entrenamiento no fue tan grande como tus pruebas. Esta discrepancia generalmente significa que está entrenando y probando en diferentes distribuciones de muestras.

    Se podría decir que tiene dos clases de puntos de datos: los que tienen una life>0.2 y los otros no. Pero debido a que introdujo una discontinuidad en la función angleToPoint , le recomiendo que se separe en tres clases: mantenga una clase para toda la life<0.2 (porque la función se comporta de manera continua) y divida la life>0.2 en "arriba (1,1)" y "abajo (1,1)".

  4. Complejidad de la red

    Podrías entrenar exitosamente una red para cada tarea por separado. Ahora quieres apilarlos . Este es el objetivo del aprendizaje profundo : cada capa se basa en los conceptos percibidos por la capa anterior, lo que aumenta la complejidad de los conceptos que puede aprender.

    Entonces, en lugar de usar 20 nodos en una sola capa, recomiendo que uses 2 capas de 10 nodos. Esto coincide con la jerarquía de clases que mencioné en el punto anterior.

El código

Al ejecutar este código tuve un error de entrenamiento / prueba 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; }

Más observaciones

Como mencioné en los comentarios y en el chat, no hay tal cosa como "ángulo entre (x, y) y (0,0)", porque la noción de ángulo entre vectores se toma generalmente como la diferencia entre sus direcciones y (0,0) no tiene dirección.

Su función angleToPoint(p1, p2) devuelve en su lugar la dirección de (p1-p2). Para p2 = (0,0) , eso significa que el ángulo entre p1 y el eje x bien. Pero para p1 = (1,1) y p2 = (1,0) no retornará 45 grados. Para p1 = p2, es indefinido en lugar de cero.