如何用C语言编程实现多层前向BP神经网络,用来解决逻辑 XOR 运算和奇偶检验问题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
6.
( 1) 试用 C 语言编程实现多层前向 NN 的 BP 算法。
要求:输入、输出结点数目,隐层数目,及各隐层中结点的数目应为任意整数。
( 2) 试用所编出的 BP 算法程序训练出一个实现 XOR 运算的 2 层前向网络。
( 3) 用所编出的 BP 算法程序训练出输入矢量的维数分别为 n=7和 n=8的两个实现奇偶检验运算(即如题 2.(2)所述)的 2 层前向 NN 。
注: 对第 6 题的要求:
(i) 列表给出训练收敛后的 NN 权值和所用的迭代次数;
(ii) 给出训练收敛后的训练误差和检验误差,及用训练集和检验集做输入时所得到的正确输出率;
(iii) 给出 NN 的学习曲线(即 E(W(k))随迭代次数 k 的变化曲线,该结果应是用计算程序计算和打印出来的曲线,而不要是用手画出的曲线)。
(1)用C 语言编程实现前向NN 的BP 算法 解:
解题思路:先用 C 语言编程实现前向 NN 的 BP 算法,再将误差保存至文本文件,最后用MATLAB 绘制出误差曲线。
(1.1)开发思路
奇偶检验问题可视为 XOR 问题的推广(由 2 输入到 n 输入的推广):若 n 个输入中有奇数个1,则输出为 1;若n 个输入中有偶数个1,则输出为 0。
一个 2 层的 NN 可实现奇偶检验运算。
本文选用2层神经网络,包括隐含层1层,输出层1层,来设计BP 神经网络。
x 1
x 2
x n
2层神经网络
本文隐含层和输出层的激活函数选用Sigmoid 函数,
1
()1s
s e
ϕ-=+,()(1())d s s ds ϕϕϕ=- 其函数曲线如下所示:
由奇偶检验问题的定义: 可定义如下分类函数:
1 y>=1/2
Y()0 y<1/2y ⎧=⎨⎩
其中y 为BP 神经网络的输出值,Y 为分类结果。
(1.2)运行流程
本文的多层前向 NN 的 BP 算法用C 语言编程实现,最后将运行结果保存成数据文件,通过MATLAB绘图显示,其运行流程图如上图所示,其源代码见附录部分。
(1.3)参数设定
输入、输出结点数目,隐层数目,及各隐层中结点的数目应为任意整数,通过宏定义改变相应的值,具体修改方式见《NeuralNetBP.h》文件,在程序运行之前,需要跳转到该文件修改具体的取值。
( 2)试用所编出的 BP 算法程序训练出一个实现 XOR 运算的 2 层前向网络。
解:
利用(1)中BP 算法程序训练出XOR 运算的 2 层前向网络。
(2.1)参数设定
首先进入《NeuralNetBP.h》文件通过宏定义改变相应的值,包括输入、输出结点数目,隐层数目,及各隐层中结点的数目。
//XOR2
#define IN_COUT 2 //输入向量维数
#define OUT_COUT 1 //输出向量维数
#define IMPLY_NUM 1 //隐含层层数
#define SampleTrain 4 //训练样本数量
#define SampleTest 4 //测试样本数量
#define NN_ImplyCout 3 //隐含层节点数
#define NN_Rate0.5 //学习速率
#define NN_Error 0.001 //精度控制参数
#define NN_LOOP100000 //最大循环次数
(2.1)程序训练结果
对所有样本进行训练,取隐含层节点数为3,运行结果如下:
BP神经网络的学习曲线如下:
可以看出BP神经网络的误差很快收敛至0。
为了测试BP神经网络的有效性,取10次运算的平均值,其正确率如下图所示:
定义正确率:
n A N
=
其中,A 为正确率,n=测试值和真实值相等的数量,N 为参与测试的样本数量。
由上图可以看出,10次测试的正确率都是A=1,有理由相信,BP 神经网络所训练出来的参数是正确的。
(2.3)XOR 运算
使用2层BP 神经网络,NN 的取隐含层节点数为3,示意图如下所示:
x 1
x 2
两层神经网络实现逻辑异或运算(XOR
计算公式如下:
()
()()()
11111111122111112
211
222
2
11113
311
322
2
2121212
111
122
133
1sigmoid sigmoid sigmoid sigmoid y w x w x y w x w x y w x w x y w y w y w y
θθθθ=++=++=++=+++ 经过28582次迭代后,可求得其权值取值如下:
111213000θθθ⎡⎤⎡⎤⎢⎥⎢⎥=⎢⎥⎢⎥⎢⎥⎢⎥⎣⎦
⎣⎦
111112112122113132 4.217332 6.575545 5.675465 5.6381996.662960 4.248221 w w w w w w ⎡⎤-⎡⎤⎢⎥⎢⎥=⎢⎥⎢⎥⎢⎥⎢⎥-⎣
⎦⎢⎥⎣⎦ []222
111213 11.867876 16.301609 11.863687 w w w ⎡⎤=--⎣⎦
21[][0]θ=
( 3) 用所编出的 BP 算法程序训练出输入矢量的维数分别为 n=7和 n=8的两个实现奇偶检验运算(即如题 2.(2)所述)的 2 层前向 NN 。
解:
n=7和 n=8的两个实现奇偶检验运算的 2 层前向 NN ,完全相同,本文以n=7为例进行说明。
(3.1)参数设定
首先进入《NeuralNetBP.h 》文件通过宏定义改变相应的值,包括输入、输出结点数目,隐层数目,及各隐层中结点的数目。
//XOR7
#define IN_COUT 7 //输入向量维数 #define OUT_COUT 1 //输出向量维数 #define IMPLY_NUM
1 //隐含层层数
#define SampleTrain 128 //训练样本数量 用0-127共128组数据全部参加训练 #define SampleTest
128
//测试样本数量 用0-127共128组数据全部参加测试
#define NN_ImplyCout 25 //隐含层节点数 #define NN_Rate 0.4
//学习速率
#define NN_Error 0.001 //精度控制参数 #define NN_LOOP
100000 //最大循环次数
(3.2)程序训练结果
7位2进制数数,共有128个样本,对所有样本进行训练,取隐含层节点数为25,运行结果如下:
经过85857次迭代学习后,收敛至指定误差围。
全部样本参加测试,所有的样本的输出值都能完全和真值吻合,正确率为1。
BP神经网络的学习曲线如下:
可以看出BP神经网络的误差很快收敛至0。
为了测试BP神经网络的有效性,取10次运算的平均值,其正确率如下图所示:
定义正确率:
n
A
N
其中,A为正确率,n=测试值和真实值相等的数量,N为参与测试的样本数量。
由上图可以看出,10次测试的正确率均值为0.967,有理由相信,BP神经网络所训练
出来的参数是正确的。
附录
一、《"NeuralNetBP.h"》
/*
参数定义
*/
#pragma once
#ifndef _NEURALNETBP_H
#define_NEURALNETBP_H
////XOR2
//#define IN_COUT 2 //输入向量维数
//#define OUT_COUT 1 //输出向量维数
//#define IMPLY_NUM 1 //隐含层层数
//
//#define SampleTrain 4 //训练样本数量用0-127共128组数据全部参加训练//#define SampleTest 4 //测试样本数量用0-127共128组数据全部参加测试//
//#define NN_ImplyCout 4//隐含层节点数
//#define NN_Rate 0.5 //学习速率
//#define NN_Error 0.001 //精度控制参数
//#define NN_LOOP 100000 //最大循环次数
/* 参数该变量
输入维数改变时,改变IN_COUT的值即可
同时需要修改SampleTrain、SampleTest、NN_ImplyCout的值;
本程序取:
SampleTrain = 2^IN_COUT
SampleTest = 2^IN_COUT
NN_ImplyCout = (2-4) * IN_COUT
*/
//XOR7
#define IN_COUT7 //输入向量维数
#define OUT_COUT 1 //输出向量维数
#define IMPLY_NUM 1 //隐含层层数
#define SampleTrain128 //训练样本数量用0-127共128组数据全部参加训练#define SampleTest128 //测试样本数量用0-127共128组数据全部参加测试
#define NN_ImplyCout25 //隐含层节点数
#define NN_Rate0.4 //学习速率
#define NN_Error0.001 //精度控制参数
#define NN_LOOP100000 //最大循环次数
typedef struct { //bp人工神经网络结构
int h; //实际使用隐层节点数
double v[IN_COUT][50]; //隐藏层权矩阵i,隐层节点最大数量为50
double w[50][OUT_COUT]; //输出层权矩阵
double a; //学习率
double b; //精度控制参数
int LoopCout; //最大循环次数
int LoopItera; //实际循环次数
double Error[NN_LOOP]; //误差
} bp_nn;
int InitBp(bp_nn *bp); //初始化bp网络
int TrainBp(bp_nn *bp, int x[SampleTrain][IN_COUT], int y[SampleTrain][OUT_COUT]); //训练bp网络,样本为x,理想输出为y
int UseBp(bp_nn *bp, int Input[IN_COUT], double Output[OUT_COUT]); //使用bp网络double TestBp(bp_nn *bp, int x[SampleTest][IN_COUT], int y[SampleTest][OUT_COUT]);//测试bp网络
#endif
二、《"NeuralNetBP.cpp"》
/*
BP人工神经网络基本算法C语言实现
*/
#include<stdlib.h>
#include<math.h>
#include<stdio.h>
#include<time.h>
#include"NeuralNetBP.h"
//神经网络激活函数
double fnet(double net) {
//Sigmoid函数
temp= 1.0 / (1 + exp(-net));
return temp;
}
int InitBp(bp_nn *bp) { //初始化bp网络
//请输入隐层节点数,最大数为50
(*bp).h = NN_ImplyCout;
//请输入学习率
(*bp).a = NN_Rate; //(*bp).a为double型数据,所以必须是lf
//请输入精度控制参数
(*bp).b = NN_Error;
//请输入最大循环次数
(*bp).LoopCout = NN_LOOP;
//产生随机数初始化权值矩阵
int i, j;
srand((unsigned)time(NULL));
for (i = 0; i < IN_COUT; i++)
for (j = 0; j < (*bp).h; j++)
(*bp).v[i][j] = rand() / (double)(RAND_MAX);
for (i = 0; i < (*bp).h; i++)
for (j = 0; j < OUT_COUT; j++)
(*bp).w[i][j] = rand() / (double)(RAND_MAX);
return 1;
}
int TrainBp(bp_nn *bp, int x[SampleTrain][IN_COUT], int y[SampleTrain][OUT_COUT]) {
//训练bp网络,样本为x,理想输出为y
double f = (*bp).b; //精度控制参数
double a = (*bp).a; //学习率
int h = (*bp).h; //隐层节点数
double v[IN_COUT][50], w[50][OUT_COUT]; //权矩阵
double ChgH[50], ChgO[OUT_COUT]; //修改量矩阵
double O1[50], O2[OUT_COUT]; //隐层和输出层输出量
int LoopCout = (*bp).LoopCout; //最大循环次数
int i, j, k, n;
for (i = 0; i < IN_COUT; i++) // 复制结构体中的权矩阵for (j = 0; j < h; j++)
v[i][j] = (*bp).v[i][j];
for (i = 0; i < h; i++)
for (j = 0; j < OUT_COUT; j++)
w[i][j] = (*bp).w[i][j];
double e = f + 1;
for (n = 0; e > f && n < LoopCout; n++)
{ //对每个样本训练网络
e = 0;
for (i = 0; i < SampleTrain; i++)
{
for (k = 0; k < h; k++) { //计算隐层输出向量
temp = 0;
for (j = 0; j < IN_COUT; j++)
temp = temp + x[i][j] * v[j][k];
O1[k] = fnet(temp);
}
for (k = 0; k < OUT_COUT; k++) { //计算输出层输出向量
temp = 0;
for (j = 0; j < h; j++)
temp = temp + O1[j] * w[j][k];
O2[k] = fnet(temp);
}
for (j = 0; j < OUT_COUT; j++) //计算输出层的权修改量ChgO[j] = O2[j] * (1 - O2[j]) * (y[i][j] - O2[j]);
for (j = 0; j < OUT_COUT; j++) //计算输出误差
e = e + (y[i][j] - O2[j]) * (y[i][j] - O2[j]);
for (j = 0; j < h; j++) { //计算隐层权修改量
temp = 0;
for (k = 0; k < OUT_COUT; k++)
temp = temp + w[j][k] * ChgO[k];
ChgH[j] = temp * O1[j] * (1 - O1[j]);
}
for (j = 0; j < h; j++) //修改输出层权矩阵
for (k = 0; k < OUT_COUT; k++)
w[j][k] = w[j][k] + a * O1[j] * ChgO[k];
for (j = 0; j < IN_COUT; j++) //修改隐含层权矩阵
for (k = 0; k < h; k++)
v[j][k] = v[j][k] + a * x[i][j] * ChgH[k];
}
(*bp).Error[n] = e; //记录误差
if (n % 10 == 0)
{
printf("循环次数:%d, 误差 : %f\n",n,e);
}
}
(*bp).LoopItera = n; //实际循环次数
printf("总共循环次数:%d\n", n);
printf("调整后的隐层权矩阵:\n");
for (i = 0; i < IN_COUT; i++) {
for (j = 0; j < h; j++)
printf("%f ", v[i][j]);
printf("\n");
}
printf("调整后的输出层权矩阵:\n");
for (i = 0; i < h; i++) {
for (j = 0; j < OUT_COUT; j++)
printf("%f ", w[i][j]);
printf("\n");
}
for (i = 0; i < IN_COUT; i++) //把结果复制回结构体
for (j = 0; j < h; j++)
(*bp).v[i][j] = v[i][j];
for (i = 0; i < h; i++)
for (j = 0; j < OUT_COUT; j++)
(*bp).w[i][j] = w[i][j];
printf("bp网络训练结束!\n\n");
return 1;
}
int UseBp(bp_nn *bp, int Input[IN_COUT], double Output[OUT_COUT]) { //使用bp网络double O1[50];
double O2[OUT_COUT]; //O1为隐层输出,O2为输出层输出
int i, j;
double temp;
for (i = 0; i < (*bp).h; i++) {
temp = 0;
for (j = 0; j < IN_COUT; j++)
temp += Input[j] * (*bp).v[j][i];
O1[i] = fnet(temp);
}
for (i = 0; i < OUT_COUT; i++) {
temp = 0;
for (j = 0; j < (*bp).h; j++)
temp += O1[j] * (*bp).w[j][i];
O2[i] = fnet(temp);
}
//输出值
for (i = 0; i < OUT_COUT; i++)
{
Output[i] = O2[i];
}
return 1;
}
double TestBp(bp_nn *bp, int x[SampleTest][IN_COUT], int y[SampleTest][OUT_COUT]) { //使用bp网络
int i, j;
int Input[IN_COUT];
double Output[OUT_COUT]; //此处的输出是实际计算输出所以为double型
int yMeasure[SampleTest];
int CorrectN = 0;
double Accuracy = 0; //正确率
for (i = 0; i < SampleTest; i++)
{
for (j = 0; j < IN_COUT; j++)
{
Input[j] = x[i][j];
}
UseBp(bp, Input, Output); //测试bp神经网络子函数
//结果分类
if (Output[OUT_COUT - 1] >= 0.5)
{
yMeasure[i] = 1;
}
else
{
yMeasure[i] = 0;
}
if (y[i][OUT_COUT - 1] == yMeasure[i]) //真值=测量值
{
CorrectN++;
}
}
Accuracy=CorrectN * 1.0 / SampleTest; //计算正确率
//显示测试结果
printf("n=7 时,BPNN 测试结果为:\n");
printf(" 测试样本数:%d\n", SampleTest);
printf(" 正确样本数:%d\n", CorrectN);
printf(" 正确率为:%f\n", Accuracy);
printf("\n\n");
return Accuracy;
}
三、《"main.cpp"》
#include<stdlib.h>
#include<math.h>
#include<stdio.h>
#include<time.h>
#include<memory.h>
#include"NeuralNetBP.h"
#include"HandleFile.h"
#include"Sample.h"
/*奇偶性判断
若 n 个输入中有奇数个 1,则输出为 1;若 n 个输入中有偶数个 1,则输出为 0。
*/
int main()
{
int TrainX[SampleTrain][IN_COUT];
int TrainY[SampleTrain][OUT_COUT];
int TestX[SampleTest][IN_COUT];
int TestY[SampleTest][OUT_COUT];
bp_nn bp;
const int TrainTimes = 1; //训练次数
double Accuracy[TrainTimes];//正确率
SampleTrain_Init(TrainX, TrainY);
SampleTest_Init(TestX, TestY);
//InitBp(&bp); //初始化bp网络结构
//TrainBp(&bp, TrainX, TrainY); //训练bp神经网络
//TestBp(&bp, TestX, TestY); //测试bp神经网络
//多次训练取平均值
for (int i = 0; i < TrainTimes;i++)
{
InitBp(&bp); //初始化bp网络结构
TrainBp(&bp, TrainX, TrainY); //训练bp神经网络
Accuracy[i]=TestBp(&bp, TestX, TestY); //测试bp神经网络
}
//保存数据
WriteToFile_Error(&bp, "Data\\XOR7_Error.txt"); //保存最后一次的误差
WriteToFile_Accuracy("Data\\XOR7_Result.txt", Accuracy, TrainTimes);//保存所有的运行结果
system("pause");
return 1;
}。