2010-01-07 4 views
21

Я пытаюсь получить простую двойную нейронную сеть XOR для работы, и у меня возникают проблемы с обратным распространением, чтобы обучить действительно простую нервную сеть с прямой передачей.
Я в основном старался следовать руководству this в получении нейронной сети, но в лучшем случае сделал программы, которые учатся с чрезвычайно медленной скоростью.Получение простой нейронной сети для работы с нуля в C++

Как я понимаю, нейронные сети:

  1. значения вычисляется путем принятия результата функции сигмовидной от суммы всех входов в этот нейрон. Затем он подается на следующий уровень с использованием веса для каждого нейрона.
  2. В конце работы вычисляется ошибка для выходных нейронов, затем с использованием весов ошибка возвращается обратно, просто умножая значения, а затем суммируя по каждый Neuron
  3. Когда все ошибки вычисляются, весы корректируются с помощью дельта = веса соединения * производной сигмоида (значение веса нейрона равно) * значение Нейрона, которое связано с * ошибкой нейрона * количество ошибки выхода нейрона, идущего на * бета (некоторая константа для скорости обучения)

This - это моя текущая ошибка кода, который я пытаюсь получить. У меня есть много других попыток, несколько смешанных, но основная функция backpropagation, которую я пытаюсь получить, - это строка 293 в Net.cpp

+0

@ Мартин ничего плохого с C++ для этого. – Simon

+0

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

ответ

19

Посмотрите на 15 Steps to implement a Neural Network, это должно вас начать.

+6

Ссылка на потенциальное решение всегда приветствуется, но, пожалуйста, [добавьте контекст вокруг ссылки] (// meta.stackoverflow.com/a/8259/169503), чтобы ваши соплеменники имели представление о том, что это и почему оно есть. Всегда указывайте наиболее значимую часть важной ссылки, если целевой сайт недоступен или постоянно находится в автономном режиме. Примите во внимание, что быть более чем ссылкой на внешний сайт_ является возможной причиной [Почему и как удаляются некоторые ответы?] (// stackoverflow.com/help/deleted-answers). – NobodyNada

+0

Ваша ссылка мертва. – HolyBlackCat

+0

Вот ссылка из архива: https://web.archive.org/web/20170813072242/http://code-spot.co.za/2009/10/08/15-steps-to-implemented-a- neural-net/ – Justas

6

Звучит так, как будто вы боретесь с backprop, и то, что вы описали выше, не совсем соответствует тому, как я понимаю, что это работает, и ваше описание немного неоднозначно.

Вы вычисляете коэффициент ошибки вывода для обратной передачи как разность между предсказанием и фактическим значением, умноженным на производную от передаточной функции. Это значение ошибки, которое вы затем распространяете назад. Производная сигмоида вычисляется достаточно просто как y (1-y), где y - ваше выходное значение. Есть много доказательств того, что доступно в Интернете.

Для узла на внутреннем слое вы умножаете эту ошибку вывода на вес между двумя узлами и суммируете все эти продукты как общую ошибку от внешнего слоя, распространяемого на узел во внутреннем слое. Ошибка, связанная с внутренним узлом, затем умножается на производную передаточной функции, примененную к исходному выходному значению. Вот некоторые псевдокод:

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

Эта ошибка затем распространяется назад таким же образом обратно через весов входного слоя.

Веса корректируются с помощью этих условий ошибок и выходных значений узлов

weight_change = outer_error * inner_output_value 

скорость обучения является важным, поскольку изменение веса рассчитывается для каждого шаблона/строки/наблюдения во входных данных. Вы хотите смягчить изменение веса для каждой строки, чтобы весы не подвергались чрезмерному изменению какой-либо одной строкой и чтобы все строки влияли на веса.Скорость обучения дает вам, что и вы регулируете изменение веса путем умножения на него

weight_change = outer_error * inner_output_value * learning_rate 

Это тоже нормально, чтобы помнить эти изменения между эпохами (итераций) и добавить часть его к изменению. Добавленная доля называется импульсом и, как предполагается, ускоряет вас по областям поверхности ошибки, где нет большого изменения и замедляется, когда есть детали.

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

Существуют алгоритмы для корректировки скорости обучения и импульса при продолжении обучения.

вес затем обновляется путем добавления изменения

new_weight = old_weight + weight_change 

Я посмотрел через код, но вместо того, правильно это и пост, что я думал, что это было лучше, чтобы описать назад опору для вас, так что вы можете закодируйте его сами. Если вы это понимаете, вы сможете настроить его и на свои обстоятельства.

HTH и удачи.

+0

Я пытаюсь исправить свой код с вашими предложениями (я еще не получил импульс), но у меня все еще проблема с системой backprop. (По крайней мере для меня) Похоже, вы говорили мне делать то же самое, но просто связываете числа перемещения в значение ошибки. Я чувствую, что мне не хватает чего-то маленького, но важного, и это заставляет мой backprop не функционировать. –

+0

Я пару раз пытался выяснить, что делает ваш код, но отказался. Я думаю, что вычисляемое значение total_error, вероятно, неверно, потому что вы делаете это для слоя и дважды вызываете DSigmoid. Я предлагаю вам сделать одну итерацию с двумя строками входных данных на бумаге или в Excel, чтобы вы знали, как все это работает. Затем заставьте свою сеть выплюнуть весы, чтобы сравнить их с вашими расчетами. На этом этапе вы должны понимать: а) то, что должно случиться, и б) что не так с вашим кодом. – Simon

+0

Я попытался реализовать алгоритм, который вы описали ... но его не совсем учить. Ум взглянуть? http://stackoverflow.com/questions/37973950/super-minimal-xor-neural-network-cannot-learn-what-did-i-do-wrong?noredirect1_comment63395814_37973950 – dicroce

4

Как насчет этого открытого кода. Он определяет простой 1 скрытый слой сети (2 входа, 2 скрытых, 1 выход) и решает проблему XOR:

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

5

Я написал простой «Учебник», который вы можете проверить ниже.

Это простая реализация модели персептрона. Вы можете представить себе персептрон как нейронную сеть с одним нейроном. Существует код проклятия, который вы можете проверить, что я написал на C++. Я прохожу через код шаг за шагом, поэтому у вас не должно быть никаких проблем.

Хотя персептрон на самом деле не является «нейронной сетью», это действительно полезно, если вы хотите начать работу и можете лучше понять, как работает полная нейронная сеть.

Надеюсь, что это поможет! Приветствия!^_^



В этом примере я буду идти через реализацию модели персептрона в C++, так что вы можете получить лучшее представление о том, как она работает.

Прежде всего, это хорошая практика написать простой алгоритм того, что мы хотим сделать.

Алгоритм:

  1. Сделать вектор для весов и инициализировать его в 0 (Не забудьте добавить термин смещения)
  2. Держите корректировки весов, пока мы не получим 0 ошибок или низкий количество ошибок.
  3. Сделайте прогнозы по невидимым данным.

Написав супер простой алгоритм, давайте теперь напишем некоторые из функций, которые нам понадобятся.

  • Нам нужна функция для расчета ввода сети (e.я * х * Wt * умножения времени входы весов)
  • ступенчатой ​​функции, так что мы получаем предсказание либо 1, либо -1
  • И это функция, которая находит идеальные значения для весов.

Итак, без дальнейших церемоний, давайте прямо в него.

Давайте начнем с простого, создав класс перцептрона:

class perceptron 
{ 
public: 

private: 

}; 

Теперь давайте добавим функции, которые нам необходимы.

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: 

}; 

Обратите внимание, как функция подходит принимает в качестве аргумента вектор вектор < поплавка>. Это связано с тем, что наш учебный набор данных является матрицей входных данных. По существу мы можем представить себе, что матрица как пара векторов x укладывает один поверх другого и каждый столбец этой матрицы является признаком.

И наконец, добавим значения, которые должен иметь наш класс. Такие, как вектор ж для удержания веса, количества эпох, который указует количество проходов, что мы будем делать по обучающему набору данных. И постоянная ета который является скоростью обучения которых мы умножаем каждое обновление веса для того, чтобы сделать процедуру обучения быстрее, набрав это значение или если эта- слишком высок, мы можем набрать его вниз получить идеальный результат (для большинства применений персептрона я бы предложил eta значение 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; 
}; 

Теперь с нашим классом. Пришло время написать каждую из функций.

Мы будем исходить из конструктора (персептрона (флоат ETA, внутр эпох);)

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 
} 

Как вы можете видеть, что мы будем делать очень простые вещи. Итак, перейдем к другой простой функции. Функция прогнозирования (int pred (вектор X);). Помните, что то, что все предсказать функция не принимает чистый ввод и возвращает значение 1, если netInput больше, чем 0 и -1 otherwhise.

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

Обратите внимание, что мы использовали заявление inline if, чтобы облегчить нашу жизнь. Вот как работает inline if:

состояние?if_true: else

Пока все хорошо. Давайте перейдем к реализации функции netInput (поплавок netInput (вектор X);)

netInput выполняет следующие действия; умножает входной вектор транспонированной вектора весов

* х * Wt *

Другими словами, она умножает каждый элемент входного вектора х соответствующими элемент вектора весов w, а затем берет свою сумму и добавляет смещение.

* (x1 * w1 + x2 * w2 + ... + х * шп) + смещение *

* смещение = 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; 
} 

Хорошо так мы в настоящее время в значительной степени сделано последнее, что нам нужно сделать, это написать fit функция, которая изменяет вес.

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 
     } 
    } 
} 

Так было по существу. Имея только 3 функции, теперь у нас есть рабочий класс персептрона, который мы можем использовать для прогнозирования!

Если вы хотите скопировать код и попробовать его. Вот весь класс (я добавил некоторые дополнительные функции, такие как печать вектора весов и ошибки в каждой эпохе, а также добавлена ​​возможность импортировать/экспортировать веса.)

Вот код:

класс заголовка:

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

класс .cpp файл с функциями:

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

Кроме того, если вы хотите попробовать пример здесь пример, который я сделал:

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

Как я импортировал ирис набор данных не совсем идеально, но я просто хотел что-то работал.

Файлы данных можно найти here.

Я надеюсь, что вы нашли это полезным!

+2

Этот ответ основанный главным образом на части документации. Скопируйте соответствующие детали в свой ответ. –

Смежные вопросы