File size: 6,023 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
import os
import argparse
from safetensors import safe_open
from safetensors.torch import save_file

def split_illustrious_compatible(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"📦 正在执行切分: {input_path} ...")

    with safe_open(input_path, framework="pt", device="cpu") as f:
        all_keys = f.keys()
        
        # 判断是否是官方的 transformers 架构
        has_text_model_g = any(k.startswith("conditioner.embedders.1.transformer.") for k in all_keys)

        for key in all_keys:
            tensor = f.get_tensor(key)
            
            # 1. 提取并清洗 CLIP L (官方和 WAI 都是 transformers 结构)
            if key.startswith("conditioner.embedders.0.transformer."):
                new_key = key.replace("conditioner.embedders.0.transformer.", "", 1)
                clipL_tensors[new_key] = tensor

            # 2. 提取并精准重组 CLIP G
            elif key.startswith("conditioner.embedders.1."):
                if has_text_model_g:
                    # 官方 Illustrious 格式 (transformers)
                    new_key = key.replace("conditioner.embedders.1.transformer.", "", 1)
                    if new_key != "logit_scale":
                        clipG_tensors[new_key] = tensor
                else:
                    # WAI 格式 (OpenCLIP 原生格式)
                    old_key = key.replace("conditioner.embedders.1.model.", "", 1)
                    if old_key == "logit_scale":
                        continue
                    
                    new_key = old_key
                    
                    # 基础结构重命名
                    if "transformer.resblocks." in old_key:
                        new_key = old_key.replace("transformer.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 old_key == "positional_embedding":
                        new_key = "text_model.embeddings.position_embedding.weight"
                    elif old_key == "token_embedding.weight":
                        new_key = "text_model.embeddings.token_embedding.weight"
                    elif old_key == "ln_final.weight":
                        new_key = "text_model.final_layer_norm.weight"
                    elif old_key == "ln_final.bias":
                        new_key = "text_model.final_layer_norm.bias"
                    elif old_key == "text_projection":
                        new_key = "text_projection"

                    # 🛠️ 核心修复:解耦 in_proj 复合权重矩阵为标准的 q/k/v 组件
                    if ".attn.in_proj_weight" in old_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 old_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 old_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 old_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
                
            # 3. 提取 VAE
            elif key.startswith("first_stage_model."):
                vae_tensors[key.replace("first_stage_model.", "", 1)] = tensor
                
            # 4. 提取 UNet
            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)})")
    if vae_tensors: save_file(vae_tensors, vae_path)
    if diffusion_tensors: save_file(diffusion_tensors, unet_path)

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

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("input_file", type=str)
    args = parser.parse_args()
    split_illustrious_compatible(args.input_file)