Alex_McAvoy

想要成为渔夫的猎手

【概述】

反向传播(Error Back Propagation,BP)算法,是迄今为止最成功的神经网络训练算法,其不仅可用于多层前馈神经网络中,还可用于其他神经网络,但通常说到 BP 神经网络时,一般是指用 BP 算法所训练的多层前馈神经网络,此外,在实际应用中,当使用神经网络建模时,大多使用 BP 算法进行训练

BP 算法是一种迭代学习算法,在迭代的每一轮中采用感知机学习算法对参数进行更新,其仍是基于梯度下降法,以目标的负梯度方向对参数进行调整

阅读全文 »

原子操作

CUDA 编程的基本思想利用 GPU 来尽可能地并行执行相同的核函数,对于大多数并行任务,线程间不需要合作或使用其他线程的资源,只需要保证自己能够正常执行即可

但对于某些需要同步执行的操作,例如多个核函数需要对同一个变量进行读取-修改-写入,由于核函数之间是异步的,当试图同时执行时,就会导致出现问题

阅读全文 »

Reference

计算与传输重叠

CPU 与 GPU 间交互时涉及两个引擎:内存复制引擎核函数执行引擎,内存复制引擎负责 CPU 与 GPU 间的数据传输,核函数执行引擎负责 CPU 向 GPU 部署核函数任务

阅读全文 »

1D Grid, 1D Block 向量加法

普通实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <math.h>
#define N 100
const double EPS = 1E-6;

void __global__ add(const double *x, const double *y, double *z, int n) {
// 获取全局索引
const int index = blockDim.x * blockIdx.x + threadIdx.x;

// 步长
int stride = blockDim.x * gridDim.x;
for (int i = index; i < n; i += stride) {
z[i] = x[i] + y[i];
}
}

// 误差检测
void check(const double *z, const int n) {
bool error = false;
double maxError = 0;

for (int i = 0; i < n; i++) {
maxError = fmax(maxError, fabs(z[i]-70));
if (fabs(z[i] - 70) > EPS) {
error = true;
}
}

printf("%s\n", error ? "Errors" : "Pass");
printf("最大误差: %lf\n", maxError);
}

int main() {
const int arraySize = sizeof(double) * N;

// 申请host锁定内存
double *h_x, *h_y, *h_z;
cudaMallocHost(&h_x, arraySize);
cudaMallocHost(&h_y, arraySize);
cudaMallocHost(&h_z, arraySize);

// 初始化数据
for (int i = 0; i < N; i++) {
h_x[i] = 50;
h_y[i] = 20;
}

// 申请device显存
double *d_x, *d_y, *d_z;
cudaMalloc((void **)&d_x, arraySize);
cudaMalloc((void **)&d_y, arraySize);
cudaMalloc((void **)&d_z, arraySize);

// host数据传输到device
cudaMemcpy(d_x, h_x, arraySize, cudaMemcpyHostToDevice);
cudaMemcpy(d_y, h_y, arraySize, cudaMemcpyHostToDevice);

// 核函数执行配置
dim3 blockSize(128);
dim3 gridSize((N + blockSize.x - 1) / blockSize.x);

// 执行核函数
add<<<gridSize, blockSize>>>(d_x, d_y, d_z, N);

// 将device得到的结果传输到host
cudaMemcpy(h_z, d_z, arraySize, cudaMemcpyDeviceToHost);

// 检查执行结果
check(h_z, N);

// 释放host锁定内存
cudaFreeHost(h_x);
cudaFreeHost(h_y);
cudaFreeHost(h_z);

// 释放device显存
cudaFree(d_x);
cudaFree(d_y);
cudaFree(d_z);

return 0;
}
阅读全文 »

Reference

线程层次结构

核函数在 device 端执行时,会启动若干线程,一个核函数所启动的所有线程被称为一个线程网格(Thread Grid),同一个线程网格上的线程共享相同的全局内存空间,每个线程网格又可分为若干线程块(Thread Block),每个线程块中又包含若干线程

阅读全文 »

Reference

函数执行环境标识符

由于 GPU 是异构模型,所以需要区分 host 端和 device 端上的代码,在 CUDA 中是通过函数类型限定词开区别 host 和 device 上的函数,主要的三个函数类型限定词如下:

阅读全文 »

GPU

在 GPU 出现之前,对于各种绘制计算机图形所需的运算,包括顶点设置、光影、像素操作等,都是由 CPU 配合特定软件实现的

图形处理器(Graphic Processing Unit,GPU)本质上是一组图形函数的集合,专门用于处理绘制计算机图形所需的运算,而这些函数由硬件实现,因此,GPU 从某种意义上讲就是为了在图形处理过程中充当主角而出现的

阅读全文 »