File size: 6,903 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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import os
import argparse
from safetensors import safe_open
from safetensors.torch import save_file

def split_sdxl(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]
    
    clipL_path = os.path.join(base_dir, f"{base_name}_clipL.safetensors")
    clipG_path = os.path.join(base_dir, f"{base_name}_clipG.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")

    clipL_tensors = {}
    clipG_tensors = {}
    vae_tensors = {}
    diffusion_tensors = {}

    print(f"📦 正在执行 SDXL 纯净切分与权重重组: {input_path} ...")

    with safe_open(input_path, framework="pt", device="cpu") as f:
        all_keys = f.keys()
        
        # 动态判断 CLIP-G 是否为原生 OpenCLIP 格式
        # 如果有 resblocks 或是 model.transformer.resblocks,说明需要转换
        is_openclip_g = any("resblocks." in k for k in all_keys if k.startswith("conditioner.embedders.1."))

        for key in all_keys:
            tensor = f.get_tensor(key)
            
            # 1. 提取并清洗 CLIP L (768 维)
            if key.startswith("conditioner.embedders.0."):
                clean_key = key.replace("conditioner.embedders.0.", "", 1)
                if clean_key.startswith("transformer."):
                    clean_key = clean_key.replace("transformer.", "", 1)
                
                # 过滤 position_ids
                if "position_ids" in clean_key:
                    continue
                    
                clipL_tensors[clean_key] = tensor

            # 2. 提取并清洗 CLIP G (1280 维)
            elif key.startswith("conditioner.embedders.1."):
                clean_key = key.replace("conditioner.embedders.1.", "", 1)
                
                # 过滤全局标量
                if clean_key.endswith("logit_scale") or "position_ids" in clean_key:
                    continue

                if is_openclip_g:
                    # === OpenCLIP (如 Juggernaut) 格式转换 ===
                    # 剥离所有可能的前缀
                    openclip_key = clean_key.replace("model.", "", 1).replace("transformer.", "", 1)
                    
                    if "resblocks." in openclip_key:
                        new_key = openclip_key.replace("resblocks.", "text_model.encoder.layers.", 1)
                        new_key = new_key.replace(".ln_1.", ".layer_norm1.", 1)
                        new_key = new_key.replace(".ln_2.", ".layer_norm2.", 1)
                        new_key = new_key.replace(".mlp.c_fc.", ".mlp.fc1.", 1)
                        new_key = new_key.replace(".mlp.c_proj.", ".mlp.fc2.", 1)
                    elif openclip_key == "positional_embedding":
                        new_key = "text_model.embeddings.position_embedding.weight"
                    elif openclip_key == "token_embedding.weight":
                        new_key = "text_model.embeddings.token_embedding.weight"
                    elif openclip_key == "ln_final.weight":
                        new_key = "text_model.final_layer_norm.weight"
                    elif openclip_key == "ln_final.bias":
                        new_key = "text_model.final_layer_norm.bias"
                    elif openclip_key == "text_projection":
                        new_key = "text_projection.weight"
                    else:
                        new_key = openclip_key # 其他键保持原样

                    # 核心:处理复合权重 (in_proj)
                    if ".attn.in_proj_weight" in openclip_key:
                        base_layer = new_key.replace(".attn.in_proj_weight", ".self_attn", 1)
                        q, k, v = tensor.chunk(3, dim=0)
                        clipG_tensors[f"{base_layer}.q_proj.weight"] = q
                        clipG_tensors[f"{base_layer}.k_proj.weight"] = k
                        clipG_tensors[f"{base_layer}.v_proj.weight"] = v
                    elif ".attn.in_proj_bias" in openclip_key:
                        base_layer = new_key.replace(".attn.in_proj_bias", ".self_attn", 1)
                        q, k, v = tensor.chunk(3, dim=0)
                        clipG_tensors[f"{base_layer}.q_proj.bias"] = q
                        clipG_tensors[f"{base_layer}.k_proj.bias"] = k
                        clipG_tensors[f"{base_layer}.v_proj.bias"] = v
                    elif ".attn.out_proj.weight" in openclip_key:
                        new_key = new_key.replace(".attn.out_proj.weight", ".self_attn.out_proj.weight", 1)
                        clipG_tensors[new_key] = tensor
                    elif ".attn.out_proj.bias" in openclip_key:
                        new_key = new_key.replace(".attn.out_proj.bias", ".self_attn.out_proj.bias", 1)
                        clipG_tensors[new_key] = tensor
                    else:
                        clipG_tensors[new_key] = tensor
                else:
                    # === 已经是标准 Transformers 格式 ===
                    if clean_key.startswith("transformer."):
                        clean_key = clean_key.replace("transformer.", "", 1)
                    clipG_tensors[clean_key] = tensor

            # 3. 提取并清洗 VAE
            elif key.startswith("first_stage_model."):
                vae_tensors[key.replace("first_stage_model.", "", 1)] = tensor
                
            # 4. 提取并清洗 UNet (Diffusion)
            elif key.startswith("model.diffusion_model."):
                diffusion_tensors[key.replace("model.diffusion_model.", "", 1)] = tensor

    print("\n💾 正在保存组件...")
    
    if clipL_tensors:
        save_file(clipL_tensors, clipL_path)
        print(f"  --> CLIP-L 保存成功 (键数: {len(clipL_tensors)})")
    if clipG_tensors:
        save_file(clipG_tensors, clipG_path)
        print(f"  --> CLIP-G 保存成功 (键数: {len(clipG_tensors)})") # 正常应该输出 517
    if vae_tensors: 
        save_file(vae_tensors, vae_path)
        print(f"  --> VAE 保存成功 (键数: {len(vae_tensors)})")
    if diffusion_tensors: 
        save_file(diffusion_tensors, unet_path)
        print(f"  --> UNet 保存成功 (键数: {len(diffusion_tensors)})")

    print("\n🎉 SDXL 切分任务完成!")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="纯净 SDXL 模型切分与格式转换工具")
    parser.add_argument("input_file", type=str, help="输入的 SDXL 大模型文件路径")
    args = parser.parse_args()
    split_sdxl(args.input_file)