콘텐츠로 이동

Batch.h

Go to the source code of this file.

Batch 검사는 이미지를 한 번에 여러 장 처리하는 기능입니다.

ExampleClsRuntimeBatch.cpp 예제는 Batch 검사를 수행하는 방법을 보여줍니다. SAIGE VISION의 Batch 검사는 이미지를 한 번에 여러 장 처리하는 기능입니다. 이 때, GPU의 메모리를 더 많이 사용합니다. 또한, 한 번에 무조건 많은 이미지를 처리한다고 해서 항상 비례하게 빨라지는 것은 아닙니다. 따라서 적절한 Batch Size를 선택하는 것이 필요합니다.

Batch 검사 단계

Batch 검사를 수행하기 위해서는 다음과 같은 단계를 거칩니다.

  • 핸들 생성 시 Batch Size를 설정
  • 여러 장의 이미지 데이터를 메모리에 로드
  • Batch Infer API SaigeInferFromMultipleByteArray 호출
  • 각 결과 핸들로부터 각 이미지의 검사 결과를 받아오고 활용

Batch Size 설정

SaigeHandleConfig::batchSize 값을 설정 후 플래그를 지정하여 핸들을 생성합니다.

 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
// ExampleClsRuntimeBatch.cpp:91

SaigeInferenceHandle handle = nullptr;
SaigeHandleConfig config;
config.batchSize = batchSize;
unsigned int handleFlags = SAIGE_SET_BATCH_SIZE;
if (optimize) {
    handleFlags |= SAIGE_OPTIMIZE_HANDLE;
}
retval = SaigeCreateInferenceHandle(&handle, kProjectTypeCLS, trainedModelPath.c_str(),
                                    deviceIndex, &config, handleFlags);
if (retval < 0) {
    std::cerr << "SaigeCreateInferenceHandle failed - " << SaigeStrError(retval) << std::endl;
    return retval;
}

이미지 로드

본 예제에서는 사전에 지정된 dataDir 경로 아래의 모든 .png 파일을 읽어들여서 imagePaths에 저장합니다. 그리고 나서, 이 이미지들을 batchSize에 맞게 애플리케이션 코드에서 묶어서 처리합니다. 즉, 본 예제에서 batchSize >= numOfImages 입니다.

아래의 코드는 전체 이미지 목록을 batchSize로 나누어서 검사하는 루프의 시작 부분입니다. ucBatchImageData에 각 iteration마다 해당하는 이미지를 로드하여 저장합니다.

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// ExampleClsRuntimeBatch.cpp:224

unsigned char** ucBatchImageData = new unsigned char*[batchSize];
for (int i = 0; i < batchSize; ++i) {
    ucBatchImageData[i] = nullptr;
}

int numIter = (int)std::ceil((float)imagePaths.size() / (float)batchSize);
int numFinalImages = imagePaths.size() % batchSize;
if (numFinalImages == 0) numFinalImages = batchSize;
int numBatch = batchSize;

// start iteration
for (int iter = 0; iter < numIter; ++iter) {
    if (iter == numIter - 1) {
        numBatch = numFinalImages;
    }
    // 이미지 데이터 메모리를 해제합니다. (루프일관성)
    clearImageBatch(ucBatchImageData, batchSize);

    // 이미지 batch를 생성합니다.
    int w_batch = 0, h_batch = 0, c_batch = 0, type_batch = -1;
    bool invalid_batch = false;
    for (int i = 0; i < numBatch; ++i) {
        invalid_batch = false;
        int img_idx = i + iter * batchSize;
        ucBatchImageData[i] =
            (unsigned char*)SaigeGetImageData(imagePaths[img_idx].c_str(), &w, &h, &c, &type);
        if (i == 0) {
            w_batch = w;
            h_batch = h;
            c_batch = c;
            type_batch = type;
        }
        if (ucBatchImageData[i] == nullptr) {
            // 로드에 실패한 이미지가 있으면 다음 배치로 넘어갑니다.
            invalid_batch = true;
            std::cerr << "SaigeGetImageData() failed - " << imagePaths[img_idx] << std::endl;
            break;
        }
        if (w != w_batch || h != h_batch || c != c_batch) {
            // 한 배치 내의 이미지 크기는 모두 같아야 합니다.
            invalid_batch = true;
            std::cerr << "Image sizes in a same batch should be same." << std::endl;
            break;
        }
    }

Batch Infer API 호출

Batch Infer API인 SaigeInferFromMultipleByteArray 함수를 호출합니다.

285
286
287
288
289
290
291
292
293
// ExampleClsRuntimeBatch.cpp:287

retval = SaigeInferFromMultipleByteArray(
    handle, numBatch, (const unsigned char**)ucBatchImageData, w, h, imageFlag, results);
if (retval < 0) {
    std::cerr << "SaigeInferFromByteArray() failed - " << SaigeStrError(retval)
              << std::endl;
    continue;
}

검사 결과 받아오기

Batch Infer API 호출 후, 각 결과 핸들로부터 각 이미지의 검사 결과를 받아옵니다. 따라서 결과 핸들의 개수는 최소한 numOfImages 만큼 필요합니다.

298
299
300
301
302
303
304
305
306
307
308
309
310
// ExampleClsRuntimeBatch.cpp:300

for (int i = 0; i < numBatch; ++i) {  // DEFAULT
    retval = SaigeGetInferenceResultScore(results[i], scores[i].data(), true);
    if (retval < 0) {
        std::cerr << "SaigeGetInferenceResultScore() failed - " << SaigeStrError(retval)
                  << std::endl;
        clearImageBatch(ucBatchImageData, batchSize);
        delete[] ucBatchImageData;
        SaigeDestroyInferenceHandle(handle);
        clearResults(results, batchSize);
        return retval;
    }

Batch Size 및 numOfImages의 관계

이 때, 설정한 Batch Size와 실제로 SaigeInferFromMultipleByteArray 함수에 입력하는 이미지의 개수가 일치해야하는 것은 아닙니다.

Batch Size는 실제로 GPU에서 동시에 처리되는 이미지의 개수입니다.

따라서, Batch Infer API에 입력되는 이미지 개수인 numOfImages가 Batch Size 보다 많으면 Batch Size만큼 나뉘어서 실제 연산이 여러번 수행됩니다. 또한 입력되는 이미지가 Batch Size보다 적다면 GPU 리소스를 낭비하게 됩니다. 따라서 Batch Size는 성능의 튜닝 요소로 중요하게 설정되어야 합니다.

즉, 입력 이미지의 개수 numOfImages는 어플리케이션의 특징에 따라서 다르게 설정될 수 있습니다. 예를 들어, 여러 이미지를 일정 시간 모아서 한번에 처리할 수 있는 경우가 있고, 매번 이미지가 취득되는 순간 가능한 빨리 검사 결과가 필요한 경우도 있습니다.

일반적으로 검사의 Throughput을 높이기 위해서는 batchSize를 가능한 크게 설정하는 것이 좋습니다. 반대로 검사의 Latency가 중요하다면 검사 이미지를 모을때까지 기다리지 않고 가능한 바로 검사 API를 호출하는 것이 좋습니다.