前回の日記でcuDNNで実装した畳み込みにbiasを追加した。
畳み込みのバイアスは、フィルターごとに加算する。
バイアスは、[1][フィルター数][1][1]の4次元配列となる。
cudnnCreateTensorDescriptorで、[1][フィルター数][1][1]の4次元配列を定義する。
畳み込みの出力に対して、cudnnAddTensorで、バイアスを加算する。
前回のコードにバイアスを追加したコードを以下に示す。
コード例
cuDNNTest.cpp
#include <iostream> #include <cuda.h> #include <cudnn.h> #include "error_util.h" using namespace std; int main() { const int minibatch_size = 1; const int feature_num = 2; const int filter_num = 3; const int in_size = 8; const int filter_size = 5; // 入力 float srcData[minibatch_size][feature_num][in_size][in_size] = { { { { 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 1, 0 }, { 1, 1, 0, 0, 0, 0, 0, 1 }, { 0, 1, 0, 1, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 1, 0, 0, 0, 0, 0, 0 } }, { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1 } } } }; float *srcData_dev; checkCudaErrors(cudaMalloc((void**)&srcData_dev, sizeof(srcData))); // Copy input vectors from host memory to GPU buffers. checkCudaErrors(cudaMemcpy(srcData_dev, srcData, sizeof(srcData), cudaMemcpyHostToDevice)); // 出力 float dstData[minibatch_size][filter_num][in_size][in_size]; float *dstData_dev; checkCudaErrors(cudaMalloc((void**)&dstData_dev, sizeof(dstData))); // フィルター係数 float filterData[filter_num][feature_num][filter_size][filter_size] = { { { { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f } }, { { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f } } }, { { { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f } }, { { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f } } }, { { { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f }, { 0.1f, 1.0f, 0.1f, 0.5f, 0.2f } }, { { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f }, { 1.0f, 0.1f, 0.1f, 0.5f, 1.0f } } } }; float *filterData_dev; checkCudaErrors(cudaMalloc((void**)&filterData_dev, sizeof(filterData))); // Copy input vectors from host memory to GPU buffers. checkCudaErrors(cudaMemcpy(filterData_dev, filterData, sizeof(filterData), cudaMemcpyHostToDevice)); // バイアス float biasData[filter_num] = { 0, 0.5f, 0.2f }; float *biasData_dev; checkCudaErrors(cudaMalloc((void**)&biasData_dev, sizeof(biasData))); // Copy input vectors from host memory to GPU buffers. checkCudaErrors(cudaMemcpy(biasData_dev, biasData, sizeof(biasData), cudaMemcpyHostToDevice)); // 畳み込み準備 cudnnHandle_t cudnnHandle; cudnnTensorDescriptor_t srcTensorDesc, dstTensorDesc, biasTensorDesc; cudnnFilterDescriptor_t filterDesc; cudnnConvolutionDescriptor_t convDesc; checkCUDNN(cudnnCreate(&cudnnHandle)); checkCUDNN(cudnnCreateTensorDescriptor(&srcTensorDesc)); checkCUDNN(cudnnCreateTensorDescriptor(&dstTensorDesc)); checkCUDNN(cudnnCreateTensorDescriptor(&biasTensorDesc)); checkCUDNN(cudnnCreateFilterDescriptor(&filterDesc)); checkCUDNN(cudnnCreateConvolutionDescriptor(&convDesc)); checkCUDNN(cudnnSetTensor4dDescriptor(srcTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, minibatch_size, feature_num, in_size, in_size)); checkCUDNN(cudnnSetTensor4dDescriptor(dstTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, minibatch_size, filter_num, in_size, in_size)); checkCUDNN(cudnnSetTensor4dDescriptor(biasTensorDesc, CUDNN_TENSOR_NCHW, CUDNN_DATA_FLOAT, 1, filter_num, 1, 1)); checkCUDNN(cudnnSetFilter4dDescriptor(filterDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, filter_num, feature_num, filter_size, filter_size)); checkCUDNN(cudnnSetConvolution2dDescriptor(convDesc, 2/*pad_h*/, 2/*pad_w*/, 1/*stride_h*/, 1/*stride_w*/, 1, 1, CUDNN_CROSS_CORRELATION)); cudnnConvolutionFwdAlgo_t algo; checkCUDNN(cudnnGetConvolutionForwardAlgorithm(cudnnHandle, srcTensorDesc, filterDesc, convDesc, dstTensorDesc, CUDNN_CONVOLUTION_FWD_PREFER_FASTEST, 0, &algo )); cout << "Fastest algorithm is Algo " << algo << endl; size_t sizeInBytes = 0; checkCUDNN(cudnnGetConvolutionForwardWorkspaceSize(cudnnHandle, srcTensorDesc, filterDesc, convDesc, dstTensorDesc, algo, &sizeInBytes)); void* workSpace = NULL; if (sizeInBytes != 0) { checkCudaErrors(cudaMalloc(&workSpace, sizeInBytes)); } // 畳み込み float alpha = 1.0f; float beta = 0.0f; checkCUDNN(cudnnConvolutionForward(cudnnHandle, &alpha, srcTensorDesc, srcData_dev, filterDesc, filterData_dev, convDesc, algo, workSpace, sizeInBytes, &beta, dstTensorDesc, dstData_dev)); // バイアス alpha = 1.0f; beta = 1.0f; checkCUDNN(cudnnAddTensor(cudnnHandle, &alpha, biasTensorDesc, biasData_dev, &beta, dstTensorDesc, dstData_dev)); // 出力表示 // Copy output vector from GPU buffer to host memory. checkCudaErrors(cudaMemcpy(dstData, dstData_dev, sizeof(dstData), cudaMemcpyDeviceToHost)); for (int i = 0; i < filter_num; i++) { for (int y = 0; y < in_size; y++) { cout << "{"; for (int x = 0; x < in_size; x++) { cout << dstData[0][i][y][x] << ", "; } cout << "}, "; } cout << endl; } checkCUDNN(cudnnDestroyConvolutionDescriptor(convDesc)); checkCUDNN(cudnnDestroyFilterDescriptor(filterDesc)); checkCUDNN(cudnnDestroyTensorDescriptor(srcTensorDesc)); checkCUDNN(cudnnDestroyTensorDescriptor(dstTensorDesc)); checkCUDNN(cudnnDestroyTensorDescriptor(biasTensorDesc)); checkCUDNN(cudnnDestroy(cudnnHandle)); return 0; }