개요

  • 추론 서비스란
    • 추론 서버는 필요한 하드웨어를 사용하여 애플리케이션에서 요청(사용자가 입력한 프롬프트)이 들어오면 리소스를 할당해 적절한 모델을 실행+결과 반환
  • 연산 병목
    • 연산 제약과 메모리 대역폭 제약(시스템 내부의 데이터 전송 속도 상 e.g. 메모리-프로세서 상)
      • 후자는 메모리 제약이라고도 간단히 부르지만 메모리 용량 부족과 구분해야 함
      • 루프라인 차트 (FLOP/byte, operation intensity - FLOT/s, performance)
        • 대각선 영역(낮은 연산 강도): 메모리 병목, 수평선 영역(높은 연산 강도): 연산 제약
    • 모델에 따라 연산 병목은 다르게 나타남(스테이블 디퓨전 같은 이미지 생성 모델은 보통 연산 제약, autoregressive 언어 모델은 보통 메모리 대역폭 제약)
    • 언어 모델 추론의 경우
      • 프리필: 입력 토큰을 병렬로 처리 = 연산 제약
      • 디코딩: 출력 토큰을 한번에 하나씩 생성 = 큰 행렬(모델 가중치)를 GPU로 불러와야 하므로 메모리 대역폭 제약
  • 온라인/배치 추론 API
    • 온라인 API: 지연 시간을 최적화, 요청이 들어오면 바로 처리
      • 일반적으로 챗봇/코드 생성 같은 서비스가 선택. 유저들이 기다리기 싫어하므로 많은 온라인 API는 스트리밍 모드를 지원해서 토큰이 생성되는 대로 하나씩 보내줌 (기다리는 시간이 줄어들지만 잘못 보내더라도 어쩔 수 없음)
    • 배치 API: 비용을 최적화하고 높은 처리량에 집중, 요청을 모아서 한번에 처리하거나 저렴한 하드웨어를 쓰는 등

지표

  • 지연 시간 = 질의 시점부터 응답 받기까지 걸리는 시간
    • TTFT(Time to First Token) : 첫 토큰이 나오기까지의 시간 =프리필 단계
    • TPOT(Time per Output Token) : 출력 토큰당 시간
      • 스트리밍 모드라면 매우 빠른 독자는 토큰당 120ms 읽는다고 가정하여 120ms 또는 초당 6~8개 토큰 정도가 챗봇 스트리밍 모드에서 충분
    • 전체 지연 시간은 TTFT + TPOT x (출력 토큰 수)
    • 이러한 지표들은 평균만 보는 것이 아니라 백분위수로 보는 것이 유용 - p50, p95, p99 등
  • 처리량 (throughput)
    • = 모든 사용자/요청 통틀어 초당 몇 개의 토큰을 만들어낼 수 있는가
      • 일반적으로는 출력 토큰만을 의미함. 입력+출력을 같이 셀 수도 있지만 둘은 병목 지점이 다르고 최신 추론 서버는 둘을 분리해서 처리하는 경우가 많기에 따로 연산하는 것이 좋음
      • 주로 token/s(TPS)
      • 완료한 요청 개수로 센다면 request/s(RPS), request/m(RPM)
    • 처리량은 연산 비용과 즉결(높을수록 비용이 낮다)
    • 좋은 처리량의 기준은 모델, 하드웨어, 작업에 따라 다름
    • 지연시간과 처리량의 트레이드오프 = TTFT, TPOT를 희생해서 처리량을 2~3배 올린 사례
      • goodput = SLO(software-level objective)을 만족하는 초당 요청 개수
      • e.g. SLO가 TTFT 최대 200ms, TPOT 최대 100ms 일 때 서비스의 RPM = 100인데 이 중 30건이 SLO를 만족하면 goodput = 30 RPM.
  • 활용률 (utilization)
    • = 리소스를 얼마나 효율적으로 사용하고 있는가
    • nvidia-smi의 GPU 활용률 지표는 크게 도움이 되지 않음(N시간 동안 작업 처리한 실제 시간)
    • MFU (model FLOP/s utilization): 머신이 할 수 있는 모든 연산 중 정해진 시간에 실제로 몇 개를 하고 있는지
      • 시스템이 최고 FLOP/s로 동작할 때 달성할 수 있는 이론상 최대 처리량 대비 실제 처리량(token/s)이 어느 정도인지를 나타냄
    • MBU (model bandswith utilization) : 사용 가능한 메모리 대역폭 중 실제 쓰이는 비율
      • 일반적으로 LM 추론에서 사용되는 메모리 대역폭은 파라미터 수 * 파라미터당 바이트 * token/s 이고 MBU는 이를 이론적 대역폭으로 나눈 것
    • 연산 제약 작업은 보통 MFU가 높고 MBU가 낮고, 메모리 대역폭 제약 작업은 MFU가 낮고 MBU가 높음
    • 학습은 작업 패턴이 예측 가능해서 더 효율적인 최적화를 적용할 수 있기 때문에 MFU가 보통 추론시보다 높음. 추론 시에는 프리필 때 연산위주라 MFU가 높고 디코딩 때 MBU가 높음
    • 같은 하드웨어에서 비슷한 작업을 할 때 활용률이 높다면 서비스가 효율적으로 돌아가고 있는 것이지만 목표는 활용률이 제일 높은 칩을 고르는 것이 아니라 작업을 더 빠르고 저렴하게 처리하는 것

AI 가속기

  • 가속기 = 특정 종류의 연산 작업을 빠르게 처리하도록 만들어진 칩
    • GPU: 수천 개의 작고 (CPU 대비) 약한 코어를 가지고 있어 다량의 작은 독립적인 연산으로 나눌 수 있는 작업에 최적화
  • 추론 전용으로 설계된 칩이나 트랜스포머 전용처럼 특정 모델 아키텍처에 특화된 칩들이 존재
  • 칩 평가의 핵심 특성
    • 연산 성능: FLOP/s (초당 부동 소수점 연산 횟수)
    • 메모리 크기와 대역폭: 메모리에서 수많은 코어들로 데이터를 계속해서 옮겨야 하므로 데이터 전송 속도가 중요 = GPU메모리는 CPU 대비 더 넓은 대역폭, 더 낮은 지연 시간이 필요
    • 전력 소모: 수십억 개의 트랜지스터를 껐다 켰다 하면서 엄청난 양의 에너지를 소비하고 열을 발생시킴 (냉각 필요) 전력 소모량이 컴퓨팅 확장에서 걸림돌이 됨

모델 최적화

  • 모델 압축
    • 모델 크기를 줄이는 것 = 양자화, 증류 등
    • 프루닝: 예측에 별로 도움 되지 않는 파라미터를 찾아서 0으로 만들어서 모델을 작게 만듦. 프루닝하여 그대로 쓰거나 추가로 파인튜닝해서 조정할 수 있음 단 원본 모델 아키텍처에 대한 이해가 필요하고 성능 향상도 다른 방법 대비 적은 경우가 많아서 많이 사용되지는 않는 편
    • weight-only 양자화가 이 분야에서는 가장 인기 있는 방법
  • 디코딩 병목 극복
    • 추측 디코딩 (speculative decoding, speculative sampling)
      • 더 빠르지만 성능이 낮은 모델(초안 모델)을 사용해서 토큰 시퀀스를 생성한 후 목표 모델이 이를 검증해서 동의하는 부분까지만 수락하고 그 다음 1개의 토큰을 직접 생성
      • 검증은 병렬화할 수 있어 검증 시간이 생성 시간보다 짧고 예측하기 쉬운 토큰들은 초안 모델이 잘 맞힐 수 있음 + 디코딩에는 연산 파워가 남기 때문에 이를 검증에 활용
      • 목표 모델과 같은 어휘/토크나이저 쓰는 초안 모델을 사용
    • 참조 기반 추론
      • 응답할 때 입력 토큰들을 많이 참조해야 하므로(e.g. 첨부된 문서를 인용해서 응답할 때) 이런 토큰들은 생성하지 말고 바로 복사하자
      • 컨텍스트-출력 간의 상당한 중복이 있는 시나리오에서는 매우 유용
    • 병렬 디코딩
      • 기존 시퀀스를 알면 다음 몇 개 토큰을 충분히 예측할 수 있을 때 여러 토큰을 동시에 생성하려고 시도
      • 앞의 토큰을 모르는 상태에서 뒤의 토큰을 생성하게 되므로 검증과 통합이 매우 중요함
  • 어텐션 매커니즘 최적화
    • KV 캐시란?
      • t+1번째 토큰을 생성할 때 t번째 토큰까지의 키,값 벡터가 필요하므로 1~t까지 키,값 벡터를 연산해서 저장해놓고 가장 최큰 토큰의 키,값 벡터만 새로 연산
        • ( 사용 가능한 하드웨어 저장 공간에 의해 제한됨. 롱컨텍스트 처리 애플리케이션에서 병목, 메모리 로드에도 시간이 걸려 빠른 응답도 문제)
    • 어텐션 매커니즘 재설계
      • 로컬 윈도우 어텐션, 크로스 레이어 어텐션, 멀티 쿼리 어텐션, 그룹 쿼리 어텐션
      • 시퀀스가 클 때 주로 병목이 되는 KV 캐시 크기를 줄이는 방법들
    • KV 캐시 최적화
      • vLLM, KV 캐시 양자화, 적응형 KV 캐시 압축, 선택적 KV 캐시 등
    • 어텐션 연산을 위한 커널 작성
      • 플래시 어텐션: 트랜스포머 기반 모델에서 많이 사용되는 여러 연산을 하나로 융합해 더 빠르게 실행되도록 함 원레 엔비디아 A100용으로 개발
      • 커널과 컴파일러
        • 커널 = GPU, TPU 등 특정 칩에 최적화된 코드
          • 일반적으로 CUDA, 트리톤 등 저수준 프로그래밍 언어로 작성
        • 벡터화
        • 병렬화
        • 루프 타일링 (하드웨어의 메모리 레이아웃/캐시에 맞게 loop의 데이터 접근 순서를 최적화)
        • 연산자 융합

서비스 최적화

  • 배치 처리
    • 정적 배치 처리
    • 동적 배치 처리 : 각 배치에 최대 대기 시간 설정
    • 연속 배치 처리 : 각 응답이 끝나는 대로 바로 사용자에게 반환될 수 있도록 함
  • 프리필과 디코딩 분리
  • 프롬프트 캐싱
    • 태스크에 따라 다르나 특히 시스템 프롬프트가 긴 애플리케이션의 경우 지연 시간/비용을 모두 크게 줄일 수 있음
    • KV 캐시처럼 상당한 저장공간을 필요로 할 수 있고 직접 구현은 쉽지 않음
  • 병렬 처리
    • 데이터 병렬 처리, 모델 병렬 처리
    • 복제 병렬 처리: 서비스하려는 모델의 복제본을 여러 개 만들어 작업을 병렬화