Download checkpoint_splitter/split_checkpoint_sd15.py from waveshaker/loras_tools: direct link, hf CLI and curl.
- Browser
- Download file 4.89 kB
-
https://huggingface.co/waveshaker/loras_tools/resolve/main/checkpoint_splitter/split_checkpoint_sd15.py
- Command line
-
hf download hf://waveshaker/loras_tools/checkpoint_splitter/split_checkpoint_sd15.py
-
curl -L -o split_checkpoint_sd15.py https://huggingface.co/waveshaker/loras_tools/resolve/main/checkpoint_splitter/split_checkpoint_sd15.py
4.89 kB
| import os | |
| import sys | |
| import torch | |
| from safetensors import safe_open | |
| from safetensors.torch import save_file | |
| def split_sd15_ultimate(input_path): | |
| if not os.path.exists(input_path): | |
| print(f"❌ 错误: 找不到输入文件 '{input_path}'") | |
| return | |
| base_dir = os.path.dirname(input_path) | |
| base_name = os.path.splitext(os.path.basename(input_path))[0] | |
| clip_path = os.path.join(base_dir, f"{base_name}_clip.safetensors") | |
| vae_path = os.path.join(base_dir, f"{base_name}_VAE.safetensors") | |
| unet_path = os.path.join(base_dir, f"{base_name}_Diffusion.safetensors") | |
| clip_tensors = {} | |
| vae_tensors = {} | |
| # 这个字典既包含 UNet,也包含所有额外组件(EMA、ControlNet 等) | |
| unet_and_extras = {} | |
| print(f"📦 正在执行【终极暴力提取】SD 1.5 切分: {input_path} ...") | |
| with safe_open(input_path, framework="pt", device="cpu") as f: | |
| for key in f.keys(): | |
| # ========================================== | |
| # 1. 提取并重构 CLIP (强制清洗) | |
| # ========================================== | |
| if key.startswith("cond_stage_model.transformer."): | |
| if "lora" in key or "embedding_manager" in key: | |
| continue | |
| clean_key = key.replace("cond_stage_model.transformer.", "", 1) | |
| # 暴力特征匹配映射 | |
| if "token_embedding.weight" in clean_key: | |
| clip_tensors["text_model.embeddings.token_embedding.weight"] = f.get_tensor(key) | |
| elif "positional_embedding" in clean_key or "position_embedding.weight" in clean_key: | |
| clip_tensors["text_model.embeddings.position_embedding.weight"] = f.get_tensor(key) | |
| elif "position_ids" in clean_key: | |
| continue # 忽略原有的,我们一会自己生成标准的 | |
| elif "text_model.encoder" in clean_key: | |
| clip_tensors[clean_key] = f.get_tensor(key) | |
| elif "final_layer_norm" in clean_key: | |
| clip_tensors[clean_key] = f.get_tensor(key) | |
| else: | |
| clip_tensors[clean_key] = f.get_tensor(key) | |
| # ========================================== | |
| # 2. 提取 VAE | |
| # ========================================== | |
| elif key.startswith("first_stage_model."): | |
| clean_key = key.replace("first_stage_model.", "", 1) | |
| vae_tensors[clean_key] = f.get_tensor(key) | |
| # ========================================== | |
| # 3. 提取 UNet (主体) | |
| # ========================================== | |
| elif key.startswith("model.diffusion_model."): | |
| clean_key = key.replace("model.diffusion_model.", "", 1) | |
| unet_and_extras[clean_key] = f.get_tensor(key) | |
| # ========================================== | |
| # 4. 兼容额外组件 (EMA, ControlNet, 杂项等) | |
| # ========================================== | |
| else: | |
| # 比如 model_ema., control_model., embedding_manager. 等 | |
| # 我们原样保留它们的键名,直接塞进 UNet 字典 | |
| unet_and_extras[key] = f.get_tensor(key) | |
| print("\n💾 正在处理和保存组件...") | |
| # ========================================== | |
| # 5. 核心补全:自动生成缺失的 position_ids | |
| # ========================================== | |
| if clip_tensors: | |
| if "text_model.embeddings.token_embedding.weight" in clip_tensors: | |
| if "text_model.embeddings.position_ids" not in clip_tensors: | |
| clip_tensors["text_model.embeddings.position_ids"] = torch.arange(77).unsqueeze(0).to(torch.int64) | |
| print(" --> 💡 已自动生成缺失的 position_ids") | |
| save_file(clip_tensors, clip_path) | |
| print(f" --> ✅ CLIP 完美提取并保存成功 (键数: {len(clip_tensors)})") | |
| else: | |
| print(f" ⚠️ 警告: 未找到核心词向量 (token_embedding),CLIP 提取失败!") | |
| if vae_tensors: | |
| save_file(vae_tensors, vae_path) | |
| print(f" --> ✅ VAE 保存成功 (键数: {len(vae_tensors)})") | |
| if unet_and_extras: | |
| save_file(unet_and_extras, unet_path) | |
| print(f" --> ✅ Diffusion (含额外组件) 保存成功 (总键数: {len(unet_and_extras)})") | |
| print("\n🎉 终极切分任务完成!") | |
| if __name__ == "__main__": | |
| if len(sys.argv) != 2: | |
| print("💡 用法: python split_sd15_ultimate.py <你的_sd15_模型.safetensors>") | |
| sys.exit(1) | |
| split_sd15_ultimate(sys.argv[1]) |