Может кто-нибудь сказать мне, что не так с этой реализацией обратного распространения

Поэтому я пытаюсь реализовать нейронную сеть обратного распространения в C#. И я наткнулся на сбой. При обучении сети все выходы либо 0,49???..., либо 0,51???...

Вот мой класс сети

namespace BackPropNetwork
{
public class Network 
{
    public double[][] Values { get; set; }
    public double[][] Deltas { get; set; }
    public double[][][] Weights { get; set; }
    public Network(params int[] size)
    {
        Values = new double[size.Length][];
        Weights = new double[size.Length][][];
        Deltas = new double[size.Length][];
        Random r = new Random();
        for(int i = 0; i < size.Length; i++)
        {
            Values[i] = new double[size[i]];
            Weights[i] = new double[size[i]][];
            Deltas[i] = new double[size[i]];
            if (i != size.Length - 1) {
                for (int j = 0; j < size[i]; j++)
                {
                    Weights[i][j] = new double[size[i + 1]];
                    for(int k= 0; k < size[i + 1]; k++)
                    {
                        Weights[i][j][k] = r.NextDouble() ;
                    }
                }
            }
        }
    }
    public double[] FeedThrough (double[] input)
    {
        if(input.Length!= Values[0].Length)
        {
            throw new InvalidOperationException();
        }
        Values[0] = input;
        for(int i = 0; i < Values.Length-1; i++)
        {
            for(int j = 0; j < Values[i + 1].Length; j++)
            {
                Values[i + 1][j] = Sigmoid(GetPassValue(i, j),false);
            }
        }
        return Values[Values.Length - 1];
    }
    double GetPassValue(int layer,int neuron)
    {
        double sum = 0;
        for(int i = 0; i < Values[layer].Length; i++)
        {
            sum += Values[layer][i] * Weights[layer][i][neuron];
        }
        return sum;
    }
    public double Sigmoid(double d, bool dir)
    {
        if (dir)
        {
            return d * (1 - d);
        }else
        {
            return 1 / (1 + Math.Exp(d));
        }
    }
    public void CorrectError(double[] error)
    {
        for(int i = Values.Length - 1; i >= 0; i--)
        {

            if (i !=Values.Length - 1)
            {
                error = new double[Values[i].Length];
                for(int j = 0; j < Values[i].Length; j++)
                {
                    error[j] = 0;
                    for(int k = 0; k < Values[i + 1].Length; k++)
                    {
                        error[j] += Weights[i][j][k] * Deltas[i + 1][k];
                    }
                }    
            }

            for(int j = 0; j < Values[i].Length; j++)
            {
                Deltas[i][j] = error[j] * Sigmoid(Values[i][j],true);
            }

        }

    }
    public void ApplyCorrection(double rate)
    {
        for(int i = 0; i < Values.Length-1; i++)
        {
            for(int j = 0; j < Values[i].Length; j++)
            {
                for(int k = 0; k < Values[i + 1].Length; k++)
                {
                    Weights[i][j][k] = rate * Deltas[i + 1][k] * Values[i][j];
                }
            }
        }
    }
}

}

и вот мой класс тестера:

namespace BackPropagationTest
{
class Program
{
    static void Main(string[] args)
    {
        Network n = new Network(3, 5, 5, 1);
        double[][] input = new double[][] { new double[] { 1, 0, 1 }, new double[] { 1, 1, 1 }, new double[] { 0, 0, 0 }, new double[] {0, 1, 0 } };
        double[][] output = new double[][] { new double[] { 0 },new double[] { 1 }, new double[] { 0 }, new double[] { 0 } };
        for (int i = 0; i < 10; i++)
        {
            for(int j = 0; j < input.Length; j++)
            {
                var x = n.FeedThrough(input[j]);
                double[] error = new double[output[0].Length];
                for(int k= 0; k < x.Length; k++)
                {
                    error[k] = output[j][k] - x[k];
                }
                n.CorrectError(error);
                n.ApplyCorrection(0.01);
                for(int k = 0; k < x.Length; k++)
                {
                     Console.Write($"Expected: {output[j][k]} Got: {x[k]} ");
                }
                Console.WriteLine();


            }
            Console.WriteLine();

        }
    }
}

}

и вот мой вывод:

Expected: 0 Got: 0.270673949003643
Expected: 1 Got: 0.500116517554687
Expected: 0 Got: 0.499609458404919
Expected: 0 Got: 0.50039031963377

Expected: 0 Got: 0.500390929619276
Expected: 1 Got: 0.500390929999612
Expected: 0 Got: 0.499609680732027
Expected: 0 Got: 0.500390319841144

Expected: 0 Got: 0.50039092961941
Expected: 1 Got: 0.500390929999612
Expected: 0 Got: 0.499609680732027
Expected: 0 Got: 0.500390319841144

Expected: 0 Got: 0.50039092961941
Expected: 1 Got: 0.500390929999612
Expected: 0 Got: 0.499609680732027
Expected: 0 Got: 0.500390319841144

И так продолжается вечно.

Изменить 1:

Я внес изменение в функцию ApplyCorrection(), где я заменил

 Weights[i][j][k] = rate * Deltas[i + 1][k] * Values[i][j];

с `

 Weights[i][j][k] += rate * Deltas[i + 1][k] * Values[i][j];

и теперь весы, кажется, обновляются. но я все еще подвергаю сомнению правильность этой реализации. Ака еще нужна помощь:)

Изменить 2:

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

И я также изменил выходную пару с (0,1) на (-1, 1), пытаясь увеличить вычисленные значения ошибок. Это после 1000000 эпох при скорости обучения 0,1:

Expected: -1 Got: 0.999998429209274 Expected: 1 Got: 0.999997843901661 Expected: -1 Got: 0.687098308461306 Expected: -1 Got: 0.788960893508226 Expected: -1 Got: 0.999998429209274 Expected: -1 Got: 0.863022549216158 Expected: -1 Got: 0.788960893508226 Expected: -1 Got: 0.999998474717769

1 ответ

Решение

Попробуйте поиграть с чем-то, как показано ниже, и убедитесь, что ошибка уменьшается или остается такой же.

public double Sigmoid(double d, bool dir)
{
    if (dir)
    {
        return d * (1 - d);
    }else
    {
        if (d < -45.0) return 0.0;
        else if (d > 45.0) return 1.0;
        else return 1.0 / (1.0 + Math.Exp(-d));
    }
}
Другие вопросы по тегам