详解 BP 神经网络基本原理及 C 语言实现

BP(Back Propagation)即反向传播,指的是一种按照误差反向传播来训练神经网络的方法。而 BP 神经网络即为一种按照误差反向传播的方法训练的神经网络,是一种应用十分广泛的神经网络。

BP 神经网络主要可以解决以下两种问题:

  1. 分类问题:用给定的输入向量和标签训练网络,实现网络对输入向量的合理分类。
  2. 函数逼近问题:用给定的输入向量和输出向量训练网络,实现对函数的逼近。

本文主要介绍 BP 神经网络的基本结构,而后简单介绍用 C 语言实现神经网络必需要用到的语法知识(老手请放心跳过),最后一步一步从构建单个神经元开始到完成整个神经网络的构建和训练。

VS2013 安装及新建工程

安装过程

由于本文主要还是面向初学者,考虑到有些小白朋友的需求,所以还是简单介绍下软件安装和使用,此部分可选择性跳过。

先给出软件的百度云下载链接:VS2013。

下载完成后如下图。

解压后,如下图所示。

双击运行如下的 exe 文件。

出现下图所示的界面,在此可以修改安装位置,建议设置成全英文路径。

之后按照默认设置一路下一步到安装完成即可。但安装完成后默认不会生成桌面快捷方式,需要从开始菜单栏运行,也可以从开始菜单栏创建桌面快捷方式。

新建工程

首先运行VS2013,得到如下界面。

点击左上角的 “文件" -> "新建” -> "项目"。在下图所示的界面上可以修改保存名称和路径。

选择 “visual c++” -> “Win32 控制台应用程序"后点击“确定”,出现如下界面。

单击下一步,如下图所示。

勾选“空项目”后单击“完成”后,出现如下界面,此处重点是观察右侧的工程结构。

我们在右边的“解决方案管理器”选择“源文件”,右键选择“添加” -> “新建项”,如下图所示。

在出现的新界面的下方重命名为 main.cpp,如下图所示。

按照相同的方法在源文件中再添加一个 neuralnetwork.cpp,在头文件中添加 neuralnetwork.h。

最后完成的工程结构非常简单,如下图所示。

我们写点简单的代码,测试下整个工程能否编译通过,三部分的代码如下。

main.cpp

#include <stdio.h>#include "neural_network.h"int main(){    printf("HelloWork\n");
}

neural_network.cpp:

#include "neural_network.h"

neural_network.h:

#ifndef __NEURAL_NETWORK_H#define __NEURAL_NETWORK_H#endif

这是个简单的测试程序,如果一切正常就会输出 HelloWorld,至此软件安装和工程新建就结束了。

BP 神经网络原理

人工神经元模型

人工神经元模型如下图所示。

一般神经元模型包含这样几个要素:

  1. 输入:X1、X2、X3......Xn(可以有多个)。
  2. 权重:W1、W2、W3......Wn。
  3. 偏置:Bias。
  4. 激活函数:f(x)。
  5. 输出:y(仅一个)。

这里需要重点说明的是激活函数。如果不加入激活函数,只是简单的对输入进行加权求和,那么整个模型就是个线性模型,而线性模型的表示能力是非常有限的,因此通过加入激活函数的方式给模型引入非线性因素,以提高模型的表示能力,所以一般情况下会采用非线性函数作为激活函数。常见的激活函数有 Sigmoid、Tanh、Step、ReLU、Softmax 等。

本文只用 Sigmoid 函数,下图给出了 Sigmoid 函数图像。

现在神经元模型的结构比较清楚了,下面看看如何计算模型的输出。通过给定的权重对输入进行加权求和后(默认加上偏置 Bias),再将得到的和作为参数输入一个激活函数,得到的激活函数的输出值即为神经元的输出。

对应的数学公式如下:

下面举个例子来说明一下计算输出的过程。

设 X1=1,X2=0,X3=0,W1=0.1,W2=0.2,W3=0.3,Bias=0.1,f(x)=x。

由上面给出的计算公式可得:

z = X1W1+X2W2+X3*W3+Bias = 0.1+0+0+0.1 = 0.2 y = f(z) = z = 0.2

学过初高中生物的朋友应该可以看出,人工神经元模型的 X1、X2、X3 作为输入,和生物神经元的树突是非常相似的。而模型的 y 作为唯一的输出,和生物神经元的轴突也是非常相似。仔细观察对比还能发现更多的相似之处,此处不做更多讨论。

下面简单介绍一下人工神经元模型的训练方法。为了减少不必要的演示计算步骤,这里我们用只有两个输入一个输出(只有 X1、X2)的模型进行演示,方法如下。

先给模型一个任意权值和偏置,设 W1=0,W2=0,Bias=0,再设激活函数为:

再给定两组训练用的数据,模拟一个简单的二分类问题:

第一组:data1=1,data2=1,期望输出aim_output1=1 第二组:data3=-1,data4=-1,期望输出aim_output2=0

然后我们先把第一组 data 代入模型根据前面所说的计算神经元输出的方法进行计算,得到实际输出 real_output1=f(data1W1+data2W2+Bias)=f(0)=0。

实际输出 realoutput1 和期望输出 aimoutput1 之间就存在一个差值 err1=aimoutput1-realoutput1=1。

根据这个差值,可以通过如下公式来修正我们随机给定的权值和偏置:

W1=W1+etaerr1data1 W2=W2+etaerr1data2 Bias=Bias+eta*err1

这里的 eta 表示学习率,一般取 0~1 之间的值。eta 值越大学习速率也越快,也就是每一次训练,权值和偏置的变动越大,但也并不是越大越好。如果 eta 过大容易产生震荡而不能稳定到目标值,若 eta 值越小,则效果相反。这里我们简单的取 eta=1,带入计算式可得经过一次修正过后的权值和偏置:

W1=1,W2=1,Bias=1

注意,这里第一次训练还没结束,还要继续代入第二组的 data,并且要记住 W1、W2、Bias 已经改变,通过相同的方法计算输出:

real_output2=f(data3W1+data4W2+Bias)=f(-1-1+1)=0

计算差值为:

err2=aimoutput2-realoutput2=0

由于实际输出和期望输出没有误差,所以不用修正权值和偏置(也可以看做变化量为0)。

这样第一次训练结束,继续下一次训练。

还是先把第一组 data 代入模型进行计算得到实际输出:

real_output1=f(data1W1+data2W2+Bias)=f(11+11+1)=1

计算差值:

err1=aimoutput1-realoutput1=0

由于实际输出和期望输出没有误差,所以不修正权值和偏置。

再把第二组 data 代入模型得输出:

real_output2=f(data3W1+data4W2+Bias)=f(-1-1+1)=0

计算差值:

err2=aimoutput2-realoutput2=0

由于实际输出和期望输出没有误差,所以不修正权值和偏置。

至此训练完成,这个模型已经能对输入的两组数据准确分类,现在给出解决这个二分类问题的 C 语言代码。

#include <stdio.h>#include <stdlib.h>//定义训练用的数据double data1[2]={1,1};double data2[2]={-1,-1};//定义数据的标准分类double data1_class=1;      //假设data1的类型为1double data2_class=0;      //假设data2的类型为0//定义权重和偏置double w[2]={0,0};         //这里用任意值初始化即可,训练的目的就是自动调整这个值的大小double b=0;  //加权求和double sumfun(double *data,double *weight,double bias){    return (data[0]*weight[0]+data[1]*weight[1]+bias);
}//这里采用阶跃函数作为激活函数double step(double sum){    if(sum>0)        return 1;    else
        return 0;
}int main(){    double sum=0;                //存放加权求和的值
    double output1=0,output2=0;  //把加权求和的值代入激活函数而得到的输出值
    int count=0;                 //训练次数的计数变量
    double err=0;                //计算的误差,用于对权值和偏置的修正
    int flag1=0,flag2=0;         //训练完成的标志,如果某组数据训练结果达标,则把标志置1,否则置0

    while(1)
    {
        sum=sumfun(data1,w,b);  //代入第一组data进行计算
        output1=step(sum);        if(output1==data1_class)//判断输出是否达标,若达标则把标志置1,否则修正权值和偏置
            flag1=1;        else
        {
            flag1=0;
            err=data1_class-output1;
            w[0]=w[0]+err*data1[0];
            w[1]=w[1]+err*data1[1];
            b=b+err;
        }

        sum=sumfun(data2,w,b);  //代入第二组data进行计算
        output2=step(sum);        if(output2==data2_class)//判断输出是否达标,若达标则把标志置1,否则修正权值和偏置
            flag2=1;        else
        {
            flag2=0;
            err=data2_class-output2;
            w[0]=w[0]+err*data2[0];
            w[1]=w[1]+err*data2[1];
            b=b+err;
        }        printf("第%d次训练的输出如下:\n",count+=1);       //输出训练结果
        printf("    第一组data属于%1.0f类\n",output1);        printf("    第一组data属于%1.0f类\n",output2);        if(flag1==1&&flag2==1)   //如果所有数据都训练达标,则直接跳出循环
        {            break;
        }
    }    printf("\n\n模型训练完成!!\n\n");    return 0;
}

运行结果如下图所示。

和我们的计算结果一致,也是第二次训练得出正确的分类。

原文发布于微信公众号 - GitChat精品课(CSDN_Tech)

原文发表时间:2018-04-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AILearning

卷积神经网络

注意:本教程面向TensorFlow 的高级用户,并承担机器学习方面的专业知识和经验。 概观 CIFAR-10分类是机器学习中常见的基准问题。问题是将R...

236100
来自专栏深度学习思考者

DL开源框架Caffe | 模型微调 (finetune)的场景、问题、技巧以及解决方案

前言 什么是模型的微调?   使用别人训练好的网络模型进行训练,前提是必须和别人用同一个网络,因为参数是根据网络而来的。当然最后一层是可以修改的,因为我们...

62960
来自专栏大数据挖掘DT机器学习

Tensorflow:基于LSTM轻松生成各种古诗

RNN不像传统的神经网络-它们的输出输出是固定的,而RNN允许我们输入输出向量序列。RNN是为了对序列数据进行建模而产生的。 样本序列性:样本间存在顺序关系,...

53060
来自专栏AI研习社

TensorFlow | 自己动手写深度学习模型之全连接神经网络

前半个多月总共写了三篇深度学习相关的理论介绍文章,另外两个月前,我们使用逻辑回归算法对sklearn里面的moons数据集进行了分类实验,最终准确率和召回率都达...

584100
来自专栏和蔼的张星的图像处理专栏

4. 经典卷积网络之AlexNet

原文:《ImageNet Classification with Deep Convolutional Neural Networks》 我没有读原文,这个已...

18120
来自专栏生信小驿站

无监督学习 聚类分析②划分聚类分析

同样是聚类分析,上一次介绍的是层次聚类分法,这种方法输出的聚类树状图是其最大的优点,但是层次分析法的缺点就在于适合的样本数比较小,大概在150个左右。所以,当我...

10510
来自专栏云时之间

深度学习与神经网络:mnist数据集实现手写数字识别

对于mnist数据集,具体的一些介绍我这里就不搬运过来了,这里我直接说我们如何在TensorFlow上使用mnist数据集.

413110
来自专栏ATYUN订阅号

词序:神经网络能按正确的顺序排列单词吗?

当学习第二语言时,最困难的挑战之一可能是熟悉单词顺序。词序在机器翻译中也很重要,因为翻译大致上是一种处理目标语言词汇的过程,它与源语言是对等的。也许你已经做过一...

35340
来自专栏机器之心

教程 | 使用Keras实现多输出分类:用单个模型同时执行两个独立分类任务

之前我们介绍了使用 Keras 和深度学习的多标签分类(multi-label classification),参阅 https://goo.gl/e8RXtV...

74620
来自专栏机器之心

资源 | GitHub新项目:轻松使用多种预训练卷积网络抽取图像特征

选自GitHub 机器之心整理 参与:思源 最近 GitHub 有一个非常有意思的项目,它可以使用多种预训练 TensorFLow 模型计算图像特征。对于每一个...

33660

扫码关注云+社区

领取腾讯云代金券