File size: 4,888 Bytes
9f09426
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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])