การคำนวณแบบขนานของเมทริกซ์ความแปรปรวนร่วมขนาดใหญ่


9

เราจำเป็นต้องคำนวณเมทริกซ์ความแปรปรวนร่วมที่มีขนาดตั้งแต่ 10000×10000 ถึง 100000×100000. เราสามารถเข้าถึง GPU และกลุ่มเราสงสัยว่าอะไรคือวิธีการขนานที่ดีที่สุดในการเร่งการคำนวณเหล่านี้


1
คุณคาดหวังลักษณะพิเศษสำหรับเมทริกซ์ความแปรปรวนร่วมของคุณหรือไม่ ตัวอย่างเช่นความสัมพันธ์ "ใกล้ 0" จำนวนมาก?
Dr_Sam

ไม่เราไม่สามารถคาดหวังสิ่งใดได้ในขณะนี้
เปิดทาง

k ของคุณคืออะไร นั่นคือเวกเตอร์ข้อมูลแต่ละอันมีความยาวเท่าใด พวกเขาเป็นศูนย์หมายถึงแล้ว?
Max Hutchinson

ไม่พวกเขาไม่ได้เป็นศูนย์ - พวกเขาสามารถรับสิ่งที่มีค่า
เปิดทาง

3
@flow: '' ข้อมูลทางคลินิก '' เป็น appl8ication แต่ไม่ใช่การใช้งาน คำถามของฉันคือ: ถ้าคุณมีเมทริกซ์ความแปรปรวนร่วมคุณจะทำอะไรกับมัน (จากมุมมองทางคณิตศาสตร์) เหตุผลที่ฉันถามก็คือในท้ายที่สุดเรามักจะคำนวณน้อยมากและถ้านำมาพิจารณาหนึ่งจะสามารถเร่งสิ่งต่าง ๆ ได้อย่างมากโดยหลีกเลี่ยงการคำนวณเมทริกซ์ความแปรปรวนร่วมแบบเต็มในขณะที่ยังได้ผลลัพธ์ตามมา
อาร์โนลด์ Neumaier

คำตอบ:


17

สิ่งแรกคือการรู้ว่าคุณสามารถทำได้โดยใช้ BLAS ถ้าคุณเมทริกซ์ข้อมูลคือX=[x1x2x3...]Rม.×n (แต่ละ xเป็นเวกเตอร์คอลัมน์ที่สอดคล้องกับการวัดเดียว แถวคือการทดลอง) จากนั้นคุณสามารถเขียนความแปรปรวนร่วมเป็น:

ผมJ=E[xผม,xJ]-E[xผม]E[xJ]=1nΣkxผมkxJk-1n2(Σkxผมk)(ΣkxJk)
เราสามารถเขียนสิ่งนี้เป็น:
=1nXTX-1n2(1TX)T(1TX)
ที่ไหน (1T) คือแถวเวกเตอร์ที่มีองค์ประกอบทั้งหมด 1 ดังนั้น (1TX) เป็นเวกเตอร์แถวของผลรวมคอลัมน์ของ X. สิ่งนี้สามารถเขียนได้อย่างสมบูรณ์ในฐานะ BLASXTXเป็นGEMMหรือดีกว่าSYRK / HERKและคุณจะได้รับ(1TX)=กับGEMV ,Tกับ GEMM หรือ SYRK / Herk อีกครั้งและ prefactors กับScal

เมทริกซ์ข้อมูลและผลลัพธ์ของคุณสามารถอยู่ที่ประมาณ 64GB ดังนั้นคุณจะไม่พอดีกับโหนดเดียวหรือ GPU ที่คุ้มค่า สำหรับคลัสเตอร์ที่ไม่ใช่ GPU คุณอาจต้องการดูPBLASซึ่งให้ความรู้สึกเหมือน scalapack สำหรับ GPU นั้น multi-node libraries ยังไม่พร้อม Magmaมีการใช้งาน BLAS แบบขนานบางอย่าง แต่อาจไม่เป็นมิตรกับผู้ใช้ ฉันไม่คิดว่าCULAจะทำหลายโหนด แต่สิ่งที่ต้องคอยดู CUBLASเป็นโหนดเดียว

ฉันขอแนะนำให้คุณพิจารณาใช้ความเท่าเทียมกันโดยเฉพาะอย่างยิ่งถ้าคุณคุ้นเคยกับ MPI และต้องเชื่อมโยงสิ่งนี้เข้ากับรหัสฐานที่มีอยู่ ด้วยวิธีนี้คุณสามารถสลับระหว่าง CPU และ GPU BLAS ได้อย่างง่ายดายและเริ่มต้นและสิ้นสุดด้วยข้อมูลที่คุณต้องการ คุณไม่ควรต้องการการโทรMPI_ALLREDUCEมากกว่าสองสามครั้ง


ขอขอบคุณสำหรับการวิเคราะห์และรายการฟังก์ชัน BLAS ที่เกี่ยวข้อง หลังจากอ่านคำตอบของคุณฉันได้ใช้สิ่งเหล่านี้เพื่อเร่งและปรับการคำนวณเมทริกซ์ความแปรปรวนร่วมในรุ่นพัฒนาของ Scilab (www.scilab.org)
Stéphane Mottelet

อย่างไรก็ตามได้รับการเตือนว่าการใช้วิธีคำนวณความแปรปรวนร่วมนี้อาจมีการยกเลิกเมื่อเกิดความหายนะ E[xผม,xJ] อยู่ใกล้กับ E[xผม]E[xJ]ดูตัวอย่างเช่นen.wikipedia.org/wiki/…
Stéphane Mottelet

1

ฉันใช้สูตรที่กำหนดโดย @ Max Hutchinson กับ CUBlas และ Cuda Thrust และเปรียบเทียบกับเครื่องมือคำนวณผลต่าง co แบบออนไลน์ ดูเหมือนว่าฉันจะให้ผลลัพธ์ที่ดี รหัสด้านล่างนี้ได้วางแผนไว้ที่ 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);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.