Нейросеть выдаёт значения ~0.5. C#

Рейтинг: 2Ответов: 0Опубликовано: 14.03.2023

Провожу попытки реализации нейронной сети прямого распространения, сам подсчёт выхода происходит корректно, но при обучении результат всегда стремится к 0.5 (от 0.45 До 0.55 в зависимости от воли рандома), алгоритм пытался проверять переписывал, смотрел готовые реализации... не помогло, та же история. Так же пробовал менять структуру сети, убирать Bias нейроны, менять функцию активации, всё упирается в некую ошибку в алгоритме, а найти её я не могу, бьюсь уже 3 день. Пользовался алгоритмом обратного распространения Помогите пожалуйста! Код: Класс NeuralNetwork

using System;

namespace NeuralTest4_5
{
    public class NeuralNetwork
    {
        private int LayersCount { get; set; }//Количество слоёв
        private double LearningRate { get; set; }//Скорость обучения
        private double[][][] Weights { get; set; }//Веса нейросети ([LayerIndex][NeuronIndex][WeightIndex])
        private double[][] Inputs { get; set; }//Входы на слой ([LayerIndex][InputIndex])

        private IActivationFunc[][] ActivationFuncs { get; set; }//Функции активации ([LayerIndex][NeuronIndex])

        //индуцированное локальное поле нейрона ([LayerIndex][NeuronIndex])
        //((сумма входов умноженных на веса) / по сути Output нейрона до применения функции активации)
        private double[][] InducedLocalField { get; set; }

        private double[][] Bias { get; set; }//Смещение нейрона ([LayerIndex][NeuronIndex])

        //Локальный градиент ([LayerIndex][NeuronIndex])
        private double[][] LocalGradients { get; set; }

        public double[] FeedForward(double[] input)
        {
            for (int i = 0; i < LayersCount; i++)//Слои
            {
                Inputs[i] = input;
                input = GetOutputFromLayer(input, i);
            }
            return input;
        }

        private double[] GetOutputFromLayer(double[] input, int LayerIndex)
        {
            double[] outputFromLayer = new double[Weights[LayerIndex].Length];
            for (int i = 0; i < Weights[LayerIndex].Length; i++)//Нейроны у текущего слоя
            {
                InducedLocalField[LayerIndex][i] = 0;
                for (int j = 0; j < Weights[LayerIndex][i].Length; j++)//Веса текущего нейрона
                    InducedLocalField[LayerIndex][i] += input[j] * Weights[LayerIndex][i][j];
                InducedLocalField[LayerIndex][i] += Bias[LayerIndex][i];
                if(LayerIndex != 0)
                    outputFromLayer[i] = ActivationFuncs[LayerIndex][i].GetValue(InducedLocalField[LayerIndex][i]);
                else
                    outputFromLayer[i] = InducedLocalField[LayerIndex][i];
            }
            return outputFromLayer;
        }

        public double BackPropogation(double[][] dataset, double[][] expected, int EpochesCount)
        {
            double error = 0.0;
            for (int i = 0; i < EpochesCount; i++)
            {
                for (int j = 0; j < dataset.Length; j++)
                {
                    double[] result = FeedForward(dataset[j]);
                    double[] OutputError = new double[result.Length];

                    for (int k = 0; k < result.Length; k++)//Вычисление ошибки
                        OutputError[k] = expected[j][k] - result[k];

                    BackwardProp(OutputError);
                }
            }
            return error;
        }

        private void BackwardProp(double[] error)
        {
            int LastIndex = LayersCount - 1;
            //Вычисляем локальные градиенты для Output слоя
            for (int i = 0; i < LocalGradients[LastIndex].Length; i++)
                LocalGradients[LastIndex][i] = error[i] * ActivationFuncs[LastIndex][i].GetDX(InducedLocalField[LastIndex][i]);

            //Изменяем веса Output слоя в соответствии с локальным градиентом каждого нейрона
            for (int i = 0; i < Weights[LastIndex].Length; i++)
                for (int j = 0; j < Weights[LastIndex][i].Length; j++)
                    Weights[LastIndex][i][j] += LearningRate * LocalGradients[LastIndex][i] * Inputs[LastIndex][i];

            for (int i = LayersCount - 2; i > 0; i--)//Слои
            {
                for (int j = 0; j < LocalGradients[i].Length; j++)//Нейроны
                {
                    double[] AssociativeWeights = new double[Weights[i + 1].Length];//Веса связанные с данным нейроном в следующем слое
                    for (int k = 0; k < AssociativeWeights.Length; k++)
                        AssociativeWeights[k] = Weights[i + 1][k][j];//Вес нейрона связанного с текущим нейроном

                    double InnerSum = 0.0; //Внутренняя сумма
                    for (int k = 0; k < AssociativeWeights.Length; k++)
                        InnerSum += AssociativeWeights[k] * LocalGradients[i + 1][k];

                    //Вычисляем локальный градиент
                    LocalGradients[i][j] = InnerSum * ActivationFuncs[i][j].GetDX(InducedLocalField[i][j]);
                }

                for (int j = 0; j < Weights[i].Length; j++)
                {
                    for (int k = 0; k < Weights[i][j].Length; k++)
                        Weights[i][j][k] += LearningRate * LocalGradients[i][j] * Inputs[i][k];  
                }
            }
        }

        public NeuralNetwork(double learningRate, params int[] Layers)
        {
            LearningRate = learningRate;

            LayersCount = Layers.Length;

            //Инициализация слоёв
            Weights = new double[Layers.Length][][];
            Inputs = new double[Layers.Length][];
            Inputs[0] = new double[Layers[0]];

            LocalGradients = new double[Layers.Length][];
            ActivationFuncs = new IActivationFunc[Layers.Length][];
            InducedLocalField = new double[Layers.Length][];
            Bias = new double[Layers.Length][];

            //Инициализация нейронов внутри слоёв
            for (int i = 0; i < Layers.Length; i++)
            {
                Weights[i] = new double[Layers[i]][];
                LocalGradients[i] = new double[Layers[i]];
                ActivationFuncs[i] = new IActivationFunc[Layers[i]];
                InducedLocalField[i] = new double[Layers[i]];
                Bias[i] = new double[Layers[i]];
                if (i == 0)
                    Inputs[i] = new double[Layers[i]];
                else
                    Inputs[i] = new double[Layers[i - 1]];
            }

            //Инициализация весов
            for (int i = 0; i < Layers.Length; i++)
            {
                for (int j = 0; j < Layers[i]; j++)
                {
                    if (i == 0)
                        Weights[i][j] = new double[1];
                    else
                        Weights[i][j] = new double[Layers[i - 1]];

                    ActivationFuncs[i][j] = new Tahn();
                }
            }

            Random rnd = new Random();
            for (int i = 0; i < Weights.Length; i++)//Слои
            {
                for (int j = 0; j < Weights[i].Length; j++)//Нейроны
                {
                    if(i == Weights[i].Length - 1)
                        Bias[i][j] = rnd.NextDouble();
                    else
                        Bias[i][j] = rnd.NextDouble();
                    for (int k = 0; k < Weights[i][j].Length; k++)//Веса
                    {
                        if (i == 0)
                            Weights[i][j][k] = 1;//У входного слоя веса = 1;
                        else
                            Weights[i][j][k] = rnd.NextDouble() - 0.5f;//Случайное число от -0.5 до 0.5
                            //Weights[i][j][k] = 0.5;//Случайное число от -0.5 до 0.5
                    }
                }
            }
        }
    }
}

Функции активации

using System;

namespace NeuralTest4_5
{
    public interface IActivationFunc
    {
        double GetValue(double x);
        double GetDX(double x);
    }
    public class SigmoidFunc : IActivationFunc
    {
        public double GetDX(double x)
        {
            double sigmoid = GetValue(x);
            return sigmoid * (1 - sigmoid);
        }

        public double GetValue(double x) => 1.0 / (1.0 + Math.Exp(-x));
    }
    public class Tahn : IActivationFunc
    {
        public double GetDX(double x) => 1 / Math.Pow(Math.Cosh(x), 2);
        public double GetValue(double x) => Math.Tanh(x);
    }
}

и Program

using System;

namespace NeuralTest4_5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            NeuralNetwork net = new NeuralNetwork(0.1,  2,4,1);
            double[][] testValues = new double[4][];
            testValues[0] = new double[] { 0, 0 };
            testValues[1] = new double[] { 1, 0 };
            testValues[2] = new double[] { 0, 1 };
            testValues[3] = new double[] { 1, 1 };

            double[][] ExpectedValues = new double[4][];
            ExpectedValues[0] = new double[] { 0 };
            ExpectedValues[1] = new double[] { 1 };
            ExpectedValues[2] = new double[] { 1 };
            ExpectedValues[3] = new double[] { 0 };

            net.BackPropogation(testValues, ExpectedValues, 1000);

            for (int i = 0; i < testValues.Length; i++)
            {
                double[] result = net.FeedForward(testValues[i]);
                for (int j = 0; j < result.Length; j++)
                    Console.Write(result[j] + "|" + ExpectedValues[i][j] + "\t");
                Console.WriteLine();
            }
        }
    }
}

Если что сделал не так в реализации то подскажите пожалуйста; А так же, если что не так в оформлении вопроса(первый вопрос тут)

Ответы

Ответов пока нет.