파인튜닝을 할 것인가에 대한 의사결정

  • 해야 하는 이유
    • 뛰어난 범용 모델이 특정 작업에서는 성능이 떨어질 수 있음
    • 편향 완화
    • distillation (큰 모델이 생성한 데이터로 작은 모델을 학습)
    • 다양한 도메인/여러 크기의 오픈소스 고품질 모델이 개발되면서 파인튜닝이 이전 대비 더 실현 가능하고 매력적인 선택지가 됨
  • 하지 말아야 하는 이유
    • 잘 작성된 프롬프트와 컨텍스트로도 비슷한 효과를 낼 수 있음
      • 프롬프트 캐싱 이전에는 반복적 프롬프트를 짧게 써도 되도록 파인튜닝하는 것이 토큰 사용 최적화 이점이 있었는데 프롬프트 캐싱이 도입되면서 이런 장점도 의미가 사라짐
    • 특정 작업이 아닌 다른 작업에서는 오히려 성능이 떨어질 수 있으므로 다양한 프롬프트를 사용해야 하는 애플리케이션에서는 문제 (모든 작업 유형에 대해 다 파인튜닝 하기는 어렵다)
    • 잘 주석이 달린 데이터를 얻는 비용, 모델 학습/디버깅/평가에 대한 지식이 필요, 최적화된 서빙(직접 호스팅 또는 API서비스)의 어려움, 모델을 모니터링하고 유지보수하기 위한 정책과 예산
  • 파인튜닝 vs. RAG
    • 모델이 잘 못하는 게 정보의 부족(사실)인지 행동 방식의 문제(형식)인지가 중요
    • 시사 문제에 대한 질의, 최신 정보가 필요한 작업에서는 RAG가 더 낫다는 연구 결과가 존재
    • 모델이 원하는 출력 형식을 따르지 못하거나(sementic parsing) 요청한 작업과 무관한 대응을 하는 경우 파인튜닝이 적합
    • 물론 두 가지가 상호배타적인 것은 아님. 프롬프트만으로 해보고 예시 추가해보고 단순한 검색 수준의 RAG 추가해보고 파인튜닝 또는 임베딩 기반 검색 같은 고급 RAG 또는 둘다 해보기.. 이런 식으로 개발 과정을 밟게 됨 src

모델의 메모리 사용에 대한 이해와 양자화

  • 파인튜닝은 기본적으로 메모리를 많이 사용하므로 메모리 사용량과 병목 현상에 대한 이해가 필요
  • 모델은 어디에 메모리를 쓰는가
    • 핵심 요소는 학습 가능한 파라미터의 수
    • 추론 시에는 forward pass만, 학습 시에는 forward/backward pass
    • forward pass의 경우, 파라미터 수(N) 각 파라미터에 필요한 메모리(M) + activation 및 Transformer key-value vector (시퀀스/배치 크기에 비례하여 증가)
      • activation, key-value의 경우 모델 가중치 메모리의 약 20%로 가정하여 로 생각해볼 수 있음. 모델이 커질수록 이는 급격히 증가할 것
    • 학습시에는 여기에 backward pass를 더하여
      • 모델 가중치 + activation + gradient + optimizer state 만큼을 사용
        • 그래디언트와 옵티마이저 스테이트(옵티마이저 종류에 따라 각 파라미터별 0~2개)는 파라미터에 비례함
      • 그래디언트 계산을 위해 activation을 계속 저장한다면 그 메모리가 모델 가중치 메모리를 훨씬 넘어설 수 있으므로, 메모리 대신 속도를 희생하여 이를 저장하지 않고 필요할 때마다 재계산(gradient checkpointing)할 수 있음
  • 양자화(quantization)
    • 파라미터 수만큼이나 각 파라미터에 필요한 메모리의 양도 직접적인 영향
      • FP16, FP32, FP64, BF16, TF32,… 부동소수점을 표현하기 위해 몇바이트를 사용하느냐, 부호 1비트 외의 범위/정밀도에 얼만큼씩 쓰느냐
      • FP64는 신경망에서는 거의 사용되지 않음. 저정밀도 표현으로 변경하면 메모리 사용량을 낮추되 값이 부정확해질 수 있음
    • 정밀도를 낮추는 메모리 최적화 방식 = 양자화
      • 일반적으로는 가중치 양자화, 학습 후 양자화(PTQ; post-training)가 널리 사용됨
      • 높은 정밀도로 모델 학습 후 정밀도를 낮춰 서빙하는 것이 표준이 됨
        • 필요 시 혼합 정밀도로 서빙될 수도 있음
      • 정밀도를 낮추면 메모리 사용량뿐 아니라 계산 속도도 향상되는 경우가 많음(배치 크기를 늘릴 수 있어 학습 및 추론 지연 시간 짧게)
      • 학습 양자화는 학습 시 비용을 줄이기 위해+추론 시 양자화하면서 발생하는 성능 갭을 줄이기 위해 연구되고 있음
        • QAT(Quantization-aware training)은 학습은 고정밀도로 하되 낮은 정밀도에서의 동작을 시뮬레이션
        • 아예 저정밀도로 학습하는 경우 대체로 혼합 정밀도 방식으로 가중치 사본을 고정밀도로, 그래디언트/활성화 값은 저정밀도로 유지 + 다수의 ML 프레임워크가 AMP(automatic mixed precision) 기능을 제공

파인튜닝 기법

  • PEFT (parameter-efficient FT)
    • 훨씬 적은 파라미터를 사용하면서 전체 파인튜닝에 가까운 성능을 내기 위해
      • adapter-based : 모델 가중치에 추가 모듈을 붙이는 방식(파라미터를 추가)
      • soft prompt-based: 특별한 학습 가능한 토큰을 도입해 모델이 입력을 처리하는 방식을 바꿈 (하드 프롬프트와 달리 사람이 읽을 수 없는 수치 벡터로 튜닝 과정에서 학습을 통해 조정됨)
  • LoRA (low-rank adaptation)
    • 가중치 행렬을 두 개의 더 작은 행렬 A,B의 곱으로 분해하고 이 작은 행렬을 업데이트한 후 원래 행렬로 병합 (즉 파인튜닝에서는 이 2개의 작은 행렬의 파라미터를 업데이트함으로써 파라미터 수를 줄임. 두 행렬의 곱은 원래의 full-rank행렬의 low-rank 근사치가 됨)
    • 왜 이런 방식이 효과적인가?
      • LLM은 수많은 파라미터를 가지고 있지만 실제로는 매우 낮은 내재적 차원을 가짐(대규모 사전 학습 과정이 자연스럽게 모델 내재적 차원을 줄이는 압축 과정임 = 더 잘 학습될수록, 소규모의 파라미터와 소량의 데이터만으로도 모델을 효과적으로 파인튜닝할 수 있음)
      • 그러면 처음부터(사전학습부터) low-rank학습을 하면 안되나? 저랭크 분해가 효과적으로 작동할 수 있는 지점까지 모델의 내재적 차원을 줄이기 위해서는 여전히 풀랭크 사전 학습이 필요 (src)
    • 어떤 행렬에 LoRA를 적용할까?
      • 주로 트랜스포머 모델에서 어텐션 모듈의 4가지 가중치 행렬에 적용(쿼리, 키, 값, 출력 투영)
        • 일반적으로 모델 내 같은 종류의 모든 행렬에 일괄적으로 적용
        • 모든 행렬에 적용할 수도 있지만 학습 가능한 파라미터 수가 제한되어 있다면 피드포워드 레이어보다는 어텐션 기반 LoRA가 더 효과적이고, 그 내에서도 일부만이라면 쿼리/값 행렬을 선택하는 것이 일반적
    • 하이퍼파라미터
      • 4~64 정도의 작은 r값만으로도 대부분의 활용 사례에서 충분한 것으로 알려져 있고 r값을 많이 늘려도 성능이 크게 개선되지는 않음
      • alpha(행렬 병합 시 저랭크 행렬 곱이 새 행렬에 미치는 영향)의 경우 alpha:r 비율을 보통 1:8에서 8:1 사이로 설정
    • 서빙
      • A,B를 미리 병합해서 W’ 상태로(지연시간 늘지 않음) vs. W,A,B를 따로 유지
      • 모델이 하나뿐이라면 보통 전자가 좋고 멀티 LoRA 서빙시에는 후자 (지연시간이 늘긴 하지만 풀랭크 행렬을 저장하지 않고 어댑터만 꺼내 쓰기 때문에 저장 공간 및 로딩 시간을 대폭 절약)
    • QLoRA
      • LoRA 어댑터 메모리 사용량은 전체 모델 가중치에 비해 매우 적으므로 여기서 학습 가능한 파라미터 수를 더 줄이는 것은 메모리 사용량에는 큰 영향력이 없고 가중치/활성화/그래디언트를 양자화하는 편이 더 효과적
      • QLoRA는 NF4 (4비트) 양자화 과정에 비용이 많이 들고, 다시 되돌리는 과정에서 시간이 걸려 학습 시간이 늘어날 수 있다는 단점
  • 모델 병합과 multi-task 파인튜닝
    • 여러 모델을 결합하여 맞춤형 모델 만들기
      • 여러 작업에 대한 각각의 파인튜닝을 진행 후 서로 다른 모델을 병합할 수 있음
      • 특히 온디바이스 배포처럼 메모리가 제한된 환경에서 효과적
    • 병합 방식
      • 합산: linear combination, spherical linear interpolation(SLERP)
      • 레이어 쌓기(passthrought, frankenmerging) = 여러 모델에서 서로 다른 레이어를 가져다가 쌓아올림. 보통 추가 파인튜닝이 필요. 모델 업스케일링에도 이 방식을 활용할 수 있음
  • 파인튜닝 전술
    • 기본 모델
      • progression path: 가장 저렴하고 빠른 모델로 파인튜닝 코드를 테스트해서 작동 확인 중간급 모델 파인튜닝 좋은 모델을 써서 성능이 어디까지 올라가는지 확인 비용 대비 성능 비교해서 선택
      • distillation path : 작은 데이터셋+가장 강력한 모델에서 시작 이 파인튜닝된 모델로 더 많은 학습 데이터를 생성 더 저렴한 모델을 학습
    • 파인튜닝 방법
      • 우선 LoRA 같은 방법으로 시작하고 나중에 전체 파인튜닝 시도
      • 전체 파인튜닝은 최소 수천개, 대개는 더 많은 예시가 필요하지만 PEFT 방법은 더 적은 데이터로도 괜찮은 성능을 낼 수 있음
      • 모델이 몇 개나 필요하고 어떻게 서빙할지도 고려(전체 파인튜닝은 LoRA 같은 어댑터 계열 모델과 달리 전체 모델을 여러개 서빙해야 함)
    • 파인튜닝 프레임워크
      • 파인튜닝 API vs. 직접(컴퓨팅 자원 준비 필요)
    • 파인튜닝 하이퍼파라미터
      • 학습률, 배치 크기, 에폭 수, 프롬프트 손실 가중치