# SPDX-License-Identifier: Apache-2.0 """ Parity test for Z-Image Qwen3 text encoder support in FastVideo. This compares: 1) direct transformers AutoModel output, and 2) FastVideo's production ``TextEncoderLoader`` passthrough, and 3) FastVideo's shared native Qwen3 encoder (``Qwen3ForCausalLM``, added for Flux2 Klein and reused here — Z-Image-Turbo's ``Qwen3Model`` checkpoint routes to it via the model registry), using identical local checkpoint, tokenization, and inputs. Usage: pytest tests/local_tests/zimage/test_zimage_encoder_parity.py -v -s """ from __future__ import annotations from pathlib import Path from types import SimpleNamespace import gc import json import os import pytest import torch def _reclaim_vram() -> None: """Actually reclaim VRAM after the caller has ``del``'d its model refs. HF transformer models hold reference cycles, so ``del`` alone does not free them — a ``gc.collect()`` is required before the CUDA caching allocator will release the blocks. Without this the parity test keeps two full encoder copies resident and OOMs on a 44 GB GPU. """ gc.collect() if torch.cuda.is_available(): torch.cuda.empty_cache() def _gpu_mem_mb() -> float: if torch.cuda.is_available(): return torch.cuda.memory_allocated() / (1024**2) return 0.0 from torch.testing import assert_close from transformers import AutoModel, AutoModelForCausalLM, AutoTokenizer from safetensors.torch import safe_open from fastvideo.configs.models.encoders.qwen3 import Qwen3TextConfig from fastvideo.distributed.parallel_state import cleanup_dist_env_and_memory, maybe_init_distributed_environment_and_model_parallel from fastvideo.layers.quantization.absmax_fp8 import AbsMaxFP8MergedParameter from fastvideo.models.encoders.qwen3 import Qwen3ForCausalLM from fastvideo.models.loader.component_loader import TextEncoderLoader from fastvideo.models.loader.utils import set_default_torch_dtype from fastvideo.models.registry import ModelRegistry PARITY_SCOPE = "both" # Strict-load contract: the shared Qwen3 encoder is body-only (embed_tokens + # layers + norm, no lm_head). Z-Image-Turbo ships a full Qwen3 checkpoint, so # `lm_head.weight` is the only key that may go unmatched; anything else means a # real silent drop. Enforced here in the test since the shared encoder's loader # is intentionally lenient (it's used by multiple models). _ALLOWED_UNEXPECTED_KEYS = {"lm_head.weight"} REPO_ROOT = Path(__file__).resolve().parents[3] ZIMAGE_TEXT_ENCODER_DIR = REPO_ROOT / "official_weights" / "Z-Image" / "text_encoder" ZIMAGE_TOKENIZER_DIR = REPO_ROOT / "official_weights" / "Z-Image" / "tokenizer" @pytest.fixture(scope="module", autouse=True) def _init_dist_and_tp_groups(): os.environ.setdefault("MASTER_ADDR", "localhost") os.environ.setdefault("MASTER_PORT", "29531") os.environ.setdefault("RANK", "0") os.environ.setdefault("WORLD_SIZE", "1") os.environ.setdefault("LOCAL_RANK", "0") maybe_init_distributed_environment_and_model_parallel(1, 1) yield cleanup_dist_env_and_memory() def _load_json(path: Path) -> dict: with path.open("r", encoding="utf-8") as f: return json.load(f) def _iter_safetensors(path: Path): with safe_open(str(path), framework="pt", device="cpu") as f: for k in f.keys(): yield k, f.get_tensor(k) def _iter_pretrained_safetensors(model_dir: Path): single = model_dir / "model.safetensors" if single.exists(): yield from _iter_safetensors(single) return index = model_dir / "model.safetensors.index.json" if index.exists(): idx = _load_json(index) shard_names = sorted(set(idx["weight_map"].values())) for shard in shard_names: yield from _iter_safetensors(model_dir / shard) return raise FileNotFoundError( f"Missing safetensors checkpoint in {model_dir} (expected model.safetensors or model.safetensors.index.json)") def _pretrained_safetensor_keys(model_dir: Path) -> set[str]: return {name for name, _ in _iter_pretrained_safetensors(model_dir)} def _load_qwen3_config() -> Qwen3TextConfig: cfg_raw = _load_json(ZIMAGE_TEXT_ENCODER_DIR / "config.json") for key in ("_name_or_path", "transformers_version", "model_type", "torch_dtype"): cfg_raw.pop(key, None) config = Qwen3TextConfig() config.update_model_arch(cfg_raw) assert config.num_hidden_layers == 36 assert config.hidden_size == 2560 assert config.intermediate_size == 9728 assert config.num_attention_heads == 32 assert config.num_key_value_heads == 8 return config def _loader_args(cpu_offload: bool) -> SimpleNamespace: return SimpleNamespace( text_encoder_cpu_offload=cpu_offload, override_text_encoder_quant=None, override_text_encoder_safetensors=None, pin_cpu_memory=False, ) def _precision_name(dtype: torch.dtype) -> str: if dtype == torch.float32: return "fp32" if dtype == torch.bfloat16: return "bf16" raise ValueError(f"Unsupported parity dtype: {dtype}") def _tokenize_official_prompts(tokenizer, prompts: list[str]): formatted = [ tokenizer.apply_chat_template( [{ "role": "user", "content": prompt }], tokenize=False, add_generation_prompt=True, enable_thinking=True, ) for prompt in prompts ] return tokenizer( formatted, padding="max_length", truncation=True, max_length=512, return_tensors="pt", ) def _assert_native_load_surface( model, checkpoint_keys: set[str], loaded_params: set[str], ) -> None: param_names = {name for name, _ in model.named_parameters()} missing = param_names - loaded_params assert not missing, f"FastVideo parameters not loaded: {sorted(missing)}" normalized_keys = {name[len("model."):] if name.startswith("model.") else name for name in checkpoint_keys} mapped_keys: set[str] = set() for checkpoint_name in normalized_keys: for param_name, weight_name, _ in model.config.arch_config.stacked_params_mapping: if weight_name in checkpoint_name: mapped_keys.add(checkpoint_name.replace(weight_name, param_name)) break else: mapped_keys.add(checkpoint_name) unexpected = mapped_keys - param_names - { "rotary_emb.inv_freq", "rotary_emb.cos_cached", "rotary_emb.sin_cached", } assert unexpected <= _ALLOWED_UNEXPECTED_KEYS, ("Unexpected checkpoint keys not in allowlist: " f"{sorted(unexpected - _ALLOWED_UNEXPECTED_KEYS)}") def _fake_qwen3_weight_target(include_quant_scales: bool = False): stacked = Qwen3TextConfig().arch_config.stacked_params_mapping params = { "layers.0.self_attn.qkv_proj.weight": torch.nn.Parameter(torch.empty(1)), "layers.0.mlp.gate_up_proj.weight": torch.nn.Parameter(torch.empty(1)), } if include_quant_scales: qkv_scale = AbsMaxFP8MergedParameter(torch.zeros(3), requires_grad=False) qkv_scale.output_partition_sizes = [1, 1, 1] gate_up_scale = AbsMaxFP8MergedParameter(torch.zeros(2), requires_grad=False) gate_up_scale.output_partition_sizes = [1, 1] params.update({ "layers.0.self_attn.qkv_proj.scale_weight": qkv_scale, "layers.0.mlp.gate_up_proj.scale_weight": gate_up_scale, }) for param in params.values(): if not hasattr(param, "weight_loader"): param.weight_loader = (lambda target, loaded, *args: target.data.copy_(loaded)) model = SimpleNamespace( config=SimpleNamespace(arch_config=SimpleNamespace(stacked_params_mapping=stacked)), named_parameters=lambda: params.items(), ) return model, params, stacked # The provisional bf16 thresholds below come from the historical unpinned # 35-layer run. Per add-model-02-parity's calibration block, bf16 max-based # asserts are meaningless for deep encoders — fused-vs-unfused linear order # alone produces a long tail, so the test uses mean + median instead. The # pinned snapshot has 36 blocks and must be rerun before these thresholds are # treated as current evidence. # # Historical empirical numbers (unrecorded snapshot, 35 layers, CUDA): # last_hidden_state (post-norm): mean=0.0152, median=0.0117 # hidden_states[-2] (pre-norm): mean=0.0754, median=0.0625 # Thresholds below add ~1.6x headroom over observed. _BF16_MEAN_DRIFT_POST_NORM = 0.025 _BF16_MEAN_DRIFT_PRE_NORM = 0.120 _BF16_MEDIAN_DRIFT_POST_NORM = 0.020 _BF16_MEDIAN_DRIFT_PRE_NORM = 0.100 _FP32_ATOL = 2e-4 _FP32_RTOL = 1e-4 def _print_diag(label: str, ref: torch.Tensor, fv: torch.Tensor) -> torch.Tensor: diff = (ref.float() - fv.float()).abs() flat = diff.flatten() p99 = flat.kthvalue(max(1, int(0.99 * flat.numel()))).values print( f"[{label}] max_diff={diff.max():.4f} mean_diff={diff.mean():.4f} " f"median_diff={diff.median():.4f} p99_diff={p99:.4f}", flush=True, ) return diff @pytest.mark.parametrize( ("cpu_offload", "target_device", "expected_device"), [ (True, torch.device("cuda"), torch.device("cpu")), (False, torch.device("meta"), torch.device("meta")), ], ) def test_qwen3_production_loader_avoids_device_context_and_honors_placement( monkeypatch: pytest.MonkeyPatch, cpu_offload: bool, target_device: torch.device, expected_device: torch.device, ): """HF loading stays ambient while final placement honors target/offload.""" import fastvideo.platforms as platforms placements: list[torch.device] = [] auto_model_calls: list[dict] = [] class FakeHFModel: def eval(self): return self def to(self, device: torch.device): placements.append(torch.device(device)) return self fake_model = FakeHFModel() ambient_device = torch.empty(0).device default_devices: list[torch.device] = [] def fake_auto_model_from_pretrained(*args, **kwargs): auto_model_calls.append(kwargs) default_devices.append(torch.empty(0).device) return fake_model def fail_causal_lm_from_pretrained(*args, **kwargs): raise AssertionError("Qwen text encoding must not load AutoModelForCausalLM") monkeypatch.setattr( AutoModel, "from_pretrained", staticmethod(fake_auto_model_from_pretrained), ) monkeypatch.setattr( AutoModelForCausalLM, "from_pretrained", staticmethod(fail_causal_lm_from_pretrained), ) monkeypatch.setattr( platforms, "_current_platform", SimpleNamespace( is_mps=lambda: False, verify_model_arch=lambda _arch: None, ), ) monkeypatch.setattr(torch.cuda, "is_available", lambda: True) monkeypatch.setattr( "fastvideo.distributed.get_local_torch_device", lambda: torch.device("cuda"), ) config = Qwen3TextConfig() # The pinned checkpoint names the causal wrapper even though the official # Z-Image loader intentionally asks Transformers for the body-only model. config.arch_config.architectures = ["Qwen3ForCausalLM"] loaded = TextEncoderLoader().load_model( "unused-by-mock", config, target_device, _loader_args(cpu_offload=cpu_offload), dtype="fp32", use_text_encoder_override=True, ) assert loaded is fake_model assert auto_model_calls == [{ "local_files_only": True, "dtype": torch.float32, "low_cpu_mem_usage": True, }] assert default_devices == [ambient_device] assert placements == [expected_device] assert loaded._fastvideo_input_device == expected_device def test_set_default_torch_dtype_restores_after_exception(): original = torch.get_default_dtype() with pytest.raises(RuntimeError, match="expected failure"): with set_default_torch_dtype(torch.bfloat16): raise RuntimeError("expected failure") assert torch.get_default_dtype() == original @pytest.mark.parametrize("checkpoint_prefix", ["", "model."]) def test_qwen3_native_load_accepts_already_fused_weights_and_scales(checkpoint_prefix: str, ): """Direct FastVideo/state-dict keys satisfy each fused target atomically.""" fake_model, params, _ = _fake_qwen3_weight_target(include_quant_scales=True) expected = {name: torch.arange(1, param.numel() + 1, dtype=param.dtype) for name, param in params.items()} weights = [(f"{checkpoint_prefix}{name}", expected[name]) for name in params] loaded = Qwen3ForCausalLM.load_weights(fake_model, weights) assert loaded == set(params) for name, param in params.items(): assert torch.equal(param.data, expected[name]) _assert_native_load_surface( fake_model, {name for name, _ in weights}, loaded, ) def test_qwen3_native_load_rejects_incompatible_fused_quant_scale_shape(): fake_model, params, _ = _fake_qwen3_weight_target(include_quant_scales=True) weights = [(name, torch.ones_like(param)) for name, param in params.items() if name != "layers.0.self_attn.qkv_proj.scale_weight"] weights.append(("layers.0.self_attn.qkv_proj.scale_weight", torch.ones(1))) with pytest.raises(AssertionError, match="Attempted to load weight"): Qwen3ForCausalLM.load_weights(fake_model, weights) def test_qwen3_native_load_uses_real_shard_loader_for_split_quant_scales(): fake_model, params, stacked = _fake_qwen3_weight_target(include_quant_scales=True) weights = [(name, torch.ones_like(param)) for name, param in params.items() if name.endswith(".weight")] weights.extend((f"model.layers.0.self_attn{weight_name}.scale_weight", torch.tensor(float(index + 1))) for index, (_, weight_name, _) in enumerate(stacked[:3])) weights.extend((f"model.layers.0.mlp{weight_name}.scale_weight", torch.tensor(float(index + 4))) for index, (_, weight_name, _) in enumerate(stacked[3:])) loaded = Qwen3ForCausalLM.load_weights(fake_model, weights) assert loaded == set(params) assert torch.equal( params["layers.0.self_attn.qkv_proj.scale_weight"].data, torch.tensor([1.0, 2.0, 3.0]), ) assert torch.equal( params["layers.0.mlp.gate_up_proj.scale_weight"].data, torch.tensor([4.0, 5.0]), ) @pytest.mark.parametrize( ("missing_weight_name", "missing_shard_id"), [ (".q_proj", "q"), (".k_proj", "k"), (".v_proj", "v"), (".gate_proj", 0), (".up_proj", 1), ], ) def test_qwen3_native_load_rejects_missing_fused_weight_shard( missing_weight_name: str, missing_shard_id: str | int, ): fake_model, _, stacked = _fake_qwen3_weight_target() weights = [(f"model.layers.0.self_attn{weight_name}.weight", torch.empty(1)) for _, weight_name, _ in stacked[:3] if weight_name != missing_weight_name] weights.extend((f"model.layers.0.mlp{weight_name}.weight", torch.empty(1)) for _, weight_name, _ in stacked[3:] if weight_name != missing_weight_name) with pytest.raises(ValueError, match="Missing required stacked checkpoint shards") as exc_info: Qwen3ForCausalLM.load_weights(fake_model, weights) destination = ("layers.0.self_attn.qkv_proj.weight" if isinstance(missing_shard_id, str) else "layers.0.mlp.gate_up_proj.weight") assert f"{destination}[{missing_shard_id}]" in str(exc_info.value) @pytest.mark.parametrize( ("missing_weight_name", "missing_shard_id"), [ (".q_proj", "q"), (".k_proj", "k"), (".v_proj", "v"), (".gate_proj", 0), (".up_proj", 1), ], ) def test_qwen3_native_load_rejects_missing_fused_quant_scale_shard( missing_weight_name: str, missing_shard_id: str | int, ): fake_model, params, stacked = _fake_qwen3_weight_target(include_quant_scales=True) weights = [(name, torch.empty(1)) for name in params if name.endswith(".weight")] weights.extend((f"model.layers.0.self_attn{weight_name}.scale_weight", torch.empty(1)) for _, weight_name, _ in stacked[:3] if weight_name != missing_weight_name) weights.extend((f"model.layers.0.mlp{weight_name}.scale_weight", torch.empty(1)) for _, weight_name, _ in stacked[3:] if weight_name != missing_weight_name) with pytest.raises(ValueError, match="Missing required stacked checkpoint shards") as exc_info: Qwen3ForCausalLM.load_weights(fake_model, weights) destination = ("layers.0.self_attn.qkv_proj.scale_weight" if isinstance(missing_shard_id, str) else "layers.0.mlp.gate_up_proj.scale_weight") assert f"{destination}[{missing_shard_id}]" in str(exc_info.value) @pytest.mark.skipif(not ZIMAGE_TEXT_ENCODER_DIR.exists(), reason="Z-Image text encoder checkpoint required") @pytest.mark.parametrize( "dtype", [ pytest.param(torch.float32, id="fp32"), pytest.param( torch.bfloat16, id="bf16", marks=pytest.mark.skipif( not (torch.cuda.is_available() and torch.cuda.is_bf16_supported()), reason="bf16 parity requires a bf16-capable CUDA device", ), ), ], ) def test_zimage_qwen3_encoder_parity_forward(dtype: torch.dtype): if not ZIMAGE_TOKENIZER_DIR.exists(): pytest.skip(f"Z-Image tokenizer dir not found: {ZIMAGE_TOKENIZER_DIR}") device = torch.device("cuda" if torch.cuda.is_available() else "cpu") torch.manual_seed(11) tokenizer = AutoTokenizer.from_pretrained(str(ZIMAGE_TOKENIZER_DIR), local_files_only=True) prompts = [ "A cinematic shot of a rainy neon street at night.", "A watercolor illustration of a fox in autumn leaves.", ] toks = _tokenize_official_prompts(tokenizer, prompts) input_ids = toks["input_ids"].to(device) attention_mask = toks["attention_mask"].to(device) config = _load_qwen3_config() ref = AutoModel.from_pretrained( str(ZIMAGE_TEXT_ENCODER_DIR), local_files_only=True, trust_remote_code=True, dtype=dtype, low_cpu_mem_usage=True, ).eval().to(device=device) with torch.no_grad(): ref_out = ref( input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True, return_dict=True, ) ref_last = ref_out.last_hidden_state.detach().float().cpu() assert ref_out.hidden_states is not None ref_hs_m2 = ref_out.hidden_states[-2].detach().float().cpu() print(f"[mem] after HF ref forward: {_gpu_mem_mb():.0f} MiB", flush=True) # Keep only CPU outputs so each full encoder is resident one at a time. del ref, ref_out _reclaim_vram() print(f"[mem] after freeing HF ref: {_gpu_mem_mb():.0f} MiB", flush=True) production = TextEncoderLoader().load_model( str(ZIMAGE_TEXT_ENCODER_DIR), config, device, _loader_args(cpu_offload=False), dtype=_precision_name(dtype), use_text_encoder_override=True, ) assert production.__class__.__name__ == "Qwen3Model" assert not hasattr(production, "lm_head") assert production._fastvideo_input_device == device with torch.no_grad(): production_out = production( input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True, return_dict=True, ) production_last = production_out.last_hidden_state.detach().float().cpu() assert production_out.hidden_states is not None production_hs_m2 = production_out.hidden_states[-2].detach().float().cpu() # Production loading must preserve the independent Transformers oracle # exactly; native-kernel drift is assessed separately below. assert_close(ref_last, production_last, atol=0.0, rtol=0.0) assert_close(ref_hs_m2, production_hs_m2, atol=0.0, rtol=0.0) del production, production_out, production_last, production_hs_m2 _reclaim_vram() fv_cls, _ = ModelRegistry.resolve_model_cls("Qwen3Model") fv = fv_cls(config).eval() loaded = fv.load_weights(_iter_pretrained_safetensors(ZIMAGE_TEXT_ENCODER_DIR)) assert loaded, "No Qwen3 weights were loaded into FastVideo model" fv = fv.to(device=device, dtype=dtype) print(f"[mem] after FastVideo load: {_gpu_mem_mb():.0f} MiB", flush=True) _assert_native_load_surface( fv, _pretrained_safetensor_keys(ZIMAGE_TEXT_ENCODER_DIR), loaded, ) with torch.no_grad(): fv_out = fv( input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True, ) fv_last = fv_out.last_hidden_state.detach().float().cpu() assert fv_out.hidden_states is not None fv_hs_m2 = fv_out.hidden_states[-2].detach().float().cpu() # Z-Image uses only valid token embeddings selected by attention mask. # Compare parity on that exact path to avoid padded-token artifacts. mask_cpu = attention_mask.detach().bool().cpu() is_bf16 = dtype == torch.bfloat16 for i in range(mask_cpu.shape[0]): valid = mask_cpu[i] ref_last_v = ref_last[i][valid] fv_last_v = fv_last[i][valid] ref_hs_m2_v = ref_hs_m2[i][valid] fv_hs_m2_v = fv_hs_m2[i][valid] last_diff = _print_diag(f"batch{i} last_hidden_state {dtype}", ref_last_v, fv_last_v) hs_m2_diff = _print_diag(f"batch{i} hidden_states[-2] {dtype}", ref_hs_m2_v, fv_hs_m2_v) if is_bf16: # Distribution checks instead of element-wise assert_close: cross- # kernel bf16 produces a long max tail (fused vs unfused linears), # but a real op bug pushes mean *and* median high. Mean catches # systematic drift; median catches "wrong weights / swapped # layers" where most elements diverge. Pre-norm tensors get a # looser bound because the final RMSNorm compresses drift ~5x. assert last_diff.mean().item() < _BF16_MEAN_DRIFT_POST_NORM, ( f"batch{i} last_hidden_state bf16 mean drift " f"{last_diff.mean():.4f} >= {_BF16_MEAN_DRIFT_POST_NORM}") assert last_diff.median().item() < _BF16_MEDIAN_DRIFT_POST_NORM, ( f"batch{i} last_hidden_state bf16 median drift " f"{last_diff.median():.4f} >= {_BF16_MEDIAN_DRIFT_POST_NORM}") assert hs_m2_diff.mean().item() < _BF16_MEAN_DRIFT_PRE_NORM, ( f"batch{i} hidden_states[-2] bf16 mean drift " f"{hs_m2_diff.mean():.4f} >= {_BF16_MEAN_DRIFT_PRE_NORM}") assert hs_m2_diff.median().item() < _BF16_MEDIAN_DRIFT_PRE_NORM, ( f"batch{i} hidden_states[-2] bf16 median drift " f"{hs_m2_diff.median():.4f} >= {_BF16_MEDIAN_DRIFT_PRE_NORM}") else: assert_close(ref_last_v, fv_last_v, atol=_FP32_ATOL, rtol=_FP32_RTOL) assert_close(ref_hs_m2_v, fv_hs_m2_v, atol=_FP32_ATOL, rtol=_FP32_RTOL) @pytest.mark.skipif(not ZIMAGE_TEXT_ENCODER_DIR.exists(), reason="Z-Image text encoder checkpoint required") @pytest.mark.skipif( not (torch.cuda.is_available() and torch.cuda.is_bf16_supported()), reason="bf16 per-layer diagnostic requires a bf16-capable CUDA device", ) def test_zimage_qwen3_encoder_per_layer_bf16_diagnostic(): """Diagnostic-only: prints per-layer FastVideo-vs-HF drift for the bf16 forward to distinguish monotonic accumulation (bf16 tail) from a single-layer spike (real op mismatch). This test always passes; review the captured stdout to interpret. Expected pattern for healthy bf16 accumulation: max/mean/median grow smoothly with layer depth. Suspect pattern: one layer's diff is >>2x the previous, suggesting a real bug at that block. """ if not ZIMAGE_TOKENIZER_DIR.exists(): pytest.skip(f"Z-Image tokenizer dir not found: {ZIMAGE_TOKENIZER_DIR}") device = torch.device("cuda") dtype = torch.bfloat16 torch.manual_seed(11) tokenizer = AutoTokenizer.from_pretrained(str(ZIMAGE_TOKENIZER_DIR), local_files_only=True) toks = _tokenize_official_prompts( tokenizer, ["A cinematic shot of a rainy neon street at night."], ) input_ids = toks["input_ids"].to(device) attention_mask = toks["attention_mask"].to(device) ref = AutoModel.from_pretrained( str(ZIMAGE_TEXT_ENCODER_DIR), local_files_only=True, trust_remote_code=True, dtype=dtype, low_cpu_mem_usage=True, ).eval().to(device=device) with torch.no_grad(): ref_out = ref( input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True, return_dict=True, ) # Cache the reference hidden states on CPU, then free the HF model so # only one encoder is resident when FastVideo runs (avoids OOM). ref_hidden_cpu = [h.detach().float().cpu() for h in ref_out.hidden_states] del ref, ref_out _reclaim_vram() fv_cls, _ = ModelRegistry.resolve_model_cls("Qwen3Model") cfg_raw = _load_json(ZIMAGE_TEXT_ENCODER_DIR / "config.json") for k in ("_name_or_path", "transformers_version", "model_type", "torch_dtype"): cfg_raw.pop(k, None) cfg = Qwen3TextConfig() cfg.update_model_arch(cfg_raw) fv = fv_cls(cfg).eval() fv.load_weights(_iter_pretrained_safetensors(ZIMAGE_TEXT_ENCODER_DIR)) fv = fv.to(device=device, dtype=dtype) with torch.no_grad(): fv_out = fv( input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True, ) fv_hidden_cpu = [h.detach().float().cpu() for h in fv_out.hidden_states] assert fv_out.hidden_states is not None assert len(ref_hidden_cpu) == 37, (f"expected embeddings plus 36 block outputs, got {len(ref_hidden_cpu)} entries") assert len(ref_hidden_cpu) == len(fv_hidden_cpu), ( f"len mismatch: HF={len(ref_hidden_cpu)} FV={len(fv_hidden_cpu)}") mask_cpu = attention_mask.detach().bool().cpu()[0] print( f"\n[per-layer bf16 diagnostic] num_hidden_states={len(ref_hidden_cpu)} " f"valid_tokens={mask_cpu.sum().item()}/{mask_cpu.numel()}", flush=True, ) print( f"{'idx':>3} {'kind':<10} {'max':>8} {'mean':>8} {'median':>8} {'p99':>8}", flush=True, ) for idx, (ref_h, fv_h) in enumerate(zip(ref_hidden_cpu, fv_hidden_cpu)): ref_v = ref_h[0][mask_cpu] fv_v = fv_h[0][mask_cpu] diff = (ref_v - fv_v).abs() flat = diff.flatten() p99 = torch.quantile(flat, 0.99).item() if idx == 0: kind = "embedding" elif idx == len(ref_hidden_cpu) - 1: kind = "post-norm" else: kind = f"layer{idx - 1}-out" print( f"{idx:>3} {kind:<10} " f"{diff.max().item():>8.4f} {diff.mean().item():>8.4f} " f"{diff.median().item():>8.4f} {p99:>8.4f}", flush=True, )