文章目录
CUDNN详解
一、引言
cuDNN(CUDA Deep Neural Network library)是NVIDIA为深度神经网络开发的GPU加速库。它提供了高效实现深度学习算法所需的基本构建块,如卷积、池化、激活函数等。cuDNN通过优化这些操作,显著提高了深度学习模型的训练和推理速度,是深度学习框架(如TensorFlow、PyTorch)在GPU上高效运行的关键组件。
二、cuDNN的基本使用
1、初始化cuDNN句柄
在使用cuDNN之前,需要创建一个cuDNN句柄,该句柄用于管理cuDNN的上下文。例如:
cpp
cudnnHandle_t cudnn;
checkCUDNN(cudnnCreate(&cudnn));
这里使用了checkCUDNN
宏来检查cuDNN函数调用的返回值,确保操作成功。
2、创建和设置描述符
cuDNN使用描述符(Descriptor)来描述张量(Tensor)、卷积核(Filter)和卷积操作(Convolution)等。例如,创建一个输入张量描述符并设置其属性:
cpp
cudnnTensorDescriptor_t input_descriptor;
checkCUDNN(cudnnCreateTensorDescriptor(&input_descriptor));
checkCUDNN(cudnnSetTensor4dDescriptor(input_descriptor,
CUDNN_TENSOR_NCHW,
CUDNN_DATA_FLOAT,
batch_size, channels, height, width));
这里设置了输入张量的格式(NCHW)、数据类型(float)和维度(批量大小、通道数、高度、宽度)。
三、执行卷积操作
1、设置卷积参数
在执行卷积操作之前,需要设置卷积核描述符和卷积描述符。例如:
cpp
cudnnFilterDescriptor_t kernel_descriptor;
checkCUDNN(cudnnCreateFilterDescriptor(&kernel_descriptor));
checkCUDNN(cudnnSetFilter4dDescriptor(kernel_descriptor,
CUDNN_DATA_FLOAT,
CUDNN_TENSOR_NCHW,
output_channels, input_channels, kernel_height, kernel_width));
cudnnConvolutionDescriptor_t convolution_descriptor;
checkCUDNN(cudnnCreateConvolutionDescriptor(&convolution_descriptor));
checkCUDNN(cudnnSetConvolution2dDescriptor(convolution_descriptor,
padding_height, padding_width,
vertical_stride, horizontal_stride,
dilation_height, dilation_width,
CUDNN_CROSS_CORRELATION,
CUDNN_DATA_FLOAT));
这里设置了卷积核的大小、输入和输出通道数、步长、填充等参数。
2、选择卷积算法
cuDNN提供了多种卷积算法,可以选择最适合当前硬件和数据的算法。例如:
cpp
int algo_count;
checkCUDNN(cudnnGetConvolutionForwardAlgorithmMaxCount(cudnn, &algo_count));
cudnnConvolutionFwdAlgo_t algo;
checkCUDNN(cudnnGetConvolutionForwardAlgorithm(cudnn,
input_descriptor,
kernel_descriptor,
convolution_descriptor,
output_descriptor,
CUDNN_CONVOLUTION_FWD_PREFER_FASTEST,
0,
&algo));
这里选择了最快的卷积算法。
3、执行卷积
最后,使用选择的算法执行卷积操作:
cpp
float alpha = 1.0f, beta = 0.0f;
checkCUDNN(cudnnConvolutionForward(cudnn,
&alpha,
input_descriptor, input_data,
kernel_descriptor, kernel_data,
convolution_descriptor,
algo,
workspace, workspace_size,
&beta,
output_descriptor, output_data));
这里alpha
和beta
是缩放因子,input_data
、kernel_data
和output_data
分别是输入、卷积核和输出数据的指针。
四、使用示例
以下是一个完整的cuDNN卷积操作示例,包括初始化、设置描述符、执行卷积和清理资源:
cpp
#include <iostream>
#include <cuda_runtime.h>
#include <cudnn.h>
#define CHECK_CUDNN(call) \
{ \
cudnnStatus_t status = call; \
if (status != CUDNN_STATUS_SUCCESS) { \
std::cerr << "cuDNN error: " << cudnnGetErrorString(status) << std::endl; \
exit(1); \
} \
}
int main() {
int batch_size = 1, channels = 1, height = 28, width = 28;
int output_channels = 16, kernel_height = 3, kernel_width = 3;
// 创建cuDNN句柄
cudnnHandle_t cudnn;
CHECK_CUDNN(cudnnCreate(&cudnn));
// 创建输入张量描述符
cudnnTensorDescriptor_t input_descriptor;
CHECK_CUDNN(cudnnCreateTensorDescriptor(&input_descriptor));
CHECK_CUDNN(cudnnSetTensor4dDescriptor(input_descriptor,
CUDNN_TENSOR_NCHW,
CUDNN_DATA_FLOAT,
batch_size, channels, height, width));
// 创建输出张量描述符
cudnnTensorDescriptor_t output_descriptor;
CHECK_CUDNN(cudnnCreateTensorDescriptor(&output_descriptor));
CHECK_CUDNN(cudnnSetTensor4dDescriptor(output_descriptor,
CUDNN_TENSOR_NCHW,
CUDNN_DATA_FLOAT,
batch_size, output_channels, height, width));
// 创建卷积核描述符
cudnnFilterDescriptor_t kernel_descriptor;
CHECK_CUDNN(cudnnCreateFilterDescriptor(&kernel_descriptor));
CHECK_CUDNN(cudnnSetFilter4dDescriptor(kernel_descriptor,
CUDNN_DATA_FLOAT,
CUDNN_TENSOR_NCHW,
output_channels, channels, kernel_height, kernel_width));
// 创建卷积描述符
cudnnConvolutionDescriptor_t convolution_descriptor;
CHECK_CUDNN(cudnnCreateConvolutionDescriptor(&convolution_descriptor));
CHECK_CUDNN(cudnnSetConvolution2dDescriptor(convolution_descriptor,
1, 1, 1, 1, 1, 1,
CUDNN_CROSS_CORRELATION,
CUDNN_DATA_FLOAT));
// 选择卷积算法
cudnnConvolutionFwdAlgo_t algo;
CHECK_CUDNN(cudnnGetConvolutionForwardAlgorithm(cudnn,
input_descriptor,
kernel_descriptor,
convolution_descriptor,
output_descriptor,
CUDNN_CONVOLUTION_FWD_PREFER_FASTEST,
0,
&algo));
// 分配内存
float *input_data, *kernel_data, *output_data, *workspace;
size_t workspace_size;
cudaMalloc(&input_data, batch_size * channels * height * width * sizeof(float));
cudaMalloc(&kernel_data, output_channels * channels * kernel_height * kernel_width * sizeof(float));
cudaMalloc(&output_data, batch_size * output_channels * height * width * sizeof(float));
CHECK_CUDNN(cudnnGetConvolutionForwardWorkspaceSize(cudnn,
input_descriptor,
kernel_descriptor,
convolution_descriptor,
output_descriptor,
algo,
&workspace_size));
cudaMalloc(&workspace, workspace_size);
// 执行卷积
float alpha = 1.0f, beta = 0.0f;
CHECK_CUDNN(cudnnConvolutionForward(cudnn,
&alpha,
input_descriptor, input_data,
kernel_descriptor, kernel_data,
convolution_descriptor,
algo,
workspace, workspace_size,
&beta,
output_descriptor, output_data));
// 清理资源
cudaFree(input_data);
cudaFree(kernel_data);
cudaFree(output_data);
cudaFree(workspace);
cudnnDestroyTensorDescriptor(input_descriptor);
cudnnDestroyTensorDescriptor(output_descriptor);
cudnnDestroyFilterDescriptor(kernel_descriptor);
cudnnDestroyConvolutionDescriptor(convolution_descriptor);
cudnnDestroy(cudnn);
return 0;
}
这个示例展示了如何使用cuDNN进行二维卷积操作,包括初始化、设置描述符、选择算法、执行卷积和清理资源。
五、总结
cuDNN是深度学习中不可或缺的加速库,通过优化卷积、池化、激活等操作,显著提高了模型的训练和推理速度。掌握cuDNN的基本使用方法,可以帮助开发者更高效地实现深度学习模型。在实际应用中,cuDNN与CUDA、深度学习框架(如TensorFlow、PyTorch)紧密配合,提供了强大的计算支持。
版权声明:本博客内容为原创,转载请保留原文链接及作者信息。
参考文章:
- [cuDNN API的使用与测试-以二维卷积+Relu激活函数为例](https://www.hbblog.cn/cuda相关/2022年07月23日 23%E