在cuDNN中简化Tensor Ops
在cuDNN中簡化Tensor Ops
在Tesla V100 GPU中引入神經網絡模型以來,神經網絡模型已迅速利用NVIDIA Tensor Cores進行深度學習。例如,基于Tensor Core的解決方案宣布了ResNet50訓練的性能記錄。
NVIDIA的cuDNN庫 使CUDA程序員能夠優化循環神經網絡和卷積神經網絡,以實現GPU加速。概述了cuDNN用戶使用Tensor Core 進行卷積的簡便方法,并附有說明和示例代碼。該文章為cuDNN應用提供了一些簡單的規則:FP16數據規則,張量維數規則,ALGO_1的使用等。
cuDNN版本解除了大多數限制。cuDNN 7.2版本取消了FP16數據約束,而cuDNN 7.3刪除了張量尺寸約束(對于打包的NCHW張量數據),直接進行改進。
將FP32數據用于Tensor Ops
關于在CUDA中使用Tensor Core的帖子討論了將FP16輸入用于張量操作,如圖1所示。雖然張量操作仍然使用FP16數據,但卷積cuDNN API允許用戶選擇將FP32輸入數據轉換為FP16。如果需要,卷積的輸出數據也將轉換為FP32。
圖1. FP32數據現在可以用作輸入
該CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION枚舉值,在cuDNN 7.2,使cuDNN應用程序員選擇轉換FP32數據運算使用。該枚舉值與枚舉值一樣傳遞給cudnnSetConvolutionMathType()調用CUDNN_TENSOR_OP_MATH。此代碼段顯示了如何執行此操作:
//設置數學類型以允許cuDNN使用Tensor Core:
checkCudnnErr(cudnnSetConvolutionMathType(cudnnConvDesc,CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION));
將在后面的部分中看到使用代碼片段的上下文。
FP32數據也用于RNN
現在還為RNN啟用了類似的FP32數據轉換。只需將CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION枚舉值傳遞給cudnnSetRNNMatrixMathType()調用,即可將FP32數據轉換為在RNN中使用。如下使用:
//設置數學類型以允許cuDNN使用Tensor Core:
checkCudnnErr(cudnnSetRNNMatrixMathType(cudnnRnnDesc,CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION));
消除了NCHW張量尺寸約束
早期版本的cuDNN要求所有張量的通道維數必須為8的倍數。cuDNN可以根據需要自動填充張量。
在CUDNN_TENSOR_OP_MATH和CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION情況下,對于填充的NCHW數據,此填充都是自動的。發生填充時,性能損失可忽略不計。
//設置NCHW張量尺寸,不必設置為8的倍數(此處僅顯示輸入張量):
int dimA [] = {1,7,32,32};
int strideA [] = {7168,1024,32,1};
下一節中的示例代碼演示了如何使用。
樣例代碼
將張量運算用于FP32數據和任何通道尺寸的邏輯類似于為cuDNN的早期版本編寫時使用的邏輯。只有維度和數據類型發生了變化(以及使用CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION):
//創建一個cuDNN句柄:
checkCudnnErr(cudnnCreate(&handle_));
//創建張量描述符:
checkCudnnErr(cudnnCreateTensorDescriptor(&cudnnIdesc));
checkCudnnErr(cudnnCreateFilterDescriptor(&cudnnFdesc));
checkCudnnErr(cudnnCreateTensorDescriptor(&cudnnOdesc));
checkCudnnErr(cudnnCreateConvolutionDescriptor(&cudnnConvDesc));
//設置NCHW張量尺寸,不必設置為8的倍數(此處僅顯示輸入張量):
int dimA [] = {1,7,32,32};
int strideA [] = {7168,1024,32,1};
checkCudnnErr(cudnnSetTensorNdDescriptor(cudnnIdesc,CUDNN_DATA_FLOAT,
convDim + 2,dimA,strideA));
//分配和初始化張量(同樣,僅顯示輸入張量):
checkCudaErr(cudaMalloc((void *)&(devPtrI),(insize) sizeof(devPtrI [0]))));;
hostI =(T_ELEM *)calloc(insize,sizeof(hostI [0]));
initImage(hostI,insize);
checkCudaErr(cudaMemcpy(devPtrI,hostI,sizeof(hostI [0])* insize,cudaMemcpyHostToDevice));
//設置計算數據類型(以下為CUDNN_DATA_FLOAT):
checkCudnnErr(cudnnSetConvolutionNdDescriptor(cudnnConvDesc,convDim,padA,convstrideA,dilationA,CUDNN_CONVOLUTION,CUDNN_DATA_FLOAT));;
//設置數學類型以允許cuDNN使用Tensor Core:
checkCudnnErr(cudnnSetConvolutionMathType(cudnnConvDesc,CUDNN_TENSOR_OP_MATH_ALLOW_CONVERSION));
//選擇支持的算法:
cudnnConvolutionFwdAlgo_t算法= CUDNN_CONVOLUTION_FWD_ALGO_IMPLICIT_PRECOMP_GEMM;
//分配的工作空間:
checkCudnnErr(cudnnGetConvolutionForwardWorkspaceSize(handle_,cudnnIdesc,
cudnnFdesc,cudnnConvDesc,
cudnnOdesc,algo,&workSpaceSize));
如果(workSpaceSize> 0){
cudaMalloc(&workSpace,workSpaceSize);
}
//調用卷積:
checkCudnnErr(cudnnConvolutionForward(handle_,(void *)(&alpha),cudnnIdesc,devPtrI,
cudnnFdesc,devPtrF,cudnnConvDesc,algo,
workSpace,workSpaceSize,(void *)(&beta),
cudnnOdesc,devPtrO));
FP32性能
圖2顯示了將Tensor Core用于FP32張量數據時卷積的比較性能。該圖表將V100張量運算與V100 FMA運算進行了比較,因此,其增益并不像以前的將V100性能與P100 FMA進行比較的圖表那樣明顯。但是,與使用FMA ops相比,與FP32輸入一起使用的Tensor ops仍然代表了可觀的收益。
圖2.具有Tensor Core的Tesla V100(Volta)與Tesla V100(Volta)的卷積性能比較。比較是在每個神經網絡的卷積層運行時間的幾何方法之間進行的。兩種情況都使用FP32輸入/輸出數據和FP32計算。一種使用Tensor Core,另一種使用FP32融合乘加(FMA)。
剩余約束
盡管解除了在cuDNN中使用張量運算的主要限制,但仍然存在一些次要限制。一個限制是使用ALGO_1(IMPLICIT_PRECOMP_GEMM用于轉發)。cuDNN中還沒有其他卷積算法使用張量運算。
另一個較小的限制是卷積濾波器的大小,特別是空間尺寸(r和s)。但是,用于卷積的FFT算法非常適合于濾波器尺寸較大的用例。只需在超出張量運算濾波器限制以達到最佳性能之前就將卷積切換為使用FFT算法即可。
總結
以上是生活随笔為你收集整理的在cuDNN中简化Tensor Ops的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cuDNN概述
- 下一篇: CUDA 8的混合精度编程