Подтвердить что ты не робот

Получение простой нейронной сети для работы с нуля в С++

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

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

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

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

4b9b3361

Ответ 2

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

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

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

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 и удачи.

Ответ 3

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

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

Ответ 4

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

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

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

Надеюсь, что это поможет! Ура! ^ _ ^



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

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

Алгоритм:

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

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

  • Нам понадобится функция для вычисления сетевого ввода (e.i * x * 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:

};

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

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

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

Мы начнем с конструктора (персептрона (float eta, int epochs);)

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 (float netInput (вектор X);)

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

* x * wT *

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

* (x1 * w1 + x2 * w2 +... + xn * wn) + смещение *

* bias = 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;
}

Способ импортирования набора данных диафрагмы не идеален, но я просто хотел, чтобы что-то сработало.

Файлы данных можно найти здесь.

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