1. cuBLAS
cuBLAS背景:是一个BLAS在cuda中的实现,允许用户使用NVIDIA的GPU的计算资源。使用cuBLAS 的时候,应用程序应该分配矩阵或向量所需的GPU内存空间,并加载数据,调用所需的cuBLAS函数,然后从GPU的内存空间上传计算结果至主机,cuBLAS API也提供一些帮助函数来写或者读取数据从GPU中。
(1)基本特点
- 列优先的数组,索引以1为基准
- 头文件 include “cublas_v2.h”
- 三类函数(向量标量、向量矩阵、矩阵矩阵)
- 官方学习网站:cublas
(2)cuBLAS实现矩阵乘法
- 准备矩阵A、B、C及其内存,以及使用的线程块及网格
- 创建句柄:cublasHandle_t handle; cublasCreate(&handle)
- 调用计算函数:cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, *B, n, *A, k, &beta, *C, n);
- 销毁句柄:cublasDestroy(handle);
- 回收计算结果:(顺序可以和销毁句柄交换)
cuBLAS 辅助函数
• 句柄管理函数cublasCreate( ), cublasDestroy( )
• cublasStatus_t cublasCreate(cublasHandle_t *handle)
• cublasStatus_t cublasDestroy(cublasHandle_t handle)
• 初始化CUBLAS库,并为保存CUBLAS库上下文创建一个句柄。 它在主机和设备上分配硬件资源,并且
在进行任何其他CUBLAS库调用时必须使用它。 CUBLAS库上下文绑定到当前CUDA设备。要在多个设
备上使用该库, 需要为每个设备创建一个CUBLAS句柄。
• 创建句柄的函数 cublasCreate( ) 会返回一个 cublasStatus_t 类型的值,用来判断句柄是否创建成功
• 流管理函数cublasSetStream(), cublasGetStream()
• cublasStatus_t cublasSetStream(cublasHandle_t handle, cudaStream_t streamId)
• cublasStatus_t cublasGetStream(cublasHandle_t handle, cudaStream_t *streamId)
(3)Cublas level1函数:标量
• cublasStatus_t cublasIsamax(cublasHandle_t handle, int n, const float *x, int incx, int *result)
• cublasStatus_t cublasIsamin(cublasHandle_t handle, int n, const float *x, int incx, int *result)
• 实现功能: result = max/min( x )
• 参数意义
• Incx:x的存储间隔(如果是行存储,值就是行数,如果是列存储,那就是1)
(4)Cublas level2函数:矩阵向量
cublasStatus_t cublasSgemv(cublasHandle_t handle, cublasOperation_t trans, int m, int n, const float *alpha, const float *A, int lda, const float *x, int incx, const float *beta, float *y, int incy)
• 实现功能: y = alpha * op ( A ) * x + beta * y
• 参数意义
• Lda:A的leading dimension,若转置按行优先,则leading dimension为A的列数
• Incx/incy:x/y的存储间隔
(5)Cublas level3函数:矩阵矩阵
cublasStatus_t cublasSgemm(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,const float *alpha, const float *A, int lda, const float *B, int ldb, const float beta, floatC, int ldc)
• 实现功能: C = alpha * op ( A ) * op ( B ) + beta * C
• 参数意义
• alpha和beta是标量, A B C是以列优先存储的矩阵
• 如果 transa的参数是CUBLAS_OP_N 则op(A) = A ,如果是CUBLAS_OP_T 则op(A)=A的转置
• 如果 transb的参数是CUBLAS_OP_N 则op(B) = B ,如果是CUBLAS_OP_T 则op(B)=B的转置
• Lda/Ldb:A/B的leading dimension,若转置按行优先,则leading dimension为A/B的列数
• Ldc:C的leading dimension,C矩阵一定按列优先,则leading dimension为C的行数
具体的代码实现
float *d_A, *d_B, *d_C;
unsigned int size_C = ms.wc * ms.hc;
unsigned int mem_size_C = sizeof(float) * size_C;
float *h_CUBLAS = (float *) malloc(mem_size_C);
cudaMalloc((void **) &d_A, mem_size_A);
cudaMalloc((void **) &d_B, mem_size_B);
cudaMemcpy(d_A, h_A, mem_size_A, cudaMemcpyHostToDevice);
cudaMemcpy(d_B, h_B, mem_size_B, cudaMemcpyHostToDevice);
cudaMalloc((void **) &d_C, mem_size_C);
dim3 threads(1,1);
dim3 grid(1,1);
//cuBLAS代码(具体可以看实现代码)
const float alpha = 1.0f;
const float beta = 0.0f;
int m = A.row, n = B.col, k = A.col;
cublasHandle_t handle;
cublasCreate(&handle);
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, n, m, k, &alpha, d_B, n,
d_A, k, &beta, d_C, n);//C=AB->C_T=B_T*A_T
cublasDestroy(handle);
cudaMemcpy(h_CUBLAS, d_C, mem_size_C, cudaMemcpyDeviceToHost);
(6)其他矩阵计算相关函数
cublasStatus_t cublasSgemmBatched(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb, int m, int n, int k,const float alpha, const floatAarray[], int lda, const float *Barray[], int ldb, const float beta,floatCarray[], int ldc,int batchCount)
• 实现功能: C[i] = alpha * op ( A[i] ) * op ( B[i] ) + beta * C[i]
• 参数意义
• Lda/Ldb:A/B的leading dimension,若转置按行优先,则leading dimension为A/B的列数
• Ldc:C的leading dimension,C矩阵一定按列优先,则leading dimension为C的行数
• Batchcount:批处理数量
cublasStatus_t cublasSgemmStridedBatched(cublasHandle_t handle,cublasOperation_t transa, cublasOperation_t transb,int m, int n, int k,const float *alpha, const float *A, int lda, long long int strideA,const float *B, int ldb, long long int strideB, const float *beta,float *C, int ldc, long long int strideC,int batchCount)
• 实现功能: C +istrideC= alpha * op ( A+istrideA) * op ( B +istrideB) + beta * (C +istrideC)
• 参数意义
• alpha和beta是标量, A B C是以列优先存储的矩阵
• 如果 transa的参数是CUBLAS_OP_N 则op(A) = A ,如果是CUBLAS_OP_T 则op(A)=A的转置
• 如果 transb的参数是CUBLAS_OP_N 则op(B) = B ,如果是CUBLAS_OP_T 则op(B)=B的转置
cublasStatus_t cublasGemmEx(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb,
int m, int n, int k, const void *alpha, const void *A, cudaDataType_t Atype, int lda, const void *B, cudaDataType_t Btype, int ldb, const void *beta, void *C, cudaDataType_t Ctype, int ldc, cudaDataType_t computeType,
cublasGemmAlgo_t algo)
• 实现功能: C = alpha * op ( A ) * op ( B ) + beta * C
• 参数意义
• alpha和beta是标量, A B C是以列优先存储的矩阵
• 如果 transa的参数是CUBLAS_OP_N 则op(A) = A ,如果是CUBLAS_OP_T 则op(A)=A的转置
• 如果 transb的参数是CUBLAS_OP_N 则op(B) = B ,如果是CUBLAS_OP_T 则op(B)=B的转置
1.cuDNN
NVIDIA cuDNN是用于深度神经网络的GPU加速库。它强调性能、易用性和低内存开销,NVIDIA cuDNN可以集成到更高级别的机器学习框架中。
常用神经网络组件:
• 常用语前向后向卷积网络
• 前像后向pooling
• 前向后向softmax
• 前向后向神经元激活
• Rectified linear (ReLU)、Hyperbolic tangent (TANH)
• Tensor transformation functions
• LRN, LCN and batch normalization forward and backward
头文件 include "cudnn.h“
学习网站: https://docs.nvidia.com/deeplearning/cudnn/
(1)Cudnn实现卷积神经网络
创建cuDNN句柄
• cudnnStatus_t cudnnCreate(cudnnHandle_t *handle)
以Host方式调用在Device上运行的函数
• 比如卷积运算:cudnnConvolutionForward等
释放cuDNN句柄
• cudnnStatus_t cudnnDestroy(cudnnHandle_t handle)
将CUDA流设置&返回成cudnn句柄
• cudnnStatus_t cudnnSetStream( cudnnHandle_t handle, cudaStream_t streamId)
• cudnnStatus_t cudnnGetStream( cudnnHandle_t handle, cudaStream_t *streamId)
#include <iostream>
#include <cudnn.h>
int main(int argc, const char **argv){
cudnnStatus_t cudnn_re;
cudnnHandle_t h_cudnn;
cudnn_re = cudnnCreate(&h_cudnn);
if(cudnn_re != CUDNN_STATUS_SUCCESS){
std::cout << "创建cuDNN上下文失败!" << std::endl;
}
// =========================================cudnn操作
// 释放cuDNN
cudnnDestroy(h_cudnn);
}
使用的卷积神经网络函数:
cudnnStatus_t cudnnConvolutionForward(
cudnnHandle_t handle, //
const void *alpha, // 卷积相乘的权重 alpha
const cudnnTensorDescriptor_t xDesc,// x的描述
const void *x, // 输入张量x
const cudnnFilterDescriptor_t wDesc, // 卷积核对应描述
const void *w, // 卷积核
const cudnnConvolutionDescriptor_t convDesc, // 卷积算法对应的描述
cudnnConvolutionFwdAlgo_t algo, // 卷积算法
void *workSpace, // 工作空间
size_t workSpaceSizeInBytes, // 工作空间大小
const void *beta, // 原始地址
const cudnnTensorDescriptor_t yDesc,//输出tensor的描述
void *y //输出结果的tensor张量
)
(2)Cudnn实现卷积神经网络–具体代码流程
void cudnn_conv(){
cudnnStatus_t status; cudnnHandle_t h_cudnn; cudnnCreate(&h_cudnn);
cudnnTensorDescriptor_t ts_in, ts_out; // 1. 定义一个张量对象
status = cudnnCreateTensorDescriptor(&ts_in); // 2. 创建输入张量
if(CUDNN_STATUS_SUCCESS == status){ std::cout << “创建输入张量成功!” << std::endl; }
status = cudnnSetTensor4dDescriptor(// 3. 设置输入张量数据
ts_in, // 张量对象
CUDNN_TENSOR_NHWC, // 张量的数据布局
CUDNN_DATA_FLOAT, // 张量的数据类型
1, // 图像数量
3, // 图像通道
1080, // 图像高度
1920 // 图像宽度);
if(CUDNN_STATUS_SUCCESS == status) std::cout << "创建输出张量成功!" << std::endl;
cudnnCreateTensorDescriptor(&ts_out); // 设置输出张量
status = cudnnSetTensor4dDescriptor(ts_out, CUDNN_TENSOR_NHWC, CUDNN_DATA_FLOAT, 1, 3, 1080, 1920);
cudnnFilterDescriptor_t kernel;
cudnnCreateFilterDescriptor(&kernel); // 创建卷积核
status = cudnnSetFilter4dDescriptor(kernel, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NHWC, 3, 3, 3, 3);
cudnnConvolutionDescriptor_t conv; // 创建卷积
status = cudnnCreateConvolutionDescriptor(&conv); // 设置卷积
status = cudnnSetConvolution2dDescriptor(conv, 1, 1, 1, 1, 1, 1, CUDNN_CROSS_CORRELATION, CUDNN_DATA_FLOAT);
cudnnConvolutionFwdAlgo_t algo;
status = cudnnGetConvolutionForwardAlgorithm(h_cudnn, ts_in, kernel, conv, ts_out, CUDNN_CONVOLUTION_FWD_PREFER_FASTEST, 0, &algo); // 设置算法
size_t workspace_size = 0;
status = cudnnGetConvolutionForwardWorkspaceSize(h_cudnn, ts_in, kernel, conv, ts_out, algo,
&workspace_size);
void * workspace;
cudaMalloc(&workspace, workspace_size);
float alpha = 1.0f; float beta = -100.0f;
status = cudnnConvolutionForward(// 卷积执行
h_cudnn, &alpha, ts_in,
img_gpu, // 输入
kernel,
kernel_gpu, // 核
conv, algo, workspace, workspace_size, &beta,
ts_out, conv_gpu // 输出
);
cudnnDestroy(h_cudnn);
}
(3)Cudnn实现递归神经网络-RNN
在transformer未出来前,主流的三大神经网络为CNN、DNN、RNN
其中DNN可以使用cuBALS进行一个快速的前传,而CNN就是使用上面的方法,接下来就是cudnn实现RNN的前传