Skip to content

Commit cae8fa1

Browse files
[bugfix]: propagate Qwen2.5-VL visual dtype (#1580)
1 parent 821e5a0 commit cae8fa1

2 files changed

Lines changed: 125 additions & 3 deletions

File tree

fastvideo/models/encoders/qwen2_5_vl_custom.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@
7575

7676
logger = logging.get_logger(__name__)
7777

78+
79+
def _resolve_torch_dtype(dtype, default: torch.dtype = torch.float32) -> torch.dtype:
80+
if isinstance(dtype, torch.dtype):
81+
return dtype
82+
if isinstance(dtype, str):
83+
normalized = dtype.strip().removeprefix("torch.").lower()
84+
return {
85+
"bfloat16": torch.bfloat16,
86+
"bf16": torch.bfloat16,
87+
"float16": torch.float16,
88+
"fp16": torch.float16,
89+
"half": torch.float16,
90+
"float32": torch.float32,
91+
"fp32": torch.float32,
92+
"float": torch.float32,
93+
}.get(normalized, default)
94+
return default
95+
96+
7897
class Qwen2_5_VLMLP(nn.Module):
7998
def __init__(self, config, bias: bool = False):
8099
super().__init__()
@@ -304,10 +323,13 @@ class Qwen2_5_VisionTransformerPretrainedModel(nn.Module):
304323
config_class = Qwen2_5_VLVisionConfig
305324
_no_split_modules = ["Qwen2_5_VLVisionBlock"]
306325

307-
def __init__(self, config) -> None:
326+
def __init__(self, config, parent_torch_dtype=None) -> None:
308327
super().__init__()
309328

310-
self.dtype = torch.bfloat16 if config.torch_dtype == "bfloat16" else torch.float32
329+
config_torch_dtype = getattr(config, "torch_dtype", None)
330+
self.dtype = _resolve_torch_dtype(
331+
config_torch_dtype if config_torch_dtype is not None else parent_torch_dtype
332+
)
311333

312334
self.spatial_merge_size = config.spatial_merge_size
313335
self.patch_size = config.patch_size
@@ -1458,7 +1480,10 @@ def __init__(self, config):
14581480
super().__init__()
14591481
config = _flatten_text_config(config)
14601482
self.config = config
1461-
self.visual = Qwen2_5_VisionTransformerPretrainedModel(config.vision_config)
1483+
self.visual = Qwen2_5_VisionTransformerPretrainedModel(
1484+
config.vision_config,
1485+
parent_torch_dtype=getattr(config, "torch_dtype", None),
1486+
)
14621487

14631488
self.model = Qwen2_5_VLModel(config)
14641489
self.vocab_size = config.vocab_size
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
from types import SimpleNamespace
4+
5+
import torch
6+
7+
from fastvideo.models.encoders.qwen2_5_vl_custom import (
8+
Qwen2_5_VisionTransformerPretrainedModel,
9+
Qwen2_5_VLForConditionalGenerationSimple,
10+
)
11+
12+
13+
def _vision_config(torch_dtype=None):
14+
return SimpleNamespace(
15+
torch_dtype=torch_dtype,
16+
spatial_merge_size=2,
17+
patch_size=14,
18+
fullatt_block_indexes=[],
19+
window_size=112,
20+
temporal_patch_size=2,
21+
in_channels=3,
22+
hidden_size=8,
23+
num_heads=2,
24+
depth=0,
25+
_attn_implementation="sdpa",
26+
out_hidden_size=8,
27+
)
28+
29+
30+
def _full_config(torch_dtype):
31+
return SimpleNamespace(
32+
vision_config=_vision_config(),
33+
torch_dtype=torch_dtype,
34+
vocab_size=16,
35+
hidden_size=8,
36+
num_hidden_layers=0,
37+
num_attention_heads=2,
38+
pad_token_id=0,
39+
_attn_implementation="sdpa",
40+
rms_norm_eps=1e-6,
41+
max_position_embeddings=32,
42+
rope_theta=1_000_000.0,
43+
rope_scaling=None,
44+
)
45+
46+
47+
def test_vision_dtype_uses_parent_bfloat16_string_when_vision_dtype_missing():
48+
model = Qwen2_5_VisionTransformerPretrainedModel(
49+
_vision_config(),
50+
parent_torch_dtype="bfloat16",
51+
)
52+
53+
assert model.dtype == torch.bfloat16
54+
55+
56+
def test_vision_dtype_accepts_parent_torch_dtype_object():
57+
model = Qwen2_5_VisionTransformerPretrainedModel(
58+
_vision_config(),
59+
parent_torch_dtype=torch.bfloat16,
60+
)
61+
62+
assert model.dtype == torch.bfloat16
63+
64+
65+
def test_vision_dtype_strips_parent_dtype_string_whitespace():
66+
model = Qwen2_5_VisionTransformerPretrainedModel(
67+
_vision_config(),
68+
parent_torch_dtype=" torch.bfloat16 ",
69+
)
70+
71+
assert model.dtype == torch.bfloat16
72+
73+
74+
def test_vision_dtype_prefers_explicit_vision_dtype_over_parent_dtype():
75+
model = Qwen2_5_VisionTransformerPretrainedModel(
76+
_vision_config(torch_dtype="float16"),
77+
parent_torch_dtype="bfloat16",
78+
)
79+
80+
assert model.dtype == torch.float16
81+
82+
83+
def test_vision_dtype_falls_back_to_float32_for_missing_or_unknown_dtype():
84+
missing = Qwen2_5_VisionTransformerPretrainedModel(_vision_config())
85+
unknown = Qwen2_5_VisionTransformerPretrainedModel(
86+
_vision_config(),
87+
parent_torch_dtype="not-a-real-dtype",
88+
)
89+
90+
assert missing.dtype == torch.float32
91+
assert unknown.dtype == torch.float32
92+
93+
94+
def test_conditional_generation_passes_parent_dtype_to_visual_tower():
95+
model = Qwen2_5_VLForConditionalGenerationSimple(_full_config("torch.bfloat16"))
96+
97+
assert model.visual.dtype == torch.bfloat16

0 commit comments

Comments
 (0)