mirror of https://github.com/razor-ai/soup.git
192 lines
5.8 KiB
Python
192 lines
5.8 KiB
Python
"""Long-context fine-tuning utilities — 128k+ token support.
|
|
|
|
Configures RoPE (Rotary Position Embedding) scaling to extend model context
|
|
windows beyond their pre-training length. Supports multiple scaling strategies:
|
|
|
|
- linear: Simple linear interpolation (PI) — good baseline
|
|
- dynamic: NTK-aware Dynamic scaling — better for large extensions
|
|
- yarn: YaRN (Yet another RoPE extensioN) — best quality for 4-8x extension
|
|
- longrope: LongRoPE — progressive extension with search-based factors
|
|
|
|
Also handles gradient checkpointing configuration for memory efficiency
|
|
when training on very long sequences.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
# Supported RoPE scaling methods
|
|
ROPE_SCALING_TYPES = ("linear", "dynamic", "yarn", "longrope")
|
|
|
|
# Default context lengths for known model families
|
|
MODEL_DEFAULT_CONTEXT: dict[str, int] = {
|
|
"llama-3": 8192,
|
|
"llama-2": 4096,
|
|
"mistral": 32768,
|
|
"mixtral": 32768,
|
|
"qwen2": 32768,
|
|
"qwen3": 32768,
|
|
"phi-3": 4096,
|
|
"phi-4": 16384,
|
|
"gemma": 8192,
|
|
"gemma-2": 8192,
|
|
"deepseek": 4096,
|
|
"codellama": 16384,
|
|
}
|
|
|
|
|
|
def get_model_default_context(model_name: str) -> int:
|
|
"""Estimate the default context length for a model based on its name.
|
|
|
|
Args:
|
|
model_name: HuggingFace model name/path.
|
|
|
|
Returns:
|
|
Estimated default context length in tokens.
|
|
"""
|
|
model_lower = model_name.lower()
|
|
for family, ctx_len in MODEL_DEFAULT_CONTEXT.items():
|
|
if family in model_lower:
|
|
return ctx_len
|
|
# Conservative default for unknown models
|
|
return 4096
|
|
|
|
|
|
def get_rope_scaling_config(
|
|
scaling_type: str,
|
|
target_length: float,
|
|
original_length: int,
|
|
) -> dict:
|
|
"""Build RoPE scaling configuration for extending context.
|
|
|
|
Args:
|
|
scaling_type: One of 'linear', 'dynamic', 'yarn', 'longrope'.
|
|
target_length: Desired context length (e.g., 131072 for 128k),
|
|
or a scaling factor (e.g., 4.0 for 4x extension) when the value
|
|
is less than original_length and greater than 1.0.
|
|
original_length: Model's pre-trained context length.
|
|
|
|
Returns:
|
|
Dict to pass as `rope_scaling` in model config.
|
|
|
|
Raises:
|
|
ValueError: If scaling_type is not supported.
|
|
"""
|
|
if scaling_type not in ROPE_SCALING_TYPES:
|
|
raise ValueError(
|
|
f"Unknown RoPE scaling type: {scaling_type}. "
|
|
f"Options: {', '.join(ROPE_SCALING_TYPES)}"
|
|
)
|
|
|
|
# If target_length looks like a scaling factor (small number > 1.0 but < 64),
|
|
# treat it as a multiplier rather than an absolute token count.
|
|
# Values >= 64 are always treated as token counts (64 is the schema minimum).
|
|
if target_length < 64 and target_length > 1.0:
|
|
factor = float(target_length)
|
|
else:
|
|
factor = target_length / original_length
|
|
|
|
if factor <= 1.0:
|
|
# No scaling needed — target is within original context
|
|
return {}
|
|
|
|
if scaling_type == "linear":
|
|
return {
|
|
"type": "linear",
|
|
"factor": float(factor),
|
|
}
|
|
elif scaling_type == "dynamic":
|
|
return {
|
|
"type": "dynamic",
|
|
"factor": float(factor),
|
|
}
|
|
elif scaling_type == "yarn":
|
|
return {
|
|
"type": "yarn",
|
|
"factor": float(factor),
|
|
"original_max_position_embeddings": original_length,
|
|
}
|
|
else: # longrope — guaranteed by Literal constraint in schema
|
|
return {
|
|
"type": "longrope",
|
|
"factor": float(factor),
|
|
"original_max_position_embeddings": original_length,
|
|
}
|
|
|
|
|
|
def apply_long_context_config(
|
|
model_config,
|
|
target_length: int,
|
|
rope_scaling_type: str = "dynamic",
|
|
model_name: str = "",
|
|
) -> dict | None:
|
|
"""Apply long-context configuration to a model config object.
|
|
|
|
Modifies the model config to extend the context window using RoPE scaling
|
|
and returns the scaling config that was applied.
|
|
|
|
Args:
|
|
model_config: The model's config object (from model.config).
|
|
target_length: Desired max sequence length.
|
|
rope_scaling_type: RoPE scaling strategy.
|
|
model_name: Model name (for estimating original context length).
|
|
|
|
Returns:
|
|
The rope_scaling dict that was applied, or None if no scaling needed.
|
|
"""
|
|
# Determine original context length
|
|
original_length = getattr(
|
|
model_config,
|
|
"max_position_embeddings",
|
|
get_model_default_context(model_name),
|
|
)
|
|
|
|
if target_length <= original_length:
|
|
return None
|
|
|
|
rope_config = get_rope_scaling_config(
|
|
scaling_type=rope_scaling_type,
|
|
target_length=target_length,
|
|
original_length=original_length,
|
|
)
|
|
|
|
if not rope_config:
|
|
return None
|
|
|
|
# Apply to model config
|
|
model_config.rope_scaling = rope_config
|
|
model_config.max_position_embeddings = target_length
|
|
|
|
return rope_config
|
|
|
|
|
|
def validate_long_context_config(
|
|
max_length: int,
|
|
rope_scaling_type: str | None,
|
|
use_gradient_checkpointing: bool,
|
|
) -> list[str]:
|
|
"""Validate long-context configuration.
|
|
|
|
Args:
|
|
max_length: Target sequence length.
|
|
rope_scaling_type: RoPE scaling type, or None if not specified.
|
|
use_gradient_checkpointing: Whether gradient checkpointing is enabled.
|
|
|
|
Returns:
|
|
List of warning/error messages.
|
|
"""
|
|
errors: list[str] = []
|
|
|
|
if rope_scaling_type and rope_scaling_type not in ROPE_SCALING_TYPES:
|
|
errors.append(
|
|
f"Unknown RoPE scaling type: {rope_scaling_type}. "
|
|
f"Options: {', '.join(ROPE_SCALING_TYPES)}"
|
|
)
|
|
|
|
if max_length >= 65536 and not use_gradient_checkpointing:
|
|
errors.append(
|
|
f"Training with max_length={max_length} without gradient checkpointing "
|
|
"will likely cause OOM. Set gradient_checkpointing: true in config."
|
|
)
|
|
|
|
return errors
|