当错误梯度的当前偏导数等于0

时间:2017-09-25 10:17:08

标签: c# machine-learning neural-network .net-core backpropagation

我目前正在实施RPROP算法,并且在解决XOR问题时发现它无法正常工作。在我看来,这是因为当您给神经网络输入值时,输入神经元中的2个输出为0.因此,当您计算该神经元的误差率的偏导数时,您得到0值。这意味着当RPROP算法运行时,它会计算当前偏导数对前一个偏导数的符号,这表示符号正确为0。因此,不计算Δ,但是前一个与当前偏导数的符号变化一起使用。这会导致更新值为0,因此算法失败。

我想知道是否有人能告诉我到底出错了什么,因为我已经阅读了算法并看到了安装实施,我仍然在努力解决这个问题。

为隐藏图层计算误差梯度:

public void SetNeuronErrorGradient(Neuron neuron)
    {
        neuron.ErrorGradient = neuron.OutputSynapses.Sum(a => a.OutputNeuron.ErrorGradient * a.Weight) *
                           neuron.ActivationFunction.Derivative(neuron.LatestFedValueFromInputSynapses);
    }

输出图层的误差梯度计算如下:

public void SetNeuronErrorGradient(Neuron neuron, double target)
{
    neuron.ErrorGradient = CalculateErrorForOutputAgainstTarget(neuron, target) *
                       neuron.ActivationFunction.Derivative(neuron.LatestFedValueFromInputSynapses);
}

然后按如下方式实现RPROP算法,其中Synapse包含输入神经元,输出神经元以及权重和权重增量:

public sealed partial class ResilientBackPropagationSynapseWeightCalculator : IUpdateSynapseWeights
{
    private static ILogger Log => LoggerProvider.For<ResilientBackPropagationSynapseWeightCalculator>();

    private readonly Dictionary<Synapse, double> synapseToPreviousPartialDerivative
        = new Dictionary<Synapse, double>(SynapseEqualityComparer.Instance());

    private readonly Dictionary<Synapse, double> synapseToPreviousDeltaWithoutDirection
        = new Dictionary<Synapse, double>(SynapseEqualityComparer.Instance());

    private ResilientBackPropagationSynapseWeightCalculator()
    {}

    public void CalculateAndUpdateInputSynapseWeights(Neuron neuron, ParallelOptions parallelOptions)
    {
        neuron.InputSynapses.ForEach(
            synapse =>
            {
                var previousPartialDerivative = GetPreviousPartialDerivativeOfSynapse(synapse);
                var currentPartialDerivative = synapse.OutputNeuron.ErrorGradient * synapse.InputNeuron.Output;

                var weightDelta = CalculateSynapseWeightDelta(
                    synapse,
                    currentPartialDerivative,
                    previousPartialDerivative);

                synapse.WeightDelta = weightDelta;
                synapse.Weight = synapse.Weight + weightDelta;
            });
    }

    private double GetPreviousPartialDerivativeOfSynapse(Synapse synapse)
    {
        if (synapseToPreviousPartialDerivative.TryGetValue(synapse, out var previousPartialDerivative))
        {
            return previousPartialDerivative;
        }

        synapseToPreviousPartialDerivative[synapse] = 0;
        return 0;
    }

    private double CalculateSynapseWeightDelta(
        Synapse synapse,
        double currentPartialDerivative,
        double previousPartialDerivative)
    {
        var errorGradientSign =
            ResilientBackPropagationHelper.Sign(currentPartialDerivative * previousPartialDerivative);

        double weightDelta;

        if (errorGradientSign > 0)
        {
            weightDelta = ResilientBackPropagationHelper
                .CalculateDeltaToContinueTowardsErrorGraidentMinimum(
                    synapse,
                    currentPartialDerivative,
                    synapseToPreviousDeltaWithoutDirection);

            synapseToPreviousPartialDerivative[synapse] = currentPartialDerivative;
        }
        else if (errorGradientSign < 0)
        {
            weightDelta = ResilientBackPropagationHelper
                .CalculateDeltaToRevertPreviousWeightAdjustment(
                    synapse,
                    currentPartialDerivative,
                    synapseToPreviousDeltaWithoutDirection);

            synapseToPreviousPartialDerivative[synapse] = 0; //0 so no adjustment next iteration.
        }
        else
        {
            weightDelta = ResilientBackPropagationHelper
                .CalculateDeltaDirection(
                    synapse,
                    currentPartialDerivative,
                    synapseToPreviousDeltaWithoutDirection);

            synapseToPreviousPartialDerivative[synapse] = currentPartialDerivative; 
        }

        return weightDelta;
    }

    public static ResilientBackPropagationSynapseWeightCalculator Create()
        => new ResilientBackPropagationSynapseWeightCalculator();
}

将辅助函数类实现为:

internal static class ResilientBackPropagationHelper
{
    private const double NegativeWeightUpdateAmount = 0.5;
    private const double PositiveWeightUpdateAmount = 1.2;
    private const double MaximumWeightUpdate = 50.0;
    private const double MinimumWeightUpdate = 1.0E-6;
    private const double InitialUpdateValue = 0.1;

    private const double ZeroTolerance = 0.00000000000000001d;

    public static int Sign(double value)
    {
        if (Math.Abs(value) < ZeroTolerance)
        {
            return 0;
        }
        if (value > 0)
        {
            return 1;
        }
        return -1;
    }

    public static double CalculateDeltaToContinueTowardsErrorGraidentMinimum(
        Synapse synapse,
        double currentPartialDerivative,
        Dictionary<Synapse, double> synapseToPreviousDeltaWithoutDirection)
    {
        if (synapseToPreviousDeltaWithoutDirection.TryGetValue(synapse, out var previousUpdateValue))
        {
            var delta = Math.Min(previousUpdateValue * PositiveWeightUpdateAmount, MaximumWeightUpdate);
            synapseToPreviousDeltaWithoutDirection[synapse] = delta;
            return Sign(currentPartialDerivative) * delta;
        }

        throw new InvalidOperationException($"You cannot increase a prevous delta is none is present.");
    }

    public static double CalculateDeltaToRevertPreviousWeightAdjustment(
        Synapse synapse,
        double currentPartialDerivative,
        Dictionary<Synapse, double> synapseToPreviousDeltaWithoutDirection)
    {
        if (synapseToPreviousDeltaWithoutDirection.TryGetValue(synapse, out var previousUpdateValue))
        {
            var delta = Math.Max(previousUpdateValue * NegativeWeightUpdateAmount, MinimumWeightUpdate);
            synapseToPreviousDeltaWithoutDirection[synapse] = delta;
            return -synapse.WeightDelta;
        }

        throw new InvalidOperationException($"You cannot revert a previous change if none is known.");
    }

    public static double CalculateDeltaDirection(
        Synapse synapse,
        double currentPartialDerivative,
        Dictionary<Synapse, double> synapseToPreviousDeltaWithoutDirection)
    {
        if (synapseToPreviousDeltaWithoutDirection.TryGetValue(synapse, out var previousUpdateValue))
        {
            return Sign(currentPartialDerivative) * previousUpdateValue;
        }

        synapseToPreviousDeltaWithoutDirection.Add(synapse, InitialUpdateValue);

        return CalculateDeltaDirection(
            synapse,
            currentPartialDerivative,
            synapseToPreviousDeltaWithoutDirection);
    }
}

关于我的逻辑失败的地方的任何帮助都会非常感激,因为我已经尝试了很多方法来使这个算法对抗算法。

非常感谢 乔

0 个答案:

没有答案