2010-01-07 59 views
21

He estado tratando de hacer funcionar una red neuronal doble XOR simple y tengo problemas para obtener una propagación inversa para entrenar una red neuronal feed forward muy simple.
He estado intentando principalmente seguir la guía this para obtener una red neuronal pero, en el mejor de los casos, he creado programas que aprenden a una velocidad extremadamente lenta.Obteniendo una red neuronal simple para trabajar desde cero en C++

como lo entiendo redes neuronales:

  1. Los valores se calculan tomando el resultado de una función sigmoide de la suma de todas las entradas a esa neurona. Luego se alimenta a la siguiente capa usando el peso de cada neurona
  2. Al final de la ejecución, el error se calcula para las neuronas de salida, luego usando los pesos, el error vuelve a propagarse simplemente multiplicando los valores y luego sumando a cada neurona
  3. Cuando se calculan todos los errores, los pesos se ajustan por el delta = peso de la conexión * derivada del sigmoide (el valor del peso de la neurona va) * valor de Neuron que la conexión es * error de la neurona * cantidad de error de salida de la neurona va a * beta (una constante de velocidad de aprendizaje)

This es mi actual lodo de código que estoy tratando de conseguir trabajo. Tengo un montón de otros intentos algo mezclados, pero la función de backpropagation principal que estoy tratando de trabajar está en línea 293 en Net.cpp

+0

@Martin nada malo con C++ para esto. – Simon

+0

C/C++ Perceptron: http: // sourceforge.net/projects/ccperceptron/ – SomethingSomething

Respuesta

19

Eche un vistazo a 15 Steps to implement a Neural Network, debería comenzar.

+6

Un enlace a una solución potencial siempre es bienvenido, pero por favor [agregue contexto alrededor del enlace] (// meta.stackoverflow.com/a/8259/169503) para que los demás usuarios tengan una idea de qué es y por qué está allí. Siempre cite la parte más relevante de un enlace importante, en caso de que el sitio objetivo no esté disponible o esté permanentemente fuera de línea. Tenga en cuenta que ser apenas más que un enlace a un sitio externo es una posible razón para [¿Por qué y cómo se eliminan algunas respuestas?] (// stackoverflow.com/help/deleted-answers). – NobodyNada

+0

Tu enlace está muerto. – HolyBlackCat

+0

Aquí está el enlace del archivo: https://web.archive.org/web/20170813072242/http://code-spot.co.za/2009/10/08/15-steps-to-implemented-a- neural-net/ – Justas

6

Me parece que estás luchando con backprop y lo que describes arriba no concuerda exactamente con cómo entiendo que funcione, y tu descripción es un poco ambigua.

Se calcula el término de error de salida para rebobinar como la diferencia entre la predicción y el valor real multiplicado por la derivada de la función de transferencia. Es ese valor de error que luego se propaga hacia atrás. La derivada de un sigmoide se calcula simplemente como y (1-y) donde y es su valor de salida. Hay muchas pruebas de eso disponibles en la web.

Para un nodo en la capa interna, multiplique ese error de salida por el peso entre los dos nodos, y sume todos esos productos como el error total de la capa externa que se propaga al nodo en la capa interna. El error asociado con el nodo interno se multiplica por la derivada de la función de transferencia aplicada al valor de salida original. He aquí algunos pseudocódigo:

total_error = sum(output_errors * weights) 
node_error = sigmoid_derivative(node_output) * total_error 

Este error se propagan entonces hacia atrás de la misma manera la derecha de nuevo a través de los pesos de la capa de entrada.

Los pesos se ajustan utilizando estos términos de error y los valores de salida de los nodos

weight_change = outer_error * inner_output_value 

la tasa de aprendizaje es importante porque el cambio de peso se calcula para cada patrón/fila/observación en los datos de entrada. Desea moderar el cambio de peso para cada fila, de modo que las ponderaciones no se modifiquen indebidamente en ninguna fila y para que todas las filas tengan un efecto sobre las ponderaciones.La tasa de aprendizaje le da eso y ajustar el cambio de peso multiplicando por ella

weight_change = outer_error * inner_output_value * learning_rate 

También es normal que recordar estos cambios entre épocas (iteraciones) y añadir una fracción de la misma al cambio. La fracción añadida se llama ímpetu y se supone que te acelerará a través de las regiones de la superficie de error donde no hay muchos cambios y te ralentizará donde haya detalles.

weight_change = (outer_error*inner_output_value*learning_rate) + (last_change*momentum) 

Existen algoritmos para ajustar la velocidad de aprendizaje y el impulso a medida que avanza la capacitación.

El peso se actualiza a continuación, añadiendo el cambio

new_weight = old_weight + weight_change 

Tenía una mirada a través de su código, pero en lugar de corregir y post que pensé que era mejor para describir la espalda puntal para usted para que pueda codifícalo tú mismo. Si lo entiendes, también podrás sintonizarlo para tus circunstancias.

HTH y buena suerte.

+0

Intento corregir mi código con sus sugerencias (todavía no recibí el impulso), pero sigo teniendo problemas con el sistema backprop. (Al menos para mí) Parece que me dijiste que hiciera lo mismo, pero solo agrupa los números de movimiento en el valor del error. Siento que me falta algo pequeño pero importante y que está causando que mi backprop no funcione. –

+0

He intentado un par de veces averiguar qué está haciendo tu código, pero me he dado por vencido. Creo que el valor de total_error que calcula probablemente sea incorrecto porque lo hace por una capa y llama a DSigmoid dos veces. Te sugiero que hagas una única iteración con 2 filas de datos de entrada en papel o en Excel para que sepas cómo funciona todo. Luego haz que tu red escuche sus pesos para que puedas compararlo con tus cálculos. En ese momento, debe comprender a) lo que se supone que debe suceder yb) lo que está mal con su código. – Simon

+0

Intenté implementar el algoritmo que describió ... pero no está aprendiendo del todo. ¿Te importa mirar? http://stackoverflow.com/questions/37973950/super-minimal-xor-neural-network-cannot-learn-what-did-i-do-wrong?noredirect=1#comment63395814_37973950 – dicroce

4

Qué tal este código de código abierto. Define un simple 1 neta capa oculta (2 entradas y 2 oculto, 1 salida) y resuelve un problema XOR:

http://www.sylbarth.com/mlp.php

5

me escribió un simple un "Tutorial" que se puede comprobar a continuación.

Es una implementación simple del modelo perceptron. Puedes imaginar un perceptrón como una red neuronal con una sola neurona. Hay un código de maldición que puedes probar que escribí en C++. Repaso el código paso a paso para que no tengas problemas.

Aunque el perceptrón no es realmente una "red neuronal", es realmente útil si desea comenzar y puede ayudarlo a comprender mejor cómo funciona una red neuronal completa.

Espero que ayude! ¡Salud!^_^



En este ejemplo voy a ir a través de la implementación del modelo de perceptrón en C++ para que pueda tener una mejor idea de cómo funciona.

Lo primero es una buena práctica para escribir un algoritmo simple de lo que queremos hacer.

Algoritmo:

  1. Hacer un vector de los pesos e inicializar a 0 (No se olvide de añadir el término de sesgo)
  2. seguir ajustando los pesos hasta que consigamos 0 errores o mínima recuento de errores
  3. Haga predicciones sobre datos no vistos.

Al haber escrito un algoritmo super simple, ahora escribamos algunas de las funciones que necesitaremos.

  • Necesitaremos una función para calcular la entrada de la red (p.i * x * wT * multiplicando el tiempo entradas los pesos)
  • una función escalonada de manera que se obtiene una predicción de ya sea 1 o -1
  • y una función que encuentra los valores ideales para los pesos.

Así que sin más preámbulos vamos a entrar en eso.

Vamos a empezar sencilla mediante la creación de una clase perceptrón:

class perceptron 
{ 
public: 

private: 

}; 

Ahora vamos a añadir las funciones que vamos a necesitar.

class perceptron 
{ 
public: 
    perceptron(float eta,int epochs); 
    float netInput(vector<float> X); 
    int predict(vector<float> X); 
    void fit(vector< vector<float> > X, vector<float> y); 
private: 

}; 

Aviso cómo la función encaja toma como argumento un vector de vectores < flotador>. Eso es porque nuestro conjunto de datos de capacitación es una matriz de entradas. Básicamente, podemos imaginar que la matriz como un par de vectores x superó una sobre otra y cada columna de esa matriz es una característica.

Finalmente, agreguemos los valores que nuestra clase necesita tener. Tal como el vector w para sujetar los pesos, el número de épocas que indica el número de pasadas que vamos a hacer sobre el conjunto de datos de entrenamiento. Y la constante eta que es la tasa de aprendizaje de los cuales vamos a multiplicar cada actualización de peso con el fin de hacer que el procedimiento de entrenamiento más rápido marcando este valor hacia arriba o si eta es demasiado alto podemos marcarlo hasta obtener el resultado ideal (para la mayoría de aplicaciones del perceptrón que sugeriría un eta valor de 0,1).

class perceptron 
{ 
public: 
    perceptron(float eta,int epochs); 
    float netInput(vector<float> X); 
    int predict(vector<float> X); 
    void fit(vector< vector<float> > X, vector<float> y); 
private: 
    float m_eta; 
    int m_epochs; 
    vector <float> m_w; 
}; 

Ahora con nuestro conjunto de clases. Es hora de escribir cada una de las funciones.

Vamos a empezar desde el constructor (perceptrón (eta flotador, épocas int);)

perceptron::perceptron(float eta, int epochs) 
{ 
    m_epochs = epochs; // We set the private variable m_epochs to the user selected value 
    m_eta = eta; // We do the same thing for eta 
} 

Como se puede ver lo que vamos a hacer es algo muy simple. Pasemos a otra función simple. La función predecir (int predecir (vector X);). Recuerde que lo que el todo predicen función no se está llevando a la entrada de red y devuelve un valor de 1 si el netInput es mayor que 0 y -1 otherwhise.

int perceptron::predict(vector<float> X) 
{ 
    return netInput(X) > 0 ? 1 : -1; //Step Function 
} 

Tenga en cuenta que utilizamos una instrucción If en línea para hacer nuestras vidas más fáciles. Así es como funciona la instrucción if en línea:

condición?if_true: else

Hasta ahora todo bien. Vamos a pasar a la aplicación de la funciónnetInput (flotador netInput (vector X);)

El netInput hace lo siguiente; multiplica el vector de entrada por la transpuesta del vector de pesos

* x * wT *

En otras palabras, se multiplica cada elemento del vector de entrada x por el correspondiente elemento del vector de pesos w y luego toma su suma y agrega el sesgo.

* (x1 * x2 + w1 w2 * + ... + xn wn *) + sesgo *

* sesgo = 1 * * w0

float perceptron::netInput(vector<float> X) 
{ 
    // Sum(Vector of weights * Input vector) + bias 
    float probabilities = m_w[0]; // In this example I am adding the perceptron first 
    for (int i = 0; i < X.size(); i++) 
    { 
     probabilities += X[i] * m_w[i + 1]; // Notice that for the weights I am counting 
     // from the 2nd element since w0 is the bias and I already added it first. 
    } 
    return probabilities; 
} 

bien por lo que ahora están casi hechas. Lo último que tenemos que hacer es escribir la función de ajuste que modifica los pesos.

void perceptron::fit(vector< vector<float> > X, vector<float> y) 
{ 
    for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term 
    { 
     m_w.push_back(0); // Setting each weight to 0 and making the size of the vector 
     // The same as the number of features (X[0].size()) + 1 for the bias term 
    } 
    for (int i = 0; i < m_epochs; i++) // Iterating through each epoch 
    { 
     for (int j = 0; j < X.size(); j++) // Iterating though each vector in our training Matrix 
     { 
      float update = m_eta * (y[j] - predict(X[j])); //we calculate the change for the weights 
      for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; } // we update each weight by the update * the training sample 
      m_w[0] = update; // We update the Bias term and setting it equal to the update 
     } 
    } 
} 

Así que eso fue esencialmente. ¡Con solo 3 funciones, ahora tenemos una clase de perceptrón de trabajo que podemos usar para hacer predicciones!

En caso de que quiera copiar y pegar el código y probarlo. Aquí está toda la clase (he añadido algunas funciones extra como imprimir el vector de pesos y los errores en cada época, así como añadió la opción de importar los pesos/exportación.)

Aquí está el código:

El cabecera de la clase:

class perceptron 
{ 
public: 
    perceptron(float eta,int epochs); 
    float netInput(vector<float> X); 
    int predict(vector<float> X); 
    void fit(vector< vector<float> > X, vector<float> y); 
    void printErrors(); 
    void exportWeights(string filename); 
    void importWeights(string filename); 
    void printWeights(); 
private: 
    float m_eta; 
    int m_epochs; 
    vector <float> m_w; 
    vector <float> m_errors; 
}; 

El archivo .cpp clase con las funciones:

perceptron::perceptron(float eta, int epochs) 
{ 
    m_epochs = epochs; 
    m_eta = eta; 
} 

void perceptron::fit(vector< vector<float> > X, vector<float> y) 
{ 
    for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term 
    { 
     m_w.push_back(0); 
    } 
    for (int i = 0; i < m_epochs; i++) 
    { 
     int errors = 0; 
     for (int j = 0; j < X.size(); j++) 
     { 
      float update = m_eta * (y[j] - predict(X[j])); 
      for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; } 
      m_w[0] = update; 
      errors += update != 0 ? 1 : 0; 
     } 
     m_errors.push_back(errors); 
    } 
} 

float perceptron::netInput(vector<float> X) 
{ 
    // Sum(Vector of weights * Input vector) + bias 
    float probabilities = m_w[0]; 
    for (int i = 0; i < X.size(); i++) 
    { 
     probabilities += X[i] * m_w[i + 1]; 
    } 
    return probabilities; 
} 

int perceptron::predict(vector<float> X) 
{ 
    return netInput(X) > 0 ? 1 : -1; //Step Function 
} 

void perceptron::printErrors() 
{ 
    printVector(m_errors); 
} 

void perceptron::exportWeights(string filename) 
{ 
    ofstream outFile; 
    outFile.open(filename); 

    for (int i = 0; i < m_w.size(); i++) 
    { 
     outFile << m_w[i] << endl; 
    } 

    outFile.close(); 
} 

void perceptron::importWeights(string filename) 
{ 
    ifstream inFile; 
    inFile.open(filename); 

    for (int i = 0; i < m_w.size(); i++) 
    { 
     inFile >> m_w[i]; 
    } 
} 

void perceptron::printWeights() 
{ 
    cout << "weights: "; 
    for (int i = 0; i < m_w.size(); i++) 
    { 
     cout << m_w[i] << " "; 
    } 
    cout << endl; 
} 

Además, si quieres probar un ejemplo que aquí se muestra un ejemplo que hice:

main.cpp:

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <fstream> 
#include <string> 
#include <math.h> 

#include "MachineLearning.h" 

using namespace std; 
using namespace MachineLearning; 

vector< vector<float> > getIrisX(); 
vector<float> getIrisy(); 

int main() 
{ 
    vector< vector<float> > X = getIrisX(); 
    vector<float> y = getIrisy(); 
    vector<float> test1; 
    test1.push_back(5.0); 
    test1.push_back(3.3); 
    test1.push_back(1.4); 
    test1.push_back(0.2); 

    vector<float> test2; 
    test2.push_back(6.0); 
    test2.push_back(2.2); 
    test2.push_back(5.0); 
    test2.push_back(1.5); 
    //printVector(X); 
    //for (int i = 0; i < y.size(); i++){ cout << y[i] << " "; }cout << endl; 

    perceptron clf(0.1, 14); 
    clf.fit(X, y); 
    clf.printErrors(); 
    cout << "Now Predicting: 5.0,3.3,1.4,0.2(CorrectClass=-1,Iris-setosa) -> " << clf.predict(test1) << endl; 
    cout << "Now Predicting: 6.0,2.2,5.0,1.5(CorrectClass=1,Iris-virginica) -> " << clf.predict(test2) << endl; 

    system("PAUSE"); 
    return 0; 
} 

vector<float> getIrisy() 
{ 
    vector<float> y; 

    ifstream inFile; 
    inFile.open("y.data"); 
    string sampleClass; 
    for (int i = 0; i < 100; i++) 
    { 
     inFile >> sampleClass; 
     if (sampleClass == "Iris-setosa") 
     { 
      y.push_back(-1); 
     } 
     else 
     { 
      y.push_back(1); 
     } 
    } 

    return y; 
} 

vector< vector<float> > getIrisX() 
{ 
    ifstream af; 
    ifstream bf; 
    ifstream cf; 
    ifstream df; 
    af.open("a.data"); 
    bf.open("b.data"); 
    cf.open("c.data"); 
    df.open("d.data"); 

    vector< vector<float> > X; 

    for (int i = 0; i < 100; i++) 
    { 
     char scrap; 
     int scrapN; 
     af >> scrapN; 
     bf >> scrapN; 
     cf >> scrapN; 
     df >> scrapN; 

     af >> scrap; 
     bf >> scrap; 
     cf >> scrap; 
     df >> scrap; 
     float a, b, c, d; 
     af >> a; 
     bf >> b; 
     cf >> c; 
     df >> d; 
     X.push_back(vector <float> {a, b, c, d}); 
    } 

    af.close(); 
    bf.close(); 
    cf.close(); 
    df.close(); 

    return X; 
} 

La forma Importé el conjunto de datos del iris no es realmente ideal, pero yo sólo quería algo que funcionó.

Los archivos de datos se pueden encontrar here.

espero que haya encontrado útil esta información!

+2

basado en gran parte en una pieza de documentación. Por favor, copie las partes relevantes en su respuesta. –

Cuestiones relacionadas