65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的基本人工神经网络

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (7投票s)

2017 年 8 月 7 日

CPOL

4分钟阅读

viewsIcon

38984

downloadIcon

2217

用于实验的基本人工神经网络代码。

背景

人脑可以被看作是由数百万个相互连接的神经元组成的复杂组合。大脑接收到的信息由这些微小的神经元处理。神经元通过树突接收信号,并通过突触进一步传递。突触根据信号的强度决定是将信号传递给相邻神经元还是不传递。随着人脑获取信息,神经元之间会发展出新的连接,并修改现有的连接。

人工神经网络试图模拟(在非常基本的层面上)人脑的功能。创建一组相互连接的神经元。这些相互连接的神经元被组织成层,以便更容易处理。神经元之间的信号传递通过树突发生。神经元“激发”信号到相邻连接神经元的可能性由激活电位决定。通常,这种激发趋势(也称为动作电位)通过 sigmoid 函数建模。 Sigmoid 函数看起来像

$ S(x) = \frac{1}{1+e^{-x}} $

对于任何输入 x,sigmoid 函数返回一个介于 0 和 1 之间的值。强烈建议读者研究 sigmoid 函数的特性,以便理解其作为激活函数的使用。本文不试图讨论人工神经网络的基本原理。相反,建议读者参考关于该主题的更权威的文本。

Using the Code

人工神经网络包含以下主要类

树突

这是将信号从一个神经元传输到另一个神经元的基本单元。通过树突传输的信号被加权,以模拟正在传递的信号的强度。应该注意的是,这个类只公开一个属性 - Weight

public class Dendrite
    {
        private double wt;
        public double Weight
        {
            get { return wt; }
            set { wt = value; }
        }

        //Provide a constructor for the class.
        //It is always better to provide a constructor instead of using
        //the compiler provided constructor
        public Dendrite()
        {
            wt = getRandom(0.00000001, 1.0);
        }

        private double getRandom(double MinValue, double MaxValue)
        {
            return Util.GetRandomD() * (MaxValue - MinValue) + MinValue;
        }
    }

Neuron

神经元是人工神经网络的基本构建块。神经元暴露其中的值以及一个偏置。神经元还包含代表预期解决方案中的错误的属性以及最重要的参数,即树突。每个神经元将拥有与前一层神经元数量一样多的树突。

class Neuron
    {
        private List<Dendrite> dendrites;
        private double bias, delta, _value;

        public double Bias
        {
            get
            { return bias; }
            set
            { bias = value; }
        }
        public double Delta
        {
            get
            { return delta; }
            set
            { delta = value; }
        }
        public double Value
        {
            get
            { return _value; }
            set
            { _value = value; }
        }

        public void AddDendrites(int nDendrites)
        {
            int i;
            //Dendrite d;
            for(i=0;i<nDendrites;i++)
            {
                //d = new Dendrite();
                dendrites.Add(new Dendrite());
            }
        }

        public int nDendrites()
        {
            return dendrites.Count;
        }

        public Dendrite getDendrite(int index)
        {
            return dendrites[index];
        }

        public Neuron()
        {
            bias = Util.GetRandomD();
            dendrites = new List<Dendrite>();
        }

    }

Layer

Layer 类包含该层中的神经元列表。

 class Layer
    {
        private List<Neuron> neurons;
 
        public void Clear()
        {
            neurons.Clear();
        }
        public void Initialize(int nNeurons)
        {
            int i;
            for(i=0;i<nNeurons;i++)
            {
                neurons.Add(new Neuron());
            }
        }
 
        public Neuron getNeuron(int index)
        {
            return neurons[index];
        }
        public void setNeuron(int index, ref Neuron neuron)
        {
            neurons[index] = neuron;
        }
        public void setNeuron(int index, Double value)
        {
            Neuron n = new Neuron();
            n.Value = value;
            neurons[index] = n;
        }

        public void AddDendritesToEachNeuron(int nDendrites)
        {
            int i;
            for(i=0;i<neurons.Count;i++)
            {
                neurons[i].AddDendrites(nDendrites);
            }
        }

        public int nNeurons()
        {
            return neurons.Count;
        }

        /// <summary>
        /// Constructor of the class
        /// </summary>
        public Layer()
        {
            neurons = new List<Neuron>();
        }
    }

网络

网络类包含一个层列表。因此,类的层次结构可以显示为

网络 -> -> 神经元 -> 树突

一旦网络初始化,树突就会被分配随机权重。由于这些权重是随机的,生成的网络几乎没有任何用处。与每个树突相关的权重必须进行微调,才能获得有意义的结果。这被称为训练。神经网络需要针对给定的一组输入和相应的输出数据进行训练。可用数据集合称为训练集。训练集通常构成总可用数据的 80% - 85%。剩余的可用数据用于确认训练。训练主要包括确定输出节点上的误差,并以树突权重的变化形式将其分布。这种方法被称为反向传播。反向传播在数学上执行为梯度下降法。建议读者参考对梯度下降法的描述性处理,这被认为超出了本文的范围。

网络的训练以以下方式进行

  1. 运行输入
  2. 计算输出神经元的误差
  3. 从输出神经元计算的误差中,调整树突中的权重。 
  4. 对所有层执行此操作
  5. Repeat

当误差范数降低到阈值以下时,或者在一定的循环次数后,可以终止训练。此代码运行一定数量的循环。

一旦网络经过训练(并确认正确性),就可以输入具有未知输出的输入以获得输出。请注意,人工神经网络可能不会返回 100% 准确的结果。结果通常在可接受的准确度范围内。

关注点

各种启发式参数(例如学习率)在网络能够以良好的速度成功训练之前,需要大量的经验。此代码允许试验隐藏层的数量和每个隐藏层中的神经元数量。该代码已经使用正弦函数(在 0 和 1 之间)和其他函数(如加法、减法等)进行了测试,并且已被证明可以以显着的精度工作。

历史

  • 2017年8月7日:代码的第一个版本
© . All rights reserved.