Нейросеть выдаёт значения ~0.5. C#
Провожу попытки реализации нейронной сети прямого распространения, сам подсчёт выхода происходит корректно, но при обучении результат всегда стремится к 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();
}
}
}
}
Если что сделал не так в реализации то подскажите пожалуйста; А так же, если что не так в оформлении вопроса(первый вопрос тут)
Источник: Stack Overflow на русском