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






4.94/5 (7投票s)
用于实验的基本人工神经网络代码。
背景
人脑可以被看作是由数百万个相互连接的神经元组成的复杂组合。大脑接收到的信息由这些微小的神经元处理。神经元通过树突接收信号,并通过突触进一步传递。突触根据信号的强度决定是将信号传递给相邻神经元还是不传递。随着人脑获取信息,神经元之间会发展出新的连接,并修改现有的连接。
人工神经网络试图模拟(在非常基本的层面上)人脑的功能。创建一组相互连接的神经元。这些相互连接的神经元被组织成层,以便更容易处理。神经元之间的信号传递通过树突发生。神经元“激发”信号到相邻连接神经元的可能性由激活电位决定。通常,这种激发趋势(也称为动作电位)通过 sigmoid 函数建模。 Sigmoid 函数看起来像
对于任何输入 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%。剩余的可用数据用于确认训练。训练主要包括确定输出节点上的误差,并以树突权重的变化形式将其分布。这种方法被称为反向传播。反向传播在数学上执行为梯度下降法。建议读者参考对梯度下降法的描述性处理,这被认为超出了本文的范围。
网络的训练以以下方式进行
- 运行输入
- 计算输出神经元的误差
- 从输出神经元计算的误差中,调整树突中的权重。
- 对所有层执行此操作
- Repeat
当误差范数降低到阈值以下时,或者在一定的循环次数后,可以终止训练。此代码运行一定数量的循环。
一旦网络经过训练(并确认正确性),就可以输入具有未知输出的输入以获得输出。请注意,人工神经网络可能不会返回 100% 准确的结果。结果通常在可接受的准确度范围内。
关注点
各种启发式参数(例如学习率)在网络能够以良好的速度成功训练之前,需要大量的经验。此代码允许试验隐藏层的数量和每个隐藏层中的神经元数量。该代码已经使用正弦函数(在 0 和 1 之间)和其他函数(如加法、减法等)进行了测试,并且已被证明可以以显着的精度工作。
历史
- 2017年8月7日:代码的第一个版本