跳到正文
Hacker News· vinhnx·· 8 天前AI 评分71

用 Unsloth 训练自己的决策模型:微调 Qwen3.8 与 Gemma 4

Train your own Decision Model with Unsloth

AI 导读

Unsloth 发布教程,介绍如何用 LoRA 微调 Qwen3.8、Gemma 4 等 LLM,将其转为输出选项与概率的决策模型,而非生成文本。官方称用 Clef head 加 LoRA(r=64)训练一个 epoch,下游准确率从 30–37% 提升到最高 78%,Qwen3.5-0.8B 与 2B 分别达到 78% 和 81%。

正文 · AI 翻译

⌘Ctrlk

  1. 基础

用 Unsloth 训练你自己的决策模型

微调 Qwen 和 Gemma 等 LLM,使其以校准后的概率做出决策。

你现在可以用 Unsloth 训练自己的决策模型,就像 Jev、Laya 和 Clef 一样。微调 Qwen3.8 和 Gemma 4 LLM,将它们转化为决策模型,让它们做出决策,而不是生成文本。微调后的模型会读取你的输入,为你提供的选项打分,并返回一个带有概率的选择。训练后的基准测试见下表:

Qwen3.5-0.8B

36% -> 73%

7% -> 74%

19% -> 76%

78%

Qwen3.5-2B

33% -> 78%

1% -> 58%

1% -> 62%

81%

我们用 LoRA(r=64)为 LLM 微调了一个 Clef 头,仅训练一个 epoch,就将下游准确率从 30–37%(大致相当于随机水平)提升到高达 78%。

训练使用与 Laya 和 Clef 相同的数据集格式。测试集已针对训练数据进行了去污染处理。

所有模型都同样有效——你还可以进一步微调 Laya / Clef 等决策模型。

Qwen3.5-0.8B

78%

4GB

42 分钟

Qwen3.5-2B

81%

8GB

40 分钟

Llama 3.2 3B

79%

4.1GB

30 分钟

Gemma 4 E4B

77%

14.4GB

49 分钟

Laya(微调后)

77%

2.5GB

10 分钟

我们使用了 12 个来源的混合数据,外加 typed-decisions:ag_news、arc、banking77、boolq、clinc150、commonsense_qa、mmlu、mnli、prompt_injections、snli、sst5 和 wanli。测试集包含 3,000 行:2000 行来自 typed-decisions,500 行来自 BANKING77,500 行来自 CLINC150。

🦥 在 Unsloth 中训练

设置 Unsloth

最简单的入门方式是下载 Unsloth 桌面应用。支持 macOS、Windows 和 Linux。

下载 Unsloth

或者,如果你更喜欢手动安装:

MacOS、Linux、WSL:

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

Windows PowerShell:

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

选择你的模型和数据集

打开 Train 选项卡并选择一个 LLM,例如 unsloth/Qwen3.5-4B。然后将 Train as 设置为 Decision model。Unsloth 会自动填入效果良好的设置。要匹配我们的结果,请将 epochs 设为 2,LoRA rank 设为 16,学习率设为 2e-4。

上传文件或从 Hugging Face 选择数据集,使用该数据集格式。对于 typed-decisions,将 Subset 设置为 all。

开始训练

点击 Start Training。Unsloth 会报告训练前后在留出决策上的准确率,并校准模型的置信度。

在 Decision API 中运行

点击 Use in Decision API。使用 laya、default 或 jev-latest 的请求现在会转到你的模型。

你的模型也会以其运行名称显示在 Settings → API 下的 Decision API → Model 中。

你还可以通过 Decision API 中的“Try It”选项来试用训练好的决策模型:

📒 使用我们的免费 notebook

你可以完全免费地使用我们的 Google Colab 或 Kaggle notebook 训练自己的决策模型。只要你拥有 Google 账号,Colab 和 Kaggle 都提供慷慨的免费 GPU 算力。这些 notebook 中有分步工作流程,你可以逐步操作,我们会向你展示如何进行数据准备、训练实际模型(此处为 Qwen3.5-4B)并将其导出。

  • 通过我们的免费 notebook 将 Qwen3.5-4B 微调为决策模型:

你也可以使用我们免费的 Unsloth Studio notebook,通过 UI 界面训练决策模型:

🐍 用代码训练

用 pip install --upgrade unsloth, and use the following code to train your own decision model. 安装或更新 Unsloth

要使用 Llama 或 Gemma 4,请将 model_name 改为 unsloth/Llama-3.2-3B-Instruct 或 unsloth/gemma-4-E4B-it。Unsloth 可以微调的其他 LLM 应该也能以相同方式工作。

对于你自己的数据,请参阅数据集格式。每一行包含一个 state、要决策的问题以及标准答案。

校准并保存

在留出的 split_holdout 行上进行校准,使模型的概率与其正确频率相匹配:

然后保存。save_pretrained 保存 LoRA 适配器和 head。save_pretrained_merged 保存完整的 16 位模型:

像加载基础模型一样再次加载它:

🎯 做出决策

给 predict 一个输入和要决策的问题,格式与你的数据集相同:

answer 是 choice 的选项键,True 或 False 用于 noul,score 则用级别数字。每个答案还具有与 Decision API 答案相同的字段。

要在有标签的测试集上检查准确率,请使用 evaluate:

在 Decision API 中使用它

你可以阅读我们的详细指南来了解如何服务/运行决策模型,否则请阅读下面的快速摘要。你在 Unsloth 中训练的模型会显示在 Settings → API 中。对于你用代码训练的模型,请用你保存的合并文件夹启动 Unsloth:

在 Settings → API 中打开 Decision API。使用 laya、default 或 jev-latest 的请求现在会转到你的模型,答案格式与 Laya 的相同。该模型需要 GPU,并在首次请求时加载,这可能需要几分钟。当训练任务正在使用 GPU 时,Decision API 会等待,并在训练结束后重新开始回答。

工作原理

Unsloth 将你的输入放在一个提示中,后面跟着每个问题及其选项。LLM 读取一次。一个小的 head(设计与 Cloudflare 的 Clef 相同)查看 LLM 对每个问题和选项的输出,为每个选项打分,并一起决定所有问题。它从不写文本,因此不会把内存花在 LLM 的词语预测上。

⚙️ 推荐设置

  • 从 rank 16 的 4 位 LoRA 和学习率 2e-4 开始。新 head 默认以 1e-4 训练。你可以在 DecisionTrainer 中用 head_learning_rate 更改它。

  • 我们在 typed-decisions 上使用了 2 个 epoch。数据较少时,可以尝试 3 或 4 个。

  • 如果想快速试一下,请设置 max_steps = 60。Qwen3.5-4B 在 L4 上 10 分钟内达到了 76%。

  • 如果内存不足,请将 batch size 减半,并将梯度累积加倍。

  • max_seq_length 设置训练期间的最长输入。长输入会保留问题和选项,并截断输入末尾以适配。预测最多读取 16,384 个 token。

最后更新于 2 分钟前

这有帮助吗?

这有帮助吗?

from unsloth import FastDecisionModel, DecisionTrainer, is_bfloat16_supported
from datasets import load_dataset
from transformers import TrainingArguments

model, tokenizer = FastDecisionModel.from_pretrained(
    model_name = "unsloth/Qwen3.5-4B",
    max_seq_length = 2048,
    load_in_4bit = True,
)

model = FastDecisionModel.get_peft_model(
    model,
    r = 16,
    lora_alpha = 16,
    lora_dropout = 0,
    use_gradient_checkpointing = "unsloth",
    random_state = 3407,
)

dataset = load_dataset("LocalLLaMA/typed-decisions", "all", split = "train")
items, report = FastDecisionModel.build_dataset(dataset, tokenizer, model)
print(f"Skipped {report['skipped']} of {report['total']} decisions")

train_items, eval_items = FastDecisionModel.split_holdout(items, seed = 3407)

trainer = DecisionTrainer(
    model = model,
    processing_class = tokenizer,
    train_dataset = train_items,
    eval_dataset = eval_items,
    args = TrainingArguments(
        per_device_train_batch_size = 8,
        gradient_accumulation_steps = 4,
        num_train_epochs = 2,
        learning_rate = 2e-4,
        lr_scheduler_type = "cosine",
        warmup_steps = 10,
        weight_decay = 0.01,
        bf16 = is_bfloat16_supported(),
        fp16 = not is_bfloat16_supported(),
        eval_strategy = "epoch",
        logging_steps = 10,
        output_dir = "outputs",
        report_to = "none",
        seed = 3407,
    ),
)
trainer.train()
metrics = FastDecisionModel.calibrate(model, tokenizer, eval_items)
print(metrics) # held-out accuracy, calibration error (ece) and loss
model.save_pretrained("qwen-decisions")
model.save_pretrained_merged("qwen-decisions-merged")
# model.push_to_hub("hf_username/qwen-decisions", token = "hf_...")
model, tokenizer = FastDecisionModel.from_pretrained("qwen-decisions", load_in_4bit = True)
FastDecisionModel.for_inference(model)
answers = FastDecisionModel.predict(
    model,
    tokenizer,
    "Hi, I was charged twice for invoice #4411. Please refund the duplicate today.",
    {
        "team": {
            "type": "choice",
            "instructions": "Which team should handle this?",
            "criteria": {
                "billing": "invoices, payments, refunds",
                "technical": "bugs, outages, errors",
                "sales": "pricing, new plans",
            },
        },
        "refund": {"type": "noul", "instructions": "Does the customer ask for a refund?"},
    },
)
print(answers["team"]["answer"])        # "billing"
print(answers["team"]["probabilities"]) # {"billing": ..., "sales": ..., "technical": ...}
test = load_dataset("LocalLLaMA/typed-decisions", "all", split = "test")
test_items, _ = FastDecisionModel.build_dataset(test, tokenizer, model)
print(FastDecisionModel.evaluate(model, tokenizer, test_items))
UNSLOTH_SYSTEMONE_MODEL=/path/to/qwen-decisions-merged unsloth studio -H 0.0.0.0 -p 8888

来源:Hacker News · unsloth.ai