Skip to content

Auto-YaRN for Qwen3? Maybe even set it dynamically? #29

Description

@Downtown-Case

It appears Qwen3 is using YaRN for long contexts, like Qwen 2.5:

The maximum context length in pre-training for Qwen3 models is 32,768 tokens. It can be extended to 131,072 tokens with RoPE scaling techniques. We have validated the performance with YaRN.

https://github.com/QwenLM/Qwen3/blob/main/docs/source/inference/transformers.md#enabling-long-context

Editing the config file for the desired context is... less than ideal for users.

However, they mention this little nugget:

Transformers implements static YaRN, which means the scaling factor remains constant regardless of input length, potentially impacting performance on shorter texts. We advise adding the rope_scaling configuration only when processing long contexts is required. It is also recommended to modify the factor as needed. For example, if the typical context length for your application is 65,536 tokens, it would be better to set factor as 2.0

The default factor is 4.0, and currently the exllama code will stick to that if the 'default' long context configuration is present:

def _rope_params_yarn(self):
rs = self.rope_settings
assert rs.max_position_embeddings is not None, \
"YaRN scaling requires explicit max_position_embeddings"
base = rs.rope_theta
dim = int(rs.head_dim * rs.partial_rotary_factor)
factor = rs.rope_scaling.get("factor")
attn_factor = rs.rope_scaling.get("attention_factor", 0.1 * math.log(factor) + 1.0)
beta_fast = rs.rope_scaling.get("beta_fast", 32)
beta_slow = rs.rope_scaling.get("beta_slow", 1)
def find_correction_dim(num_rotations):
return (dim * math.log(rs.max_position_embeddings / (num_rotations * 2 * math.pi))) / (2 * math.log(base))
def find_correction_range(low_rot, high_rot):
_low = math.floor(find_correction_dim(low_rot))
_high = math.ceil(find_correction_dim(high_rot))
return max(_low, 0), min(_high, dim - 1)
def linear_ramp_factor(_min, _max, _dim):
if _min == _max:
_max += 0.001
linear_func = (torch.arange(_dim, dtype = torch.float32, device = self.device) - _min) / (_max - _min)
ramp_func = torch.clamp(linear_func, 0, 1)
return ramp_func
pos_freqs = base ** (torch.arange(0, dim, 2, device = self.device).float() / dim)
inv_freq_extrapolation = 1.0 / pos_freqs
inv_freq_interpolation = 1.0 / (factor * pos_freqs)
low, high = find_correction_range(beta_fast, beta_slow)
inv_freq_extrapolation_factor = 1 - linear_ramp_factor(low, high, dim // 2).float()
inv_freq = inv_freq_interpolation * (1 - inv_freq_extrapolation_factor)
inv_freq += inv_freq_extrapolation * inv_freq_extrapolation_factor
return inv_freq, attn_factor

But it appears a more optimal thing to do is ignore the rope_scaling factor, and statically set it with (context length of the loaded model)/original_max_position_embeddings, which would be 2.0 for 64K, 3.0 for 96K, and so on.

This is trivially easy to implement, but the question is if exllamav3 should set it automatically like that. I say it should, as most users aren't going to dig into how YaRN scaling works and match the scaling factor to their specified context length.


...But what would be even more optimal would be to dynamically set the scaling factor per request. For instance, if a 64K request was recieved, set it to 2.0. A 96K one, 3.0. So each request gets the optimal factor.

Even better would be to disable YaRN entirely for requests under 32K.

...Is this practical to do? I seem to remember it not being practical in exllamav2.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions