# Gemma 4 微调指南

你现在可以训练 Google 的 [Gemma 4](https://unsloth.ai/docs/zh/mo-xing/qwen3.5) E2B、E4B、26B-A4B 和 31B，配合 [**Unsloth**](https://github.com/unslothai/unsloth)。Unsloth 支持 Gemma 4 的所有视觉、文本、音频和 RL 微调。

* Unsloth 训练 Gemma 4 的速度 **快约 1.5 倍** 并且仅需 **少约 60% 的显存** 相比 FA2 配置（无精度损失）
* 我们修复了许多通用的 [Gemma 4 训练错误](#bug-fixes--tips) （并非源自 Unsloth）。
* Gemma 4 E2B 训练可在 **8GB 显存**上运行。E4B 需要 10GB 显存。

<a href="#quickstart" class="button primary" data-icon="bolt">快速开始</a><a href="#bug-fixes--tips" class="button secondary" data-icon="sparkle">错误修复 + 提示</a>

微调 Gemma 4 通过我们的 **免费** **Google Colab 笔记本**:

| [**E4B + E2B** （Studio）](https://colab.research.google.com/github/unslothai/unsloth/blob/main/studio/Unsloth_Studio_Colab.ipynb) | [**31B** （Kaggle）](https://www.kaggle.com/code/danielhanchen/gemma4-31b-unsloth) | [E4B **（视觉 + 文本）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Vision.ipynb) | [E4B **（音频）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Audio.ipynb) |
| -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |

{% columns %}
{% column %}
你可以在我们的界面中免费运行和训练 Gemma 4，界面位于 [Unsloth Studio](https://unsloth.ai/docs/zh/xin/studio)✨ 笔记本：

你可以查看更多 [笔记本请见此处](#unsloth-core-code-based-guide).
{% endcolumn %}

{% column %}
{% embed url="<https://colab.research.google.com/github/unslothai/unsloth/blob/main/studio/Unsloth_Studio_Colab.ipynb>" %}
{% endcolumn %}
{% endcolumns %}

* Gemma 4 E2B LoRA 可在 8-10GB 显存上运行。E4B LoRA 需要 17GB 显存。
* **31B QLoRA 可在 22GB 下运行** 而 26B-A4B LoRA 需要 >40GB
* **导出**/将模型保存为 GGUF 等格式。 以及 全量微调 **（FFT）** 也可用。

### :bug: 错误修复 + 提示

{% hint style="success" %}
如果你看到 **Gemma-4 E2B 和 E4B 的 loss 为 13-15，这是完全正常的** ——这是多模态模型常见的一个特性。Gemma-3N、Llama Vision、Mistral vision 等模型也会出现这种情况。

**Gemma 26B 和 31B 的 loss 较低，通常在 1-3 或更低。视觉任务会高 2 倍，因此是 3-5**
{% endhint %}

#### :grapes:梯度累积可能会抬高你的 loss

{% columns %}
{% column %}

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FET1GgLeZanVHDpPXLkM9%2FTransformers%20%2B%20TRL%20%2B%20Gemma-4.png?alt=media&#x26;token=0149149f-4d34-4bcb-a545-42e12d5127eb" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}

{% column %}

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FZZola3h7ujfqz87VdnQm%2FUnsloth%20%2B%20Gemma-4.png?alt=media&#x26;token=37fc2b61-ae5b-4203-b9a7-388439aefae5" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}
{% endcolumns %}

如果你看到高于 13-15 的 loss（如 100 或 300），很可能是梯度累积没有被正确处理——我们已经 **将其作为 Unsloth 和 Unsloth Studio 的一部分修复。**

要了解更多关于梯度累积的信息，请查看我们的梯度累积错误修复博客： <https://unsloth.ai/blog/gradient>

#### :interrobang:Gemma-4 31B 和 26B-A4B 推理时的 IndexError

当你使用 31B 和 26B 进行推理时，可能会看到这个错误：

```python
文件 "/.../cache_utils.py"，第 937 行，在 update 中
    keys, values = self.layers[layer_idx].update(...)
IndexError：列表索引超出范围
```

问题根源如下：

```python
if hasattr(decoder_config, "num_kv_shared_layers"):
    layer_types = layer_types[: -decoder_config.num_kv_shared_layers]
```

其中 Gemma-4 31B 和 26B-A4B 附带 `num_kv_shared_layers = 0`。在 Python 中， `-0 == 0`，因此 `layer_types[:-0]` 会退化为 `layer_types[:0] == []`。缓存会以零层槽位构建，而第一次注意力前向传播就会在 `Cache.update`.

#### :no\_entry: `use_cache = True` E2B、E4B 的生成结果会是乱码

[见问题](https://github.com/huggingface/transformers/issues/45242) “\[Gemma 4] `use_cache=False` 会破坏注意力计算，产生错误 logits #45242”

Gemma-4 E2B 和 E4B 在层之间共享 KV 状态（`num_kv_shared_layers = 20` 以及 `18`）。缓存是早期层存储 KV 以供后续层复用的唯一位置。当 `use_cache=False` （正如每个 QLoRA 教程都会设置的那样，并且如 `gradient_checkpointing=True` 所强制的一样）， `Gemma4TextModel.forward` 会跳过缓存构建，因此 KV 共享层会退回为从当前隐藏状态本地重新计算 K 和 V。logits 会变成垃圾值，训练 loss 会发散。

**修复前（`unsloth/gemma-4-E2B-it`，提示词“1+1 等于多少？”）：**

```
use_cache=True  -> '1 + 1 = **2**'
use_cache=False -> 'BROAD\肯. Specificallyboard K supposed\_n통  \'
max_abs_logit_diff: 48.937500
```

**修复后：**

```
use_cache=True  -> '1 + 1 = **2**'
use_cache=False -> '1 + 1 = **2**'
max_abs_logit_diff: 0.000000     （位级完全一致，全部 9 个 token 相同）
```

#### :radio:音频 float16 溢出

`Gemma4AudioAttention` 使用 `config.attention_invalid_logits_value = -1e9` 在一个 `masked_fill` 调用中。在 fp16（Tesla T4）上，-1e9 超过了 fp16 最大值 65504，导致：

```python
RuntimeError：值无法在不溢出的情况下转换为类型 c10::Half
```

这是由于 `self.config.attention_invalid_logits_value` :

```python
attn_weights = attn_weights.masked_fill(
    attention_mask.logical_not(), self.config.attention_invalid_logits_value
)
```

#### 💡Gemma-4 提示

1. 如果你想 **保留推理** 能力，你可以将推理风格示例与直接答案混合使用（至少保留 75% 为推理）。否则你也可以完全输出它。\
   \
   使用 `gemma-4` 作为非思考型聊天模板，使用 `gemma-4-thinking` 作为思考型变体。\
   较大的 26B 和 31B 建议使用思考型，小模型则使用非思考型。<br>

   ```python
   from unsloth.chat_templates import get_chat_template
   tokenizer = get_chat_template(
       tokenizer,
       chat_template = "gemma-4-thinking", # 或 "gemma-4"
   )
   ```
2. 要启用思考模式，请使用 `enable_thinking = True / False` 在 `tokenizer.apply_chat_template`<br>

   已启用思考：

   <pre class="language-python" data-overflow="wrap"><code class="lang-python">processor.tokenizer.apply_chat_template([
       {"role" : "user", "content" : "2+2 等于多少？"},
   ], tokenize = False, enable_thinking = True, add_generation_prompt = True)
   </code></pre>

   将会打印 `<bos><|turn>system\n<|think|><turn|>\n<|turn>user\n2+2 等于多少？<turn|>\n<|turn>model\n`<br>

   已禁用思考：

   ```python
   processor.tokenizer.apply_chat_template([
       {"role" : "user", "content" : "2+2 等于多少？"},
   ], tokenize = False, enable_thinking = False, add_generation_prompt = True)
   ```

   将会打印 `<bos><|turn>user\n2+2 等于多少？<turn|>\n<|turn>model\n<|channel>thought\n<channel|>`
3. Gemma 4 非常适合多语言微调，因为它支持 140 种语言。
4. 建议训练 **E4B QLoRA** 而不是 **E2B LoRA** 因为 E4B 更大，而量化精度差异极小。Gemma 4 E4B LoRA 甚至更好。
5. 微调后，你可以导出为 [GGUF](#saving-export-your-fine-tuned-model) （用于 llama.cpp/Unsloth/Ollama 等）

### ⚡快速开始

#### 🦥 Unsloth Studio 指南

{% columns %}
{% column %}
Gemma 4 可以在 [Unsloth Studio](https://unsloth.ai/docs/zh/xin/studio)中运行和微调，这是我们新的本地 AI 开源网页界面。

使用 Unsloth Studio，你可以在本地运行模型于 **MacOS、Windows**、Linux 上，并训练 NVIDIA GPU。本月将支持 Intel、MLX 和 AMD 训练。
{% endcolumn %}

{% column %}

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FpZlhqoILYOzznGpbudUk%2Funsloth%20studio%20gemma%20graphic.png?alt=media&#x26;token=75e41585-e363-45cf-a87e-4d02960766ed" alt=""><figcaption></figcaption></figure></div>
{% endcolumn %}
{% endcolumns %}

{% stepper %}
{% step %}

#### 安装 Unsloth

在终端中运行：

**MacOS、Linux、WSL：**

```bash
curl -fsSL https://unsloth.ai/install.sh | sh
```

**Windows PowerShell：**

```bash
irm https://unsloth.ai/install.ps1 | iex
```

{% hint style="success" %}
**安装会很快，约需 1-2 分钟。**
{% endhint %}
{% endstep %}

{% step %}

#### 启动 Unsloth

**MacOS、Linux、WSL 和 Windows：**

```bash
unsloth studio -H 0.0.0.0 -p 8888
```

**然后打开 `http://localhost:8888` 在浏览器中。**
{% endstep %}

{% step %}

#### 训练 Gemma 4

首次启动时，你需要创建一个密码来保护你的账户，并在之后重新登录。随后你会看到一个简短的入门向导，用于选择模型、数据集和基本设置。你可以随时跳过它。

在搜索栏中搜索 Gemma 4，并选择你想要的模型和数据集。接着，按需调整你的超参数和上下文长度。

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FpZlhqoILYOzznGpbudUk%2Funsloth%20studio%20gemma%20graphic.png?alt=media&#x26;token=75e41585-e363-45cf-a87e-4d02960766ed" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

#### 监控训练进度

点击开始训练后，你将能够监控并观察模型的训练进度。训练 loss 应该会稳定下降。\
完成后，模型将自动保存。

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FeBrnu9zxARIkhOHzd0pq%2FScreenshot%202026-04-07%20at%205.53.32%E2%80%AFAM.png?alt=media&#x26;token=dae77231-5020-4e8c-b2b8-cc49a98a9edf" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

#### 导出你微调后的模型

完成后，Unsloth Studio 允许你将模型导出为 GGUF、safetensor 等格式。

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2FBtpx58zCdrOD4zB4DPSC%2FScreenshot%202026-04-07%20at%206.12.41%E2%80%AFAM.png?alt=media&#x26;token=05f05af2-5f7f-4b91-9c99-21d6a9b04935" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

#### 比较微调模型与原始模型

点击 `比较模式` 来比较 LoRA 适配器与原始模型。

<div data-with-frame="true"><figure><img src="https://2657992854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxhOjnexMCB3dmuQFQ2Zq%2Fuploads%2Fvm2CBSg7QBkKwTMKutyr%2FScreenshot%202026-04-07%20at%206.14.50%E2%80%AFAM.png?alt=media&#x26;token=8c9c159f-9d5b-4468-8984-681d19ebc427" alt="" width="563"><figcaption></figcaption></figure></div>
{% endstep %}
{% endstepper %}

#### 🦥 Unsloth Core（基于代码）指南

我们为 Gemma 4 制作了免费笔记本：

| [E4B **（推理 + 文本）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Text.ipynb) | [E4B **（视觉 + 文本）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Vision.ipynb) | [E4B **（音频）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E4B\)-Audio.ipynb) |
| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [**31B** （Kaggle）](https://www.kaggle.com/code/danielhanchen/gemma4-31b-unsloth)                                         | [E2B **（视觉 + 文本）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E2B\)-Vision.ipynb) | [E2B **（音频）**](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(E2B\)-Audio.ipynb) |

我们也为更大的 Gemma 4 模型制作了笔记本，但它们需要 A100：

| [Gemma-4-26B-A4B](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(26B_A4B\)-Vision.ipynb) - A100 GPU | [Gemma-4-31B](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma4_\(31B\)-Vision.ipynb) - A100 GPU |
| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |

{% hint style="info" %}
**如果你想进行** [**GRPO**](https://unsloth.ai/docs/zh/kai-shi-shi-yong/reinforcement-learning-rl-guide)**，它在 Unsloth 中可用，只要你禁用快速 vLLM 推理并改用 Unsloth 推理即可。请参考我们的** [**视觉 RL**](https://unsloth.ai/docs/zh/kai-shi-shi-yong/reinforcement-learning-rl-guide/vision-reinforcement-learning-vlm-rl) **笔记本示例。**
{% endhint %}

下面是一个独立的 Gemma-4-26B-A4B-it 文本 SFT 配方。这只是文本——更多细节另见我们的 [视觉微调](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/vision-fine-tuning) 部分。

{% code expandable="true" %}

````python
from unsloth import FastModel
import torch

model, tokenizer = FastModel.from_pretrained(
    model_name = "unsloth/gemma-4-26B-A4B-it", # 将其改为 unsloth/gemma-4-E2B-it 等
    dtype = None, # None 表示自动检测
    max_seq_length = 8192, # 长上下文可任意选择！
    load_in_4bit = True,  # 4 位量化以减少内存
    full_finetuning = False, # [新！] 我们现在支持全量微调了！
    # token = "YOUR_HF_TOKEN", # 门控模型所需的 HF Token
)

"""# Gemma 4 可以处理文本、视觉和音频！

让我们先体验一下 Gemma 4 如何处理多模态输入。我们使用 Gemma 4 推荐的设置：`temperature = 1.0, top_p = 0.95, top_k = 64`
"""

from transformers import TextStreamer
# 推理辅助函数
def do_gemma_4_inference(messages, max_new_tokens = 128):
    _ = model.generate(
        **tokenizer.apply_chat_template(
            messages,
            add_generation_prompt = True, # 生成时必须添加
            tokenize = True,
            return_dict = True,
            return_tensors = "pt",
        ).to("cuda"),
        max_new_tokens = max_new_tokens,
        use_cache=True,
        temperature = 1.0, top_p = 0.95, top_k = 64,
        streamer = TextStreamer(tokenizer, skip_prompt = True),
    )

"""# Gemma 4 可以看图！

<img src="https://files.worldwildlife.org/wwfcmsprod/images/Sloth_Sitting_iStock_3_12_2014/story_full_width/8l7pbjmj29_iStock_000011145477Large_mini__1_.jpg" alt="替代文本" height="256">
"""

sloth_link = "https://files.worldwildlife.org/wwfcmsprod/images/Sloth_Sitting_iStock_3_12_2014/story_full_width/8l7pbjmj29_iStock_000011145477Large_mini__1_.jpg"

messages = [{
    "role" : "user",
    "content": [
        { "type": "image", "image" : sloth_link },
        { "type": "text",  "text" : "这种动物出现在哪些电影中？" }
    ]
}]
# 你可能需要等待 1 分钟，让 Unsloth 的自动编译器完成
do_gemma_4_inference(messages, max_new_tokens = 256)

"""让我们写一首关于树懒的诗吧！"""

messages = [{
    "role": "user",
    "content": [{ "type" : "text",
                  "text" : "写一首关于树懒的诗。" }]
}]
do_gemma_4_inference(messages)

"""# 让我们微调 Gemma 4！

你现在可以通过选择来微调视觉和文本部分——音频部分也可以微调——我们正在努力让它也可选择！

我们现在添加 LoRA 适配器，这样只需要更新少量参数！
"""

model = FastModel.get_peft_model(
    model,
    finetune_vision_layers     = False, # 仅文本时关闭！
    finetune_language_layers   = True,  # 应保持开启！
    finetune_attention_modules = True,  # 注意力层对 GRPO 很有用
    finetune_mlp_modules       = True,  # 应始终开启！

    r = 8,           # 越大 = 精度越高，但可能过拟合
    lora_alpha = 8,  # 推荐至少 alpha == r
    lora_dropout = 0,
    bias = "none",
    random_state = 3407,
)

"""<a name="Data"></a>
### 数据准备
我们现在对对话风格微调使用 `Gemma-4` 格式。我们使用 [Maxime Labonne 的 FineTome-100k](https://huggingface.co/datasets/mlabonne/FineTome-100k) 数据集，采用 ShareGPT 风格。Gemma-4 按如下方式渲染多轮对话：

```
<bos><|turn>user
你好<turn|>
<|turn>model
你好呀！<turn|>
```
我们使用 `get_chat_template` 函数获取正确的聊天模板。我们支持 `zephyr, chatml, mistral, llama, alpaca, vicuna, vicuna_old, phi3, llama3, phi4, qwen2.5, gemma3, gemma-4` 等。
"""

from unsloth.chat_templates import get_chat_template
tokenizer = get_chat_template(
    tokenizer,
    chat_template = "gemma-4-thinking",
)

"""我们获取数据集的前 3000 行"""

from datasets import load_dataset
dataset = load_dataset("mlabonne/FineTome-100k", split = "train[:3000]")

"""我们现在使用 `standardize_data_formats`，尝试将数据集转换为适合微调的正确格式！"""

from unsloth.chat_templates import standardize_data_formats
dataset = standardize_data_formats(dataset)

"""让我们看看第 100 行是什么样子！"""

dataset[100]

"""现在我们必须将 `Gemma-3` 的聊天模板应用到这些对话上，并将其保存到 `text` 中。由于我们在做微调，因此使用 removeprefix(`'<bos>'`) 移除 `<bos>` token。Processor 会在训练前添加这个 token，而模型只期望有一个。"""

def formatting_prompts_func(examples):
   convos = examples["conversations"]
   texts = [tokenizer.apply_chat_template(convo, tokenize = False, add_generation_prompt = False).removeprefix('<bos>') for convo in convos]
   return { "text" : texts, }

dataset = dataset.map(formatting_prompts_func, batched = True)

"""让我们看看聊天模板应用后的效果！注意这里没有 `<bos>` token，因为 processor tokenizer 会添加一个。"""

dataset[100]["text"]

"""<a name="Train"></a>
### 训练模型
现在让我们训练模型。我们做 60 步来加快速度，但你可以设置 `num_train_epochs=1` 进行完整训练，并关闭 `max_steps=None`。
"""

from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    eval_dataset = None, # 可以设置评估！
    args = SFTConfig(
        dataset_text_field = "text",
        per_device_train_batch_size = 1,
        gradient_accumulation_steps = 4, # 使用 GA 来模拟批大小！
        warmup_steps = 5,
        # num_train_epochs = 1, # 设为 1 进行一次完整训练。
        max_steps = 60,
        learning_rate = 2e-4, # 长时间训练时降到 2e-5
        logging_steps = 1,
        optim = "adamw_8bit",
        weight_decay = 0.001,
        lr_scheduler_type = "linear",
        seed = 3407,
        report_to = "none", # 可使用 TrackIO/WandB 等
    ),
)

"""我们还使用 Unsloth 的 `train_on_completions` 方法，仅对助手输出进行训练，并忽略用户输入部分的 loss。这有助于提高微调精度！"""

from unsloth.chat_templates import train_on_responses_only
trainer = train_on_responses_only(
    trainer,
    instruction_part = "<|turn>user\n",
    response_part = "<|turn>model\n",
)

"""让我们验证指令部分是否已正确屏蔽！再次打印第 100 行。注意这个样本只有一个 `<bos>`，符合预期！"""

tokenizer.decode(trainer.train_dataset[100]["input_ids"])

"""现在让我们打印被屏蔽后的示例——你应该只能看到答案部分："""

tokenizer.decode([tokenizer.pad_token_id if x == -100 else x for x in trainer.train_dataset[100]["labels"]]).replace(tokenizer.pad_token, " ")

"""# 让我们训练模型！

要恢复训练运行，请设置 `trainer.train(resume_from_checkpoint = True)`
"""

trainer_stats = trainer.train()
````

{% endcode %}

{% hint style="info" %}
如果你 OOM：

* 降低 `per_device_train_batch_size` 到 **1** 和/或减小 `max_seq_length`.&#x20;
* 保持 `use_`[`gradient_checkpointing`](https://unsloth.ai/docs/zh/bo-ke/500k-context-length-fine-tuning#unsloth-gradient-checkpointing-enhancements)`="unsloth"` 开启（它旨在减少显存占用并扩展上下文长度）。
  {% endhint %}

**MoE 的加载器示例（bf16 LoRA）：**

```python
import os
import torch
from unsloth import FastModel

model, tokenizer = FastModel.from_pretrained(
    model_name = "unsloth/Gemma-4-26B-A4B-it",
    max_seq_length = 2048,
    load_in_4bit = False,     # 不推荐 MoE QLoRA，稠密 31B 没问题
    load_in_16bit = True,     # bf16/16 位 LoRA
    full_finetuning = False,
)
```

加载完成后，你将附加 LoRA 适配器，并类似于上面的 SFT 示例进行训练。

### MoE 微调（26B-A4B）

这个 **26B-A4B** 模型是在 Gemma 4 系列中速度与质量的折中。由于它是一个 **MoE** 模型，每个 token 只激活一部分参数，因此较稳妥的微调方式是：

* 使用 **LoRA** 而不是全量微调
* 优先选择 **16 位 / bf16 LoRA** 如果内存允许
* 先从更短的上下文和更小的 rank 开始
* 仅在流水线稳定后再扩展

如果你的目标是最高质量并且有更多内存，请改用 **31B** 。

### 多模态微调（E2B / E4B）

因为 **E2B** 以及 **E4B** 支持 **图像** 以及 **音频**，它们是多模态微调的主要 Gemma 4 变体。

* 使用以下方式加载多模态模型： `FastVisionModel`
* 保持 `finetune_vision_layers = False` 一开始
* 只微调语言、注意力和 MLP 层
* 如果任务需要，稍后再启用视觉或音频层

#### Gemma 4 多模态 LoRA 示例：

{% code expandable="true" %}

````python
from unsloth import FastVisionModel # LLM 使用 FastLanguageModel
import torch

model, processor = FastVisionModel.from_pretrained(
    "unsloth/gemma-4-26B-A4B-it",
    load_in_4bit = True, # 使用 4bit 以减少内存占用。16bit LoRA 时设为 False。
    use_gradient_checkpointing = "unsloth", # 长上下文时使用 True 或 "unsloth"
)

"""我们现在添加 LoRA 适配器，以实现参数高效微调，使我们只需高效训练全部模型参数中的 1%。

**[新]** 我们还支持仅微调视觉组件、仅微调语言组件，或两者都微调。此外，你还可以选择微调注意力模块、MLP 层，或者两者都微调！
"""

model = FastVisionModel.get_peft_model(
    model,
    finetune_vision_layers     = True, # 如果不微调视觉层则设为 False
    finetune_language_layers   = True, # 如果不微调语言层则设为 False
    finetune_attention_modules = True, # 如果不微调注意力层则设为 False
    finetune_mlp_modules       = True, # 如果不微调 MLP 层则设为 False

    r = 32,                           # 越大，精度越高，但可能过拟合
    lora_alpha = 32,                  # 推荐至少 alpha == r
    lora_dropout = 0,
    bias = "none",
    random_state = 3407,
    use_rslora = False,               # 我们支持 rank-stabilized LoRA
    loftq_config = None,               # 以及 LoftQ
    target_modules = "all-linear",    # 现在是可选项！如有需要可指定列表
)

"""<a name="Data"></a>
### 数据准备
我们将使用一个手写数学公式的采样数据集。目标是将这些图像转换为计算机可读格式——具体来说是 LaTeX——以便进行渲染。这对复杂表达式尤其有用。

你可以在[这里](https://huggingface.co/datasets/unsloth/LaTeX_OCR)访问该数据集。完整数据集在[这里](https://huggingface.co/datasets/linxy/LaTeX_OCR)。
"""

from datasets import load_dataset
dataset = load_dataset("unsloth/LaTeX_OCR", split = "train")

"""让我们概览一下这个数据集。我们将查看第二张图像及其对应的说明文字。"""

dataset

dataset[2]["image"]

dataset[2]["text"]

"""我们还可以直接在浏览器中渲染 LaTeX！"""

from IPython.display import display, Math, Latex

latex = dataset[3]["text"]
display(Math(latex))

"""为了格式化数据集，所有视觉微调任务都应遵循如下格式：

```python
[
    {
        "role": "user",
        "content": [
            {"type": "text", "text": instruction},
            {"type": "image", "image": sample["image"]},
        ],
    },
    {
        "role": "user",
        "content": [
            {"type": "text", "text": instruction},
            {"type": "image", "image": sample["image"]},
        ],
    },
]
```
"""

instruction = "为这张图像写出 LaTeX 表示。"

def convert_to_conversation(sample):
    conversation = [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": instruction},
                {"type": "image", "image": sample["image"]},
            ],
        },
        {"role": "assistant", "content": [{"type": "text", "text": sample["text"]}]},
    ]
    return {"messages": conversation}
pass

"""让我们将数据集转换为适合微调的“正确”格式："""

converted_dataset = [convert_to_conversation(sample) for sample in dataset]

"""第一个示例现在的结构如下："""

converted_dataset[0]

"""让我们采用 Gemma 4 指令聊天模板，并在我们的基础模型中使用它"""

from unsloth import get_chat_template

processor = get_chat_template(
    processor,
    "gemma-4-thinking"
)

"""在微调之前，让我们评估基础模型的表现。我们不期望有很强的结果，因为它之前没有见过这种聊天模板。"""

image = dataset[2]["image"]
instruction = "为这张图像写出 LaTeX 表示。"

messages = [
    {
        "role": "user",
        "content": [{"type": "image"}, {"type": "text", "text": instruction}],
    }
]
input_text = processor.apply_chat_template(messages, add_generation_prompt = True)
inputs = processor(
    image,
    input_text,
    add_special_tokens = False,
    return_tensors = "pt",
).to("cuda")

from transformers import TextStreamer

text_streamer = TextStreamer(processor, skip_prompt = True)
result = model.generate(**inputs, streamer = text_streamer, max_new_tokens = 128,
                        use_cache = True, temperature = 1.0, top_p = 0.95, top_k = 64)

"""你可以看到它绝对糟糕！它完全没有遵循指令

<a name="Train"></a>
### 训练模型
现在让我们训练我们的模型。我们做 60 步以加快速度，但你可以设置 `num_train_epochs=1` 进行完整运行，并关闭 `max_steps=None`。我们还支持用于强化学习的 `DPOTrainer` 和 `GRPOTrainer`！！

我们使用新的 `UnslothVisionDataCollator`，它将帮助我们进行视觉微调设置。
"""

from unsloth.trainer import UnslothVisionDataCollator
from trl import SFTTrainer, SFTConfig

trainer = SFTTrainer(
    model = model,
    train_dataset = converted_dataset,
    processing_class = processor.tokenizer,
    data_collator = UnslothVisionDataCollator(model, processor),
    args = SFTConfig(
        per_device_train_batch_size = 1,
        gradient_accumulation_steps = 4,
        max_grad_norm = 0.3,
        warmup_ratio = 0.03,
        max_steps = 60,
        # num_train_epochs = 2, # 在完整训练运行中，用这个代替 max_steps
        learning_rate = 2e-4,
        logging_steps = 1,
        save_strategy = "steps",
        optim = "adamw_8bit",
        weight_decay = 0.001,
        lr_scheduler_type = "cosine",
        seed = 3407,
        output_dir = "outputs",
        report_to = "none", # 用于 Weights and Biases 或其他工具

        # 进行视觉微调时，你必须设置下面这些项：
        remove_unused_columns = False,
        dataset_text_field = "",
        dataset_kwargs = {"skip_prepare_dataset": True},
        max_length = 2048,
    )
)

trainer_stats = trainer.train()
````

{% endcode %}

#### 图片示例格式

记住：对于 Gemma 4 多模态提示，把图片 **放在** 文本指令之前。

{% code expandable="true" %}

```json
{
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "image", "image": "/path/to/image 或 object"},
        {"type": "text", "text": "从这张收据中提取所有文本。将明细项、总计、商家和日期以 JSON 返回。"}
      ]
    },
    {
      "role": "assistant",
      "content": [
        {"type": "text", "text": "{\"merchant\": \"Example Store\", \"total\": \"19.99\"}"}
      ]
    }
  ]
}
```

{% endcode %}

#### 音频示例格式

音频仅用于 **E2B / E4B** 。仅限此用途。保持片段简短且与任务相关。

{% code expandable="true" %}

```json
{
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "audio", "audio": "/path/to/audio 或 object"},
        {"type": "text", "text": "将以下英文语音片段转写为英文文本。只输出转写结果。"}
      ]
    },
    {
      "role": "assistant",
      "content": [
        {"type": "text", "text": "大家好，欢迎回来。"}
      ]
    }
  ]
}
```

{% endcode %}

### 保存 / 导出微调后的模型

你可以查看我们针对以下内容的特定推理 / 部署指南： [Unsloth Studio](https://unsloth.ai/docs/zh/xin/studio/export), [llama.cpp](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/saving-to-gguf), [vLLM](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/vllm-guide), [llama-server](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/llama-server-and-openai-endpoint), [Ollama](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/saving-to-ollama) 或 [SGLang](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/sglang-guide).

#### 保存为 GGUF

Unsloth 支持直接保存为 GGUF：

```python
model.save_pretrained_gguf("directory", tokenizer, quantization_method = "q4_k_m")
model.save_pretrained_gguf("directory", tokenizer, quantization_method = "q8_0")
model.save_pretrained_gguf("directory", tokenizer, quantization_method = "f16")
```

或者将 GGUF 推送到 Hugging Face：

```python
model.push_to_hub_gguf("hf_username/directory", tokenizer, quantization_method = "q4_k_m")
model.push_to_hub_gguf("hf_username/directory", tokenizer, quantization_method = "q8_0")
```

如果导出的模型在另一个运行时中的表现更差，Unsloth 会指出最常见的原因： **推理时使用了错误的聊天模板 / EOS 令牌** （你必须使用与你训练时相同的聊天模板）。

更多细节请阅读我们的推理指南：

{% columns %}
{% column width="50%" %}
{% content-ref url="../../ji-chu-zhi-shi/inference-and-deployment" %}
[inference-and-deployment](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment)
{% endcontent-ref %}

{% content-ref url="../../ji-chu-zhi-shi/inference-and-deployment/saving-to-gguf" %}
[saving-to-gguf](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/saving-to-gguf)
{% endcontent-ref %}
{% endcolumn %}

{% column width="50%" %}
{% content-ref url="../../xin/studio/export" %}
[export](https://unsloth.ai/docs/zh/xin/studio/export)
{% endcontent-ref %}

{% content-ref url="../../ji-chu-zhi-shi/inference-and-deployment/vllm-guide" %}
[vllm-guide](https://unsloth.ai/docs/zh/ji-chu-zhi-shi/inference-and-deployment/vllm-guide)
{% endcontent-ref %}
{% endcolumn %}
{% endcolumns %}

### Gemma 4 数据最佳实践

Gemma 4 有一些你需要注意的格式细节。

#### 1. 使用标准聊天角色

Gemma 4 使用标准的：

* `system`
* `user`
* `assistant`

这意味着你的 SFT 数据集应写成常规聊天格式，而不是旧的 Gemma 特定角色格式。

#### 2. 思考模式是显式的

如果你想在 SFT 期间保留思考风格的行为：

* 保持格式一致
* 决定你是想训练 **可见的思考块** 还是 **仅最终答案**
* 要 **不要** 在同一数据集中混合多种不兼容的思考格式

对于大多数生产环境的助手，最简单的设置是在 **仅最终可见答案**.

#### 3. 多轮规则

对于多轮对话，只保留 **最终可见答案** 在对话历史中。不要 **不要** 将更早的思考块反馈到后续轮次中。
