4

パラメータ( )を使用して関数を最適化しようとしています(最小値を見つけるなど)。すべての は特定の範囲 (たとえば ) にバインドされており、いずれかのパラメーターがこの範囲を離れると、関数は非常に高速に無限大になります。ただし、大きくなる可能性があり ( から約 まで)、その値の計算には長い時間がかかります。nXnXi-200200n2060-70

関数の詳細はあまり重要ではないと思いますが、ここにいくつかを示します。これは、20-30小さな関数 (すべて異なる) の加重和で構成されており、逆関数の符号の下の内積の和で構成されています。正弦関数 ( arcsinarccosarctanなど)。のようなものarcsin(X1 . X2) + arcsin(X4 . X7) + ...

関数には一般に多くの極小値があるため、(単純な) 共役勾配や準ニュートンなどのアプローチは役に立ちません。ドメイン全体を力ずくで検索するのは遅すぎます。

私の最初のアイデアは、遺伝的アルゴリズムと組み合わせてある種の大規模な並列化を使用することでした。これは、関数のドメイン内のさまざまな場所で多くの検索を実行し、検索の一部が極小値に達したかどうかを定期的にチェックします。はいの場合、それらを比較し、最小のものを除くすべての結果を破棄し、適度に小さい値が見つかるまで検索を続けます。

私の2つの質問は次のとおりです。

1) この問題を CUDA または同様の技術で実装することは可能ですか? CUDA はこのような関数の値を十分に高速に計算できますか?

2) マルチコア PC (12 コア以上) で問題を実装する方が良い/速いですか?

4

3 に答える 3

4

GPUにグローバル最適化アルゴリズムを実装することは確かに可能ですが、最高のパフォーマンスを得るにはアルゴリズムを変更する必要がある場合があります。私は個人的にGPUに約6ダースの人口ベースのメタヒューリスティックを実装しており、マルチスレッドCPUと比較して優れたスピードアップを得ることができます。

世代を超えて繰り返される母集団ベースのアルゴリズムでは、母集団のサイズがパフォーマンスの制限要因になる場合があります。目的関数を簡単に並列化できない場合、並列スレッドの最大数は通常、母集団内の候補ソリューションの数です。GPUは、少なくとも数万の同時スレッドで最適に動作します。これは、人口の慣性やその他の影響により、人口ベースのアルゴリズムでは必ずしも実用的ではありません。

最適化の複数のインスタンスを並行して実行し、それぞれが異なるランダムシードで開始することにより、母集団の制限をある程度回避できます。さらに、並列インスタンスがいくつかのネットワークトポロジ(これはアイランドモデルアルゴリズムになります)を介して通信するように調整できるため、並列インスタンスは複雑な問題のソリューションを共同で進化させることができます。私は実際にMScE論文のためにこれをOpenCLに実装しました。

全体として、ここにあなたの質問に対する簡潔な答えがあります:

1)はい、これはCUDAまたは同様のテクノロジーで実装できます。スピードアップは、目的関数の並列化の程度と、同時に実行する並列インスタンスの数によって異なります。2)既存のライブラリの範囲が広く、CPUプログラミングモデルがGPUモデルよりも概念的に単純であるという事実により、CPUに実装する方がほぼ確実に高速です。それが「より良い」かどうかは、あなたが何を大切にするかによります。

これはまだ活発な研究分野であり、マルチコアCPU実装の場合よりも、動作するGPU実装の構築に時間がかかる可能性があります。実装時間が主な関心事である場合は、PaGMOプロジェクト(およびそのPythonバインディングPyGMO)を確認することをお勧めします。これは、さまざまなローカルおよびグローバル最適化関数を備えたアイランドモデルオプティマイザーの優れた実装です。含まれています。アイランドには、独立して実行する任意のアルゴリズムを割り当てることができます。また、アイランドが通信する方法を正確に指定して、目的関数を協調的に最適化することができます。

http://sourceforge.net/apps/mediawiki/pagmo/index.php?title=Main_Page

あなたの質問は私の研究分野の範囲内です。必要に応じて、さらにサポートさせていただきます。

于 2012-07-04T15:01:07.520 に答える
0

上記の私の答えは、通常、ローカル最適化アルゴリズムで処理される非常に多数の未知数を持つ問題に最も適しています。他のユーザーが参照できるように、ここに残しておきます。

あなたが言及したように、あなたが扱っている60のは、グローバル最適化アルゴリズム70によってより簡単に管理できるシナリオである未知数です。

上記で下線を引いたように、コスト汎関数は多くの場合、合計で構成されているため、それらの計算はその後の変換および削減操作になります。このように多くの未知数があるため、共有メモリの削減は興味深いオプションになる可能性があります。幸いなことに、CUBは共有メモリを削減するためのプリミティブを提供します。

これは、中程度の数の未知数を持つ問題に対して多数のコスト関数値を計算するために CUB を使用する方法に関する実用的な例です。この場合の費用汎関数は Rastrigin 関数として選択されていますが、対応する__device__関数を変更するだけで、この例を他の費用汎関数に適応させることができます。

#include <cub/cub.cuh>
#include <cuda.h>

#include "Utilities.cuh"

#include <iostream>

#define BLOCKSIZE           256

const int N = 4096;

/************************/
/* RASTRIGIN FUNCTIONAL */
/************************/
__device__ float rastrigin(float x) {

    return x * x - 10.0f * cosf(2.0f * x) + 10.0f;

}

/******************************/
/* TRANSFORM REDUCTION KERNEL */
/******************************/
__global__ void CostFunctionalCalculation(const float * __restrict__ indata, float * __restrict__ outdata) {

    unsigned int tid = threadIdx.x + blockIdx.x * gridDim.x;

    // --- Specialize BlockReduce for type float. 
    typedef cub::BlockReduce<float, BLOCKSIZE> BlockReduceT;

    __shared__ typename BlockReduceT::TempStorage  temp_storage;

    float result;
    if(tid < N) result = BlockReduceT(temp_storage).Sum(rastrigin(indata[tid]));

    if(threadIdx.x == 0) outdata[blockIdx.x] = result;

    return;
}

/********/
/* MAIN */
/********/
int main() {

    // --- Allocate host side space for 
    float *h_data       = (float *)malloc(N               * sizeof(float));
    float *h_result     = (float *)malloc((N / BLOCKSIZE) * sizeof(float));

    float *d_data;      gpuErrchk(cudaMalloc(&d_data,   N               * sizeof(float)));
    float *d_result;    gpuErrchk(cudaMalloc(&d_result, (N / BLOCKSIZE) * sizeof(float)));

    for (int i = 0; i < N; i++) {
        h_data[i] = 1.f;
    }

    gpuErrchk(cudaMemcpy(d_data, h_data, N * sizeof(float), cudaMemcpyHostToDevice));

    CostFunctionalCalculation<<<iDivUp(N, BLOCKSIZE), BLOCKSIZE>>>(d_data, d_result);
    gpuErrchk(cudaPeekAtLastError());
    gpuErrchk(cudaDeviceSynchronize());

    gpuErrchk(cudaMemcpy(h_result, d_result, (N / BLOCKSIZE) * sizeof(float), cudaMemcpyDeviceToHost));

    std::cout << "output: \n";
    for (int k = 0; k < N / BLOCKSIZE; k++) std::cout << k << " " << h_result[k] << "\n";
    std::cout << std::endl;

    gpuErrchk(cudaFree(d_data));
    gpuErrchk(cudaFree(d_result));

    return 0;
}
于 2015-08-12T06:48:16.140 に答える