打印
[资源共享]

C51实现8051上运行的神经网络

[复制链接]
楼主: 家有两宝呀
手机看帖
扫描二维码
随时随地手机跟帖
21
家有两宝呀|  楼主 | 2022-6-28 23:28 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
尽管代码不是绝对的初学者,但是如果您熟悉数组和循环的概念,则希望能够阅读本文随附的草图并遵循逻辑。这是一个高级细分:

    初始化数组。权重设置为随机数,而两个用于保存反向传播中使用的更改值的其他数组都设置为零。
    开始一个大循环,通过完整的训练数据集运行系统。
    随机化训练集在每次迭代中运行的顺序,以减少局部最小值的振荡或收敛。
    计算隐藏层激活,输出层激活和错误。
    将错误反向传播到隐藏层。
    更新权重。
    如果系统错误大于成功阈值,则运行训练数据的另一次迭代。
    如果系统错误小于成功阈值,则中断,声明成功,然后将数据发送到串行终端。
    每1000个周期将训练集的测试运行结果发送到串行终端。

除了编程逻辑外,还有三个要理解的网络基本概念:激活函数,梯度良好和偏置。

使用特权

评论回复
22
家有两宝呀|  楼主 | 2022-6-28 23:30 | 只看该作者
激活函数根据馈入该神经元的加权连接的总和来计算该神经元的输出。尽管有变化,但此草图使用了最常见的激活函数,称为“ Sigmoid函数”,因为其独特的形状如下图所示。

使用特权

评论回复
23
家有两宝呀|  楼主 | 2022-6-28 23:35 | 只看该作者

使用特权

评论回复
24
家有两宝呀|  楼主 | 2022-6-28 23:36 | 只看该作者
该函数的关键特征是无论输入如何,输出都将落在0到1之间。此功能在编码神经网络时非常方便,因为神经元的输出始终可以表示为full on和full之间的范围。关。激活函数可以在代码中采用通用形式的几个地方看到:
输出= 1.0 /(1.0 + exp(-Accum)) 

使用特权

评论回复
25
家有两宝呀|  楼主 | 2022-6-28 23:37 | 只看该作者
输出是代表已激活神经元输出的数组变量,而Accum是该神经元加权输入的总和。特定公式的复杂性并不重要,除了它们可以方便地产生S型输出。

梯度下降是反向传播的秘密武器。这是一种数学方法,使我们能够计算每个输出神经元的误差幅度,确定与该神经元的每个连接对误差的贡献程度,并对这些连接的权重进行增量调整以系统地减少误差。

使用特权

评论回复
26
家有两宝呀|  楼主 | 2022-6-28 23:38 | 只看该作者
梯度下降的第一步是为每个神经元计算一个称为增量的值。增量反映了误差的大小-神经元的目标值与其实际输出之间的差异越大,则增量越大。在输出层,增量计算很简单:
 增量=(目标-实际)*实际*(1-实际)

使用特权

评论回复
27
家有两宝呀|  楼主 | 2022-6-28 23:38 | 只看该作者
在代码中被视为
OutputDelta [i] =(Target [p] [i]-Output [i])* Output [i] *(1.0-Output [i]);

使用特权

评论回复
28
家有两宝呀|  楼主 | 2022-6-28 23:39 | 只看该作者
由于没有可测量的目标,因此计算隐藏层的增量将变得更加复杂。取而代之的是,每个隐藏神经元的误差幅度是根据权重与针对输出层计算的增量之间的关系得出的。对于每个隐藏的神经元,代码逐步遍历所有输出连接,将权重乘以增量,并保持运行总计:
 累计+ = OutputWeights [i] [j] * OutputDelta [j];

使用特权

评论回复
29
家有两宝呀|  楼主 | 2022-6-28 23:40 | 只看该作者
然后,通过将存储在Accum中的值替换为在第一次计算中看到的公式中的Target [p] [i]-Output [i]值来计算内层增量:
HiddenDelta [i] =累计* Hidden [i] *(1.0-Hidden [i]);

使用特权

评论回复
30
家有两宝呀|  楼主 | 2022-6-28 23:41 | 只看该作者
计算出两层的增量值后,下一步就是实际进行操作并调整权重。在这里,我们看到学习率和动量的值如何改变权重的变化。对于每个重量,更改量由以下公式确定:
 变化=(学习率*体重*差异)+(动量*以前的变化)

使用特权

评论回复
31
家有两宝呀|  楼主 | 2022-6-28 23:43 | 只看该作者
然后通过将旧权重添加到更改值中来找到新权重:

  体重=体重+变化

使用特权

评论回复
32
家有两宝呀|  楼主 | 2022-6-28 23:47 | 只看该作者
对于内层和隐藏层之间的权重,公式在代码中显示为:

  ChangeHiddenWeights [j] [i] =学习率*输入[p] [j] * HiddenDelta [i] +动量* ChangeHiddenWeights [j] [i];

  HiddenWeights [j] [i] + = ChangeHiddenWeights [j] [i];

使用特权

评论回复
33
家有两宝呀|  楼主 | 2022-6-28 23:50 | 只看该作者
对于隐藏层和输出层之间的权重,公式在代码中显示为:

  ChangeOutputWeights [j] [i] =学习率*隐藏[j] * OutputDelta [i] +动量* ChangeOutputWeights [j] [i];

  OutputWeights [j] [i] + = ChangeOutputWeights [j] [i];

使用特权

评论回复
34
家有两宝呀|  楼主 | 2022-6-28 23:52 | 只看该作者
最后,我们开始偏见,这是一个相对简单的概念,但是当不理解它时,可能会使代码有些混乱。输入层和隐藏层每个都包含一个始终触发的额外神经元(换句话说,它始终隐含激活为“ 1”)。偏差值对网络有几个积极影响。它增加了稳定性并扩大了可能的解决方案的数量。最重要的是,它消除了所有输入均为零且因此没有信号通过网络传播的可能性。如果查看包含权重和变化值的数组的声明,则会看到该额外的神经元。

使用特权

评论回复
35
家有两宝呀|  楼主 | 2022-6-28 23:54 | 只看该作者
这样,我们将结束对网络的讨论。如果这里介绍的概念对您来说都是新手,那么毫无疑问,它将需要一些持续的努力才能对反向传播的工作原理形成令人满意的理解。并不缺少与神经网络相关的材料,但是其中很多材料已深入到数学理论中,对初学者尝试掌握基本概念并将其转换为代码没有帮助。

计算机科学伯明翰大学医学院的约翰Bullinaria发布了一个分步指南以实施神经网络用C www.cs.bham.ac.uk/~jxb/NN/nn.html。本指南是特别有用的资源,我发现在编写本文时它是无价的。

使用特权

评论回复
36
家有两宝呀|  楼主 | 2022-6-28 23:55 | 只看该作者
在TEK271.com上可以找到另外两个对入门有用的资源:http: //www.tek271.com/?about = docs/neuralNet/IntoToNeuralNets.html 和南加州机器人学会:http:// www.rssc.org/content/introduction-neural-nets-part-1。

#include <math.h>

#include "HC89F0541.h"

#include <stdio.h>

#include <stdlib.h>



/******************************************************************

* Network Configuration - customized per network

******************************************************************/

#define PatternCount      10

#define InputNodes                   7

#define HiddenNodes                8

#define OutputNodes                4



//const int PatternCount = Pattern_Count;

//const int InputNodes = Input_Nodes;

//const int HiddenNodes = Hidden_Nodes;

//const int OutputNodes = Output_Nodes;

const float LearningRate = 0.3;

const float Momentum = 0.9;

const float InitialWeightMax = 0.5;

const float Success = 0.1;//0.0004;



typedef ux8 byte;



const byte Input[PatternCount][InputNodes] = {

  { 1, 1, 1, 1, 1, 1, 0 },  // 0

  { 0, 1, 1, 0, 0, 0, 0 },  // 1

  { 1, 1, 0, 1, 1, 0, 1 },  // 2

  { 1, 1, 1, 1, 0, 0, 1 },  // 3

  { 0, 1, 1, 0, 0, 1, 1 },  // 4

  { 1, 0, 1, 1, 0, 1, 1 },  // 5

  { 0, 0, 1, 1, 1, 1, 1 },  // 6

  { 1, 1, 1, 0, 0, 0, 0 },  // 7

  { 1, 1, 1, 1, 1, 1, 1 },  // 8

  { 1, 1, 1, 0, 0, 1, 1 }   // 9

};



const byte Target[PatternCount][OutputNodes] = {

  { 0, 0, 0, 0 },

  { 0, 0, 0, 1 },

  { 0, 0, 1, 0 },

  { 0, 0, 1, 1 },

  { 0, 1, 0, 0 },

  { 0, 1, 0, 1 },

  { 0, 1, 1, 0 },

  { 0, 1, 1, 1 },

  { 1, 0, 0, 0 },

  { 1, 0, 0, 1 }

};



typedef int (*func1)(int);

typedef int (*func2)(const char*, int) reentrant;

typedef int (*func3)(const char*, float) reentrant;



typedef struct S {

         func1        begin;

         func2        println;// = printf;

         func2        print;// = printf;

         func3        printf;// = printf;

        

}s_t;



s_t Serial;



void Uart_SendByte(unsigned char buf)

{

         SCON &=~ 0x10;                                  //失能UART1接收

         SBUF = buf;                                            //发送8位串口数据

        while(!(SCON & 0x02));

         SCON &=~ 0x02;                                  //清除发送中断标志位

         SCON |= 0x10;                             //UART1接收使能

}



char putchar(char c)

{

        Uart_SendByte(c);

         return c;

}



int sendmsg(const char* str, float d){

         printf(str, d);



         return 1;

}



/******************************************************************

* End Network Configuration

******************************************************************/





xdata int i, j, p, q, r;

xdata int ReportEvery1000;

xdata int RandomizedIndex[PatternCount];

xdata long  TrainingCycle;

xdata float Rando;

xdata float Error;

xdata float Accum;





xdata float Hidden[HiddenNodes];

xdata float Output[OutputNodes];

xdata float HiddenWeights[InputNodes+1][HiddenNodes];

xdata float OutputWeights[HiddenNodes+1][OutputNodes];

xdata float HiddenDelta[HiddenNodes];

xdata float OutputDelta[OutputNodes];

xdata float ChangeHiddenWeights[InputNodes+1][HiddenNodes];

xdata float ChangeOutputWeights[HiddenNodes+1][OutputNodes];





int random(int r){

  return rand() % r;

}

void toTerminal()

{



  for( p = 0 ; p < PatternCount ; p++ ) {

    Serial.println("\n");

    Serial.print ("  Training Pattern: %d ", p);

    //Serial.println (p);     

    Serial.print ("  Input ");

    for( i = 0 ; i < InputNodes ; i++ ) {

      Serial.print ("%d", Input[p][i]);

      Serial.print (" ");

    }

    Serial.print ("  Target ");

    for( i = 0 ; i < OutputNodes ; i++ ) {

      Serial.print ("%d", Target[p][i]);

      Serial.print (" ");

    }

/******************************************************************

* Compute hidden layer activations

******************************************************************/



    for( i = 0 ; i < HiddenNodes ; i++ ) {   

      Accum = HiddenWeights[InputNodes][i] ;

      for( j = 0 ; j < InputNodes ; j++ ) {

        Accum += Input[p][j] * HiddenWeights[j][i] ;

      }

      Hidden[i] = 1.0/(1.0 + exp(-Accum)) ;

    }



/******************************************************************

* Compute output layer activations and calculate errors

******************************************************************/



    for( i = 0 ; i < OutputNodes ; i++ ) {   

      Accum = OutputWeights[HiddenNodes][i] ;

      for( j = 0 ; j < HiddenNodes ; j++ ) {

        Accum += Hidden[j] * OutputWeights[j][i] ;

      }

      Output[i] = 1.0/(1.0 + exp(-Accum)) ;

    }

    Serial.print ("  Output ");

    for( i = 0 ; i < OutputNodes ; i++ ) {      

      Serial.printf ("%f", Output[i]);

      Serial.print (" ");

    }

  }





}



void predict(u8 *input)

{

/******************************************************************

* Compute hidden layer activations

******************************************************************/



    for( i = 0 ; i < HiddenNodes ; i++ ) {   

      Accum = HiddenWeights[InputNodes][i] ;

      for( j = 0 ; j < InputNodes ; j++ ) {

        Accum += input[j] * HiddenWeights[j][i] ;

      }

      Hidden[i] = 1.0/(1.0 + exp(-Accum)) ;

    }



/******************************************************************

* Compute output layer activations and calculate errors

******************************************************************/



    for( i = 0 ; i < OutputNodes ; i++ ) {   

      Accum = OutputWeights[HiddenNodes][i] ;

      for( j = 0 ; j < HiddenNodes ; j++ ) {

        Accum += Hidden[j] * OutputWeights[j][i] ;

      }

      Output[i] = 1.0/(1.0 + exp(-Accum)) ;

           Output[i] = Output[i] > 0.8 ? 1:0;

    }

    Serial.print ("result: ");

    for( i = 0 ; i < OutputNodes ; i++ ) {      

      Serial.printf ("%f", Output[i]);

      Serial.print (" ");

    }

    Serial.print ("\n");

}



void setup(){



  Serial.begin = (9600);

  Serial.println = sendmsg;

  Serial.print = sendmsg;

  Serial.printf = sendmsg;

  //randomSeed(analogRead(3));

  ReportEvery1000 = 1;

  for( p = 0 ; p < PatternCount ; p++ ) {   

    RandomizedIndex[p] = p ;

  }

}



void main(){



         setup();



/******************************************************************

* Initialize HiddenWeights and ChangeHiddenWeights

******************************************************************/



  for( i = 0 ; i < HiddenNodes ; i++ ) {   

    for( j = 0 ; j <= InputNodes ; j++ ) {

      ChangeHiddenWeights[j][i] = 0.0 ;

      Rando = (float)(random(100))/100;

      HiddenWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;

    }

  }

/******************************************************************

* Initialize OutputWeights and ChangeOutputWeights

******************************************************************/



  for( i = 0 ; i < OutputNodes ; i ++ ) {   

    for( j = 0 ; j <= HiddenNodes ; j++ ) {

      ChangeOutputWeights[j][i] = 0.0 ;

      Rando = (float)(random(100))/100;      

      OutputWeights[j][i] = 2.0 * ( Rando - 0.5 ) * InitialWeightMax ;

    }

  }

  Serial.println("Initial/Untrained Outputs: ");

  toTerminal();

/******************************************************************

* Begin training

******************************************************************/



  for( TrainingCycle = 1 ; TrainingCycle < 2147483647 ; TrainingCycle++) {   



/******************************************************************

* Randomize order of training patterns

******************************************************************/



    for( p = 0 ; p < PatternCount ; p++) {

      q = random(PatternCount);

      r = RandomizedIndex[p] ;

      RandomizedIndex[p] = RandomizedIndex[q] ;

      RandomizedIndex[q] = r ;

    }

    Error = 0.0 ;

/******************************************************************

* Cycle through each training pattern in the randomized order

******************************************************************/

    for( q = 0 ; q < PatternCount ; q++ ) {   

      p = RandomizedIndex[q];



/******************************************************************

* Compute hidden layer activations

******************************************************************/



      for( i = 0 ; i < HiddenNodes ; i++ ) {   

        Accum = HiddenWeights[InputNodes][i] ;

        for( j = 0 ; j < InputNodes ; j++ ) {

          Accum += Input[p][j] * HiddenWeights[j][i] ;

        }

        Hidden[i] = 1.0/(1.0 + exp(-Accum)) ;

      }



/******************************************************************

* Compute output layer activations and calculate errors

******************************************************************/



      for( i = 0 ; i < OutputNodes ; i++ ) {   

        Accum = OutputWeights[HiddenNodes][i] ;

        for( j = 0 ; j < HiddenNodes ; j++ ) {

          Accum += Hidden[j] * OutputWeights[j][i] ;

        }

        Output[i] = 1.0/(1.0 + exp(-Accum)) ;  

        OutputDelta[i] = (Target[p][i] - Output[i]) * Output[i] * (1.0 - Output[i]) ;  

        Error += 0.5 * (Target[p][i] - Output[i]) * (Target[p][i] - Output[i]) ;

      }



/******************************************************************

* Backpropagate errors to hidden layer

******************************************************************/



      for( i = 0 ; i < HiddenNodes ; i++ ) {   

        Accum = 0.0 ;

        for( j = 0 ; j < OutputNodes ; j++ ) {

          Accum += OutputWeights[i][j] * OutputDelta[j] ;

        }

        HiddenDelta[i] = Accum * Hidden[i] * (1.0 - Hidden[i]) ;

      }





/******************************************************************

* Update Inner-->Hidden Weights

******************************************************************/





      for( i = 0 ; i < HiddenNodes ; i++ ) {   

        ChangeHiddenWeights[InputNodes][i] = LearningRate * HiddenDelta[i] + Momentum * ChangeHiddenWeights[InputNodes][i] ;

        HiddenWeights[InputNodes][i] += ChangeHiddenWeights[InputNodes][i] ;

        for( j = 0 ; j < InputNodes ; j++ ) {

          ChangeHiddenWeights[j][i] = LearningRate * Input[p][j] * HiddenDelta[i] + Momentum * ChangeHiddenWeights[j][i];

          HiddenWeights[j][i] += ChangeHiddenWeights[j][i] ;

        }

      }



/******************************************************************

* Update Hidden-->Output Weights

******************************************************************/



      for( i = 0 ; i < OutputNodes ; i ++ ) {   

        ChangeOutputWeights[HiddenNodes][i] = LearningRate * OutputDelta[i] + Momentum * ChangeOutputWeights[HiddenNodes][i] ;

        OutputWeights[HiddenNodes][i] += ChangeOutputWeights[HiddenNodes][i] ;

        for( j = 0 ; j < HiddenNodes ; j++ ) {

          ChangeOutputWeights[j][i] = LearningRate * Hidden[j] * OutputDelta[i] + Momentum * ChangeOutputWeights[j][i] ;

          OutputWeights[j][i] += ChangeOutputWeights[j][i] ;

        }

      }

    }



/******************************************************************

* Every 1000 cycles send data to terminal for display

******************************************************************/

    ReportEvery1000 = ReportEvery1000 - 1;

    if (ReportEvery1000 == 0)

    {

      Serial.println("\n");

      Serial.println("\n");

      Serial.print ("TrainingCycle: %d",TrainingCycle );

      //Serial.print (TrainingCycle);

      Serial.printf ("  Error = %f", Error);

      //Serial.println (Error, 5);



      toTerminal();



      if (TrainingCycle==1)

      {

        ReportEvery1000 = 999;

      }

      else

      {

        ReportEvery1000 = 1000;

      }

    }   





/******************************************************************

* If error rate is less than pre-determined threshold then end

******************************************************************/



    if( Error < Success ) break ;

  }

  Serial.println("\n");

  Serial.println("\n");

  Serial.print ("TrainingCycle: %d", TrainingCycle);

  //Serial.print (TrainingCycle);

  Serial.printf ("  Error = %f", Error);

// Serial.println (Error, 5);



  toTerminal();



  Serial.println ("\n");

  Serial.println ("\n");

  Serial.println ("Training Set Solved! ");

  Serial.println ("--------");

  Serial.println ("\n");

  Serial.println ("\n");

  ReportEvery1000 = 1;





/******************************************************************

* from a 7 segments input data predict the value

******************************************************************/

  predict(&Input[3]);

}

使用特权

评论回复
37
tpgf| | 2022-7-4 14:27 | 只看该作者
代码量不大呀

使用特权

评论回复
38
qcliu| | 2022-7-4 14:36 | 只看该作者
主要还是依靠经验啊

使用特权

评论回复
39
drer| | 2022-7-4 14:45 | 只看该作者
需要的基础知识比较多啊

使用特权

评论回复
40
coshi| | 2022-7-4 15:01 | 只看该作者
请问什么是隐藏的神经元啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则