ajh-code commited on
Commit
f44fcbf
·
verified ·
1 Parent(s): e4bfc4f

Add vendor/mage_flow/models/utils.py

Browse files
Files changed (1) hide show
  1. vendor/mage_flow/models/utils.py +175 -0
vendor/mage_flow/models/utils.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import math
3
+ import os
4
+
5
+ import torch
6
+ from einops import rearrange
7
+ from loguru import logger
8
+ from safetensors.torch import load_file
9
+ from safetensors.torch import load_file as load_sft
10
+ from torch import Tensor
11
+
12
+ from .mage_flow import MageFlow, MageFlowParams
13
+
14
+
15
+ def get_noise(
16
+ num_samples: int,
17
+ channel: int,
18
+ height: int,
19
+ width: int,
20
+ device: torch.device,
21
+ dtype: torch.dtype,
22
+ seed: int,
23
+ ):
24
+ # MageVAE: 16x downsample, no patch packing
25
+ return torch.randn(
26
+ num_samples,
27
+ channel,
28
+ math.ceil(height / 16),
29
+ math.ceil(width / 16),
30
+ device=device,
31
+ dtype=dtype,
32
+ generator=torch.Generator(device=device).manual_seed(seed),
33
+ )
34
+
35
+
36
+ def unpack(x: Tensor, height: int, width: int) -> Tensor:
37
+ # MageVAE: [B, H*W, C] -> [B, C, H, W], no patch unpacking
38
+ return rearrange(
39
+ x,
40
+ "b (h w) c -> b c h w",
41
+ h=math.ceil(height / 16),
42
+ w=math.ceil(width / 16),
43
+ )
44
+
45
+
46
+ PROMPT_TEMPLATE = {
47
+ "default": {"template": "{}", "start_idx": 0},
48
+ "default-nonthinking": {"template": "{}<think>\n\n</think>\n\n", "start_idx": 0},
49
+ "mage-flow": {
50
+ "template": (
51
+ "<|im_start|>system\nDescribe the image by detailing the color, shape, size, texture, quantity, "
52
+ "text, spatial relationships of the objects and background:"
53
+ "<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
54
+ ),
55
+ "start_idx": 34,
56
+ },
57
+ "mage-flow-edit": {
58
+ "template": (
59
+ "<|im_start|>system\nDescribe the key features of the input image (color, shape, size, texture,"
60
+ " objects, background), then explain how the user's text instruction should alter or modify the image. "
61
+ "Generate a new image that meets the user's requirements while maintaining consistency with the original "
62
+ "input where appropriate.<|im_end|>\n<|im_start|>user\n{}<|im_end|>\n<|im_start|>assistant\n"
63
+ ),
64
+ "start_idx": 64,
65
+ },
66
+ }
67
+
68
+
69
+ def print_load_warning(missing: list[str], unexpected: list[str]) -> None:
70
+ if len(missing) > 0 and len(unexpected) > 0:
71
+ logger.warning(f"Got {len(missing)} missing keys:\n\t" + "\n\t".join(missing))
72
+ logger.warning("\n" + "-" * 79 + "\n")
73
+ logger.warning(f"Got {len(unexpected)} unexpected keys:\n\t" + "\n\t".join(unexpected))
74
+ elif len(missing) > 0:
75
+ logger.warning(f"Got {len(missing)} missing keys:\n\t" + "\n\t".join(missing))
76
+ elif len(unexpected) > 0:
77
+ logger.warning(f"Got {len(unexpected)} unexpected keys:\n\t" + "\n\t".join(unexpected))
78
+
79
+
80
+ def correct_model_weight(state_dict):
81
+ result = {}
82
+ for key in state_dict.keys():
83
+ if "_orig_mod." in key:
84
+ result[key[10:]] = state_dict[key]
85
+ else:
86
+ result[key] = state_dict[key]
87
+ return result
88
+
89
+
90
+ def load_hf_style_weight(pretrain_path, device):
91
+ index_path = os.path.join(pretrain_path, "diffusion_pytorch_model.safetensors.index.json")
92
+
93
+ with open(index_path) as f:
94
+ index = json.load(f)
95
+
96
+ weight_map = index["weight_map"]
97
+
98
+ sd = {}
99
+ loaded_shards = set()
100
+
101
+ for shard_file in weight_map.values():
102
+ if shard_file in loaded_shards:
103
+ continue
104
+ shard_path = os.path.join(pretrain_path, shard_file)
105
+ shard_sd = load_file(shard_path, device="cpu")
106
+ sd.update(shard_sd)
107
+ loaded_shards.add(shard_file)
108
+
109
+ return sd
110
+
111
+
112
+ def load_model_weight(model, pretrain_path, device="cpu"):
113
+ if os.path.exists(pretrain_path):
114
+ logger.info(f"Loading checkpoint from {pretrain_path}")
115
+ try:
116
+ if pretrain_path.endswith("safetensors"):
117
+ sd = load_sft(pretrain_path, device="cpu")
118
+ elif os.path.exists(os.path.join(pretrain_path, "diffusion_pytorch_model.safetensors.index.json")):
119
+ sd = load_hf_style_weight(pretrain_path, device)
120
+ else:
121
+ sd = torch.load(pretrain_path, map_location="cpu")
122
+
123
+ sd = correct_model_weight(sd)
124
+ sd = optionally_expand_state_dict(model, sd)
125
+ missing, unexpected = model.load_state_dict(sd, strict=False, assign=True)
126
+ print_load_warning(missing, unexpected)
127
+ return True
128
+ except Exception as e:
129
+ logger.info(f"CANNOT Load {pretrain_path}, because {e}")
130
+ return False
131
+ return False
132
+
133
+
134
+ def load_model(dit_structure: dict, pretrain_path: str | None = None):
135
+ logger.info("Init DiT model")
136
+
137
+ # If name is a dict, we assume it contains the parameters directly
138
+ # We need to determine the model class based on some heuristic or just default to MageFlow/Flux
139
+ # For now, let's assume it's MageFlow if time_type is present, or check other fields
140
+ params = MageFlowParams(**dit_structure)
141
+ # Default to MageFlow for now as per user context, or we could add a 'model_type' field to the dict
142
+ # The user mentioned "model structure option", implying we are configuring the structure.
143
+ # Let's assume MageFlow for this refactor as the user was using qwen-image-tiny-wo-textemb
144
+ model = MageFlow(params)
145
+
146
+ # logger.info(f"Loading {name if isinstance(name, str) else 'custom config'} checkpoint from {pretrain_path}")
147
+ if pretrain_path is not None:
148
+ load_model_weight(model, pretrain_path, device="cpu")
149
+ # if isinstance(name, str) and configs[name].lora_path is not None:
150
+ # logger.info("Loading LoRA")
151
+ # lora_sd = load_sft(configs[name].lora_path, device="cpu")
152
+ # # loading the lora params + overwriting scale values in the norms
153
+ # missing, unexpected = model.load_state_dict(lora_sd, strict=False, assign=True)
154
+ # print_load_warning(missing, unexpected)
155
+ return model
156
+
157
+
158
+ def optionally_expand_state_dict(model: torch.nn.Module, state_dict: dict) -> dict:
159
+ """
160
+ Optionally expand the state dict to match the model's parameters shapes.
161
+ """
162
+ for name, param in model.named_parameters():
163
+ if name in state_dict:
164
+ if state_dict[name].shape != param.shape:
165
+ logger.info(
166
+ f"Expanding '{name}' with shape {state_dict[name].shape} to model parameter with shape "
167
+ f"{param.shape}."
168
+ )
169
+ # expand with zeros:
170
+ expanded_state_dict_weight = torch.zeros_like(param, device=state_dict[name].device)
171
+ slices = tuple(slice(0, dim) for dim in state_dict[name].shape)
172
+ expanded_state_dict_weight[slices] = state_dict[name]
173
+ state_dict[name] = expanded_state_dict_weight
174
+
175
+ return state_dict