美文网首页
BP算法的C语言实现(注释详解)

BP算法的C语言实现(注释详解)

作者: 五柯是个小菜鸡 | 来源:发表于2020-01-16 22:48 被阅读0次

    如有错漏,欢迎大佬指出并进行探讨

    #include<stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #define ETA 1//学习率,步长
    #define PRECISION 0.00001//精度要求
    
    typedef struct Neuron//神经元结构体,output=sigmoid[for(weight*output)]
    {
        double input;//输入
        double output;//输出
        double* weights;//权重
        double Error;//误差
    } NEURON;
    
    typedef struct Layer//层结构体
    {
        int numberOfNeurons;//神经元个数
        NEURON *neurons;//第一个神经元头指针,数组
    } LAYER;
    
    typedef struct NNet //网络结构体
    {
        int numberOfLayers;//神经层数
        LAYER *layers;//第一层神经层头指针,数组
    } NNET;
    
    double sigmoid(double v)//S形,初始rule函数
    {
        return 1 / (1 + exp(-v));
    }
    
    double randomWeight()//随机权重值
    {
        return ((int)rand() % 100000) / (float)100000 - 1;
    }
    
    void createNetWorks(NNET *nnet, int NumberOfLayers, int* NumberOfNeurons)
    //构造网络,参数为网络头指针、神经层数、神经元个数
    {
        nnet->numberOfLayers = NumberOfLayers;//将神经层数赋值给网络结构体的层数元素
        nnet->layers = (LAYER*)malloc(NumberOfLayers * sizeof(LAYER));//构造得到一个神经层头指针
        for (int i = 0; i < NumberOfLayers; i++)//神经层和神经元都是连续数组结构
        {
            nnet->layers[i].numberOfNeurons = NumberOfNeurons[i];//将神经元个数一一赋值代入具体元素
            nnet->layers[i].neurons = (NEURON*)malloc(NumberOfNeurons[i] * sizeof(NEURON));//构造得到每一层的神经元头指针
        }
    }
    
    void init(NNET *nnet, double * inputs) //神经网络初始化,参数为神经网络指针,输入参数
    {
        for (int i = 0; i < nnet->layers[0].numberOfNeurons; i++) //遍历网络第一层的所有神经元
        {
            nnet->layers[0].neurons[i].output = inputs[i];//将输入序列依次赋值给网络第一层各个神经元的输出元素
        }
        for (int i = 1; i < nnet->numberOfLayers; i++) //遍历网络的所有层
        {
            for (int j = 0; j < nnet->layers[i].numberOfNeurons; j++)
                //遍历每层的所有神经元,由于i从1开始,因此这里从第二层开始遍历
            {
                nnet->layers[i].neurons[j].weights = (double*)malloc(nnet->layers[i - 1].numberOfNeurons * sizeof(double));
                //为每个神经元的权值参数申请一个双精度的存储空间,个数为上一层神经元个数
                //因为权重值是与上一层的输出进行结合计算的
                //且本层每一个神经元都默认与上一层的每个输出进行计算,因此是神经元里的权重数组
                //因此个数应为上一层的神经元个数,且第一层只有输入作为输出,不用权重处理
                double input = 0;//初定义输入为0
                for (int kk = 0; kk < nnet->layers[i - 1].numberOfNeurons; kk++)
                    //当kk小于上一层的神经元个数时进行循环(因为从第二层开始遍历)
                {
                    double  weight = randomWeight();//定义一个权重数进行随机赋值
                    nnet->layers[i].neurons[j].weights[kk] = weight;//将这个随机数赋值给权重数组的依次位
                    input += nnet->layers[i - 1].neurons[kk].output*weight;//将权重值和上一层对应的神经元的输出进行乘积计算并累加     
                }
                nnet->layers[i].neurons[j].input = input;//将输入累加值赋值为此神经元的输入值
                nnet->layers[i].neurons[j].output = sigmoid(input);//用rule函数对输入值进行计算得到本神经元的输出值
            }
        }
    }
    
    void feedforward(NNET *nnet) //前向网络
    {
        for (int i = 1; i < nnet->numberOfLayers; i++) //遍历每层,从第二层开始
        {
            for (int j = 0; j < nnet->layers[i].numberOfNeurons; j++) //遍历每个神经元,从第一个开始
            {
                double input = 0;//输入初始为0
                for (int kk = 0; kk < nnet->layers[i - 1].numberOfNeurons; kk++) //根据上一层的神经元个数配置权重数组
                {
                    double  weight = nnet->layers[i].neurons[j].weights[kk];
                    input += nnet->layers[i - 1].neurons[kk].output*weight;
                }
                nnet->layers[i].neurons[j].input = input;//累加为函数输入
                nnet->layers[i].neurons[j].output = sigmoid(input);//用rule函数求输出
            }
        }
    }
    
    void feedforwardWithiInput(NNET *nnet, double* input) //带有初始输入的前向网络
    {
        for (int i = 0; i < nnet->layers[0].numberOfNeurons; i++) //遍历第一层的神经元
        {
            nnet->layers[0].neurons[i].output = input[i];//将输入作为第一层神经元输出
        }
        for (int i = 1; i < nnet->numberOfLayers; i++) //从第二层开始遍历神经层,类似地,进行参数配置
        {
            for (int j = 0; j < nnet->layers[i].numberOfNeurons; j++)
            {
                double input = 0;
                for (int kk = 0; kk < nnet->layers[i - 1].numberOfNeurons; kk++)
                {
                    double  weight = nnet->layers[i].neurons[j].weights[kk];
                    input += nnet->layers[i - 1].neurons[kk].output*weight;
                }
                nnet->layers[i].neurons[j].input = input;
                nnet->layers[i].neurons[j].output = sigmoid(input);
            }
        }
    }
    
    void backprop(NNET *nnet, double* targets) //后向反馈调节,参数为网络头指针和目标值数组
    {
        //double **Errors= (double**)malloc(nnet->numberOfLayers * sizeof(double*));
        int num = nnet->layers[nnet->numberOfLayers - 1].numberOfNeurons;//定义最后一层的神经元个数
        //Errors[nnet->numberOfLayers - 1]=(double*)malloc((num+1)*sizeof(double));
    
        for (int i = 0; i < num; i++)//遍历最后一层的神经元
        {
            double out = nnet->layers[nnet->numberOfLayers - 1].neurons[i].output;//取出out进行赋值
            nnet->layers[nnet->numberOfLayers - 1].neurons[i].Error = out*(1 - out)*(targets[i] - out);//误差计算
        }
    
        for (int i = nnet->numberOfLayers - 1; i >= 0;)
            //从最后一层遍历神经层,直到第一层则停止操作,因为没有更前层用来调节
        {
            if (i != 0)//只要不是第一层
            {
                //  Errors[i - 1] = (double*)malloc(nnet->layers[i - 1].numberOfNeurons * sizeof(double));
                for (int jj = 0; jj < nnet->layers[i - 1].numberOfNeurons; jj++)
                    //前一层的神经元数量,逐个用上一层的神经元对本层所有神经元进行权值调整
                {
                    double temp = 0;//用temp将权值*误差进行累加
                    for (int kk = 0; kk < nnet->layers[i].numberOfNeurons; kk++)//本层神经元数量
                    {
                        temp += nnet->layers[i].neurons[kk].weights[jj] * nnet->layers[i].neurons[kk].Error;
                        nnet->layers[i].neurons[kk].weights[jj] = nnet->layers[i].neurons[kk].weights[jj] + ETA * nnet->layers[i].neurons[kk].Error *nnet->layers[i - 1].neurons[jj].output;
                        //将学习率*当次本神经元的误差 *本神经元的误差所对应的上层神经元的输出 所得值加在权值进行调节;
                    }
                    double out = nnet->layers[i - 1].neurons[jj].output;
                    //用上层一个神经元对一层权值进行了调整之后,取出上一层用来调节的神经元的output
                    nnet->layers[i - 1].neurons[jj].Error = out * (1 - out)*temp;//非末层的误差计算公式不一样
                }
            }
            i--;
        }
    }
    
    int main()
    {
        NNET* net = (NNET*)malloc(sizeof(NNET));//建立网络头指针
        int num = 3;
        int a[4] = { 3, 3, 1 };
        createNetWorks(net, num, a);//建立网络,3层网络,神经元个数分别为3、3、1
    
        double input0[4] = { 1, 1, 1 };
        double input1[4] = { 1, 0, 1 };
        double input2[4] = { 1, 1, 0 };
        double input3[4] = { 0, 1, 1 };//4种输入,3个元素因为第一层有3个神经元
    
        double target0[1] = { 0.8 };
        double target1[1] = { 0.7 };
        double target2[1] = { 0.5 };
        double target3[1] = { 0.3 };//4种输出,一个元素因为最后一层只有1个神经元
    
        init(net, input0);//用第一个输入进行初始化,不能省,
        printf("\n");
        int alpha = 0;
        int flag = 0;
        while (1)
        {
    
            //训练过程
            feedforwardWithiInput(net, input0);//前向一次,后向一次,四个输入输出轮一回
            backprop(net, target0);
    
            feedforwardWithiInput(net, input1);
            backprop(net, target1);
    
            feedforwardWithiInput(net, input2);
            backprop(net, target2);
    
            feedforwardWithiInput(net, input3);
            backprop(net, target3);
    
            alpha++;//迭代次数计数
    
            //测试过程
            feedforwardWithiInput(net, input0);
            if (fabs(net->layers[2].neurons[0].output - target0[0]) >= PRECISION)//精度要求0.00001
            {
                //flag = 1;
                continue;
            }
            feedforwardWithiInput(net, input1);
            if (fabs(net->layers[2].neurons[0].output - target1[0]) >= PRECISION)
            {
                //flag = 1;
                continue;
            }
            feedforwardWithiInput(net, input2);
            if (fabs(net->layers[2].neurons[0].output - target2[0]) >= PRECISION)
            {
                //flag = 1;
                continue;
            }
    
            feedforwardWithiInput(net, input3);
            if (fabs(net->layers[2].neurons[0].output - target3[0]) >= PRECISION)
            {
                //flag = 1;
                continue;
            }
            break;
        }
    
        //输出结果
        printf("\n");
        printf("Numbers of iteration : %d", alpha);
        printf("\n");
        //再次前向计算并输出
        feedforwardWithiInput(net, input0);
        printf(" %f  \n", net->layers[2].neurons[0].output);
        feedforwardWithiInput(net, input1);
        printf(" %f  \n", net->layers[2].neurons[0].output);
        feedforwardWithiInput(net, input2);
        printf(" %f  \n", net->layers[2].neurons[0].output);
        feedforwardWithiInput(net, input3);
        printf(" %f  \n", net->layers[2].neurons[0].output);
        getchar();
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:BP算法的C语言实现(注释详解)

          本文链接:https://www.haomeiwen.com/subject/rhvqzctx.html