큰 공분산 행렬의 병렬 계산


9

~ 범위의 크기를 갖는 공분산 행렬을 계산해야합니다 . 우리는 GPU와 클러스터에 액세스 할 수 있으며 이러한 계산 속도를 높이는 가장 좋은 병렬 접근 방법이 무엇인지 궁금합니다.10000×10000100000×100000


1
공분산 행렬에 대한 특수성이 기대됩니까? 예를 들어, "0 근처"상관 관계가 많습니까?
Dr_Sam

아니요, 지금은 아무것도 기대할 수 없습니다.
길을여십시오.

당신의 k는 무엇입니까? 즉, 각 데이터 벡터의 길이는 얼마입니까? 그들은 이미 제로 평균입니까?
Max Hutchinson

아니, 그들은 제로 평균이 아니고, 어떤 가치를 가질 수 있습니다
길을여십시오

3
@flow :``임상 데이터 ''는 적용이지만 사용하지는 않습니다. 내 질문은 : 공분산 행렬이 있다고 가정하면 (수학적 관점에서) 어떻게 할 것입니까? 내가 묻는 이유는 결국 항상 계산을 거의하지 않기 때문이며, 이것을 고려하면 전체 공분산 행렬을 계산하지 않으면 서 원하는 후속 결과를 얻을 수 있으므로 일반적으로 속도를 크게 높일 수 있습니다.
Arnold Neumaier

답변:


17

첫 번째는 BLAS를 사용하여이 작업을 수행 할 수 있다는 것을 인식하는 것입니다. 데이터 행렬이 (각 는 하나의 측정 값에 해당하는 열 벡터이며 행은 시험판 임) 공분산은 다음과 같습니다. 다음과 같이 작성할 수 있습니다. 여기서 는 모든 요소가 1 인 행 벡터 이므로 는 열 합의 행 벡터입니다. . 이것은 BLAS로 완전히 쓸 수 있습니다.엑스=[엑스1엑스2엑스...]아르 자형미디엄×엑스

나는제이=이자형[엑스나는,엑스제이]이자형[엑스나는]이자형[엑스제이]=1케이엑스나는케이엑스제이케이12(케이엑스나는케이)(케이엑스제이케이)
=1엑스엑스12(1엑스)(1엑스)
(1)(1엑스)엑스엑스엑스중 하나입니다 GEMM A, 더 나은 아직, 또는 SYRK이 / HERK 하고 얻을 수 있습니다 로모그래퍼 GEMV , 다시 GEMM 또는 SYRK / HERK와,와 함께 prefactors SCAL .(1엑스)=

데이터 및 결과 매트릭스는 약 64GB가 될 수 있으므로 단일 노드 또는 노드 가치 GPU에 적합하지 않습니다. 비 GPU 클러스터의 경우 스칼라 팩 처럼 느껴지는 PBLAS 를 볼 수 있습니다 . GPU의 경우 다중 노드 라이브러리가 아직 없습니다. Magma 에는 일종의 기본 병렬 BLAS 구현이 있지만 사용자 친화적이지 않을 수 있습니다. CULA 는 아직 다중 노드를 하지 않는다고 생각 하지만 계속 지켜봐야 할 부분 입니다. CUBLAS 는 단일 노드입니다.

또한 병렬 처리를 직접 구현하는 것이 좋습니다. 특히 MPI에 익숙하고 기존 코드 기반에 연결해야하는 경우 특히 그렇습니다. 이렇게하면 CPU와 GPU BLAS간에 쉽게 전환 할 수 있으며 원하는 위치에서 정확하게 데이터를 시작하고 끝낼 수 있습니다. MPI_ALLREDUCE 호출 이 몇 개 이상 필요하지 않아야 합니다.


귀하의 분석 및 관련 BLAS 기능 목록에 감사드립니다. 귀하의 답변을 읽은 후 Scilab 개발 버전 (www.scilab.org)에서 공분산 행렬 계산을 가속화하고 최적화하는 데 사용했습니다.
Stéphane Mottelet

그러나이 방법으로 공분산을 계산하면 이자형[엑스나는,엑스제이] ~에 가깝다 이자형[엑스나는]이자형[엑스제이]예 : en.wikipedia.org/wiki/…
Stéphane Mottelet

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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.