CUDA实现的BP神经网络

solin205 2013-12-23 10:24:05
刚开始学习CUDA,用CUDA实现的BP神经网络,发现运算效率很低,时间不但没有比在CPU下少,还是20倍以上,求问如何优化程序

#include <fstream>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <cuda_runtime.h>
#include <sm_20_atomic_functions.h>
#include <cublas_v2.h>
using namespace std;

#define random(x) (rand()%x)

const int total = 489;
const int totalTest = 210;
const int NumIn = 9;
const int NumMid = 50;
const int NumOut = 1;
const char * openFileNameTrain = "D:\\test\\CUDATEST\\DataSet\\BreastCancerWisconsin_train.txt" ;//数据位置
const char * openFileNameTest = "D:\\test\\CUDATEST\\DataSet\\BreastCancerWisconsin_train.txt" ;//数据位置
const double A = 30;
const double B = 20;
double COEF = 0.003; //网络的学习效率
const double TCOEF = 0.001;//网络的阀值调整效率
const double alpha = 0.9;//网络要求精度
const double ERROR = 0.02; // 网络训练中的允许误差
const double ACCURACY = 0.004;//网络要求精度
const int MAX = 3000;

float wITM[NumIn * NumMid]; //权值矩阵,输入层到隐藏层
float wMTO[NumMid * NumOut]; //权值矩阵,隐藏层到输出层
float wcITM[NumIn * NumMid]; //权值矩阵调整量,输入层到隐藏层
float wcMTO[NumMid * NumOut]; //权值矩阵调整量,隐藏层到输出层
float tMid[NumMid]; //隐藏层阈值
float tOut[NumOut]; //输出层阈值
float tcMid[NumMid]; //隐藏层阈值调整量
float tcOut[NumOut]; //输出层阈值调整量
float dMid[NumMid]; //隐藏层误差
float dOut[NumOut]; //输出层误差
float MidIn[NumMid] = {0},MidOut[NumMid]; //隐藏层输入、输出
float OutIn[NumOut],OutOut[NumOut]; //输出层输入、输出
float diff; //输出层误差
float datasetIn[total][NumIn]; //训练数据集输入
float datasetOut[total][NumOut]; //训练数据集输出
int set1[total][NumIn + NumOut + 1]; //数据集
double datasetTest[total][NumIn]; //测试数据集
int res[4];

//数据读取
bool DataLoad()
{
...
}

//计算输出
void culOut(int Index)
{
...
}

//阈值初始化
void InitW()
{
...
}
void InitT()
{
...
}

//计算隐藏层输出
__global__ static void gpu_CalMidOut(float *dev_MidIn, float *dev_MidOut, float *dev_tMid)
{
int Index = blockIdx.x;
dev_MidIn[Index] = dev_MidIn[Index] - dev_tMid[Index];
dev_MidOut[Index] = 30 / ( 1 + exp( - dev_MidIn[Index] / 20 ) );
}

//计算输出层输出
__global__ static void gpu_CalOutOut(float *dev_OutIn, float *dev_OutOut, float *dev_tOut)
{
int Index = blockIdx.x;
dev_OutIn[Index] = dev_OutIn[Index] - dev_tOut[Index];
dev_OutOut[Index] = 30 / ( 1 + exp( - dev_OutIn[Index] / 20 ) );
}

//计算输出误差
void CalDiff(int Index)
{
for(int i = 0; i < NumOut; i++)
{
diff = 0.5 * ( OutOut[i] - datasetOut[Index][i] ) * ( OutOut[i] - datasetOut[Index][i] );
}
}

//计算输出层反向传播误差
__global__ static void gpu_CalDOut(float *dev_OutOut, float *dev_Expect, float *dev_dOut, float *dev_OutIn)
{
int Index = blockIdx.x;
dev_dOut[Index] = ( dev_OutOut[Index] - dev_Expect[Index] ) * (30 / 20) * exp( - dev_OutIn[Index] / 20) / pow( 1 + exp( - dev_OutIn[Index] / 20) , 2 );
}

//计算隐藏层反向传播误差
__global__ static void gpu_CalDMid(float *dev_dOut, float *dev_wMTO, float *dev_dMid, float *dev_MidIn)
{
int Index = blockIdx.x;
float temp = 0;
for(int i = 0; i < NumOut; i++)
{
temp += dev_dOut[i] * dev_wMTO[i * NumMid + Index];
}
dev_dMid[Index] = temp * (30 / 20) * exp( - dev_MidIn[Index] / 20) / pow( 1 + exp( - dev_MidIn[Index] / 20) , 2 );
}

//计算权值矩阵调整量,并调整权值矩阵,隐藏层到输出层
__global__ static void gpu_CalWCMTO(float *dev_dOut, float *dev_MidOut, float *dev_wcMTO, float *dev_wMTO, float *dev_tcOut, float *dev_tOut)
{
int outIndex = blockIdx.x;
int midIndex = threadIdx.x;
int Index = outIndex * blockDim.x + midIndex;
if(outIndex < NumOut && midIndex < NumMid)
{
dev_wcMTO[Index] = - 0.006 * dev_dOut[outIndex] * dev_MidOut[midIndex] + 0.9 * dev_wcMTO[Index];
dev_wMTO[Index] = dev_wMTO[Index] + dev_wcMTO[Index];
}
dev_tcOut[outIndex] = 0.002 * dev_dOut[outIndex];
dev_tOut[outIndex] = dev_tOut[outIndex] + dev_tcOut[outIndex];
}

//计算权值矩阵调整量,并调整权值矩阵,输入层到隐藏层
__global__ static void gpu_CalWCITM(float *dev_dMid, float *dev_Input, float *dev_wcITM, float *dev_wITM, float *dev_tcMid, float *dev_tMid)
{
int midIndex = blockIdx.x;
int inIndex = threadIdx.x;
int Index = midIndex * blockDim.x + inIndex;
if(inIndex < NumIn && midIndex < NumMid)
{
dev_wcITM[Index] = - 0.006 * dev_dMid[midIndex] * dev_Input[inIndex] + 0.9 * dev_wcITM[Index];
dev_wITM[Index] = dev_wITM[Index] + dev_wcITM[Index];
}
dev_tcMid[midIndex] = 0.002 * dev_dMid[midIndex];
dev_tMid[midIndex] = dev_tMid[midIndex] + dev_tcMid[midIndex];
}

//计算阈值调整量,并调整阈值,输出层
__global__ static void gpu_CalDCOut(float *dev_dOut, float *dev_tcOut, float *dev_tOut)
{
int Index = blockIdx.x;
dev_tcOut[Index] = 0.001 * dev_dOut[Index];
dev_tOut[Index] = dev_tOut[Index] + dev_tcOut[Index];
}
//计算阈值调整量,并调整阈值,隐藏层
__global__ static void gpu_CalDCMid(float *dev_dMid, float *dev_tcMid, float *dev_tMid)
{
int Index = blockIdx.x;
dev_tcMid[Index] = 0.001 * dev_dMid[Index];
dev_tMid[Index] = dev_tMid[Index] + dev_tcMid[Index];
}

//清零,隐藏层到输出层权值矩阵,输出层阈值
__global__ static void gpu_ClearWCMTO(float *dev_wcMTO, float *dev_tOut)
{
int outIndex = blockIdx.x;
int midIndex = threadIdx.x;
int Index = outIndex * blockDim.x + midIndex;
dev_wcMTO[Index] = 0;
dev_tOut[outIndex] = 0;
}

//清零,输入层到隐藏层权值矩阵,隐藏层阈值
__global__ static void gpu_ClearWCITM(float *dev_wcITM, float *dev_tMid)
{
int midIndex = blockIdx.x;
int inIndex = threadIdx.x;
int Index = midIndex * blockDim.x + inIndex;
dev_wcITM[Index] = 0;
dev_tMid[midIndex] = 0;
}

double culTotalDiff()
{
double sum = 0;
for(int i = 0; i < total; i++)
{
culOut(i);
for(int j = 0; j < NumOut; j++)
{
sum = sum + pow( OutOut[j] - datasetOut[i][j] , 2);
}
}
return sum / total;
}
int main()
{
...
//神经网络初始化
InitW();
InitT();

//设备端变量定义,添加cublas
cudaEvent_t start,stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start,0);

float *d_Input, *d_wITM, *d_wMTO, *d_wcITM, *d_wcMTO, *d_tMid, *d_tOut, *d_tcMid, *d_tcOut, *d_dMid, *d_dOut, *d_MidIn, *d_MidOut, *d_OutIn, *d_OutOut, *d_Expect, *d_diff;
float p1 = 1.0, p2 = 0.0;
cublasStatus_t status;
cublasHandle_t handle;
status = cublasCreate(&handle);
if (status != CUBLAS_STATUS_SUCCESS)
{
fprintf(stderr, "!!!! CUBLAS initialization error\n");
printf("输入任意字符继续......");
scanf("%d");
return EXIT_FAILURE;
}

cudaMalloc((void **)&d_Input, NumIn * sizeof(float));
cudaMalloc((void **)&d_wITM, NumIn * NumMid * sizeof(float));
cudaMalloc((void **)&d_wMTO, NumMid * NumOut * sizeof(float));
cudaMalloc((void **)&d_wcITM, NumIn * NumMid * sizeof(float));
cudaMalloc((void **)&d_wcMTO, NumMid * NumOut * sizeof(float));
cudaMalloc((void **)&d_tMid, NumMid * sizeof(float));
cudaMalloc((void **)&d_tOut, NumOut * sizeof(float));
cudaMalloc((void **)&d_tcMid, NumMid * sizeof(float));
cudaMalloc((void **)&d_tcOut, NumOut * sizeof(float));
cudaMalloc((void **)&d_dMid, NumMid * sizeof(float));
cudaMalloc((void **)&d_dOut, NumOut * sizeof(float));
cudaMalloc((void **)&d_MidIn, NumMid * sizeof(float));
cudaMalloc((void **)&d_MidOut, NumMid * sizeof(float));
cudaMalloc((void **)&d_OutIn, NumOut * sizeof(float));
cudaMalloc((void **)&d_OutOut, NumOut * sizeof(float));
cudaMalloc((void **)&d_Expect, NumOut * sizeof(float));
cudaMalloc((void **)&d_diff, sizeof(float));

cudaMemcpy(d_wITM, wITM, NumIn * NumMid * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_wMTO, wMTO, NumMid * NumOut * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_tMid, tMid, NumMid * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_tOut, tOut, NumOut * sizeof(float), cudaMemcpyHostToDevice);
int count1 = 0;
for(int i = 0; i < 1; i++)
{
count1 = 0;
for(int j = 0; j < total; j++)
{
cudaMemcpy(d_Input, datasetIn[j], NumIn * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_Expect, datasetOut[j], NumOut * sizeof(float), cudaMemcpyHostToDevice);
gpu_ClearWCMTO<<<NumOut, NumMid>>>(d_wcMTO, d_tOut);
gpu_ClearWCITM<<<NumMid, NumIn>>>(d_wcITM, d_tMid);
cublasSgemm(handle, CUBLAS_OP_T, CUBLAS_OP_N, 1, NumMid, NumIn, &p1, d_Input, NumIn, d_wITM, NumIn, &p2, d_MidIn, 1);
gpu_CalMidOut<<<NumMid, 1>>>(d_MidIn, d_MidOut, d_tMid);
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, 1, NumOut, NumMid, &p1, d_MidOut, 1, d_wMTO, NumMid, &p2, d_OutIn, 1);
gpu_CalOutOut<<<NumOut, 1>>>(d_OutIn, d_OutOut, d_tOut);
cudaMemcpy(OutOut, d_OutOut, NumOut * sizeof(float), cudaMemcpyDeviceToHost);
CalDiff(j);
while(diff > ERROR)
{
gpu_CalDOut<<<NumOut, 1>>>(d_OutOut, d_Expect, d_dOut, d_OutIn);
gpu_CalDMid<<<NumMid, 1>>>(d_dOut, d_wMTO, d_dMid, d_MidIn);
gpu_CalWCMTO<<<NumOut, NumMid>>>(d_dOut, d_MidOut, d_wcMTO, d_wMTO, d_tcOut, d_tOut);
gpu_CalWCITM<<<NumMid, NumIn>>>(d_dMid, d_Input, d_wcITM, d_wITM, d_tcMid, d_tMid);

cublasSgemm(handle, CUBLAS_OP_T, CUBLAS_OP_N, 1, NumMid, NumIn, &p1, d_Input, NumIn, d_wITM, NumIn, &p2, d_MidIn, 1);
gpu_CalMidOut<<<NumMid, 1>>>(d_MidIn, d_MidOut, d_tMid);
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, 1, NumOut, NumMid, &p1, d_MidOut, 1, d_wMTO, NumMid, &p2, d_OutIn, 1);
gpu_CalOutOut<<<NumOut, 1>>>(d_OutIn, d_OutOut, d_tOut);
cudaMemcpy(OutOut, d_OutOut, NumOut * sizeof(float), cudaMemcpyDeviceToHost);
CalDiff(j);
count1 ++;
}
printf("%d %lf %lf %lf\n",count1,datasetIn[j][0],datasetOut[j][0],OutOut[0]);
}
cudaMemcpy(wITM, d_wITM, NumMid * NumIn * sizeof(float), cudaMemcpyDeviceToHost);
cudaMemcpy(wMTO, d_wMTO, NumOut * NumMid * sizeof(float), cudaMemcpyDeviceToHost);
printf("This is %d times training NN...\n",i);
float totaldiff = culTotalDiff();
printf("count: %d\n",count1);
printf("totaldiff: %f\n",totaldiff);
if(totaldiff < ACCURACY)
{
break;
}
}
cudaEventRecord(stop,0);
cudaEventSynchronize(stop);
float elapsedTime;
cudaEventElapsedTime(&elapsedTime,start,stop);
printf("Classify Processed Time : %f\n",elapsedTime);
}
...全文
2169 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
tengwl 2016-03-18
  • 打赏
  • 举报
回复
为什么很多的函数只有blockIdx,没有看到threadIdx
chaofanhe 2016-03-17
  • 打赏
  • 举报
回复
完整的源代码能发一份给我吗,O(∩_∩)O谢谢 eecfhe@foxmail.com
Spidey212 2015-06-15
  • 打赏
  • 举报
回复
引用 4 楼 blackhark1 的回复:
[quote=引用 3 楼 Spidey212 的回复:] 《GPU高性能运算之CUDA》,第四章,CUDA程序优化 以及 http://blog.sina.com.cn/s/blog_8f12464b0100zq92.html 都是必看的
你学cuda多久了?[/quote]1个月了
blackhark1 2015-06-13
  • 打赏
  • 举报
回复
引用 3 楼 Spidey212 的回复:
《GPU高性能运算之CUDA》,第四章,CUDA程序优化 以及 http://blog.sina.com.cn/s/blog_8f12464b0100zq92.html 都是必看的
你学cuda多久了?
Spidey212 2015-06-09
  • 打赏
  • 举报
回复
《GPU高性能运算之CUDA》,第四章,CUDA程序优化 以及 http://blog.sina.com.cn/s/blog_8f12464b0100zq92.html 都是必看的
blackhark1 2015-06-09
  • 打赏
  • 举报
回复
我觉得如果是BP算法本身的优化,从权重初始化方法上优化是目前最有效果的方法吧,BP本身的权重初始化方法存在很多问题,只是通过简单的随机数初始化权重的,效率比较低。我看的paper里权重初始化方法比较应用广泛的是用遗传算法初始化权重,再用这些权重是训练神经网络,收敛速度比较快,正确率也高。 另外一个是从GPU架构上考虑也可以实现优化,从cuda访存方面,一个方法是把block中thread数量设为自己device thread per blcok 数量的整数倍,会加快GPU执行效率,另外一个方法是把训练数据从global memory全部拷到local memory,这样增大内存带宽,减少访存次数,运行效率也会大大提高。
blackhark1 2015-06-09
  • 打赏
  • 举报
回复
完整的源代码能发一份给我吗,O(∩_∩)O谢谢 coldpoint@sina.cn
【资源说明】 1、基于BP神经网络的自动驾驶模型车,包含收集数据、控制模型生成与在线离线自动运行源码+说明.zip 2、该资源包括项目的全部源码,下载可以直接使用! 3、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 4、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于BP神经网络的自动驾驶模型车,包含收集数据、控制模型生成与在线离线自动运行源码+说明.zip # TensorRider 自动驾驶车 ![](pics/TensorRider.jpg) TensorRider是一种基于BP神经网络,对驾驶场景具有学习能力的的简易自动驾驶车模型。目前,TensorRider仅能实现基本的车道保持功能,即在学习了操作者的遥控驾驶行为后,根据经验对类似的场景做出自动驾驶行为。 TensorRider使用Google TensorFlow作为算法框架,可以在CPU或支持CUDA的NVIDIA GPU上进行训练。 完成训练后,可以使用一台计算机(Linux,macOS或Windows)作为TensorRider的计算服务器,令实验小车在线运行,也可以通过在TensorRider的Raspberry Pi上连接Intel Movidius Neural Compute Stick(NCS),实现离线运行。 # 硬件需求 * TensorRider自动驾驶车模型,包括Raspberry Pi,摄像头,电机驱动电路等 * 运行Linux,Apple macOS 10.12+或Microsoft Windows 7+的计算机 * 带有模拟摇杆的游戏控制器(手柄) * (非必需)兼容CUDA的NVIDIA GPU,Intel Movidius NCS * 连接质量良好的无线局域网 # 使用方法 TensorRider实现基于学习的自动驾驶分为3个阶段:收集数据,建立模型和自动运行。 ## 1.收集数据 在这一步骤中,车辆模型在人工指引下,边行驶边记录摄像头拍摄的图像,同时记录下拍摄图像时车辆模型的转向角度。当收集了足够多的图像-转向角度数据后,即可以这些数据为依据,训练神经网络。 我们假设你已经在计算机上安装好了```Python3```和```Jupyter Notebook```。首先在计算机端安装依赖的python软件包。 ``` pip3 install pygame ``` *注:我们假设你已掌握使用pip安装软件包的方法,以及在虚拟环境中安装软件包的方法。例如根据你使用的python环境的不同,你可能需要使用conda install或pip install。* 目录```Step_1_Data_Collection```内包含进行数据采集所需的程序。先使用SFTP或NFS等方式,将```copy_to_rpi```目录中的文件传输至实验小车端的Raspberry Pi上。在Raspberry Pi上连接一个使用FAT32文件系统的USB闪存盘,并启动Raspberry Pi。 使用SSH登录Raspberry Pi,建立USB闪存盘的挂载点 ``` sudo mkdir /mnt/pdisk ``` 然后运行 ``` sudo mount -o uid=pi,gid=pi /dev/sda1 /mnt/pdisk/ ``` 挂载USB闪存盘。 *你也可以选择将数据存储在Raspberry Pi的SD卡,甚至内存文件系统中。但是比起使用网络传输采集的大量文件,使用USB闪存盘拷贝是更加快速的方式。如果你希望变更文件的存储位置,可在```rpi.py```的第161--163行中更改。* 挂载完成后运行```rpi.py```。如果实验小车上安装了状态指示灯,在接收到控制数据之前,红色指示灯每3秒闪烁一次。接着,在计算机上,在```transmitter.py```的第9行写入小车的局域网IP地址,然后运行```transmitter.py```。你将看到终端持续打印出读取到的手柄数据。如果与实验小车的连接正常,小车上的指示灯将转为绿色闪烁。 向前轻推控制器的左侧手柄,实验小车开始加速行驶,向后拉左侧手柄可使小车减速。向左、右方向推动右侧手柄,可使实验小车转向。若需要急停,可同时按下```L2```+```R2```键,实验小车立即停车。 熟悉实验小车的操控方法后,可在适当的时机按下```START```键,实验小车开始收集行驶数据。再按一次```START```停止收集数据。 *数据收集建议至少持续30min。在收集数据时,需要使用较慢的恒定速度行驶,因为小车的转弯半径与速度相关。可以同时按下`

580

社区成员

发帖
与我相关
我的任务
社区描述
CUDA™是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。 它包含了CUDA指令集架构(ISA)以及GPU内部的并行计算引擎。
社区管理员
  • CUDA编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧