CLIP 최적화 계획과 기대 성능 지표 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki

1. 배치

a. 문제 정의

  • 현재 시스템은 이미지 하나씩 단위로 추론을 반복 처리
    • 매번 모델을 호출하면서 불필요한 연산 반복과 I/O 오버헤드가 발생할 수 있음
  • 또한, PyTorch는 배치 단위 입력에 대해 더 효율적인 연산 경로를 사용하므로, batch size 조절을 통해 성능 개선 가능성이 있음

b. 가설

[ "CLIP 모델 추론은 이미지 단위의 독립 처리가 아니라, 배치 단위로 처리할 때 더 높은 연산 효율을 낼 수 있다.” ]

  • CLIP은 Transformer 기반 모델이며, 대부분의 Transformer는 내부적으로 배치 단위 병렬 처리에 최적화됨 → 배치 단위 입력이 벡터화된 연산에 더 잘 맞음
  • PyTorch 연산은 내부적으로 멀티스레드 최적화(OpeanMP, MKL 등) → 반복적인 작은 단위보다는 큰 단위로 일괄 처리할 때 CPU 효율이 좋음
  • I/O 또는 컨텍스트 스위칭 오버헤드 존재 → 100장을 하나씩 처리하면 100번 함수 호출 vs. 배치 처리로 오버헤드 최소화
  • 실험 환경이 멀티코어 기반 (Apple M2 Pro, 10코어) → 병렬 처리에 유리한 환경이므로, 배치 사용 시 더욱 최적화된 성능 기대 가능

⇒ 16, 32 단위로 배치 처리 실험

c. 테스트 결과 요약

항목 Batch 1 Batch 16 Batch 32
실행 시간 (s) 3.61 1.86 1.78
평균 CPU (%) 590.76 594.92 584.31
최대 CPU (%) 624.62 653.28 658.68
💾 평균 RAM (MB) 1496.76 1510.26 1545.84
💾 최대 RAM (MB) 1518.07 1621.06 1670.97

d. 분석

가. 실행 시간

배치비교1.png

Batch 1   : 3.61초
Batch 16  : 1.86초  🔽 -48.5%
Batch 32  : 1.78초  🔽 -4.3% (거의 유지)

batch size 16에서 큰 성능 향상, 이후에는 미세한 개선

나. CPU 사용률

배치비교2.png

Batch 1   : 590.76%
Batch 16  : 594.92% 🔼
Batch 32  : 584.31% 🔽 (약간 감소)

멀티코어 최대 활용 구간에서 포화, batch 증가로 연산 효율 분산 추정

다. 메모리 사용량

배치비교3.png

Batch 1   : 1496MB
Batch 16  : 1510MB 🔼 +14MB
Batch 32  : 1545MB 🔼 +35MB

거의 선형 증가, 과도하지 않음 → 서비스용으로도 무리 없음

e. 결론

  • 실험 결과, 이미지 100장을 처리하는 데 걸리는 시간은
    • Batch 1: 평균 3.61초
    • Batch 16: 평균 1.86초 (⏱️ 약 48.5% 감소)
    • Batch 32: 평균 1.78초 (⏱️ 추가 개선 미미)
  • 이러한 결과는 다음과 같은 이유로 가설을 입증
    1. 배치 단위 처리는 CLIP 모델의 내부 연산 방식(벡터화 연산, 병렬 매트릭스 곱 등)에 잘 부합 → 연산량은 유지되면서도 반복 호출 오버헤드가 제거

    2. CPU 멀티코어 환경에서 PyTorch의 연산 병렬화가 배치 단위 입력에서 더욱 효율적으로 작동

      → 평균 CPU 사용률은 Batch 600% 범위로 유지되면서 성능은 개선

    3. **메모리 사용량(RAM)**은 batch 크기에 따라 선형 증가했지만, 1.5~1.6GB 내외

      → 시스템 리소스 한계 내에서 안정적으로 배치 크기를 늘릴 수 있었음

f. 적용 계획

가.적용 배치 사이즈 : 16

나. 적용 배경 및 근거

  1. 처리 속도 최적점 도달
    • Batch 1 대비 Batch 16에서 추론 시간이 약 48.5% 감소 (3.61초 → 1.86초)
    • Batch 32는 성능이 거의 동일한 수준(1.78초)으로, 추가 향상은 미미
    • 따라서 Batch 16이 가장 높은 속도 향상을 보이는 구간
  2. CPU 사용률 최대 활용
    • 평균 CPU 사용률은 Batch 16에서 594.92%로 측정됨 (시스템 논리 코어 ≒ 600%)
    • 병렬 연산이 포화 상태에 근접 → 이후 배치 증가는 효과 미미
  3. 메모리 사용량 안정
    • Batch 16 평균 RAM 사용량: 1510MB
    • Batch 32: 1545MB로 증가하지만 성능 이득은 적음
    • 따라서 Batch 16은 메모리 대비 성능 효율이 가장 높은 지점
  4. 서비스 안정성과 확장성 확보
    • 배치 기반 병렬 추론은 추후 GPU 도입 시에도 적용 가능
    • Batch Size 16은 CPU 환경에서의 안정성과 성능의 균형점에 위치

2. 연산 스레드 제한

a. 문제 정의

  • 현재 Batch Size 16 기준 CPU 평균 사용률은 약 595%,
    • 이는 시스템의 6개 이상 논리 코어가 동시에 사용되고 있음을 의미
    • 이는 PyTorch가 내부적으로 연산을 자동 병렬화하고 있다는 증거

b.  가설

[ “CLIP 모델을 단일 스레드로 실행해도, 배치 처리와 연산 최적화를 통해 여전히 수용 가능한 처리 속도를 유지할 수 있으며, 동시에 CPU 사용량을 100% 이하로 낮춰 다중 사용자 환경에서 자원 경합 없이 안정적으로 동작할 수 있다.” ]

  • PyTorch는 기본적으로 멀티 스레드를 사용하여 벡터/행렬 연산을 빠르게 수행

⇒ 스레드 개수를 1개로 제한한 후, 배치 크기를 1, 16으로 실험

c. 테스트 결과 요약

항목 Batch 1 (기본) Batch 1 (Thread 1) Batch 16 (기본) Batch 16 (Thread 1) 변화 (기본 ↔ 단일 스레드)
⏱️ 실행 시간 3.61s 3.30s 1.86s 2.25s 🔺 Batch 1: ↓8.6%, Batch 16: 🔺 21% 느려짐
🧠 평균 CPU 사용률 (%) 590.76% 122.36% 594.92% 118.60% 🔻 약 80% 감소
🚀 최대 CPU 사용률 (%) 624.62% 134.30% 653.28% 137.16% 🔻 대폭 감소
💾 평균 RAM 사용량 (MB) 1496.76 1407.30 1510.26 1454.32 🔽 소폭 감소
💾 최대 RAM 사용량 (MB) 1518.07 1424.91 1621.06 1508.28 🔽 소폭 감소

d. 분석

가. 실행 시간 (num_threads=1 설정 전후)

스레드1.png

Batch 1   : 3.61초 → 3.30초  🔽 -8.6%
Batch 16  : 1.86초 → 2.25초  🔼 +21.0%

→ 단일 입력에서는 오히려 스레드 제한이 더 효율적이었고,

→ 배치 처리에서는 병렬화 이득을 잃으며 성능 감소

나. 평균 CPU 사용률

스레드2.png

Batch 1   : 590.76% → 122.36%  🔽 -79.3%
Batch 16  : 594.92% → 118.60%  🔽 -80.1%

→ 스레드 수 제한으로 두 경우 모두 6코어 → 1~2코어 수준으로 제한

다. 평균 메모리 사용량

스레드3.png

Batch 1   : 1496.76MB → 1407.30MB  🔽 -6.0%
Batch 16  : 1510.26MB → 1454.32MB  🔽 -3.7%

→ 스레드가 줄어들며 메모리 버퍼 및 캐시 사용량도 함께 감소, RAM이 효율적으로 관리됨

e. 결론

  • 연산 스레드를 1개로 제한한 경우, 이미지 100장을 처리하는 데 걸리는 시간은
    • Batch 1: 3.30초 (⏱️ 기존 대비 8.6% 향상)
    • Batch 16: 2.25초 (⏱️ 기존 대비 21% 느려짐)
  • 이러한 결과는 다음과 같은 이유로 가설을 부분적으로 입증:
    1. 단일 입력 처리에서는 스레드 병렬화 오버헤드가 오히려 성능 저하를 유발

      Batch 1의 경우, 연산 스레드를 제한하자 오히려 속도가 개선되었고,

      → CPU 및 메모리 사용량 또한 함께 감소하여 가볍고 효율적인 실행 가능

    2. 배치 입력 처리에서는 병렬화 이점을 포기하면서 속도 저하 발생

      Batch 16은 병렬 연산 최적화를 잃으면서 추론 시간이 증가

      → 하지만 CPU 사용률은 약 600% → 120%로 대폭 절감되었고, 메모리도 안정적으로 감소

    3. 스레드 제한은 성능보다 자원 예측성과 안정성이 중요한 상황에 적합

      → 다수의 사용자 요청이 들어오는 API 환경, 리소스 격리가 필요한 컨테이너 환경에서

      스레드 제한이 매우 유효한 전략이 될 수 있음

f. 적용 계획

가. 적용 배치 사이즈, 스레드 수 : 16, 1

나. 적용 배경 및 근거

  • 리소스 사용량 최적화
    • 스레드를 1개로 제한함으로써 CPU 사용률을 600% → 120% 수준으로 절감
    • 멀티 요청 환경에서도 자원 경합 없이 안정적인 추론 성능 유지 가능
  • 속도-자원 효율의 균형
    • Batch Size 16은 병렬성 없이도 2.25초의 처리 속도 확보
    • Batch 1보다 빠르며, Batch 32 대비 성능/메모리 사용량 측면에서 더 안정적
  • 배치 처리의 연산 효율성 유지
    • 16장 단위 입력은 여전히 CLIP의 벡터화 연산 구조에 잘 부합
    • 단일 이미지 반복보다 I/O와 호출 오버헤드가 줄어들어 전체 처리 효율이 향상됨

3. 캐싱

a. 문제 정의

  • 현재 파이프라인에서는 이미지 입력 시, CLIP 모델로부터 임베딩을 추출한 뒤 여러 후속 작업(카테고리화, 중복 필터링, 저품질 분류, 하이라이트 스코어링 등)을 수행
  • 하지만, 매번 후속 태스크가 실행될 때마다 같은 이미지에 대해 임베딩을 다시 계산하거나 중복 보관하는 비효율이 발생할 수 있음
  • 이로 인해 불필요한 연산, 메모리 낭비, 모델 호출 오버헤드가 누적될 수 있음

b. 가설

[ "이미지 임베딩을 한 번만 추출하고, 이후 모든 태스크에서 이를 공유해 사용한다면 성능 향상뿐 아니라 리소스 사용 최적화도 가능할 것이다." ]

  • CLIP 임베딩은 입력 이미지의 핵심 표현 벡터로, 이후 단계에서는 동일한 임베딩을 반복 활용
  • 따라서 한 번 계산한 임베딩을 공유/재사용하면 중복 연산이 제거됨
  • 모든 태스크(중복 필터링, 품질 평가, 하이라이트 스코어링 등)는 공통적으로 동일한 임베딩을 입력으로 사용
  • 캐싱 구조를 도입하면 모델 호출 횟수를 1회로 고정, 이후는 numpy 또는 tensor 연산만 수행

c. 테스트 결과 요약

항목 평균 CPU (%) 평균 메모리 (MB) 처리 시간 비고
CLIP 임베딩 118.60% 1454.32 MB 2.25초 가장 연산량이 큰 단계
중복 판별 101.26% 254.98 MB 0.01초 연산량 거의 없음
품질 측정 100.70% 238.15 MB 0.01초 유사도 기반 연산만 수행
카테고리 분류 100.18% 249.66 MB 0.01초 대표 벡터와 유사도 계산
하이라이트 스코어링 102.44% 239.94 MB 0.01초 선형 회귀 추론만 수행

d. 분석

기준 임베딩 캐싱 X (작업마다 임베딩 포함) 임베딩 캐싱 O (한 번만 수행)
전체 처리 시간 1.86초 × 5 = 9.3초 1.86 + (0.01×4) = 1.90초
성능 향상 비율 ⏱️ 약 5배 이상 속도 향상
불필요한 중복 연산 임베딩 연산이 5번 반복 1회만 수행

e. 결론

  • 실험 및 시스템 구조 분석 결과, 다음과 같은 이점이 확인
  1. 중복 연산 제거
    • 기존에는 동일 이미지에 대해 여러 태스크에서 임베딩 연산 반복
    • 캐싱 후에는 한 번만 모델 추론, 나머지는 재사용만 하므로 효율성 극대화
  2. 성능 및 응답 속도 개선
    • 실측 결과, CLIP 모델 추론(임베딩)은 전체 처리 시간의 대부분을 차지
    • 캐싱 적용 시, 후속 태스크들은 0.01초 이내 처리 가능
  3. 메모리 안정성 확보
    • 임베딩은 단일 벡터(예: 512차원 float32 → 약 2KB)
    • 수백 장 이미지를 캐싱해도 수 MB 수준이므로 RAM 부담 없음
  4. 아키텍처 확장성 확보
    • 임베딩을 중심으로 후속 태스크들을 모듈화하면
    • 추후 분산처리 또는 비동기 구조 전환 시에도 효율적인 상태 유지 가능

f. 적용 계획

가. 적용 항목: CLIP 임베딩 캐싱

나. 적용 위치: 이미지 업로드 직후 또는 추론 전단에서 1회 수행

다. 활용 방식:

  • embedding = get_clip_embedding(image) 결과를 메모리/DB/파일 등으로 캐싱
  • 후속 처리 (중복 판별, 품질 분석, 하이라이트 등)는 이 임베딩만 참조