Bidili Generator实操手册:SDXL 1.0 LoRA权重合并与导出方法

张开发
2026/4/9 5:16:00 15 分钟阅读

分享文章

Bidili Generator实操手册:SDXL 1.0 LoRA权重合并与导出方法
Bidili Generator实操手册SDXL 1.0 LoRA权重合并与导出方法你是不是也遇到过这样的问题好不容易训练好了一个LoRA模型想把它用在SDXL 1.0上结果发现要么加载不了要么效果不对要么显存直接爆掉别担心今天我就来手把手教你如何把Bidili自定义LoRA权重完美地合并到SDXL 1.0底座模型里并导出成一个独立的、可以直接使用的模型文件。整个过程就像把两个乐高积木拼在一起那么简单而且完全在本地运行不需要联网。1. 为什么需要合并LoRA权重在开始之前我们先搞清楚为什么要做这件事。你可能已经用过Bidili Generator它确实很方便通过Streamlit界面实时调整LoRA强度就能生成图片。但这种方式每次生成都需要同时加载基础模型和LoRA权重有两个明显的痛点第一是速度慢。每次启动都要加载两个模型文件特别是SDXL 1.0本身就不小加载时间自然就长了。第二是灵活性差。如果你想把这个定制好的模型分享给朋友或者部署到其他没有安装Bidili Generator的环境里就变得很麻烦。你需要把基础模型、LoRA权重、还有一堆配置文件都打包过去。而把LoRA权重合并到基础模型里就能一劳永逸地解决这些问题。合并后的模型加载更快只需要加载一个文件部署更简单一个文件走天下分享和迁移都方便兼容性更好任何支持SDXL 1.0的推理工具都能直接使用简单来说合并就是把Bidili的风格“烧录”到SDXL模型里让它变成一个全新的、自带风格的模型。2. 准备工作环境与模型检查工欲善其事必先利其器。在开始合并之前我们需要确保环境一切就绪。2.1 确认你的Bidili Generator环境首先确保你已经按照Bidili Generator的部署指南成功安装并运行了工具。如果你还没安装这里有个快速检查清单Python版本3.8以上PyTorch2.0以上最好支持CUDA关键库diffusers, transformers, accelerate, safetensors显存建议至少12GB合并过程比单纯推理需要更多显存你可以在Bidili Generator的目录下打开终端运行以下命令检查环境python -c import torch; print(fPyTorch版本: {torch.__version__}) python -c import diffusers; print(fDiffusers版本: {diffusers.__version__})2.2 找到模型文件合并需要两个核心文件SDXL 1.0基础模型通常是一个包含model_index.json的文件夹Bidili LoRA权重文件通常是.safetensors格式的文件在Bidili Generator的典型目录结构里它们通常在这里your_bidili_directory/ ├── models/ │ ├── stable-diffusion-xl-base-1.0/ # SDXL基础模型 │ └── bidili_lora.safetensors # Bidili LoRA权重 └── app.py # Streamlit主程序重要提示请确认你的LoRA权重确实是针对SDXL 1.0训练的。有些LoRA是针对SD 1.5训练的如果用在SDXL上会导致模型崩溃。3. 核心步骤LoRA权重合并实战现在进入正题我将分三步带你完成整个合并过程。不用担心每一步都有详细的代码和解释。3.1 第一步加载基础模型和LoRA权重我们先写一个Python脚本把两个模型都加载到内存里。这里我推荐使用diffusers库因为它对SDXL和LoRA的支持最好。import torch from diffusers import StableDiffusionXLPipeline from safetensors.torch import load_file # 设置设备GPU或CPU device cuda if torch.cuda.is_available() else cpu print(f使用设备: {device}) # 1. 加载SDXL 1.0基础模型 print(正在加载SDXL 1.0基础模型...) base_model_path ./models/stable-diffusion-xl-base-1.0 # 使用BF16精度加载节省显存的同时保持质量 pipe StableDiffusionXLPipeline.from_pretrained( base_model_path, torch_dtypetorch.bfloat16, # 使用BF16精度 variantfp16, # 加载fp16变体 use_safetensorsTrue # 使用safetensors格式更安全 ).to(device) print(✓ SDXL基础模型加载完成) # 2. 加载Bidili LoRA权重 print(正在加载Bidili LoRA权重...) lora_path ./models/bidili_lora.safetensors # 读取LoRA权重文件 lora_state_dict load_file(lora_path) print(f✓ LoRA权重加载完成包含 {len(lora_state_dict)} 个参数)这段代码做了几件重要的事使用torch.bfloat16精度加载模型这在40系显卡上效率更高指定variantfp16确保加载正确的模型变体使用safetensors格式避免pickle的安全风险3.2 第二步将LoRA权重注入到基础模型这是最关键的一步。LoRA权重不是独立的模型而是一组“增量参数”需要正确地加到基础模型的对应层上。# 定义LoRA权重注入函数 def apply_lora_to_pipe(pipe, lora_state_dict, lora_scale1.0): 将LoRA权重应用到SDXL Pipeline 参数: pipe: SDXL Pipeline对象 lora_state_dict: LoRA权重字典 lora_scale: LoRA强度默认1.0 # 获取UNet模型的state_dict unet_state_dict pipe.unet.state_dict() print(开始注入LoRA权重...) # 遍历LoRA权重中的所有键 for key in lora_state_dict.keys(): # LoRA权重通常包含lora_unet前缀 if lora_unet in key: # 提取基础模型中的对应层名 # 例如: lora_unet.down_blocks.0.attentions.0.to_q.lora_down.weight # 对应: down_blocks.0.attentions.0.to_q.weight base_key key.replace(lora_unet., ).replace(.lora_down, ).replace(.lora_up, ) # 检查基础模型中是否有对应的层 if base_key in unet_state_dict: # 获取LoRA的down和up权重 if lora_down in key: lora_down_key key lora_up_key key.replace(lora_down, lora_up) if lora_up_key in lora_state_dict: # 计算LoRA更新: ΔW A * B * scale lora_down lora_state_dict[lora_down_key].float() lora_up lora_state_dict[lora_up_key].float() # LoRA更新公式 lora_update torch.mm(lora_up, lora_down) * lora_scale # 将更新加到基础权重上 unet_state_dict[base_key] lora_update.to(unet_state_dict[base_key].dtype) print(f ✓ 已更新层: {base_key}) # 将更新后的state_dict加载回UNet pipe.unet.load_state_dict(unet_state_dict) print(f✓ LoRA权重注入完成强度系数: {lora_scale}) return pipe # 应用LoRA权重强度设为1.0与Bidili Generator默认值一致 lora_strength 1.0 print(f\n应用LoRA强度: {lora_strength}) pipe apply_lora_to_pipe(pipe, lora_state_dict, lora_scalelora_strength)这里有几个技术细节需要注意LoRA权重结构LoRA通常包含lora_down和lora_up两部分对应低秩分解的A和B矩阵更新公式ΔW A × B × scale这就是LoRA的核心思想强度系数lora_scale参数控制风格强度1.0就是原始训练强度3.3 第三步测试合并效果并导出模型在导出之前我们先测试一下合并是否成功。# 测试生成一张图片 print(\n测试合并效果生成测试图片...) prompt a beautiful portrait of a woman, detailed eyes, professional photography, 8k negative_prompt ugly, blurry, bad anatomy, deformed # 生成图片 with torch.no_grad(): image pipe( promptprompt, negative_promptnegative_prompt, num_inference_steps25, guidance_scale7.0, height1024, width1024, generatortorch.Generator(devicedevice).manual_seed(42) # 固定种子确保可复现 ).images[0] # 保存测试图片 test_output_path ./bidili_sdxl_merged_test.png image.save(test_output_path) print(f✓ 测试图片已保存: {test_output_path}) # 检查图片是否带有Bidili风格 print(请查看生成的图片确认Bidili风格是否正确应用) print(如果风格明显说明合并成功)如果测试图片显示出了Bidili的风格特征恭喜你合并成功了现在可以导出最终的模型了。# 导出合并后的模型 print(\n开始导出合并后的模型...) # 设置导出路径 output_dir ./bidili_sdxl_merged save_path pipe.save_pretrained(output_dir) print(f✓ 模型已导出到: {output_dir}) print(f导出文件包括:) print(f - model_index.json) print(f - unet/diffusion_pytorch_model.safetensors) print(f - vae/diffusion_pytorch_model.safetensors) print(f - text_encoder/model.safetensors) print(f - text_encoder_2/model.safetensors) print(f - scheduler/scheduler_config.json) print(f - tokenizer/ 和 tokenizer_2/ 目录) # 验证导出是否成功 import os if os.path.exists(os.path.join(output_dir, unet/diffusion_pytorch_model.safetensors)): print(\n✅ 模型导出成功现在你可以:) print(1. 在任何支持SDXL的WebUI中使用这个模型) print(2. 分享给朋友他们不需要安装Bidili Generator) print(3. 用于批量生成速度比实时加载LoRA更快) else: print(\n❌ 导出失败请检查路径和权限)4. 高级技巧权重强度调节与多LoRA合并基本的合并掌握了我们来看看一些进阶玩法。4.1 调节LoRA强度导出不同版本你可能想要导出不同风格强度的版本比如“轻度风格”和“重度风格”。这很简单只需要调整lora_scale参数。def export_model_with_lora_strength(base_model_path, lora_path, output_dir, strength1.0): 导出指定LoRA强度的模型 print(f\n导出LoRA强度 {strength} 的模型...) # 重新加载干净的基础模型 pipe StableDiffusionXLPipeline.from_pretrained( base_model_path, torch_dtypetorch.bfloat16, variantfp16, use_safetensorsTrue ).to(device) # 加载LoRA权重 lora_state_dict load_file(lora_path) # 应用指定强度的LoRA pipe apply_lora_to_pipe(pipe, lora_state_dict, lora_scalestrength) # 导出模型 strength_suffix str(strength).replace(., _) final_output_dir f{output_dir}_strength_{strength_suffix} pipe.save_pretrained(final_output_dir) print(f✓ 已导出到: {final_output_dir}) return final_output_dir # 导出三个不同强度的版本 strengths [0.5, 1.0, 1.5] # 轻度、标准、重度 for strength in strengths: export_model_with_lora_strength( base_model_path, lora_path, ./bidili_sdxl, strengthstrength )这样你就得到了三个模型bidili_sdxl_strength_0_5轻度Bidili风格bidili_sdxl_strength_1_0标准Bidili风格bidili_sdxl_strength_1_5重度Bidili风格4.2 合并多个LoRA权重如果你想融合多个风格比如Bidili风格某个特定画风可以同时合并多个LoRA。def merge_multiple_loras(base_model_path, lora_paths, output_dir, lora_scalesNone): 合并多个LoRA权重到一个模型 print(f合并 {len(lora_paths)} 个LoRA权重...) # 加载基础模型 pipe StableDiffusionXLPipeline.from_pretrained( base_model_path, torch_dtypetorch.bfloat16, variantfp16, use_safetensorsTrue ).to(device) # 如果没有指定强度默认都用1.0 if lora_scales is None: lora_scales [1.0] * len(lora_paths) # 依次应用每个LoRA for i, (lora_path, scale) in enumerate(zip(lora_paths, lora_scales)): print(f\n应用第 {i1} 个LoRA: {os.path.basename(lora_path)} (强度: {scale})) lora_state_dict load_file(lora_path) pipe apply_lora_to_pipe(pipe, lora_state_dict, lora_scalescale) # 导出最终模型 pipe.save_pretrained(output_dir) print(f\n✓ 多LoRA合并完成已导出到: {output_dir}) return pipe # 示例合并两个LoRA lora_paths [ ./models/bidili_lora.safetensors, ./models/another_style_lora.safetensors # 另一个风格的LoRA ] scales [1.0, 0.3] # Bidili用1.0强度另一个风格用0.3强度 merged_pipe merge_multiple_loras( base_model_path, lora_paths, ./bidili_combined_sdxl, lora_scalesscales )5. 常见问题与解决方案在实际操作中你可能会遇到一些问题。这里我整理了几个常见的情况和解决方法。5.1 显存不足怎么办SDXL模型很大加上LoRA合并需要额外的显存。如果遇到CUDA out of memory错误可以尝试方案一使用CPU卸载# 在加载模型时启用CPU卸载 pipe StableDiffusionXLPipeline.from_pretrained( base_model_path, torch_dtypetorch.bfloat16, variantfp16, use_safetensorsTrue, device_mapauto, # 自动分配设备 offload_folder./offload # CPU卸载的临时文件夹 )方案二分块加载和合并如果LoRA文件太大可以分块加载def apply_lora_in_chunks(pipe, lora_path, chunk_size10): 分块应用LoRA权重减少显存峰值 lora_state_dict load_file(lora_path) keys list(lora_state_dict.keys()) for i in range(0, len(keys), chunk_size): chunk_keys keys[i:ichunk_size] chunk_dict {k: lora_state_dict[k] for k in chunk_keys} # 应用当前chunk pipe apply_lora_chunk(pipe, chunk_dict) print(f进度: {ilen(chunk_keys)}/{len(keys)}) # 清理显存 torch.cuda.empty_cache() return pipe5.2 LoRA权重不兼容SDXL如果你发现合并后模型崩溃或者生成乱码可能是LoRA权重不兼容。检查方法def check_lora_compatibility(lora_path, base_model_path): 检查LoRA是否与SDXL兼容 # 加载基础模型的UNet结构 from diffusers import UNet2DConditionModel unet UNet2DConditionModel.from_pretrained( base_model_path, subfolderunet, torch_dtypetorch.float16 ) # 获取基础模型的参数名 base_keys set(unet.state_dict().keys()) # 加载LoRA权重 lora_state_dict load_file(lora_path) # 检查LoRA键名 lora_keys list(lora_state_dict.keys()) sample_key lora_keys[0] if lora_keys else print(fLoRA权重示例键名: {sample_key}) print(fLoRA是否包含lora_unet前缀: {lora_unet in sample_key}) # 尝试匹配一个键 if lora_unet in sample_key: test_key sample_key.replace(lora_unet., ).replace(.lora_down, ) if test_key in base_keys: print(✅ LoRA权重与SDXL UNet结构兼容) return True else: print(❌ LoRA权重可能与SDXL不兼容) print(f转换后的键 {test_key} 不在基础模型中) return False else: print(❌ LoRA权重格式不正确缺少lora_unet前缀) return False5.3 导出模型文件太大合并后的模型文件可能很大通常7-8GB。如果你需要减小体积方案一导出为FP16精度# 导出时指定FP16精度 pipe.save_pretrained( output_dir, variantfp16, # 保存为fp16版本 safe_serializationTrue # 使用safetensors格式 )方案二使用模型修剪高级技巧def prune_model_weights(pipe, prune_ratio0.1): 修剪模型权重减少文件大小会损失一些质量 print(f修剪模型权重比例: {prune_ratio}) with torch.no_grad(): for name, param in pipe.unet.named_parameters(): if weight in name and param.dim() 2: # 计算阈值 threshold torch.quantile(torch.abs(param), prune_ratio) # 将小权重置零 mask torch.abs(param) threshold param.data * mask.float() return pipe6. 总结从合并到部署的全流程让我们回顾一下完整的流程并看看合并后的模型怎么用。6.1 完整合并脚本这里是一个完整的、可以直接运行的合并脚本#!/usr/bin/env python3 Bidili LoRA权重合并脚本 将Bidili LoRA合并到SDXL 1.0基础模型 import torch import os from diffusers import StableDiffusionXLPipeline from safetensors.torch import load_file def main(): # 配置参数 BASE_MODEL_PATH ./models/stable-diffusion-xl-base-1.0 LORA_PATH ./models/bidili_lora.safetensors OUTPUT_DIR ./bidili_sdxl_merged LORA_STRENGTH 1.0 DEVICE cuda if torch.cuda.is_available() else cpu print( * 50) print(Bidili LoRA权重合并工具) print( * 50) # 检查文件是否存在 if not os.path.exists(BASE_MODEL_PATH): print(f错误: 基础模型路径不存在: {BASE_MODEL_PATH}) return if not os.path.exists(LORA_PATH): print(f错误: LoRA权重路径不存在: {LORA_PATH}) return # 1. 加载基础模型 print(f\n[1/4] 加载SDXL基础模型: {BASE_MODEL_PATH}) pipe StableDiffusionXLPipeline.from_pretrained( BASE_MODEL_PATH, torch_dtypetorch.bfloat16, variantfp16, use_safetensorsTrue ).to(DEVICE) # 2. 加载LoRA权重 print(f[2/4] 加载LoRA权重: {os.path.basename(LORA_PATH)}) lora_weights load_file(LORA_PATH) print(f 找到 {len(lora_weights)} 个LoRA参数) # 3. 应用LoRA权重 print(f[3/4] 应用LoRA权重 (强度: {LORA_STRENGTH})) # 获取UNet状态字典 unet_state_dict pipe.unet.state_dict() applied_layers 0 for key in lora_weights.keys(): if lora_unet in key and lora_down in key: # 提取基础层名 base_key key.replace(lora_unet., ).replace(.lora_down, ) if base_key in unet_state_dict: # 找到对应的lora_up键 up_key key.replace(lora_down, lora_up) if up_key in lora_weights: # 计算LoRA更新 lora_down lora_weights[key].float() lora_up lora_weights[up_key].float() lora_update torch.mm(lora_up, lora_down) * LORA_STRENGTH # 更新权重 unet_state_dict[base_key] lora_update.to(unet_state_dict[base_key].dtype) applied_layers 1 # 加载回UNet pipe.unet.load_state_dict(unet_state_dict) print(f 成功应用到 {applied_layers} 个层) # 4. 导出合并后的模型 print(f[4/4] 导出模型到: {OUTPUT_DIR}) pipe.save_pretrained(OUTPUT_DIR) print(f\n✅ 合并完成) print(f模型已保存到: {OUTPUT_DIR}) print(f\n使用说明:) print(f1. 在ComfyUI中: 将整个文件夹放到 models/checkpoints/ 目录下) print(f2. 在Automatic1111中: 放到 stable-diffusion-webui/models/Stable-diffusion/ 目录下) print(f3. 模型名称: bidili_sdxl_merged) if __name__ __main__: main()6.2 如何使用合并后的模型合并后的模型可以像普通SDXL模型一样使用在ComfyUI中使用将bidili_sdxl_merged文件夹复制到ComfyUI/models/checkpoints/在ComfyUI的Load Checkpoint节点中选择bidili_sdxl_merged像使用普通SDXL模型一样使用无需额外加载LoRA在Automatic1111 WebUI中使用将bidili_sdxl_merged文件夹复制到stable-diffusion-webui/models/Stable-diffusion/重启WebUI在模型选择下拉框中找到bidili_sdxl_merged直接使用所有参数设置与SDXL相同在代码中使用from diffusers import StableDiffusionXLPipeline import torch # 加载合并后的模型 pipe StableDiffusionXLPipeline.from_pretrained( ./bidili_sdxl_merged, torch_dtypetorch.float16, ).to(cuda) # 直接生成无需额外LoRA设置 image pipe( a beautiful portrait with bidili style, num_inference_steps25, guidance_scale7.0 ).images[0]6.3 性能对比最后让我们看看合并带来的实际好处对比项实时加载LoRA合并后模型加载时间15-20秒加载两个模型8-12秒加载一个模型显存占用基础模型LoRA权重仅合并后模型部署复杂度需要基础模型LoRA配置单个模型文件兼容性需要特定工具支持任何SDXL兼容工具分享便利性需要打包多个文件分享一个文件从表格可以看出合并后的模型在加载速度、部署便利性和兼容性上都有明显优势。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章