23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
지난 포스팅에서 No-Code 파인튜닝 툴인 LLaMA-Factory를 소개해 드렸습니다.
모델을 파인튜닝하려면 GPU 및 학습 환경 설정뿐만 아니라 Python 언어와 Huggingface의 Transformers와 같은 라이브러리에 익숙해져야 합니다.
물론 데이터 전처리는 더 중요합니다!
No-Code 파인튜닝 툴은 이러한 준비 과정을 단순화해 줍니다.
구체적으로는 다양한 오픈 소스 LLM 모델에 대한 기본 설정 및 파인튜닝에 필요한 파라미터들을 템플릿 형태로 관리하여 좀 더 손쉽고 체계적으로 파인튜닝을 시작할 수 있습니다.
이번 포스팅에서는 이러한 파인튜닝 툴 중 가장 많이 사용된다고 생각되는 axolotl를 소개하려고 합니다.
가장 많이 사용된다고 하면서 왜 이전 포스팅에서 LLaMA-Factory를 먼저 소개했냐고 물으신다면, LLaMA-Factory가 전체적으로 사용과 구성 방법이 더 간단하다고 느꼈기 때문입니다.
사실 이러한 툴들의 구성이나 사용 방법은 비슷하기 때문에 하나의 툴에 익숙해지면 다른 툴도 그리 어렵지 않게 사용할 수 있을 것 입니다.
만일 이 글을 처음 읽으신다면 LLaMA-Factory 포스팅을 먼저 확인하는 것을 추천 드립니다.
본 글에서는 다음과 같은 주제를 다룹니다.
Custom 데이터셋 만들기 두 번째 : 멀티턴 데이터셋 생성 및 데이터셋 포맷 이해
Axolotl 설치 및 환경 설정
Axolotl을 이용한 학습 구성 및 실행
Axolotl CLI 인터페이스를 통한 추론 방법
학습된 LoRA를 base 모델에 통합하기
No-Code 파인튜닝 솔루션의 장단점 비교
사용된 데이터셋과 환경 설정 파일, Jupyter notebook 파일은 아래 github에서 다운로드 할 수 있습니다.
이전 LLaMA-Factory 포스팅에서 GPT-4를 활용해 커스텀 데이터셋을 만드는 방법에 대해 알아봤습니다.
GPT-4와 같은 강력한 모델을 통해 학습 데이터셋을 만드는 기법을 데이터 증강(Data Augmentation)이라고 하며, 현재는 보편적으로 사용하는 방식입니다.
이전에 살펴봤던 Alpaca 형식의 대화 데이터셋도 이러한 방식으로 만들어졌습니다.
그런데 이러한 데이터 증강 방식의 단점은 무엇일까요?
학습 데이터셋을 생산하는 모델의 편향성, 노이즈, 왜곡 등 다양한 문제점이 있을 수 있겠지만, 제일 큰 문제는 비용입니다.
때로는 파인튜닝 모델을 학습하기 위해 수만 개에서 수십만 개 이상의 데이터셋을 생성해야 하는데, 이 과정에서 소비되는 비용이 결코 저렴하지 않습니다.
그래서 최근에는 오픈소스 모델을 통해 데이터셋을 만들려는 노력들이 이어지고 있는데, 그 중 하나를 활용해 보려고 합니다.
지난번에 우리는 Alpaca 형식의 데이터셋을 만들어봤는데, 이 데이터셋은 "질문"과 "답변" 형식으로 이루어져 있어 하나의 싱글턴 대화 형식이 될 수 있습니다.
이번에는 대화가 계속 이어지도록 멀티턴 대화셋을 만들어 보겠습니다.
아래는 Maywell님이 한국어 멀티턴 데이터셋 KoVast를 만들 때 사용했던 KoMultiGen-General이라는 모델입니다.
이 모델이 재미 있는 것은, 아래와 같은 프롬프트를 주면 3~5 턴의 Q/A 대화를 생성해 내게 됩니다.
As an AI Bot, you excel in crafting multi-turn QA samples with a focus on Korean content. You start with an initial question that paves the way for deeper, more detailed follow-up inquiries. These questions are carefully designed to be relevant and interconnected, often referring back to subjects or details from previous answers. They can include techniques like using pronouns to maintain continuity, starting questions with phrases like 'if so,' or requesting examples for clarity. Your answers are expected to be rooted in thorough logical analysis. The dialogue can unfold over 3 to 5 exchanges. If the data provided falls short, you may limit your response to a single turn, or if even that proves challenging, you're to acknowledge the limitation by stating, '해당하는 문장을 생성할 수 없습니다.'
### Instruction:
### Start of provided data
{prompt}
### End of provided data
주어진 데이터로 3~5 turn의 QA를 생성해라.
인용표현을 사용하지 말아야한다.
Question의 말투는 '~는 ~야?' 같은 반말 어휘를 사용하며, Answer는 '~입니다.' 어휘를 사용한다.
### Response:하지만 이 모델은 Synatra-Mixtral 모델 기반으로 만들어져서, 모델 크기가 좀 큽니다. 그래서 Ja-ck 님이 LLaMA-3-8B 모델로 지식 증류를 통해 만든 경량화된 모델을 사용하겠습니다.
데이터셋은 지난번 포스팅과 동일하게 공무원 복무 제도에 대한 내용이지만, 이번에는 멀티턴 데이터셋을 생성해 봅시다.
https://www.mpm.go.kr/mpm/info/infoService/serviceIntro/
위의 예시로 든 프롬프트 템플릿에 아래의 내용을 넣고 모델에 추론을 시켜봤습니다.
공무원 연가 제도란
- 정신적·신체적 휴식을 취함으로써 근무능률을 유지하고 개인 생활의 편의를 위하여 사용하는 휴가
- 미사용시 연가보상비 지급으로 갈음(최대 20일 한도)
- 행정기관의 장은 매년 3월 31일까지 그해에 최소한으로 사용하여야 할 권장 연가일수를 10일 이상 정하여 공지하여야 함모델이 생성한 내용은 다음과 같습니다.
[[Question]]
공무원 연가 제도가 뭐야?
[[Answer]]
공무원 연가 제도는 정신적·신체적 휴식을 취함으로써 근무능률을 유지하고 개인 생활의 편의를 위해 사용하는 휴가입니다.
[[Question]]
그럼 연가 사용하지 않으면 어떻게 되는 거야?
[[Answer]]
미사용시 연가 보상비 지급으로 갈음이 됩니다. 또한, 최대 20일 한도까지 사용해야 합니다.
[[Question]]
연가 사용 권장 일수는 언제까지야?
[[Answer]]
행정기관의 장은 매년 3월 31일까지 그해에 최소한으로 사용하여야 할 권장 연가일수를 10일 이상 정하여 공지해야 합니다.해당 내용에 대해 제법 쓸만한 멀티턴 데이터를 생성하네요!
KoMultiGen-General 모델은 멀티턴 데이터셋 생성에 유용하지만, 추론을 실행 했을 때 서술형 데이터의 결과들이 좀 더 좋았습니다.
공무원 복무규정의 글들은 목록 형식의 요약글과 특히 표로 구성되어 있는 문서들이 많은데, 이 부분은 적절한 전처리를 통해 서술 형태로 변경해서 추론을 수행 했습니다.
이렇게 멀티턴 데이터셋을 생성했지만, 한가지 할 일이 더 남았습니다.
이전에도 확인했듯이 모델에 입력으로 들어가는 학습 데이터셋은 일반적으로 Json 형식이어야 합니다.
이전 포스팅에서는 alpaca 형식의 싱글턴 대화 데이터셋을 만들었습니다. 이번에는 멀티턴 대화형식의 데이터셋을 만들어봤는데요.
이쯤해서 LLM의 파인튜닝 데이터셋 포맷으로 많이 쓰이는 3가지 형태를 한번 정리하고 가는 것이 좋을 것 같습니다.
Alpaca 포맷은 일반적은 Q/A 데이터셋이나 특정한 지시사항에 대해 선택적으로 입력 데이터를 제공해야 할 때 활용하기 좋습니다.
instruction : 모델에게 무엇을 해야 하는지 명확하게 지시하는 부분
input : 지시사항을 수행하기 위해 필요한 추가 정보가 있다면 여기에 입력. 꼭 필요한 것은 아님
output : 모델이 지시사항과 입력을 바탕으로 생성해야 할 결과
[ 예시 ]
{
"instruction": "연가란 무엇입니까?",
"input": "",
"output": "정신적·신체적 휴식을 취함으로써 근무능률을 유지하고 개인 생활의 편의를 위하여 사용하는 휴가입니다."
}ShareGPT는 인간과 모델(GPT)간의 대화 기록을 토대로 만들어진 포멧입니다. 일반적으로 대화 형식의 데이터셋으로 많이 사용됩니다.
conversations : 대화의 각 단계가 배열로 저장
from : 메시지를 보낸 주체를 나타냄. "human"이면 인간, "gpt"이면 GPT 모델
value : 대화 내용
1.1 멀티턴 대화셋 만들기에서 KoMultiGen-General 모델이 만든 내용을 ShareGPT 포멧으로 변환 한다면 아래와 같이 됩니다.
[ 예시 ]
{"conversations": [
{"from": "human", "value": "공무원 연가 제도가 뭐야?"},
{"from": "gpt", "value": "공무원 연가 제도는 정신적·신체적 휴식을 취함으로써 근무능률을 유지하고 개인 생활의 편의를 위해 사용하는 휴가입니다."},
{"from": "human", "value": "그럼 연가 사용하지 않으면 어떻게 되는 거야?"},
{"from": "gpt", "value": "미사용시 연가 보상비 지급으로 갈음이 됩니다. 또한, 최대 20일 한도까지 사용해야 합니다."}
]
}Completion 포맷은 비구조화된 텍스트를 통해 학습할 때 사용되며, 별도의 지시사항이나 대화 문맥 없이 다양한 텍스트 데이터를 학습하는 데 적합합니다.
text : 모델이 학습해야 할 원본 텍스트 데이터
[ 예시 ]
{
"text": "행정기관의 장은 매년 3월 31일까지 그해에 최소한으로 사용하여야 할 권장 연가일수를 10일 이상 정하여 공지하여야 함"
}이밖에 사용자 정의 포멧을 만들어 사용하기도 하지만 위에서 언급한 3가지 포맷이 가장 대중적으로 사용하는 형태입니다.
공무원 복무제도에 대해 모델이 생성한 멀티턴 샘플 데이터셋은 ShareGPT 포맷으로 변경하여 이번 실습의 데이터셋 으로 사용하겠습니다.
Axolotl은 OpenAccess AI Collective 라는 오픈소스 커뮤니티 그룹에서 시작된 프로젝트입니다.
그룹의 로고가 재미 있는데요 tool의 이름인 axolotl 에서 알 수 있듯이 우파루파라고도 불리는 귀여운 도룡뇽입니다.
앞서 살펴본 LLaMa-Factory와 마찬가지로 다양한 AI 모델의 파인튜닝을 쉽게 할 수 있도록 지원하는 솔루션을 표방하고 있습니다.
주요 특징은 다음과 같은데요
llama, gemma, mistral, mpt 등 다양한 Huggingface 모델의 학습 지원
fullfinetune, lora, qlora, relora 및 gptq 지원
yaml 기반의 간편한 설정 파일
유연한 데이터셋 관리
Xformer, Flash Attention, Rope Scaling 및 Multipacking과의 통합
FSDP 또는 Deepspeed를 통해 단일 GPU 또는 다중 GPU와 작동
Docker를 로컬 또는 클라우드에서 쉽게 실행
GUI 인터페이스를 제공하지 않는 다는 점을 제외하면 LLaMA-Factory 와 상당히 유사한 지원 범위와 사용자 인터페이스를 가지고 있습니다.
특히 LLaMA-Factory에서는 unsloth 라이브러리를 통해 xformer attention을 지원 했는데, Axolotl은 이 기능을 자체로 제공하고 있는 점도 흥미롭습니다.
Axolotl도 호스트에 직접 인스톨하는 방법, Container를 사용하는 방법과 더불어 Cloud GPU를 사용할 때의 안내도 비교적 자세히 설명 되어 있습니다.
이번에도 LLaMA-Factory와 동일하게 Container 환경에서 사용하는 방법을 설명 드리겠습니다.
Axolotl은 호스트에 직접 설치하거나 Docker 컨테이너를 구동하여 사용할 수 있습니다.
호스트에 직접 설치 시 각종 라이브러리의 호환성 체크를 고려하면, 컨테이너 방식이 가장 간편하며 권장 드리는 방식입니다.
이 후 설명은 Docker 컨테이너를 통해 사용하는 것으로 설명하겠습니다.
학습 설정 파일과 학습 결과를 호스트에 저장하기 위해 Container와 마운트할 디렉토리를 axolotl 디렉토리 하위에 생성합니다.
$ mkdir /home/user/axolotl/data
$ mkdir /home/user/axolotl/outputs
$ mkdir /home/user/axolotl/config우리가 이전에 만들어 두었던 멀티턴 데이터셋을 생성한 데이터 디렉토리에 복사 합니다.
$ cp ko_civil_service.multichat.json /home/user/axolot/data가장 간단하게 사용하려면 axolotl의 최신 이미지를 사용하는 것입니다.
하지만 추가 설정이나 특정 버전의 라이브러리가 필요하다면 직접 이미지를 빌드하여 사용할 수 있습니다.
아래 Dockerfile을 참조해 이미지를 빌드 합니다. Dockfile에서 활용한 Base image는 아래 docker hub에서 확인할 수 있습니다.
ARG BASE_TAG=main-base-py3.10-cu118-2.1.2
FROM winglian/axolotl-base:$BASE_TAG
ARG TORCH_CUDA_ARCH_LIST="7.0 7.5 8.0 8.6+PTX"
ARG AXOLOTL_EXTRAS=""
ARG AXOLOTL_ARGS=""
ARG CUDA="118"
ENV BNB_CUDA_VERSION=$CUDA
ARG PYTORCH_VERSION="2.1.2"
ENV PYTORCH_VERSION=$PYTORCH_VERSION
RUN apt-get update && \
apt-get install -y --allow-change-held-packages vim curl nano libnccl2 libnccl-dev rsync s3fs
WORKDIR /workspace
RUN git clone --depth=1 https://github.com/OpenAccess-AI-Collective/axolotl.git
WORKDIR /workspace/axolotl
# If AXOLOTL_EXTRAS is set, append it in brackets
RUN pip install causal_conv1d
RUN if [ "$AXOLOTL_EXTRAS" != "" ] ; then \
pip install -e .[deepspeed,flash-attn,mamba-ssm,galore,$AXOLOTL_EXTRAS] $AXOLOTL_ARGS; \
else \
pip install -e .[deepspeed,flash-attn,mamba-ssm,galore] $AXOLOTL_ARGS; \
fi
# So we can test the Docker image
RUN pip install pytest
# fix so that git fetch/pull from remote works
RUN git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" && \
git config --get remote.origin.fetch
# helper for huggingface-login cli
RUN git config --global credential.helper storeAxolotl을 컨테이너로 사용하기 위한 준비 작업이 되었습니다. Docker 이미지 빌드를 진행합니다.
$ docker build --no-cache -f ./Dockerfile -t axolotl:py3.10-cu118-2.1.2 .아래와 같은 명령으로 컨테이너를 실행합니다.
docker run --gpus=all \
--rm -it \
-v ~/.hf_cache:/root/.cache/huggingface/ \
-v /home/user/axolotl/mount/data:/workspace/axolotl/data \
-v /home/user/axolotl/mount/outputs:/workspace/axolotl/outputs \
-v /home/user/axolotl/mount/config:/workspace/axolotl/config \
--shm-size 16G \
--name axolotl \
axolotl:py3.10-cu118-2.1.2hf_cache: 호스트의 huggingface model 및 dataset cache 디렉토리 마운트 (Linux의 경우 보통 home 디렉토리에 ./hf_cache 에 있음)
data : 호스트와 데이터셋을 공유할 디렉토리를 마운트
output: 호스트와 학습된 모델을 공유할 디렉토리를 마운트
config: 호스토와 학습 설정 파일을 공유할 디렉토리를 마운트
컨테이너가 잘 실행 되었다면 컨테이너 내로 진입이 되었을 것입니다.
컨테이너내에 호스트 디렉토리들이 잘 마운트 되어 있는지 확인 합니다.
# ls data
ko_civil_service-multiturn.json
# ls config
llama-3-8b-Instruct-bnb-4bit-qlora.yaml우리는 앞 1.2.2에서 KoMultiGen-General 모델이 생성한 대화 형식의 데이터를 모델 학습용 데이터셋으로 사용하기 위해 Json 형식의 ShareGPT 포맷으로 변환했습니다.
ShareGPT 포맷은 대화 형식의 데이터셋인데요. 즉 대화 형식의 데이터셋를 통해 우리 모델이 대화를 인식할 수 있도록 파인튜닝하는 것이 목적입니다.
하지만 모델이 이러한 대화 형식을 이해하려면 사전에 정의된 chat 형식의 프롬프트를 사용해야 합니다.
meta-llama/Meta-Llama-3-8B-Instruct 모델의 chat 템플릿을 확인하기 위해 모델 카드에서 제공하는 tokenizer_config.json을 살펴보겠습니다.
huggingface -> 모델 검색 -> meta-llama/Meta-Llama-3-8B-Instruct -> Files and versions
"bos_token": "<|begin_of_text|>",
"chat_template": "{% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{% if add_generation_prompt %}{{ '<|start_header_id|>assistant<|end_header_id|>\n\n' }}{% endif %}",
"clean_up_tokenization_spaces": true,
"eos_token": "<|eot_id|>",기존 llama2와는 다른 chat 템플릿이네요.
어쨌든 이 형식으로 Llama-3-8B-Instruct가 학습되었기 때문에, 우리가 파인튜닝하려면 동일한 형식으로 맞춰줘야 할 것 같습니다.
Gemma 파인튜닝 포스팅을 상기해보면, chat 템플릿 형태로 변환해주는 프롬프트 함수를 작성해서 모델에 입력했었는데요. Axolotl은 이러한 과정을 좀 더 쉽게 할 수 있습니다.
이후 설명할 "3.1 학습 환경 구성"의 데이터셋 및 로딩 섹션에서 모델에 맞는 chat 템플릿을 지정하면 자동으로 변환되어 입력됩니다.
이제 우리가 만든 데이터셋 형식이 맞는지, llama3의 chat 템플릿 형식으로 제대로 변환이 되는지 검증을 해보겠습니다.
이 과정은 아래 "3.1 학습 환경 구성" 을 완료 한 후 실행 해야지만 글의 순서상 여기서 먼저 말씀 드리겠습니다.
Axolotl은 데이터 전처리를 위한 다음과 같은 단계별 프로세싱을 지원합니다.
데이터 셋의 구문 분석
프롬프트 템플릿을 적용하여 데이터셋 변화
모델의 토큰나이저를 사용해서 변환된 데이터셋 토큰화
둘 이상의 데이터셋이 사용되는 경우 여러 데이터셋을 섞고 병합
이렇게 전처리를 수행하면 데이터셋의 검증 뿐만 아니라 변환된 데이터셋을 캐싱할 수 있으므로 반복 학습시 빠르게 데이터셋을 로딩할 수 있다는 장점이 있습니다.
python -m axolotl.cli.preprocess ./config/llama-3-8b-Instruct-bnb-4bit-qlora.yaml --debug명령을 수행하면 다음과 같이 전처리를 진행하게 됩니다.
llama3의 chat 템플릿 적용 및 토큰 변환이 잘 된 것을 확인할 수 있습니다!
한 가지 특이한 점은 변환된 데이터셋이 빨간색과 초록색으로 구분되어 있다는 것입니다.
빨간색에 해당하는 토큰들은 프롬프트이고, 녹색은 답변, 즉 레이블입니다.
빨간색 토큰에는 모두 -100이 부여되었는데, 이는 패딩 토큰에 대해 Loss 계산을 하지 않기 위해 사용하는 방법과 동일합니다.
모델이 학습되는 과정을 생각해보면, 모델의 출력과 정답 간의 차이를 통해 Loss를 계산합니다.
프롬프트에 해당하는 본문은 예측 대상이 아니므로 손실 계산에서 제외되는 것입니다.
만약 입력 데이터 전체를 모두 학습하고 싶다면, 이후 학습 설정에서 train_on_inputs를 true로 설정하면 마스킹된 빨간색 프롬프트들이 초록색으로 바뀌는 것을 확인할 수 있습니다.
Axolotl에서 지원하는 다양항 모델들의 예제들은 다음 링크에서 확인 할 수 있습니다.
컨테이너 내 마운트된 config 디렉토리로 이동하여 아래와 같이 학습 설정 파일을 작성 합니다.
llama-3-8b-Instruct-bnb-4bit-qlora.yaml
base_model: meta-llama/Meta-Llama-3-8B-Instruct
model_type: LlamaForCausalLM
tokenizer_type: AutoTokenizer
load_in_8bit: false
load_in_4bit: true
strict: false
chat_template: llama3
datasets:
- path: data/ko_civil_service-multiturn.json
type: sharegpt
chat_template: llama3
dataset_prepared_path:
output_dir: ./outputs/llama-3-8b-instruct-bnb-4bit-qlora/multiturn
sequence_len: 4096
sample_packing: false
pad_to_sequence_len: true
adapter: qlora
lora_model_dir:
lora_r: 8
lora_alpha: 16
lora_dropout: 0.05
lora_target_linear: true
lora_fan_in_fan_out:
#wandb_project:
#wandb_entity:
#wandb_watch:
#wandb_name:
#wandb_log_model:
gradient_accumulation_steps: 2
micro_batch_size: 1
num_epochs: 5
optimizer: adamw_bnb_8bit
lr_scheduler: cosine
learning_rate: 0.0002
train_on_inputs: false
group_by_length: false
bf16: auto
fp16:
tf32: false
gradient_checkpointing: true
gradient_checkpointing_kwargs:
use_reentrant: true
early_stopping_patience:
resume_from_checkpoint:
local_rank:
logging_steps: 1
xformers_attention:
flash_attention: true
warmup_steps: 5
evals_per_epoch: 1
eval_table_size:
saves_per_epoch: 1
debug:
deepspeed:
weight_decay: 0.0
fsdp:
fsdp_config:
special_tokens:
pad_token: <|end_of_text|>
eos_token: <|eot_id|>Axolotl은 LLaMA-Factory와 마찬가지로 Yaml 형태의 설정 파일을 작성합니다.
설정 내용을 기능별로 섹션 분리하면 좀 더 가독성 높게 설정 파일을 관리 할 수 있습니다.
아래는 설정 내용에 대한 추가 설명입니다.
Model 구성 섹션
모델의 유형에 대해 아래 3가지 설정으로 구성됩니다.
base_model
model_type
tokenizer_type
Method 양자화 섹션
Lora 또는 Qlora 를 통해 파인튜닝을 할지 여부입니다.
strict: false로 설정하면 어댑터에 누락된 기본 가중치가 선택됨
데이터 로딩 및 경로 섹션
데이터셋의 위치와 데이터셋을 어떻게 나눌지(Train/Val) 그리고 데이터의 포멧과 템플릿을 정의합니다.
datasets:
type : 데이터셋의 포멧 (alpaca, sharegpt, etc)
chat_template: chatml 프롬프트 형식을 설정
Sequence 구성 섹션
입력 데이터 형식에 대해 정의합니다.
sequence_len : 입력의 최대 길이 설정 (모델의 허용 sequnce length 확인)
sample_packing: 짧은 시퀀스를 최대 시퀀스 길이 만큼 함께 묶어서 입력
pad_to_sequence_len: 모든 시퀀스가 같은 길이를 갖도록 Padding 추가
Lora 설정 섹션
Lora 학습 설정을 합니다.
lora_r: lora low-rank 크기. 값이 높을 수록 더 세밀한 튜닝 가능하지만 파라메터가 늘어남
lora_alpha: 스케일링 펙터 조정. 원래 모델의 가중치가 기여하는 정도. 값이 높으면 Lora의 영향이 커짐
lora_dropout: 과적합 방지를 위한 정규화
lora_target_modules : Lora을 적용할 모델 아키텍처의 모듈 지정
lora_target_linear: Ture이면 모든 선형 모델에 적용
ora_fan_in_fan_out: 기본 false
학습 설정 섹션
학습에 대한 설정
데이터 및 정밀도 섹션
train_on_inputs: 학습 레이블에 프롬프트를 포함할지 아니면 마스킹 할지 결정, False면 프롬프트가 학습레이블에서 제외
group_by_length: 학습 중 패딩을 최소화하기 위해 비슷한 크기의 데이터를 그룹화 할지 여부
최적화 섹션
xformers_attention: Transformer 에 최적화된 attention 연산 수행, 학습 속도 증가 및 메모리 사용 감소
flash_attention: 학습 속도 향상
기타 파라메터 섹션
deepspeed: Deepspeed 라이브러리 사용
fsdp_config: FSDP의 동작에 대한 설정
special_tokens: 스페셜 토큰에 대한 설정 (필요한 경우 명시적으로 지정하는 것이 좋음)
pad_token: <|end_of_text|>
eos_token: <|eot_id|>
resize_token_embeddings_to_32x:토큰 임베딩의 크기를 조정하여 다양한 어휘 크기에 효율적으로 적용
자, 이제 dataset과 학습 설정이 완료 되었으니 axolotl을 통해 학습을 진행할 수 있습니다.
컨테이너 내에서 아래와 같은 명령을 통해 학습을 시작 합니다.
# accelerate launch -m axolotl.cli.train ./config/llama-3-8b-Instruct-bnb-4bit-qlora.yaml학습 과정에 나오는 로그들은 주의 깊게 볼 필요성이 있습니다.
첫번째로 accelerate 설정에 관련된 내용이 나옵니다. 이번 학습에는 GPU 1개만을 사용했기 때문에 process와 machine 개수가 1개씩으로 나오고 있습니다.
만일 Multi GPU로 분산 학습을 진행해야 한다면 Deepspeed 설정을 통해 실행할 수 있습니다. 그렇다면 아래에 설정된 수 만큼의 프로세스가 보이겠죠.
The following values were not passed to `accelerate launch` and had defaults used instead:
`--num_processes` was set to a value of `1`
`--num_machines` was set to a value of `1`
`--mixed_precision` was set to a value of `'no'`
`--dynamo_backend` was set to a value of `'no'`그 다음으로 확인해야할 내용은 관련 라이브러리들이 설치된 버전으로 제대로 인식되고 있는지 입니다.
qlora를 사용할 것이므로 bitsandbytes는 당연히 올라와야겠죠?
****************************************
**** Axolotl Dependency Versions *****
accelerate: 0.30.1
peft: 0.11.1
transformers: 4.41.1
trl: 0.8.7.dev0
torch: 2.1.2+cu118
bitsandbytes: 0.43.1
****************************************다음으로 토큰나이저에서 인식하는 Special Token을 살펴봅시다.
[2024-06-24 08:23:25,144] [DEBUG] [axolotl.load_tokenizer:280] [PID:8896] [RANK:0] EOS: 128009 / <|eot_id|>
[2024-06-24 08:23:25,144] [DEBUG] [axolotl.load_tokenizer:281] [PID:8896] [RANK:0] BOS: 128000 / <|begin_of_text|>
[2024-06-24 08:23:25,144] [DEBUG] [axolotl.load_tokenizer:282] [PID:8896] [RANK:0] PAD: 128001 / <|end_of_text|>이후에는 Dataset load와 llama3의 chat template을 적용한 토큰화 과정이 나오는데, 이 부분은 데이터 전처리 과정에서 살펴봤으니 설명을 생락합니다.
이제 4bit로 양자화한 llama3 모델을 GPU 메모리로 로드하고 qlora로 학습할 준비를 시작 합니다.
설정에서 bf16을 auto로 두었으므로 GPU가 Ampere 아키텍처를 지원한다면 bf16을 사용하고 더불어 flash attention도 사용하는 것을 확인 할 수 있습니다.
qlora 설정에 의해 실제 학습되는 파라메터 수가 0.26% 약 2천만개 정도 되네요.
학습될 Lora Adapter를 GPU 메모리에 올려도 5.35GB 밖에 차지하지 않는 것을 확인 할 수있습니다.
[2024-06-24 08:23:32,910] [INFO] [axolotl.load_model:734] [PID:8896] [RANK:0] GPU memory usage after model load: 5.311GB (+0.087GB cache, +1.336GB misc)
[2024-06-24 08:23:32,931] [INFO] [axolotl.load_model:785] [PID:8896] [RANK:0] converting PEFT model w/ prepare_model_for_kbit_training
[2024-06-24 08:23:32,935] [INFO] [axolotl.load_model:794] [PID:8896] [RANK:0] converting modules to torch.bfloat16 for flash attention
[2024-06-24 08:23:32,939] [INFO] [axolotl.load_lora:951] [PID:8896] [RANK:0] found linear modules: ['up_proj', 'o_proj', 'down_proj', 'q_proj', 'k_proj', 'gate_proj', 'v_proj']
trainable params: 20,971,520 || all params: 8,051,232,768 || trainable%: 0.2605
[2024-06-24 08:23:33,519] [INFO] [axolotl.load_model:843] [PID:8896] [RANK:0] GPU memory usage after adapters: 5.350GB (+4.001GB cache, +1.336GB misc)
... 중략이제 실제 학습을 시작 하게 됩니다.
{'loss': 1.037, 'grad_norm': 2.671875, 'learning_rate': 4e-05, 'epoch': 0.05}
1%|█▏ | 1/110 [00:03<05:35, 3.08s/it]
{'loss': 1.8728, 'grad_norm': 1.6640625, 'learning_rate': 8e-05, 'epoch': 0.09}
... 중략
{'loss': 0.1255, 'grad_norm': 1.1171875, 'learning_rate': 1.7898702322648453e-07, 'epoch': 4.91}
{'loss': 0.0799, 'grad_norm': 1.0, 'learning_rate': 4.475677164966774e-08, 'epoch': 4.95}
100%|██████████████████████████| 110/110 [05:28<00:00, 2.87s/it]
Axolotl 은 추론도 CLI 인터페이스를 통해 간편하게 수행할 수 있습니다만, LLaMA-Factory와 비교하면 매우 불편합니다.
첫번째로 prompt tempate 적용이 되지 않아 직접 prompt를 입력해야 하는데 멀티턴 형식의 chat template 같은 경우는 매우 번거로워집니다.
두번째는 Inference 최적화가 안되어 출력이 느립니다.
때문에 Axolot 추론은 모델 학습 후 빠르게 테스트 해보는 용도로 사용하고, 다양한 추론 테스트시에는 vLLM 과 같은 별도의 Inference 라이브러리를 사용하는 것을 추천합니다.
아래의 명령으로 Axolot 추론을 실행 합니다.
# python -m axolotl.cli.inference ./config/llama-3-8b-Instruct-bnb-4bit-lora-ko.yaml \
--lora_model_dir="./outputs/llama-3-8b-Instruct-multiturn-bnb-4bit-qlora/multiturn" \
--load_in_4bit=True추론이 실행되면 아래와 같이 instruction을 입력하라는 메시지가 나오는데, 여기에 입력 프롬프트를 넣은 후 Ctrl +D 를 눌러 제출 하게 됩니다.
아래와 같이 llama3 instruction 모델의 chat template 형태로 입력을 넣어줍니다.
================================================================================
Give me an instruction (Ctrl + D to submit):
<|begin_of_text|><|begin_of_text|><|begin_of_text|><|start_header_id|>system<|end_header_id|>
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
공무원의 1주간 근무 시간은 얼마나 되는 거야?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>아래와 같이 출력이 나오는데, Axolotl 추론은 Instruction 형태의 싱글턴 기준이기 때문에 우리의 멀티턴 테스트를 하려면 이전 대화를 다시 프롬프트에 넣어줘야 합니다.
공무원의 1주간 근무 시간은 하절기와 동절기 구분 없이 점심시간을 제외한 주 40시간입니다.<|eot_id|>
================================================================================
Give me an instruction (Ctrl + D to submit):자, 출력된 내용에 이전 대화를 chat template 형식으로 다시 넣어 줍니다.
<|begin_of_text|><|begin_of_text|><|begin_of_text|><|start_header_id|>system<|end_header_id|>
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
공무원의 1주간 근무 시간은 얼마나 되는 거야?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>
공무원의 1주간 근무 시간은 하절기와 동절기 구분 없이 점심시간을 제외한 주 40시간입니다.<|eot_id|><|start_header_id|>user<|end_header_id|>
그럼 점심 시간은 언제까지야?
<|eot_id|><|start_header_id|>assistant<|end_header_id|>아래와 같이 멀티턴 출력이 나오게 됩니다.
점심 시간은 12시부터 13시까지로 되어 있으며, 이 시간은 근무 시간에 포함되지 않습니다.<|eot_id|>
================================================================================
Give me an instruction (Ctrl + D to submit):추론 과정이 다소 번거로웠는데, Axolotl은 gradio를 이용하여 GUI 추론 인터페이스 제공합니다.
(하지만 역시나 instruction 베이스라서 chat 형식의 대화는 어렵습니다.)
아래와 같이 --gradio 옵션을 주면 외부의 gradio 인터페이스와 연결 해줍니다.
python -m axolotl.cli.inference ./config/llama-3-8b-Instruct-bnb-4bit-lora-ko.yaml \
--lora_model_dir="./outputs/llama-3-8b-Instruct-multiturn-bnb-4bit-qlora/multiturn" \
--load_in_4bit=True --gradio학습된 Lora adapter를 base 모델과 합쳐 저장 하는 과정은 한줄의 명령으로 가능합니다.
아래 명령을 통해 Lora 를 병합합니다. 병합된 모델은 Lora가 저장된 디렉토리 하위에 merged 디렉토리에 저장됩니다.
# python -m axolotl.cli.merge_lora ./config/llama-3-8b-Instruct-bnb-4bit-qlora.yaml --lora_model_dir="./outputs/llama-3-8b-Instruct-multiturn-bnb-4bit-qlora/multiturn"저장된 디렉토리를 확인해보면 병합된 모델을 확인할 수 있습니다.
# ls -alh outputs/llama-3-8b-Instruct-multiturn-bnb-4bit-qlora/multiturn/merged/
total 15G
drwxr-xr-x 2 root root 333 Jun 25 09:48 .
drwxr-xr-x 7 root root 275 Jun 25 09:48 ..
-rw-r--r-- 1 root root 729 Jun 25 09:48 config.json
-rw-r--r-- 1 root root 194 Jun 25 09:48 generation_config.json
-rw-r--r-- 1 root root 4.7G Jun 25 09:48 pytorch_model-00001-of-00004.bin
-rw-r--r-- 1 root root 4.7G Jun 25 09:48 pytorch_model-00002-of-00004.bin
-rw-r--r-- 1 root root 4.6G Jun 25 09:48 pytorch_model-00003-of-00004.bin
-rw-r--r-- 1 root root 1.1G Jun 25 09:48 pytorch_model-00004-of-00004.bin
-rw-r--r-- 1 root root 24K Jun 25 09:48 pytorch_model.bin.index.json
-rw-r--r-- 1 root root 444 Jun 25 09:48 special_tokens_map.json
-rw-r--r-- 1 root root 8.7M Jun 25 09:48 tokenizer.json
-rw-r--r-- 1 root root 50K Jun 25 09:48 tokenizer_config.json이전 포스팅에서 No-Code 파인튜닝 솔루션의 장단점을 Unsloth까지 다룬 후 정리한다고 했으나,
Unsloth는 파인튜닝 자동화 툴로 보기에는 무리가 있어 이번 편에서 정리하겠습니다.
지난번 LLaMA-Factory를 소개하면서 예상보다 복잡하고 사용하기 힘들다는 피드백을 받았습니다.
LLM을 파인튜닝하는 과정 자체가 LLM에 대한 기본 지식, 데이터셋 처리, GPU 활용, 라이브러리 최적화 등 여러 가지를 고려해야 할 부분이 많습니다.
그래서 사실 자동화 툴이 잘 만들어졌다고 하더라도 여전히 이러한 배경지식과 많은 시행착오들이 필요한 것은 사실입니다.
마지막으로 제가 생각하는 No-Code 파인튜닝 솔루션의 장단점은 아래와 같습니다.
Fine-tuning as code
YAML 파일을 통해 손쉽게 학습 파라미터를 설정할 수 있어 여러 실험을 빠르게 반복하기 용이
데이터셋과 학습 설정을 표준화하여 사용
다양한 학습 방법 및 고급 기법의 손쉬운 활용
QLoRA, LoRA 등 여러 학습 기법을 지원하여 다양한 모델 학습 방법을 적용
분산학습과 xformers_attention, flash attention등의 고급 기법 적용
커뮤니티
활발한 오픈소스 커뮤니티와 다양한 예제 코드, 문서가 있어 새로운 사용자도 쉽게 접근하고 활용
복잡한 설정
다양한 설정 옵션이 존재하여 처음 사용하는 사용자에게는 다소 복잡할 수 있음
일부 설정은 제대로 문서화되지 않아 코드를 직접 봐야 하는 어려움
성능 최적화의 어려움
여러 최적화 옵션이 있지만, 최적의 성능을 얻기 위해서는 많은 실험과 튜닝이 필요 (경험적인 노력)
버그들
QLoRA와 LoRA 설정 간의 일관성 문제나, load_in_4bit와 관련된 설정 충돌 문제가 발생 가능성
동일한 설정을 사용하는데도 결과가 일관되지 않을 때가 있어 디버깅이 필요
이번 포스팅에서는 Axolotl의 사용법을 단계별로 자세히 살펴보았습니다.
LLaMA-Factory와 비교했을 때 어떠셨나요?
어느 툴이 더 낫다고 단정짓기는 어렵지만, 저는 전반적으로 Axolotl이 더 많은 기능을 지원하는 것 같습니다.
글의 마무리 부분에서 장단점을 비교하며 여러 단점을 짚어봤지만,
그럼에도 불구하고 이러한 No-Code 파인튜닝 솔루션을 사용해야 하는 이유는 Fine-tuning as code가 가져다주는 편리함 때문입니다.
올해만 해도 수십 개의 모델이 쏟아져 나오고, 한 달 간격으로 SOTA 모델이 바뀌는 상황에서 여러 모델을 검증하고 관리하는 것은 개발자들에게 많은 부담을 줍니다.
그래서 이러한 툴들이 도움이 될 수 있으며, 환경 구성과 모델 관리에 들어가는 노력을 줄여 튜닝이라는 본연의 목적에 충실할 수 있게 만들어 줄 수 있다고 생각합니다.
그런 취지에서 이런 솔루션들을 바라보셨으면 합니다.
하지만 이런 툴을 상용 서비스나 사내 표준 플랫폼으로 사용하는 것은 조심해야 합니다. 오픈소스 생태계가 그렇듯,
특히 LLM쪽의 오픈소스 툴들은 소규모 커뮤니티 위주로 운영되는 경우가 많아 툴의 지속성에 대해 항상 생각해야 합니다.
다음 편에서는 가난한 자의 파인튜닝 솔루션 Unsloth를 다뤄보겠습니다.
다음 포스팅에서 뵙겠습니다!
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.