0

私は、arrayfire を使用して、生物学に触発されたニューラル ネットワークをシミュレートするプロジェクトに取り組んでいます。いくつかのタイミング テストを行うところまで来ましたが、得られた結果にがっかりしました。私は、タイミング テスト ケースの最も高速で汚れのないモデルの 1 つである Izhikevich モデルを試してみることにしました。そのモデルで新しいテストを実行したところ、結果はさらに悪化しました。私が使用しているコードは以下のとおりです。派手なことをしているわけではありません。それは単なる標準的な行列代数です。ただし、たった 10 個のニューロンの方程式を 1 回評価するのに 5 秒以上かかります。その後のすべての停車時間もほぼ同じです。

コード:

unsigned int neuron_count = 10;

array a = af::constant(0.02, neuron_count);
array b = af::constant(0.2, neuron_count);
array c = af::constant(-65.0, neuron_count);
array d = af::constant(6, neuron_count);

array v = af::constant(-70.0, neuron_count);
array u = af::constant(-20.0, neuron_count);
array i = af::constant(14, neuron_count);

double tau = 0.2;

void StepIzhikevich()
{
    v = v + tau*(0.04*pow(v, 2) + 5 * v + 140 - u + i);
    //af_print(v);
    u = u + tau*a*(b*v - u);
    //Leaving off spike threshold checks for now
}

void TestIzhikevich()
{
    StepIzhikevich();

    timer::start();

    StepIzhikevich();

    printf("elapsed seconds: %g\n", timer::stop());
}

異なる数のニューロンのタイミング結果を次に示します。

結果:

neurons   seconds
10        5.18275
100       5.27969
1000      5.20637
10000     4.86609

ニューロンの数を増やしても、大きな効果はないようです。タイムが少し下がります。ここで何か間違ったことをしていますか?より良い結果を得るために arrayfire を最適化するより良い方法はありますか?

v 方程式を pow(v, 2) の代わりに v*v を使用するように切り替えたところ、ステップに必要な時間は 3.75762 に短縮されました。ただし、それでも非常に遅いため、奇妙なことが起こっています。

[編集] 処理を分割しようとしたところ、何か新しいことがわかりました。ここに私が今使っているコードがあります。

コード:

unsigned int neuron_count = 10;

array a = af::constant(0.02, neuron_count);
array b = af::constant(0.2, neuron_count);
array c = af::constant(-65.0, neuron_count);
array d = af::constant(6, neuron_count);

array v = af::constant(-70.0, neuron_count);
array u = af::constant(-20.0, neuron_count);
array i = af::constant(14, neuron_count);

array g = af::constant(0.0, neuron_count);

double tau = 0.2;

void StepIzhikevich()
{
    array j = tau*(0.04*pow(v, 2));
    //af_print(j);

    array k = 5 * v + 140 - u + i;
    //af_print(k);

    array l = v + j + k;
    //af_print(l);

    v = l;  //If this line is here time is long on second loop
    //g = l; //If this is here then time is short.

    //u = u + tau*a*(b*v - u);
    //Leaving off spike threshold checks for now
}

void TestIzhikevich()
{
    timer::start();

    StepIzhikevich();

    printf("elapsed seconds: %g\n", timer::stop());

    timer::start();

    StepIzhikevich();

    printf("elapsed seconds: %g\n", timer::stop());
}

v に再度割り当てたり、新しい変数 g に割り当てたりせずに実行すると、1 回目と 2 回目の実行の両方でステップの時間が短くなります。

結果:

経過秒数: 0.0036143

経過秒数: 0.00340621

ただし、 v = l; と入力すると、戻ると、最初に実行するときは高速ですが、それ以降は遅くなります。

結果:

経過秒数: 0.0034497

経過秒数: 2.98624

これを引き起こしている原因についてのアイデアはありますか?

[編集2]

なぜこれを行うのかはまだわかりませんが、再度使用する前に v 配列をコピーすることで回避策を見つけました。

コード:

unsigned int neuron_count = 100000;

array v = af::constant(-70.0, neuron_count);
array u = af::constant(-20.0, neuron_count);
array i = af::constant(14, neuron_count);

double tau = 0.2;

void StepIzhikevich()
{
    //array vp = v;
    array vp = v.copy();
    //af_print(vp);

    array j = tau*(0.04*pow(vp, 2));
    //af_print(j);

    array k = 5 * vp + 140 - u + i;
    //af_print(k);

    array l = vp + j + k;
    //af_print(l);

    v = l;  //If this line is here time is long on second loop
}

void TestIzhikevich()
{
    for (int i = 0; i < 10; i++)
    {
        timer::start();

        StepIzhikevich();

        printf("loop: %d  ", i);
        printf("elapsed seconds: %g\n", timer::stop());

        timer::start();
    }
}

これが今の結果です。2 回目の実行は少し遅いですが、その後は高速です。以前に比べて大幅な改善。

結果: ループ: 0 秒経過: 0.657355

ループ: 1 秒経過: 0.981287

ループ: 2 経過秒: 0.000416182

ループ: 3 経過秒: 0.000415045

ループ: 4 経過秒: 0.000421014

ループ: 5 経過秒: 0.000413339

ループ: 6 経過秒: 0.00041675

ループ: 7 経過秒: 0.000412202

ループ: 8 経過秒: 0.000473321

ループ: 9 経過秒: 0.000677432

4

0 に答える 0