нейронная сеть с++ не обучается на данных с MNISЕ

я писал свою нейросеть на с++, используя датасет MNIST с цифрами. но что-то идет не так, и нейросеть просто не обучается, выдавая рандомные значения на выходе. на вход же я подаю массив нейронов на 784 штуки(28Х28 пикселей в картинках MNIST). затем у меня есть 2 промежуточных слоя на 128 и 16 элементов. как функцию активации я использую RELU, только немного модифицированную(пробовал также с сигмоидой, ничего не вышло). помогите найти ошибку

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



#define _AMOUNT_OF_LAYERS 4
#define _PATH_OF_IMAGES "train-images.idx3-ubyte"
#define _PATH_OF_LABELS "train-labels.idx1-ubyte"
#define _BATCH = 100
#define _SIZES_OF_LAYERS {784,128,16,10};
using namespace std;



struct Layer {
    vector<long double>neurons;
    vector<long double>biases;
    vector<vector<long double>>weights;
};


int fromBigToLittleEndian(int i)
{
    unsigned char ch1, ch2, ch3, ch4;
    ch1 = i & 255;
    ch2 = (i >> 8) & 255;
    ch3 = (i >> 16) & 255;
    ch4 = (i >> 24) & 255;
    return ((int)ch1 << 24) + ((int)ch2 << 16) + ((int)ch3 << 8) + ch4;
}


void readImages(vector<vector<long double> >& Images) {
    ifstream file;
    file.open(_PATH_OF_IMAGES, ios::binary);
    if (file.is_open())
    {
        int magicNumber = 0;
        int numOfImages = 0;
        int Rows = 0;
        int Cols = 0;
        file.read((char*)&magicNumber, sizeof(magicNumber));
        magicNumber = fromBigToLittleEndian(magicNumber);
        file.read((char*)&numOfImages, sizeof(numOfImages));
        numOfImages = fromBigToLittleEndian(numOfImages);
        file.read((char*)&Rows, sizeof(Rows));
        Rows = fromBigToLittleEndian(Rows);
        file.read((char*)&Cols, sizeof(Cols));
        Cols = fromBigToLittleEndian(Cols);
        for (int i = 0; i < numOfImages; i++)
        {
            Images.push_back({});
            for (int r = 0; r < (Rows * Cols); r++)
            {
                unsigned char pixel = 0;
                file.read((char*)&pixel, sizeof(pixel));
                Images[i].push_back(pixel/255.0);
            }
        }
    }

}


void readLabels(vector<long double>& Labels) {
    ifstream file;
    file.open(_PATH_OF_LABELS, ios::binary);
    if (file.is_open())
    {
        int magicNumber = 0;
        int numOfLabels = 0;
        file.read((char*)&magicNumber, sizeof(magicNumber));
        magicNumber = fromBigToLittleEndian(magicNumber);
        file.read((char*)&numOfLabels, sizeof(numOfLabels));
        numOfLabels = fromBigToLittleEndian(numOfLabels);
        for (int i = 0; i < numOfLabels; i++)
        {
            unsigned char label = 0;
            file.read((char*)&label, sizeof(label));;
            Labels.push_back(label);
        }
        cout << magicNumber << endl << numOfLabels << endl;
    }
}



long double sigmoid(long double x) {
    return (1.0 / (1 + exp(-x)));
}


long double modRelu(long double x) {
    if (x < 0) return 0.01 * x;
    if (x >= 0 and x <= 1) return x;
    if (x > 1) return 1 + 0.01 * (x - 1);
}

long double derivative(long double x) {
    if (x < 0) return 0.01;
    if (x >= 0 and x <= 1) return 1;
    if (x > 1) return 0.01;
}

void DoLayers(vector<Layer>& layers, vector<int> sizes) {
    for (int i = 0; i < sizes.size(); i++) {
        int nextsize = 0;
        if (i < sizes.size() - 1) nextsize = sizes[i + 1];
        layers[i].neurons.resize(sizes[i]);
        layers[i].biases.resize(sizes[i]);
        layers[i].weights.resize(sizes[i]);
        for (int q = 0; q < sizes[i]; q++) {
            layers[i].weights[q].resize(nextsize);
        }
        for (int j = 0; j < sizes[i]; j++) {
            if(rand()%2==0)
                layers[i].biases[j] = (rand()%100)/100.0;
            else 
                layers[i].biases[j] = -(rand() % 100)/100.0;
            for (int k = 0; k < nextsize; k++) {
                if (rand() % 2 == 0)
                    layers[i].weights[j][k] = (rand() % 100)/100.0;
                else layers[i].weights[j][k] = -(rand() % 100) / 100.0;
            }
        }
    }
}



void forwardFeeding(vector<long double>input, vector<Layer>& layers) {
    for (int j = 0; j < input.size(); j++) {
        layers[0].neurons[j] = (input[j]);
    }
    for (int i = 1; i < layers.size(); i++) {
        Layer l = layers[i - 1];
        Layer l1 = layers[i];
        for (int j = 0; j < l1.neurons.size(); j++) {
            l1.neurons[j] = 0;
            for (int k = 0; k < l.neurons.size(); k++) {
                layers[i].neurons[j] += layers[i - 1].neurons[k] * layers[i - 1].weights[k][j];
            }
            layers[i].neurons[j] += layers[i].biases[j];
            layers[i].neurons[j] = modRelu(layers[i].neurons[j]);
        }
    }

}






void backPropagation(int predict, vector<Layer>& layers, double learningRate) {
    vector<long double>errors;
    errors.resize(10);
    for (int i = 0; i < 10; i++) {
        if (i == predict) errors[i] = (1.0 - layers[3].neurons[i])*derivative(1.0 - layers[3].neurons[i]);
        else errors[i] = (0.0 - layers[3].neurons[i])*derivative(0.0 - layers[3].neurons[i]);
    }
    for (int k = 2; k >= 0; k--) {
        vector<long double>errsNext;
        vector<long double>gradients;
        errsNext.resize(layers[k].neurons.size());
        gradients.resize(layers[k + 1].neurons.size());
        for (int i = 0; i < layers[k + 1].neurons.size(); i++) {
            gradients[i] = errors[i] * derivative(layers[k + 1].neurons[i]);
            gradients[i] *= learningRate;
        }
        vector<vector<long double> > delta;
        delta.resize(layers[k + 1].neurons.size());
        for (int i = 0; i < layers[k + 1].neurons.size(); i++) {
            delta[i].resize(layers[k].neurons.size());
        }
        for (int i = 0; i < layers[k + 1].neurons.size(); i++) {
            for (int j = 0; j < layers[k].neurons.size(); j++) {
                delta[i][j] = gradients[i] * layers[k].neurons[j];
            }
        }
        for (int i = 0; i < layers[k].neurons.size(); i++) {
            errsNext[i] = 0;
            for (int j = 0; j < layers[k + 1].neurons.size(); j++) {
                errsNext[i] += layers[k].weights[i][j] * errors[j];
            }
        }
        errors.resize(0);
        errors.resize(layers[k].neurons.size());
        for (int i = 0; i < layers[k].neurons.size(); i++) {
            errors[i] = errsNext[i];
        }
        vector<vector<long double> > newWeights;
        newWeights.resize(layers[k].neurons.size());
        for (int i = 0; i < newWeights.size(); i++) {
            newWeights[i].resize(layers[k+1].neurons.size());
        }
        for (int i = 0; i < layers[k+1].neurons.size(); i++) {
            for (int j = 0; j < layers[k].neurons.size(); j++) {
                newWeights[j][i] = (layers[k].weights[j][i] + delta[i][j]);
            }
        }
        for (int i = 0; i < layers[k].weights.size(); i++) {
            for (int j = 0; j < layers[k].weights[i].size(); j++) {
                layers[k].weights[i][j] = newWeights[i][j];
            }
        }
        for (int i = 0; i < layers[k + 1].biases.size(); i++) {
            layers[k + 1].biases[i] += gradients[i];
        }

    }

}


int predict(vector<Layer>& layers) {
    long double maxWeight = 0;
    int digit = 0;
    for (int i = 0; i < 10; i++) {
        if (layers[layers.size() - 1].neurons[i] > maxWeight) {
            maxWeight = layers[layers.size() - 1].neurons[i];
            digit = i;
        }
    }
    return digit;
}



int main() {

    vector<vector<long double> >arrayOfImages;
    vector<long double>arrayOfLabels;
    vector<int> sizes = _SIZES_OF_LAYERS;
    vector<Layer> layers;


    readImages(arrayOfImages);
    readLabels(arrayOfLabels);

    layers.resize(_AMOUNT_OF_LAYERS);

    int batch = 100;


    int count = 0;



    int epochs = 60;
    
    int indexOfImg = 0;
    DoLayers(layers, sizes); 
    for (int i = 0; i < epochs; i++) {
        
        int count = 0;
        long double errSum = 0;
        for (int j = 0; j < batch; j++) {
            int rightDigit = arrayOfLabels[indexOfImg];
            forwardFeeding(arrayOfImages[indexOfImg], layers);
            int predictedDigit = predict(layers);
            if (predictedDigit == rightDigit) count++;

            for (int i = 0; i < 10; i++) {
                if (i == rightDigit) errSum += pow(1.0 - layers[3].neurons[i], 2);
                else errSum += pow(0.0 - layers[3].neurons[i], 2);
            }
            backPropagation(predictedDigit, layers, 0.001);
            indexOfImg++;
        }
        cout << "epoch: " << i << " right: " << count << "/100 " << "error: " << errSum << endl;
    }
}

Ответы (1 шт):

Автор решения: Artem

Всё, я нашел ошибку, не работало из-за того, что в функцию backPropagate не передавал ошибки с полседнего слоя.

→ Ссылка