- 深度学习
- 计算机视觉
- 媒体生成
- 模型训练
- 微调
【免费下载链接】sd-scripts
本文基于 sd-scripts 仓库中的 train_network_oft_boft.md,系统讲解如何在train_network.py(SD1.x / SD2.x)与sdxl_train_network.py(SDXL)中训练 OFTv2 与 BOFT 两类正交微调适配器。与 LoRA 通过低秩矩阵叠加增量不同,正交适配器通过在目标层输入侧乘以学习到的正交(旋转)矩阵来微调模型,具有保留预训练权重范数与夹角结构的独特特性。读完本文你将掌握两种模块的数学原理、全部命令行参数、完整训练命令、权重格式与 PEFT/OMI 兼容加载方式,以及合并与性能优化要点。
1. 概述:正交微调与传统 LoRA 的本质区别
OFTv2(Orthogonal Fine-Tuning v2)与 BOFT(Butterfly Orthogonal Fine-Tuning)都属于正交变换类适配器。它们与 LoRA 的核心差异在于:
- LoRA:在权重矩阵上叠加低秩增量(
W' = W + BA),改变权重本身; - OFTv2 / BOFT:不修改权重矩阵本身,而是在目标层输入侧乘以一个学习到的正交(旋转)矩阵(
x' = R·x),预训练权重被整体旋转。
由于旋转保持范数与向量间夹角,预训练权重向量的范数(norm)与两两夹角(pairwise angles)均被保留,这被认为能更好地维持预训练模型的特征几何结构。
| 模块 | 方法 | 每层可训练参数 |
|---|---|---|
networks.oft_v2 | OFTv2(块对角正交变换,Cayley 参数化) | oft_R.weight |
networks.boft | BOFT(蝴蝶分解正交变换 + 每输出通道缩放) | boft_R、boft_s |
适用模型范围:SD1.x、SD2.x、SDXL(U-Net 与 Text Encoder)。不支持FLUX.1、SD3、Lumina、HunyuanImage 与 Anima。
目标层范围与networks.lora一致:U-Net 中Transformer2DModel块内的全部 Linear 层与 1x1 Conv2d 层,以及 Text Encoder 的 attention / MLP 层;通过enable_conv=true可额外纳入 3x3 Conv2d 层。
说明:这两个模块由 umisetokikaze 在 PR #2357 中贡献,实现遵循 PEFT 的 OFT / BOFT 层规范,因此可以加载 PEFT 格式的权重。传统
networks.oft模块(输出侧旋转、不同权重格式)保持原样不变,仍可继续使用。
1.1 源码实现路径
两个模块分别位于 networks/oft_v2.py 与 networks/boft.py,二者共享 networks/orthogonal_common.py 中的公共工具。从源码可以确认的模块结构:
- 目标替换模块常量(orthogonal_common.py):
TEXT_ENCODER_TARGET_REPLACE_MODULE = ["CLIPAttention", "CLIPSdpaAttention", "CLIPMLP"],UNET_TARGET_REPLACE_MODULE = ["Transformer2DModel"],UNET_TARGET_REPLACE_MODULE_CONV2D_3X3 = ["ResnetBlock2D", "Downsample2D", "Upsample2D"]; - 层筛选:遍历
named_modules(),只接受类名恰好为Linear或Conv2d的子模块;Conv2d 默认仅限 1x1(is_conv2d_1x1),3x3 需enable_conv=true(orthogonal_common.py); - 限制条件(orthogonal_common.py):正交适配器不支持分组卷积(
groups != 1)与 dilation > 1 的 Conv2d 层,遇到会直接抛出ValueError。
2. 命令行参数:模块选择、block size 语义与 dropout
2.1 模块选择与全局参数
--network_module:指定训练的网络模块,OFTv2 用networks.oft_v2,BOFT 用networks.boft;--network_alpha:被两个模块完全忽略(源码create_network中直接del network_alpha);--network_dim:语义与 LoRA 及旧版networks.oft均不同。
关键语义提醒:对
networks.oft_v2与networks.boft,--network_dim被解释为块大小(block size),即单个正交块的尺寸,而不是块数量(旧版networks.oft中它表示块数)。省略--network_dim时,OFTv2 的默认块大小为32,BOFT 的默认块大小为4。
--network_dropout会被映射为旋转块的乘性 dropout(multiplicative dropout):训练过程中按概率将随机选中的旋转块替换为单位矩阵。
从源码实现看,这一映射发生在两个层面(train_network.py):训练脚本通过neuron_dropout=args.network_dropout与net_kwargs["dropout"]传入,而_create_network_args中 dropout 的取值优先级为「显式--network_dropout>--network_args中的dropout/dropout_probability(OFTv2)或boft_dropout/dropout(BOFT)」。
2.2 OFTv2 专属参数(--network_args)
| 参数 | 默认值 | 说明 |
|---|---|---|
block_size(或oft_block_size) | --network_dim(32) | 每个正交块的大小;优先于--network_dim |
coft(或oft_coft) | false | 启用约束 OFT(COFT),每步把旋转投影到 ε 球内 |
coft_eps | 6e-5 | COFT 的约束半径 |
block_share(或oft_block_share) | false | 层内所有块共享同一个旋转块(参数更少) |
dropout(或dropout_probability) | 0.0 | 块 dropout 概率;若显式给出--network_dropout则以其为准 |
enable_conv | false | 同时作用于 3x3 Conv2d 层(ResNet 块、上/下采样器) |
auto_adjust | true | 当in_features不能被块大小整除时,自动挑选最近的约数;false则直接报错 |
include_patterns/exclude_patterns | 无 | Python 正则列表,对模块名做完整匹配,例如"exclude_patterns=['.*attn2.*']" |
2.3 BOFT 专属参数(--network_args)
| 参数 | 默认值 | 说明 |
|---|---|---|
block_size(或boft_block_size) | --network_dim(4) | 每个正交块的大小;与block_num二选一 |
block_num(或boft_block_num) | 无 | 每层块数量;块大小由in_features推导 |
boft_n_butterfly_factor | 1 | 蝴蝶因子数量。1为带缩放因子的普通块对角 OFT;大于 1 时引入蝴蝶置换。大于 1 要求块大小与块数量均为偶数,且in_features能被block_size * 2^(factor-1)整除 |
dropout(或boft_dropout) | 0.0 | 块 dropout 概率;--network_dropout优先 |
enable_conv | false | 同时作用于 3x3 Conv2d 层 |
auto_adjust | true | 请求形状不合法时自动选择有效块形状 |
include_patterns/exclude_patterns | 无 | 与 OFTv2 相同 |
2.4 参数解析与形状校验的源码佐证
- OFTv2 参数解析(oft_v2.py):
_create_network_args依次解析block_size(取oft_block_size或block_size,缺省回落network_dim,再缺省 32)、coft、coft_eps、block_share、dropout、enable_conv、auto_adjust、include_patterns/exclude_patterns; - BOFT 参数解析(boft.py):支持
boft_block_size/boft_block_num互斥指定,block_size缺省为network_dim,再缺省为 4; - 形状校验(boft.py):
_valid_boft_shape检查block_size * block_num == in_features,当因子数 > 1 时还要求block_num % 2^(factor-1) == 0、block_size与block_num均为偶数、in_features % (block_size * 2^(factor-1)) == 0。若同时给定block_size与block_num会直接抛出ValueError; - auto_adjust 实现(orthogonal_common.py):
adjust_block_size在给定块大小不能整除in_features时,向上/向下寻找最近的约数;OFTv2 侧若auto_adjust=false且不整除则抛错(oft_v2.py)。
3. 完整训练命令示例
3.1 SDXL + OFTv2(仅 U-Net)
accelerate launch --num_cpu_threads_per_process 1 sdxl_train_network.py \ --pretrained_model_name_or_path="/path/to/sdxl_model.safetensors" \ --dataset_config="/path/to/config.toml" \ --output_dir="./output" --output_name="sdxl_oftv2" --save_model_as=safetensors \ --network_module=networks.oft_v2 --network_dim=32 \ --network_args "coft=false" "block_share=false" \ --network_train_unet_only \ --learning_rate=1e-4 --max_train_steps=1000 --train_batch_size=1 \ --mixed_precision=fp16 --sdpa --gradient_checkpointing --cache_latents要点说明:
--network_dim=32作为 OFTv2 的块大小;--network_train_unet_only仅训练 U-Net 部分(对应脚本中的--network_train_unet_only参数,SD1.x/SD2.x 使用train_network.py时还可结合--network_train_text_encoder_only等);--cache_latents缓存潜在表示以节省显存;--gradient_checkpointing会同时在反向传播阶段重算旋转矩阵(见第 6 节注意事项)。
3.2 BOFT 双蝴蝶因子
--network_module=networks.boft --network_dim=8 \ --network_args "boft_n_butterfly_factor=2"即块大小 8、蝴蝶因子为 2(2^(2-1) = 2,要求in_features能被 16 整除且块数量为偶数,auto_adjust默认开启可自动适配)。
3.3 其余通用选项
--unet_lr、--text_encoder_lr、--network_weights(加载已有权重继续训练)、采样生成等选项与 LoRA 训练完全一致,详见 train_network.md 与 sdxl_train_network.md。
4. 工作原理:Cayley 参数化、COFT 与蝴蝶置换
4.1 OFTv2:块对角旋转与 Cayley 参数化
OFTv2 将每层的输入特征in_features划分为rank = in_features // block_size个块,每个块使用一个block_size × block_size的正交矩阵。可训练参数oft_R.weight的形状为(rank, n_elements),其中n_elements = block_size * (block_size - 1) / 2,即反对称矩阵的上三角独立元素数——正交矩阵的 Cayley 参数化只需要这么多自由度。
核心实现(oft_v2.py):
_pytorch_skew_symmetric:把参数向量组装成反对称矩阵(matrix - matrix.transpose(-2, -1));_cayley_batch:通过Cayley 变换R = (I + Q)^{-1}(I - Q)从反对称矩阵Q生成正交矩阵。默认启用 Neumann 级数近似(use_cayley_neumann=true,默认展开 5 项),可在精度与速度间取舍;关闭时使用精确的torch.linalg.solve;forward:将输入xreshape 为(..., rank, block_size),用torch.einsum("...rk,rkc->...rc", ...)逐块旋转后还原形状;block_share=true时,仅保留 1 个旋转块,按rank次数repeat复用(oft_v2.py),参数量从rank × n_elements降为1 × n_elements。
4.2 COFT:ε 球约束投影
coft=true时,每步前向先将旋转参数投影到以单位矩阵为中心的ε 球内(oft_v2.py):计算参数矩阵到原点的 Frobenius 范数,若超出coft_eps则缩放回边界。这一约束能限制旋转偏离单位阵的程度,防止微调破坏原始权重结构。测试用例 test_orthogonal_networks.py 验证了 COFT 投影后梯度依然正常传播、基础权重保持不变。
4.3 BOFT:蝴蝶分解与输出缩放
BOFT 的可训练参数为boft_R(形状(n_factors, block_num, block_size, block_size))与boft_s(形状(out_features, 1),每输出通道缩放)。其特点(boft.py):
- 蝴蝶置换:
boft_n_butterfly_factor > 1时,通过block_butterfly_perm生成置换矩阵boft_P(非持久 buffer),对块做奇偶交错重排,使不同块之间产生信息交互,突破纯块对角的表达限制; - Cayley 变换:
cayley_batch对每个block_size × block_size块独立做 Cayley 变换生成正交块; - 旋转-缩放:前向时逐因子做
P @ blockdiag(R) @ P^T并连乘,再右乘输入权重,最后按输出通道乘以boft_s缩放; - dropout 差异:BOFT 的乘性 dropout 在随机选中的单个因子内随机替换
p × block_num个块为单位矩阵(boft.py),与 OFTv2 按块整体掩码的实现略有不同。
5. 权重格式与兼容性
5.1 保存格式
权重使用与 LoRA 相同的前缀保存:lora_unet_...、lora_te_...、lora_te1_...、lora_te2_...,后接参数名,例如:
lora_unet_down_blocks_1_attentions_0_transformer_blocks_0_attn1_to_q.oft_R.weight前缀规则定义在 orthogonal_common.py,native 键名由native_prefix(orthogonal_common.py)把模块名中的.替换为_生成。BOFT 对应...boft_R、...boft_s。
5.2 加载时的多格式识别
使用--network_weights、gen_img.py或合并流程加载时,除 native 格式外还识别以下键格式(orthogonal_common.py):
- OMI 风格前缀:
unet./clip_l./clip_g.加原始点分模块名(omi_prefix); - PEFT 适配器格式:
base_model.model.<模块名>.oft_R.weight/.boft_R/.boft_s。可以直接传入包含adapter_model.safetensors的 PEFT 适配器目录——load_weights_sd会自动识别目录并从中读取适配器文件(orthogonal_common.py)。
匹配不到的键会发出警告并忽略。测试用例 test_orthogonal_networks.py 验证了 PEFT 风格权重可直接构建网络与训练加载;SDXL 场景下 PEFT 键不区分两个 Text Encoder,加载时需自行注意映射关系(native 格式的lora_te1_/lora_te2_前缀则能精确区分)。
兼容性细节:BOFT 在加载时会对
boft_s的形状做自适应转置(_adapt_state_for_local_shape,boft.py),兼容 PEFT 中(out_features, 1)与本地布局的差异;auto_adjust选出的实际块形状在加载时通过_infer_from_state从权重张量形状反推(OFTv2 用三角元素数反推块大小,BOFT 读取boft_R形状),因此不影响自己训练产出的 checkpoint 兼容性。
6. 注意事项与性能优化
- 速度:旋转矩阵每步前向都要从参数出发通过大量小矩阵运算重建(Cayley 变换 + einsum),因此每步开销高于 LoRA,小 batch 下 GPU 利用率可能偏低。加大 batch size可摊薄该固定开销;
--gradient_checkpointing会在反向阶段同样重算旋转,进一步增加计算量; - 合并:两个模块均支持
merge_to(网络接口is_mergeable()返回True),可在gen_img.py等场景把适配器合并进基础权重,合并结果是原权重被精确旋转后的版本。训练脚本中也可通过--base_weights预合并加载(train_network.py); auto_adjust=true(默认):当请求的块大小不整除in_features时,某些层可能被静默选择不同的块大小。由于加载时从保存权重推断实际形状,这不会影响与你自己 checkpoint 的兼容性;若希望严格报错而非静默调整,请设auto_adjust=false;- 前向一致性测试:仓库测试 test_orthogonal_networks.py 对 Linear、1x1 Conv2d、3x3 Conv2d 三种层验证了「hook 前向输出」与「merge 后权重」数值一致(
atol/rtol=1e-5),可作为自行验证合并正确性的参考方法。
7. 快速决策:OFTv2 还是 BOFT?
| 维度 | OFTv2 | BOFT |
|---|---|---|
| 参数语义 | 纯块对角旋转(Cayley) | 块对角旋转 + 蝴蝶置换 + 输出缩放 |
| 默认块大小 | 32 | 4 |
| 每层参数 | oft_R.weight | boft_R+boft_s |
| 约束变体 | COFT(ε 球投影) | 无(依赖缩放因子) |
| 表达力扩展 | block_share减参 | boft_n_butterfly_factor增参增表达 |
| 适用对象 | SD1.x / SD2.x / SDXL | 同左 |
两者共享同一套目标层筛选、权重前缀与多格式加载逻辑,切换只需更换--network_module并按上表调整--network_dim(块大小)与--network_args。开始训练前,建议先在小步数、小 batch 上验证训练曲线,再根据显存与速度表现决定 batch size 与是否启用--gradient_checkpointing。
- 深度学习
- 计算机视觉
- 媒体生成
- 模型训练
- 微调
【免费下载链接】sd-scripts
相关推荐
SDXL 训练完全指南:sd-scripts 中 sdxl_train.py 微调、LoRA 与 Textual Inversion 实战
SDXL 训练完全指南:sd scripts 中 sdxl_train.py 微调、LoRA 与 Textual Inversion 实战 导读 本文面向希望在
深度学习计算机视觉媒体生成模型训练微调sd-scripts 之 DreamBooth 训练完整指南:从数据准备、正则化图像到 Text Encoder+U-Net 微调实战
sd scripts 之 DreamBooth 训练完整指南:从数据准备、正则化图像到 Text Encoder+U Net 微调实战 本文是基于 sd scr
深度学习计算机视觉媒体生成模型训练微调如何用 kohya-ss/sd-scripts 轻松掌握AI图像生成与训练?超详细新手教程
如何用 kohya ss/sd scripts 轻松掌握AI图像生成与训练?超详细新手教程 kohya ss/sd scripts 是一套功能强大的AI图像生成
深度学习计算机视觉媒体生成模型训练微调
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考