JOCLを使用して、JavaでOpenCLでバックプロップ学習を行うフィードフォワードマルチレイヤパーセプトロンの実装に問題があります。このOpenCLカーネルがエラーの原因CL_INVALID_COMMAND_QUEUE
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
__kernel void Neuron(__global const double *inputPatterns,
__global double *weights,
__global const int *numInputs,
__global const int *activation,
__global const double *bias,
__global const int *usingBias,
__global double *values,
__global const int *maxNumFloats,
__global const int *patternIndex,
__global const int *inputPatternSize,
__global const int *indexOffset,
__global const int *isInputNeuron,
__global const int *inputs)
{
int gid = get_global_id(0);
double sum = 0.0;
for(int i = 0; i < numInputs[gid+indexOffset[0]]; i++)
{
sum += values[inputs[(gid+indexOffset[0]) * maxNumFloats[0] + i]] *
weights[(gid+indexOffset[0]) * maxNumFloats[0] + i];
}
if(usingBias[gid+indexOffset[0]])
sum += bias[gid+indexOffset[0]];
if(isInputNeuron[gid+indexOffset[0]])
sum += inputPatterns[gid+indexOffset[0]+(patternIndex[0] * inputPatternSize[0])];
if(activation[gid+indexOffset[0]] == 1)
sum = 1.0/(1.0 + exp(-sum));
values[gid + indexOffset[0]] = sum;
}
基本的には、このカーネルをネットワークの各レイヤーに対して実行します。最初の層には "入力"がないので、ループは実行されません。しかし、第1層は入力ノード層であるため、入力パターンから関連する値を追加します。これは正常に実行され、この時点で値を読み取ることができます。
ただし、入力があり、最初のレイヤーからのすべてのノードを持つSECONDレイヤーを実行しようとすると、clFinish()を呼び出すとCL_INVALID_COMMAND_QUEUEというエラーが返されます。ときには、このエラーはドライバーのクラッシュと回復と結びついています。私はTDRのタイムアウトに問題がある可能性があることを(例えばhereのように)読んだことがあります。
私はclSetKernelArg()を呼び出して何か愚かなことをチェックするつもりですが、誰でもコード内の何かを明らかに見つけることができますか? forループが含まれているため、2番目のレイヤーにエラーが導入されているように見えます。必要な場合はすべてのパラメータを明確にすることができますが、最初の投稿では少し過剰なようです。
また、私はこのコードは、おそらくどこでも有能なプログラマーへの侮辱であってもよいが、炎に無料で感じることが十分に承知している:P
EDIT:ホストコード:
//Calc
for(int k = 0; k < GPUTickList.length; k++)
{
clFlush(clCommandQueue);
clFinish(clCommandQueue);
//If input nodes
if(k == 0)
//Set index offset to 0
GPUMapIndexOffset.asIntBuffer().put(0, 0);
else
//Update index offset
GPUMapIndexOffset.asIntBuffer().put(0,
GPUMapIndexOffset.asIntBuffer().get(0) + GPUTickList[k-1]);
//Write index offset to GPU buffer
ret = clEnqueueWriteBuffer(clCommandQueue, memObjects[12], CL_TRUE, 0,
Sizeof.cl_int, Pointer.to(GPUMapIndexOffset.position(0)), 0, null, null);
//Set work size (width of layer)
global_work_size[0] = GPUTickList[k];
ret = clEnqueueNDRangeKernel(clCommandQueue, kernel_iterate, 1,
global_work_offset, global_work_size, local_work_size,
0, null, null);
}
EDIT 2:I pastebinに完全なコードをアップロードしました。
共有するホストコードの一部がありますか?私はそれがニューロンの第1の層のために動くなら、カーネル自体に問題があるとは思わない。また、clFinish()の代わりにclWaitForEvents()を試してみましたか? – mfa
確かに、このカーネルがループ内でエンキューされている場所にビットを追加し、必要に応じてさらにポストして、何を言うことができます。 – chrisvarnz