본문 바로가기

dev_AI_framework

GPU 백엔드와 GEMM/GEMV 에 대해...

백엔드 연산을 구현하면서 기초 지식을 다시 다지기

 

백엔드란?

아이디어: “수학 연산을 어디서 돌릴지”를 추상화한 층.

  • CPU 백엔드 → numpy/Eigen 같은 걸로 계산
  • GPU 백엔드 → CUDA 커널 + cuBLAS/cuSOLVER로 계산

코드에서 토글

xp = cupy if use_gpu else numpy  # 공통 연산자(dot, exp, sum...)를 xp로 호출

흐름(GPU일 때):

  1. 호스트(Python/CPU)에서 **장치 메모리(GPU)**에 배열을 올림(H2D 복사)
  2. 연산 요청 → 커널/라이브러리 호출(cuBLAS, cuSOLVER…)
  3. 필요하면 결과를 CPU로 내림(D2H 복사)
    왕복을 줄이고 GPU에 “상주”시키는 게 핵심

 

BLAS가 뭐고, GEMM/GEMV는 뭔데?

BLAS는 “표준 선형대수 연산” 규격이고, CUDA에선 cuBLAS가 구현체

  • Level-1 (벡터-벡터): AXPY( y ← a·x + y ), DOT(내적), NRM2(노름), SCAL(스칼라 곱)
  • Level-2 (행렬-벡터): GEMV( y ← α·op(A)·x + β·y )
    • A( m×n ), x( n ) → y( m )
  • Level-3 (행렬-행렬): GEMM( C ← α·op(A)·op(B) + β·C )
    • A( m×k ), B( k×n ) → C( m×n )
    • GPU는 이게 최고 효율(연산/메모리 비율이 큼)

 

왜 이게 중요한가(모든 걸 GEMM/GEMV로 생각하기)

대부분의 ML/DL 핵심 연산은 결국 아래 둘의 조합

  • 로지스틱/선형회귀
    • 예측: z = X @ w → GEMV (X: n×d, w: d)
    • 그라디언트: g = Xᵀ @ (p − y) → GEMV(전치)
    • (선택) Xᵀ X → GEMM, Xᵀ y → GEMV
  • Ridge/OLS (직접해법)
    • XtX = Xᵀ X → GEMM, Xty = Xᵀ y → GEMV
    • 선형시스템 (XtX + αI) w = Xty → 선형해법(Cholesky/QR/CG)
  • KNN/KMeans (거리)
    • ∥x ⁣− ⁣y∥2=∥x∥2+∥y∥2−2x⋅y\|x\!-\!y\|^2 = \|x\|^2 + \|y\|^2 − 2 x·y
    • x·y 부분을 큰 GEMM으로 처리 + 노름은 벡터 리덕션
  • 소프트맥스/시그모이드/손실: 원소별 연산(elementwise) + 리덕션(sum/mean)
    → 커널 하나(혹은 두세 개)면 충분

 

최소 연산 체크리스트(진짜 필요한 것)

“레이어별 커널 잔뜩”은 필요 없고, 아래만 있으면 대부분 커버돼.

  1. BLAS 기본
    • GEMM, GEMV, DOT, AXPY, SCAL, 리덕션(sum, mean)
    • (있으면 좋음) Strided-batched GEMM: 작은 행렬 다수 처리
  2. 원소 연산
    • add_bias(행 단위), sigmoid, softmax(안정 버전), logsumexp, clip
  3. 선형시스템/최적화
    • Cholesky/QR solve(cuSOLVER) 혹은 CG(Conjugate Gradient) 반복해법
    • Prox-soft-threshold(L1용 shrink): Lasso/ElasticNet의 핵심
  4. (옵션) 거리/히스토그램
    • KNN/KMeans용 pairwise 거리(사실상 GEMM 변형)
    • 트리/GBDT는 히스토그램 축적(초기엔 CPU로 두자)

 

“Backend” 클래스를 두면 쉬워진다

레이어 중심이 아니라 연산 중심으로 한 층을 두면, 상위 알고리즘은 수학만 신경 쓰면 돼.

class Backend:
    def gemm(self, A,B, tA=False, tB=False): ...
    def gemv(self, A,x, tA=False): ...
    def add_bias(self, X,b): ...
    def sigmoid(self, Z): ...
    def softmax(self, Z): ...
    def sum(self, X, axis=None): ...
    def cholesky_solve(self, A,b): ...    # Ridge/OLS
    def prox_soft_threshold(self, w,tau): ...  # L1(엘라스틱넷)

 

  • CPUBackend → numpy/scipy로 위 함수 구현
  • CUDABackend → cuBLAS/cuSOLVER/네 커널로 구현
  • 알고리즘(Linear/Logistic/Lasso 등)은 오직 backend.gemv/gemm/...만 호출