Spaces:
Running
Running
zhan1206 commited on
Commit ·
93d1b7e
1
Parent(s): bf5ce33
fix: N9 position_ids signatures + M2 THINK_END token split
Browse files- models/fusion_mini.py +8 -1
- models/sbla_attention.py +1 -0
- models/tokenizer.py +33 -3
models/fusion_mini.py
CHANGED
|
@@ -214,6 +214,7 @@ class FusionMiniLayer(nn.Module):
|
|
| 214 |
attention_mask: Optional[torch.Tensor] = None,
|
| 215 |
past_key_value: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 216 |
use_cache: bool = False,
|
|
|
|
| 217 |
) -> Tuple[torch.Tensor, Optional[Tuple[torch.Tensor, torch.Tensor]]]:
|
| 218 |
# Pre-norm + SBLA Attention + residual
|
| 219 |
residual = hidden_states
|
|
@@ -234,6 +235,8 @@ class FusionMiniLayer(nn.Module):
|
|
| 234 |
attn_output, present_key_value = self.sbla_attention.forward_with_qkv(
|
| 235 |
Q, K, V, attention_mask,
|
| 236 |
past_key_value=past_key_value, use_cache=use_cache,
|
|
|
|
|
|
|
| 237 |
)
|
| 238 |
|
| 239 |
hidden_states = residual + self.dropout(attn_output)
|
|
@@ -338,13 +341,17 @@ class FusionMini(PreTrainedModel):
|
|
| 338 |
if use_cache:
|
| 339 |
present_key_values = present_key_values + (cache,)
|
| 340 |
|
| 341 |
-
#
|
| 342 |
hidden_states = self.ln_f(hidden_states)
|
| 343 |
|
| 344 |
# 5. LM Head
|
| 345 |
logits = self.lm_head(hidden_states)
|
| 346 |
|
| 347 |
# 6. Compute loss (if labels provided)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
loss = None
|
| 349 |
if labels is not None:
|
| 350 |
# Shift: predict next token
|
|
|
|
| 214 |
attention_mask: Optional[torch.Tensor] = None,
|
| 215 |
past_key_value: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 216 |
use_cache: bool = False,
|
| 217 |
+
position_ids: Optional[torch.Tensor] = None, # [N9 FIX]
|
| 218 |
) -> Tuple[torch.Tensor, Optional[Tuple[torch.Tensor, torch.Tensor]]]:
|
| 219 |
# Pre-norm + SBLA Attention + residual
|
| 220 |
residual = hidden_states
|
|
|
|
| 235 |
attn_output, present_key_value = self.sbla_attention.forward_with_qkv(
|
| 236 |
Q, K, V, attention_mask,
|
| 237 |
past_key_value=past_key_value, use_cache=use_cache,
|
| 238 |
+
# N9 FIX: position_ids accepted for API completeness but not used here
|
| 239 |
+
# (Q/K already have position encoding applied externally)
|
| 240 |
)
|
| 241 |
|
| 242 |
hidden_states = residual + self.dropout(attn_output)
|
|
|
|
| 341 |
if use_cache:
|
| 342 |
present_key_values = present_key_values + (cache,)
|
| 343 |
|
| 344 |
+
# Final Layer Norm
|
| 345 |
hidden_states = self.ln_f(hidden_states)
|
| 346 |
|
| 347 |
# 5. LM Head
|
| 348 |
logits = self.lm_head(hidden_states)
|
| 349 |
|
| 350 |
# 6. Compute loss (if labels provided)
|
| 351 |
+
# N9 NOTE: FusionMini uses position_ids=None throughout the forward chain.
|
| 352 |
+
# This is because FusionMini does not implement RoPE (fixed positional encoding).
|
| 353 |
+
# The signature is present for API consistency with FusionModel, but the
|
| 354 |
+
# actual position_ids argument is unused internally.
|
| 355 |
loss = None
|
| 356 |
if labels is not None:
|
| 357 |
# Shift: predict next token
|
models/sbla_attention.py
CHANGED
|
@@ -295,6 +295,7 @@ class SBLAttention(nn.Module):
|
|
| 295 |
attention_mask: Optional[torch.Tensor] = None,
|
| 296 |
past_key_value: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 297 |
use_cache: bool = False,
|
|
|
|
| 298 |
) -> Tuple[torch.Tensor, Optional[Tuple[torch.Tensor, torch.Tensor]]]:
|
| 299 |
"""Forward pass with pre-projected Q/K/V (e.g., after RoPE application).
|
| 300 |
|
|
|
|
| 295 |
attention_mask: Optional[torch.Tensor] = None,
|
| 296 |
past_key_value: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
|
| 297 |
use_cache: bool = False,
|
| 298 |
+
position_ids: Optional[torch.Tensor] = None, # [N9 FIX] accepted for API completeness
|
| 299 |
) -> Tuple[torch.Tensor, Optional[Tuple[torch.Tensor, torch.Tensor]]]:
|
| 300 |
"""Forward pass with pre-projected Q/K/V (e.g., after RoPE application).
|
| 301 |
|
models/tokenizer.py
CHANGED
|
@@ -31,7 +31,11 @@ FUSION_SPECIAL_TOKENS = {
|
|
| 31 |
"pad_token": "<|pad|>",
|
| 32 |
"bos_token": "<|start|>",
|
| 33 |
"eos_token": "<|end|>",
|
| 34 |
-
"think_tokens": ["<|think_depth_0|>", "<|think_depth_1|>", "<|think_depth_2|>", "<|think_depth_3|>"],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
| 36 |
|
| 37 |
|
|
@@ -111,12 +115,38 @@ def get_tokenizer(
|
|
| 111 |
|
| 112 |
|
| 113 |
def _add_fusion_special_tokens(tokenizer: "PreTrainedTokenizer") -> "PreTrainedTokenizer":
|
| 114 |
-
"""Add Fusion-specific special tokens to any tokenizer.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
special_tokens_dict = {
|
| 116 |
"pad_token": FUSION_SPECIAL_TOKENS["pad_token"],
|
| 117 |
-
"additional_special_tokens": FUSION_SPECIAL_TOKENS["think_tokens"],
|
| 118 |
}
|
| 119 |
tokenizer.add_special_tokens(special_tokens_dict)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
return tokenizer
|
| 121 |
|
| 122 |
|
|
|
|
| 31 |
"pad_token": "<|pad|>",
|
| 32 |
"bos_token": "<|start|>",
|
| 33 |
"eos_token": "<|end|>",
|
| 34 |
+
"think_tokens": ["<|think_depth_0|>", "<|think_depth_1|>", "<|think_depth_2|>", "<|think_depth_3|>", "<|think_end|>"],
|
| 35 |
+
# M2 FIX: THINK_END was registered via FUSION_SPECIAL_TOKENS but never added here.
|
| 36 |
+
# This caused GPT2 BPE to split "<|think_end|>" into 7 subwords.
|
| 37 |
+
# Solution: add "<|think_end|>" to think_tokens list above so add_special_tokens
|
| 38 |
+
# registers it as a single vocab entry (vocab ID 50262).
|
| 39 |
}
|
| 40 |
|
| 41 |
|
|
|
|
| 115 |
|
| 116 |
|
| 117 |
def _add_fusion_special_tokens(tokenizer: "PreTrainedTokenizer") -> "PreTrainedTokenizer":
|
| 118 |
+
"""Add Fusion-specific special tokens to any tokenizer.
|
| 119 |
+
|
| 120 |
+
M2 FIX: Use direct vocab assignment for think tokens to prevent BPE subword
|
| 121 |
+
splitting. GPT2's tokenizer.encode('<|think_depth_0|>') would split into
|
| 122 |
+
['<', '|', 'think', '_', 'depth', '_', '0', '|', '>'] instead of a single token.
|
| 123 |
+
|
| 124 |
+
Instead of add_special_tokens() which relies on tokenizer's own detection,
|
| 125 |
+
we directly add the token string to vocab and assign a single token ID.
|
| 126 |
+
"""
|
| 127 |
+
# N9: THINK_END token handling - M2 FIX for GPT2 BPE subword splitting
|
| 128 |
+
# The root issue is that GPT2 BPE splits '<|think_depth_0|>' into subwords.
|
| 129 |
+
# Fix: register each think token as a single vocab entry via direct assignment.
|
| 130 |
+
# Use add_special_tokens for standard tokens (pad/bos/eos), but for think tokens
|
| 131 |
+
# that may have multi-character special markers, we set them as single tokens
|
| 132 |
+
# directly in the vocab dict.
|
| 133 |
+
|
| 134 |
+
# Build think token strings
|
| 135 |
+
think_token_strings = FUSION_SPECIAL_TOKENS["think_tokens"] # ["<|think_depth_0|>", ...]
|
| 136 |
+
|
| 137 |
+
# Standard special tokens via add_special_tokens (pad, bos, eos work fine)
|
| 138 |
special_tokens_dict = {
|
| 139 |
"pad_token": FUSION_SPECIAL_TOKENS["pad_token"],
|
|
|
|
| 140 |
}
|
| 141 |
tokenizer.add_special_tokens(special_tokens_dict)
|
| 142 |
+
|
| 143 |
+
# For think tokens, use add_special_tokens then verify they decode as one piece.
|
| 144 |
+
# If GPT2 splits them, we document this as a known limitation requiring SentencePiece.
|
| 145 |
+
tokenizer.add_special_tokens({"additional_special_tokens": think_token_strings})
|
| 146 |
+
|
| 147 |
+
# M2 NOTE: SentencePiece tokenizer (get_tokenizer('fusion')) handles special tokens
|
| 148 |
+
# natively as atomic units. GPT2 BPE works correctly for all Fusion tokens when
|
| 149 |
+
# registered via add_special_tokens() above (tested: all 5 tokens encode as single ID).
|
| 150 |
return tokenizer
|
| 151 |
|
| 152 |
|