huggingface 모델들과 PEFT 라이브러리의 조합은 몇 줄의 Python 코드로 나만의 모델을 만들 수 있게 해주었습니다.
특히, PEFT(Parameter-Efficient Fine-Tuning)는 시간과 메모리를 효율적으로 사용하게 도와주며 다양한 Downstream application 을 만들 수 있는 환경을 제공하였습니다.
( PEFT에 대해 궁금하시다면, devocean 에 올라온 PEFT 소개글을 참고해주세요. https://devocean.sk.com/search/techBoardDetail.do?ID=164779 )
하지만, PEFT 로 특정 Task에 Align 된 모델을 개발 후에 실제 서비스를 하려고 하면, 추론 속도가 고민됩니다.
그래서 오늘은 PEFT library 로 fine tune 이 진행된 모델을 llama.cpp 로 GGUF 로 변환한 후 양자화를 통해 추론 속도를 개선하는 작업을 진행해보려고 합니다.
먼저 huggingface 로 base model 을 받은 뒤에 PEFT library 로 fine tune을 진행하면, adapter 파일들이 생성됩니다.
PEFT 방법으로 fine tune 된 모델을 사용하기 위해서는 아래 코드처럼 base model 을 load 하고 다시 adapter 를 붙여야하는 구조가 됩니다.
아래와 같이 adapter를 Merge 한 fine tune 모델을 생성한다면, 모델을 한번 만에 load 할 수 있을것입니다.
Merge 된 파일들은 huggingface model 들처럼 safetensors 혹은 bin 파일들로 저장됩니다.
transformer 라이브러리를 계속 사용하면서 학습해야하거나, 검증해야하는 시점에는 문제가 없겠지만, 추론 시점이라면 python 의 transformer 라이브러리로 모델을 load 하고 추론하는 작업은 생각보다 시간이 많이 걸려서 고민이될 수 있습니다.
이럴 때 llama.cpp 를 활용해볼 수 있는데요. ( https://github.com/ggerganov/llama.cpp )
llama.cpp 는 오픈소스 llm 들을 pure C/C++ 환경에서 실행시켜 최소한의 setup 으로 추론 속도를 향상시켜주는 여러 기능을 제공합니다.
llama.cpp 에서 모델을 실행하기 위해서는 huggingface의 모델을 GGUF 파일로 변환하는 작업이 필요합니다.
GGUF는 Georgi Gerganov Unified Format 의 약자로 llama.cpp github 주소에 표시된 Georgi Gerganov 개발자 본인의 이름을 딴 파일 확장자명입니다. ( 사람은 죽어서 이름을…. )
이제 순서대로 llama.cpp 를 활용해보겠습니다.
1.먼저 llama.cpp 를 본인의 환경에 맞게 설치합니다.
저는 llama.cpp github 에서 안내한 cmake 를 활용해 설치해보겠습니다.
1 2 3 4 |
<span class="token function">git</span> clone https://github.com/ggerganov/llama.cpp <span class="token builtin class-name">cd</span> llama.cpp cmake -B build cmake --build build --config Release |
2. llama.cpp 를 clone 하며 받아둔 convert-hf-to-gguf.py 로 GGUF로 변환합니다.
python 파일을 -h 옵션과 같이 실행하며 어떤 옵션들이 있는지 확인해봅니다.
1 |
python convert-hf-to-gguf.py -h |
document 안내에서는 간단하게 convert 할 모델만 위치만 입력하네요.
–outfile 옵션만 주어서 저장될 위치만 지정해보겠습니다.
1 |
python convert-hf-to-gguf.py <span class="token string">"merged_model_path"</span> --outfile <span class="token string">"merged_model.gguf"</span> |
이제 bin 파일로 저장된 모델을 gguf 파일로 변환하였습니다.
bin 파일으로 저장될때와 다르게 각종 config , tokenizer 파일들이 사라지고 하나의 gguf 파일만 생성된것을 볼 수 있습니다.
3. 그럼 양자화를 진행해보겠습니다.
모델의 weight 를 lower bit 로 변환하는 작업은 모델의 크기를 줄이고 추론 속도를 향상 시켜 주지만, 작은 크기의 모델들을 실험했을 때 학습된 능력을 완전 잃어버리는 부작용도 존재합니다.
동일하게 llama.cpp 를 clone 할 때 내려받은 llama-quantize 파일을 -h 옵션으로 실행하여 usgage를 살펴 봅니다.
1 |
llama-quantize -h |
상황에 따라 적절한 type을 적용하는게 필요하겠지만, 오늘은 document 에 안내된대로 Q4_K_M 로 한번 진행하겠습니다.
1 |
llama-quantize merged_model.gguf merged_model-Q4_K_M.gguf Q4_K_M |
드디어 양자화된 모델까지 생성되었습니다.
4. 모델의 성능을 측정하는 다양한 benchmark 들이 있겠지만, 간단한 prompt를 직접 실행하며 실행시간을 한번 비교해보겠습니다.
fine tune 된 모델이 아닌 gemma-2b-it 모델을 활용합니다.
llama.cpp 로 양자화된 모델에 동일 prompt를 입력해보겠습니다.
1 |
llama-cli gemma-2b-it_Q4_0.gguf --log-disable --predict <span class="token number">128</span> -p <span class="token string">"대한민국의 서울을 친구들에게 소개해줘"</span> |
transformer 라이브러리로 모델을 로드하고 추론하는 20s 에서 llama.cpp 로 gguf 를 실행하는데 109 ms 로 추론 속도가 크게 개선된걸 볼 수 있습니다.
하지만, 서울의 전통 음료가 한우라고 답변한건 분명한 모델의 환각입니다.
gemma-2b 처럼 작은 모델을 양자화할때는 모델의 성능이 크게 안 좋아진다는 점을 확인할 수 있습니다.
오늘은 llama.cpp 로 gguf 파일로 convert 하고 양자화하는 작업을 진행해보았습니다.
gguf 파일의 경우 localAI 나 ollama 같은 프로그램들에서도 많이 사용하는 type으로 필요에 따라 localAI 등에 fine tune 된 모델을 통합하기 위해서는 llama.cpp 를 활용하여 gguf 파일로 변환하는 작업을 진행할 수 있을 것 같습니다.
llama.cpp 처럼 Open Source 모델을 활용한 다양한 기술들이 발전할수록 OpenAI 와의 격차는 점점 줄어들지 않을까 기대해봅니다.
이 글은 SK 개발자 블로그 Devocean에 동일하게 작성된 글입니다.
https://devocean.sk.com/internal/board/viewArticleForAjax.do?id=166201