1

既存の「ホストのみ」のバックプロパゲーションの実装を CUDA に移植する必要があります。ここではアルゴリズムの性質は問題ではないと思うので、その仕組みについてはあまり説明しません。私が重要だと思うのは、3次元すべてが動的に割り当てられる3次元配列を使用していることです。私はCUDA 5.0でVS2010を使用しています。そして私のデバイスは2.1です。元のホストオンリー コードはここからダウンロードできます → http://files.getwebb.org/view-cre62u4d.html

コードの要点:

  1. adult.data のパターンは、「pattern.h」にあるデータ構造を使用してメモリにロードされます。
  2. いくつかの多次元配列が割り当てられます
  3. 直前に割り当てられた配列を使用して、アルゴリズムがパターンに対して実行されます。

コードを実行したい場合は、kernel.cu の先頭にある PATH 定数を変更することを忘れないでください。また、「2」層、「5」ニューロン、および「0.00001」の学習率を使用することをお勧めします。ご覧のとおり、これは完全に機能します。「MSE」は改善しています。このアルゴリズムが何をしているのかわからない人のために、パターンに存在する 14 の変数に基づいて目標値を予測する方法を学習すると簡単に言っておきましょう。「MSE」が減少します。これは、各「エポック」後にアルゴリズムがミスを減らすことを意味します。

このコードをデバイスで実行するのに非常に長い時間を費やしました。そして、私はまだ成功していません。最後の試みは、配列を初期化してアルゴリズムを実行するコードを大きなカーネルにコピーするだけで行われました。これは再び失敗しました。このコードはそこからダウンロードできます → http://files.getwebb.org/view-cre62u4c.html

正確には、元のホストオンリー コードとの違いは次のとおりです。

  • アルゴリズムで使用する f() と fder() は、デバイス 関数になります。
  • パラメーターはハードコーディングされています: 2 層、5 ニューロン、学習率 0.00001
  • 「w」配列は、rand() ではなく、固定値 (0.5) を使用して初期化されます
  • データ構造はデバイスのメモリに割り当てられ、データはホストのメモリ内の adult.data からロードされた後、デバイスのメモリに送信されます

コードをカーネルで実行するために必要な最小限の変更を行ったと思います。「kernel_check_learningData」カーネルは、デバイスのメモリにロードされたパターンに関するいくつかの情報を表示し、ホストからデバイスにパターンを送信する次のコードが機能したことを証明します。

Data data;
Data* dev_data;
int* dev_t;
double* dev_x;
...
input_adult(PathFile, &data);
...
cudaMalloc((void**)&dev_data, sizeof(Data));
cudaMalloc((void**)&dev_t, data.N * sizeof(int));
cudaMalloc((void**)&dev_x, data.N * data.n * sizeof(double));
// Filling the device with t and x's data.
cudaMemcpy(dev_t, data.t, data.N * sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(dev_x, data.x, data.N * data.n * sizeof(double), cudaMemcpyHostToDevice);
// Updating t and x pointers into devices Data structure.
cudaMemcpy(&dev_data->t, &dev_t, sizeof(int*), cudaMemcpyHostToDevice);
cudaMemcpy(&dev_data->x, &dev_x, sizeof(double*), cudaMemcpyHostToDevice);
// Copying N and n.
cudaMemcpy(&dev_data->N, &data.N, sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(&dev_data->n, &data.n, sizeof(int), cudaMemcpyHostToDevice);

「w」配列を読み取るときに、順方向フェーズの開始時に明らかに失敗します。私はそれについての説明を見つけることができません。

2 つの可能性があります。

  1. デバイスのメモリにパターンを送信するコードは、適切に動作しているように見えるにもかかわらず、バグがあり、フォワードフェーズを開始するときにさらにバグを引き起こします。
  2. CUDA API が正常に動作していません!

私は非常に長い間、自分の間違いを必死に探しています。それで、コミュニティが私に助けを提供してくれるのではないかと思いました。

ありがとう。

4

1 に答える 1

1

コードの問題と、64 ビット マシン モードでは機能するのに 32 ビット マシン モードでは機能しない理由を次に示します。

バックプロパゲーション カーネルのフォワード パスには、次のような一連のコードがあります。

/*
* for layer = 0
*/
for (i = 0; i < N[0]; i++) {    // for all neurons i of layer 0
a[0][i] = x[ data->n * pat + i];    // a[0][i] = input i
}

32 ビット マシン モード (Win32 プロジェクト、--machine 32nvcc に渡されている) では、書き込みがa[0][7]発生すると i=7 の繰り返しでエラーが発生します。この書き込みは範囲外です。この時点で、a[0][7]は値を保持することを目的としていdoubleますが、何らかの理由でインデックス作成によって範囲外に配置されています。

ちなみに、これを確認するには、実行可能ファイルがビルドされたディレクトリでコマンド プロンプトを開き、次のコマンドを実行します。

cuda-memcheck test_bp

仮定test_bp.exeは実行可能ファイルの名前です。cuda-memcheck は、範囲外の書き込みが発生していることを便利に識別し、それが発生しているソース行も識別します。

では、なぜこれが範囲外なのですか?a[0][]が割り当てられているカーネル コードの前半を見てみましょう。

a[0] = (double *)malloc( N[0] * sizeof(double *) );
                                              ^ oops!!

a[0][]double データを保持することを目的としていますが、ポインターストレージを割り当てています。結局のところ、64 ビット マシンでは 2 種類のストレージが同じサイズであるため、最終的には機能します。しかし、32 ビット マシンでは、double ポインターは 4 バイトで、double データは 8 バイトです。そのため、32 ビット マシンでは、8 バイトのデータ ストライドを使用してこの配列をインデックス処理すると、最終的には配列の末尾から実行されます。

カーネルコードの他の場所で、次のaような他の「レイヤー」にストレージを割り当てています。

a[layer] = (double *)malloc( N[layer] * sizeof(double) );  

どちらが正しい。元の「ホストのみ」のコードにもこのエラーが含まれているようです。そのコードにも潜在的な欠陥がある可能性があります。

Windows wddm デバイスで実行する場合は、何らかの方法で Windows TDR イベントを回避するために、カーネルの実行時間に対処する必要があります。すでに指摘したように、このコードはマシンの並列機能を使用しようとはしていません。

于 2013-04-30T04:21:57.637 に答える