【Bug已解决】FLUX kohya LoRA conversion crashes on `final_layer` (KeyError on missing adaLN_modulation_1;

发布时间:2026/8/11 1:28:09
【Bug已解决】FLUX kohya LoRA conversion crashes on `final_layer` (KeyError on missing adaLN_modulation_1;
【Bug已解决】FLUX kohya LoRA conversion crashes onfinal_layer(KeyError on missing adaLN_modulation_1; Incompatible keys on final_layer alphas) 解决方案一、现象长什么样把 Kohya 格式的 FLUX LoRA 转成 diffusers 格式时只要 LoRA 覆盖了 FLUX 的final_layer输出层含adaLN_modulation自适应层归一化转换就会崩from diffusers.loaders import LoraLoaderMixin LoraLoaderMixin.lora_state_dict(None, kohya_flux_final_layer_lora.safetensors)报错KeyError Cannot find corresponding diffusers key for lora_unet_final_layer_adaLN_modulation_1.lora_up.weight或者走了另一条路径但 alpha 对不上RuntimeError Error(s) in loading state_dict for FluxTransformer2DModel: Missing key(s): proj_out.lora_up.weight Unexpected key(s): final_layer.adaLN_modulation_1.lora_up.weight现象总结FLUX 的final_layer包含adaLN_modulation_1/adaLN_modulation_2这类自适应层归一化权重而 Kohya→diffusers 转换器的 key 正则只覆盖 transformer blocks 的注意力/前馈没覆盖final_layer及其adaLN_modulation于是要么 KeyError要么 key 映射到了错误的proj_out导致 Incompatible keys。二、背景FLUX 的 transformer 在双块double_blocks/单块single_blocks之后有一个final_layer负责把最后隐状态投影回像素空间。它的结构里有一个adaLN_modulation小 MLP由时间步/文本条件调制包含两个线性adaLN_modulation_1、adaLN_modulation_2。Kohya 对 FLUX 产 LoRA 时会把final_layer.adaLN_modulation_1这类也纳入尤其当训练脚本把 final_layer 标为可训练。但 diffusers 的 FLUX 转换器在写 key 映射时注意力只列举了double_blocks_*/single_blocks_*漏掉了final_layer_*。于是转换器正则匹配不到final_layer_adaLN_modulation_1走「未知 key」分支 →KeyError或者有人强行把final_layer映射到proj_outFLUX 确实有个proj_out但adaLN_modulation和proj_out是不同模块映射后权重形状/语义全错 → Incompatible keys。三、根因根因两点转换器 key 正则漏掉final_layer家族adaLN_modulation_1/adaLN_modulation_2没有在UNET_TO_DIFFUSERSFLUX 版的映射表里匹配不到就 KeyError。final_layer与proj_out被错误混为一谈有人把整个final_layer前缀映射到proj_out但 FLUX 的final_layer含adaLN_modulation调制和linear/proj多个子模块不能整体平移到proj_out。本质转换器的 FLUX 模块家族白名单漏了final_layer及其自适应调制子层且把它的语义和proj_out混淆。四、最小可运行复现用标准库复现「转换器正则漏掉 final_layer」import re # FLUX 转换器不完整只管 double/single blocks FLUX_MAP { rdouble_blocks_\d_img_attn_proj$: attn.to_q, rsingle_blocks_\d_linear$: attn.to_q, } def convert_flux(kohya_key: str): base kohya_key.replace(lora_unet_, ).replace(.lora_up.weight, ) for pat, repl in FLUX_MAP.items(): if re.search(pat, base): return ftransformer.{base}.lora.up.weight.replace(base, repl) raise KeyError(fCannot find corresponding diffusers key for {kohya_key}) try: convert_flux(lora_unet_final_layer_adaLN_modulation_1.lora_up.weight) except KeyError as e: print(KeyError, e)要复现「错误映射到 proj_out」把final_layer整体替换成proj_out转换后的 key 是transformer.proj_out.adaLN_modulation_1.lora.up.weight——而真实 FLUX 里proj_out没有adaLN_modulation子模块加载即 Incompatible keys。五、解决方案第一层最小直接修复最小修复在 FLUX 转换器里补上final_layer家族的正确映射区分adaLN_modulation与linear/proj且绝不整体平移到proj_outimport re FLUX_FINAL_LAYER_MAP { rfinal_layer_adaLN_modulation_1$: final_layer.adaLN_modulation.1, rfinal_layer_adaLN_modulation_2$: final_layer.adaLN_modulation.2, rfinal_layer_linear$: final_layer.linear, } def convert_flux_key(kohya_key: str): base kohya_key.replace(lora_unet_, ) suffix .lora.up.weight if kohya_key.endswith(.lora_down.weight): suffix .lora.down.weight elif kohya_key.endswith(.alpha): suffix .alpha stem base.replace(.lora_up.weight, ).replace(.lora_down.weight, ).replace(.alpha, ) # 先试 final_layer 家族 for pat, repl in FLUX_FINAL_LAYER_MAP.items(): if re.search(pat, stem): return ftransformer.{repl}{suffix} # 再试 double/single blocks原有逻辑 if re.search(rdouble_blocks_\d, stem) or re.search(rsingle_blocks_\d, stem): return ftransformer.{stem}{suffix} raise KeyError(f未识别的 FLUX key: {kohya_key})这样final_layer.adaLN_modulation.1这类 key 能被正确翻译且不会误进proj_out。六、解决方案第二层结构性改进把「FLUX 各模块家族含 final_layer 自适应调制的 key 规则」收敛成一个 dataclass 单一真源from dataclasses import dataclass, field from typing import Dict dataclass(frozenTrue) class FluxFinalLayerLoraPolicy: FLUX kohya LoRA 转换含 final_layer的单一真源。 # final_layer 子模块kohya 片段 - diffusers 路径片段 final_layer_modules: Dict[str, str] field(default_factorylambda: { final_layer_adaLN_modulation_1: final_layer.adaLN_modulation.1, final_layer_adaLN_modulation_2: final_layer.adaLN_modulation.2, final_layer_linear: final_layer.linear, }) # 注意力块前缀 block_prefixes: tuple (double_blocks, single_blocks) # 明确 NOT 映射到 proj_out 的模块防止混淆 forbidden_proj_out_mapping: tuple (final_layer_adaLN_modulation,) def convert(self, kohya_key: str) - str: stem kohya_key.replace(lora_unet_, ) suffix self._suffix(kohya_key) stem stem.replace(.lora_up.weight, ).replace(.lora_down.weight, ).replace(.alpha, ) for frag, path in self.final_layer_modules.items(): if stem frag or stem.startswith(frag): return ftransformer.{path}{suffix} if any(stem.startswith(p _) for p in self.block_prefixes): return ftransformer.{stem}{suffix} raise KeyError(f未识别的 FLUX key: {kohya_key}) def _suffix(self, kohya_key: str) - str: if kohya_key.endswith(.lora_down.weight): return .lora.down.weight if kohya_key.endswith(.alpha): return .alpha return .lora.up.weight def validate_no_proj_out_confusion(self, diffusers_key: str) - bool: if proj_out in diffusers_key: return not any(f in diffusers_key for f in self.forbidden_proj_out_mapping) return True转换主函数只调用policy.convert且转换后跑validate_no_proj_out_confusion防回退到错误映射。七、解决方案第三层断言 / CI 守护用 pytest 把「final_layer 可翻译 不混 proj_out 未知 key 报错」固化成回归import pytest from mylib.flux_final_layer import FluxFinalLayerLoraPolicy POLICY FluxFinalLayerLoraPolicy() def test_adaln_modulation_1_translates(): out POLICY.convert(lora_unet_final_layer_adaLN_modulation_1.lora_up.weight) assert out transformer.final_layer.adaLN_modulation.1.lora.up.weight def test_adaln_modulation_2_translates(): out POLICY.convert(lora_unet_final_layer_adaLN_modulation_2.lora_down.weight) assert out.endswith(final_layer.adaLN_modulation.2.lora.down.weight) def test_linear_translates(): out POLICY.convert(lora_unet_final_layer_linear.lora_up.weight) assert out.endswith(final_layer.linear.lora.up.weight) def test_not_mapped_to_proj_out(): key POLICY.convert(lora_unet_final_layer_adaLN_modulation_1.lora_up.weight) assert POLICY.validate_no_proj_out_confusion(key) is True assert proj_out not in key def test_unknown_key_raises(): with pytest.raises(KeyError, match未识别): POLICY.convert(lora_unet_final_layer_unknown.lora_up.weight) def test_block_keys_still_work(): out POLICY.convert(lora_unet_double_blocks_0_img_attn_proj.lora_up.weight) assert double_blocks_0 in outCI 把test_adaln_modulation_1_translates与test_not_mapped_to_proj_out作为 FLUX LoRA 转换的必过项要求「任何 final_layer key 必须正确映射且不得进 proj_out」。八、排查清单FLUX kohya LoRA 转 final_layer 失败按顺序查报错 key 是否含final_layer/adaLN_modulation这些是转换器常漏的 final_layer 家族。是否被错误映射到了proj_outadaLN_modulation和proj_out是不同模块形状/语义都不该混。转换正则是否只列了double/single_blocks是就补final_layer_modules映射。adaLN_modulation_1/_2是否分别映射Kohya 用数字后缀diffusers 是adaLN_modulation.1/.2。alpha标量是否一起转换final_layer 的 alpha 也要映射到 diffusers 的 alpha 字段。转换后load_lora_weights是否真的注入了transformer.final_layer.*确认权重落地而非空张量。九、小结「FLUX kohya LoRA conversion crashes on final_layer」本质是转换器的 FLUX 模块家族白名单漏了 final_layer 及其 adaLN_modulation 自适应调制子层且把它和 proj_out 错误混淆导致 KeyError 或 Incompatible keys。第一层补上 final_layer 家族的正确映射并明确禁止平移到 proj_out第二层把 FLUX 各模块含 final_layer的 key 规则收敛到FluxFinalLayerLoraPolicy单一真源第三层用 pytest 守住「final_layer 可翻译、不混 proj_out、未知 key 报错」。通用教训**key 转换工具必须覆盖目标模型的所有叶子模块包括输出层的自适应调制且不能把结构不同的模块强行合并映射否则要么 KeyError 要么静默错配。