ajh-code commited on
Commit
4ed4b92
·
verified ·
1 Parent(s): 371a6d9

Add runtime/text_encoder_variants.py

Browse files
Files changed (1) hide show
  1. runtime/text_encoder_variants.py +199 -0
runtime/text_encoder_variants.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test-only loaders for alternate Qwen3-VL text-encoder artifacts.
2
+
3
+ The released mixed NVFP4/FP8 loader remains immutable. This module reuses its
4
+ validated tensor mapping and module implementation while accepting the
5
+ ComfyUI scaled-FP8 policy (252 language projections, all FP8 E4M3).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections import Counter
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import torch
15
+ from accelerate import init_empty_weights
16
+ from safetensors import safe_open
17
+ from transformers import AutoConfig, AutoProcessor, AutoTokenizer
18
+
19
+
20
+ EXPECTED_FP8_SHA256 = (
21
+ "54bd5144df0bbc25dd6ccadfcb826b521445a1b06ae5a42570bdd2974ca87094"
22
+ )
23
+ EXPECTED_FP8_PROJECTION_COUNT = 252
24
+
25
+
26
+ def _base_loader() -> Any:
27
+ import quant_text_encoder
28
+
29
+ return quant_text_encoder
30
+
31
+
32
+ def _install_scaled_fp8_linears(
33
+ hf_module: torch.nn.Module,
34
+ artifact_path: Path,
35
+ ) -> dict[str, Any]:
36
+ base = _base_loader()
37
+ format_counts: Counter[str] = Counter()
38
+ installed: list[str] = []
39
+ storage_bytes = 0
40
+ original_bf16_bytes = 0
41
+ full_precision_matrix_mult_counts: Counter[bool] = Counter()
42
+
43
+ with safe_open(str(artifact_path), framework="pt", device="cpu") as handle:
44
+ config_keys = sorted(
45
+ key for key in handle.keys() if key.endswith(".comfy_quant")
46
+ )
47
+ for config_key in config_keys:
48
+ layer_key = config_key.removesuffix(".comfy_quant")
49
+ quant_config = base._decode_quant_config(
50
+ handle.get_tensor(config_key)
51
+ )
52
+ quant_format = quant_config["format"]
53
+ if quant_format != "float8_e4m3fn":
54
+ raise RuntimeError(
55
+ f"{layer_key}: expected float8_e4m3fn, got {quant_format}"
56
+ )
57
+ full_precision_matrix_mult_counts[
58
+ bool(quant_config.get("full_precision_matrix_mult", False))
59
+ ] += 1
60
+ module_name = base._artifact_layer_to_hf_module(layer_key)
61
+ parent, leaf = base._resolve_parent(hf_module, module_name)
62
+ original = getattr(parent, leaf)
63
+ if not isinstance(original, torch.nn.Linear):
64
+ raise TypeError(
65
+ f"{module_name}: expected torch.nn.Linear, got "
66
+ f"{type(original).__name__}"
67
+ )
68
+ if original.bias is not None:
69
+ raise ValueError(f"{module_name}: quantized projection has a bias")
70
+
71
+ replacement = base.PublishedQuantLinear(
72
+ in_features=original.in_features,
73
+ out_features=original.out_features,
74
+ quant_format=quant_format,
75
+ qdata=handle.get_tensor(f"{layer_key}.weight"),
76
+ weight_scale=handle.get_tensor(f"{layer_key}.weight_scale"),
77
+ weight_scale_2=None,
78
+ )
79
+ setattr(parent, leaf, replacement)
80
+ format_counts[quant_format] += 1
81
+ installed.append(module_name)
82
+ original_bf16_bytes += (
83
+ original.in_features * original.out_features * 2
84
+ )
85
+ storage_bytes += replacement.qdata.nbytes
86
+ storage_bytes += replacement.weight_scale.nbytes
87
+
88
+ summary = {
89
+ "installed_module_count": len(installed),
90
+ "format_counts": dict(format_counts),
91
+ "full_precision_matrix_mult_counts": {
92
+ str(key).lower(): value
93
+ for key, value in sorted(
94
+ full_precision_matrix_mult_counts.items()
95
+ )
96
+ },
97
+ "original_projection_bf16_bytes": original_bf16_bytes,
98
+ "packed_projection_bytes": storage_bytes,
99
+ "projection_saving_bytes": original_bf16_bytes - storage_bytes,
100
+ "projection_saving_gib": (
101
+ (original_bf16_bytes - storage_bytes) / float(1 << 30)
102
+ ),
103
+ }
104
+ if (
105
+ summary["installed_module_count"] != EXPECTED_FP8_PROJECTION_COUNT
106
+ or summary["format_counts"]
107
+ != {"float8_e4m3fn": EXPECTED_FP8_PROJECTION_COUNT}
108
+ or summary["full_precision_matrix_mult_counts"] != {"false": 252}
109
+ ):
110
+ raise RuntimeError(f"unexpected scaled-FP8 policy: {summary}")
111
+ return summary
112
+
113
+
114
+ def load_scaled_fp8_text_encoder(
115
+ *,
116
+ text_encoder_dir: str | Path,
117
+ artifact_path: str | Path,
118
+ tokenizer_max_length: int,
119
+ dit_structure: dict[str, Any],
120
+ use_packed_text_infer: bool,
121
+ attn_type: str = "flash2",
122
+ ) -> tuple[torch.nn.Module, dict[str, Any]]:
123
+ """Build Mage's Qwen wrapper from the 252-projection scaled-FP8 file."""
124
+
125
+ base = _base_loader()
126
+ from mage_flow.models.modules.text_encoder import (
127
+ CustomQwen3VLForConditionalGeneration,
128
+ TextEncoder,
129
+ _resolve_hf_attn_impl,
130
+ )
131
+
132
+ text_encoder_dir = Path(text_encoder_dir).resolve()
133
+ artifact_path = Path(artifact_path).resolve()
134
+ actual_sha256 = base.sha256(artifact_path)
135
+ if actual_sha256 != EXPECTED_FP8_SHA256:
136
+ raise RuntimeError(
137
+ "scaled-FP8 text artifact SHA-256 mismatch: "
138
+ f"{actual_sha256}"
139
+ )
140
+
141
+ config = AutoConfig.from_pretrained(
142
+ str(text_encoder_dir),
143
+ local_files_only=True,
144
+ )
145
+ hf_attn_implementation = _resolve_hf_attn_impl(attn_type)
146
+ with init_empty_weights():
147
+ hf_module = CustomQwen3VLForConditionalGeneration._from_config(
148
+ config,
149
+ attn_implementation=hf_attn_implementation,
150
+ dtype=torch.bfloat16,
151
+ )
152
+
153
+ quant_summary = _install_scaled_fp8_linears(
154
+ hf_module,
155
+ artifact_path,
156
+ )
157
+ load_summary = base._load_nonquantized_weights(
158
+ hf_module,
159
+ artifact_path,
160
+ )
161
+
162
+ text_encoder = TextEncoder.__new__(TextEncoder)
163
+ torch.nn.Module.__init__(text_encoder)
164
+ text_encoder.model_name = str(text_encoder_dir)
165
+ text_encoder.tokenizer_max_length = int(tokenizer_max_length)
166
+ text_encoder.tokenizer = AutoTokenizer.from_pretrained(
167
+ str(text_encoder_dir),
168
+ local_files_only=True,
169
+ )
170
+ text_encoder.tokenizer.padding_side = "right"
171
+ text_encoder.processor = AutoProcessor.from_pretrained(
172
+ str(text_encoder_dir),
173
+ local_files_only=True,
174
+ )
175
+ text_encoder.hf_module = hf_module.eval().requires_grad_(False)
176
+ text_encoder.prompt_template_encode = ""
177
+ text_encoder.prompt_template_encode_start_idx = 0
178
+ text_encoder.dit_structure = dict(dit_structure)
179
+ text_encoder.use_packed_text_infer = bool(use_packed_text_infer)
180
+ text_encoder.eval().requires_grad_(False)
181
+
182
+ return (
183
+ text_encoder,
184
+ {
185
+ "artifact": str(artifact_path),
186
+ "artifact_sha256": actual_sha256,
187
+ "attention_backend": attn_type,
188
+ "hf_attention_implementation": hf_attn_implementation,
189
+ "quantized": quant_summary,
190
+ "nonquantized": load_summary,
191
+ },
192
+ )
193
+
194
+
195
+ __all__ = [
196
+ "EXPECTED_FP8_PROJECTION_COUNT",
197
+ "EXPECTED_FP8_SHA256",
198
+ "load_scaled_fp8_text_encoder",
199
+ ]