Паралельне обчислення великих матриць коваріації


9

Нам потрібно обчислити коваріаційні матриці розмірами від до . У нас є доступ до графічних процесорів та кластерів, ми цікавимося, який найкращий паралельний підхід для прискорення цих обчислень.10000×10000100000×100000


1
Чи очікуєте ви особливостей для вашої матриці коваріації? Наприклад, велика кількість кореляцій "біля 0"?
Dr_Sam

ні, ми нічого не можемо очікувати зараз
Відкрийте шлях

Який твій к? Тобто, скільки триває кожен вектор даних. Вони вже мають нульове значення?
Макс Хатчінсон

ні, вони не нульові, вони можуть приймати будь-яке значення
Відкрийте шлях

3
@flow: '' клінічні дані '' - це додаток, але не використання. Моє запитання було: Припустимо, у вас є матриця коваріації, що ви будете робити з нею (з математичної точки зору)? Причина, яку я запитую, полягає в тому, що врешті-решт з неї завжди обчислюється дуже мало, і якщо це враховувати, зазвичай можна надзвичайно прискорити справи, уникаючи обчислити матрицю повної коваріації, при цьому все-таки отримати бажаний наступний результат.
Арнольд Ноймаєр

Відповіді:


17

Перше, що потрібно визнати, що ви можете це зробити за допомогою BLAS. Якщо у вас є матриця данихХ=[х1х2х3...]Rм×н (кожен х- вектор стовпця, що відповідає одному вимірюванню; рядки - випробування), тоді ви можете записати коваріацію як:

Сij=Е[хi,хj]-Е[хi]Е[хj]=1нкхiкхjк-1н2(кхiк)(кхjк)
Ми можемо записати це так:
С=1нХТХ-1н2(1ТХ)Т(1ТХ)
де (1Т) - вектор-рядок з усіма елементами 1 так (1ТХ) - рядовий вектор суми стовпців Х. Це можна записати повністю як BLAS, деХТХце або GEMM, або, ще краще, SYRK / HERK, і ви можете отримати(1ТХ)=бз GEMV ,бТбзнову з GEMM або SYRK / HERK, а збірники з SCAL .

Ваші матриці даних та результатів можуть становити близько 64 Гб, тому ви не збираєтесь розміщуватись на одному вузлі чи GPU, який коштує. Для кластера, який не є GPU, ви можете подивитися на PBLAS , який відчуває себе як скалапак. Для графічних процесорів багатовузлових бібліотек ще не існує. У Magma є якась основна паралельна реалізація BLAS, але це може бути не зручним для користувачів. Я не думаю, що CULA ще не працює у багатьох вузлах, але це щось, на що слідкувати. CUBLAS - одновузлова.

Я б також запропонував вам настійно розглянути можливість здійснення паралелізму самостійно, особливо якщо ви знайомі з MPI і вам доведеться приєднати це до наявної кодової бази. Таким чином, ви можете легко перемикатися між процесором та GPU BLAS і починати та закінчувати дані саме там, де вам це потрібно. Вам не потрібно більше, ніж кілька дзвінків MPI_ALLREDUCE .


Дякуємо за Ваш аналіз та список відповідних функцій BLAS. Прочитавши вашу відповідь, я використав їх для прискорення та оптимізації обчислення коваріаційної матриці у розробній версії Scilab (www.scilab.org).
Стефан

Однак слід попередити, що використання цього способу обчислення коваріації підлягає катастрофічному скасуванню, коли Е[хi,хj] близький до Е[хi]Е[хj], див., наприклад, en.wikipedia.org/wiki/…
Стефан Мотлет

1

Я реалізував формулу, подану @Max Hutchinson з CUBlas і Cuda Thrust, і порівняв інструменти для обчислення дисперсії в Інтернеті. Здається, міна дає хороші результати. Код нижче планується QDA Bayes. Отже, наведена матриця може містити більше одного класу. Так обчислюються множинні дисперсійні матриці. Сподіваюсь, комусь це стане в нагоді.

//! Calculates one or more than one coVarianceMatrix given data.
//  There can be many classes since many covariance matrixes.
/*!
    \param inMatrix This vector contains matrix data in major storage. 
    Forexample if inMatrix=[1 2 3 4 5 6] and trialSizes=[2] this means matrix we will work on a matrix like :
        |1 4 |
        |2 5 |
        |3 6 | -> 2 Trials, 3 Features. Columns contains feature rows contains trials (samples)
    \param trialSizes There can be many classes since many covariance matrixes. Samples from all classes will be given with inMatrix.
    But we need to know how many trials(samples) we have for each class. 
    For example if inMatrix=[1 2 3 4 5 6 7 8 9 10 11 12] and trialSizes=[2,2] 
    this means matrix we will work on a matrix like :
        |1 4 |  |7 10 |
        |2 5 |  |8 11 |
        |3 6 |  |9 12 |  --> Total number of trials(samples which is total rowCount) 2 + 2 = 4 , 
                             So colSize = inMatrix.size()/4 = 3(feature vector size)
                         --> There is two element in trialSize vec so each vector has to samples
*/
void multiQDACovianceCalculator(std::vector<float>& inMatrix, std::vector<int>& trialSizes)
{
    cublasHandle_t handle; // CUBLAS context
    int classCount = trialSizes.size();
    int rowSize = std::accumulate(trialSizes.begin(), trialSizes.end(), 0);
    int dimensionSize = inMatrix.size() / rowSize;
    float alpha = 1.0f;
    float beta = 0.0f; // bet =1

    thrust::device_vector<float> d_cov1(dimensionSize * dimensionSize);
    thrust::device_vector<float> d_cov2(dimensionSize * dimensionSize);
    thrust::device_vector<float> d_covResult(dimensionSize * dimensionSize);

    thrust::device_vector<float> d_wholeMatrix(inMatrix);
    thrust::device_vector<float> d_meansVec(dimensionSize); // rowVec of means of trials
    float *meanVecPtr = thrust::raw_pointer_cast(d_meansVec.data());
    float *device2DMatrixPtr = thrust::raw_pointer_cast(d_wholeMatrix.data());
    auto maxTrialNumber = *std::max_element(trialSizes.begin(), trialSizes.end());
    thrust::device_vector<float> deviceVector(maxTrialNumber, 1.0f);

    cublasCreate(&handle);
    // Inside of for loop  one covariance matrix calculated each time
    for (int i = 0; i < trialSizes.size(); i++)
    {
        // X*transpose(X) / N
        alpha = 1.0f / trialSizes[i];
        cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_T, dimensionSize, dimensionSize, trialSizes[i], &alpha,
            device2DMatrixPtr, dimensionSize, device2DMatrixPtr, dimensionSize, &beta,
            thrust::raw_pointer_cast(d_cov1.data()), dimensionSize);

        // Mean vector of each column
        alpha = 1.0f;
        cublasSgemv(handle, CUBLAS_OP_N, dimensionSize, trialSizes[i], &alpha, device2DMatrixPtr,
            dimensionSize, thrust::raw_pointer_cast(deviceVector.data()), 1, &beta, meanVecPtr, 1);

        // MeanVec * transpose(MeanVec) / N*N
        alpha = 1.0f / (trialSizes[i] * trialSizes[i]);
        cublasSgemm(handle, CUBLAS_OP_T, CUBLAS_OP_N, dimensionSize, dimensionSize, 1, &alpha,
            meanVecPtr, 1, meanVecPtr, 1, &beta,
            thrust::raw_pointer_cast(d_cov2.data()), dimensionSize);

        alpha = 1.0f;
        beta = -1.0f;
        //  (X*transpose(X) / N) -  (MeanVec * transpose(MeanVec) / N*N)
        cublasSgeam(handle, CUBLAS_OP_N, CUBLAS_OP_N, dimensionSize, dimensionSize, &alpha,
            thrust::raw_pointer_cast(d_cov1.data()), dimensionSize, &beta, thrust::raw_pointer_cast(d_cov2.data()), 
            dimensionSize, thrust::raw_pointer_cast(d_covResult.data()), dimensionSize);

        // Go to other class and calculate its covarianceMatrix
        device2DMatrixPtr += trialSizes[i] * dimensionSize;
    }
    printVector(d_covResult);
    cublasDestroy(handle);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.