C++ 人工神经网络类 。。有错误。。先留着吧。

本文深入探讨神经网络编程的核心概念,通过C++面向对象类设计实现神经网络层,包括权重调整、神经元计算和误差计算等关键步骤。详细介绍了神经网络层类的构造、初始化、清理、权值随机化、神经元计算、误差计算及权重调整方法,为读者提供了一套完整的神经网络编程框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

 

 

这个学期我学习是神经网络课程,有很多的知识国内都不是很完善,而国外就有很大的进步,下面就是来自一本
<AI for Game>的电子版英文书.其中我就拿用面向对象写的C++类进行说明怎样编写神经网络程序.
神经网络的基本思想就是怎样去改变权值.
神经网络层类
class NeuralNetworkLayer
{
public:
     int               NumberOfNodes;
     int               NumberOfChildNodes;
     int               NumberOfParentNodes;
     double**          Weights;
     double**          WeightChanges;
     double*           NeuronValues;
     double*           DesiredValues;
     double*           Errors;
     double*           BiasWeights;
     double*           BiasValues;
     double            LearningRate;
     bool              LinearOutput;
     bool              UseMomentum;
     double            MomentumFactor;
     NeuralNetworkLayer*     ParentLayer;
     NeuralNetworkLayer*     ChildLayer;
     NeuralNetworkLayer();
     void     Initialize(int NumNodes,
                            NeuralNetworkLayer* parent,
                            NeuralNetworkLayer* child);
     void     CleanUp(void);
     void     RandomizeWeights(void);
     void     CalculateErrors(void);
     void     AdjustWeights(void);
     void     CalculateNeuronValues(void);
};
NumberOfNodes 层中神经元数目
NumberOfChildNodes     子层神经元数目
NumberOfParentNodes    父层神经元数目
Weights权值数组
WeightChanges 权值改变数组
NeuronValues   神经元值
DesiredValues 导师信号
Errors 误差
BiasWeights    偏差权值
LearningRate   学习效率
LinearOutput   是否线性输出
UseMomentum    是否有动力因素
MomentumFactor有动力因素的话,则动力因素大小值
ParentLayer    父层
ChildLayer     子层
构造函数
NeuralNetworkLayer::NeuralNetworkLayer()
{
     ParentLayer = NULL;
     ChildLayer = NULL;
     LinearOutput = false;
     UseMomentum = false;
      MomentumFactor = 0.9;
}
初始化类
void NeuralNetworkLayer::Initialize(int NumNodes,
                                              NeuralNetworkLayer* parent,
                                               NeuralNetworkLayer* child)
{
     int     i, j;
     // 分配内存
     NeuronValues = (double*) malloc(sizeof(double) *
                                               NumberOfNodes);
     DesiredValues = (double*) malloc(sizeof(double) *
                                               NumberOfNodes);
     Errors = (double*) malloc(sizeof(double) * NumberOfNodes);
     if(parent != NULL)
     {
          ParentLayer = parent;
     }
     if(child != NULL)
     {
          ChildLayer = child;
          Weights = (double**) malloc(sizeof(double*) *
                                              NumberOfNodes);
          WeightChanges = (double**) malloc(sizeof(double*) *
                                              NumberOfNodes);
          for(i = 0; i<NumberOfNodes; i++)
          {
               Weights[i] = (double*) malloc(sizeof(double) *                                                     NumberOfChildNodes);
               WeightChanges[i] = (double*) malloc(sizeof(double) *                                                         NumberOfChildNodes);
          }
          BiasValues = (double*) malloc(sizeof(double) *
                                        NumberOfChildNodes);
          BiasWeights = (double*) malloc(sizeof(double) *
                                         NumberOfChildNodes);
     } else {
          Weights = NULL;
          BiasValues = NULL;
          BiasWeights = NULL;
          WeightChanges = NULL;
     }
     // 确保所有归 0
     for(i=0; i<NumberOfNodes; i++)
     {
          NeuronValues[i] = 0;
          DesiredValues[i] = 0;
          Errors[i] = 0;
          if(ChildLayer != NULL)
               for(j=0; j<NumberOfChildNodes; j++)
               {
                    Weights[i][j] = 0;
                    WeightChanges[i][j] = 0;
               }
     }
     // Initialize the bias values and weights
     if(ChildLayer != NULL) 
          for(j=0; j<NumberOfChildNodes; j++)
          {
               BiasValues[j] = -1;
               BiasWeights[j] = 0;
          }
}
这个构造函数用于初始化层,为变量分配空间。这个函数共三个参数:一个是确定层是神经元的个数,一个是父层,一个是子层。如果这个层是一个输入层则父层为null,同理,如果这个层是一个输出层,则子层为null,而当这个层是一个隐层,则父层和子层都不可能为null。



清空层函数
void NeuralNetworkLayer::CleanUp(void)
{
     int i;
     free(NeuronValues);
     free(DesiredValues);
     free(Errors);
     if(Weights != NULL)
     {
          for(i = 0; i<NumberOfNodes; i++)
          {
               free(Weights[i]);
               free(WeightChanges[i]);
          }
          free(Weights);
          free(WeightChanges);
     }
     if(BiasValues != NULL) free(BiasValues);
     if(BiasWeights != NULL) free(BiasWeights);
}
这个函数主要是用于清空已经不再需要利用的层。悉放内存空间。



随机产生初始权值函数
void NeuralNetworkLayer::RandomizeWeights(void)
{
     int     i,j;
     int     min = 0;
     int     max = 200;
     int     number;
     srand( (unsigned)time( NULL ) );
     for(i=0; i<NumberOfNodes; i++)
     {
          for(j=0; j<NumberOfChildNodes; j++)
          {
               number = (((abs(rand())%(max-min+1))+min));
               if(number>max)
                    number = max;
               if(number<min)
                   number = min;
               Weights[i][j] = number / 100.0f - 1;
          }
     }
     for(j=0; j<NumberOfChildNodes; j++)
     {
               number = (((abs(rand())%(max-min+1))+min));
               if(number>max)
                    number = max;
               if(number<min)
                    number = min;
               BiasWeights[j] = number / 100.0f - 1;
      }
}
这个方法产生一个值大小在-1到1之间的值并把值作为初始权值。同时也产生偏值,并储存在BiasWeights数组中。你可以在训练网络前调用这个方法。



计算神经元值的方法
void NeuralNetworkLayer::CalculateNeuronValues(void)
{
     int i,j;
     double x;
     if(ParentLayer != NULL)
     {
          for(j=0; j<NumberOfNodes; j++)
          {
               x = 0;
               for(i=0; i<NumberOfParentNodes; i++)
               {
                    x += ParentLayer->NeuronValues[i] *
                          ParentLayer->Weights[i][j];
               }
               x += ParentLayer->BiasValues[j] *
                     ParentLayer->BiasWeights[j];
               if((ChildLayer == NULL) && LinearOutput)
                    NeuronValues[j] = x;
               else
                    NeuronValues[j] = 1.0f/(1+exp(-x));
          }
     }
}
所有的权值调整都通过嵌套的for循环来实现。j循环访问所有子层的所有结点,i循环访问所有父层的所有结点。在这些循环中,网络的输入被计算并且储存在变量x中。父层中各神经元所有相联的输入加权和被反馈到各个元结点。当你计算输入层中所有神经元,神经元值被输入激活函数。除了输出层,其它各层都会用logistic activation function,当然logistic activation function是否为线性或非线性取决于LinearOutput的值.

计算误差函数
void NeuralNetworkLayer::CalculateErrors(void)
{
     int i, j;
     double sum;
     if(ChildLayer == NULL) // output layer
     {
          for(i=0; i<NumberOfNodes; i++)
          {
                 Errors[i] = (DesiredValues[i] - NeuronValues[i]) *
                               NeuronValues[i] *
                               (1.0f - NeuronValues[i]);
          }
     } else if(ParentLayer == NULL) { // 输入层
          for(i=0; i<NumberOfNodes; i++)
          {
               Errors[i] = 0.0f;
          }
     } else { // 隐层
          for(i=0; i<NumberOfNodes; i++)
          {
               sum = 0;
               for(j=0; j<NumberOfChildNodes; j++)
               {
                    sum += ChildLayer->Errors[j] * Weights[i][j];
               }
               Errors[i] = sum * NeuronValues[i] *
                               (1.0f - NeuronValues[i]);
          }
     }
}
如果层中没有子层,这个函数仅仅在输出层有效。但如果层中没有父层,则这个函数会在输入层被调用。这个误差被设置为0。如是神经网络有三层:输入层、隐层、输出层,则这个函数会被应用于隐层。
权值调整方法
void NeuralNetworkLayer::AdjustWeights(void)
{
     int          i, j;
     double       dw;
     if(ChildLayer != NULL)
     {
          for(i=0; i<NumberOfNodes; i++)
          {
               for(j=0; j<NumberOfChildNodes; j++)
               {
                    dw = LearningRate * ChildLayer->Errors[j] *
                          NeuronValues[i];
                    if(UseMomentum)
                    {
                         Weights[i][j] += dw + MomentumFactor *
                                                WeightChanges[i][j];
                         WeightChanges[i][j] = dw;
                    } else {
                             Weights[i][j] += dw;
                    }
               }
          }
          for(j=0; j<NumberOfChildNodes; j++)
          {
               BiasWeights[j] += LearningRate *
                                      ChildLayer->Errors[j] *
                                      BiasValues[j];
          }
     }
}
权值只有一个神经网络有子层的时候才会被调整,换句话说:输入层和隐层才会出现权值的调整。输出层没有子层,因此没有连接到下一层,更不会有权值可以调整。For嵌套遍历层和子层中的所有节点。由神经网络定义可知:每个神经元会和子层中各个神经元相连。在这些嵌套循环中,权值会用规则进行调整。如果动力要素被应用,动力要素值会被添加到权值的改变之中。当次的权值变化值会被储存在变量WeightChanges中,为下次的利用做好准备。如果动力因素不被使用,则不必将其添加到权值改变函数中进行计算。
一、神经网络类(The Neural Network Class)
    class NeuralNetwork
{
    public:
     NeuralNetworkLayer     InputLayer;
     NeuralNetworkLayer     HiddenLayer;
     NeuralNetworkLayer     OutputLayer;
     void     Initialize(int nNodesInput, int nNodesHidden,
                              int nNodesOutput);
     void     CleanUp();
     void     SetInput(int i, double value);
     double GetOutput(int i);
     void     SetDesiredOutput(int i, double value);
     void     FeedForward(void);
     void     BackPropagate(void);
     int      GetMaxOutputID(void);
     double CalculateError(void);
     void     SetLearningRate(double rate);
     void     SetLinearOutput(bool useLinear);
     void     SetMomentum(bool useMomentum, double factor);
     void     DumpData(char* filename);
};
这个类共有三个变量,分别为输入层、隐层、输出层。但方法有很多,一共13个方法。下面我们将会详细地介绍。
初始化函数
void NeuralNetwork::Initialize(int nNodesInput,
                                         int nNodesHidden,
                                         int nNodesOutput)
{
     InputLayer.NumberOfNodes = nNodesInput;
     InputLayer.NumberOfChildNodes = nNodesHidden;
     InputLayer.NumberOfParentNodes = 0;
     InputLayer.Initialize(nNodesInput, NULL, &HiddenLayer);
     InputLayer.RandomizeWeights();
     HiddenLayer.NumberOfNodes = nNodesHidden;
     HiddenLayer.NumberOfChildNodes = nNodesOutput;
     HiddenLayer.NumberOfParentNodes = nNodesInput;
     HiddenLayer.Initialize(nNodesHidden,&InputLayer,&OutputLayer);
     HiddenLayer.RandomizeWeights();
     OutputLayer.NumberOfNodes = nNodesOutput;
     OutputLayer.NumberOfChildNodes = 0;
     OutputLayer.NumberOfParentNodes = nNodesHidden;
     OutputLayer.Initialize(nNodesOutput, &HiddenLayer, NULL);
}
初始化函数,用于初始化类中的变量,并且分配空间。
清空网络方法
void NeuralNetwork::CleanUp()
{
     InputLayer.CleanUp();
     HiddenLayer.CleanUp();
     OutputLayer.CleanUp();
}
很简单的一个方法,只调用了神经网络层类(NeuralNetworkLayer)中的清空方法。
设置输入方法
void     NeuralNetwork::SetInput(int i, double value)
{
     if((i>=0) && (i<InputLayer.NumberOfNodes))
     {
          InputLayer.NeuronValues[i] = value;
     }
}
设置输入层第i个神经元的输入值。你可以使用这个函数在训练期间和初始输入值。
取得输出值函数
double     NeuralNetwork::GetOutput(int i)
{
     if((i>=0) && (i<OutputLayer.NumberOfNodes))
     {
          return OutputLayer.NeuronValues[i];
     }
     return (double) INT_MAX; 
}
这个函数有一个输入参数i,返回的是输出层第i个神经元的输出值。
设置导师信号
void NeuralNetwork::SetDesiredOutput(int i, double value)
{
     if((i>=0) && (i<OutputLayer.NumberOfNodes))
     {
          OutputLayer.DesiredValues[i] = value;
     }
}
在训练过程中你需要把实现的输出和导师信号进行比较。而我们这个类提供的方法SetDesiredOutput可以轻易地对神经网络的导师信号进行设置。这个方法有两个输入参数,对应的含义为:输出层第i个神经元的导师信号。
信号前馈函数
void NeuralNetwork::FeedForward(void)
{
     InputLayer.CalculateNeuronValues ();
 HiddenLayer.CalculateNeuronValues (); OutputLayer.CalculateNeuronValues ();
}
这个函数很简单。只不过调用了神经网络的层类中的CalculateNeuronValues进行对网络输入层、隐层、输出层的信号前馈当函数调用完毕,则输出层将包含前馈神经网络的输出。
反向传播方法
void NeuralNetwork::BackPropagate(void)
{
     OutputLayer.CalculateErrors();
     HiddenLayer.CalculateErrors();
     HiddenLayer.AdjustWeights();
     InputLayer.AdjustWeights();
}
反向传播函数首先调用输出层和隐层的函数CalculateErrors。之后再隐层的和输入层的AdjustWeights函数。当然,这些调用次序是不可以改变的。
取得输出值最大神经元的ID
int     NeuralNetwork::GetMaxOutputID(void)
{
     int          i, id;
     double     maxval;
     maxval = OutputLayer.NeuronValues[0];
     id = 0;
     for(i=1; i<OutputLayer.NumberOfNodes; i++)
     {
          if(OutputLayer.NeuronValues[i] > maxval)
          {
               maxval = OutputLayer.NeuronValues[i];
               id = i;
          }
     }
     return id;
}
这个函数用于返回输出层输出神经元输出值最大的神经元ID。
计算误差方法
double NeuralNetwork::CalculateError(void)
{
     int          i;
     double     error = 0;
     for(i=0; i<OutputLayer.NumberOfNodes; i++)
     {
          error += pow(OutputLayer.NeuronValues[i] --
                   OutputLayer.DesiredValues[i], 2);
     }
     error = error / OutputLayer.NumberOfNodes;
     return error;
}
这个函数主要是返回输出值和导师信号联合的均方,用于了解训练效果和进行权值调整。
设置学习效率
void NeuralNetwork::SetLearningRate(double rate)
{
     InputLayer.LearningRate = rate;
     HiddenLayer.LearningRate = rate;
     OutputLayer.LearningRate = rate;
}
很简单,主要是调用神经网络层类的方法LearningRate进行设置。
设置输出是否线性
void     NeuralNetwork::SetLinearOutput(bool useLinear)
{
     InputLayer.LinearOutput = useLinear;
     HiddenLayer.LinearOutput = useLinear;
      OutputLayer.LinearOutput = useLinear;
}
这个函数有一个参数,当参数为false时则为线性输出。反之就为非线性。
设置动力因素
void     NeuralNetwork::SetMomentum(bool useMomentum, double factor)
{
     InputLayer.UseMomentum = useMomentum;
     HiddenLayer.UseMomentum = useMomentum;
     OutputLayer.UseMomentum = useMomentum;
     InputLayer.MomentumFactor = factor;
     HiddenLayer.MomentumFactor = factor;
     OutputLayer.MomentumFactor = factor;
}
用于设置是不具有动力因素,当有动力因素时,动力因素大小就是factor。
 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值