<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: finetuning</title>
    <description>The latest articles tagged 'finetuning' on DEV Community.</description>
    <link>https://dev.to/t/finetuning</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/finetuning"/>
    <language>en</language>
    <item>
      <title>Fine-Tuning LLMs with LoRA &amp; QLoRA: Complete Production Guide</title>
      <dc:creator>Phil IPS</dc:creator>
      <pubDate>Mon, 12 Oct 2026 21:48:24 +0000</pubDate>
      <link>https://dev.to/ipsphil_digital/fine-tuning-llms-with-lora-qlora-complete-production-guide-3gf2</link>
      <guid>https://dev.to/ipsphil_digital/fine-tuning-llms-with-lora-qlora-complete-production-guide-3gf2</guid>
      <description>&lt;h1&gt;
  
  
  Fine-Tuning LLMs with LoRA &amp;amp; QLoRA: Complete Practical Guide
&lt;/h1&gt;

&lt;p&gt;Master parameter-efficient fine-tuning to adapt any open-source LLM to your specific domain, style, or task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Full fine-tuning requires massive compute. &lt;strong&gt;LoRA (Low-Rank Adaptation)&lt;/strong&gt; and &lt;strong&gt;QLoRA (Quantized LoRA)&lt;/strong&gt; change the game — fine-tune 7B-70B models on consumer GPUs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why LoRA/QLoRA?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Trainable Params&lt;/th&gt;
&lt;th&gt;VRAM (7B)&lt;/th&gt;
&lt;th&gt;Quality&lt;/th&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full FT&lt;/td&gt;
&lt;td&gt;100% (7B)&lt;/td&gt;
&lt;td&gt;~48 GB&lt;/td&gt;
&lt;td&gt;Best&lt;/td&gt;
&lt;td&gt;A100/H100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA (r=64)&lt;/td&gt;
&lt;td&gt;~0.5% (35M)&lt;/td&gt;
&lt;td&gt;~16 GB&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;RTX 3090/4090&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA (r=64)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~0.5% (35M)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~8 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Excellent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RTX 3060 12GB+&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA (r=16)&lt;/td&gt;
&lt;td&gt;~0.1% (7M)&lt;/td&gt;
&lt;td&gt;~12 GB&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA (r=16)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~0.1% (7M)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~6 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Good&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RTX 3060 8GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;QLoRA = LoRA + 4-bit quantization + paged optimizers + gradient checkpointing&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Core dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;torch torchvision torchaudio &lt;span class="nt"&gt;--index-url&lt;/span&gt; https://download.pytorch.org/whl/cu121
pip &lt;span class="nb"&gt;install &lt;/span&gt;transformers accelerate peft bitsandbytes trl datasets

&lt;span class="c"&gt;# For quantization&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;auto-gptq optimum

&lt;span class="c"&gt;# Optional: Weights &amp;amp; Biases for logging&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;wandb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding LoRA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Math
&lt;/h3&gt;

&lt;p&gt;Instead of updating all weights $W \in \mathbb{R}^{d \times d}$, LoRA learns two small matrices:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
W' = W + \Delta W = W + BA&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$B \in \mathbb{R}^{d \times r}$ (down-projection)&lt;/li&gt;
&lt;li&gt;$A \in \mathbb{R}^{r \times d}$ (up-projection)&lt;/li&gt;
&lt;li&gt;$r \ll d$ (rank, typically 8-64)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Only $A$ and $B$ are trained. $W$ stays frozen.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Target Modules
&lt;/h3&gt;

&lt;p&gt;Which layers to adapt? Research shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# For LLaMA/Mistral/Gemma architecture
&lt;/span&gt;&lt;span class="n"&gt;TARGET_MODULES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Attention
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# MLP
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# For smaller models or less VRAM, use attention only
&lt;/span&gt;&lt;span class="n"&gt;TARGET_MODULES_ATTENTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data Preparation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Format: Instruction Tuning (Alpaca/ShareGPT)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;alpaca_format.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write a Python function to calculate fibonacci numbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"def fibonacci(n):&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    if n &amp;lt;= 1:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        return n&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    return fibonacci(n-1) + fibonacci(n-2)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Explain the concept of recursion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"In the context of computer science"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recursion is a programming technique where a function calls itself..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Format: Conversation (ChatML)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;chatml_format.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a helpful coding assistant."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"How do I handle errors in Go?"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"In Go, errors are values..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data Processing Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# data_prep.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_and_format_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpaca&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpaca&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_alpaca&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Input:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format_alpaca&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chatml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_chatml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c1"&gt;# Apply chat template
&lt;/span&gt;            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format_chatml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tokenize_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;truncation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batched&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remove_columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column_names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Train/val split
&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_and_format_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;train_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;eval_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LoRA Fine-Tuning with Hugging Face TRL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete Training Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# train_lora.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BitsAndBytesConfig&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prepare_model_for_kbit_training&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SFTTrainer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# ============ CONFIGURATION ============
&lt;/span&gt;&lt;span class="n"&gt;MODEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Or mistralai/Mistral-7B-Instruct-v0.3
&lt;/span&gt;&lt;span class="n"&gt;DATASET_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./llama-3.2-3b-lora-finetuned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;
&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;GRAD_ACCUM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;  &lt;span class="c1"&gt;# Effective batch = 16
&lt;/span&gt;&lt;span class="n"&gt;LEARNING_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2e-4&lt;/span&gt;
&lt;span class="n"&gt;NUM_EPOCHS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;LORA_R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="n"&gt;LORA_ALPHA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="n"&gt;LORA_DROPOUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;

&lt;span class="c1"&gt;# ============ QUANTIZATION CONFIG (QLoRA) ============
&lt;/span&gt;&lt;span class="n"&gt;bnb_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BitsAndBytesConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;load_in_4bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_quant_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nf4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# NormalFloat 4-bit
&lt;/span&gt;    &lt;span class="n"&gt;bnb_4bit_compute_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_use_double_quant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# Double quantization
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ LoRA CONFIG ============
&lt;/span&gt;&lt;span class="n"&gt;lora_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LORA_R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lora_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LORA_ALPHA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;lora_dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LORA_DROPOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CAUSAL_LM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# For QLoRA: modules_to_save=["embed_tokens", "lm_head"]  # If extending vocab
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ LOAD MODEL &amp;amp; TOKENIZER ============
&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eos_token&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;padding_side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;quantization_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bnb_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attn_implementation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flash_attention_2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Requires flash-attn
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Prepare for k-bit training
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prepare_model_for_kbit_training&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lora_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_trainable_parameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Example output: trainable params: 35,651,584 || all params: 3,200,000,000 || trainable%: 1.11%
&lt;/span&gt;
&lt;span class="c1"&gt;# ============ LOAD DATA ============
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formatting_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DATASET_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ TRAINING ARGS ============
&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_train_epochs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;NUM_EPOCHS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;per_device_eval_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GRAD_ACCUM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LEARNING_RATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lr_scheduler_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cosine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;warmup_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;logging_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;eval_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;eval_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_total_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_best_model_at_end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metric_for_best_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eval_loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bf16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tf32&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_checkpointing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_checkpointing_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;use_reentrant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paged_adamw_32bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# QLoRA optimizer
&lt;/span&gt;    &lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wandb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# or "tensorboard" or "none"
&lt;/span&gt;    &lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3.2-3b-lora-r&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LORA_R&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ddp_find_unused_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ TRAINER ============
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;eval_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;formatting_func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;formatting_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;packing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Efficient packing of sequences
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ TRAIN ============
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save final model
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model saved to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Launch Training
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Single GPU&lt;/span&gt;
python train_lora.py

&lt;span class="c"&gt;# Multi-GPU with Accelerate&lt;/span&gt;
accelerate config  &lt;span class="c"&gt;# First time setup&lt;/span&gt;
accelerate launch train_lora.py

&lt;span class="c"&gt;# With DeepSpeed ZeRO-3 (for larger models)&lt;/span&gt;
accelerate launch &lt;span class="nt"&gt;--config_file&lt;/span&gt; ds_zero3.yaml train_lora.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  QLoRA Specific Optimizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Paged Optimizers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Automatically used with optim="paged_adamw_32bit"
# Moves optimizer states to CPU when not in use
# Enables larger models on same VRAM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gradient Checkpointing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In TrainingArguments:
&lt;/span&gt;&lt;span class="n"&gt;gradient_checkpointing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;gradient_checkpointing_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;use_reentrant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="c1"&gt;# Saves ~30% VRAM at cost of ~20% slower training
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flash Attention 2
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;flash-attn &lt;span class="nt"&gt;--no-build-isolation&lt;/span&gt;

&lt;span class="c"&gt;# Use in model loading&lt;/span&gt;
&lt;span class="nv"&gt;attn_implementation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flash_attention_2"&lt;/span&gt;

&lt;span class="c"&gt;# 2-3x faster, less memory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring Training
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weights &amp;amp; Biases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In TrainingArguments
&lt;/span&gt;&lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wandb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-lora-run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="c1"&gt;# Before training
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wandb&lt;/span&gt;
&lt;span class="n"&gt;wandb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm-finetuning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3b-lora-r64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TensorBoard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start tensorboard&lt;/span&gt;
tensorboard &lt;span class="nt"&gt;--logdir&lt;/span&gt; ./llama-3.2-3b-lora-finetuned/runs

&lt;span class="c"&gt;# In TrainingArguments&lt;/span&gt;
&lt;span class="nv"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tensorboard"&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Metrics to Watch
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Healthy Range&lt;/th&gt;
&lt;th&gt;Warning Signs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;train_loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decreasing steadily&lt;/td&gt;
&lt;td&gt;Plateauing early, diverging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;eval_loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decreasing, close to train&lt;/td&gt;
&lt;td&gt;Increasing (overfitting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;learning_rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cosine decay to 0&lt;/td&gt;
&lt;td&gt;Stuck, not decaying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;grad_norm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 1.0&lt;/td&gt;
&lt;td&gt;&amp;gt; 10 (exploding gradients)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GPU memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;Growing (memory leak)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Merging &amp;amp; Exporting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Merge LoRA Weights
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# merge_lora.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="n"&gt;BASE_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LORA_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./llama-3.2-3b-lora-finetuned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./llama-3.2-3b-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Load base model in fp16/bf16 (not 4-bit)
&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;BASE_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load LoRA adapter
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LORA_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Merge and unload
&lt;/span&gt;&lt;span class="n"&gt;merged_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge_and_unload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save merged model
&lt;/span&gt;&lt;span class="n"&gt;merged_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Merged model saved to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Export to GGUF (for llama.cpp/Ollama)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install llama.cpp with Python bindings&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;llama-cpp-python

&lt;span class="c"&gt;# Convert using llama.cpp convert script&lt;/span&gt;
python llama.cpp/convert_hf_to_gguf.py &lt;span class="se"&gt;\&lt;/span&gt;
  ./llama-3.2-3b-merged &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outfile&lt;/span&gt; ./llama-3.2-3b-finetuned.Q4_K_M.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outtype&lt;/span&gt; q4_k_m

&lt;span class="c"&gt;# Or use llama.cpp quantize directly&lt;/span&gt;
./llama.cpp/quantize ./llama-3.2-3b-merged/model.fp16.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  ./llama-3.2-3b-finetuned.Q4_K_M.gguf q4_k_m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Ollama Modelfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Modelfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ./llama-3.2-3b-finetuned.Q4_K_M.gguf&lt;/span&gt;

TEMPLATE "{{ .System }}\n{{ .Prompt }}\n{{ .Response }}"

PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create Ollama model&lt;/span&gt;
ollama create my-finetuned-model &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile

&lt;span class="c"&gt;# Test&lt;/span&gt;
ollama run my-finetuned-model &lt;span class="s2"&gt;"Write a Python function for binary search"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced: LoRA Hyperparameter Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rank (r) Selection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Experiment with different ranks
&lt;/span&gt;&lt;span class="n"&gt;RANK_CONFIGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;    &lt;span class="c1"&gt;# Minimal, fast, less capacity
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# Good balance
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# More capacity
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# High capacity (alpha often = r/4 to r/2)
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# Maximum capacity
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Target Module Ablation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Test different target module sets
&lt;/span&gt;&lt;span class="n"&gt;TARGET_CONFIGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mlp_only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all_linear&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_mlp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# Key ones
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Learning Rate Schedule
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# For LoRA, higher LR often works (2e-4 to 1e-3)
# But depends on dataset size:
# Small dataset (&amp;lt;1k): 1e-4 to 5e-4
# Medium (1k-10k): 2e-4
# Large (&amp;gt;10k): 1e-4 to 2e-4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Evaluation &amp;amp; Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quantitative Evaluation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# eval_model.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_data_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;base_model_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;base_model_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Load test data
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_data_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;test_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;test_data&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;  &lt;span class="c1"&gt;# Sample 50
&lt;/span&gt;        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;do_sample&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;pad_token_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eos_token_id&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:],&lt;/span&gt; &lt;span class="n"&gt;skip_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# Save for human evaluation
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eval_results.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Qualitative Evaluation (Human/LLM Judge)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# judge_eval.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;llm_judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generated&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Rate the quality of the generated response compared to expected.

Instruction: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Expected: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Generated: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;generated&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Rate 1-5 on:
1. Accuracy
2. Completeness  
3. Clarity
4. Relevance

Return JSON: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completeness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Run evaluation
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eval_results.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completeness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm_judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Issues &amp;amp; Solutions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OOM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model too large&lt;/td&gt;
&lt;td&gt;Increase gradient_accumulation, reduce batch_size, use QLoRA, gradient_checkpointing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loss not decreasing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LR too low/high&lt;/td&gt;
&lt;td&gt;Try 2e-4, check data format, verify tokenization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overfitting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Too many epochs, small data&lt;/td&gt;
&lt;td&gt;Reduce epochs, add dropout, more data, weight decay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gibberish output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wrong chat template&lt;/td&gt;
&lt;td&gt;Match training format exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NaN loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unstable training&lt;/td&gt;
&lt;td&gt;Reduce LR, add gradient clipping, check data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Slow training&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No Flash Attention&lt;/td&gt;
&lt;td&gt;Install flash-attn, use bf16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Training Cost (Estimates)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;Time (1k samples, 3 epochs)&lt;/th&gt;
&lt;th&gt;Cost (Cloud)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3B&lt;/td&gt;
&lt;td&gt;LoRA&lt;/td&gt;
&lt;td&gt;RTX 3090 (24GB)&lt;/td&gt;
&lt;td&gt;~30 min&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;RTX 3060 (12GB)&lt;/td&gt;
&lt;td&gt;~45 min&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;LoRA&lt;/td&gt;
&lt;td&gt;RTX 3090 (24GB)&lt;/td&gt;
&lt;td&gt;~1 hr&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;RTX 3090 (24GB)&lt;/td&gt;
&lt;td&gt;~1.5 hr&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;2× RTX 3090&lt;/td&gt;
&lt;td&gt;~3 hr&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;4× A100 (80GB)&lt;/td&gt;
&lt;td&gt;~8 hr&lt;/td&gt;
&lt;td&gt;$40+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Local training on owned hardware = $0 marginal cost&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Quality &amp;gt; Quantity&lt;/strong&gt;: 1k high-quality &amp;gt; 100k noisy&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Deduplicate&lt;/strong&gt;: Remove exact/near duplicates&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Balance&lt;/strong&gt;: Cover all target domains evenly&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Format consistency&lt;/strong&gt;: Match training format exactly at inference&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Training
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Start small&lt;/strong&gt;: Test with r=16, 1 epoch first&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Monitor eval_loss&lt;/strong&gt;: Stop when it plateaus/increases&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Save checkpoints&lt;/strong&gt;: Every 100-500 steps&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Use cosine LR&lt;/strong&gt;: With warmup (3-5% of steps)&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Pack sequences&lt;/strong&gt;: &lt;code&gt;packing=True&lt;/code&gt; in SFTTrainer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  LoRA Config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;r=64, alpha=16&lt;/strong&gt; for most cases&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Target all linear layers&lt;/strong&gt; for best quality&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Dropout 0.05-0.1&lt;/strong&gt; for regularization&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Don't quantize LoRA weights&lt;/strong&gt; (keep in bf16/fp16)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Merge weights&lt;/strong&gt; for inference speed&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Quantize merged model&lt;/strong&gt; to GGUF Q4_K_M&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Test inference&lt;/strong&gt; with same prompt format&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Benchmark latency&lt;/strong&gt; before production&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Complete Example: Domain Adaptation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Complete workflow for adapting LLaMA-3.2-3B to medical QA
# 1. Collect 2k high-quality medical QA pairs
# 2. Format as Alpaca
# 3. Train with QLoRA r=64, 3 epochs
# 4. Evaluate on held-out medical questions
# 5. Merge, quantize to GGUF, deploy via Ollama
&lt;/span&gt;
&lt;span class="c1"&gt;# Expected: 90%+ accuracy on medical specialty vs 60% base model
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer"&gt;LoRA Paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2305.14314" rel="noopener noreferrer"&gt;QLoRA Paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/docs/peft" rel="noopener noreferrer"&gt;PEFT Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/docs/trl/sft_trainer" rel="noopener noreferrer"&gt;TRL SFTTrainer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/TimDettmers/bitsandbytes" rel="noopener noreferrer"&gt;BitsAndBytes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/unslothai/unsloth" rel="noopener noreferrer"&gt;Unsloth (Faster Training)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/axolotl-ai-cloud/axolotl" rel="noopener noreferrer"&gt;Axolotl (Config-Based Training)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Fine-tuning is no longer reserved for big tech. With QLoRA, a $300 GPU can customize a 7B model to beat GPT-3.5 on your specific task. Start small, iterate fast, deploy locally.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>finetuning</category>
      <category>lora</category>
      <category>qlora</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Fine-Tuning LLMs with LoRA &amp; QLoRA: Complete Practical Guide</title>
      <dc:creator>Phil IPS</dc:creator>
      <pubDate>Mon, 12 Oct 2026 17:31:02 +0000</pubDate>
      <link>https://dev.to/ipsphil_digital/fine-tuning-llms-with-lora-qlora-complete-practical-guide-560k</link>
      <guid>https://dev.to/ipsphil_digital/fine-tuning-llms-with-lora-qlora-complete-practical-guide-560k</guid>
      <description>&lt;h1&gt;
  
  
  Fine-Tuning LLMs with LoRA &amp;amp; QLoRA: Complete Practical Guide
&lt;/h1&gt;

&lt;p&gt;Master parameter-efficient fine-tuning to adapt any open-source LLM to your specific domain, style, or task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Full fine-tuning requires massive compute. &lt;strong&gt;LoRA (Low-Rank Adaptation)&lt;/strong&gt; and &lt;strong&gt;QLoRA (Quantized LoRA)&lt;/strong&gt; change the game — fine-tune 7B-70B models on consumer GPUs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why LoRA/QLoRA?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Trainable Params&lt;/th&gt;
&lt;th&gt;VRAM (7B)&lt;/th&gt;
&lt;th&gt;Quality&lt;/th&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Full FT&lt;/td&gt;
&lt;td&gt;100% (7B)&lt;/td&gt;
&lt;td&gt;~48 GB&lt;/td&gt;
&lt;td&gt;Best&lt;/td&gt;
&lt;td&gt;A100/H100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA (r=64)&lt;/td&gt;
&lt;td&gt;~0.5% (35M)&lt;/td&gt;
&lt;td&gt;~16 GB&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;RTX 3090/4090&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA (r=64)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~0.5% (35M)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~8 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Excellent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RTX 3060 12GB+&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA (r=16)&lt;/td&gt;
&lt;td&gt;~0.1% (7M)&lt;/td&gt;
&lt;td&gt;~12 GB&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA (r=16)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~0.1% (7M)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~6 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Good&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;RTX 3060 8GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;QLoRA = LoRA + 4-bit quantization + paged optimizers + gradient checkpointing&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Core dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;torch torchvision torchaudio &lt;span class="nt"&gt;--index-url&lt;/span&gt; https://download.pytorch.org/whl/cu121
pip &lt;span class="nb"&gt;install &lt;/span&gt;transformers accelerate peft bitsandbytes trl datasets

&lt;span class="c"&gt;# For quantization&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;auto-gptq optimum

&lt;span class="c"&gt;# Optional: Weights &amp;amp; Biases for logging&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;wandb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding LoRA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Math
&lt;/h3&gt;

&lt;p&gt;Instead of updating all weights $W \in \mathbb{R}^{d \times d}$, LoRA learns two small matrices:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
W' = W + \Delta W = W + BA&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$B \in \mathbb{R}^{d \times r}$ (down-projection)&lt;/li&gt;
&lt;li&gt;$A \in \mathbb{R}^{r \times d}$ (up-projection)&lt;/li&gt;
&lt;li&gt;$r \ll d$ (rank, typically 8-64)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Only $A$ and $B$ are trained. $W$ stays frozen.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Target Modules
&lt;/h3&gt;

&lt;p&gt;Which layers to adapt? Research shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# For LLaMA/Mistral/Gemma architecture
&lt;/span&gt;&lt;span class="n"&gt;TARGET_MODULES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Attention
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# MLP
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# For smaller models or less VRAM, use attention only
&lt;/span&gt;&lt;span class="n"&gt;TARGET_MODULES_ATTENTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data Preparation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Format: Instruction Tuning (Alpaca/ShareGPT)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;alpaca_format.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write a Python function to calculate fibonacci numbers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"def fibonacci(n):&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    if n &amp;lt;= 1:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;        return n&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    return fibonacci(n-1) + fibonacci(n-2)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Explain the concept of recursion"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"In the context of computer science"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recursion is a programming technique where a function calls itself..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Format: Conversation (ChatML)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;chatml_format.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a helpful coding assistant."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"How do I handle errors in Go?"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"In Go, errors are values..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data Processing Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# data_prep.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_and_format_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpaca&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpaca&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_alpaca&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Input:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format_alpaca&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chatml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_chatml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c1"&gt;# Apply chat template
&lt;/span&gt;            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;format_chatml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tokenize_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;truncation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batched&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remove_columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column_names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Train/val split
&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_and_format_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;train_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;eval_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LoRA Fine-Tuning with Hugging Face TRL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Complete Training Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# train_lora.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BitsAndBytesConfig&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prepare_model_for_kbit_training&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SFTTrainer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# ============ CONFIGURATION ============
&lt;/span&gt;&lt;span class="n"&gt;MODEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Or mistralai/Mistral-7B-Instruct-v0.3
&lt;/span&gt;&lt;span class="n"&gt;DATASET_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./llama-3.2-3b-lora-finetuned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;
&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;GRAD_ACCUM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;  &lt;span class="c1"&gt;# Effective batch = 16
&lt;/span&gt;&lt;span class="n"&gt;LEARNING_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2e-4&lt;/span&gt;
&lt;span class="n"&gt;NUM_EPOCHS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;LORA_R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;
&lt;span class="n"&gt;LORA_ALPHA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="n"&gt;LORA_DROPOUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;

&lt;span class="c1"&gt;# ============ QUANTIZATION CONFIG (QLoRA) ============
&lt;/span&gt;&lt;span class="n"&gt;bnb_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BitsAndBytesConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;load_in_4bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_quant_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nf4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# NormalFloat 4-bit
&lt;/span&gt;    &lt;span class="n"&gt;bnb_4bit_compute_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_use_double_quant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# Double quantization
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ LoRA CONFIG ============
&lt;/span&gt;&lt;span class="n"&gt;lora_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LORA_R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lora_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LORA_ALPHA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;lora_dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LORA_DROPOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CAUSAL_LM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# For QLoRA: modules_to_save=["embed_tokens", "lm_head"]  # If extending vocab
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ LOAD MODEL &amp;amp; TOKENIZER ============
&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eos_token&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;padding_side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;quantization_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bnb_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attn_implementation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flash_attention_2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Requires flash-attn
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Prepare for k-bit training
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prepare_model_for_kbit_training&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lora_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_trainable_parameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Example output: trainable params: 35,651,584 || all params: 3,200,000,000 || trainable%: 1.11%
&lt;/span&gt;
&lt;span class="c1"&gt;# ============ LOAD DATA ============
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formatting_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DATASET_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ TRAINING ARGS ============
&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_train_epochs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;NUM_EPOCHS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;per_device_eval_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GRAD_ACCUM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LEARNING_RATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lr_scheduler_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cosine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;warmup_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;logging_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;eval_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;eval_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_total_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_best_model_at_end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;metric_for_best_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eval_loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bf16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tf32&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_checkpointing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_checkpointing_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;use_reentrant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paged_adamw_32bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# QLoRA optimizer
&lt;/span&gt;    &lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wandb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# or "tensorboard" or "none"
&lt;/span&gt;    &lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3.2-3b-lora-r&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LORA_R&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ddp_find_unused_parameters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ TRAINER ============
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;eval_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;formatting_func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;formatting_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;packing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Efficient packing of sequences
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ============ TRAIN ============
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save final model
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model saved to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Launch Training
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Single GPU&lt;/span&gt;
python train_lora.py

&lt;span class="c"&gt;# Multi-GPU with Accelerate&lt;/span&gt;
accelerate config  &lt;span class="c"&gt;# First time setup&lt;/span&gt;
accelerate launch train_lora.py

&lt;span class="c"&gt;# With DeepSpeed ZeRO-3 (for larger models)&lt;/span&gt;
accelerate launch &lt;span class="nt"&gt;--config_file&lt;/span&gt; ds_zero3.yaml train_lora.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  QLoRA Specific Optimizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Paged Optimizers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Automatically used with optim="paged_adamw_32bit"
# Moves optimizer states to CPU when not in use
# Enables larger models on same VRAM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gradient Checkpointing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In TrainingArguments:
&lt;/span&gt;&lt;span class="n"&gt;gradient_checkpointing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;gradient_checkpointing_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;use_reentrant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="c1"&gt;# Saves ~30% VRAM at cost of ~20% slower training
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flash Attention 2
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;flash-attn &lt;span class="nt"&gt;--no-build-isolation&lt;/span&gt;

&lt;span class="c"&gt;# Use in model loading&lt;/span&gt;
&lt;span class="nv"&gt;attn_implementation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flash_attention_2"&lt;/span&gt;

&lt;span class="c"&gt;# 2-3x faster, less memory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring Training
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Weights &amp;amp; Biases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In TrainingArguments
&lt;/span&gt;&lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wandb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-lora-run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="c1"&gt;# Before training
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wandb&lt;/span&gt;
&lt;span class="n"&gt;wandb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm-finetuning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3b-lora-r64&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TensorBoard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start tensorboard&lt;/span&gt;
tensorboard &lt;span class="nt"&gt;--logdir&lt;/span&gt; ./llama-3.2-3b-lora-finetuned/runs

&lt;span class="c"&gt;# In TrainingArguments&lt;/span&gt;
&lt;span class="nv"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tensorboard"&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Metrics to Watch
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Healthy Range&lt;/th&gt;
&lt;th&gt;Warning Signs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;train_loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decreasing steadily&lt;/td&gt;
&lt;td&gt;Plateauing early, diverging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;eval_loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decreasing, close to train&lt;/td&gt;
&lt;td&gt;Increasing (overfitting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;learning_rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cosine decay to 0&lt;/td&gt;
&lt;td&gt;Stuck, not decaying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;grad_norm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 1.0&lt;/td&gt;
&lt;td&gt;&amp;gt; 10 (exploding gradients)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GPU memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;td&gt;Growing (memory leak)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Merging &amp;amp; Exporting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Merge LoRA Weights
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# merge_lora.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="n"&gt;BASE_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LORA_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./llama-3.2-3b-lora-finetuned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./llama-3.2-3b-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Load base model in fp16/bf16 (not 4-bit)
&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;BASE_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load LoRA adapter
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LORA_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Merge and unload
&lt;/span&gt;&lt;span class="n"&gt;merged_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge_and_unload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save merged model
&lt;/span&gt;&lt;span class="n"&gt;merged_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Merged model saved to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Export to GGUF (for llama.cpp/Ollama)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install llama.cpp with Python bindings&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;llama-cpp-python

&lt;span class="c"&gt;# Convert using llama.cpp convert script&lt;/span&gt;
python llama.cpp/convert_hf_to_gguf.py &lt;span class="se"&gt;\&lt;/span&gt;
  ./llama-3.2-3b-merged &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outfile&lt;/span&gt; ./llama-3.2-3b-finetuned.Q4_K_M.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outtype&lt;/span&gt; q4_k_m

&lt;span class="c"&gt;# Or use llama.cpp quantize directly&lt;/span&gt;
./llama.cpp/quantize ./llama-3.2-3b-merged/model.fp16.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  ./llama-3.2-3b-finetuned.Q4_K_M.gguf q4_k_m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Ollama Modelfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Modelfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ./llama-3.2-3b-finetuned.Q4_K_M.gguf&lt;/span&gt;

TEMPLATE "{{ .System }}\n{{ .Prompt }}\n{{ .Response }}"

PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create Ollama model&lt;/span&gt;
ollama create my-finetuned-model &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile

&lt;span class="c"&gt;# Test&lt;/span&gt;
ollama run my-finetuned-model &lt;span class="s2"&gt;"Write a Python function for binary search"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced: LoRA Hyperparameter Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rank (r) Selection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Experiment with different ranks
&lt;/span&gt;&lt;span class="n"&gt;RANK_CONFIGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;    &lt;span class="c1"&gt;# Minimal, fast, less capacity
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# Good balance
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# More capacity
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# High capacity (alpha often = r/4 to r/2)
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# Maximum capacity
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Target Module Ablation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Test different target module sets
&lt;/span&gt;&lt;span class="n"&gt;TARGET_CONFIGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mlp_only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all_linear&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_mlp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# Key ones
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Learning Rate Schedule
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# For LoRA, higher LR often works (2e-4 to 1e-3)
# But depends on dataset size:
# Small dataset (&amp;lt;1k): 1e-4 to 5e-4
# Medium (1k-10k): 2e-4
# Large (&amp;gt;10k): 1e-4 to 2e-4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Evaluation &amp;amp; Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quantitative Evaluation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# eval_model.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;evaluate_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_data_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;base_model_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;base_model_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Load test data
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_data_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;test_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;test_data&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;  &lt;span class="c1"&gt;# Sample 50
&lt;/span&gt;        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;do_sample&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;pad_token_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eos_token_id&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:],&lt;/span&gt; &lt;span class="n"&gt;skip_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# Save for human evaluation
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eval_results.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Qualitative Evaluation (Human/LLM Judge)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# judge_eval.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;llm_judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generated&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Rate the quality of the generated response compared to expected.

Instruction: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Expected: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Generated: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;generated&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Rate 1-5 on:
1. Accuracy
2. Completeness  
3. Clarity
4. Relevance

Return JSON: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completeness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1-5, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Run evaluation
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eval_results.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completeness&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clarity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm_judge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generated&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Issues &amp;amp; Solutions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OOM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model too large&lt;/td&gt;
&lt;td&gt;Increase gradient_accumulation, reduce batch_size, use QLoRA, gradient_checkpointing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loss not decreasing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LR too low/high&lt;/td&gt;
&lt;td&gt;Try 2e-4, check data format, verify tokenization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overfitting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Too many epochs, small data&lt;/td&gt;
&lt;td&gt;Reduce epochs, add dropout, more data, weight decay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gibberish output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wrong chat template&lt;/td&gt;
&lt;td&gt;Match training format exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NaN loss&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unstable training&lt;/td&gt;
&lt;td&gt;Reduce LR, add gradient clipping, check data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Slow training&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No Flash Attention&lt;/td&gt;
&lt;td&gt;Install flash-attn, use bf16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cost Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Training Cost (Estimates)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;Time (1k samples, 3 epochs)&lt;/th&gt;
&lt;th&gt;Cost (Cloud)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3B&lt;/td&gt;
&lt;td&gt;LoRA&lt;/td&gt;
&lt;td&gt;RTX 3090 (24GB)&lt;/td&gt;
&lt;td&gt;~30 min&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;RTX 3060 (12GB)&lt;/td&gt;
&lt;td&gt;~45 min&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;LoRA&lt;/td&gt;
&lt;td&gt;RTX 3090 (24GB)&lt;/td&gt;
&lt;td&gt;~1 hr&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;RTX 3090 (24GB)&lt;/td&gt;
&lt;td&gt;~1.5 hr&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;2× RTX 3090&lt;/td&gt;
&lt;td&gt;~3 hr&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70B&lt;/td&gt;
&lt;td&gt;QLoRA&lt;/td&gt;
&lt;td&gt;4× A100 (80GB)&lt;/td&gt;
&lt;td&gt;~8 hr&lt;/td&gt;
&lt;td&gt;$40+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Local training on owned hardware = $0 marginal cost&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Quality &amp;gt; Quantity&lt;/strong&gt;: 1k high-quality &amp;gt; 100k noisy&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Deduplicate&lt;/strong&gt;: Remove exact/near duplicates&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Balance&lt;/strong&gt;: Cover all target domains evenly&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Format consistency&lt;/strong&gt;: Match training format exactly at inference&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Training
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Start small&lt;/strong&gt;: Test with r=16, 1 epoch first&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Monitor eval_loss&lt;/strong&gt;: Stop when it plateaus/increases&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Save checkpoints&lt;/strong&gt;: Every 100-500 steps&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Use cosine LR&lt;/strong&gt;: With warmup (3-5% of steps)&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Pack sequences&lt;/strong&gt;: &lt;code&gt;packing=True&lt;/code&gt; in SFTTrainer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  LoRA Config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;r=64, alpha=16&lt;/strong&gt; for most cases&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Target all linear layers&lt;/strong&gt; for best quality&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Dropout 0.05-0.1&lt;/strong&gt; for regularization&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Don't quantize LoRA weights&lt;/strong&gt; (keep in bf16/fp16)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Merge weights&lt;/strong&gt; for inference speed&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Quantize merged model&lt;/strong&gt; to GGUF Q4_K_M&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Test inference&lt;/strong&gt; with same prompt format&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Benchmark latency&lt;/strong&gt; before production&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Complete Example: Domain Adaptation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Complete workflow for adapting LLaMA-3.2-3B to medical QA
# 1. Collect 2k high-quality medical QA pairs
# 2. Format as Alpaca
# 3. Train with QLoRA r=64, 3 epochs
# 4. Evaluate on held-out medical questions
# 5. Merge, quantize to GGUF, deploy via Ollama
&lt;/span&gt;
&lt;span class="c1"&gt;# Expected: 90%+ accuracy on medical specialty vs 60% base model
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer"&gt;LoRA Paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2305.14314" rel="noopener noreferrer"&gt;QLoRA Paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/docs/peft" rel="noopener noreferrer"&gt;PEFT Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://huggingface.co/docs/trl/sft_trainer" rel="noopener noreferrer"&gt;TRL SFTTrainer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/TimDettmers/bitsandbytes" rel="noopener noreferrer"&gt;BitsAndBytes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/unslothai/unsloth" rel="noopener noreferrer"&gt;Unsloth (Faster Training)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/axolotl-ai-cloud/axolotl" rel="noopener noreferrer"&gt;Axolotl (Config-Based Training)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Fine-tuning is no longer reserved for big tech. With QLoRA, a $300 GPU can customize a 7B model to beat GPT-3.5 on your specific task. Start small, iterate fast, deploy locally.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lora</category>
      <category>qlora</category>
      <category>finetuning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Fine-Tuning vs RAG vs Prompt Engineering: When to Use Which</title>
      <dc:creator>Phil IPS</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:50:29 +0000</pubDate>
      <link>https://dev.to/ipsphil_digital/fine-tuning-vs-rag-vs-prompt-engineering-when-to-use-which-520f</link>
      <guid>https://dev.to/ipsphil_digital/fine-tuning-vs-rag-vs-prompt-engineering-when-to-use-which-520f</guid>
      <description>&lt;h1&gt;
  
  
  Fine-Tuning vs RAG vs Prompt Engineering: When to Use Which
&lt;/h1&gt;

&lt;p&gt;A practical decision framework for choosing the right LLM customization approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Three main ways to adapt LLMs to your needs. Each has different cost, complexity, and effectiveness. This guide helps you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Decision Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Prompt Engineering&lt;/th&gt;
&lt;th&gt;RAG&lt;/th&gt;
&lt;th&gt;Fine-Tuning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minutes&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Days-Weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0 (API only)&lt;/td&gt;
&lt;td&gt;$0-50/mo&lt;/td&gt;
&lt;td&gt;$50-5000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Documents&lt;/td&gt;
&lt;td&gt;Labeled examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;td&gt;Re-index&lt;/td&gt;
&lt;td&gt;Retrain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hallucination fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;✅ Strong&lt;/td&gt;
&lt;td&gt;✅ Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain knowledge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Style/format control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;✅ Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency added&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;~100-500ms&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expertise needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reversibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trivial&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Approach 1: Prompt Engineering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Crafting input instructions to guide model behavior without changing the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quick experiments&lt;/li&gt;
&lt;li&gt;General knowledge tasks&lt;/li&gt;
&lt;li&gt;Style/format control&lt;/li&gt;
&lt;li&gt;One-off or varied tasks&lt;/li&gt;
&lt;li&gt;No proprietary data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Techniques
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Zero-shot
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a SQL query for...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Few-shot
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Examples:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Input: ...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Output: ...&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Now: ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Chain-of-thought
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Think step by step: ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Structured output
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Return ONLY valid JSON: {...}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Role + Context
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a senior engineer. Context: ... Task: ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros/Cons
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Pros&lt;/th&gt;
&lt;th&gt;❌ Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instant iteration&lt;/td&gt;
&lt;td&gt;Limited by context window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No training cost&lt;/td&gt;
&lt;td&gt;Can't learn new facts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works on any model&lt;/td&gt;
&lt;td&gt;Inconsistent on complex tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version controllable&lt;/td&gt;
&lt;td&gt;Prompt injection risk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API calls only&lt;/strong&gt;: ~$0.001-0.01 per request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No infrastructure&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Approach 2: RAG (Retrieval-Augmented Generation)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Ground model responses in your documents by retrieving relevant chunks at query time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query → Embed → Vector Search → Top-K Chunks → LLM + Context → Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Large document collections (100+ docs)&lt;/li&gt;
&lt;li&gt;Frequently changing data&lt;/li&gt;
&lt;li&gt;Need citations/sources&lt;/li&gt;
&lt;li&gt;Regulatory compliance (audit trail)&lt;/li&gt;
&lt;li&gt;Multiple domains in one system&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Levels
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Level 1: Basic (Chroma + OpenAI)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 50 lines, runs locally
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chromadb&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chromadb.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;embedding_functions&lt;/span&gt;

&lt;span class="n"&gt;ef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;embedding_functions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SentenceTransformerEmbeddingFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all-MiniLM-L6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chromadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PersistentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding_function&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadatas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;metas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_texts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;n_results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Level 2: Production (Qdrant + Reranker)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Hybrid search + cross-encoder reranking
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;qdrant_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QdrantClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sentence_transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CrossEncoder&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;QdrantClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reranker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CrossEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cross-encoder/ms-marco-MiniLM-L-6-v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Retrieve 20, rerank to 5
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reranker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;top5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Level 3: Advanced (Agentic RAG)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Multi-hop reasoning, tool use
# Query → Decompose → Retrieve per subquery → Synthesize
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros/Cons
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Pros&lt;/th&gt;
&lt;th&gt;❌ Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grounded in your data&lt;/td&gt;
&lt;td&gt;Added latency (retrieval)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Easy updates (re-index)&lt;/td&gt;
&lt;td&gt;Chunking strategy matters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Citations built-in&lt;/td&gt;
&lt;td&gt;Retrieval can miss context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No training needed&lt;/td&gt;
&lt;td&gt;Embedding quality critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works with any LLM&lt;/td&gt;
&lt;td&gt;Context window limits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Monthly (1M vectors)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vector DB (Qdrant Cloud)&lt;/td&gt;
&lt;td&gt;$0-150&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embeddings (OpenRouter)&lt;/td&gt;
&lt;td&gt;$0-20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM calls&lt;/td&gt;
&lt;td&gt;$10-100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$10-270&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Approach 3: Fine-Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Update model weights on your data to bake in knowledge, style, or behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Params Trained&lt;/th&gt;
&lt;th&gt;VRAM (7B)&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full FT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;80 GB+&lt;/td&gt;
&lt;td&gt;Research, max quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.1-1%&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;Most production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.1-1%&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;Consumer GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prefix/Adapter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;0.1%&lt;/td&gt;
&lt;td&gt;6 GB&lt;/td&gt;
&lt;td&gt;Style only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When to Use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Consistent style/format requirements&lt;/li&gt;
&lt;li&gt;Domain-specific terminology (legal, medical, code)&lt;/li&gt;
&lt;li&gt;High-volume, repetitive tasks&lt;/li&gt;
&lt;li&gt;Model must work offline&lt;/li&gt;
&lt;li&gt;Regulatory: model = artifact&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data Requirements
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Alpaca&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(instruction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tuning)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write a Python function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"calculate fibonacci"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"def fib(n):..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Chat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are a Python expert"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write a fibonacci function"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"def fib(n):..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Preference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pairs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(DPO)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"chosen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"better response"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rejected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"worse response"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Minimums:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LoRA: 500-1000 quality examples&lt;/li&gt;
&lt;li&gt;Full FT: 10K-100K+&lt;/li&gt;
&lt;li&gt;DPO: 1K-5K pairs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Training Pipeline (QLoRA)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Prepare data&lt;/span&gt;
python prepare_data.py &lt;span class="nt"&gt;--input&lt;/span&gt; raw.jsonl &lt;span class="nt"&gt;--output&lt;/span&gt; train.jsonl &lt;span class="nt"&gt;--val-size&lt;/span&gt; 100

&lt;span class="c"&gt;# 2. Train (4-bit, 7B model, 1x A100 40GB)&lt;/span&gt;
python train.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; meta-llama/Llama-3.1-8B-Instruct &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; train.jsonl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; ./lora-adapter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lora-r&lt;/span&gt; 64 &lt;span class="nt"&gt;--lora-alpha&lt;/span&gt; 16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--batch-size&lt;/span&gt; 2 &lt;span class="nt"&gt;--grad-accum&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lr&lt;/span&gt; 2e-4 &lt;span class="nt"&gt;--steps&lt;/span&gt; 500 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--quantization&lt;/span&gt; 4bit

&lt;span class="c"&gt;# 3. Merge &amp;amp; export&lt;/span&gt;
python merge.py &lt;span class="nt"&gt;--base&lt;/span&gt; meta-llama/Llama-3.1-8B-Instruct &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--adapter&lt;/span&gt; ./lora-adapter &lt;span class="nt"&gt;--output&lt;/span&gt; ./merged-model

&lt;span class="c"&gt;# 4. Convert to GGUF&lt;/span&gt;
python convert-hf-to-gguf.py ./merged-model &lt;span class="nt"&gt;--outfile&lt;/span&gt; model.q4_k_m.gguf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros/Cons
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Pros&lt;/th&gt;
&lt;th&gt;❌ Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Best task performance&lt;/td&gt;
&lt;td&gt;High upfront cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No retrieval latency&lt;/td&gt;
&lt;td&gt;Hard to update&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works offline&lt;/td&gt;
&lt;td&gt;Catastrophic forgetting risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bakes in style/format&lt;/td&gt;
&lt;td&gt;Needs ML expertise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single artifact to deploy&lt;/td&gt;
&lt;td&gt;GPU required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scale&lt;/th&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Est. Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7B QLoRA&lt;/td&gt;
&lt;td&gt;1× A100 40GB&lt;/td&gt;
&lt;td&gt;2-4 hrs&lt;/td&gt;
&lt;td&gt;$10-30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13B QLoRA&lt;/td&gt;
&lt;td&gt;1× A100 80GB&lt;/td&gt;
&lt;td&gt;4-8 hrs&lt;/td&gt;
&lt;td&gt;$40-80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70B QLoRA&lt;/td&gt;
&lt;td&gt;4× A100 80GB&lt;/td&gt;
&lt;td&gt;8-24 hrs&lt;/td&gt;
&lt;td&gt;$300-1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B Full FT&lt;/td&gt;
&lt;td&gt;8× A100 80GB&lt;/td&gt;
&lt;td&gt;12-48 hrs&lt;/td&gt;
&lt;td&gt;$1000-5000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Decision Framework
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;START: What's your primary need?
│
├─→ "Control output format/style" 
│   └─→ **Prompt Engineering** (try first, 15 min)
│
├─→ "Answer from my documents/data"
│   ├─→ Data changes frequently?
│   │   ├─→ Yes → **RAG** (re-index on change)
│   │   └─→ No → Continue
│   ├─→ Need citations?
│   │   └─→ Yes → **RAG**
│   ├─→ &amp;lt; 100 docs?
│   │   └─→ Yes → **RAG** (Chroma local)
│   └─→ &amp;gt; 10K docs, high query volume?
│       └─→ **RAG** (Qdrant/Milvus) or **Fine-tune** if style critical
│
├─→ "Model must know domain deeply"
│   ├─→ Can you get 500+ quality examples?
│   │   ├─→ Yes → **Fine-tune (QLoRA)**
│   │   └─→ No → **RAG + few-shot prompts**
│   ├─→ Need offline/air-gapped?
│   │   └─→ Yes → **Fine-tune** (deploy GGUF)
│   └─→ High volume (&amp;gt;10K req/day)?
│       └─→ **Fine-tune** (cheaper at scale)
│
└─→ "Reduce hallucinations on facts"
    ├─→ Facts in documents?
    │   └─→ Yes → **RAG**
    └─→ Facts = training knowledge?
        └─→ **Fine-tune** or better prompts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Hybrid Approaches (Real World)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. RAG + Fine-Tuned Embedder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fine-tune embedder on your domain, use with RAG
# Better retrieval → better answers
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. RAG + Fine-Tuned Generator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fine-tune small model (7B) for style/format
# Use RAG for knowledge
# Best of both worlds
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Prompt → RAG → Fine-Tune (Progression)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Week 1: Prompt engineering (validate concept)
Week 2: Add RAG (ground in data)
Week 3: Fine-tune if needed (optimize)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Router Pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Route queries to best approach
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_factual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rag_chain&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;is_creative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prompt_chain&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;is_coding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;finetuned_coder&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;default_chain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Cost Comparison at Scale
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;1K req/mo&lt;/th&gt;
&lt;th&gt;100K req/mo&lt;/th&gt;
&lt;th&gt;1M req/mo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prompt (API)&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;td&gt;$500&lt;/td&gt;
&lt;td&gt;$5,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG (managed)&lt;/td&gt;
&lt;td&gt;$30&lt;/td&gt;
&lt;td&gt;$300&lt;/td&gt;
&lt;td&gt;$2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fine-tuned (self-host)&lt;/td&gt;
&lt;td&gt;$100*&lt;/td&gt;
&lt;td&gt;$100*&lt;/td&gt;
&lt;td&gt;$100*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*Fixed GPU cost&lt;/td&gt;
&lt;td&gt;(A100)&lt;/td&gt;
&lt;td&gt;(A100)&lt;/td&gt;
&lt;td&gt;(4×A100)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Break-even:&lt;/strong&gt; Fine-tuning pays off at ~50K req/mo if you own GPUs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Reality&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Fine-tune for facts"&lt;/td&gt;
&lt;td&gt;Use RAG; FT memorizes poorly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"RAG solves everything"&lt;/td&gt;
&lt;td&gt;Bad retrieval = bad answers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Prompt engineering is easy"&lt;/td&gt;
&lt;td&gt;Requires systematic testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Bigger model = better"&lt;/td&gt;
&lt;td&gt;7B FT &amp;gt; 70B prompt for specific tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"One approach fits all"&lt;/td&gt;
&lt;td&gt;Hybrid usually wins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Evaluation Checklist
&lt;/h2&gt;

&lt;p&gt;Before committing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Defined success metrics (accuracy, latency, cost)&lt;/li&gt;
&lt;li&gt;[ ] Created test set (50+ representative queries)&lt;/li&gt;
&lt;li&gt;[ ] Benchmarked current approach&lt;/li&gt;
&lt;li&gt;[ ] Estimated total cost (dev + infra + ongoing)&lt;/li&gt;
&lt;li&gt;[ ] Defined update strategy (how often, how)&lt;/li&gt;
&lt;li&gt;[ ] Considered compliance/privacy needs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  My Recommendations by Use Case
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Primary&lt;/th&gt;
&lt;th&gt;Secondary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Customer support bot&lt;/td&gt;
&lt;td&gt;RAG&lt;/td&gt;
&lt;td&gt;Fine-tune style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code assistant&lt;/td&gt;
&lt;td&gt;Fine-tune (7B coder)&lt;/td&gt;
&lt;td&gt;RAG for docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legal document analysis&lt;/td&gt;
&lt;td&gt;RAG&lt;/td&gt;
&lt;td&gt;Fine-tune format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creative writing&lt;/td&gt;
&lt;td&gt;Prompt engineering&lt;/td&gt;
&lt;td&gt;Fine-tune voice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL generation&lt;/td&gt;
&lt;td&gt;Fine-tune (7B)&lt;/td&gt;
&lt;td&gt;RAG for schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chat with PDFs&lt;/td&gt;
&lt;td&gt;RAG&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API documentation bot&lt;/td&gt;
&lt;td&gt;RAG&lt;/td&gt;
&lt;td&gt;Prompt for format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal knowledge base&lt;/td&gt;
&lt;td&gt;RAG&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline mobile app&lt;/td&gt;
&lt;td&gt;Fine-tune (GGUF)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-volume classification&lt;/td&gt;
&lt;td&gt;Fine-tune (small)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with prompt engineering&lt;/strong&gt; — 15 minutes, validates the task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add RAG&lt;/strong&gt; when you have documents and need grounded answers — hours to set up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fine-tune&lt;/strong&gt; only when: you have 500+ examples, need consistent style/format, high volume, or offline deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most production systems use hybrids&lt;/strong&gt;: RAG for knowledge + fine-tuned model for behavior.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Part of the LLM Customization series on Dev.to&lt;/em&gt;&lt;/p&gt;

</description>
      <category>finetuning</category>
      <category>rag</category>
      <category>promptengineering</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Fine-Tuning LLMs on Consumer Hardware: LoRA Guide 2024</title>
      <dc:creator>Phil IPS</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:23:44 +0000</pubDate>
      <link>https://dev.to/ipsphil_digital/fine-tuning-llms-on-consumer-hardware-lora-guide-2024-2i73</link>
      <guid>https://dev.to/ipsphil_digital/fine-tuning-llms-on-consumer-hardware-lora-guide-2024-2i73</guid>
      <description>&lt;h1&gt;
  
  
  Fine-Tuning LLMs on Consumer Hardware: LoRA Guide 2024
&lt;/h1&gt;

&lt;p&gt;Train custom models on your GPU using LoRA/QLoRA — no cloud credits needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Fine-tuning used to require A100s and thousands of dollars. With LoRA (Low-Rank Adaptation) and QLoRA (Quantized LoRA), you can fine-tune 7B-70B models on a single 24GB GPU (RTX 3090/4090) or even 12GB with quantization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LoRA?
&lt;/h2&gt;

&lt;p&gt;LoRA freezes the base model and trains only small adapter matrices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base Model (frozen) + LoRA Adapters (trainable) = Fine-tuned Model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100-1000x fewer trainable parameters&lt;/li&gt;
&lt;li&gt;Fast training (minutes to hours)&lt;/li&gt;
&lt;li&gt;Small checkpoint files (MBs not GBs)&lt;/li&gt;
&lt;li&gt;Mergeable back into base model&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hardware Requirements
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model Size&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;VRAM Needed&lt;/th&gt;
&lt;th&gt;GPU Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;LoRA (fp16)&lt;/td&gt;
&lt;td&gt;~16 GB&lt;/td&gt;
&lt;td&gt;RTX 3090/4090&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;QLoRA (4-bit)&lt;/td&gt;
&lt;td&gt;~8 GB&lt;/td&gt;
&lt;td&gt;RTX 3060 12GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13B&lt;/td&gt;
&lt;td&gt;QLoRA (4-bit)&lt;/td&gt;
&lt;td&gt;~12 GB&lt;/td&gt;
&lt;td&gt;RTX 3080 12GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;33B&lt;/td&gt;
&lt;td&gt;QLoRA (4-bit)&lt;/td&gt;
&lt;td&gt;~20 GB&lt;/td&gt;
&lt;td&gt;RTX 3090/4090&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70B&lt;/td&gt;
&lt;td&gt;QLoRA (4-bit)&lt;/td&gt;
&lt;td&gt;~40 GB&lt;/td&gt;
&lt;td&gt;Dual 3090/4090&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Setup Environment
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create environment&lt;/span&gt;
conda create &lt;span class="nt"&gt;-n&lt;/span&gt; lora &lt;span class="nv"&gt;python&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.10 &lt;span class="nt"&gt;-y&lt;/span&gt;
conda activate lora

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;torch torchvision torchaudio &lt;span class="nt"&gt;--index-url&lt;/span&gt; https://download.pytorch.org/whl/cu121
pip &lt;span class="nb"&gt;install &lt;/span&gt;transformers peft trl accelerate bitsandbytes datasets wandb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prepare Dataset
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;data/train.jsonl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Write a Python function to reverse a string&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;def reverse_string(s):&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;    return s[::-1]"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"### Instruction:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Explain what a decorator does in Python&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;### Response:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;A decorator is a function that takes another function and extends its behavior..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Format: &lt;strong&gt;Alpaca/ShareGPT style&lt;/strong&gt; with &lt;code&gt;### Instruction:&lt;/code&gt; and &lt;code&gt;### Response:&lt;/code&gt; markers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training Script (QLoRA)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# train_lora.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;BitsAndBytesConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TrainingArguments&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prepare_model_for_kbit_training&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SFTTrainer&lt;/span&gt;

&lt;span class="c1"&gt;# Config
&lt;/span&gt;&lt;span class="n"&gt;MODEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mistralai/Mistral-7B-v0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# or "meta-llama/Llama-2-7b-hf"
&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./mistral-7b-code-lora&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DATA_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/train.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# 4-bit quantization config
&lt;/span&gt;&lt;span class="n"&gt;bnb_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BitsAndBytesConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;load_in_4bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_quant_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nf4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_compute_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bnb_4bit_use_double_quant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load model
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;quantization_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bnb_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prepare_model_for_kbit_training&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# LoRA config
&lt;/span&gt;&lt;span class="n"&gt;lora_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;# Rank (higher = more capacity)
&lt;/span&gt;    &lt;span class="n"&gt;lora_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# Scaling factor
&lt;/span&gt;    &lt;span class="n"&gt;target_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;lora_dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CAUSAL_LM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lora_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print_trainable_parameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Trainable params: ~0.5% of total
&lt;/span&gt;
&lt;span class="c1"&gt;# Tokenizer
&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eos_token&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;padding_side&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;right&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Dataset
&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DATA_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Training arguments
&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;warmup_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# Adjust for your dataset
&lt;/span&gt;    &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2e-4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fp16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;logging_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;save_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wandb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# or "none"
&lt;/span&gt;    &lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paged_adamw_8bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Trainer
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dataset_text_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;packing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Train
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save
&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model saved to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run Training
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Single GPU&lt;/span&gt;
python train_lora.py

&lt;span class="c"&gt;# Multi-GPU (if available)&lt;/span&gt;
accelerate launch &lt;span class="nt"&gt;--multi_gpu&lt;/span&gt; train_lora.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Merge &amp;amp; Export
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# merge_lora.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="n"&gt;BASE_MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mistralai/Mistral-7B-v0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LORA_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./mistral-7b-code-lora&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./mistral-7b-code-merged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Load base
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;BASE_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load &amp;amp; merge LoRA
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LORA_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge_and_unload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save merged
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE_MODEL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Convert to GGUF (llama.cpp)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install llama.cpp&lt;/span&gt;
git clone https://github.com/ggerganov/llama.cpp
&lt;span class="nb"&gt;cd &lt;/span&gt;llama.cpp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make

&lt;span class="c"&gt;# Convert&lt;/span&gt;
python convert-hf-to-gguf.py ../mistral-7b-code-merged &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outfile&lt;/span&gt; ../mistral-7b-code.Q4_K_M.gguf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--outtype&lt;/span&gt; q4_k_m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Inference with Ollama
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create Modelfile&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Modelfile &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
FROM ./mistral-7b-code.Q4_K_M.gguf
TEMPLATE "{{ .System }}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;{{ .Prompt }}"
PARAMETER temperature 0.7
PARAMETER top_p 0.9
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Create model&lt;/span&gt;
ollama create mistral-code &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile

&lt;span class="c"&gt;# Test&lt;/span&gt;
ollama run mistral-code &lt;span class="s2"&gt;"Write a Python decorator for timing functions"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cost Breakdown (RTX 3090 24GB)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Electricity (4 hrs @ 350W)&lt;/td&gt;
&lt;td&gt;~$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU depreciation&lt;/td&gt;
&lt;td&gt;~$1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total per run&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$1.20&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;vs. Cloud (A100 40GB): &lt;strong&gt;$1.50-3.00/hr&lt;/strong&gt; → $6-12 for same training.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips for Success
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dataset quality &amp;gt; quantity&lt;/strong&gt; — 500 clean examples beat 5000 noisy ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start small&lt;/strong&gt; — 100 steps, check loss, then scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor VRAM&lt;/strong&gt; — Reduce batch size if OOM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use gradient checkpointing&lt;/strong&gt; — &lt;code&gt;model.gradient_checkpointing_enable()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eval regularly&lt;/strong&gt; — Keep a validation set&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common Issues
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CUDA OOM&lt;/td&gt;
&lt;td&gt;Reduce batch size, increase grad accumulation, use 4-bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loss not decreasing&lt;/td&gt;
&lt;td&gt;Increase learning rate, check data format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NaN loss&lt;/td&gt;
&lt;td&gt;Lower learning rate, add gradient clipping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow training&lt;/td&gt;
&lt;td&gt;Enable flash attention, use &lt;code&gt;packing=True&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;LoRA/QLoRA democratized LLM fine-tuning. With a consumer GPU, you can create domain-specialized models for coding, writing, analysis — whatever your use case. Start with 7B QLoRA, iterate on data, and deploy locally via Ollama or llama.cpp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; Try DPO/ORPO for preference alignment, or merge multiple LoRAs for multi-task models.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Part of the LLM Fine-Tuning series on Dev.to&lt;/em&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>finetuning</category>
      <category>lora</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I Learned Trying to Ship a Fine-Tuned Model to the NPU</title>
      <dc:creator>Jaydeep Shah (JD)</dc:creator>
      <pubDate>Wed, 07 Oct 2026 04:18:40 +0000</pubDate>
      <link>https://dev.to/jdshah/what-i-learned-trying-to-ship-a-fine-tuned-model-to-the-npu-edo</link>
      <guid>https://dev.to/jdshah/what-i-learned-trying-to-ship-a-fine-tuned-model-to-the-npu-edo</guid>
      <description>&lt;p&gt;There is a quiet assumption in the on-device AI ecosystem: that the models shipping from Hugging Face and litert-community are good enough. Drop them into your app, write a good system prompt, and you are done.&lt;/p&gt;

&lt;p&gt;I believed that too, until I benchmarked it.&lt;/p&gt;

&lt;p&gt;This post is about what I learned when a strong prompt and an under-resourced fine-tune went head to head, why task-aligned fine-tuning still matters for serious on-device apps, and why the toolchain makes it hard to ship fine-tuned models to the hardware where they would matter most.&lt;/p&gt;




&lt;h2&gt;
  
  
  General models are general. Your app is not.
&lt;/h2&gt;

&lt;p&gt;Gemma 4 E2B is a strong general-purpose model. Instruction-tuned, INT4-quantized, 2.59 GB - it runs comfortably on a phone GPU at about 25 tokens per second. For chatbot-style apps, it works out of the box.&lt;/p&gt;

&lt;p&gt;But Redacto is not a chatbot. It is a zero-trust PII/PHI redaction engine with 7 specialized document categories: medical records, police reports, financial statements, journalism source notes, field service logs, legal filings, and a general fallback. Each category has its own detection rules. A patient's daughter's name is PHI in a medical record. A suspect's description is explicitly &lt;em&gt;not&lt;/em&gt; PII in a police report - it needs to be preserved. These distinctions are domain-specific, and a general model does not know them by default.&lt;/p&gt;

&lt;p&gt;Prompt engineering gets you surprisingly far. With carefully crafted system prompts, explicit preserve lists, and the structured &lt;code&gt;[CATEGORY_N]&lt;/code&gt; output format, the standard Gemma 4 E2B model scores 80.5% overall on our 85-entry benchmark suite. That is real and usable.&lt;/p&gt;

&lt;p&gt;But there are domains where prompt engineering hits a ceiling.&lt;/p&gt;




&lt;h2&gt;
  
  
  The numbers, and what they do and do not prove
&lt;/h2&gt;

&lt;p&gt;We ran a head-to-head benchmark: standard Gemma 4 E2B versus a QLoRA fine-tuned variant, both on GPU, same device (Galaxy S25 Ultra), same 85-entry dataset across all 5 modes (the benchmark covers 5 of the 7 document categories; legal filings and the general fallback were not part of this run).&lt;/p&gt;

&lt;p&gt;The overall scores tell one story - the fine-tuned model scored 70.3% versus the standard model's 80.5%, largely because of an output format mismatch (the fine-tuned model was trained on generic &lt;code&gt;[REDACTED]&lt;/code&gt; labels, not Redacto's &lt;code&gt;[CATEGORY_N]&lt;/code&gt; format). That is a training data problem, not a fine-tuning problem. In other words, a strong prompt on the stock model beat a quick, under-resourced fine-tune overall.&lt;/p&gt;

&lt;p&gt;A caveat before the table: every number here comes from a single directional benchmarking session in May 2026 on one Galaxy S25 Ultra. No raw per-run logs were saved and the device is no longer available, so treat these as directional signals, not a rigorous multi-run study.&lt;/p&gt;

&lt;p&gt;The per-mode entity recall tells a different story:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Standard&lt;/th&gt;
&lt;th&gt;Fine-tuned&lt;/th&gt;
&lt;th&gt;Delta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TACTICAL&lt;/td&gt;
&lt;td&gt;63.7%&lt;/td&gt;
&lt;td&gt;76.8%&lt;/td&gt;
&lt;td&gt;+13.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FIELD_SERVICE&lt;/td&gt;
&lt;td&gt;82.1%&lt;/td&gt;
&lt;td&gt;95.3%&lt;/td&gt;
&lt;td&gt;+13.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FINANCIAL&lt;/td&gt;
&lt;td&gt;83.8%&lt;/td&gt;
&lt;td&gt;85.5%&lt;/td&gt;
&lt;td&gt;+1.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JOURNALISM&lt;/td&gt;
&lt;td&gt;71.1%&lt;/td&gt;
&lt;td&gt;61.3%&lt;/td&gt;
&lt;td&gt;-9.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HIPAA&lt;/td&gt;
&lt;td&gt;95.7%&lt;/td&gt;
&lt;td&gt;39.9%&lt;/td&gt;
&lt;td&gt;-55.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Look at TACTICAL. The standard model catches 63.7% of entities - victims, witnesses, minors - that should be redacted. The fine-tuned model catches 76.8%. That is a 13-point improvement.&lt;/p&gt;

&lt;p&gt;For a police report, that 13% gap is the difference between protecting a victim's identity and exposing it. An app that misses 1 in 3 names is not a privacy tool. It is a liability.&lt;/p&gt;

&lt;p&gt;FIELD_SERVICE shows the same pattern: +13.2%, driven by the fine-tuned model's ability to recognize contextual security information - "the key is under the mat," WiFi passwords embedded in natural language, gate codes in conversational text. These are not standard PII patterns. A general model does not look for them. A fine-tuned model does.&lt;/p&gt;

&lt;p&gt;The HIPAA regression (-55.8%) and JOURNALISM regression (-9.8%) are instructive too. They show that fine-tuning on generic PII data actively hurts domains where the standard model's specialized prompt engineering is already strong. The standard model's HIPAA system prompt was specifically engineered for relational PHI - "the patient's daughter Lisa" - which the fine-tuned model's generic training data does not cover. The lesson is not "don't fine-tune." The lesson is "fine-tune on your app's actual task data, with your app's actual output format." If we retrained with Redacto's system prompts and &lt;code&gt;[CATEGORY_N]&lt;/code&gt; labels, the gains in TACTICAL and FIELD_SERVICE would likely hold while the regressions would disappear. Both quantity and alignment of the training data matter here, and this run was short on both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Consistency is the hidden killer
&lt;/h2&gt;

&lt;p&gt;An 80% entity recall score sounds decent in a benchmark table. In production, it means 1 in 5 sensitive identifiers leaks through. Every time.&lt;/p&gt;

&lt;p&gt;Users do not experience averages. They experience individual documents. A medical provider using Redacto to redact a discharge summary before faxing it to an insurance company does not care that the model works 80% of the time. They care that &lt;em&gt;this&lt;/em&gt; document, &lt;em&gt;right now&lt;/em&gt;, is clean.&lt;/p&gt;

&lt;p&gt;This is why task-aligned fine-tuning matters for apps where accuracy matters. The jump from 63.7% to 76.8% on TACTICAL is not just a benchmark improvement - it is the difference between an app that is trustworthy and one that is not. And with proper task-specific training data, those numbers should climb further.&lt;/p&gt;




&lt;h2&gt;
  
  
  The toolchain works - up to a point
&lt;/h2&gt;

&lt;p&gt;Here is the fine-tuning pipeline, step by step, with what actually happens at each stage:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: QLoRA on Colab.&lt;/strong&gt; Works. We fine-tuned Gemma 4 E2B using QLoRA (LoRA rank 8, alpha 16, dropout 0.05) on 3,000 examples from the ai4privacy/pii-masking-400k dataset. Trainable parameters: 2,850,816 - just 0.06% of the model. Training time on an NVIDIA RTX PRO 6000: 217 seconds. Final loss: 4.3064. The tooling here is mature. &lt;code&gt;peft&lt;/code&gt;, &lt;code&gt;transformers&lt;/code&gt;, &lt;code&gt;bitsandbytes&lt;/code&gt; - it all works.&lt;/p&gt;

&lt;p&gt;It is worth being honest about what this fine-tune was: 3,000 of the roughly 400,000 samples in ai4privacy/pii-masking-400k, a single epoch, only a few minutes of actual training (about 217 seconds, though wrestling the toolchain end to end took the better part of a day), and trained on generic &lt;code&gt;[REDACTED]&lt;/code&gt; labels rather than Redacto's &lt;code&gt;[CATEGORY_N]&lt;/code&gt; output format. That is an under-resourced and partly misaligned run, not a serious training effort. So the results above are best read as "what a strong prompt did against a quick, under-resourced fine-tune," not as the ceiling of what fine-tuning can do. With the full dataset and task-aligned labels, the overall picture could flip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Export to .litertlm.&lt;/strong&gt; Works, with a trap. The &lt;code&gt;litert_torch.generative.export_hf&lt;/code&gt; tool converts the merged weights to a quantized &lt;code&gt;.litertlm&lt;/code&gt; bundle. But there is an undocumented incompatibility: the Gemma 4 chat template from Hugging Face uses &lt;code&gt;map.get()&lt;/code&gt; in Jinja, which LiteRT-LM's on-device template parser does not support. The error - &lt;code&gt;Failed to apply template: unknown method: map has no method named get&lt;/code&gt; - will stop every developer who tries this pipeline. The fix: swap in Gemma 3's chat template before export. Not obvious. Not documented. But it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: GPU inference.&lt;/strong&gt; This is where the story gets muddy. In our notes the fine-tuned &lt;code&gt;.litertlm&lt;/code&gt; loaded on the Adreno 830 GPU and ran inference at 9.0 tokens per second (slower than the standard model's roughly 25 tok/s, which we attributed to the fine-tuned export being 4.7 GB versus the standard bundle's 2.59 GB - a quantization granularity difference, not a fundamental limitation). The fine-tuned export is 4.7 GB and GPU-only; it is too large and the wrong shape to compile for the NPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: NPU inference.&lt;/strong&gt; Blocked.&lt;/p&gt;




&lt;h2&gt;
  
  
  The wall
&lt;/h2&gt;

&lt;p&gt;The standard Gemma 4 E2B model runs on the Hexagon V79 NPU at about 42 tokens per second - roughly 1.7x faster than GPU. Because NPU decode is memory-bandwidth-bound, that rate stays roughly flat rather than scaling with much else, which is exactly what you would expect. Time to first token drops from 366ms to 92ms. For a redaction app processing sensitive documents, that speed difference is the gap between a noticeable pause and an instant response.&lt;/p&gt;

&lt;p&gt;But the standard model was compiled for the NPU by Qualcomm's hardware team. The &lt;code&gt;.litertlm&lt;/code&gt; file at litert-community contains a QNN-prepared bundle (QNN being Qualcomm AI Engine Direct) with custom &lt;code&gt;DISPATCH_OP&lt;/code&gt; operations compiled specifically for the Hexagon V79 DSP on the Snapdragon 8 Elite. This compilation step - converting quantized weights into a QNN graph that the NPU can execute - requires the AIMET (AI Model Efficiency Toolkit) and an ahead-of-time (AoT) QNN toolchain.&lt;/p&gt;

&lt;p&gt;That toolchain is not part of the public LiteRT-LM SDK.&lt;/p&gt;

&lt;p&gt;Community projects have since demonstrated ahead-of-time QNN compilation for LLMs through their own toolchains, which suggests the gap is specific to the LiteRT-LM export path rather than QNN itself. For developers working inside the LiteRT-LM pipeline, though, the wall is the same.&lt;/p&gt;

&lt;p&gt;This is not an engineering shortcut we missed. This is a hardware-team integration boundary. The tools required to compile a model into a QNN-prepared NPU bundle are internal to Qualcomm's AI stack. Developers can download the Qualcomm AI Engine Direct SDK and access QNN APIs for classical ML models, but the specific pipeline for compiling LLM-class models - with their attention mechanisms, KV caches, and multi-head architectures - into Hexagon-optimized graphs is not publicly available for independent use on arbitrary fine-tuned weights.&lt;/p&gt;

&lt;p&gt;The net result, at least in our attempt: a fine-tuned Gemma 4 E2B that showed a double-digit TACTICAL entity-recall gain in a directional benchmark, but that we could not compile for the NPU. The gap between "I improved my model" and "my users benefit from it on the fastest hardware" is hard to bridge today without vendor support.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this means for every developer
&lt;/h2&gt;

&lt;p&gt;This is not a Redacto-specific problem. Every developer who needs domain-specific accuracy is likely to hit this wall in the same sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ship the standard model. It works.&lt;/li&gt;
&lt;li&gt;Discover domain-specific accuracy gaps. They exist.&lt;/li&gt;
&lt;li&gt;Fine-tune. It can help, if the data is aligned.&lt;/li&gt;
&lt;li&gt;Try to deploy the fine-tuned model to NPU. Blocked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result: fine-tuning becomes a GPU-only optimization. The penalty is not just speed, either. Our fine-tuned model drew roughly 3x the current of the standard model on GPU (301 mA vs 101 mA) - a system-wide reading, so directional rather than precise, but the direction matters for battery life too. And the entire value proposition of the NPU - purpose-built silicon that runs inference about 1.7x faster than GPU (and several times faster than CPU), and by widely reported figures at lower power - stays tied to the standard models that were pre-compiled for it.&lt;/p&gt;

&lt;p&gt;This is less a villain than two reasonable design choices colliding. Ahead-of-time NPU compilation lives inside the vendor's AI stack, which was not built with third-party fine-tunes in mind; and the open export path targets generic INT4, not a QNN graph. The practical effect is still awkward: the developers who invest in accuracy by fine-tuning end up on the slower runtime, while those who ship the stock model get the faster one. That is an outcome to fix, not a conspiracy to blame.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the ecosystem needs
&lt;/h2&gt;

&lt;p&gt;The fix is conceptually simple: a public, documented pipeline from fine-tuned Hugging Face weights to NPU-compiled &lt;code&gt;.litertlm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In concrete terms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A public QNN AoT compilation path for LLMs.&lt;/strong&gt; Today, Qualcomm's QNN SDK supports classical model compilation. Extending this to LLM architectures - or providing a hosted compilation service - would let developers compile their fine-tuned weights for Hexagon without needing internal toolchain access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AIMET integration with the LiteRT-LM export pipeline.&lt;/strong&gt; AIMET handles quantization-aware training and post-training quantization for Qualcomm targets. If &lt;code&gt;litert_torch.generative.export_hf&lt;/code&gt; could target QNN as a backend (not just generic INT4), the export step would produce NPU-ready bundles directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A compilation service, if open-sourcing the toolchain is not feasible.&lt;/strong&gt; Upload merged weights, receive a QNN-compiled &lt;code&gt;.litertlm&lt;/code&gt;. This is how some cloud TPU compilation workflows operate - the developer never touches the compiler directly, but they get hardware-optimized artifacts back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation of the compilation requirements.&lt;/strong&gt; Even knowing &lt;em&gt;what&lt;/em&gt; the pipeline needs - AIMET version, QNN SDK version, target SoC specification, supported quantization schemes, memory layout constraints - would let the community start building bridges.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bigger picture
&lt;/h2&gt;

&lt;p&gt;On-device AI is at an inflection point. The models are good enough. The hardware is fast enough. The runtimes are stable enough. The missing piece is the development loop: train, evaluate, compile, deploy, measure, iterate.&lt;/p&gt;

&lt;p&gt;Right now, that loop is broken at the compile step for anyone who fine-tunes. And task-aligned fine-tuning is often what closes the last domain-specific accuracy gap. Which means the loop is broken for exactly the developers who are building the most demanding applications.&lt;/p&gt;

&lt;p&gt;The standard Gemma 4 E2B on NPU at about 42 tokens per second with 92ms time-to-first-token is genuinely impressive hardware. Getting fine-tuned models onto that hardware - with the kind of domain-specific gains we saw in the benchmark - is the difference between on-device AI as a demo and on-device AI as a product.&lt;/p&gt;

&lt;p&gt;The models are ready. The silicon is ready. The remaining gap is a public path through the toolchain.&lt;/p&gt;




&lt;h3&gt;
  
  
  Related in this series of "Edge AI from the Trenches"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/jdshah/prompt-engineering-beat-my-fine-tuned-model-here-is-why-4l74"&gt;Prompt Engineering Beat My Fine-Tuned Model. Here Is Why.&lt;/a&gt; - the decision framework for when fine-tuning is the right lever to pull&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/jdshah/my-fine-tuned-gemma-4-loaded-fine-then-broke-on-the-first-message-1nef"&gt;My Fine-Tuned Gemma 4 Loaded Fine, Then Broke on the First Message&lt;/a&gt; - the undocumented export failure that blocks fine-tuned models before they even reach the NPU wall&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/jdshah/i-opened-a-litertlm-file-here-is-what-is-actually-in-there-4aof"&gt;I Opened a .litertlm File. Here Is What Is Actually in There.&lt;/a&gt; - why the compiled bundle is the end of the pipeline, and what gets sealed at export time&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Jaydeep Shah is a developer with roots in embedded systems, Android platform internals, and silicon-level AI optimization. He now explores on-device AI inference - bringing models from the cloud to phones and edge hardware. Along with his team Edge Artists, he builds applications using LiteRT-LM and Gemma models on mobile hardware, and writes about what works, what breaks, and what he learns along the way. This post is part of the Edge AI from the Trenches series.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Last updated: Oct 2026&lt;/em&gt;&lt;br&gt;
&lt;em&gt;22nd of 23 posts in the "Edge AI from the Trenches" series&lt;/em&gt;&lt;/p&gt;

</description>
      <category>edgeai</category>
      <category>android</category>
      <category>litertlm</category>
      <category>finetuning</category>
    </item>
    <item>
      <title>Fine-tuning a 0.8B model to beat hosted LLMs at one job</title>
      <dc:creator>Abhash Chakraborty</dc:creator>
      <pubDate>Mon, 05 Oct 2026 16:07:46 +0000</pubDate>
      <link>https://dev.to/abhash_chakraborty/fine-tuning-a-08b-model-to-beat-hosted-llms-at-one-job-4ajl</link>
      <guid>https://dev.to/abhash_chakraborty/fine-tuning-a-08b-model-to-beat-hosted-llms-at-one-job-4ajl</guid>
      <description>&lt;p&gt;I needed a model for one narrow decision. When a new fact about someone comes in, does it replace something already known? "I moved to Berlin" should retire "I live in Pune". "Watched two episodes of The Bear" should leave "works at Infosys" alone.&lt;/p&gt;

&lt;p&gt;It sounds trivial, but any assistant with long-term memory makes this call all the time, and both kinds of mistake hurt. Miss an update and it keeps repeating stale facts. Replace too eagerly and it throws away things that are still true.&lt;/p&gt;

&lt;p&gt;This post covers the models I compared, how I fine-tuned the best starting point, how I tested it, and what it took to train. The short version: a 0.8B open model, fine-tuned on 6,000 examples on a single T4, beat every hosted LLM I tested at this one job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9kfyd0j285qzii50727h.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9kfyd0j285qzii50727h.webp" alt="Bar chart: the fine-tuned kev-mem scores 0.954 mean ROC AUC on memory-update decisions, ahead of Gemini 3.7 Flash (0.892), Gemma 4 31B (0.887), GPT-5.4 mini (0.873), Kev as released (0.852), Jev (0.834) and Laya (0.711)" width="799" height="453"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Every model on the same held-out tests. For my definition of an update, not a general ranking.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a System-One model
&lt;/h2&gt;

&lt;p&gt;The obvious approach is to prompt an LLM and parse what comes back. That works, but it's slow, every decision is an API call, and the answer is free text that you then have to interpret.&lt;/p&gt;

&lt;p&gt;TypeSafe's Jev takes a different approach. It's a "System One" model: instead of writing text, it answers typed questions with calibrated probabilities, like a choice from a list or a yes/no. For this task I send it the new fact and up to eight existing ones, and ask two questions about each existing fact:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does the new fact relate to it: duplicate, updates, elaborates, related or unrelated?&lt;/li&gt;
&lt;li&gt;Is the existing fact now outdated?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The score I use is the average of the two: &lt;code&gt;p_update = (P(updates) + P(outdated)) / 2&lt;/code&gt;. At 0.70 or above, the old fact is replaced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The candidates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jev&lt;/strong&gt; (hosted, by TypeSafe). Solid, but tuned for general decisions rather than my definition of an update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laya&lt;/strong&gt;, an open 421M ModernBERT model that answers in Jev's format. Fast and light, but off the shelf it replaced too many everyday events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kev&lt;/strong&gt;, Jared Palmer's open-weight model, compatible with Jev. I tried two sizes. Kev-0.8B was the best of everything as released on stated updates. Kev-4B scored lower on that, and on the T4 it ran out of memory on longer requests and took 5.5 seconds per decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For reference I also ran a zero-shot NLI model (DeBERTa-v3-large) and several hosted LLMs, each prompted with the same definition of an update. Kev-0.8B was the clear starting point: the best as released, and the only size that both trains and runs on a 16 GB T4.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data
&lt;/h2&gt;

&lt;p&gt;Each training example is one request: a new fact, up to eight existing facts, and the typed questions, with a label for every question. I built them from two pools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;6,818 logged Jev decisions.&lt;/strong&gt; Training on these is distillation: Kev learns to make Jev's calls on real requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10,219 decisions with ground truth.&lt;/strong&gt; I simulated 48 lives of a year each. People move city, change jobs, partners and managers, switch gyms, cars, courses, diets and laptops, reschedule appointments, mention crucial facts and make small talk. Because the simulator knows what actually changed, every pair has the right answer: same attribute with a new value is an update, the same value is a duplicate, anything else is related or unrelated. That's 53,870 fact pairs, 2,298 of them real updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 6,000 training examples took every decision that contains a real update first, then filled up from the shuffled mix. Nothing from the test data went in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: supervised fine-tuning
&lt;/h2&gt;

&lt;p&gt;LoRA on Qwen3.5-0.8B-Base, starting from Kev's released weights: rank 16, alpha 32, dropout 0.05, on every linear and linear-attention projection, plus a small decision head. One epoch over the 6,000 examples at a learning rate of 5e-5, with label smoothing of 0.05.&lt;/p&gt;

&lt;p&gt;The GPU was a single NVIDIA T4 with 16 GB. It has no bf16, so everything ran in fp32, with batch 1, gradient accumulation of 8 and gradient checkpointing. The requests are short (87 tokens on average, 138 at the 95th percentile), so nothing was truncated. That came to 750 optimizer steps and 5.52M tokens in 6.55 hours, peaking at 5.09 GB of GPU memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 2: reward-ranked RL (ReST)
&lt;/h2&gt;

&lt;p&gt;Supervised training teaches the format and the definition, but it doesn't know which mistakes matter more. So I let the stage-1 model make the real decision, replace or keep at the 0.70 threshold, on 2,000 training decisions (10,574 pairs), and scored every pair:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;+1 for catching a real update&lt;/li&gt;
&lt;li&gt;−1 for missing one&lt;/li&gt;
&lt;li&gt;−2 for replacing a fact that was still true&lt;/li&gt;
&lt;li&gt;+0.25 for correctly keeping a fact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mean reward was 0.265. There were 15 wrong replacements and 65 missed updates, spread over 52 decisions with a negative reward. Those 52 were relabelled with the truth (repeated twice when they included a −2) and mixed with replayed stage-1 examples, for 366 records in total. A second, gentler pass at a learning rate of 1e-5 took 46 steps, 0.34M tokens and 23 minutes, peaking at 4.98 GB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxihm3elhjp9fjabmun45.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxihm3elhjp9fjabmun45.webp" alt="The training run on one NVIDIA T4: stage 1, supervised fine-tuning on 6,000 decisions, 750 steps, 5.52M tokens, 6.55 hours, 5.09 GB peak; stage 2, ReST on 366 records, 46 steps, 0.34M tokens, 23 minutes, 4.98 GB peak; 885M parameters of which 11.3M trained" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Both stages on one T4. About 12 GPU-hours for the whole project, baseline tests included.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I picked the final checkpoint on six separate validation lives (400 decisions), where it scored 0.9991 AUC against 0.9983 for stage 1. The test sets were never used to choose anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I tested it
&lt;/h2&gt;

&lt;p&gt;Two held-out tests, both kept out of training:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A two-year simulated life&lt;/strong&gt; with template text: 122 decisions, 428 pairs, 186 updates, 45 of them stated outright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A live run&lt;/strong&gt; where an LLM wrote the facts from a three-month simulated life, so they read more like real conversation: 127 decisions, 498 pairs, 104 updates, 36 stated. This is the harder and more realistic one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A stated update says it directly ("I moved to Berlin"). An implied one doesn't, like a new address in another city with no mention of moving. I report both.&lt;/p&gt;

&lt;p&gt;The main metric is ROC AUC. Take one real update and one non-update at random: AUC is the chance the model scores the real update higher. 0.5 is a coin flip and 1.0 is perfect. It isn't accuracy, so 0.954 doesn't mean "right 95% of the time". I also check precision and recall at the 0.70 threshold, because that's where the model actually acts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fva096zy9wjkhmvttxn96.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fva096zy9wjkhmvttxn96.webp" alt="Line chart: on the live test, Kev's ROC AUC for stated updates goes from 0.752 as released to 0.929 after SFT and 0.945 after ReST, above Jev's 0.781; for any update it goes from 0.708 to 0.873 and 0.872" width="800" height="467"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The live test. Most of the gain comes from SFT; ReST makes the model more careful about replacing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the live test, stated updates went from 0.752 as released to 0.929 after stage 1 and 0.945 after stage 2. For any update, including implied ones, it went from 0.708 to 0.873 and 0.872. Jev scores 0.781 and 0.570 on the same test. On the two-year life the stated score went from 0.952 to 0.962, and for any update from 0.583 to 0.840.&lt;/p&gt;

&lt;p&gt;At the 0.70 threshold on the live test, precision was 1.00: it never replaced a fact that was still true. The cost is a recall of 0.14, so it only replaces when it's very sure. Kev and Laya as released never crossed the threshold at all. For this job, a cautious model is the right default, and a lower threshold chosen on validation data recovers more of the misses.&lt;/p&gt;

&lt;p&gt;Averaged over both tests on stated updates, with every hosted LLM given the same definition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;my fine-tuned Kev (kev-mem): &lt;strong&gt;0.954&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Gemini 3.7 Flash: 0.892&lt;/li&gt;
&lt;li&gt;Gemma 4 31B: 0.887&lt;/li&gt;
&lt;li&gt;GPT-5.4 mini: 0.873&lt;/li&gt;
&lt;li&gt;Kev as released: 0.852&lt;/li&gt;
&lt;li&gt;Jev: 0.834&lt;/li&gt;
&lt;li&gt;DeBERTa-v3-large NLI, zero-shot: 0.815&lt;/li&gt;
&lt;li&gt;Laya: 0.711&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Size and cost
&lt;/h2&gt;

&lt;p&gt;The model has about 885M parameters: 873.4M in the base, 10.8M in the LoRA adapter and 0.5M in the decision head, so only 11.3M were trained. The adapter is 43 MB. It runs in about 4 GB of GPU memory and takes 0.81 seconds per decision on the T4, without any of the optimised kernels. Counting the baseline tests and a second stage-1 run, the whole project used about 12 GPU-hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This is my use case and my definition of an update. Jev and Kev are general-purpose models and the hosted LLMs were only prompted, so a lot of the gain comes from specialising. That's the point, but it isn't a general ranking.&lt;/li&gt;
&lt;li&gt;These are single training runs, with no confidence intervals yet.&lt;/li&gt;
&lt;li&gt;Jev answered 234 of the 249 test decisions; the rest failed after retries.&lt;/li&gt;
&lt;li&gt;The test lives are simulated, apart from the LLM-written text of the live run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://huggingface.co/abhash-chakraborty/kev-mem-0.8b" rel="noopener noreferrer"&gt;kev-mem-0.8b on Hugging Face&lt;/a&gt;, served through Kev's own server, so any Jev client works with it&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.kaggle.com/models/abhashchakraborty/kev-mem" rel="noopener noreferrer"&gt;kev-mem on Kaggle&lt;/a&gt; and the &lt;a href="https://www.kaggle.com/code/abhashchakraborty/kev-mem-system-1-benchmark" rel="noopener noreferrer"&gt;benchmark notebook&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.kaggle.com/benchmarks/tasks/abhashchakraborty/memory-update-detection" rel="noopener noreferrer"&gt;memory-update detection benchmark&lt;/a&gt; for hosted LLMs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to TypeSafe for Jev, Jared Palmer for Kev, the Laya authors for open-sourcing their work (Apache-2.0), and the Qwen team for Qwen3.5.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://abhashchakraborty.tech/writing/fine-tuning-a-small-model-for-memory-updates?utm_source=devto" rel="noopener noreferrer"&gt;abhashchakraborty.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>llm</category>
      <category>finetuning</category>
      <category>opensource</category>
    </item>
    <item>
      <title>QLoRA fine-tuning: a complete guide for students</title>
      <dc:creator>PRANJUL RATHOUR</dc:creator>
      <pubDate>Sun, 04 Oct 2026 04:32:21 +0000</pubDate>
      <link>https://dev.to/pranjulrathour/qlora-fine-tuning-a-complete-guide-for-students-aml</link>
      <guid>https://dev.to/pranjulrathour/qlora-fine-tuning-a-complete-guide-for-students-aml</guid>
      <description>&lt;p&gt;Full fine-tuning updates every parameter in a model, which for anything past a few billion parameters needs GPU memory most students don't have access to. QLoRA sidesteps this by quantizing the base model to 4-bit precision and training small, low-rank adapter matrices on top of it instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the memory savings are so large
&lt;/h2&gt;

&lt;p&gt;The frozen base model in 4-bit takes a fraction of the memory of a full-precision copy, and the trainable adapters are tiny by comparison. That's how a 1.7B model fine-tunes at around 3.2 GB of peak VRAM instead of needing tens of gigabytes — the exact setup behind &lt;a href="https://pranjulrathour.scult.in/blog/finetune-studio-qlora-on-a-budget" rel="noopener noreferrer"&gt;FineTune Studio&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The steps, in order
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Validate your dataset first.&lt;/strong&gt; Malformed examples fail loudly hours into training if you don't catch them at upload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick a small base model&lt;/strong&gt; appropriate to your task and hardware — bigger isn't automatically better for a narrow use case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set a learning rate and rank&lt;/strong&gt; conservatively for a first run; QLoRA is forgiving, but not infinitely so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch the loss curve live&lt;/strong&gt;, not after the run finishes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate base versus tuned on the same prompts.&lt;/strong&gt; This step is the one people skip and shouldn't.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The honest tradeoff
&lt;/h2&gt;

&lt;p&gt;QLoRA won't match full fine-tuning on every metric. For a narrow, well-defined task with a good dataset, the gap rarely matters — and it's the difference between a project you can actually run and one you can only read about.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Pranjul Rathour&lt;/strong&gt; · GenAI engineer, 3x hackathon winner, campus mentor. Open for GenAI roles, hackathon judging, mentorship sessions and guest talks: &lt;strong&gt;&lt;a href="mailto:pranjulrathour41@gmail.com"&gt;pranjulrathour41@gmail.com&lt;/a&gt;&lt;/strong&gt; · &lt;a href="https://pranjulrathour.scult.in/invite" rel="noopener noreferrer"&gt;Invite me to your campus&lt;/a&gt;&lt;br&gt;
&lt;a href="https://pranjulrathour.scult.in" rel="noopener noreferrer"&gt;Portfolio &amp;amp; blog&lt;/a&gt; · &lt;a href="https://www.linkedin.com/in/pranjul-rathour/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://x.com/PranjulRathourx" rel="noopener noreferrer"&gt;X&lt;/a&gt; · &lt;a href="https://www.instagram.com/pranjulrathour.in/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; · &lt;a href="https://bsky.app/profile/pranjulrathour.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt; · &lt;a href="https://github.com/Pranjulrathour" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://dev.to/pranjulrathour"&gt;Dev.to&lt;/a&gt;&lt;/p&gt;

</description>
      <category>finetuning</category>
      <category>qlora</category>
      <category>guide</category>
    </item>
    <item>
      <title>How to evaluate a fine-tuned model honestly (base vs tuned)</title>
      <dc:creator>PRANJUL RATHOUR</dc:creator>
      <pubDate>Fri, 02 Oct 2026 04:32:30 +0000</pubDate>
      <link>https://dev.to/pranjulrathour/how-to-evaluate-a-fine-tuned-model-honestly-base-vs-tuned-40a5</link>
      <guid>https://dev.to/pranjulrathour/how-to-evaluate-a-fine-tuned-model-honestly-base-vs-tuned-40a5</guid>
      <description>&lt;p&gt;The most common fine-tuning mistake I see isn't a bad hyperparameter — it's skipping evaluation entirely and assuming the loss curve going down means the model got better at the task you actually care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the same prompts through both models
&lt;/h2&gt;

&lt;p&gt;Before touching the fine-tuned model, save a fixed set of representative prompts and the base model's answers to them. After training, run the identical prompts through the tuned model. Side by side, not from memory — memory of "how the base model used to answer" is unreliable after a training run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Score what actually matters for your task
&lt;/h2&gt;

&lt;p&gt;A general benchmark score tells you very little about whether the model got better at your specific narrow task. Write five to ten task-specific questions with known correct answers, and check both models against those.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real regression looks like
&lt;/h2&gt;

&lt;p&gt;Sometimes the tuned model is worse on general knowledge and better on your task — that's expected and fine. Sometimes it's worse on both, which means something in the dataset or hyperparameters needs fixing before you ship it. You only find out which one happened if you actually run the comparison.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Pranjul Rathour&lt;/strong&gt; · GenAI engineer, 3x hackathon winner, campus mentor. Open for GenAI roles, hackathon judging, mentorship sessions and guest talks: &lt;strong&gt;&lt;a href="mailto:pranjulrathour41@gmail.com"&gt;pranjulrathour41@gmail.com&lt;/a&gt;&lt;/strong&gt; · &lt;a href="https://pranjulrathour.scult.in/invite" rel="noopener noreferrer"&gt;Invite me to your campus&lt;/a&gt;&lt;br&gt;
&lt;a href="https://pranjulrathour.scult.in" rel="noopener noreferrer"&gt;Portfolio &amp;amp; blog&lt;/a&gt; · &lt;a href="https://www.linkedin.com/in/pranjul-rathour/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · &lt;a href="https://x.com/PranjulRathourx" rel="noopener noreferrer"&gt;X&lt;/a&gt; · &lt;a href="https://www.instagram.com/pranjulrathour.in/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; · &lt;a href="https://bsky.app/profile/pranjulrathour.bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt; · &lt;a href="https://github.com/Pranjulrathour" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://dev.to/pranjulrathour"&gt;Dev.to&lt;/a&gt;&lt;/p&gt;

</description>
      <category>finetuning</category>
      <category>evaluation</category>
      <category>guide</category>
    </item>
    <item>
      <title>From benchmarking to fine-tuning: what I learned about small decision models</title>
      <dc:creator>Gokula Krishna</dc:creator>
      <pubDate>Wed, 30 Sep 2026 05:47:25 +0000</pubDate>
      <link>https://dev.to/gokula_krishna_1f814922bb/from-benchmarking-to-fine-tuning-what-i-learned-about-small-decision-models-2ln3</link>
      <guid>https://dev.to/gokula_krishna_1f814922bb/from-benchmarking-to-fine-tuning-what-i-learned-about-small-decision-models-2ln3</guid>
      <description>&lt;p&gt;I tested two small decision models. First I tested TypeSafe AI's hosted model, Jev. Then I tested Laya, an independent open-source alternative. I started out benchmarking and ended up fine-tuning.&lt;/p&gt;

&lt;p&gt;Here is what I found, in the order I found it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Benchmarking Jev
&lt;/h2&gt;

&lt;p&gt;TypeSafe AI &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;launched Jev on September 15, 2026&lt;/a&gt;. It does not write text. You give it a message and a list of possible answers. It tells you how confident it is in each one.&lt;/p&gt;

&lt;p&gt;I skipped the launch demos and ran my own evals on public Kaggle datasets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;31,369&lt;/strong&gt; test samples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;12&lt;/strong&gt; classification tasks&lt;/li&gt;
&lt;li&gt;Three areas: anti-money laundering, customer support intent, and malicious network traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0.3 s&lt;/strong&gt; median response time, calling from Singapore to the US&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqfdrosx9v6fycla1bbfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqfdrosx9v6fycla1bbfb.png" alt="Jev results across banking, network security and customer support." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main lesson: accuracy depends on the task, and even more on how you set the task up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Describe what the data shows, not what the criminal wants
&lt;/h3&gt;

&lt;p&gt;My first anti-money laundering pattern descriptions said what the criminal was trying to achieve. I rewrote them to describe what the transactions look like.&lt;/p&gt;

&lt;p&gt;Accuracy went from &lt;strong&gt;65% to 77%&lt;/strong&gt;. The model was the same. Only the descriptions changed.&lt;/p&gt;

&lt;p&gt;The model sees transactions, not motives. So describe the labels using what's in the input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove hidden bias from the setup
&lt;/h3&gt;

&lt;p&gt;In account-level laundering detection, I switched to using the same time window for every account. That removed a hidden bias in the setup. Accuracy went from &lt;strong&gt;63% to 75%&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some tasks have no signal
&lt;/h3&gt;

&lt;p&gt;Not everything worked. Classifying a &lt;strong&gt;single transaction&lt;/strong&gt; as laundering or not gave &lt;strong&gt;54%&lt;/strong&gt;, which is about chance. One transaction on its own doesn't carry enough signal. This is a limit of the task, not of the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confidence you can trust
&lt;/h3&gt;

&lt;p&gt;For customer support intent, Jev picked the right answer &lt;strong&gt;92.3%&lt;/strong&gt; of the time. On banking intents it was &lt;strong&gt;79.8%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The confidence score is honest. When Jev said it was 90% sure, it was right about 90% of the time.&lt;/p&gt;

&lt;p&gt;That makes thresholds useful. If you accept only predictions above 90% confidence, accuracy rises to &lt;strong&gt;97.6%&lt;/strong&gt;, and that still covers &lt;strong&gt;82%&lt;/strong&gt; of cases. The other 18% go to a fallback: a bigger model, a person, or a request for more information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F58s4l2crt0gwwf0jqxll.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F58s4l2crt0gwwf0jqxll.png" alt="Customer support: 92% accuracy on all cases, 97.6% on the 82% of cases above the confidence threshold. The other 18% go to fallback." width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask the question directly
&lt;/h3&gt;

&lt;p&gt;To catch off-topic messages, I first used a low top score as the signal. That gave &lt;strong&gt;91.7%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then I added "is this off-topic?" as a separate yes/no question. That gave &lt;strong&gt;97.7%&lt;/strong&gt;. It costs no extra time, because everything is answered in one pass.&lt;/p&gt;

&lt;p&gt;If you care about a decision, ask for it directly. Don't infer it from another score.&lt;/p&gt;

&lt;h3&gt;
  
  
  The dataset can be wrong too
&lt;/h3&gt;

&lt;p&gt;On malicious network traffic, Jev scored &lt;strong&gt;78%&lt;/strong&gt; against the dataset labels as given.&lt;/p&gt;

&lt;p&gt;One capture had about &lt;strong&gt;400 windows labeled "benign"&lt;/strong&gt;. They were unanswered scans of thousands of hosts. Jev flagged them as malicious with 80% to 90% confidence. Without that capture, the score was &lt;strong&gt;98%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I report both numbers. The 98% is a filtered result. But the lesson holds: when the model disagrees with the ground truth, look at the data before you count it as a model error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: Laya, the open-source alternative
&lt;/h2&gt;

&lt;p&gt;Less than a week later, an open-source alternative appeared. &lt;a href="https://github.com/NandhaKishorM/laya" rel="noopener noreferrer"&gt;Laya&lt;/a&gt; is a separate project, not a release of Jev's weights. It claims performance equal to Jev.&lt;/p&gt;

&lt;p&gt;I ran it on 11 of the same tasks, using the same harness I built for Jev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8ehwy56jk540a2iordj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8ehwy56jk540a2iordj.png" alt="Jev vs untuned Laya: accuracy per task, confidence calibration and confidence filtering." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accuracy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Jev&lt;/th&gt;
&lt;th&gt;Laya (untuned)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Help-desk requests&lt;/td&gt;
&lt;td&gt;92.3%&lt;/td&gt;
&lt;td&gt;49.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banking questions&lt;/td&gt;
&lt;td&gt;79.8%&lt;/td&gt;
&lt;td&gt;39.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money-laundering patterns&lt;/td&gt;
&lt;td&gt;77.3%&lt;/td&gt;
&lt;td&gt;13.5% (no better than guessing)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Across 11 tasks and 3 setups, Laya never beat Jev. On binary (yes/no) decisions it came much closer. On choosing between many answers it fell well behind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confidence was the real problem
&lt;/h3&gt;

&lt;p&gt;The accuracy gap was not the biggest issue. The confidence scores were.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Help-desk task:&lt;/strong&gt; 4,173 of 4,500 answers were at 99% confidence, and about half of them were right.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banking task:&lt;/strong&gt; Laya claimed about 97% confidence on almost everything, and got more than half wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Calibration error measures how far confidence is from actual accuracy, where 0 is perfect. Jev scored &lt;strong&gt;0.013&lt;/strong&gt;. Laya scored &lt;strong&gt;0.486&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That breaks confidence-based routing. With Jev, keeping only answers at 90%+ confidence raised accuracy to 97.6%. With Laya, the same filter dropped 7% of questions and gained only 2 points, to 51.7%.&lt;/p&gt;

&lt;p&gt;It also breaks refusals. Of the off-topic questions Laya should have refused, it still answered &lt;strong&gt;88%&lt;/strong&gt; with 90%+ confidence. Jev did that on &lt;strong&gt;14%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My conclusion then: I wouldn't use untuned Laya to route between many options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Fine-tuning changed my mind
&lt;/h2&gt;

&lt;p&gt;The next day I fine-tuned Laya on the banking task. It took about &lt;strong&gt;140 minutes&lt;/strong&gt; on my laptop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fad4xpmwsps8ld7go87p8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fad4xpmwsps8ld7go87p8.png" alt="Banking accuracy: Jev 79.8%, untuned Laya 39.4%, fine-tuned Laya 78.3%. Random guessing is 1.3%." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accuracy went from &lt;strong&gt;39.4% to 78.3%&lt;/strong&gt;, just 1.5 points below Jev's 79.8%. The task has 77 possible answers, so random guessing would get 1.3%.&lt;/p&gt;

&lt;p&gt;The confidence fix mattered more. After fine-tuning, when Laya said it was 97% confident, it was right &lt;strong&gt;96%&lt;/strong&gt; of the time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frsuonkerwys2hfc60tvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frsuonkerwys2hfc60tvz.png" alt="After fine-tuning, Laya predictions at 97% confidence were right 96% of the time." width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is one confidence group, not a full calibration curve. But it's a big change from "99% sure, right half the time."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Jev&lt;/th&gt;
&lt;th&gt;Laya (fine-tuned)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Banking accuracy&lt;/td&gt;
&lt;td&gt;79.8%&lt;/td&gt;
&lt;td&gt;78.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;~309 ms (hosted)&lt;/td&gt;
&lt;td&gt;~90 ms (local)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inference cost&lt;/td&gt;
&lt;td&gt;~$0.07 per 1,000 requests&lt;/td&gt;
&lt;td&gt;$0 (runs on my laptop)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;fine-tune per task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The catch is that you need to fine-tune for each task, and that takes task-specific data. I have not yet tested off-topic detection on the fine-tuned model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: Laya experts
&lt;/h2&gt;

&lt;p&gt;So I built &lt;strong&gt;Laya experts&lt;/strong&gt;: Laya models fine-tuned for specific tasks. Each one is lightweight, runs on a single machine, and makes a decision in about &lt;strong&gt;80 ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One expert detects personally identifiable information (PII). On entity F1, the untuned Laya scored &lt;strong&gt;0.156&lt;/strong&gt;, Jev &lt;strong&gt;0.736&lt;/strong&gt;, and the Laya PII expert &lt;strong&gt;0.967&lt;/strong&gt;. A check like this could run at each stage of a data pipeline to support compliance.&lt;/p&gt;

&lt;p&gt;Laya experts is published here: &lt;a href="https://huggingface.co/goku-san/laya-experts" rel="noopener noreferrer"&gt;Laya experts&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone trying these models
&lt;/h2&gt;

&lt;p&gt;These models look like a real "System 1" decision layer. They handle fast classification, routing and other bounded decisions, so an LLM or agent doesn't have to call a generative model for everything.&lt;/p&gt;

&lt;p&gt;To get good results, spend less time on the model and more on the setup around it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Descriptions.&lt;/strong&gt; Describe labels using what's visible in the input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask directly.&lt;/strong&gt; Make important decisions their own question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check confidence.&lt;/strong&gt; Test whether "90% sure" really means right 90% of the time. If it doesn't, thresholds won't work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set thresholds and a fallback.&lt;/strong&gt; Decide what happens to the cases the model isn't sure about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect disagreements.&lt;/strong&gt; Sometimes the label is wrong, not the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tune when needed.&lt;/strong&gt; An untuned open-source model can be far off. A short fine-tune on task data can close most of the gap.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Benchmark note: all figures are my own results on public benchmarks, with thresholds tuned per task. They are not vendor claims. Jev 1.13.0 (hosted). Laya 421M via laya-mlx, run locally on an Apple M3. The untuned Laya evaluation covered 23 runs and 65,078 requests.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://gokulakrishna.co/2026/09/30/benchmarking-to-fine-tuning-decision-models/" rel="noopener noreferrer"&gt;gokulakrishna.co&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>benchmarking</category>
      <category>finetuning</category>
      <category>modelevaluation</category>
    </item>
    <item>
      <title>Unwrapping the wrappers: who actually trains their own models, and why</title>
      <dc:creator>Tyler Edwards</dc:creator>
      <pubDate>Tue, 29 Sep 2026 10:54:39 +0000</pubDate>
      <link>https://dev.to/tyler007/unwrapping-the-wrappers-who-actually-trains-their-own-models-and-why-3mh6</link>
      <guid>https://dev.to/tyler007/unwrapping-the-wrappers-who-actually-trains-their-own-models-and-why-3mh6</guid>
      <description>&lt;p&gt;Cursor, Intercom, Replit and Perplexity all train specialist models on their own production data. Each picked one narrow, high-volume task, trained on traffic no competitor can access, and shipped a model that matches or beats frontier APIs on that job at lower latency and cost. None of them tried to out-build OpenAI.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.overmindlab.ai/research/who-trains-their-own-models?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=research-repost" rel="noopener noreferrer"&gt;overmindlab.ai&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Application layer companies, often labelled as 'ChatGPT wrappers', are starting to train their own models. Specialist models, post-trained or fine-tuned on a dataset their competitors cannot touch: their own production traffic.&lt;/p&gt;

&lt;p&gt;Four recent examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cursor trained Composer, the model inside its coding agent, with online RL on production agent traces. Delivering frontier-level coding at a fraction of the latency and cost.&lt;/li&gt;
&lt;li&gt;Intercom trained Fin Apex, a customer-service model post-trained on billions of support interactions. It bought them control of the resolution-rate metric at scale.&lt;/li&gt;
&lt;li&gt;Replit trained a 7B code-repair model, a fine-tune of DeepSeek-Coder-Instruct-v1.5. It bought them a fast, narrow specialist trained on IDE data nobody else has.&lt;/li&gt;
&lt;li&gt;Perplexity trained Sonar, its search-answer model, a fine-tune of Llama 3.3 70B served on Cerebras hardware. It bought them sub-second, citation-heavy answers at search scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Which companies train their own models?
&lt;/h2&gt;

&lt;p&gt;The same four, with the base model, the method and the measured result in one view.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Company&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Base model&lt;/th&gt;
&lt;th&gt;How they trained it&lt;/th&gt;
&lt;th&gt;Measured result&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Composer&lt;/td&gt;
&lt;td&gt;Not disclosed, a mixture-of-experts architecture&lt;/td&gt;
&lt;td&gt;Online RL on production agent traces, inside a harness mirroring Cursor's real tool space; new checkpoints ship several times a day&lt;/td&gt;
&lt;td&gt;Generates tokens several times faster than comparable frontier models, landing close to the top tier on internal coding benchmarks&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://cursor.com/blog/composer" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;, &lt;a href="https://arxiv.org/html/2603.24477v1" rel="noopener noreferrer"&gt;Composer 2 report&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intercom&lt;/td&gt;
&lt;td&gt;Fin Apex 1.0&lt;/td&gt;
&lt;td&gt;Not disclosed&lt;/td&gt;
&lt;td&gt;Post-trained on the company's own support interaction data, billions of interactions&lt;/td&gt;
&lt;td&gt;73.1% benchmark resolution rate against 71.1% for GPT-5.4 and Claude Opus 4.5; one enterprise customer went from 68% to 75% overnight&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.intercom.com/blog/announcing-fin-apex-the-age-of-vertical-models-is-here/" rel="noopener noreferrer"&gt;Intercom&lt;/a&gt;, &lt;a href="https://venturebeat.com/technology/intercoms-new-post-trained-fin-apex-1-0-beats-gpt-5-4-and-claude-sonnet-4-6" rel="noopener noreferrer"&gt;VentureBeat&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replit&lt;/td&gt;
&lt;td&gt;7B code-repair model&lt;/td&gt;
&lt;td&gt;DeepSeek-Coder-Instruct-v1.5 (7B)&lt;/td&gt;
&lt;td&gt;Fine-tuned on IDE telemetry, scoped to roughly 20 diagnostic categories&lt;/td&gt;
&lt;td&gt;Matched or beat GPT-4 and Claude 3 on real-world fixes inside that use case&lt;/td&gt;
&lt;td&gt;&lt;a href="https://replit.com/blog/code-repair" rel="noopener noreferrer"&gt;Replit&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perplexity&lt;/td&gt;
&lt;td&gt;Sonar&lt;/td&gt;
&lt;td&gt;Llama 3.3 70B&lt;/td&gt;
&lt;td&gt;Fine-tuned for factual grounding, citation quality and readability, served on Cerebras wafer-scale hardware&lt;/td&gt;
&lt;td&gt;Roughly 1,200 tokens per second, and matched or beat larger frontier models on user satisfaction in Perplexity's own A/B tests&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.perplexity.ai/hub/blog/meet-new-sonar" rel="noopener noreferrer"&gt;Perplexity&lt;/a&gt;, &lt;a href="https://www.cerebras.ai/press-release/cerebras-powers-perplexity-sonar-with-industrys-fastest-ai-inference" rel="noopener noreferrer"&gt;Cerebras&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Where we sit, for the record. Overmind is the model training platform for AI teams, and the pattern running through these four case studies is the one it is built for. The case studies themselves are other people's work, cited at source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cursor: reinforcement learning on live agent traces
&lt;/h2&gt;

&lt;p&gt;Cursor didn't fine-tune once and ship. The Composer family of models run on real-time RL: model checkpoints go to production, real user interactions become the reward signal, and an improved checkpoint ships several times a day. The model is a mixture-of-experts architecture trained inside a harness built to mirror Cursor's actual tool space, including file edits, semantic search, and terminal commands. The reward signal reflects how developers really use the product rather than a synthetic benchmark, according to &lt;a href="https://cursor.com/blog/composer" rel="noopener noreferrer"&gt;Cursor's technical post on Composer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cursor reports Composer generates tokens several times faster than comparable frontier models while landing close to the top tier on internal coding benchmarks, a tradeoff explored in more depth by the &lt;a href="https://arxiv.org/html/2603.24477v1" rel="noopener noreferrer"&gt;Composer 2 technical report&lt;/a&gt;. Nobody else has Cursor's trace data, which means nobody else can train Cursor's model, even with an identical base checkpoint and an identical training recipe.&lt;/p&gt;

&lt;p&gt;What it bought them: coding performance competitive with larger models, at preferential latency and cost, plus a moat provided by their own proprietary data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intercom: post-training on the metric the product is sold on
&lt;/h2&gt;

&lt;p&gt;Fin launched in 2023 on top of GPT-4. Fin 2 moved to Claude in late 2024. Then Intercom's roughly 60-person AI research group, led by Fergal Reid, built Fin Apex 1.0, a model post-trained on the company's own support interaction data, as described in the &lt;a href="https://www.intercom.com/blog/announcing-fin-apex-the-age-of-vertical-models-is-here/" rel="noopener noreferrer"&gt;Fin Apex launch post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One large enterprise customer saw its resolution rate jump from 68% to 75% overnight after the switch to Apex, a 22% cut in unresolved conversations according to Intercom's own numbers. Independent coverage from &lt;a href="https://venturebeat.com/technology/intercoms-new-post-trained-fin-apex-1-0-beats-gpt-5-4-and-claude-sonnet-4-6" rel="noopener noreferrer"&gt;VentureBeat&lt;/a&gt; puts Apex's benchmark resolution rate at 73.1%, against 71.1% for both GPT-5.4 and Claude Opus 4.5.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Benchmark resolution rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fin Apex 1.0&lt;/td&gt;
&lt;td&gt;73.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4&lt;/td&gt;
&lt;td&gt;71.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.5&lt;/td&gt;
&lt;td&gt;71.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Intercom's own model documentation breaks the pipeline into separate retrieval, reranking, and generation models rather than one monolithic system, detailed on the &lt;a href="https://fin.ai/cx-models" rel="noopener noreferrer"&gt;Fin CX models page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Worth noting: Intercom hasn't abandoned rented models. Apex is layered on top of retrieval and routing components, and the company still uses third-party models elsewhere. This architecture is common as companies balance performance, latency and cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replit: a narrow 7B specialist trained on IDE telemetry
&lt;/h2&gt;

&lt;p&gt;Replit's bet was the opposite of "bigger is better." The team fine-tuned a 7B open-weights model, DeepSeek-Coder-Instruct-v1.5, chosen after testing several code-focused base models and documented in its &lt;a href="https://replit.com/blog/code-repair" rel="noopener noreferrer"&gt;code-repair write-up&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The model targets a specific use case, roughly the 20 diagnostic categories responsible for the majority of everyday coding errors, and the fine-tuned 7B checkpoint matched or beat GPT-4 and Claude 3 on real-world fixes in that use case. Small &lt;a href="https://huggingface.co/deepseek-ai/deepseek-coder-7b-instruct-v1.5" rel="noopener noreferrer"&gt;open source models&lt;/a&gt; can beat much larger general-purpose models at many tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perplexity: search-optimised inference at 1,200 tokens per second
&lt;/h2&gt;

&lt;p&gt;Perplexity fine-tuned Meta's Llama 3.3 70B into Sonar, tuned specifically for factual grounding, citation quality, and readability in its default search mode, as the company explains in its &lt;a href="https://www.perplexity.ai/hub/blog/meet-new-sonar" rel="noopener noreferrer"&gt;Sonar launch post&lt;/a&gt;. Sonar runs on Cerebras wafer-scale inference hardware, which Perplexity and &lt;a href="https://www.cerebras.ai/press-release/cerebras-powers-perplexity-sonar-with-industrys-fastest-ai-inference" rel="noopener noreferrer"&gt;Cerebras jointly announced&lt;/a&gt; pushes throughput to roughly 1,200 tokens per second, an order of magnitude faster than comparable models on conventional GPU inference.&lt;/p&gt;

&lt;p&gt;In Perplexity's own A/B testing, Sonar matched or beat larger frontier models on user satisfaction inside search, while running far cheaper and faster than calling those models directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build a specialist model instead of prompting a frontier one?
&lt;/h2&gt;

&lt;p&gt;Calling a frontier model for every request is sometimes the equivalent of running a data centre to toast bread. It works, but it's the wrong tool for a narrow, high-volume, well-defined job. Across these four companies, three conditions kept showing up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A narrow task, not a general one. Small specialised models are excellent at one narrow task. Don't ask them to be broadly intelligent.&lt;/li&gt;
&lt;li&gt;Proprietary production data no competitor can access. Cursor has Cursor's traces. Intercom has Intercom's support conversations. Your production traffic is the one dataset nobody can buy.&lt;/li&gt;
&lt;li&gt;Enough volume that per-token latency and cost actually move the P&amp;amp;L. Fine-tuning a model can be expensive. It only pays off once inference volume is high enough that shaving cost per call compounds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these companies want to out-build OpenAI or Anthropic on general intelligence. Each trained a model that wins one specific task, using the one dataset it uniquely owned.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the traces-to-training loop look like?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftuc4hrt0ff13e2l248hp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftuc4hrt0ff13e2l248hp.png" alt="Flywheel diagram titled " width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What it produces&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Production runs&lt;/td&gt;
&lt;td&gt;Traces of what the agent actually did&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Labelled dataset&lt;/td&gt;
&lt;td&gt;Those traces, curated and labelled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fine-tuned model&lt;/td&gt;
&lt;td&gt;A specialist trained on that dataset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redeploy&lt;/td&gt;
&lt;td&gt;The specialist back in production, generating the next round of traces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Capture production traces, turn them into a labelled dataset, train a model on it, and ship that model back into production. That's where the application layer is going. We break the middle two stages down in &lt;a href="https://www.overmindlab.ai/research/traces-to-training-dataset?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=research-repost" rel="noopener noreferrer"&gt;how do you turn traces into a training dataset&lt;/a&gt; and &lt;a href="https://www.overmindlab.ai/research/how-to-train-your-agent?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=research-repost" rel="noopener noreferrer"&gt;how to train your agent&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do companies that train their own models still use frontier APIs?
&lt;/h3&gt;

&lt;p&gt;Usually yes, alongside. Intercom hasn't abandoned rented models. Fin Apex sits on top of retrieval and routing components, and the company still uses third-party models elsewhere. A mix of owned and rented models is the common architecture, chosen to balance performance, latency and cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you need a large model to beat a frontier one?
&lt;/h3&gt;

&lt;p&gt;Not on a narrow task. Replit's 7B fine-tune of DeepSeek-Coder-Instruct-v1.5 matched or beat GPT-4 and Claude 3 on real-world fixes inside its target diagnostic categories. Perplexity's Sonar, a 70B fine-tune, matched or beat larger frontier models on user satisfaction inside search.&lt;/p&gt;

&lt;h3&gt;
  
  
  What data do you need to train a specialist model?
&lt;/h3&gt;

&lt;p&gt;Your own production traffic. Cursor used live agent traces, Intercom used its support interactions, Replit used IDE telemetry, and Perplexity tuned against its own search behaviour. The common ingredient is a dataset the product generates itself, which is the one thing a competitor cannot buy.&lt;/p&gt;

&lt;h3&gt;
  
  
  When does training your own model not pay off?
&lt;/h3&gt;

&lt;p&gt;When any of the three conditions is missing. A broad task rather than a narrow one, no proprietary data sitting behind it, or volume too low for per-call savings to compound. Fine-tuning costs money, so it only pays back once inference volume is high enough.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Overmind is the model training platform for AI teams. It turns your production traces into specialised models you own. &lt;a href="https://console.overmindlab.ai/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=research-repost" rel="noopener noreferrer"&gt;Get started&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>finetuning</category>
    </item>
    <item>
      <title>How to Run Unsloth on AMD GPUs (ROCm) [2026 Guide]</title>
      <dc:creator>Kunal</dc:creator>
      <pubDate>Sun, 27 Sep 2026 12:43:38 +0000</pubDate>
      <link>https://dev.to/kunal_d6a8fea2309e1571ee7/how-to-run-unsloth-on-amd-gpus-rocm-2026-guide-287h</link>
      <guid>https://dev.to/kunal_d6a8fea2309e1571ee7/how-to-run-unsloth-on-amd-gpus-rocm-2026-guide-287h</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at &lt;a href="https://www.kunalganglani.com/blog/unsloth-amd-gpu-rocm-fine-tuning-guide" rel="noopener noreferrer"&gt;kunalganglani.com&lt;/a&gt; — read it there for inline code, hero image, and live links.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don’t need another “it installed!” ROCm post. You need Unsloth fine-tuning on an AMD GPU to &lt;strong&gt;actually train&lt;/strong&gt;, with a loss curve that goes down for the right reasons.&lt;/p&gt;

&lt;p&gt;By the end of this guide, you’ll have Unsloth fine-tuning running on an AMD GPU with ROCm, plus a quick sanity run that proves it’s converging (not just “it launched”). If your setup is close to supported, you can get from zero to a minimal LoRA run in ~45–90 minutes. If you’ve been stuck in “HIP error / no kernel image available” hell, plan for an afternoon.&lt;/p&gt;

&lt;p&gt;This is an &lt;strong&gt;Unsloth AMD GPU ROCm fine tuning guide&lt;/strong&gt; written for late‑2026 reality: ROCm support is way better than it was, but it’s still version-fragile, kernel-fragile, and “works” is not the same as “trains fast and converges”.&lt;/p&gt;

&lt;p&gt;I’m going to be blunt. Most AMD fine-tuning failures aren’t because Unsloth is “broken”. They’re because your stack is a Jenga tower: kernel + driver + ROCm userland + PyTorch ROCm wheels + Triton/attention kernels + mixed precision. One mismatch and you get either a hard crash or, worse, silent slow fallbacks.&lt;/p&gt;

&lt;p&gt;(Visual break: matrix screenshot)&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Unsloth?
&lt;/h2&gt;

&lt;p&gt;Unsloth is a training and inference optimization toolkit that patches Hugging Face Transformers/TRL flows to make fine-tuning large language models faster and more memory-efficient, typically using LoRA and related parameter-efficient methods.&lt;/p&gt;

&lt;p&gt;Where Unsloth earns its keep is (a) smart model patching and (b) optional high-performance kernels. On NVIDIA/CUDA, those kernels are usually just… there. On AMD/ROCm, kernel availability depends on your exact GPU architecture and software versions. That’s the whole game.&lt;/p&gt;

&lt;p&gt;If you’re still deciding whether you need fine-tuning at all, read my decision framework on &lt;a href="https://dev.to/blog/fine-tuning-vs-rag-prompt-engineering"&gt;fine-tuning&lt;/a&gt; vs &lt;a href="https://dev.to/blog/fine-tuning-vs-rag-prompt-engineering"&gt;RAG&lt;/a&gt; (retrieval-augmented generation) and prompt work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unsloth AMD GPU ROCm fine tuning guide: the late‑2026 compatibility matrix
&lt;/h2&gt;

&lt;p&gt;Here’s the matrix I wish existed the first time I tried to help someone debug Unsloth on a Radeon card. It’s not “official”. It’s the set of combinations that tend to behave in the real world, plus the landmines that waste your time.&lt;/p&gt;

&lt;p&gt;Two anchors before we start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Treat the PyTorch ROCm wheel selector as the source of truth.&lt;/strong&gt; PyTorch’s install page currently shows Stable &lt;strong&gt;PyTorch 2.7.0&lt;/strong&gt; and ROCm wheel availability for &lt;strong&gt;ROCm 6.3&lt;/strong&gt; (that compute platform option exists right in the selector). If your installed ROCm runtime is 6.1 and you pip-installed a 6.3 wheel, you’re basically asking for missing &lt;code&gt;hip&lt;/code&gt; libraries and runtime weirdness. See the official selector on &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROCm itself ships a published compatibility matrix and install guidance.&lt;/strong&gt; Start from AMD’s docs, not random Reddit incantations. &lt;a href="https://rocm.docs.amd.com/" rel="noopener noreferrer"&gt;AMD ROCm documentation&lt;/a&gt; has an explicit “Compatibility matrix” section and per-component pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Known-good stacks (pin these first)
&lt;/h3&gt;

&lt;p&gt;“Known-good” means one thing: &lt;strong&gt;you can run a small SFT, loss decreases smoothly, no NaNs, and throughput isn’t obviously crippled&lt;/strong&gt;. Anything else is a science project.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU class&lt;/th&gt;
&lt;th&gt;Example GPUs&lt;/th&gt;
&lt;th&gt;OS baseline&lt;/th&gt;
&lt;th&gt;ROCm baseline&lt;/th&gt;
&lt;th&gt;PyTorch ROCm baseline&lt;/th&gt;
&lt;th&gt;Attention kernel mode&lt;/th&gt;
&lt;th&gt;Quantization path&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Consumer RDNA3 (24GB)&lt;/td&gt;
&lt;td&gt;RX 7900 XTX (24GB)&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04 / kernel 6.x&lt;/td&gt;
&lt;td&gt;ROCm 6.2–6.3&lt;/td&gt;
&lt;td&gt;torch 2.6–2.7 ROCm build&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SDPA&lt;/strong&gt; first, try FlashAttn later&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;FP16 LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Most reliable Radeon path. SDPA is usually “good enough”.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer RDNA3 (16GB)&lt;/td&gt;
&lt;td&gt;RX 7800 XT (16GB)&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04&lt;/td&gt;
&lt;td&gt;ROCm 6.2–6.3&lt;/td&gt;
&lt;td&gt;torch 2.6–2.7 ROCm build&lt;/td&gt;
&lt;td&gt;SDPA&lt;/td&gt;
&lt;td&gt;FP16 LoRA + grad ckpt&lt;/td&gt;
&lt;td&gt;VRAM is the constraint. Keep seq len sane (see VRAM section).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer RDNA4&lt;/td&gt;
&lt;td&gt;RX 8800/8900 class&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04+&lt;/td&gt;
&lt;td&gt;ROCm 6.3+&lt;/td&gt;
&lt;td&gt;torch 2.7 ROCm build&lt;/td&gt;
&lt;td&gt;SDPA&lt;/td&gt;
&lt;td&gt;FP16 LoRA&lt;/td&gt;
&lt;td&gt;Newer arch usually means better kernel coverage, but also sharper version coupling.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datacenter CDNA (MI series)&lt;/td&gt;
&lt;td&gt;MI250/MI300/MI350&lt;/td&gt;
&lt;td&gt;ROCm-certified distros&lt;/td&gt;
&lt;td&gt;ROCm 6.2–6.3+&lt;/td&gt;
&lt;td&gt;torch matched to ROCm&lt;/td&gt;
&lt;td&gt;FlashAttn &lt;strong&gt;more likely&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;FP16/BF16 LoRA&lt;/td&gt;
&lt;td&gt;MI parts tend to have fewer “invalid device function” surprises.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few sharp edges I’ll save you from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you’re on &lt;strong&gt;Windows&lt;/strong&gt;: ROCm exists there, but Unsloth fine-tuning is still dramatically less predictable than on Linux. If your goal is “fine-tune this weekend”, use Linux.&lt;/li&gt;
&lt;li&gt;If you’re on &lt;strong&gt;WSL2&lt;/strong&gt;: it’s great for a lot of dev. For ROCm fine-tuning, you’re stacking two “almost works” layers. If you want my honest recommendation: don’t.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on the benchmark data I maintain at &lt;a href="https://www.kunalganglani.com/llm-benchmarks" rel="noopener noreferrer"&gt;kunalganglani.com/llm-benchmarks&lt;/a&gt;, the pattern I keep seeing across non-NVIDIA platforms is: &lt;strong&gt;getting the model to load is easy; getting stable throughput is the real work&lt;/strong&gt;. ROCm fine-tuning is the same story.&lt;/p&gt;

&lt;p&gt;(Visual break: “known good” command snippet screenshot)&lt;/p&gt;

&lt;h2&gt;
  
  
  Install ROCm and verify HIP works (rocminfo/hipcc)
&lt;/h2&gt;

&lt;p&gt;This section is intentionally boring. Boring is the point. If you skip verification, you’ll spend the next 6 hours blaming Unsloth for a driver/runtime problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Pick a supported OS and kernel first
&lt;/h3&gt;

&lt;p&gt;Start with a mainstream distro ROCm expects. In 2026, Ubuntu LTS is the pragmatic choice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu &lt;strong&gt;24.04 LTS&lt;/strong&gt; is a solid baseline.&lt;/li&gt;
&lt;li&gt;Linux kernel &lt;strong&gt;6.x&lt;/strong&gt; is common.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The moment you go off-road (custom kernels, odd GCC/libstdc++ combos), you’re volunteering to debug C++ ABI errors instead of training models.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Install ROCm from AMD’s docs
&lt;/h3&gt;

&lt;p&gt;Use AMD’s official install steps and their compatibility matrix: &lt;a href="https://rocm.docs.amd.com/" rel="noopener noreferrer"&gt;AMD ROCm documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Practical advice I stick to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep your ROCm install &lt;strong&gt;single-source&lt;/strong&gt;. Don’t mix distro packages + random tarballs + Docker bind mounts unless you know exactly why.&lt;/li&gt;
&lt;li&gt;If you’re using containers (often best for Radeon), make sure the host driver stack matches what the container expects. ROCm is not magic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3) Verify &lt;code&gt;rocminfo&lt;/code&gt; and &lt;code&gt;hipcc&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;After install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# should print GPU + agent info&lt;/span&gt;
/opt/rocm/bin/rocminfo | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 50

&lt;span class="c"&gt;# should print ROCm/HIP compiler info&lt;/span&gt;
/opt/rocm/bin/hipcc &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;rocminfo&lt;/code&gt; can’t see your GPU, stop. Don’t “try pip installs until it works”. Fix the driver/runtime first.&lt;/p&gt;

&lt;p&gt;Common failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rocminfo: HSA runtime not found&lt;/code&gt; → ROCm userland isn’t installed correctly or not on PATH.&lt;/li&gt;
&lt;li&gt;Permission issues accessing &lt;code&gt;/dev/kfd&lt;/code&gt; → user not in the right group (often &lt;code&gt;video&lt;/code&gt;/&lt;code&gt;render&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install PyTorch for ROCm (matching wheel to ROCm)
&lt;/h2&gt;

&lt;p&gt;This is the #1 root cause of “it imports, but training explodes”. The PyTorch ROCm wheel bakes assumptions about the ROCm runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  The only rule that matters
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Your installed ROCm runtime version and your PyTorch ROCm build must match.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PyTorch makes this easy if you actually use their selector. On the official page, the compute platform dropdown includes &lt;strong&gt;ROCm 6.3&lt;/strong&gt; and generates the matching &lt;code&gt;--index-url&lt;/code&gt; command. That selector is maintained by the people shipping the wheels. Use it: &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Example (ROCm wheel install style):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

&lt;span class="c"&gt;# example only. generate your exact command from pytorch.org&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
pip &lt;span class="nb"&gt;install &lt;/span&gt;torch torchvision torchaudio &lt;span class="nt"&gt;--index-url&lt;/span&gt; https://download.pytorch.org/whl/rocm6.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify PyTorch sees the AMD GPU
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
import torch
print('torch', torch.__version__)
print('hip available', torch.cuda.is_available())
print('device', torch.cuda.get_device_name(0) if torch.cuda.is_available() else None)
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, it still prints &lt;code&gt;cuda&lt;/code&gt; in APIs. No, you’re not secretly using NVIDIA.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;is_available()&lt;/code&gt; is false:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You installed a CPU wheel.&lt;/li&gt;
&lt;li&gt;Or you installed a ROCm wheel that doesn’t match your ROCm runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see linker errors about missing &lt;code&gt;libamdhip64.so&lt;/code&gt; or friends, you’re mismatched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up Transformers + TRL + Unsloth
&lt;/h2&gt;

&lt;p&gt;Unsloth rides on top of Hugging Face Transformers and TRL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unsloth repo: &lt;a href="https://github.com/unslothai/unsloth" rel="noopener noreferrer"&gt;Unsloth AI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TRL docs for trainer knobs (packing, clipping, etc.): &lt;a href="https://huggingface.co/docs/trl/index" rel="noopener noreferrer"&gt;TRL&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  My opinionated dependency strategy for AMD
&lt;/h3&gt;

&lt;p&gt;On CUDA I’m okay with “latest-ish” because the ecosystem is brutally well-tested.&lt;/p&gt;

&lt;p&gt;On ROCm I pin. Always.&lt;/p&gt;

&lt;p&gt;Pin your layers like you mean it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pin &lt;code&gt;torch&lt;/code&gt; to the ROCm wheel you installed.&lt;/li&gt;
&lt;li&gt;Pin &lt;code&gt;transformers&lt;/code&gt;, &lt;code&gt;trl&lt;/code&gt;, &lt;code&gt;accelerate&lt;/code&gt;, &lt;code&gt;peft&lt;/code&gt; to a tested set.&lt;/li&gt;
&lt;li&gt;Upgrade only one layer at a time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"transformers==4.56.*"&lt;/span&gt; &lt;span class="s2"&gt;"trl==0.12.*"&lt;/span&gt; &lt;span class="s2"&gt;"accelerate==1.0.*"&lt;/span&gt; &lt;span class="s2"&gt;"peft==0.13.*"&lt;/span&gt;

&lt;span class="c"&gt;# install unsloth&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"unsloth[rocm]"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;unsloth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the extra &lt;code&gt;[rocm]&lt;/code&gt; doesn’t exist in the current Unsloth packaging when you read this, that’s fine. The point is: install Unsloth, then verify what optional kernels it pulled in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attention kernels: assume SDPA first
&lt;/h3&gt;

&lt;p&gt;One of the most common traps is people assuming FlashAttention is mandatory because every NVIDIA guide reads like it’s oxygen.&lt;/p&gt;

&lt;p&gt;On AMD, &lt;strong&gt;start with SDPA&lt;/strong&gt; (PyTorch scaled dot-product attention). Chase FlashAttention only after you have a converging baseline.&lt;/p&gt;

&lt;p&gt;Why I’m so stubborn about this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FlashAttention availability on ROCm is architecture- and version-dependent.&lt;/li&gt;
&lt;li&gt;The failure mode is often not “missing package”. It’s “Triton compiled something, ran it, and now you get NaNs at step 200.” That’s the worst kind of failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Run a minimal LoRA/SFT sanity check (prove it converges)
&lt;/h2&gt;

&lt;p&gt;If you only do one thing from this post, do this. Don’t launch your full dataset run first. That’s how you burn an evening.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 5-step checklist (minimal moving parts)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Confirm &lt;code&gt;rocminfo&lt;/code&gt; sees the GPU.&lt;/li&gt;
&lt;li&gt;Confirm PyTorch ROCm sees the GPU (&lt;code&gt;torch.cuda.is_available()&lt;/code&gt; is true).&lt;/li&gt;
&lt;li&gt;Run a &lt;strong&gt;single-batch forward+backward&lt;/strong&gt; in fp16.&lt;/li&gt;
&lt;li&gt;Run &lt;strong&gt;50–200 training steps&lt;/strong&gt; on a tiny dataset.&lt;/li&gt;
&lt;li&gt;Confirm loss decreases smoothly and no NaNs appear.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s your “stack is sane” certificate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimal TRL SFTTrainer script (ROCm-friendly defaults)
&lt;/h3&gt;

&lt;p&gt;This uses conservative knobs: fp16, gradient checkpointing, modest learning rate, clipping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# sft_rocm_sanity.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TOKENIZERS_PARALLELISM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SFTTrainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SFTConfig&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LoraConfig&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unsloth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastLanguageModel&lt;/span&gt;

&lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta-llama/Meta-Llama-3-8B-Instruct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# 8B class is a sane starting point
&lt;/span&gt;
&lt;span class="c1"&gt;# Tiny dataset on purpose
&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_list&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a haiku about GPUs.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain what ROCm is in one paragraph.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the difference between LoRA and full fine-tuning.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;max_seq_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastLanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_in_4bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# start without QLoRA on AMD
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;lora_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoraConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lora_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lora_dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CAUSAL_LM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;peft_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lora_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SFTConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./out_rocm_sanity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2e-4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;warmup_ratio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;logging_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;fp16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bf16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gradient_checkpointing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_grad_norm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;packing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;report_to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python sft_rocm_sanity.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What “good” looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loss starts high and trends down over the first &lt;strong&gt;50–200 steps&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;nan&lt;/code&gt; loss. No sudden spikes to absurd values.&lt;/li&gt;
&lt;li&gt;GPU utilization is non-zero and stable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you get NaNs, don’t immediately blame ROCm. Do the boring trainer triage first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower LR from &lt;code&gt;2e-4&lt;/code&gt; → &lt;code&gt;1e-4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;max_grad_norm=1.0&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;Turn off packing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TRL documents these knobs and what they change: &lt;a href="https://huggingface.co/docs/trl/index" rel="noopener noreferrer"&gt;TRL&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshoot common ROCm errors (HIP runtime, missing libs, kernel image issues)
&lt;/h2&gt;

&lt;p&gt;Most guides turn this section into an error-message graveyard. That’s useless. You want a decision tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) “HIP error / invalid device function / no kernel image available”
&lt;/h3&gt;

&lt;p&gt;Translation: a compiled kernel doesn’t match your GPU architecture, or you installed a build that simply wasn’t compiled for your card.&lt;/p&gt;

&lt;p&gt;Fix path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm your GPU is actually supported by your ROCm version in AMD’s matrix: &lt;a href="https://rocm.docs.amd.com/" rel="noopener noreferrer"&gt;AMD ROCm documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Confirm your PyTorch wheel matches your ROCm runtime. Reinstall PyTorch using the official selector: &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If this happens when enabling FlashAttention/Triton kernels: force SDPA and disable fused attention paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful env knobs to try (these move around across versions, so treat this as a debugging tactic, not a forever setting):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TORCH_LOGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"+dynamo,+inductor"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TORCHDYNAMO_VERBOSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see Triton compilation errors referencing HIP, that’s your sign to stop chasing fancy kernels and get a clean SDPA baseline.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Missing &lt;code&gt;hipblaslt&lt;/code&gt; / &lt;code&gt;rccl&lt;/code&gt; / shared library errors
&lt;/h3&gt;

&lt;p&gt;This is almost always packaging mismatch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ROCm userland not fully installed.&lt;/li&gt;
&lt;li&gt;Container image expects a different ROCm stack than the host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My stance: &lt;strong&gt;containers are usually more reliable on Radeon&lt;/strong&gt;, because you can pin userland and Python deps together. But your host driver still matters.&lt;/p&gt;

&lt;p&gt;If you want the broader ROCm vs CUDA story for &lt;a href="https://dev.to/blog/amd-rocm-vs-cuda-local-ai-open-source-guide"&gt;local AI&lt;/a&gt;, I wrote that comparison already. This post is narrower: making Unsloth fine-tuning stop failing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) bitsandbytes fails on ROCm (4-bit/8-bit)
&lt;/h3&gt;

&lt;p&gt;This question shows up constantly: “Why does bitsandbytes 4-bit/8-bit quantization fail on ROCm?”&lt;/p&gt;

&lt;p&gt;Because bitsandbytes has historically been CUDA-first. ROCm support has improved, but it’s still fragile, and it lags the newest training stacks.&lt;/p&gt;

&lt;p&gt;Practical alternatives that don’t waste your weekend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FP16 LoRA&lt;/strong&gt; (the baseline I recommend in this post).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8-bit optimizers&lt;/strong&gt; only if your ROCm build supports the exact kernel path you need.&lt;/li&gt;
&lt;li&gt;If your goal is cheap inference, skip training quantization entirely. Use GGUF for inference-only. I’ve written about quantization tradeoffs in &lt;a href="https://dev.to/blog/llm-quantization-levels-q4-q8-fp16"&gt;LLM quantization levels&lt;/a&gt; and &lt;a href="https://dev.to/blog/llm-quantization-gguf-gptq-exl2"&gt;GGUF vs GPTQ vs EXL2&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re on a 16GB Radeon card and trying to QLoRA a 13B model because TikTok told you it’s “easy”, stop. Use a smaller base model or add VRAM.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) “It runs” but throughput is awful (kernel fallbacks)
&lt;/h3&gt;

&lt;p&gt;This is the sneaky failure. No crash. No obvious error. Just performance that feels like you’re training through a straw.&lt;/p&gt;

&lt;p&gt;Symptoms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPU utilization is low.&lt;/li&gt;
&lt;li&gt;Tokens/sec is way lower than expected.&lt;/li&gt;
&lt;li&gt;You see unexpected CPU usage spikes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How to diagnose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Turn on PyTorch compile logs (&lt;code&gt;TORCH_LOGS&lt;/code&gt; above) to see if Inductor/Triton is bailing.&lt;/li&gt;
&lt;li&gt;Start from SDPA. If FlashAttention “works” but is slower, you’re probably hitting a fallback path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also: measure something real.&lt;/p&gt;

&lt;p&gt;In my benchmark work for this site, throughput is the metric people hand-wave the most. That’s why I keep pushing tok/s and TTFT in posts like &lt;a href="https://dev.to/blog/local-llm-benchmark-methodology"&gt;local LLM benchmark methodology&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5) NaNs, divergence, and fake convergence
&lt;/h3&gt;

&lt;p&gt;On AMD you’ll sometimes get “training” that doesn’t crash but is numerically unstable.&lt;/p&gt;

&lt;p&gt;Things I check, in this order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loss: do you ever see &lt;code&gt;nan&lt;/code&gt;? If yes, don’t continue.&lt;/li&gt;
&lt;li&gt;Gradient clipping: set &lt;code&gt;max_grad_norm&lt;/code&gt; to &lt;strong&gt;1.0&lt;/strong&gt; (or even 0.5).&lt;/li&gt;
&lt;li&gt;Precision: prefer &lt;strong&gt;fp16&lt;/strong&gt; first. bf16 can be great, but bf16 stability varies by GPU + stack.&lt;/li&gt;
&lt;li&gt;Sequence packing: turn it off until you’re stable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rule of thumb VRAM budgets (practical, not theoretical):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;7B/8B&lt;/strong&gt; LoRA: 16GB can work with small batch, seq len ~512–1024, grad checkpointing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;13B&lt;/strong&gt; LoRA: 24GB is the sane starting point for non-suffering.&lt;/li&gt;
&lt;li&gt;Anything beyond: either multi-GPU (more pain on Radeon) or change strategy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the bigger hardware sizing story for &lt;a href="https://dev.to/blog/local-llm-hardware-requirements-2026"&gt;local LLM&lt;/a&gt; work, I have a dedicated guide. This post stays focused on Unsloth + ROCm.&lt;/p&gt;

&lt;p&gt;Here’s an official baseline walkthrough from AMD itself. It’s worth watching once to calibrate your environment expectations:&lt;/p&gt;

&lt;p&gt;[YOUTUBE:GNF32XuRAN4|How to Fine-Tune a Model on AMD GPUs using LoRA]&lt;/p&gt;

&lt;h2&gt;
  
  
  Container vs bare-metal for Radeon: my default recommendation
&lt;/h2&gt;

&lt;p&gt;If you’re on consumer Radeon, I generally recommend containers for fine-tuning.&lt;/p&gt;

&lt;p&gt;Reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can pin Python deps and ROCm userland together.&lt;/li&gt;
&lt;li&gt;You can share a known-good image with your team.&lt;/li&gt;
&lt;li&gt;You reduce “my system Python upgraded libstdc++ and now hip runtime crashes” incidents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bare-metal is fine too if you’re disciplined. Most people aren’t.&lt;/p&gt;

&lt;p&gt;If you do bare-metal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep a dedicated venv per ROCm/PyTorch stack.&lt;/li&gt;
&lt;li&gt;Don’t upgrade blindly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mindset is similar to what I preach for reproducible dev envs in general, like in my &lt;a href="https://dev.to/blog/python-ai-development-setup-2026"&gt;Python dev setup&lt;/a&gt; and &lt;a href="https://dev.to/blog/linux-migration-plan-developers"&gt;Linux migration plan&lt;/a&gt; posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  My prediction for 2027: “ROCm works” won’t be enough
&lt;/h2&gt;

&lt;p&gt;By 2027, “can I run it?” will be boring. It’ll be solved for AMD consumer GPUs.&lt;/p&gt;

&lt;p&gt;The dividing line will be: &lt;strong&gt;can you update your stack without accidentally losing fast kernels, falling back to slow paths, or reintroducing numerical weirdness?&lt;/strong&gt; NVIDIA has earned that kind of stability. AMD is getting closer, but Unsloth-style optimization stacks are exactly where the cracks still show.&lt;/p&gt;

&lt;p&gt;If you’re building a serious fine-tuning workflow on Radeon today, don’t treat this as a one-off setup. Turn it into a pinned, scripted environment you can recreate on a fresh machine. That’s how you stop ROCm from becoming a recurring calendar event.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://www.kunalganglani.com/blog/unsloth-amd-gpu-rocm-fine-tuning-guide?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=unsloth-amd-gpu-rocm-fine-tuning-guide" rel="noopener noreferrer"&gt;kunalganglani.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>amd</category>
      <category>rocm</category>
      <category>finetuning</category>
      <category>unsloth</category>
    </item>
    <item>
      <title>Enterprise Fine-Tuning Services: Scale Smarter AI Solutions</title>
      <dc:creator>Michael Keller</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:58:17 +0000</pubDate>
      <link>https://dev.to/michael_keller_9d83ef0ce5/enterprise-fine-tuning-services-scale-smarter-ai-solutions-3636</link>
      <guid>https://dev.to/michael_keller_9d83ef0ce5/enterprise-fine-tuning-services-scale-smarter-ai-solutions-3636</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5u9tmwsriz71z5vj1gob.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5u9tmwsriz71z5vj1gob.jpg" alt=" " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enterprise organizations are adopting artificial intelligence to improve operational efficiency, support employees, automate repetitive tasks, and deliver more personalized customer experiences. However, general-purpose AI models may not always perform effectively when applied to specialized business processes. &lt;strong&gt;&lt;a href="https://zignuts.com/llm-genai-services/fine-tuning?utm_source=seo_web2.0&amp;amp;utm_medium=backlink&amp;amp;utm_campaign=seo_referral&amp;amp;utm_id=9" rel="noopener noreferrer"&gt;Enterprise Fine Tuning Services&lt;/a&gt;&lt;/strong&gt; help organizations adapt AI models to specific operational requirements, domain terminology, response formats, and workflow expectations.&lt;/p&gt;

&lt;p&gt;Large organizations often manage complex systems, multiple departments, diverse datasets, and strict security requirements. A model that performs well in a general demonstration may produce inconsistent results when introduced into a real enterprise environment. Fine-tuning can help address certain behavioral and task-specific limitations, particularly when a business has reliable training data and a clearly defined objective.&lt;/p&gt;

&lt;p&gt;Enterprise fine-tuning is not simply about training a model with more information. It requires careful planning around model selection, data preparation, infrastructure, security, evaluation, deployment, and ongoing maintenance. Business leaders must understand when customization creates value, when other approaches are more suitable, and how to scale AI responsibly across the organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Enterprise Fine-Tuning Services?
&lt;/h2&gt;

&lt;p&gt;Enterprise fine-tuning services involve adapting a pretrained AI model to meet the requirements of a particular organization or business function. The process uses curated examples to improve how a model performs a specific task or follows a defined response pattern.&lt;/p&gt;

&lt;p&gt;Depending on the use case, fine-tuning may help a model learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Industry-specific terminology&lt;/li&gt;
&lt;li&gt;Organization-specific communication patterns&lt;/li&gt;
&lt;li&gt;Structured classification categories&lt;/li&gt;
&lt;li&gt;Document processing formats&lt;/li&gt;
&lt;li&gt;Specialized customer service responses&lt;/li&gt;
&lt;li&gt;Internal workflow instructions&lt;/li&gt;
&lt;li&gt;Domain-specific language patterns&lt;/li&gt;
&lt;li&gt;Consistent output structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fine-tuning does not automatically provide continuous access to current enterprise information. A model trained on historical examples may still require retrieval systems, application integrations, or external tools to access updated policies, records, and business data.&lt;/p&gt;

&lt;p&gt;The objective is to adapt model behavior for a particular task, not to assume that fine-tuning alone can solve every enterprise AI requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2027 Outlook for Enterprise AI Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;As enterprise AI adoption develops, organizations are likely to combine multiple customization methods based on their workflows, data, and performance requirements. Fine-tuning may become one component of a broader AI architecture that includes retrieval, automation, governance, and evaluation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expected Direction in 2027&lt;/th&gt;
&lt;th&gt;Potential Development&lt;/th&gt;
&lt;th&gt;Enterprise Business Implication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Specialized model deployment&lt;/td&gt;
&lt;td&gt;Organizations may customize models for individual departments and business functions&lt;/td&gt;
&lt;td&gt;AI systems can be aligned more closely with operational needs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efficient training methods&lt;/td&gt;
&lt;td&gt;Businesses may explore parameter-efficient fine-tuning and smaller specialized models&lt;/td&gt;
&lt;td&gt;Some use cases may require fewer computational resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated evaluation&lt;/td&gt;
&lt;td&gt;Fine-tuned models may be tested through structured business-specific benchmarks&lt;/td&gt;
&lt;td&gt;Enterprises can make more informed deployment decisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid AI architectures&lt;/td&gt;
&lt;td&gt;Fine-tuning may be combined with retrieval, tools, and workflow automation&lt;/td&gt;
&lt;td&gt;Businesses can use different methods for different requirements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are strategic expectations rather than confirmed outcomes. Their success will depend on model capabilities, dataset quality, infrastructure, governance, security, and organizational readiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Enterprises Consider Model Fine-Tuning
&lt;/h2&gt;

&lt;p&gt;General-purpose AI models are designed to handle many types of tasks. This flexibility makes them useful for broad applications, but it can create limitations when organizations need highly consistent performance in specialized workflows.&lt;/p&gt;

&lt;p&gt;For example, an enterprise may require an AI system to classify thousands of service requests according to internal categories. A general-purpose model may understand the overall meaning of a request but struggle to apply the company’s exact classification rules consistently.&lt;/p&gt;

&lt;p&gt;Fine-tuning may be considered when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The task is repeated frequently&lt;/li&gt;
&lt;li&gt;The organization has high-quality examples&lt;/li&gt;
&lt;li&gt;The desired behavior is difficult to achieve through prompting alone&lt;/li&gt;
&lt;li&gt;Consistent formatting is important&lt;/li&gt;
&lt;li&gt;The workflow requires specialized terminology&lt;/li&gt;
&lt;li&gt;The business needs predictable task-specific performance&lt;/li&gt;
&lt;li&gt;The organization can support testing and ongoing evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fine-tuning should be assessed against alternatives. If a workflow mainly requires access to current documents, retrieval-augmented generation may be more appropriate. If the issue is unclear instructions, prompt engineering may be sufficient. If the task follows fixed rules, traditional software logic may offer more predictable results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enterprise Fine-Tuning Compared With Other AI Customization Methods
&lt;/h2&gt;

&lt;p&gt;A strategic AI implementation begins with identifying the actual problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt Engineering
&lt;/h3&gt;

&lt;p&gt;Prompt engineering improves how instructions are written for an existing model. It can define roles, context, task requirements, output formats, and restrictions.&lt;/p&gt;

&lt;p&gt;This approach may be suitable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model already understands the subject&lt;/li&gt;
&lt;li&gt;The task changes frequently&lt;/li&gt;
&lt;li&gt;The organization needs rapid iteration&lt;/li&gt;
&lt;li&gt;Training data is limited&lt;/li&gt;
&lt;li&gt;The desired improvement can be achieved through clearer instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Retrieval-Augmented Generation
&lt;/h3&gt;

&lt;p&gt;Retrieval-augmented generation connects an AI model with external sources such as internal documentation, knowledge bases, and business databases.&lt;/p&gt;

&lt;p&gt;RAG is often useful when the system needs access to information that changes regularly. It can provide relevant context without requiring the model to be retrained whenever a document or policy is updated.&lt;/p&gt;

&lt;p&gt;However, retrieval quality, source permissions, document structure, and citation behavior must be evaluated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-Tuning
&lt;/h3&gt;

&lt;p&gt;Fine-tuning adapts model behavior through additional training on task-specific examples. It may be appropriate for classification, formatting, response style, and other repeatable behaviors.&lt;/p&gt;

&lt;p&gt;Fine-tuning and RAG can be used together. For example, a model may be fine-tuned to follow a specific output format while retrieval supplies current enterprise information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Enterprise Applications
&lt;/h2&gt;

&lt;p&gt;Fine-tuning can support multiple enterprise use cases, but the suitability of each application depends on data quality, risk level, and performance requirements.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Enterprise Function&lt;/th&gt;
&lt;th&gt;Potential Fine-Tuning Use Case&lt;/th&gt;
&lt;th&gt;Possible Business Benefit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Customer support&lt;/td&gt;
&lt;td&gt;Classify customer requests and follow approved response patterns&lt;/td&gt;
&lt;td&gt;More consistent handling of recurring inquiries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document processing&lt;/td&gt;
&lt;td&gt;Extract or categorize information from specialized documents&lt;/td&gt;
&lt;td&gt;Reduced manual organization and processing effort&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal knowledge operations&lt;/td&gt;
&lt;td&gt;Generate structured summaries using defined formats&lt;/td&gt;
&lt;td&gt;More consistent internal documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Financial operations&lt;/td&gt;
&lt;td&gt;Categorize selected records or identify predefined data patterns&lt;/td&gt;
&lt;td&gt;Improved support for repetitive information processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sales enablement&lt;/td&gt;
&lt;td&gt;Organize account information and classify customer intent&lt;/td&gt;
&lt;td&gt;More standardized preparation for sales activities&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These use cases require appropriate validation. Fine-tuned models should not be treated as autonomous decision-makers in sensitive processes without suitable controls and human oversight.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Enterprise Fine-Tuning Workflow
&lt;/h2&gt;

&lt;p&gt;A scalable fine-tuning project should follow a structured development process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business Objective → Data Assessment → Model Selection → Training → Evaluation → Controlled Deployment → Continuous Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each stage should have clear ownership, success criteria, and documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Define the Business Objective
&lt;/h3&gt;

&lt;p&gt;The project should begin with a specific operational or strategic goal. Instead of pursuing model customization simply because it is technically possible, enterprises should identify the business problem they want to address.&lt;/p&gt;

&lt;p&gt;Possible objectives include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improving document classification&lt;/li&gt;
&lt;li&gt;Reducing repetitive manual processing&lt;/li&gt;
&lt;li&gt;Standardizing customer response drafts&lt;/li&gt;
&lt;li&gt;Supporting specialized information extraction&lt;/li&gt;
&lt;li&gt;Improving the consistency of internal summaries&lt;/li&gt;
&lt;li&gt;Reducing the number of corrections required in a workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective should be connected to measurable outcomes. For example, a business may evaluate whether fine-tuning reduces classification errors or improves the consistency of structured outputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Assess the Data
&lt;/h3&gt;

&lt;p&gt;Training data is a major factor in the success of fine-tuning. Enterprises should review the availability, quality, ownership, and permitted use of the data before beginning development.&lt;/p&gt;

&lt;p&gt;Important considerations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the data relevant to the task?&lt;/li&gt;
&lt;li&gt;Does it represent real business scenarios?&lt;/li&gt;
&lt;li&gt;Are the labels consistent?&lt;/li&gt;
&lt;li&gt;Does it contain duplicate or conflicting examples?&lt;/li&gt;
&lt;li&gt;Is sensitive information handled appropriately?&lt;/li&gt;
&lt;li&gt;Does the dataset include difficult and unusual cases?&lt;/li&gt;
&lt;li&gt;Is the data sufficiently current for the intended use?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A large dataset is not automatically a high-quality dataset. Poor examples can teach the model incorrect patterns and create additional evaluation challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Select an Appropriate Model
&lt;/h3&gt;

&lt;p&gt;Model selection should consider the intended task and deployment environment. Relevant factors may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model capabilities&lt;/li&gt;
&lt;li&gt;Fine-tuning support&lt;/li&gt;
&lt;li&gt;Context requirements&lt;/li&gt;
&lt;li&gt;Inference cost&lt;/li&gt;
&lt;li&gt;Response latency&lt;/li&gt;
&lt;li&gt;Hosting options&lt;/li&gt;
&lt;li&gt;Security requirements&lt;/li&gt;
&lt;li&gt;Integration compatibility&lt;/li&gt;
&lt;li&gt;Maintenance complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enterprises should evaluate whether a large model, smaller specialized model, or existing &lt;a href="https://en.wikipedia.org/wiki/Open-source_software" rel="noopener noreferrer"&gt;open-source model&lt;/a&gt; is appropriate for the workflow. The most advanced model is not always the most practical option for a narrow and repetitive task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training Data and Enterprise Governance
&lt;/h2&gt;

&lt;p&gt;Enterprise data often includes confidential information, customer records, internal policies, and commercially sensitive material. Fine-tuning projects must therefore incorporate governance from the beginning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Privacy
&lt;/h3&gt;

&lt;p&gt;Organizations should determine whether data can be used for training and whether sensitive information must be removed, anonymized, or protected through other methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Management
&lt;/h3&gt;

&lt;p&gt;Only authorized teams should be able to access training datasets, model checkpoints, evaluation results, and deployment environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Lineage
&lt;/h3&gt;

&lt;p&gt;Teams should document where training examples originated, how they were processed, and which version of the dataset was used. This information supports troubleshooting and audit requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Policy Alignment
&lt;/h3&gt;

&lt;p&gt;The fine-tuning workflow should align with applicable organizational policies, contractual obligations, and relevant regulatory requirements. Specific controls will vary depending on the industry and use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dataset Maintenance
&lt;/h3&gt;

&lt;p&gt;Enterprise data changes over time. New products, customer behaviors, policies, and workflows may create patterns that are not represented in the original dataset. The organization should define when data needs to be reviewed or updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluating Fine-Tuned Enterprise Models
&lt;/h2&gt;

&lt;p&gt;Evaluation should determine whether the fine-tuned model performs better for the intended task than an appropriate baseline.&lt;/p&gt;

&lt;p&gt;A baseline might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A general-purpose model&lt;/li&gt;
&lt;li&gt;A model using improved prompts&lt;/li&gt;
&lt;li&gt;A retrieval-based workflow&lt;/li&gt;
&lt;li&gt;An existing manual process&lt;/li&gt;
&lt;li&gt;A traditional &lt;a href="https://en.wikipedia.org/wiki/Machine_learning" rel="noopener noreferrer"&gt;machine learning system&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Evaluation criteria should reflect the actual use case.&lt;/p&gt;

&lt;p&gt;For a classification workflow, useful measures may include accuracy, precision, recall, and error distribution. For structured content generation, the evaluation may focus on completeness, formatting, relevance, and adherence to business requirements.&lt;/p&gt;

&lt;p&gt;Testing should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Common inputs&lt;/li&gt;
&lt;li&gt;Unusual examples&lt;/li&gt;
&lt;li&gt;Incomplete information&lt;/li&gt;
&lt;li&gt;Ambiguous requests&lt;/li&gt;
&lt;li&gt;Conflicting information&lt;/li&gt;
&lt;li&gt;Out-of-scope questions&lt;/li&gt;
&lt;li&gt;Inputs that require escalation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A model should not be approved based only on examples it handled successfully. Enterprises need to understand where the system fails and how those failures will be managed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Executive Questions Before Scaling Fine-Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What problem will fine-tuning solve?
&lt;/h3&gt;

&lt;p&gt;Leaders should define the specific limitation that requires model customization. If the problem can be solved through better prompts, improved retrieval, or workflow rules, fine-tuning may introduce unnecessary complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do we have reliable training examples?
&lt;/h3&gt;

&lt;p&gt;The organization should evaluate whether the available data accurately represents the intended task. Missing categories, inconsistent labels, and low-quality examples can reduce the value of the training process.&lt;/p&gt;

&lt;h3&gt;
  
  
  How will the model be monitored after deployment?
&lt;/h3&gt;

&lt;p&gt;Enterprise AI systems require ongoing monitoring because data patterns, user expectations, and business requirements can change. Teams should define how performance issues will be detected and addressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the consequences of incorrect outputs?
&lt;/h3&gt;

&lt;p&gt;The risk associated with an incorrect classification or response varies by workflow. Customer communications, financial processes, and sensitive internal operations may require additional approval and validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can the solution scale economically?
&lt;/h3&gt;

&lt;p&gt;The total cost may include data preparation, training, infrastructure, inference, evaluation, security, monitoring, and maintenance. Leaders should assess the complete operating model rather than only the initial development expense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who owns the model after deployment?
&lt;/h3&gt;

&lt;p&gt;Ownership should cover performance monitoring, dataset updates, version control, incident response, and decisions about retraining or rollback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges in Enterprise Fine-Tuning
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data Inconsistency
&lt;/h3&gt;

&lt;p&gt;Enterprise datasets may come from different systems and departments. Differences in terminology, formatting, and labeling can make training more difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model Overfitting
&lt;/h3&gt;

&lt;p&gt;A model may memorize or over-adapt to training examples instead of learning patterns that generalize to new inputs. Separate evaluation data and carefully designed testing can help identify this issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Model Drift
&lt;/h3&gt;

&lt;p&gt;Performance may change as business processes, customer behavior, and data patterns evolve. Ongoing monitoring is necessary to determine whether the model remains suitable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure Requirements
&lt;/h3&gt;

&lt;p&gt;Training and hosting may require specialized infrastructure, technical expertise, and cost management. Enterprises should assess whether the chosen approach fits their operational environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Exposure
&lt;/h3&gt;

&lt;p&gt;Sensitive training data, model artifacts, and deployment interfaces may introduce security risks. Access control, monitoring, encryption, and appropriate data handling procedures should be considered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintenance Complexity
&lt;/h3&gt;

&lt;p&gt;Fine-tuned models require ongoing management. Organizations may need to update datasets, evaluate new versions, monitor quality, and maintain compatibility with other enterprise systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Excessive Reliance on Model Output
&lt;/h3&gt;

&lt;p&gt;A fine-tuned model may produce consistent but incorrect responses. Enterprises should establish review procedures for high-impact tasks and avoid treating model confidence or fluency as proof of accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Scalable Enterprise Fine-Tuning Strategy
&lt;/h2&gt;

&lt;p&gt;A sustainable strategy should consider both technical performance and organizational adoption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Establish a Pilot Project
&lt;/h3&gt;

&lt;p&gt;Begin with a well-defined workflow that has measurable outcomes and manageable risk. A pilot allows the team to evaluate the training process, deployment requirements, and user feedback before expanding the solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Reusable Standards
&lt;/h3&gt;

&lt;p&gt;Develop common standards for data preparation, testing, documentation, security, and deployment. These standards can reduce duplicated effort across departments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separate Development and Production
&lt;/h3&gt;

&lt;p&gt;Testing environments should be separated from production systems where appropriate. This helps teams evaluate changes without affecting active business workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduce Version Management
&lt;/h3&gt;

&lt;p&gt;Track model versions, training datasets, prompts, configuration settings, and evaluation results. Version management supports troubleshooting and controlled releases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plan for Rollback
&lt;/h3&gt;

&lt;p&gt;If a new model version performs poorly, the organization should have a process for reverting to a previously approved version or switching to a fallback workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Train Employees
&lt;/h3&gt;

&lt;p&gt;Employees should understand the model’s purpose, limitations, appropriate usage, and escalation procedures. Technical deployment alone does not guarantee successful adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Enterprise Business Value
&lt;/h2&gt;

&lt;p&gt;Fine-tuning projects should be evaluated through business and technical measures. Potential indicators include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Task-specific accuracy&lt;/li&gt;
&lt;li&gt;Reduction in manual corrections&lt;/li&gt;
&lt;li&gt;Processing time&lt;/li&gt;
&lt;li&gt;Output consistency&lt;/li&gt;
&lt;li&gt;Cost per request&lt;/li&gt;
&lt;li&gt;Response latency&lt;/li&gt;
&lt;li&gt;Employee adoption&lt;/li&gt;
&lt;li&gt;Escalation frequency&lt;/li&gt;
&lt;li&gt;Error severity&lt;/li&gt;
&lt;li&gt;Workflow completion rates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measurements should be collected before and after implementation when possible. Organizations should also examine whether improvements in one area create new costs or risks elsewhere.&lt;/p&gt;

&lt;p&gt;For example, a model may reduce processing time but produce more errors that require additional review. A meaningful assessment should consider both efficiency and output quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of Enterprise AI Customization
&lt;/h2&gt;

&lt;p&gt;Enterprise AI architecture is likely to include multiple models and customization methods. Organizations may use smaller fine-tuned models for focused tasks, larger models for complex requests, and retrieval systems for access to current information.&lt;/p&gt;

&lt;p&gt;Model routing may allow businesses to select a system based on task complexity, cost, latency, security, and accuracy requirements. This approach can reduce the pressure to use a single model for every workflow.&lt;/p&gt;

&lt;p&gt;Fine-tuning may also become more closely integrated with &lt;a href="https://en.wikipedia.org/wiki/Intelligent_agent" rel="noopener noreferrer"&gt;AI agents&lt;/a&gt; and enterprise automation. However, greater integration increases the importance of permissions, monitoring, evaluation, and human oversight.&lt;/p&gt;

&lt;p&gt;Businesses should view fine-tuning as one component of a broader AI strategy. Its value will depend on how well it supports actual business processes and whether the organization can maintain the solution over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Enterprise Fine-Tuning Services can help organizations adapt AI models to specialized workflows, business terminology, response formats, and operational requirements. When supported by high-quality data, appropriate model selection, and structured evaluation, fine-tuning may improve consistency and task-specific performance.&lt;/p&gt;

&lt;p&gt;However, fine-tuning is not a universal solution. Enterprises should compare it with prompt engineering, retrieval-augmented generation, traditional software, and other customization approaches before making an investment.&lt;/p&gt;

&lt;p&gt;For business leaders, successful enterprise fine-tuning requires more than model training. It involves data governance, security, infrastructure planning, evaluation, monitoring, employee adoption, and long-term ownership. A structured approach can help organizations scale AI solutions while maintaining control over performance, cost, and risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. What are enterprise fine-tuning services?
&lt;/h3&gt;

&lt;p&gt;Enterprise fine-tuning services adapt pretrained AI models to specific organizational tasks, domains, workflows, or response requirements using curated training data.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Why do enterprises need fine-tuned AI models?
&lt;/h3&gt;

&lt;p&gt;Enterprises may need fine-tuned models when general-purpose systems do not consistently meet specialized task requirements and when prompting or retrieval alone is insufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Is fine-tuning suitable for every enterprise AI project?
&lt;/h3&gt;

&lt;p&gt;No. Fine-tuning may not be necessary when the main requirement involves current information, simple instructions, or fixed business rules. Organizations should evaluate alternative approaches before selecting fine-tuning.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. What data is needed for enterprise fine-tuning?
&lt;/h3&gt;

&lt;p&gt;The data should be relevant, accurate, representative, and consistently formatted. It should reflect the intended task and include important variations, edge cases, and examples requiring escalation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. How can enterprises evaluate a fine-tuned model?
&lt;/h3&gt;

&lt;p&gt;Organizations can compare the model with an appropriate baseline using task-specific metrics such as accuracy, consistency, relevance, latency, cost, and error frequency.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. What are the risks of enterprise fine-tuning?
&lt;/h3&gt;

&lt;p&gt;Potential risks include poor training data, overfitting, model drift, security exposure, maintenance costs, and inaccurate outputs. Governance, testing, and monitoring help manage these risks.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Can fine-tuning work with RAG?
&lt;/h3&gt;

&lt;p&gt;Yes. Fine-tuning can adapt model behavior, while RAG can provide access to relevant and updated external information. The combination should be tested according to the workflow’s requirements.&lt;/p&gt;

</description>
      <category>modelcustomization</category>
      <category>finetuning</category>
      <category>generativeai</category>
    </item>
  </channel>
</rss>
