이전 내용
모델 가볍게 만들기
LLM을 배포하는 경우 GPU에서 가장 많은 비용이 발생하므로, GPU를 가능하면 적게 사용해서 비용을 낮춰야 비용 효율적인 서빙을 할 수 있다. LLM은 기존 딥러닝 모델에 비해 크기가 훨씬 크므로 효율적인 서빙이 중요하다.
※ 모델 서빙
모델 서빙이란, 훈련된 모델을 실서비스에 사용할 수 있도록, 클라이언트에게 모델 예측 결과를 효율적으로 전달하는 방식을 말한다.
[GPU를 효율적으로 활용하는 방식]
- 모델의 성능을 희생하더라도 비요을 크게 낮추는 방법
- 모델의 성능을 그대로 유지하면서 연산 과정의 비효율을 줄이는 방법
여기서는 모델의 성능을 일부 희생하더라도 비용을 크게 낮출 수 있는 방법에 대해 살펴본다.
이를 살펴보기에 앞서, 필요한 라이브러리를 먼저 설치한다. (코랩)
!pip install transformers==4.40.1 bitsandbytes==0.43.1 accelerate==0.30.0 datasets==2.19.0 auto-gptq==0.7.1 autoawq==0.2.5 optimum==1.19.1 -qqq
!pip install -U bitsandbytes
!pip install optimum -qqq
언어 모델 추론 이해하기
언어 모델은 입력한 텍스트 다음에 올 토큰의 확률을 계산하고 그중에서 가장 확률이 높은 토큰을 입력 텍스트에 추가하면서 한 토큰씩 생성한다. 언어 모델이 텍스트 생성을 마치는 이유는 두 가지로 나눌 수 있는데,
- 다음 토큰으로 생성 종료를 의미하는 특수 토큰을 생성하는 경우 생성을 종료한다.
- 사용자가 최대 길이로 설정한 길이에 도달하면 더 이상 생성하지 않고 종료한다.
▶ 두 가지 경우에 해당하기 전까지는 새로운 토큰을 추가한 텍스트를 다시 모델의 입력으로 넣는 과정을 반복한다.
언어 모델이 텍스트를 생성할 때는 한 번에 한 토큰씩만 생성할 수 있고, 다음 토큰과 그다음 토큰을 함께 예측할 수는 없다. 이를 입력 텍스트를 기반으로 바로 다음 토큰만 예측하는 자기 회귀적(auto-regressive) 특성을 갖는다고 하는데, 새롭게 생성하는 부분이 아니라 언어 모델에 입력하는 프롬프트는 이미 작성된 텍스트이기 때문에 한 번에 하나씩 토큰을 처리할 필요 없이 동시에 병렬적으로 처리할 수 있다.
이런 이유로 추론 과정을 사전 계산 단계(프롬프트 처리)와 디코딩 단계(한 토큰씩 생성)로 구분한다.
◆ 중복 연산을 줄이는 KV 캐시
KV(Key-Value) 캐시는 셀프 어텐션 연산 과정에서 동일한 입력 토큰에 대해 중복 계산이 발생하는 비효율을 줄이기 위해 먼저 계산했던 키와 값 결과를 메모리에 저장해 활용하는 방법을 말한다. 더 빠른 텍스트 생성을 위해 KV 캐시를 사용하면 추론을 수행할 때와 키와 값에 대한 계산 결과를 메모리에 저장하기 때문에 GPU 메모리는 모델이 차지하는 부분과 KV 캐시가 차지하는 부분으로 크게 나뉜다.
◆ GPU 구조와 최적의 배치 크기
[서빙이 효율적인지 판단하는 기준]
- 비용
- 처리량: 시간당 처리한 요청 수(querys/s)
- 지연 시간: 하나의 토큰을 생성하는 데 걸리는 시간 (token/s)
▶ 적은 비용으로 더 많은 요청을 처리하면서 생성한 다음 토큰을 빠르게 전달할 수 있다면 효율적인 서빙이라고 할 수 있다.
GPU를 더 효율적으로 활용하기 위해서는 최대 배치 크기가 최적의 배치 크기에 가까워질 수 있는 방법을 찾아야 하는데, 배치 크기를 키울 수 있는 방안은 모델의 용량을 줄이는 방법과 KV 캐시의 용량을 줄이는 방법으로 나눌 수 있다.
[KV 캐시를 줄이는 방법]
- 멀티 쿼리 어텐션: 여러 헤드와 쿼리 벡터가 하나의 키와 값 벡터를 사용하여 KV 캐시를 저장하는 데 훨씬 적은 메모리를 사용하나, 키와 값을 1개만 사용하면서 성능이 떨어지는 문제 발생.
- 그룹 쿼리 어텐션: 멀티 헤드 어텐션보다는 키와 값의 수를 줄이지만 멀티 쿼리 어텐션보다는 많은 키와 값을 사용하는 절충형 방식으로, 사용하는 키와 값 벡터의 수를 줄임으로써 성능 하락이 거의 없이도 모델의 추론 속도를 향상하고 KV 캐시의 메모리 사용량을 줄일 수 있음.
양자화로 모델 용량 줄이기
★ 양자화: 부동소수점 데이터를 더 적은 메모리를 사용하는 정수 형식으로 변환해 GPU를 효율적으로 사용하는 방법.
양자화는 양자화를 수행하는 시점에 따라 아래와 같이 나뉜다.
- 학습 후 양자화(PTQ, Post-Training Quantization)
- 양자화 학습(QAT, Quantization-Aware Training)
▶ LLM은 보통 학습에 많은 자원이 들기 때문에 새로운 학습이 필요한 양자화 학습보다는 학습 후 양자화를 주로 활용하며, 허깅페이스에서 가장 활발하게 활용되는 양자화의 형태는 다음의 세 가지가 있으며, 모두 학습 후 양자화 방식에 해당한다.
- 비츠앤바이츠 (bits-and-bytes): 워싱턴대학교의 팀 데트머스가 개발한 양자화 방식을 쉽게 사용할 수 있도록 제공하는 양자화 라이브러리. (8비트 행렬 연산 / 4비트 정규 분포 양자화 방식 제공)
- GPTQ (GPT Quantization): 양자화를 위한 작은 데이터셋을 준비하고 그 데이터셋을 활용해 모델 연산을 수행하면서 양자화 이전과 유사한 결과가 나오도록 모델을 업데이트하여 양자화 이전과 이후의 오차가 가장 작아지도록 하는 방식.
- AWQ (Activation-aware Weight Quantization): 모든 파라미터가 동등하게 중요하지는 않으며 특별히 중요한 파라미터의 정보를 유지하면 양자화를 수행하면서도 성능 저하를 막을 수 있다는 아이디어에서 출발했다.
[비츠앤바이츠 양자화 모델 불러오기]
# 비츠앤바이츠 양자화 모델 불러오기
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
# 8비트 양자화 모델 불러오기
bnb_config_8bit = BitsAndBytesConfig(load_in_8bit=True)
model_8bit = AutoModelForCausalLM.from_pretrained("facebook/opt-350m",
quantization_config=bnb_config_8bit)
# 4비트 양자화 모델 불러오기
bnb_config_4bit = BitsAndBytesConfig(load_in_4bit=True,
bnb_4bit_quant_type="nf4")
model_4bit = AutoModelForCausalLM.from_pretrained("facebook/opt-350m",
low_cpu_mem_usage=True,
quantization_config=bnb_config_4bit)
- 비츠앤바이츠가 지원하는 8비트 양자화와 4비트 양자화를 사용하려면 모델을 불러올 때 양자화 설정을 전달하면 된다.
- 8비트 양자화 수행 시: load_in_8bit 인자에 True 설정 후, AutoModelForCausalLM 클래스로 모델을 불러오면서 양자화 설정에 bnb_config_8bit 설정 전달
- 4비트 양자화 수행 시: load_in_4bit 인자에 True 설정 후, 양자화 데이터 형식으로 nf4 설정. 또한 모델을 불러오면서 양자화 설정에 bnb_config_4bit 설정 전달
[GPTQ 양자화 수행 코드]
허깅페이스 모델에 GPTQ 양자화를 적용하고 싶다면 아래와 같은 코드를 입력하면 된다.
# GPTQ 양자화 수행 코드
from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
model_id = "facebook/opt-125m"
tokenizer = AutoTokenizer.from_pretrained(model_id)
quantization_config = GPTQConfig(bits=4, dataset="c4", tokenizer=tokenizer)
model = AutoModelForCausalLM.from_pretrained(model_id,
device_map="auto",
quantization_config=quantization_config)
이 코드에서는 양자화 설정을 전달하기 위해 GPTQConfig 클래스에 비트 수(bits), 사용할 데이터셋(dataset), 토크나이저(tokenizer)를 설정했다.
양자화에 시간이 걸리기 때문에 (175B 모델을 양자화하는 데 엔비디아 A100 GPU로 4시간 정도가 소요된다고 한다)
허깅페이스 모델 허브를 탐색해 보고 이미 양자화된 모델이 있다면 아래와 같이 양자화된 모델을 불러와 사용하는 것이 좋다.
# GPTQ 양자화된 모델 불러오기
from transfomers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("TheBloke/zephyr-7B-beta-GPTQ",
device_map="auto",
trust_remote_code=False,
revision="main")
[AWQ 양자화 모델 불러오기]
AWQ는 모든 파라미터가 동등하게 중요하지는 않으며 특별히 중요한 파라미터의 정보를 유지하면 양자화를 수행하면서도 성능 저하를 막을 수 있다는 아이디어에서 출발했다고 했는데, 어떤 파라미터가 중요한지 판단할 수 있는 방법으로는 대표적으로 두 가지를 생각해 볼 수 있다.
- 모델 파라미터의 값이 큰 것: 모델 파라미터의 값이 크다면 연산 과정에서도 큰 영향이 있을 확률이 높다.
- 입력 데이터의 활성화 값이 큰 채널
▶ AWQ를 개발한 MIT 연구지은 모델 파라미터 자체와 활성화 값을 기준으로 상위 1%에 해당하는 모델 파라미터를 찾고 해당 파라미터는 기존 모델의 데이터 타입인 FP16으로 유지하고 나머지는 양자화하여 활성화 값을 기준으로 중요한 1% 파라미터의 정보만 지키면 모델의 성능이 유지된다는 사실을 발견했다.
AWQ는 모델의 활성화 값 분포를 통해 중요한 파라미터를 결정하고 양자화를 수행하기 때문에 양자화에 많은 시간이 걸리지 않고 기존 모델의 성능을 거의 유지할 수 있어 활발히 사용되고 있다.
이미 양자화된 모델을 불러올 때는 AWQ 논문의 저자가 작성한 라이브러리인 llm-awq나 autoawq를 사용할 수 있다.
# AWQ 양자화 모델 불러오기
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name_or_path = "TheBloke/zephyr-7B-beta-AWQ"
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, trust_remote_code=False)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path,
fuse_layers=True,
trust_remote_code=False,
safetensors=True)
지식 증류 활용하기
★ 지식 증류(knowledge distrillation)
지식 증류란, 딥러닝 분야에서 더 크고 성능이 높은 선생 모델의 생성 결과를 활용해 더 작고 성능이 낮은 학생 모델을 만드는 방법을 말한다. 모델의 크기를 줄이면서 중요한 부분만 남기는 방식으로, 전이 학습(transfer learning)과 비슷하지만 사이즈를 줄이는 것이 목적이다.
▶ 학생 모델의 크기가 선생 모델에 비해 작기 때문에 선생 모델에 쌓은 지식을 더 작은 모델로 압축해 전달한다는 의미에서 '증류'라고 부른다.
지식 증류의 예시로는 선생님이 학생에게 역사 지식을 가르쳐주는 것을 들 수 있다. 지식 증류를 통해 프로젝트를 수행하면서 지식이 쌓여가지만 중요한 결정은 지식이 없는 초반에 많이 필요로 할 때 사용할 수 있다.
[지식 증류의 방법]
- Feature Distillation: 지식 증류 과정 중에 정말 잘하고 있는지 확인하기 위해 착안된 방법.
- Data-free Knowledge Distillation: 학생이 학습하기 위해 선생의 학습 데이터가 아니더라도 지식 증류를 가능하게 하는 방법.
지식 증류를 이해하는데 중요한 두 가지 키워드는 Teacher Model(원본 모델)과 Student Model(경량화된 모델)이다.
지식 증류는 최근 선생 모델을 활용해 완전히 새로운 학습 데이터셋을 대규모로 구축하거나 데이터셋 구축에 사람의 판단이 필요한 부분을 선생 모델이 수행하는 등 더 폭넓게 활용되고 있다.
다음 내용
[출처]
LLM을 활용한 실전 AI 애플리케이션 만들기
https://huggingface.co/docs/transformers/ko/quantization/awq
https://velog.io/@synoti21/ML-Model-Serving-Pipeline
https://github.com/huggingface/transformers/blob/main/docs/source/ko/quantization/awq.md
'[파이썬 Projects] > <파이썬 Gen AI, LLM>' 카테고리의 다른 글
[LLM] sLLM 학습하기 (0) | 2024.12.30 |
---|---|
[LLM] GPU 효율적인 학습 (1) | 2024.12.30 |
[LLM] 텍스트 분류 모델 학습시키기 (2) | 2024.12.27 |
[LLM] 허깅페이스 라이브러리 사용법 익히기 (4) | 2024.12.27 |
[Gen AI] 스테이블 디퓨전 API (이 아닌 stability.ai API 사용법) (3) | 2024.12.22 |