Öppna språkmodeller som Qwen3, Llama och Mistral har blivit bra nog att ersätta dyra API-anrop för många uppgifter, men bara om du anpassar dem till din egen data. Problemet har länge varit hårdvaran. Att finjustera en modell med 7-8 miljarder parametrar krävde tidigare ett GPU-kluster du som utvecklare eller litet säkerhetsteam i Norden sällan hade tillgång till. Med Unsloth och tekniken QLoRA går det idag att göra samma jobb på ett enda gaming-GPU, i många fall på under en timme.

Den här guiden går igenom hela kedjan: installation, dataförberedelse, träning, utvärdering, export till GGUF-format och slutligen körning av din egen modell lokalt. Vi använder Unsloth 2026.9.14, den senaste paketversionen på PyPI vid skrivande stund, tillsammans med lm-evaluation-harness v0.4.0 för utvärdering och llama.cpp för att servera resultatet. Du behöver ingen tidigare erfarenhet av maskininlärningsträning, men grundläggande Python och terminalvana underlättar betydligt.

Räkna med runt 90 minuter aktiv tid för att följa alla elva steg på ett eget dataset, plus själva träningstiden som varierar med modellstorlek. Guiden är skriven för dig som redan kan grundläggande Python, men som aldrig tränat en egen språkmodell förut. Vi länkar till källkod och officiell dokumentation genomgående, så att du kan verifiera versionsnummer och API-ändringar själv innan du kör något i produktion.

Varför finjustera en egen AI-modell med Unsloth just nu

Tre saker har hänt samtidigt under 2026. Öppna basmodeller har blivit kompetenta nog för produktion, GPU-minnet har blivit den verkliga flaskhalsen snarare än modellkvaliteten, och verktygen för effektiv träning har mognat. Unsloth är det projekt som tagit störst steg på den tredje punkten. Enligt projektets egen beskrivning på PyPI tränar paketet modeller upp till 2-5 gånger snabbare och med upp till 70 procent mindre VRAM jämfört med en vanlig Hugging Face-installation, utan att noggrannheten ska behöva försämras.

Det gör skillnaden mellan “behöver ett datacenter” och “kör på min egen dator” konkret. Ett svenskt säkerhetsteam som vill bygga en intern klassificeringsmodell för supportärenden, eller ett spelstudio som vill ha en NPC-dialogmotor tränad på sitt eget manus, kan nu göra det utan att skicka data till en extern API-leverantör. Det spelar också roll ur ett dataskyddsperspektiv: när träningsdatan aldrig lämnar din egen infrastruktur blir GDPR-bedömningen betydligt enklare, eftersom du slipper utreda vilket tredje land en molnleverantör faktiskt processar datan i.

Kostnadsbilden spelar också in. Ett API-anrop till en stor molnmodell kostar per token och skalar med användningen. En egen finjusterad modell, körd på hårdvara du redan äger eller hyr per timme, har en fast kostnad som snabbt blir billigare vid hög eller förutsägbar volym. För interna verktyg som körs dygnet runt, som en supportklassificerare eller en kodgranskningsbot, brukar brytpunkten infalla betydligt tidigare än man tror innan man räknat på det.

Regelverket spelar också en roll för organisationer som redan omfattas av NIS2 eller hanterar känslig kunddata. Att kunna visa exakt var träningsdatan har legat, vem som haft åtkomst till den, och att den aldrig passerat en extern tredjepartsserver, förenklar både internrevision och eventuella tillsynsfrågor. Det är ett argument som väger allt tyngre i takt med att fler svenska myndigheter och företag skärper kraven på sina leverantörskedjor.

Unsloth stöder i sin senaste version (2026.9.14) LoRA, QLoRA, full finjustering, förträning, DPO, GRPO och FP8-precision i samma ramverk, enligt projektets GitHub-sida. I resten av guiden fokuserar vi på QLoRA, eftersom det är den metod som ger bäst balans mellan hårdvarukrav och resultat för de flesta som läser detta. Vill du läsa mer om hela kategorin innan du börjar, har vi en bredare översikt i vår sektion om AI och maskininlärning.

QLoRA, LoRA och full finjustering: vad skiljer dem åt

LoRA (Low-Rank Adaptation) fryser basmodellens miljarder vikter och tränar istället två små matriser som “sitter på toppen” av varje lager. Resultatet blir en liten adapterfil, ofta några hundra megabyte, som du kan ladda ovanpå originalmodellen. Du förlorar inget av basmodellens kunskap och riskerar betydligt mindre överanpassning än vid full finjustering, eftersom merparten av nätverket aldrig rör sig under träningen.

QLoRA lägger till ett steg till. Basmodellen laddas i 4-bitars kvantiserat format (vanligtvis NF4, Normal Float 4) under hela träningen, vilket gör att minnesavtrycket faller drastiskt. Det är precis det här steget Unsloth optimerar hårdast genom egna CUDA-kärnor och ett eget gradient checkpointing-läge, och det är orsaken till att en modell som tidigare krävde 24 GB VRAM nu kan tränas på kort med betydligt mindre minne. Själva adaptern tränas fortfarande i högre precision, vilket förklarar varför kvaliteten inte faller trots att basmodellen är starkt kvantiserad.

Full finjustering, där alla vikter uppdateras, ger dig störst flexibilitet men kräver mångdubbelt mer minne och en betydligt större risk för att modellen “glömmer” generell kunskap när den tränas hårt på ett smalt dataset. De flesta praktiska projekt, från kundtjänstbottar till interna kodassistenter, löses bättre med LoRA eller QLoRA. Om du redan har provat Hugging Face-vägen finns vår guide om att finjustera en AI-modell med Hugging Face, och om du specifikt jobbar med Mistral-modeller har vi en egen genomgång av Mistral-finjustering. Själva adaptertekniken bygger på Hugging Face-biblioteket PEFT, som Unsloth använder under huven och utökar med egna optimeringar.

En praktisk tumregel: välj QLoRA som standard. Byt bara till full finjustering om du har gott om GPU-minne, ett stort och varierat dataset, och ett specifikt behov av att ändra modellens djupaste beteenden snarare än att lägga till ny kunskap ovanpå det befintliga.

Unsloth jämfört med vanlig Hugging Face-träning

Det går att köra QLoRA utan Unsloth, direkt via Hugging Face transformers, PEFT och bitsandbytes. Skillnaden ligger i hur mycket manuellt arbete och GPU-minne det kostar dig. Unsloth skriver om de tyngsta beräkningsstegen med egna CUDA-kärnor, vilket enligt projektets egen dokumentation ger den hastighets- och minnesvinst som sammanfattas i tabellen nedan. Observera att siffrorna kommer från Unsloth-teamets egna publicerade jämförelser, inte från en oberoende tredjepartsmätning, så behandla dem som en indikation snarare än en garanti för exakt din arbetsbelastning.

EgenskapVanlig HF transformers + PEFTUnsloth 2026.9.14
TräningshastighetBaslinjeUpp till 2-5x snabbare enligt Unsloths egna uppgifter
VRAM-användningBaslinjeUpp till 70% lägre enligt Unsloths egna uppgifter
Inbyggt GGUF-exportKräver separata konverteringsverktygInbyggt via save_pretrained_gguf
Stöd för DPO/GRPOKräver separata bibliotek och mer manuell konfigurationInbyggt i samma API
InstallationskomplexitetFlera paket att versionsmatcha manuelltEtt paket med fördefinierade kombinationer

För nybörjare är den inbyggda GGUF-exporten ofta den enskilt mest tidsbesparande skillnaden, eftersom den tar bort ett helt steg av manuell konvertering som annars kräver separata skript och extra felsökning. Mer erfarna team som redan har en fungerande pipeline med vanlig transformers kan välja att behålla den och bara byta ut träningsstegen mot Unsloths snabbare implementation, eftersom biblioteket är designat för att vara kompatibelt med befintlig Hugging Face-kod snarare än att kräva en helt ny arbetsprocess.

Förutsättningar: hårdvara, mjukvara och versioner

Innan du börjar, se till att du har följande på plats. Listan är medvetet konkret, för otydliga versionskrav är den vanligaste orsaken till att en finjusteringskörning kraschar efter tjugo minuter istället för att misslyckas direkt vid installationen.

  • Python 3.11 eller senare
  • En NVIDIA-GPU med CUDA 12.1 eller senare (konsumentkort som RTX 3090/4090 fungerar bra för 7-8B-modeller)
  • unsloth version 2026.9.14 (installeras via pip)
  • PyTorch, bitsandbytes, trl, peft, transformers och accelerate i de versioner Unsloth själv kräver
  • Minst 30 GB ledigt diskutrymme för modellvikter, checkpoints och exporterade GGUF-filer
  • Stabil internetanslutning för att ladda ner basmodellen, som för en 8B-modell ofta ligger mellan 4 och 8 GB
  • llama.cpp byggt lokalt, för att testa och servera GGUF-filen i slutet av guiden
  • lm-evaluation-harness v0.4.0, valfritt men rekommenderat för att mäta resultat objektivt

Du kan köra hela guiden på Linux, Windows med WSL2, eller macOS för själva dataförberedelsen (dock inte träningen, som kräver en NVIDIA-GPU). Om du planerar att köra på en molninstans istället för egen hårdvara duger ofta ett enkelt GPU-kort med 12-16 GB VRAM för de flesta projekt i den här guiden, vilket håller kostnaden nere jämfört med att boka ett helt A100-kluster.

Tabellen nedan ger vägledande VRAM-behov för QLoRA-träning i 4-bitars läge, baserat på Unsloths egna exempel och vanlig community-erfarenhet. Siffrorna varierar beroende på sekvenslängd och batchstorlek, så räkna alltid med marginal uppåt innan du planerar en längre körning.

ModellstorlekUngefärligt VRAM-behov (QLoRA 4-bit)Typiskt GPU-exempelTräningstid för en mindre dataset
3-4B parametrar4-6 GBRTX 3060 / RTX 406010-20 minuter
7-8B parametrar6-10 GBRTX 3080 / RTX 407020-45 minuter
13-14B parametrar10-14 GBRTX 3090 / RTX 408045-90 minuter
27-30B parametrar18-24 GBRTX 4090 / A60002-4 timmar
70B parametrar35-48 GB (ofta flera GPU:er)2x A100 eller liknandeFlera timmar

Steg 1-2: Installera Unsloth och ladda basmodellen i 4-bitars läge

Första steget är att skapa en ren Python-miljö och installera Unsloth med rätt tillägg för din CUDA-version. Unsloth-teamet rekommenderar att installera paketet med extraflaggor som matchar din GPU-generation, så kontrollera alltid vilken CUDA-version din drivrutin stöder innan du kör kommandot nedan. Att köra i en isolerad venv eller conda-miljö är inte valfritt här, eftersom bitsandbytes och torch är notoriskt känsliga för versionskonflikter med andra paket du redan kan ha installerat.

python3 -m venv unsloth-env
source unsloth-env/bin/activate

pip install --upgrade pip
pip install "unsloth[cu121-torch220]==2026.9.14"
pip install trl peft accelerate bitsandbytes

# Kontrollera att GPU:n syns korrekt
python3 -c "import torch; print(torch.cuda.get_device_name(0))"

Med miljön på plats laddar du basmodellen. Här väljer vi en öppen modell i 4-bitars format direkt, vilket betyder att hela nedladdningen och träningen sker med reducerad precision från start. Unsloth tillhandahåller färdigkvantiserade versioner av de populäraste öppna modellerna, vilket sparar både bandbredd och tid jämfört med att ladda ner en fullstor modell och kvantisera den själv lokalt.

from unsloth import FastLanguageModel

max_seq_length = 2048

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/llama-3-8b-bnb-4bit",
    max_seq_length = max_seq_length,
    dtype = None,
    load_in_4bit = True,
)

När laddningen lyckas skriver Unsloth ut en statusrad i terminalen som bekräftar version och GPU-detektering, ungefär enligt mönstret nedan. Dyker den inte upp, eller kraschar skriptet innan den hinner skrivas ut, är det nästan alltid ett CUDA- eller minnesproblem (se felsökningsavsnittet längre ner i guiden för konkreta lösningar).

==((====))==  Unsloth 2026.9.14: Fast Llama patching.
   \\   /|    GPU: NVIDIA RTX 4090. Max memory: 24.0 GB.
O^O/ \_/ \    Pytorch: 2.2.0. CUDA: 8.9. CUDA Toolkit: 12.1.
\        /    Bfloat16 = TRUE.
 "-____-"     Free license: github.com/unslothai/unsloth

Om du vill byta basmodell senare, till exempel till en Qwen3- eller Mistral-variant, räcker det att ändra model_name-strängen. Unsloth håller en uppdaterad lista med förkvantiserade modeller på sin Hugging Face-profil, så kontrollera alltid att den modell du vill använda faktiskt finns i en 4-bitars variant innan du planerar runt den.

Steg 3: Förbered och formatera träningsdatasetet

Det här är steget flest nybörjare underskattar. En finjusterad modell blir aldrig bättre än datan den tränas på, och fel format ger ofta en modell som “nästan” fungerar men svarar konstigt i produktion. Strukturera din data som JSONL med tydliga roller, och använd gärna samma chattmall som basmodellen redan är tränad på, snarare än att hitta på ett eget format.

{"messages": [{"role": "user", "content": "Vad är skillnaden mellan LoRA och QLoRA?"}, {"role": "assistant", "content": "LoRA tränar små adaptermatriser ovanpå en fryst basmodell. QLoRA gör samma sak men laddar basmodellen i 4-bitars precision, vilket minskar minnesbehovet kraftigt under träningen."}]}
{"messages": [{"role": "user", "content": "Hur stort dataset behöver jag för att finjustera en 8B-modell?"}, {"role": "assistant", "content": "Allt från några hundra till några tusen välformaterade exempel brukar ge mätbar förbättring, förutsatt att exemplen är konsekventa och representativa för din faktiska användning."}]}

Innan du går vidare till träning, städa datan i tre steg. Först, ta bort dubbletter och nästintill identiska exempel, eftersom de ger modellen en falsk känsla av säkerhet kring vissa svarsmönster. Sedan, dela upp datasetet i en tränings- och en valideringsdel, gärna 90/10, så att du kan upptäcka överanpassning tidigt. Till sist, granska manuellt ett slumpmässigt urval på femtio till hundra rader för att fånga formateringsfel innan de kostar dig en hel träningskörning.

Ladda in filen och applicera modellens chattmall innan tokeniseringen, så att träningen ser exakt samma textformat som modellen förväntar sig vid inferens. Att hoppa över det här steget, och istället skicka in rå text utan mall, är en av de vanligaste orsakerna till att en annars korrekt tränad modell svarar med konstiga specialtecken eller avbrutna meningar.

from datasets import load_dataset
from unsloth.chat_templates import get_chat_template

tokenizer = get_chat_template(tokenizer, chat_template = "llama-3")

dataset = load_dataset("json", data_files = "min_data.jsonl", split = "train")

def formatera(exempel):
    text = tokenizer.apply_chat_template(
        exempel["messages"], tokenize = False, add_generation_prompt = False
    )
    return {"text": text}

dataset = dataset.map(formatera)

Var hittar du eller skapar du bra träningsdata

Det vanligaste hindret i praktiken är inte verktygen utan datan. Tre källor brukar fungera bra för de flesta projekt. Första källan är din egen historik: supportloggar, interna wikis, kodgranskningskommentarer eller tidigare kundkonversationer, så länge du anonymiserar personuppgifter innan de hamnar i träningsfilen. Andra källan är öppna instruktionsdataset på Hugging Face Hub, som ofta går att filtrera och blanda med din egen data för att bredda modellens generella förmåga utan att späda ut din specifika uppgift för mycket.

Tredje källan, och den som flest underskattar värdet av, är syntetisk data genererad av en starkare modell. Du kan till exempel be en stor molnmodell generera hundra varianter av en supportfråga i din bransch, granska dem manuellt, och använda de bästa som träningsexempel. Det fungerar särskilt bra för att täcka ovanliga formuleringar och kantfall som är svåra att samla in organiskt från egen historik. Blanda aldrig enbart syntetisk data utan någon verklig data alls, eftersom modellen då riskerar att lära sig den genererande modellens stil snarare än din faktiska verklighet.

Steg 4: Konfigurera LoRA-adaptrar

Nu bestämmer du hur stora adaptrarna ska vara. Parametern r (rank) styr hur mycket extra kapacitet modellen får att lära sig nya mönster. Högre rank ger mer flexibilitet men också större filer och högre risk för överanpassning på små dataset. En rank mellan 8 och 32 fungerar för de flesta praktiska uppgifter, och det är sällan värt att gå högre än det om du inte tränar på ett mycket stort och varierat dataset.

model = FastLanguageModel.get_peft_model(
    model,
    r = 16,
    target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
                       "gate_proj", "up_proj", "down_proj"],
    lora_alpha = 16,
    lora_dropout = 0,
    bias = "none",
    use_gradient_checkpointing = "unsloth",
    random_state = 3407,
)

Tumregeln är att sätta lora_alpha lika med eller dubbelt så stort som r. Gradient checkpointing-flaggan med värdet “unsloth” aktiverar projektets egen minnesoptimering, som är en av huvudanledningarna till att träningen går snabbare och drar mindre minne än en standardinstallation. Listan med target_modules täcker samtliga projektionslager i en transformerarkitektur, vilket ger adaptern tillgång att justera både uppmärksamhetsmekanismen och de framåtkopplade lagren. Vill du spara ytterligare minne på bekostnad av lite flexibilitet kan du begränsa listan till bara q_proj och v_proj, men för de flesta ändamål är det inte värt avkallet.

Steg 5-6: Ställ in träningsargument och starta träningen

Med modell och data klara återstår själva träningskonfigurationen. Håll batchstorlek och gradient accumulation i balans mot ditt GPU-minne, och börja alltid med en konservativ inlärningshastighet. Det är bättre att träna en extra epok med lägre learning rate än att tvingas starta om från noll efter en körning som divergerat.

from trl import SFTTrainer
from transformers import TrainingArguments

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    dataset_text_field = "text",
    max_seq_length = max_seq_length,
    args = TrainingArguments(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,
        warmup_steps = 10,
        num_train_epochs = 3,
        learning_rate = 2e-4,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        logging_steps = 1,
        optim = "adamw_8bit",
        save_strategy = "epoch",
        output_dir = "resultat",
    ),
)

trainer.train()

Flaggan save_strategy = "epoch" ser till att du får en checkpoint efter varje epok, vilket gör det möjligt att backa till en tidigare version om den sista epoken skulle visa tecken på överanpassning. Tre epoker är en rimlig startpunkt för de flesta instruktionsdataset i storleksordningen några hundra till några tusen exempel. Har du ett mycket större dataset räcker det ofta med en eller två epoker.

Under träningen loggas förlustvärdet (loss) för varje steg. En hälsosam körning ser ut ungefär som nedan: värdet sjunker stadigt och planar sedan ut mot slutet. Stiger värdet igen efter att ha planat ut, eller hoppar det oregelbundet mellan epoker, är det ett tecken på för hög inlärningshastighet eller ett dataset med inkonsekvent formatering som du bör granska igen innan du fortsätter.

{'loss': 1.842, 'learning_rate': 0.0001, 'epoch': 0.2}
{'loss': 1.390, 'learning_rate': 0.00018, 'epoch': 0.6}
{'loss': 1.015, 'learning_rate': 0.00014, 'epoch': 1.2}
{'loss': 0.871, 'learning_rate': 0.00008, 'epoch': 2.0}
{'loss': 0.793, 'learning_rate': 0.00002, 'epoch': 2.8}

Steg 7: Utvärdera modellen med lm-evaluation-harness

Att “känna” att modellen svarar bättre räcker inte i produktion. Kör istället ett objektivt test med lm-evaluation-harness, som i version 0.4.0 fick ett nytt pluginsystem där egna modellbackends, filter och mätvärden kan registreras utan att du behöver forka projektet. Jämför alltid basmodellen och den finjusterade versionen på samma uppgifter, inte bara på din egen träningsdata, annars riskerar du att dra fel slutsats om hur mycket modellen faktiskt förbättrats.

pip install lm-eval==0.4.0

# Kör basmodellen som referens
lm_eval --model hf \
    --model_args pretrained=unsloth/llama-3-8b-bnb-4bit,load_in_4bit=True \
    --tasks mmlu,hellaswag,arc_challenge \
    --device cuda:0 \
    --batch_size 8

# Kör samma test på din finjusterade modell
lm_eval --model hf \
    --model_args pretrained=./resultat,load_in_4bit=True \
    --tasks mmlu,hellaswag,arc_challenge \
    --device cuda:0 \
    --batch_size 8

Förutom de generella testerna i tabellen nedan bör du komplettera med ett litet eget benchmark som speglar din faktiska uppgift, till exempel tio till tjugo frågor som en kundtjänstmodell förväntas kunna svara korrekt på. De generella testerna visar om modellen tappat allmän förmåga, medan ditt eget test visar om den faktiskt blivit bättre på det du tränade den för.

UtvärderingsuppgiftVad den mäterLämplig för
mmluBred faktakunskap över ämnesområdenAllmänna assistenter
hellaswagFörmåga att förutsäga rimlig textfortsättningDialog och skrivassistans
arc_challengeResonemang kring naturvetenskapliga frågorUtbildnings- och forskningsverktyg
gsm8kMatematisk problemlösning i flera stegTekniska och analytiska assistenter

Steg 8-9: Slå ihop adaptern och exportera till GGUF

Innan du kan köra modellen utanför Python-skriptet behöver LoRA-adaptern slås ihop med basmodellens vikter till en sammanhängande modell. Unsloths changelog från slutet av september 2026 bekräftar att hela sammanslagna modeller, inte bara LoRA-adaptrar, numera kan exporteras direkt till GGUF-format, vilket förenklar det här steget avsevärt jämfört med tidigare versioner där ett extra mellansteg via andra konverteringsverktyg ofta krävdes.

# Slå ihop adaptern med basmodellen
model.save_pretrained_merged(
    "min-modell-sammanslagen",
    tokenizer,
    save_method = "merged_16bit",
)

# Exportera direkt till GGUF
model.save_pretrained_gguf(
    "min-modell-gguf",
    tokenizer,
    quantization_method = "q4_k_m",
)

Lägg märke till att save_method = "merged_16bit" skriver ut modellen i 16-bitars precision, inte 4-bitars. Det är avsiktligt: den slutliga GGUF-kvantiseringen görs i ett separat steg från en högkvalitativ mellanversion, snarare än att kvantisera en redan kvantiserad modell, vilket annars skulle ge sämre resultat. Räkna med att mellansteget tillfälligt tar betydligt mer diskutrymme än den slutliga GGUF-filen, så kontrollera att du har marginal innan du startar exporten.

Unsloth stöder också det nyare kvantiseringsformatet Dynamic v3.0 GGUF, som enligt projektets egen dokumentation kan ge högre noggrannhet för samma filstorlek jämfört med äldre GGUF-varianter, särskilt för större modeller. Om du exporterar en modell i till exempel Qwen3-familjen är det värt att testa Dynamic-varianten innan du nöjer dig med standardkvantiseringen, eftersom skillnaden kan vara märkbar på uppgifter som kräver precisa svar.

Steg 10-11: Välj kvantiseringsnivå och servera modellen lokalt

GGUF-formatet, som används av llama.cpp, stöder flera kvantiseringsnivåer. Valet handlar om en avvägning mellan filstorlek, hastighet och svarskvalitet. Tabellen nedan sammanfattar de vanligaste nivåerna och när du bör välja vilken.

KvantiseringRelativ filstorlekKvalitetRekommenderas för
Q2_KMinstMärkbart lägreExtremt begränsad hårdvara, test endast
Q4_K_MLitenBra balansDe flesta lokala applikationer
Q5_K_MMedelHögProduktion där kvalitet väger tyngre än storlek
Q6_KStörreMycket högServrar med gott om minne
Q8_0StorNära originaletUtvärdering och arkivering
F16StörstFullständig precisionVidare träning eller forskning

För snabb lokal testning fungerar llama.cpp direkt, och det är ofta den enklaste vägen om du bara vill prova modellen på din egen dator. Vill du istället servera modellen för flera samtidiga användare är vLLM ofta ett bättre val, särskilt i version 0.30.0 som förbättrat genomströmningen ytterligare jämfört med tidigare utgåvor. Vi har jämfört de två körningssätten i detalj i vår guide om Ollama vs vLLM, och om du vill köra flera öppna modeller sida vid sida rekommenderar vi även vår genomgång av att köra DeepSeek och Llama lokalt med Ollama eller vår guide till LM Studio för ett mer grafiskt gränssnitt utan egen kommandoradshantering.

# Testa modellen direkt med llama.cpp
./llama-cli -m min-modell-gguf/model-q4_k_m.gguf \
    -p "Förklara QLoRA på svenska i två meningar." \
    -n 200

# Eller servera den som ett lokalt API med vLLM
vllm serve ./min-modell-sammanslagen --port 8000

Innan du går vidare till produktion, kör ett par manuella testfrågor mot både den exporterade GGUF-filen och den sammanslagna 16-bitars versionen, och jämför svaren. Stora avvikelser mellan de två tyder på att kvantiseringen varit för aggressiv för din specifika uppgift, och att du bör testa en högre kvalitetsnivå från tabellen ovan.

Vanliga fallgropar vid QLoRA-finjustering

Nästan alla problem i den här typen av projekt går att spåra till en handfull återkommande misstag. Här är de som orsakar mest frustration i praktiken, oavsett om du tränar en liten testmodell eller något som ska i produktion.

  • Fel chattmall. Om träningsdatan formateras med en annan mall än den modellen faktiskt använder vid inferens, svarar modellen inkonsekvent eller blandar roller på ett sätt som är svårt att felsöka i efterhand.
  • För hög inlärningshastighet. En aggressiv learning rate får modellen att snabbt glömma generell kunskap, ett fenomen som brukar kallas catastrophic forgetting, och som ofta syns först efter att träningen redan avslutats.
  • Fel rank på LoRA-adaptern. För lågt värde på r ger en modell som inte lär sig uppgiften ordentligt. För högt värde ökar risken för överanpassning och gör adaptern onödigt stor utan motsvarande kvalitetsvinst.
  • Glömd max_seq_length. Om värdet inte matchar din faktiska datalängd trunkeras exempel mitt i meningen, vilket skadar träningskvaliteten utan att ge något tydligt felmeddelande du kan agera på direkt.
  • Exportera adaptern istället för den sammanslagna modellen. Många glömmer save_pretrained_merged-steget och undrar sedan varför GGUF-filen bara innehåller basmodellens beteende och ingenting från träningen.
  • Utvärdering på träningsdata. Testar du modellen på samma exempel den tränats på får du missvisande bra resultat som inte håller i produktion när riktiga användare ställer nya frågor.
  • Fel kvantiseringsnivå för hårdvaran. En Q8_0-fil på en bärbar dator med begränsat RAM leder till att operativsystemet börjar swappa, vilket gör modellen orimligt långsam trots att filen i sig fungerar korrekt.

Felsökning: vanliga fel och lösningar

Stöter du på problem under installationen, träningen eller exporten, kolla igenom listan nedan innan du söker vidare på egen hand. De flesta av dessa fel har en enkel, konkret orsak som går att åtgärda på några minuter.

  • CUDA out of memory under träning. Minska per_device_train_batch_size, öka gradient_accumulation_steps istället för att kompensera, eller sänk max_seq_length om dina exempel inte kräver full längd.
  • ImportError kopplat till bitsandbytes. Oftast en versionskonflikt mellan torch och bitsandbytes. Installera om i en helt ren venv och matcha exakt de versioner Unsloth anger för din CUDA-version.
  • Träningsförlusten blir NaN. Sänk inlärningshastigheten, kontrollera att datasetet inte innehåller tomma eller korrupta rader, och slå på gradient clipping om det inte redan är aktiverat.
  • Modellen ignorerar instruktioner efter träning. Kontrollera chattmallen igen, och se till att systemrollen (om du använder en) är konsekvent formulerad i alla träningsexempel.
  • GGUF-exporten misslyckas eller blir orimligt stor. Kör save_pretrained_merged före save_pretrained_gguf, och kontrollera att du har tillräckligt diskutrymme för mellansteget innan du startar om.
  • “unsupported architecture” i llama.cpp. Uppdatera llama.cpp till senaste version, eftersom stöd för nya modellarkitekturer läggs till löpande i projektet.
  • Träningen är långsam trots kraftfull GPU. Kontrollera att bf16 eller fp16 faktiskt är aktiverat, och att gradient checkpointing är satt till “unsloth” snarare än standardläget i transformers.
  • lm-evaluation-harness hittar inte modellkonfigurationen. Peka explicit på katalogen med den sammanslagna modellen, inte adaptermappen, i --model_args, annars saknas filer harnessen förväntar sig.
  • Tokeniseraren matchar inte efter sammanslagning. Spara alltid tokeniseraren tillsammans med modellen i samma steg som du kör save_pretrained_merged, aldrig separat i efterhand.

Avancerade tips: DPO, GRPO och flera GPU:er

När grundläggande övervakad finjustering sitter, finns det flera vägar vidare. Unsloth stöder sedan 2026.9.14 även DPO (Direct Preference Optimization) och GRPO, två tekniker som tränar modellen på jämförelser mellan bra och dåliga svar istället för enbart exempel på rätt svar. Det är särskilt användbart om du vill forma modellens ton, minska oönskade svar, eller få den att hålla sig till ett specifikt format mer konsekvent, snarare än att bara lära den helt ny fakta.

DPO kräver par av svar, ett som föredras och ett som inte gör det, för samma fråga. GRPO fungerar mer som ett förstärkningsinlärningssteg där modellen genererar flera svarsförslag och belönas för de bättre, vilket passar uppgifter där det är svårare att definiera ett enskilt “rätt” svar men lättare att rangordna flera alternativ mot varandra.

Ett praktiskt sätt att komma igång med DPO utan att bygga en helt ny pipeline är att återanvända modellen du redan tränat med SFTTrainer som startpunkt, och sedan lägga en kortare DPO-körning ovanpå den med ett mindre dataset av preferenspar. Många team ser bättre resultat av den kombinationen, sekventiell SFT följt av en lätt DPO-polering, än av att försöka lösa allt i ett enda träningssteg.

För större modeller eller snabbare iteration kan du skala till flera GPU:er, men var medveten om att QLoRA:s minnesfördelar delvis försvinner när du lägger till distribuerad träning, eftersom varje enhet fortfarande behöver sin egen kopia av basmodellen. FP8-stöd, som också finns i den aktuella Unsloth-versionen, kan ge ytterligare hastighetsvinster på nyare GPU-arkitekturer, men kräver att både drivrutiner och hårdvara stödjer formatet fullt ut. Testa alltid FP8 på en liten körning innan du litar på det i ett längre träningsjobb, så att du upptäcker eventuella kompatibilitetsproblem tidigt.

Komplett projekt: från rådata till körbar GGUF-modell

Sätter du ihop alla steg ovan får du en komplett, repeterbar process. Installera Unsloth, ladda en 4-bitars basmodell, formatera ditt JSONL-dataset med rätt chattmall, konfigurera LoRA med rank 16, träna i tre epoker, utvärdera med lm-evaluation-harness, slå ihop adaptern och exportera till GGUF med Q4_K_M-kvantisering. Hela kedjan går att köra som ett enda Python-skript plus två terminalkommandon, och för en 7-8B-modell med ett par tusen träningsexempel tar hela processen vanligtvis under två timmar på ett modernt konsumentkort, inklusive installation.

Som en snabb checklista innan du anser projektet klart: kontrollera att träningsloss har planat ut, att den finjusterade modellen presterar bättre än basmodellen på ditt egna lilla benchmark, att GGUF-filen går att ladda i llama.cpp utan fel, och att svaren håller samma kvalitet efter kvantisering som i den sammanslagna 16-bitars versionen. Klarar modellen alla fyra punkterna är den redo att testas av riktiga användare i en kontrollerad miljö.

Resultatet är en fristående GGUF-fil som du kan versionshantera, flytta mellan maskiner och köra helt offline, utan beroende av någon extern API-leverantör. Det är precis den typen av kontroll över både data och modell som gör egen finjustering värd investeringen för team som arbetar med känslig information, eller som helt enkelt vill äga hela sin AI-stack från data till inferens.

Vanliga frågor

Behöver jag ett dyrt GPU-kort för att testa det här?
Nej. För en modell i 7-8B-klassen räcker ofta ett konsumentkort med 8-10 GB VRAM när du använder QLoRA i 4-bitars läge. Större modeller kräver mer minne, men du behöver sällan ett datacenterkort bara för att komma igång och lära dig processen.

Hur mycket data behöver jag för att se en skillnad?
Det varierar med uppgiften, men några hundra till några tusen väl formaterade exempel ger ofta en mätbar förbättring, förutsatt att kvaliteten och konsekvensen i formatet är hög genom hela datasetet.

Kan jag finjustera flera modeller i rad med samma dataset?
Ja. Så länge chattmallen anpassas till respektive basmodell går samma datasetformat att återanvända för olika modellfamiljer utan att du behöver skriva om själva träningsdatan.

Är QLoRA sämre än full finjustering?
Inte nödvändigtvis sämre, men annorlunda. QLoRA ger mindre flexibilitet att ändra modellens grundläggande beteende men betydligt lägre risk för överanpassning och ett mycket lägre hårdvarukrav för samma typ av uppgift.

Vilken GGUF-kvantisering ska jag välja som standard?
Q4_K_M är en bra startpunkt för de flesta lokala applikationer. Om kvaliteten inte räcker för din uppgift, testa Q5_K_M eller Q6_K innan du går hela vägen till Q8_0.

Måste jag köra lm-evaluation-harness, eller räcker manuell testning?
Manuell testning fångar uppenbara fel, men ett objektivt benchmark ger dig siffror att jämföra mot nästa gång du ändrar data eller hyperparametrar. Det blir särskilt viktigt när du itererar flera gånger på samma projekt över tid.

Kan jag servera den finjusterade modellen för flera användare samtidigt?
Ja, men då bör du använda vLLM snarare än llama.cpp för enkel lokal testning. vLLM är byggt för att hantera flera samtidiga förfrågningar effektivt på samma GPU utan att kön av väntande förfrågningar växer okontrollerat.

Vad gör jag om resultatet blir sämre än basmodellen?
Backa till en tidigare checkpoint, sänk inlärningshastigheten, och granska datasetet igen för inkonsekvent formatering. Ett för litet eller för rörigt dataset är den vanligaste orsaken till att en finjusterad modell presterar sämre än originalet på generella uppgifter.

Fungerar samma process för modeller på andra språk än engelska?
Ja, så länge basmodellen redan har viss täckning av språket i fråga. Finjustering lär modellen ett format, en ton eller ett sakområde, men den bygger inte in ett helt nytt språk från noll. För svenska fungerar flerspråkiga basmodeller som redan tränats på nordiska texter betydligt bättre som utgångspunkt än en modell som enbart sett engelska under förträningen.