記事についてのディスカッション

 

新しい記事「ニューラルネットワークが簡単にできるように(その5).OpenCLでのマルチスレッド計算」はパブリッシュされました:

ニューラルネットワークの実装のいくつかのタイプについては、これまで説明してきました。 これまで考慮されたネットワークでは、各ニューロンに対して同じ操作が繰り返されます。 さらに論理的な進展としては、ニューラルネットワークの学習プロセスを高速化するために、現代の技術が提供するマルチスレッドコンピューティング機能を利用することです。 可能な実装の1つは、この記事で説明しています。

どの技術を使うかは選択しました。 あとは、計算をスレッドに分割する処理を決める必要があります。 フィードフォワードパス中の完全接続パーセプトロンアルゴリズムを覚えていますか? シグナルは、入力層から隠しレイヤーへ、そして出力層へと順次移動します。 計算は順次実行されなければならないので、各レイヤーにスレッドを割り当てる意味がありません。 レイヤーの計算は、前のレイヤーの結果を受信するまで開始できません。 ある層の個々のニューロンの計算は、その層の他のニューロンの計算結果には依存しません。 各ニューロンに別々のスレッドを割り当てて、あるレイヤーのすべてのニューロンを並列計算のために送ることができるということです。  

完全に接続されたパーセプトロン

1つのニューロンの動作にまで踏み込んで、入力値の重み係数による積の計算を並列化することも考えられます。 しかし、結果として得られた値のさらなる集計と活性化関数値の計算は、1つのスレッドにまとめられています。 これらの操作をベクトル関数を使って1つのOpenCLカーネルで実装することにしました。

作者: Dmitriy Gizlyk

 

この記事は、OpenCLに熱心でない人間にとっては非常に複雑で、私は本質を理解しようとしたし、あなたのところで紹介されている記事も読んだが......。何も理解できなかったというか、極めて部分的にしか理解できなかった。

あなたの記事はおそらくスーパーですが、複雑すぎます。OpenCLの非常に基本的なことを考える必要があり、おそらくOpenCL.mqhのようなビルトイン・ライブラリで作業する必要があると思います。私はあなたのコードを理解できません。なぜなら、そのコードには私が知らない多くのライブラリへの参照が含まれているからです。

私が理解できない2つ目の質問は、最適化 モードでOpenCLを使う方法です。OpenCLの複数のインスタンスが同時にビデオカードにアクセスするとどうなるのでしょうか?それから、OpenCLには1つの同じものの計算を高速化する方法がたくさんある。例えば、doubleを廃止してintにする。などなど・・・。たくさんの疑問がある。

マルチスレッドのプログラミングコードで、非常に簡単な例から始めようと思う。n個の入力を持つ1つのパーセプトロンがあるとすると、それぞれ[-1,1]の範囲で正規化関数を計算し、tanhによってニューロンの値を計算する簡単なコードをOpenCLを使って書く必要があります。そして、OpenCLのコード自体にはコメントを付けなければならない。また、1つのインジケータを使用し、しかもRSIやCCIのような1つの値のみを与える単純なインジケータを使用して、テスターで履歴を最適化した非常に単純な取引のExpert Advisorを考えてみてください。つまり、すべてが可能な限り単純化され、工夫がなく、可能な限りシンプルなのです。将来的には、これを多くのパーセプトロンに拡張し、独自のExpert Advisorを書くことも問題ないと思います。

ありがとうございました。

 
この記事はまさにあなたが必要としているものだ。著者に感謝する。ニューロンでの並列計算が 初めて理解できました。あとは "成熟 "するだけだ。)
 
Реter Konow:
この記事はまさにあなたが必要としているものだ。著者に感謝する。ニューロンでの並列計算が 初めて理解できました。あとは "成熟 "するだけだ。)

まあ、一方はまだ成熟していないし、もう一方は......。でも、みんなそうしたいんだろうね。

この記事が必要であることに同意する

私はそれを理解しようと努力している。

もしそれがあなたにとって簡単なことなら、私が書くように頼んだものを書くことができるかもしれない。
 
Boris Egorov:

まあ、ひとつは熟していないし、もうひとつは......。みんな欲しがっているみたいだね。

これは有益な記事だと思う。

私はそれを理解するのに懸命だ。

もしそれがあなたにとって簡単なことなら、私があなたに頼んだことを書くことができるかもしれません。
私が理解している限りでは、最適化自体がマルチスレッド化されているので、最適化モードで OpenCLを使う必要はありません。 ニューラルネットワークはトレーニングによって最適化されるのであって、社内テスターによって最適化されるわけではありません。

ネットワークのマルチスレッドは、あるレイヤーの情報量(データ)の処理を高速化し、次のレイヤーにバトンを渡すために必要です。

各ニューロンは、他のニューロンを待たせることなく、独自のスレッドで値空間の "断片 "を処理する。これが、OpenCL技術が提供するNSでマルチスレッドを使用する利点である。
 
しかし、トレーニングに関与するコアが1つ多いだけなのに、10倍も増えたのはどこから来たのだろうか?
 
Aleksey Vyazmikin:
しかし、トレーニングに関与するコアが1つ増えただけなのに、なぜ10倍になったのか?

私の理解では、OpenCLでトレーニングする場合、一度に複数の指標を並列計算する。

 
Aleksey Vyazmikin:
しかし、トレーニングで使用するコアが1つ増えただけなのに、なぜ10倍も増えるのか?

カーネルは 物理コアでも論理コアでもなく、プロセッサやビデオカードのすべての論理コアで 並列に実行されるファームウェアである。

 

以下は、私が理解していないいくつかの点です。

__kernel void FeedForward(__global double *matrix_w,
                          __global double *matrix_i,
                          __global double *matrix_o,
                          int inputs, 
                          int activation)
  {
   int i=get_global_id(0); //この行は何をするのか?
   double sum=0.0;
   double4 inp, weight;
   int shift=(inputs+1)*i; //この行は何をするのか?
   for(int k=0; k<=inputs; k=k+4)
     {
      switch(inputs-k)
        {
         case 0:
           inp=(double4)(1,0,0,0); //この行は何をするのか?
           weight=(double4)(matrix_w[shift+k],0,0,0);
           break;
         case 1:
           inp=(double4)(matrix_i[k],1,0,0);
           weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],0,0);
           break;
         case 2:
           inp=(double4)(matrix_i[k],matrix_i[k+1],1,0);
           weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],matrix_w[shift+k+2],0);
           break;
         case 3:
           inp=(double4)(matrix_i[k],matrix_i[k+1],matrix_i[k+2],1);
           weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],matrix_w[shift+k+2],matrix_w[shift+k+3]);
           break;
         default:
           inp=(double4)(matrix_i[k],matrix_i[k+1],matrix_i[k+2],matrix_i[k+3]);
           weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],matrix_w[shift+k+2],matrix_w[shift+k+3]);
           break;
        }
      sum+=dot(inp,weight); //この行は何をするのか?
     }
   switch(activation)
     {
      case 0:
        sum=tanh(sum);
        break;
      case 1:
        sum=pow((1+exp(-sum)),-1);
        break;
     }
   matrix_o[i]=sum;
  }

遠方の」人たちのために説明する必要があると思う。主な点は、コードを見ても、どこですべてが初期化され、どこですべてが呼び出されるのかがはっきりしないことだ。

 
Boris Egorov:

私が理解できないことをいくつか挙げよう。

遠方の」人たちのために説明する必要があると思う。主なことは、どこですべてが初期化され、どこですべてが呼び出されるのか、コードから理解できないことだ。

Good day, Boris.
カーネルコードを添付していますね。Maximが上で書いたように、これはマイクロプロセッサまたはビデオカードのコアで実行されるマイクロプログラムです(初期の初期化コンテキストに依存します)。重要なのは、このようなプログラムが一度に複数呼び出され、すべてのコアで並列に実行されるということだ。各プログラムはそれぞれのデータで動作する。

int i=get_global_id(0); //この行は何をするのか?

この行は、並列実行コピーのプールからマイクロプログラムのシリアル番号を取得するだけである。コードでは、この番号を使って、どのデータチャンクを処理に渡すかを決定する。この場合、レイヤーのニューロン数に対応する。

さらに、マイクロプログラム内のアクションをさらに並列化するために、ベクトル変数が使われる。この場合、ベクトルの次元は4、つまり4要素の配列である。

double4 inp, weight;

しかし、入力配列のサイズが常に4の倍数になるとは限らない。したがって、間違った配列参照を避けるために、swith が使用され、欠損値は "0 "で埋められる。この場合、各ニューロンについて 重み配列の次元数が 入力要素の次元数より 1 要素大きいので、この要素をベイズバイアスとして使用する。以前、この目的のために追加のニューロンを使ったが、そのときは重みだけを修正し、出力値の再計算は行わなかった。ここでは、入力配列に常に "1 "を追加するのではなく、コードに直接書き込んだので、入力配列のサイズは変わらない。

           inp=(double4)(1,0,0,0); //この行は何をするのか?
           weight=(double4)(matrix_w[shift+k],0,0,0);

dot関数は2つのベクトルのスカラー積を返す。つまり、今回のケースでは、入力値の重みによる4つの積の和を1行で数える。

sum+=dot(inp,weight); //この行は何をするのか?
 
Maxim Dmitrievsky:

カーネルは 物理コアでも論理コアでもなく、プロセッサやビデオカードのすべての論理コア 上で並行して動作するファームウェアである

つまり、1コアで負荷がかかっていたのが、2コアになって負荷が半分になった......というニュースではない。ほとんどの場合、変化はもっと大きく、比較は正しくない。