Aby uruchomić dostrojony model w Ollama albo llama.cpp, scalasz adapter LoRA z modelem bazowym, konwertujesz wynik do GGUF skryptem konwersji z llama.cpp, kwantyzujesz go i opakowujesz w plik Modelfile. Cały potok to cztery polecenia, a ten poradnik omawia każde z nich — łącznie z krokiem szablonu czatu, który za pierwszym razem wszyscy robią źle.
Ostatnia aktualizacja: lipiec 2026
Potok w skrócie
adapter LoRA + model bazowy → scalenie → folder HF → convert_hf_to_gguf.py
→ model-f16.gguf → llama-quantize → model-Q4_K_M.gguf → Modelfile → ollama createSkończyłeś fine-tuning swojego pierwszego LLM-a i masz folder z wagami adaptera. Frameworki treningowe zapisują adapter LoRA — kilkaset megabajtów różnic wag, a nie gotowy do uruchomienia model. Ollama oczekuje pojedynczego pliku GGUF. Ten poradnik służy właśnie do przerzucenia mostu nad tą przepaścią.
Krok 0 — Wymagania wstępne
Na Macu z Apple Silicon zanim zaczniesz, upewnij się, że masz natywnego Pythona arm64 — interpreter x86 pod Rosettą to powód, dla którego MLX i część zależności konwersji odmawiają instalacji.
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
pip install -r requirements.txt # dla skryptów konwersji
cmake -B build && cmake --build build --config Release -t llama-quantizePotrzebujesz zapasu miejsca na dysku: potok dla modelu 8B przez chwilę przechowuje scalony model FP16 (~16 GB), plik GGUF F16 (~16 GB) i kwant (~5 GB). Jeśli miejsca jest mało, usuwaj pliki pośrednie na bieżąco.
Krok 1 — Scal adapter LoRA z modelem bazowym
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
import torch
base = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B-Instruct", # musi być DOKŁADNIE ta baza, na której trenowałeś
torch_dtype=torch.float16, device_map="cpu")
model = PeftModel.from_pretrained(base, "./my-lora-adapter")
merged = model.merge_and_unload() # wtapia różnice w wagi
merged.save_pretrained("./merged-model", safe_serialization=True)
AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct").save_pretrained("./merged-model")Dwie pułapki: nazwa modelu bazowego musi dokładnie odpowiadać temu, na czym prowadziłeś fine-tuning (adapter wytrenowany na wariancie Instruct i scalony z wariantem bazowym daje subtelnie zepsuty model), i nie zapomnij zapisać tokenizera — bez niego konwersja się nie powiedzie. device_map="cpu" pozwala uruchomić to na maszynach bez zapasu VRAM; scalanie potrzebuje RAM-u systemowego, nie GPU.
(Skrót: jeśli trenowałeś w Unsloth, model.save_pretrained_gguf(...) wykonuje kroki 1–3 jednym wywołaniem — ten poradnik opisuje standardową ścieżkę dla wszystkich pozostałych oraz dla sytuacji, gdy potrzebujesz kontroli nad każdym etapem).
Krok 2 — Konwersja HF → GGUF
python convert_hf_to_gguf.py ./merged-model \
--outfile my-model-f16.gguf --outtype f16Konwertuj do F16, a kwantyzuj w osobnym kroku — kwantyzowanie prosto z konwertera skazuje Cię na powtarzanie konwersji przy każdym poziomie kwantyzacji, który zechcesz sprawdzić.
Jeśli skrypt zgłosi błąd Model architecture not supported, Twój model bazowy jest nowszy niż Twoja kopia llama.cpp — wykonaj git pull i spróbuj ponownie; wsparcie dla nowych architektur trafia tam w ciągu kilku dni od ważnych premier.
Krok 3 — Kwantyzacja
./build/bin/llama-quantize my-model-f16.gguf my-model-Q4_K_M.gguf Q4_K_MQ4_K_M to właściwy domyślny wybór (najlepsza jakość na gigabajt; ~4,9 GB dla modelu 8B). Sięgnij po Q5_K_M albo Q8_0, jeśli VRAM masz pod dostatkiem, a po Q3_K_M tylko pod prawdziwą presją — pełną tabelę decyzyjną znajdziesz w poradniku wyboru kwantyzacji. Warto przygotować dwa poziomy kwantyzacji i porównać je w teście A/B na swoim zadaniu: dostrojone zachowania (zwłaszcza sztywne formaty wyjścia) bywają bardziej wrażliwe na kwantyzację niż zwykła rozmowa.
Krok 4 — Modelfile (miejsce, gdzie umierają fine-tuningi)
FROM ./my-model-Q4_K_M.gguf
# Llama 3.1 chat template - MUST match what you trained with
TEMPLATE """<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|><|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"""
PARAMETER stop <|eot_id|>
SYSTEM You are a helpful assistant.ollama create my-finetune -f Modelfile
ollama run my-finetune "A test prompt from your training distribution"Kluczowa jest linia TEMPLATE. Twój fine-tuning nauczył się oczekiwać dokładnie tego formatu promptu, na którym był trenowany (powyżej nagłówki Llama 3.1; modele z rodziny Qwen/ChatML używają zamiast tego znaczników <|im_start|> — skopiuj poprawny szablon z modelu z tej samej rodziny poleceniem ollama show llama3.1 --template). Niedopasowany szablon to przyczyna numer jeden tego, że świeżo skonwertowany model wypisuje bzdury, ignoruje swój trening albo nigdy nie przestaje generować — spektakularnie wyglądające awarie, a wszystko przez jeden zły ciąg znaków. Jeśli Twój pierwszy test wygląda na zepsuty, zobacz bełkot na wyjściu: zły szablon promptu.
Walidacja wyniku
- Test dymny: 5–10 promptów prosto z rozkładu Twoich danych treningowych — dostrojone zachowanie powinno być nie do przeoczenia. Jeśli model zachowuje się jak zwykła Llama, scaliłeś adapter z niewłaściwą bazą albo szablon jest zły.
- Sprawdzenie tokenu stop: jeśli generowanie ciągnie się w nieskończoność, Twój
PARAMETER stopnie odpowiada tokenom, które faktycznie emituje dostrojony model. - Sprawdzenie kwantyzacji: porównaj Q4_K_M z Q8_0 na swoim zadaniu; zauważalny spadek jakości przy Q4 w wyspecjalizowanym zadaniu to zjawisko rzadkie, ale realne — warto je wychwycić wcześnie.
- Publikacja:
ollama push(wymaga konta w rejestrze) albo po prostu rozdaj plik GGUF — odbiorcy potrzebują tylko Twojego pliku Modelfile obok niego.
Najczęściej zadawane pytania
Czy Ollama może użyć adaptera LoRA bezpośrednio, bez scalania?
llama.cpp obsługuje adaptery skonwertowane do GGUF w czasie działania (convert_lora_to_gguf.py + --lora), ale to ścieżka ze scalaniem jest tym, czego oczekuje Ollama i co powinieneś dystrybuować: jeden artefakt, brak rozjazdu wersji adaptera i bazy, nieznacznie szybsze wnioskowanie.
Czy muszę powtarzać ten potok po każdym ponownym treningu?
Kroki 1–4 tak — ale to czynności mechaniczne; napisz do nich skrypt raz. Plik Modelfile zmienia się tylko wtedy, gdy zmienia się szablon albo parametry, więc kolejne iteracje treningu sprowadzają się do: scal → konwertuj → kwantyzuj → ollama create.
Dlaczego mój skonwertowany model działa gorzej niż podczas ewaluacji treningowej?
W kolejności prawdopodobieństwa: zły szablon czatu w pliku Modelfile (naprawiane w kilka minut), scalenie z innym wariantem bazy niż ten, na którym trenowałeś, albo kwantyzacja przy zadaniu wrażliwym na kwantyzację — przetestuj plik GGUF w F16, żeby ustalić, która z tych przyczyn zadziałała.
Ile miejsca na dysku potrzebuje konwersja modelu 8B?
W szczycie ~37 GB: scalony FP16 (~16 GB) + GGUF F16 (~16 GB) + kwant Q4 (~5 GB). Po walidacji usuń pliki pośrednie, a zostanie Ci tylko kwant o wielkości ~5 GB. Potok dla modelu 70B osiąga szczyt w okolicach 300 GB — zaplanuj to albo przeprowadź konwersję na wynajętej maszynie (poradnik o GPU w chmurze).