ARCore之路-计算机视觉之实例(二)

版权声明:David Wang原创ARCore文章,仅供学习研究之用,不得用于任何商业目的,未经授权不得转载! https://blog.csdn.net/yolon3000/article/details/84889697

  在前一节中,我们已编写完成了神经元细胞类(Neuron)和突触类(Synapse),有了这两个基本组件,我们就可以建设神经网络的高楼大厦了。我们的目标是建设一个带一个隐藏层的双层神经网络,当然,各层神经元细胞的个数由使用者自己决定,示意图如下:

DavidWang原创
public class NeuralNet 
{
    public double LearnRate;
    public double Momentum;
    public List<Neuron> InputLayer;
    public List<Neuron> HiddenLayer;
    public List<Neuron> OutputLayer;
    //private static readonly System.Random Random = new System.Random();

    public NeuralNet(int inputSize, int hiddenSize, int outputSize, double? learnRate = null, double? momentum = null)
    {
        LearnRate = learnRate ?? .4;
        Momentum = momentum ?? .9;
        InputLayer = new List<Neuron>();
        HiddenLayer = new List<Neuron>();
        OutputLayer = new List<Neuron>();

        for (var i = 0; i < inputSize; i++)
            InputLayer.Add(new Neuron());

        for (var i = 0; i < hiddenSize; i++)
            HiddenLayer.Add(new Neuron(InputLayer));

        for (var i = 0; i < outputSize; i++)
            OutputLayer.Add(new Neuron(HiddenLayer));
      }
    }

  上面的代码很容易理解,我们定义了如上图示所示的三个层(一个输入层,一个隐藏层,一个输出层),定义了两个变量 LearnRate,Momentum。LearnRate是一个用户决定的训练速度值,越大则训练越早结束,当然精度会受到一定影响,Momentum是一个冲量,这个值用于梯度下降算法中,用于优化梯度下降速度与性能。然后我们定义了一个构造函数,这个构造函数按照用户需求初始化一个双层神经网络,如上图所示。

  在上面我们定义了一个神经网络,但现在这个神经网络除了构建了一张网络以外什么也不做,所以我们需要添加训练神经网络的方法以及在训练完后能对未知输入进行预测的计算方法。我们扩展这个类如下:

public class NeuralNet 
{
    public double LearnRate;
    public double Momentum;
    public List<Neuron> InputLayer;
    public List<Neuron> HiddenLayer;
    public List<Neuron> OutputLayer;
    //private static readonly System.Random Random = new System.Random();

    public NeuralNet(int inputSize, int hiddenSize, int outputSize, double? learnRate = null, double? momentum = null)
    {
        LearnRate = learnRate ?? .4;
        Momentum = momentum ?? .9;
        InputLayer = new List<Neuron>();
        HiddenLayer = new List<Neuron>();
        OutputLayer = new List<Neuron>();

        for (var i = 0; i < inputSize; i++)
            InputLayer.Add(new Neuron());

        for (var i = 0; i < hiddenSize; i++)
            HiddenLayer.Add(new Neuron(InputLayer));

        for (var i = 0; i < outputSize; i++)
            OutputLayer.Add(new Neuron(HiddenLayer));
    }

    public void Train(List<DataSet> dataSets, int numEpochs)
    {
        for (var i = 0; i < numEpochs; i++)
        {
            foreach (var dataSet in dataSets)
            {
                ForwardPropagate(dataSet.Values);
                BackPropagate(dataSet.Targets);
            }
        }
    }

    public void Train(List<DataSet> dataSets, double minimumError)
    {
        var error = 1.0;
        var numEpochs = 0;

        while (error > minimumError && numEpochs < int.MaxValue)
        {
            var errors = new List<double>();
            foreach (var dataSet in dataSets)
            {
                ForwardPropagate(dataSet.Values);
                BackPropagate(dataSet.Targets);
                errors.Add(CalculateError(dataSet.Targets));
            }
            var total = 0.0;
            foreach(var e in errors)
            {
                total += e;
            }
            error = total / errors.Count;
            numEpochs++;
        }
    }

    private void ForwardPropagate(params double[] inputs)
    {
        var i = 0;
        if (i < inputs.Length)
        {
            InputLayer.ForEach(a => a.Value = inputs[i++]);
            HiddenLayer.ForEach(a => a.CalculateValue());
            OutputLayer.ForEach(a => a.CalculateValue());
        }
    }

    private void BackPropagate(params double[] targets)
    {
        var i = 0;
        if (i < targets.Length)
        {
            OutputLayer.ForEach(a => a.CalculateGradient(targets[i++]));
            HiddenLayer.ForEach(a => a.CalculateGradient());
            HiddenLayer.ForEach(a => a.UpdateWeights(LearnRate, Momentum));
            OutputLayer.ForEach(a => a.UpdateWeights(LearnRate, Momentum));
        }
    }

    public double[] Compute(params double[] inputs)
    {
        ForwardPropagate(inputs);
        return OutputLayer.Select(a => a.Value).ToArray();
    }

    private double CalculateError(params double[] targets)
    {
        var i = 0;
        return OutputLayer.Sum(a => Mathf.Abs((float)a.CalculateError(targets[i++])));
    }
}

  在这个神经网络类(NeuralNet)中,我们采用了两种方法来控制训练(Train),一种是使用训练组数来控制,一种是使用最小误差来控制,但训练方法本身是一样的,使用了正向传播结合反向传播来加快收敛速度。Compute()即是在训练完后用于预测未知数的计算方法。
  这里只解释一下反向传播,反向传播其实就是一种利用误差来反向更新权重和偏置值的方法,其实就是一种利用误差来反向更新权重和偏置的方法,反向传播算法是神经网络中最有效的加速算法,其主要的思想是将网络最后输出的结果计算其误差,并且将误差反向逐级传下去。 反向传播运用的是链式求导的基本思想(隐函数求导)。

  训练一个神经网络其实就是计算各突触(Synapse)上的权重值的过程,通过样本对神经网络进行训练,使误差值最小化,也就是一个回归过程,然后就可以利用这些最优化的权重值对未知输入进行预测。所以,在未进行训练之前,神经网络基本上什么也干不了。一个神经元细胞(Neuron)的值由下面的公式模拟。

DavidWang原创

  这就是一个最简单的求和公式,然后我们再用Sigmoid激活函数对这个值进行判读,即这些输入经过这个神经元细胞后这个神经元决定对不对外传递其接收的信息,换句话说就是这些输入能不能激活这个神经元细胞使其有反应,如果不能激活自然就没有输出(我们输出0),激活了则有输出,这也是Sigmoid函数叫激活函数的由来。当然,最后我们还得加上偏置值。所以最终公式如下:

DavidWang原创
  在这里,公式中的符号含义如下:
  • n = 所有输入的神经元
  • Wi=突触的权重值
  • I = 输入值
  • O = 最终的输出值
  • S =sigmoid激活函数:

  好了,我们的神经网络终于编写完成了,下步我们将训练并利用这个神经网络来进行一些AR应用。

猜你喜欢

转载自blog.csdn.net/yolon3000/article/details/84889697