并行化实现基于BP神经网络的手写体数字识别

本文介绍并行化实现基于BP神经网络的手写体数字识别,通过MPI和OpenMP在Linux环境下进行。讨论了环境搭建、BP神经网络算法基础、数据处理、并行机制及结果分析。实验表明,在计算量不大且通信开销较大时,OpenMP的并行优化优于MPI。

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

并行化实现基于BP神经网络的手写体数字识别

手写体数字识别可以堪称是神经网络学习的“Hello World” ,我今天要说的是如何实现BP神经网络算法的并行化,我们仍然是以手写体数字识别为例,会给出实现原理与不同参数的实例分析。
并行的实现是基于MPICHOpenMP两种,运行环境是Linux

环境搭建

MPI

MPI的环境搭建说简单也简单,说难也难,优快云上有各种教程,大部分都是对的,我只提一下我在搭建环境的时候遇到的问题以及要注意的地方。
1.如果只在一台机器上跑代码的话,就相对简单一些,只需要下载压缩包,解压,编译安装就好了,这里要注意的是安装的时候,MPI依赖于gcc、g++、Fortran等编译工具,我们大多数人的机器上应该只有C/C++的编译工具,如果我们不打算在Fortran上使用MPI的话,可以选择在安装的时候禁用掉Fortran,好像是在安装命令的末尾加上–disable Fortran就可以了。
2.如果是想搭集群,首先要把MPI安装在相同的目录例如:/usr/local/mpich,然后还要注意要使用相同的用户名进行ssh免密登录的配置。这些东西都有很完整的教程,我在这里就不进行详细说了。

OpenMP

如果你的编译链工具版本够新的话,编译OpenMP只需要加上一句-fopenmp即可

BP神经网络算法基础

1. 算法框架


BP神经网络的过程主要分为两个阶段,第一阶段是信号的前向传播,从输入层经过隐含层,最后到达输出层;第二阶段是误差的反向传播,从输出层到隐含层,最后到输入层,依次调节隐含层到输出层的权重和偏置,输入层到隐含层的权重和偏置。

2.样本训练

正向传播

对每一层遍历每一个神经细胞,做如下操作:

  1. 获取第n个神经细胞的输入权重数组
  2. 遍历输入权重数组每一个输入权重,累加该权重和相应输入的乘积
  3. 将累加后的值通过激活函数,得到当前神经细胞的最终输出
  4. 该输出作为下一层的输入,对下一层重复上述操作,直到输出层输出为止
    激活函数: sigmoid(S型)函数
反向训练

1.首先输入期望输出,同输出层的输出进行计算得到输出误差数组
2.然后对包括输出层的每一层: 遍历当前层的神经细胞,得到该神经细胞的输出,同时利用反向传播激活函数计算反向传播回来的误差, 进行调整权重矩阵。
激活函数: sigmoid(S型)函数的导函数

算法参考博客:
https://blog.youkuaiyun.com/xuanwolanxue/article/details/71565934
https://blog.youkuaiyun.com/qq_41645895/article/details/85265148
https://blog.youkuaiyun.com/u014303046/article/details/7820001

3. 数据处理

1. 训练数据集

简介

样本来源于美国提供的MNIST数据集一共包含7万个样本

数据集包含四个二进制文件:
train-images-idx3-ubyte: training set images #训练集图片
train-labels-idx1-ubyte: training set labels #训练集标签
t10k-images-idx3-ubyte: test set images #测试集图片
t10k-labels-idx1-ubyte: test set labels #测试集标签
训练集有60000个训练样本,测试集有10000个样本

文件的格式如下
TRAINING SET IMAGE FILE (train-images-idx3-ubyte):
[offset] [type] [value] [description]
0000 32 bit integer 0x00000803(2051) magic number #文件头魔数
0004 32 bit integer 60000 number of images #图像个数
0008 32 bit integer 28 number of rows #图像宽度
0012 32 bit integer 28 number of columns #图像高度
0016 unsigned byte ?? pixel #图像像素值
0017 unsigned byte ?? pixel
………
xxxx unsigned byte ?? pixel
数据来源:http://yann.lecun.com/exdb/mnist/

读取数据

数据集的读取单独设置一个类,每次读数据传进一个index参数,index表示要读取文件中第几张图片,这就需要用到C++文件流的seekg()函数来索引到正确的位置,seekg()函数有两种形式的重载,我们采用的单参数重载,参数是相对于文件初始的偏移量。
偏移量 = 文件头大小 + (index - 1) * 图片大小

bool dataLoader::readIndex(int* label, int pos) {
   
   
	if (mLabelFile.is_open() && !mLabelFile.eof()) {
   
   
		mLabelFile.seekg(mLableStartPos + pos*mLabelLen);
		mLabelFile.read((char*)label, mLabelLen);
		return mLabelFile.gcount() == mLabelLen;
	}
	return false;
}

bool dataLoader::readImage(char imageBuf[], int pos) {
   
   
	if (mImageFile.is_open() && !mImageFile.eof()) {
   
   
		mImageFile.seekg(mImageStartPos + pos*mImageLen);
		mImageFile.read(imageBuf, mImageLen);

		return mImageFile.gcount() == mImageLen;
	}
	return false;
}
//label 用于保存标签 imagebuf保存图片像素 pos表示需要读取数据集中第几张图片
bool dataLoader::read(int* label, char imageBuf[], int pos) {
   
   
	if (readIndex(label, pos)) {
   
   
		return readImage(imageBuf, pos);
	}

	return false;
}

2. 算法相关参数声明

样本参数:
每个图片样本从二进制文件中读取的格式是unsigned char [28 * 28] (图片大小为28 * 28)
然后对样本的像素矩阵进行归一化处理,转成double数组:像素值大于128置1否则置0

inline void preProcessInputData(const unsigned char src[], double out[], int size) {
   
   
    for (int i = 0; i < size; i++) {
   
   
        out[i] = (src[i] >= 128) ? 1.0 : 0.0;
    }
}

权重参数:
神经网络的每一层都有一个二维的权重数组,在隐藏层每一个神经细胞都会有28*28个输入,每个细胞对应一个输出,所以输出层的每个神经细胞对应有隐藏层细胞总数个的输入。权重的初始化是由随机数生成。

// 随机整数数[x, y]
inline int RandInt(int x, int y)
{
   
    
	return rand() % (y - x + 1) + x; 
}

// 随机浮点数(0, 1)
inline double RandFloat()
{
   
    
	return (rand()) / (RAND_MAX + 1.0); 
}

// 随机布尔值
inline bool RandBool()
{
   
   
	return RandInt(0
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值