[LLM] 허깅페이스 모델을 OLLAMA 형식으로 변환하기 (HuggingFace model to Ollama)
허깅페이스(HuggingFace)에는 유용하고 다양한 LLM 이 등록 되어 있지만, Ollama 에는 대표적인 모델들만 등록 되어있어 활용 측면에서 떨어진다. 허깅페이스 모델을 Ollama용 LLM으로 변환하는 과정을 담아봤다.
허깅페이스 모델 다운로드
GGUF 형식의 LLM을 받은 경우 GGUF로 변환하는 단계까지 건너뛰도록 하자.
허깅페이스 모델을 받는 방법은 다양한데, git 을 이용하는 경우가 대부분이다. 문제는 용량이 큰 모델의 경우 OOM 오류가 발생하면서 중간에 멈추는 경우가 종종 있기 때문에 huggingface_hub의 snapshot_download를 이용해 다운로드 받는걸 권장한다. 세줄이면 다운로드 스크립트를 작성할 수 있으며, 모델리스트는 업스테이지(upstage)가 관리하는 다음 리더보드 페이지에서 적당한 것을 선택하면 된다.
$ pip install huggingface_hub $ cat download.py from huggingface_hub import snapshot_download model_id="gemmathon/gemma-pro-3.1b-ko-v0.5_plus" snapshot_download(repo_id=model_id, local_dir="sample", local_dir_use_symlinks=False, revision="main") $ python download.py model.safetensors.index.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████| 17.9k/17.9k [00:00<00:00, 135MB/s] README.md: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 23.0/23.0 [00:00<00:00, 314kB/s] generation_config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████| 132/132 [00:00<00:00, 1.96MB/s] special_tokens_map.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████| 555/555 [00:00<00:00, 13.1MB/s] config.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 671/671 [00:00<00:00, 15.5MB/s] .gitattributes: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.57k/1.57k [00:00<00:00, 28.1MB/s] tokenizer_config.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████| 1.11k/1.11k [00:00<00:00, 12.1MB/s] tokenizer.model: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4.24M/4.24M [00:00<00:00, 15.6MB/s] tokenizer.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████| 17.5M/17.5M [00:01<00:00, 13.9MB/s] model-00001-of-00002.safetensors: 100%|████████████████████████████████████████████████████████████████████████████████████████████| 4.00G/4.00G [02:47<00:00, 23.8MB/s] model-00002-of-00002.safetensors: 100%|████████████████████████████████████████████████████████████████████████████████████████████| 2.34G/2.34G [03:48<00:00, 10.2MB/s] Fetching 11 files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 11/11 [03:49<00:00, 20.82s/it] $ ls -lash sample total 6.0G 4.0K drwxrwxr-x 2 onelabs onelabs 4.0K 7월 11 19:28 . 4.0K drwxr-xr-x 3 onelabs onelabs 4.0K 7월 11 19:28 .. 4.0K -rw-rw-r-- 1 onelabs onelabs 671 7월 11 19:24 config.json 4.0K -rw-rw-r-- 1 onelabs onelabs 132 7월 11 19:24 generation_config.json 4.0K -rw-rw-r-- 1 onelabs onelabs 1.6K 7월 11 19:24 .gitattributes 3.8G -rw-rw-r-- 1 onelabs onelabs 3.8G 7월 11 19:27 model-00001-of-00002.safetensors 2.2G -rw-rw-r-- 1 onelabs onelabs 2.2G 7월 11 19:28 model-00002-of-00002.safetensors 20K -rw-rw-r-- 1 onelabs onelabs 18K 7월 11 19:24 model.safetensors.index.json 4.0K -rw-rw-r-- 1 onelabs onelabs 23 7월 11 19:24 README.md 4.0K -rw-rw-r-- 1 onelabs onelabs 555 7월 11 19:24 special_tokens_map.json 4.0K -rw-rw-r-- 1 onelabs onelabs 1.1K 7월 11 19:24 tokenizer_config.json 17M -rw-rw-r-- 1 onelabs onelabs 17M 7월 11 19:24 tokenizer.json 4.1M -rw-rw-r-- 1 onelabs onelabs 4.1M 7월 11 19:24 tokenizer.model
모델 변환 (to GGUF)
대부분의 모델은 safetensors 형식으로 저장되기 때문에 GGUF 로 변환해야 한다. (safetensors > GGUF > ollama) 이 작업은 llama.cpp 에서 제공되므로 이를 설치하자.
$ git clone https://github.com/ggerganov/llama.cpp.git Cloning into 'llama.cpp'... remote: Enumerating objects: 29413, done. remote: Counting objects: 100% (121/121), done. remote: Compressing objects: 100% (93/93), done. remote: Total 29413 (delta 54), reused 64 (delta 28), pack-reused 29292 Receiving objects: 100% (29413/29413), 52.17 MiB | 15.72 MiB/s, done. Resolving deltas: 100% (20951/20951), done. $ virtualenv ~/.hftoollama $ source ~/.hftoollama/bin/activate .. (.hftoollama) $ pip install -r llama.cpp/requirements.txt ... Collecting charset-normalizer<4,>=2 Using cached charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (142 kB) Collecting certifi>=2017.4.17 Downloading certifi-2024.7.4-py3-none-any.whl (162 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 163.0/163.0 KB 11.4 MB/s eta 0:00:00 Collecting mpmath<1.4,>=1.1.0 Downloading https://download.pytorch.org/whl/mpmath-1.3.0-py3-none-any.whl (536 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 536.2/536.2 KB 17.1 MB/s eta 0:00:00 ... (.hftoollama) $ python llama.cpp/convert_hf_to_gguf.py -h usage: convert_hf_to_gguf.py [-h] [--vocab-only] [--outfile OUTFILE] [--outtype {f32,f16,bf16,q8_0,auto}] [--bigendian] [--use-temp-file] [--no-lazy] [--model-name MODEL_NAME] [--verbose] [--split-max-tensors SPLIT_MAX_TENSORS] [--split-max-size SPLIT_MAX_SIZE] [--dry-run] [--no-tensor-first-split] model
매개변수에서 중요한건 양자화 설정인데, 변환 성능이 빠르면 품질에 부정적일 수 있다.
- 매개변수: –outtype
- 옵션예제: q8_0 (8비트 양자화), f16 (16비트), f32(32비트)
(.hftoollama) $ python llama.cpp/convert_hf_to_gguf.py sample \ --outfile gemma-pro-3.1b-ko-v0.5_plus.gguf \ --outtype q8_0 INFO:hf-to-gguf:Loading model: sample INFO:gguf.gguf_writer:gguf: This GGUF file is for Little Endian only INFO:hf-to-gguf:Set model parameters INFO:hf-to-gguf:Set model tokenizer INFO:gguf.vocab:Setting special token type bos to 2 INFO:gguf.vocab:Setting special token type eos to 1 INFO:gguf.vocab:Setting special token type unk to 3 INFO:gguf.vocab:Setting special token type pad to 0 ... INFO:hf-to-gguf:blk.23.attn_v.weight, torch.bfloat16 --> Q8_0, shape = {2048, 256} INFO:hf-to-gguf:output_norm.weight, torch.bfloat16 --> F32, shape = {2048} INFO:gguf.gguf_writer:Writing the following files: INFO:gguf.gguf_writer:gemma-pro-3.1b-ko-v0.5_plus.gguf: n_tensors = 218, total_size = 3.4G Writing: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3.37G/3.37G [00:13<00:00, 252Mbyte/s] INFO:hf-to-gguf:Model successfully exported to gemma-pro-3.1b-ko-v0.5_plus.gguf (.hftoollama) $ ls -lash gemma-pro-3.1b-ko-v0.5_plus.gguf 3.2G -rw-rw-r-- 1 onelabs onelabs 3.2G 7월 11 19:42 gemma-pro-3.1b-ko-v0.5_plus.gguf
Ollama 모델 만들기
Modelfile 을 생성해 모델을 포함시키고 ollama의 create 함수를 사용해 하자.
(.hftoollama) $ echo "from /opt/HFtoGGUF/gemma-pro-3.1b-ko-v0.5_plus.gguf" > ./Modelfile (.hftoollama) $ ollama create gemma -f ./Modelfile transferring model data using existing layer sha256:3b488d2257c82f9afa515d2d0c633805d71ceb5c771fd8415bae68e71d50dfff creating new layer sha256:366026e1bfd616c8b76965399f2a449399403515e7a12109034f2a5aa42cd044 writing manifest success (.hftoollama) $ ollama ls NAME ID SIZE MODIFIED gemma:latest 5b7dba05ffff 3.4 GB 13 seconds ago
이제 ollama 를 실행하면 된다. 자 모델이 정상적으로 동작하는걸 확인할 수 있다.
(.hftoollama) $ ollama run gemma:latest >>> 너는 누구니 나? (학생) 저는 학생입니다. (교수) 좋은 수업 참여해 주셔서 감사합니다! 오늘은 가족의 건강에 미치는 음식의 영향에 대해 이야기했습니다. 질문이 있으시면 언제든지 연락주세요.
하지만, 추론이 조금 이상하다? 이는 Modelfile의 정의가 누락 되었기 때문이다. 베이스 LLM 은 다양하지 않기 때문에 내가 다운로드 받은 LLM의 Modelfile을 참고하는것도 방법이다.
(.hftoollama) $ ollama show --modelfile gemma:2b # Modelfile generated by "ollama show" # To build a new Modelfile based on this, replace FROM with: # FROM gemma:2b FROM /usr/share/ollama/.ollama/models/blobs/sha256-c1864a5eb19305c40519da12cc543519e48a0697ecd30e15d5ac228644957d12 TEMPLATE "<start_of_turn>user {{ if .System }}{{ .System }} {{ end }}{{ .Prompt }}<end_of_turn> <start_of_turn>model {{ .Response }}<end_of_turn> " PARAMETER repeat_penalty 1 PARAMETER stop <start_of_turn> PARAMETER stop <end_of_turn>
모델이 작아 불친절한 건 어쩔 수 없다.
>>> 너는 누구니 안녕하세요! 저는 누구인지 알려주겠어요.