TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

WindowsでcuDNNを使用して畳み込みを行う(bias追加)

前回の日記で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;
}