Проблема с нейросетью C#
Вот код:
public class NeuralNetwork
{
public int[] Layers;
private float[][] Neurons;
private float[][] B;
private float[][][] Weights;
public float LearningRate = 0.01f;
public NeuralNetwork(params int[] sizes)
{
var r = new Random();
Layers = sizes.ToArray();
Neurons = new float[sizes.Length][];
B = new float[sizes.Length][];
Weights = new float[sizes.Length - 1][][];
for (int i = 0; i < sizes.Length; i++)
{
Neurons[i] = new float[sizes[i]];
B[i] = new float[sizes[i]];
if (i != 0)
for (int j = 0; j < sizes[i]; j++)
B[i][j] = (float)(r.NextDouble() * (double)r.Next());
}
for (int i = 0; i < sizes.Length - 1; i++)
{
Weights[i] = new float[sizes[i]][];
for (int j = 0; j < sizes[i]; j++)
{
Weights[i][j] = new float[sizes[i + 1]];
for (int k = 0; k < sizes[i + 1]; k++)
Weights[i][j][k] = (float)(r.NextDouble() * (double)r.Next());
}
}
}
public NeuralNetwork(float learningRate, int[] layers, float[][] b, float[][][] weights)
{
LearningRate = learningRate;
Layers = layers;
B = b;
Weights = weights;
Neurons = new float[layers.Length][];
for (int i = 0; i < Neurons.Length; i++)
Neurons[i] = new float[layers[i]];
}
public float[] Forward(params float[] input)
{
if (input.Length != Neurons[0].Length) throw new ArgumentException();
Neurons[0] = input.Select(x => 1 / (1 + MathF.Exp(-x))).ToArray();
for (int i = 1; i < Neurons.Length; i++)
{
for (int j = 0; j < Neurons[i].Length; j++)
{
Neurons[i][j] = 0;
for (int k = 0; k < Neurons[i - 1].Length; k++)
Neurons[i][j] += Neurons[i - 1][k] * Weights[i - 1][k][j];
Neurons[i][j] = 1 / (1 + MathF.Exp(-(Neurons[i][j] + B[i][j])));
}
}
return Neurons[Neurons.Length - 1].ToArray();
}
public void Backward(float[] input, float[] output)
{
float[] errors = new float[output.Length];
float[] outputNet = Forward(input);
for (int i = 0; i < errors.Length; i++)
errors[i] = output[i] - outputNet[i];
for (int i = Neurons.Length - 2; i >= 0; i--)
{
float[] errorsNext = new float[Neurons[i].Length];
float[] gradient = new float[Neurons[i + 1].Length];
float[][] deltas = new float[Neurons[i + 1].Length][];
for (int j = 0; j < Neurons[i + 1].Length; j++)
{
gradient[j] = errors[j] * Neurons[i + 1][j] * (1 - Neurons[i + 1][j]) * LearningRate;
B[i + 1][j] += gradient[j];
}
for (int j = 0; j < Neurons[i + 1].Length; j++)
{
deltas[j] = new float[Neurons[i].Length];
for (int k = 0; k < Neurons[i].Length; k++)
deltas[j][k] = gradient[j] * Neurons[i][k];
}
for (int j = 0; j < Neurons[i].Length; j++)
{
for (int k = 0; k < Neurons[i + 1].Length; k++)
errorsNext[j] += Weights[i][j][k] * errors[k];
}
errors = errorsNext;
for (int j = 0; j < Neurons[i + 1].Length; j++)
for (int k = 0; k < Neurons[i].Length; k++)
Weights[i][k][j] = Weights[i][k][j] + deltas[j][k];
}
}
public (float[][][], float[][], int[], float) GetNetworkData() => (Weights.ToArray(), B.ToArray(), Layers.ToArray(), LearningRate);
}
Проблема в том, что при прямом проходе все нейроны равны 1, поэтому после функции деактивации Neurons[i + 1][j] * (1 - Neurons[i + 1][j]) он возвращает 0 (такая проблема возникла в нейросети[784;16;16;10] и в нейросети[784;512;128;32;10], в нейронных сетях для решения задач XOR,NXOR и в задаче о виде ириса с нейросетью[4;5;3] такой проблемы не возникло). Использую функцию сигмойда. Как это исправить?
Для обучения нейросети использовал базу рукописных цифр MNIST датасет Вот код который читает эту базу
public static class P
{
public static List<(float[],float[])> Read()
{
var images = new List<(float[], float[])>(60000);
var a = new BinaryReader(new FileStream("train-labels.idx1-ubyte", FileMode.Open));
var b = new BinaryReader(new FileStream("train-images.idx3-ubyte", FileMode.Open));
var m = a.ReadInt32();
var f = a.ReadInt32();
b.ReadInt32();
b.ReadInt32();
b.ReadInt32();
b.ReadInt32();
for (int i = 0; i < 60000; i++)
{
var pixels = new float[28*28];
for (int j = 0; j < 28; j++)
{
for (int k = 0; k < 28; k++)
{
var v=(float)(b.ReadByte());
pixels[j+k*28] = v;
}
}
var t=new float[10];
t[a.ReadByte()]=1f;
images.Add((pixels,t));
}
return images;
}
}
Дополнительные медоты к нейронке
public static void Save(this NeuralNetwork Net, string name)
{
var NetData = Net.GetNetworkData();
List<string> NetMemory = new List<string>() { string.Join(";", NetData.Item3), NetData.Item4.ToString() };
foreach (var i in NetData.Item1)
foreach (var j in i)
NetMemory.Add(string.Join(";", j));
foreach (var i in NetData.Item2)
NetMemory.Add(string.Join(";", i));
File.WriteAllLines(name + " (" + string.Join("; ", Net.Layers) + ").txt", NetMemory);
}
public static NeuralNetwork Load(string name)
{
string[] NetworkMemoryFile = File.ReadAllLines(name + ".txt");
int[] layers = NetworkMemoryFile[0].Split(';').Select(x => int.Parse(x)).ToArray();
float learningRate = float.Parse(NetworkMemoryFile[1]);
float[][][] weights = new float[layers.Length - 1][][];
int weightsI = 0;
for (int i = 0; i < layers.Length - 1; i++)
{
weights[i] = new float[layers[i]][];
for (int j = 0; j < layers[i]; j++)
weights[i][j] = NetworkMemoryFile[2 + weightsI++].Split(";").Select(x => float.Parse(x)).ToArray();
}
float[][] b = new float[layers.Length][];
int bI = 0;
for (int i = 0; i < layers.Length; i++)
b[i] = NetworkMemoryFile[2 + weightsI + bI++].Split(";").Select(x => float.Parse(x)).ToArray();
return new NeuralNetwork(learningRate, layers, b, weights);
}
А вот обучение и тестирование
public static void Main()
{
var data = P.Read();
//var a=new NeuralNetwork(784,16,16,10);
//a.Save("MNIST");
var a = NeuralNetworkExpansion.Load("MNIST (784; 16; 16; 10)");
for (int j = 0; j < 1; j++)
{
foreach (var i in data)
a.Backward(i.Item1, i.Item2);
a.Save("MNIST");
Console.WriteLine(j);
}
foreach (var i in data.Take(100))
{
Console.WriteLine(string.Join("; ", a.Forward(i.Item1).Select(x => Math.Round(x, 2))));
Console.WriteLine(string.Join("; ", i.Item2));
}
}
Попробовал изменить функцию на RELu и Tanh, такая же ситуация
Ответы (1 шт):
В общем и целом, я нашёл как исправить проблему: сделал так чтобы веса и смещения при создании могли быть отрицательными, а так же отредактировал данные для обучения (1 если значение пикселя больше 200, и 0 в ином случае);