Нейронная сеть обратного распространения не учится должным образом

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

Это моя функция ошибок L = plogq + (1−p)log(1−q)

И моя функция активации f(x)=1/(1+e^(-x))

Я пытаюсь решить простую задачу x > y, для которой на самом деле не нужен многослойный персептрон.

#include <bits/stdc++.h>

using namespace std;
const double ETA=0.001;
const double EPS=0.000001;
const bool OUTPUT=true;
typedef function<double(vector<double>, vector<double>)> func;
double scalar_product(vector<double> a, vector<double> b)
{
    assert(a.size() == b.size());
    double ret = 0.0;
    for (int i=0; i<a.size(); i++)
    {
        ret += a[i] * b[i];
    }
    return ret;
}
class Perceptron
{
public:
    int n;
    double val,der;
    double delta;
    vector<int> next;
    vector<double> w;
    function<double(vector<double>, vector<double>)> h;
    function<double(vector<double>, vector<double>)> d;

    Perceptron(int n,func h,func d):n(n),h(h),d(d)
    {
        for(int i=0; i<n+1; i++)
            w.push_back(1.0*rand()/RAND_MAX);
    }
    double calc(vector<double> x)
    {
        val=h(w,x);
        der=d(w,x);
        return val;
    }
};
class NeuralNetwork
{
public:
    int inputLayer,hiddenLayer,outputLayer;
    vector<Perceptron> inputNeurons;
    vector<Perceptron> hiddenNeurons;
    vector<Perceptron> outputNeurons;
    NeuralNetwork(int in,int hid,int out)
    {
        inputLayer=in;
        hiddenLayer=hid;
        outputLayer=out;

        auto logistic = [] (vector<double> w, vector<double> x) -> double
        {
            x.push_back(1.0);
            return 1.0 / (1.0 + exp(-scalar_product(w, x)));
        };
        auto d_logistic = [logistic] (vector<double> w, vector<double> x) -> double
        {
            double lst = logistic(w, x);
            return lst * (1.0 - lst);
        };

        auto input = [] (vector<double> w, vector<double> x) -> double
        {
            return x[0];
        };
        auto d_input = [] (vector<double> w, vector<double> x) -> double
        {
            return 1.0;
        };

        auto ident = [] (vector<double> w, vector<double> x) -> double
        {
            x.push_back(1.0);
            return scalar_product(w, x);
        };
        auto d_ident = [] (vector<double> w, vector<double> x) -> double
        {
            return 1.0;
        };

        for(int i=0; i<inputLayer; i++)
            inputNeurons.push_back(Perceptron(1,input,d_input));
        if(OUTPUT)cout<<"Created "<<inputLayer<<" input neurons."<<endl;
        for(int i=0; i<hiddenLayer; i++)
            hiddenNeurons.push_back(Perceptron(inputLayer,logistic,d_logistic));
        if(OUTPUT)cout<<"Created "<<hiddenLayer<<" hidden neurons."<<endl;
        for(int i=0; i<outputLayer; i++)
            outputNeurons.push_back(Perceptron(hiddenLayer,logistic,d_logistic));
        if(OUTPUT)cout<<"Created "<<outputLayer<<" output neurons."<<endl;

    }
    vector<double> feedForward(vector<double> x)
    {
        for(int i=0; i<inputLayer; i++)
            inputNeurons[i].calc({x[i]});
        vector<double> inputLayerOut;
        for(int i=0; i<inputLayer; i++)
            inputLayerOut.push_back(inputNeurons[i].val);

        for(int i=0; i<hiddenLayer; i++)
            hiddenNeurons[i].calc(inputLayerOut);
        vector<double> hiddenLayerOut;
        for(int i=0; i<hiddenLayer; i++)
            hiddenLayerOut.push_back(hiddenNeurons[i].val);

        for(int i=0; i<outputLayer; i++)
            outputNeurons[i].calc(hiddenLayerOut);
        vector<double> outputLayerOut;
        for(int i=0; i<outputLayer; i++)
            outputLayerOut.push_back(outputNeurons[i].val);
        return outputLayerOut;
    }
    void backPropagation(vector<vector<double> > x, vector<vector<double> > y, int max_steps)
    {
        double diff;
        do
        {
            diff=0.0;
            for(int t=0; t<x.size(); t++)
            {
                vector<double> out=feedForward(x[t]);
                for(int i=0; i<outputLayer; i++)
                    outputNeurons[i].delta=(y[t][i]-outputNeurons[i].val);
                for(int i=0; i<hiddenLayer; i++)
                {
                    double sum=0.0;
                    for(int j=0; j<outputLayer; j++)
                        sum+=outputNeurons[j].delta*hiddenNeurons[i].w[j];
                    hiddenNeurons[i].delta=hiddenNeurons[i].der*sum;
                }
            }
            for(int i=0; i<outputLayer; i++)
            {
                for (int j=0; j<outputNeurons[i].w.size(); j++)
                {
                    double z = (j < outputNeurons[i].n) ? hiddenNeurons[j].val : 1.0;
                    double curr = ETA * outputNeurons[i].delta * z;
                    outputNeurons[i].w[j] += curr;
                    diff += curr * curr;
                }
            }
            for(int i=0; i<hiddenLayer; i++)
            {
                for (int j=0; j<hiddenNeurons[i].w.size(); j++)
                {
                    double z = (j < hiddenNeurons[i].n) ? inputNeurons[j].val : 1.0;
                    double curr = ETA * hiddenNeurons[i].delta * z;
                    hiddenNeurons[i].w[j] += curr;
                    diff += curr * curr;
                }
            }
            if(OUTPUT&&max_steps%100==0)cout<<"Current diff: "<<diff<<endl;
        }
        while(diff>EPS&&max_steps--);
    }

};

int main()
{
    //srand(time(NULL));
    NeuralNetwork nn=NeuralNetwork(2,5,1);
    vector<vector<double> > trainingInput;
    vector<vector<double> > trainingOutput;
    trainingInput.resize(100);
    trainingOutput.resize(100);
    for(int i=0; i<100; i++)
    {
        int x=rand()%100;
        int y=rand()%100;
        trainingInput[i].push_back(x);
        trainingInput[i].push_back(y);
        trainingOutput[i].push_back(x>y?1:0);
    }
    nn.backPropagation(trainingInput,trainingOutput,10000);
    while(true)
    {
        int x,y;
        cin>>x>>y;
        cout<<nn.feedForward({x,y})[0]<<endl;
    }
    return 0;
}

person Filip V    schedule 20.06.2016    source источник
comment
Но проблема в том, что я не совсем понимаю обратное распространение.   -  person Filip V    schedule 21.06.2016
comment
Я не уверен, что мои формулы верны.   -  person Filip V    schedule 21.06.2016
comment
Я разместил весь код только для понимания понятий, проблема где-то в функции backPropagation.   -  person Filip V    schedule 21.06.2016
comment
Что ж, как уже упоминалось, используйте отладчик, сделайте шаг в этот момент и проверьте текущие значения переменных:   -  person πάντα ῥεῖ    schedule 21.06.2016
comment
@πάνταῥεῖ Не задавайте вопросы — это бесполезный совет на сайте, посвященном вопросам.   -  person Robert Dodier    schedule 21.06.2016
comment
Но я не совсем понимаю математику и не могу знать, какие значения переменных допустимы, а какие нет.   -  person Filip V    schedule 21.06.2016
comment
Не по теме: Обычный комментарий. stackoverflow.com/questions/1452721/ и stackoverflow.com/questions/31816095/   -  person user4581301    schedule 21.06.2016
comment
оффтоп: Ну это моя привычка от соревновательных навыков программирования.   -  person Filip V    schedule 21.06.2016
comment
Веса и ввод функции активации могут быть отрицательными, даже если окончательная активация не является отрицательной. Поэтому, когда вы инициализируете свои веса, половина возможного диапазона должна быть отрицательной. Кроме того, мне не нравятся большие начальные веса со средним абсолютным значением 0,5. Это просто помещает большинство начальных активаций на один из крайних значений сигмоиды, насыщенных при 0 или 1, и с производной где-то близкой к нулю, поэтому обновления будут изменять веса очень мало или не изменят совсем. Ваши начальные веса при типичных входных данных должны давать активацию от 0,1 до 0,9, поэтому уменьшите их.   -  person Christopher Oicles    schedule 21.06.2016


Ответы (1)


Для кода обратного распространения ключевым моментом является правильное вычисление градиента. Вы можете проверить градиент, вычислив числовое приближение и сравнив его. Числовая аппроксимация градиента — это просто (F(x; w + dw) - F(x; w))/dw, где x — входной вектор (может быть что угодно, не имеет значения для этой цели), w — один из весов или смещений, а dw — небольшое число (например, 1e-4) . Для каждого веса и смещения вычислите эту разницу, а затем сравните вектор приближений с тем, что вы получаете из своего кода. Они должны быть примерно одинаковыми и должны сближаться по мере того, как вы уменьшаете dw.

person Robert Dodier    schedule 20.06.2016
comment
(F(x; w + dw) - F(x; w))/dw является производным определением. Я не знаю, где это применить и откуда код? - person Filip V; 21.06.2016
comment
У вас есть материалы, которые хорошо описывают обратное распространение? - person Filip V; 21.06.2016
comment
Взгляните, скажем, на «Распознавание образов и нейронные сети» Брайана Рипли и/или «Элементы статистического обучения» Хасти, Тибширани и Фридмана. - person Robert Dodier; 21.06.2016