Skip to content

Commit 8447987

Browse files
committed
Refuse rotary variants kernel injection cannot serve
@tohtana is right that this patch does not make DeepSeek-R1-Distill-Llama-8B work. It uses rope_type="llama3" with factor, low_freq_factor, high_freq_factor and original_max_position_embeddings, and only rope_theta reaches the kernel. The injected path has no place to put them. Its rotary embedding is built from a scalar base and nothing else: InferenceContext.get_rotary(rotary_dim, rope_theta) and DeepSpeedInferenceConfig carries rope_theta with no scaling fields at all. So after the crash fix that model would have started and run with unscaled positions, producing wrong output with no error. That is worse than the AttributeError this change exists to remove: the crash is at least visible. A config asking for a variant the kernel cannot implement is now refused, naming the type and pointing at running without kernel injection. Both spellings are covered, rope_parameters on 5.x and rope_scaling on 4.x. `default` and an absent type are unscaled and still resolve, so the configuration #8340 reported is fixed as before. Implementing llama3 scaled RoPE would mean propagating the parameters through DeepSpeedInferenceConfig and adding an inv_freq path and dispatch in the kernel. That is a feature rather than a crash fix and belongs in its own change. Signed-off-by: alanhuangyoo <alanhuangyoo@gmail.com>
1 parent 3dd2716 commit 8447987

2 files changed

Lines changed: 89 additions & 6 deletions

File tree

deepspeed/module_inject/containers/llama.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@
1919
maybe_get_lora,
2020
)
2121

22+
# The injected kernel builds its rotary embedding from a scalar base and nothing else
23+
# (`InferenceContext.get_rotary(rotary_dim, rope_theta)`), so a config asking for a scaled
24+
# variant cannot be honoured here. These are the spellings that mean "no scaling".
25+
_UNSCALED_ROPE_TYPES = (None, 'default')
26+
27+
28+
def _rope_type(config, rope_parameters):
29+
"""The rotary variant a config asks for, however the installed transformers spells it."""
30+
if rope_parameters:
31+
rope_type = rope_parameters.get('rope_type', rope_parameters.get('type'))
32+
if rope_type is not None:
33+
return rope_type
34+
scaling = getattr(config, 'rope_scaling', None)
35+
if isinstance(scaling, dict):
36+
return scaling.get('rope_type', scaling.get('type'))
37+
return None
38+
2239

2340
def _get_rope_theta(self_attn):
2441
"""Read rope_theta from whichever place the installed transformers keeps it.
@@ -27,14 +44,29 @@ def _get_rope_theta(self_attn):
2744
settings into the ``rope_parameters`` dict and dropped the attribute, so the
2845
older reads raise AttributeError against a stock LlamaConfig. Very old
2946
versions kept it on the attention module itself.
47+
48+
A scaled variant is refused rather than silently reduced to its base. The kernel
49+
implements a scalar theta only, so running one of these with just ``rope_theta``
50+
produces wrong positions with no error, which is worse than the AttributeError this
51+
function exists to remove.
3052
"""
3153
config = getattr(self_attn, 'config', None)
32-
if config is not None:
33-
if hasattr(config, 'rope_theta'):
34-
return config.rope_theta
35-
rope_parameters = getattr(config, 'rope_parameters', None)
36-
if rope_parameters is not None and 'rope_theta' in rope_parameters:
37-
return rope_parameters['rope_theta']
54+
if config is None:
55+
return self_attn.rope_theta
56+
57+
rope_parameters = getattr(config, 'rope_parameters', None)
58+
rope_type = _rope_type(config, rope_parameters if isinstance(rope_parameters, dict) else None)
59+
if rope_type not in _UNSCALED_ROPE_TYPES:
60+
raise ValueError(f"DeepSpeed kernel injection cannot serve rope_type={rope_type!r}. The injected "
61+
"attention kernel builds its rotary embedding from rope_theta alone, so the "
62+
"scaling parameters this configuration carries would be dropped and the model "
63+
"would run with unscaled positions. Run this model without kernel injection "
64+
"(replace_with_kernel_inject=False).")
65+
66+
if hasattr(config, 'rope_theta'):
67+
return config.rope_theta
68+
if isinstance(rope_parameters, dict) and 'rope_theta' in rope_parameters:
69+
return rope_parameters['rope_theta']
3870
return self_attn.rope_theta
3971

4072

tests/unit/module_inject/test_llama_rope_theta.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,54 @@ def test_the_two_spellings_do_not_disagree_on_a_real_config():
8989
def test_raises_when_nothing_carries_it():
9090
with pytest.raises(AttributeError):
9191
_get_rope_theta(SimpleNamespace(config=SimpleNamespace()))
92+
93+
94+
# --- scaled rotary variants ----------------------------------------------------
95+
#
96+
# The injected kernel builds its rotary embedding from a scalar base
97+
# (`InferenceContext.get_rotary(rotary_dim, rope_theta)`) and carries no scaling
98+
# parameters at all, so a config asking for one cannot be served here.
99+
100+
101+
@pytest.mark.parametrize("rope_type", ["llama3", "linear", "dynamic", "yarn", "longrope"])
102+
def test_a_scaled_rope_variant_is_refused(rope_type):
103+
"""Reading only rope_theta out of a scaled config is silently wrong.
104+
105+
DeepSeek-R1-Distill-Llama-8B (#8340) is the live case: `rope_type="llama3"` with
106+
`factor`, `low_freq_factor`, `high_freq_factor` and `original_max_position_embeddings`.
107+
Dropping those and keeping the base runs the model with unscaled positions and no error,
108+
which is worse than the AttributeError this helper exists to remove.
109+
"""
110+
config = SimpleNamespace(rope_parameters={
111+
"rope_type": rope_type,
112+
"rope_theta": 500000.0,
113+
"factor": 8.0,
114+
"low_freq_factor": 1.0,
115+
"high_freq_factor": 4.0,
116+
"original_max_position_embeddings": 8192,
117+
})
118+
119+
with pytest.raises(ValueError, match="cannot serve rope_type"):
120+
_get_rope_theta(SimpleNamespace(config=config))
121+
122+
123+
def test_a_scaled_variant_in_the_legacy_rope_scaling_spelling_is_refused():
124+
"""transformers < 5.0 carries the same request under `rope_scaling`."""
125+
config = SimpleNamespace(rope_theta=500000.0, rope_scaling={"rope_type": "llama3", "factor": 8.0})
126+
127+
with pytest.raises(ValueError, match="cannot serve rope_type"):
128+
_get_rope_theta(SimpleNamespace(config=config))
129+
130+
131+
def test_the_default_rope_type_is_not_refused():
132+
"""`rope_type: "default"` is what standardize_rope_params writes for plain RoPE."""
133+
config = SimpleNamespace(rope_parameters={"rope_theta": 500000.0, "rope_type": "default"})
134+
135+
assert _get_rope_theta(SimpleNamespace(config=config)) == 500000.0
136+
137+
138+
def test_a_real_llama_config_is_not_refused():
139+
"""The stock config the crash fix targets carries no scaling and must still resolve."""
140+
LlamaConfig = pytest.importorskip("transformers.models.llama.configuration_llama").LlamaConfig
141+
142+
assert _get_rope_theta(SimpleNamespace(config=LlamaConfig(rope_theta=500000.0))) == 500000.0

0 commit comments

Comments
 (0)