FLUX.2 klein 的微调是两件事:训练跑在你自己显卡上,对着开放权重的 Base 检查点;调用发生在 api.bfl.ai,端点在请求体里多加两个字段。两边参数名不同,混着用就是最常见的翻车方式。

微调到底改了哪一部分

训练目标是 Base 变体,不是那个快的。总览页把 Base 称为保留完整训练信号的未蒸馏基础模型,训练页补上关键一句:这种架构正是 LoRA 需要的,而且训出来的适配器之后仍能加载到蒸馏版上。许可证决定你能发布哪个尺寸:4B Base 是 Apache 2.0,9B Base 挂在 FLUX Non-Commercial 下,训练页推荐多数人用 4B,因为硬件门槛更低。适配器通常 10–200MB,在消费级显卡上跑一到三小时;全量微调则是大文件、数天到数周。两个尺寸都不在公共 API 上提供,所以回路从你自己的机器开始。

数据集规则与最低硬件

训练页对图片质量说得很细,对张数一个字没提:1024px 起、质量一致、没有伪影。另外两个官方页面给出的数字互相矛盾——分步示例说 20–40 张最优,Hugging Face 上的教程说 15–40 张。以你实际读到的那页为准。

caption 的规则和直觉相反:描述内容,不要描述你想教给模型的风格,每条都以同一个触发词开头,训练页把它记作 [trigger]。分辨率是两段式:迭代用 512px,最终训练用 1024px 或更高。

官方公布的最低配置

变体最低显卡内存许可证
FLUX.2 klein 4B BaseNVIDIA,12GB 显存32GBApache 2.0
FLUX.2 klein 9B BaseNVIDIA,22GB 显存64GBFLUX Non-Commercial

厂商教程说 4B 的 LoRA 训练占用不到 24GB 显存,RTX 4090 上约一小时。官方没有描述 AMD 或 Apple Silicon 的路径;点名的两个训练器是 AI-Toolkit 和 Diffusers。

微调回路:一步步走一遍

一张四阶段示意图:一个装着带 caption 训练图片的文件夹进入训练运行,训练产出适配器文件,适配器被上传后通过微调端点调用,最终得到一张生成的样本图。

把这个回路读成四个阶段,因为每个阶段的用词都不一样:装着图片和同名 caption 的文件夹;由 trigger_wordtrain.lr 这类键驱动的训练运行;训练留下的 .safetensors 适配器,在 Customization → Finetunes 上传;以及最后那个端点——finetune_idfinetune_strength 在这里出现,训练器的键全部消失。

最后这个切换点就是多数集成出错的地方:train.lr 属于一个已经跑完的配置文件,finetune_strength 属于一个正在进行的请求,谁都不接受对方的名字。

写训练配置

分步示例公布了一份完整的 AI-Toolkit 作业,真正有用的地方在于每次要改的很少。移动的只有三个键:config.nametrigger_worddatasets[0].folder_path,分别指定输出目录、对齐 caption、指向图片位置。network 里的 linearconv 设定适配器的秩,linear_alphaconv_alpha 是缩放系数,默认值是 32/32 配 16/16。

先准备数据,因为 caption 的文件名必须和图片一一对应。

# 一个目录:每张图都要有一个同名 caption 文件。
mkdir -p ~/klein-lora/dataset
cd ~/klein-lora/dataset

for i in 1 2 3; do
  cp ~/raw/shirt-$i.png "SHIRT_$i.png"
  printf '%s\n' \
    "SHIRTTR1GGER. A cream knit sweater on a plain white background, front view." \
    > "SHIRT_$i.txt"
done

ls

作业本身就是一个 YAML 文件加一条命令,键名逐字照抄官方文档。

mkdir -p ~/klein-lora/config
cat > ~/klein-lora/config/shirt_lora.yaml <<'YAML'
job: "extension"
config:
  name: "shirt_lora_v1"
  process:
    - type: "diffusion_trainer"
      training_folder: "/app/ai-toolkit/output"
      device: "cuda"
      trigger_word: "SHIRTTR1GGER"
      network:
        type: "lora"
        linear: 32
        linear_alpha: 32
        conv: 16
        conv_alpha: 16
      save:
        dtype: "bf16"
        save_every: 250
      datasets:
        - folder_path: "/home/you/klein-lora/dataset"
          caption_ext: "txt"
          resolution:
            - 512
            - 768
            - 1024
      train:
        batch_size: 1
        steps: 3000
        lr: 0.0001
        optimizer: "adamw8bit"
        timestep_type: "weighted"
        content_or_style: "balanced"
      model:
        name_or_path: "black-forest-labs/FLUX.2-klein-base-4B"
        quantize: true
meta:
  name: "[name]"
  version: "1.0"
YAML

cd /app/ai-toolkit && python run.py ~/klein-lora/config

quantize: true 是让 4B 训练塞进 12GB 显卡的那一行,示例把它列在显存优化第一条。文档默认配置指向的其实是 9B 检查点。

跑训练,读结果

学习率这一档的不对称性是官方写明的:训练页给 LoRA 的是 8e-5 到 1e-4,示例补了一条纠偏——loss 降得太慢就提到 2e-4,loss 来回跳就降到 5e-5。

步数在官方页面之间明确打架。训练页说风格 LoRA 用 1500–2500 步、角色 LoRA 用 1500–3000 步;示例按数据集规模缩放,从 800–1200 步到 50 张图的 2000–3000 步,而它自己的配置写 3000 步、save_every: 150,最后又报告 1500 步的 checkpoint 效果最好。所以盯的是 checkpoint 本身:没存下来的点你根本选不了。

要看样本输出,而不是只盯 loss 曲线。训练页把过拟合点名为需要盯防的失败模式:如果每步的样本不管提示词怎么写都长得像你的数据集,那就是信号。

上传适配器并调用微调端点

上传走 Dashboard,不是 API。在 Customization → Finetunes 下,+ Add Finetune 要你填名称、底模、精度、可选的触发短语和 .safetensors 文件。名称会成为你的 finetune_id,底模的选择决定哪个端点能提供它。

官方公布了六个端点。两个 Base 端点 /v1/flux-2-klein-base-4b-finetuned/v1/flux-2-klein-base-9b-finetuned 服务于在 Base 检查点上训练的适配器,四个蒸馏端点同样接受它们。默认精度是 FP8。

提交加轮询的形状和其他所有 FLUX.2 端点一样,新增的只有两个字段。

# 端点必须和你在 Dashboard 里选的底模与精度一致。
MODEL="flux-2-klein-base-4b-finetuned"

RESPONSE=$(curl -s -X POST "https://api.bfl.ai/v1/${MODEL}" \
  -H "x-key: ${BFL_API_KEY}" \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "A cream knit sweater on a plain white background, SHIRTTR1GGER product shot.",
    "finetune_id": "shirt-lora-v1",
    "finetune_strength": 1.0
  }')
POLLING_URL=$(echo "$RESPONSE" | jq -r '.polling_url')

while true; do
  RESULT=$(curl -s "$POLLING_URL" -H "x-key: ${BFL_API_KEY}")
  STATUS=$(echo "$RESULT" | jq -r '.status')
  [ "$STATUS" = "Ready" ] && echo "$RESULT" | jq -r '.result.sample' && break
  [ "$STATUS" = "Error" ] || [ "$STATUS" = "Failed" ] && echo "$RESULT" && break
  sleep 1
done

finetune_id 必填,别人共享的适配器写成 {owner_org_id}/{name}finetune_strength 默认 1.0;适配器盖过提示词时,官方解法是固定 seed 后在 0.7 → 0.9 之间扫。

同一个适配器也能在本地跑,走训练页给出的 Diffusers 管线。

python - <<'PY'
import torch
from diffusers import Flux2KleinPipeline

pipe = Flux2KleinPipeline.from_pretrained(
    "black-forest-labs/FLUX.2-klein-base-4B", torch_dtype=torch.bfloat16
)
pipe.load_lora_weights("klein-lora/output/shirt_lora_v1/shirt_lora_v1.safetensors")
pipe.to("cuda")

image = pipe(
    "A cream knit sweater on a plain white background, SHIRTTR1GGER product shot.",
    num_inference_steps=50,
    guidance_scale=4.0,
).images[0]
image.save("shirt-lora-sample.png")
PY

两条限制框住设计空间:一次请求只接受一个 LoRA,不支持叠加;底模匹配严格,4B 端点会拒绝为 9B 上传的适配器。公开测试期间微调端点按底模同分辨率计费,训练本身的价格官方没有公布。官方没给的数字去看各端点怎么计费,别抄第三方报价。本地 ComfyUI 部署先讲采样器设置,提交与轮询范式在别处有完整记录,中文上手与提示词管的是模型而不是管线出问题的情况。

常见问题

能直接通过 API 训练吗? 不能。Base 变体是面向本地开发的开放权重,不在公共 API 上提供。

该训哪个 Base 尺寸? 4B,除非你需要 9B 的质量并且有 22GB 显存。它是 Apache 2.0,硬件门槛也更低。

9B 的适配器能挂在 4B 端点上吗? 不能。底模匹配严格,不匹配的适配器会失败。

finetune_strength 该给多少? 从默认的 1.0 起步,只有适配器盖过提示词时才固定 seed 去 0.7 → 0.9 之间扫。