File size: 14,177 Bytes
5216a17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
"""
DSpark-specific components for ScrapeGoat model.
Implements DSpark attention, Markov heads, and related components.
"""

import math
import torch
import torch.nn as nn
from typing import Optional, Tuple


def get_dspark_topk_idxs(window_size: int, bsz: int, block_size: int, start_pos: int):
    """
    Get top-k indices for DSpark attention.
    Based on the implementation from DeepSeek-V4-Pro-DSpark.
    """
    assert start_pos > 0
    # Create tensor: [0, 1, ..., min(window_size, start_pos+1)-1, window_size, window_size+1, ..., window_size+block_size-1]
    idx1 = torch.arange(min(window_size, start_pos + 1), device='cpu')  # Will be moved to correct device later
    idx2 = window_size + torch.arange(block_size, device='cpu')
    matrix = torch.cat([idx1, idx2])
    return matrix.int().view(1, 1, -1).expand(bsz, block_size, -1)


class DSparkAttention(nn.Module):
    """
    DSpark Attention mechanism from DeepSeek-V4-Pro-DSpark.
    Combines window-based attention with compressed attention for efficient long-context processing.
    """

    def __init__(self, config):
        super().__init__()
        self.config = config
        self.hidden_size = config.hidden_size
        self.num_heads = config.num_attention_heads
        self.head_dim = self.hidden_size // self.num_heads
        self.num_key_value_heads = config.num_key_value_heads
        self.num_key_value_groups = self.num_heads // self.num_key_value_heads
        self.max_position_embeddings = config.max_position_embeddings
        self.rope_theta = getattr(config, 'rope_theta', 10000.0)
        self.is_causal = True
        self.attn_sink = nn.Parameter(torch.zeros(self.num_heads))  # Learnable attention sink bias

        # Projections
        self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=config.attention_bias)
        self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=config.attention_bias)
        self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=config.attention_bias)
        self.o_proj = nn.Linear(self.num_heads * self.head_dim, self.hidden_size, bias=config.attention_bias)

        # For RoPE
        self.rotary_emb = None  # Will be set by the model or created internally if needed

        # DSpark-specific parameters
        self.window_size = getattr(config, 'window_size', 128)
        self.compress_ratio = getattr(config, 'compress_ratio', 4)
        self.has_indexer = self.compress_ratio == 4

        # KV cache will be managed externally
        self.kv_cache = None

    def forward(
        self,
        hidden_states: torch.Tensor,
        attention_mask: Optional[torch.Tensor] = None,
        position_ids: Optional[torch.LongTensor] = None,
        past_key_value: Optional[Tuple[torch.Tensor]] = None,
        main_x: Optional[torch.Tensor] = None,  # This is the key DSpark input - features from target layers
        output_attentions: bool = False,
        use_cache: bool = False,
        **kwargs,
    ) -> tuple:
        """
        DSpark Attention forward pass.

        Args:
            hidden_states: Input tensor [batch_size, seq_len, hidden_size]
            attention_mask: Attention mask
            position_ids: Position IDs
            past_key_value: Cached key/value states
            main_x: Features from target layers (specific to DSpark)
            output_attentions: Whether to return attention weights
            use_cache: Whether to use caching

        Returns:
            tuple of (output, attention_weights, present_key_value)
        """
        bsz, q_len, _ = hidden_states.size()

        # Query projections
        query_states = self.q_proj(hidden_states)
        key_states = self.k_proj(hidden_states)
        value_states = self.v_proj(hidden_states)

        # Reshape for multi-head attention
        query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2)
        key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
        value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)

        # Handle past key value
        kv_seq_len = key_states.shape[-2]
        if past_key_value is not None:
            kv_seq_len += past_key_value[0].shape[-2]
            key_states = torch.cat([past_key_value[0], key_states], dim=2)
            value_states = torch.cat([past_key_value[1], value_states], dim=2)

        # Apply RoPE if available (simplified - in practice would use precomputed freqs)
        # For now, we'll skip RoPE implementation details and focus on DSpark logic

        # Repeat k/v heads if n_kv_heads < n_heads
        key_states = key_states.repeat_interleave(self.num_key_value_groups, dim=1)
        value_states = value_states.repeat_interleave(self.num_key_value_groups, dim=1)

        # DSpark-specific logic: if we have main_x, use DSpark attention
        if main_x is not None:
            attn_output = self._dspark_attention_forward(
                query_states, key_states, value_states,
                attention_mask, position_ids, past_key_value, main_x
            )
        else:
            # Standard attention
            attn_weights = torch.matmul(query_states, key_states.transpose(2, 3)) / math.sqrt(self.head_dim)

            if attention_mask is not None:
                attn_weights = attn_weights + attention_mask

            attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype)
            attn_output = torch.matmul(attn_weights, value_states)
            attn_output = attn_output.transpose(1, 2).contiguous().view(bsz, q_len, self.hidden_size)

        attn_output = self.o_proj(attn_output)

        if not output_attentions:
            attn_weights = None

        present_key_value = (key_states, value_states) if use_cache else None

        return attn_output, attn_weights, present_key_value

    def _dspark_attention_forward(
        self,
        query_states: torch.Tensor,
        key_states: torch.Tensor,
        value_states: torch.Tensor,
        attention_mask: Optional[torch.Tensor],
        position_ids: Optional[torch.LongTensor],
        past_key_value: Optional[Tuple[torch.Tensor]],
        main_x: torch.Tensor
    ) -> torch.Tensor:
        """
        DSpark-specific attention computation that combines:
        1. Window attention on recent tokens from main_x
        2. Compressed attention on older tokens from main_x
        3. Standard attention on current hidden_states
        """
        bsz, q_len, _ = query_states.shape[:2]  # [b, q_len, h*d] -> reshape to [b, q_len, h, d]

        # Reshape query states for processing
        query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim)

        # Process main_x to get key/value states for DSpark
        # Main x comes from target layers, so we need to project it to K/V space
        main_key_states = self.k_proj(main_x)
        main_value_states = self.v_proj(main_x)

        # Reshape main key/value states
        main_key_states = main_key_states.view(main_x.size(0), main_x.size(1),
                                              self.num_key_value_heads, self.head_dim).transpose(1, 2)
        main_value_states = main_value_states.view(main_x.size(0), main_x.size(1),
                                                  self.num_key_value_heads, self.head_dim).transpose(1, 2)

        # Repeat k/v heads for main_x
        main_key_states = main_key_states.repeat_interleave(self.num_key_value_groups, dim=1)
        main_value_states = main_value_states.repeat_interleave(self.num_key_value_groups, dim=1)

        # Current sequence length from main_x
        main_seq_len = main_x.size(1)

        # Calculate effective lengths for window and compression
        effective_window_size = min(self.window_size, main_seq_len)

        # Window attention: attend to recent tokens in main_x
        if effective_window_size > 0:
            window_start = max(0, main_seq_len - effective_window_size)
            window_keys = main_key_states[:, :, window_start:main_seq_len, :]
            window_values = main_value_states[:, :, window_start:main_seq_len, :]

            # Compute window attention scores
            window_q = query_states  # [b, q_len, h, d]
            window_k = window_keys   # [b, h, kv_len, d]

            window_scores = torch.matmul(window_q, window_k.transpose(-2, -1)) / math.sqrt(self.head_dim)

            # Apply causal mask if needed
            if self.is_causal:
                q_positions = torch.arange(q_len, device=query_states.device).unsqueeze(1)
                k_positions = torch.arange(window_start, main_seq_len, device=query_states.device).unsqueeze(0)
                causal_mask = q_positions >= (k_positions - window_start)
                causal_mask = causal_mask.unsqueeze(0).unsqueeze(1)  # [1, 1, q_len, kv_len]
                window_scores = window_scores.masked_fill(~causal_mask, float('-inf'))

            # Apply attention sink bias
            window_scores = window_scores + self.attn_sink.view(1, -1, 1, 1)

            window_attn_weights = nn.functional.softmax(window_scores, dim=-1, dtype=torch.float32)
            window_attn_output = torch.matmul(window_attn_weights.to(window_values.dtype), window_values)
        else:
            window_attn_output = torch.zeros_like(query_states)

        # Compressed attention: attend to compressed representation of older tokens
        # This is a simplified version - full implementation would use the compressor/indexer
        if main_seq_len > self.window_size:
            # For simplicity, we'll just use average pooling as compression
            # In practice, this would use the Compressor and Indexer modules
            remaining_len = max(0, main_seq_len - self.window_size)
            if remaining_len > 0:
                # Simple average compression (placeholder)
                compressed_k = torch.mean(main_key_states[:, :, :main_seq_len - self.window_size, :], dim=2, keepdim=True)
                compressed_v = torch.mean(main_value_states[:, :, :main_seq_len - self.window_size, :], dim=2, keepdim=True)

                # Expand to match heads
                compressed_k = compressed_k.expand(-1, self.num_heads, -1, -1)
                compressed_v = compressed_v.expand(-1, self.num_heads, -1, -1)

                # Compute compressed attention
                compressed_q = query_states
                compressed_scores = torch.matmul(compressed_q, compressed_k.transpose(-2, -1)) / math.sqrt(self.head_dim)
                compressed_attn_weights = nn.functional.softmax(compressed_scores, dim=-1, dtype=torch.float32)
                compressed_attn_output = torch.matmul(compressed_attn_weights.to(compressed_v.dtype), compressed_v)
            else:
                compressed_attn_output = torch.zeros_like(query_states)
        else:
            compressed_attn_output = torch.zeros_like(query_states)

        # Standard attention on current hidden states (if needed)
        # In DSpark, this might be skipped or weighted differently
        std_attn_output = torch.zeros_like(query_states)  # Placeholder

        # Combine outputs (in practice, this would be learned weights)
        # For now, simple sum
        combined_output = window_attn_output + compressed_attn_output + std_attn_output

        # Reshape back
        combined_output = combined_output.transpose(1, 2).contiguous().view(bsz, q_len, self.hidden_size)

        return combined_output


class DSparkMarkovHead(nn.Module):
    """
    DSpark Markov Head for next token prediction based on Markov chains.
    """

    def __init__(self, config):
        super().__init__()
        self.vocab_size = config.vocab_size
        self.markov_rank = getattr(config, 'dspark_markov_rank', 256)

        # Markov transition matrices
        self.markov_w1 = nn.Embedding(self.vocab_size, self.markov_rank)
        self.markov_w2 = nn.Linear(self.markov_rank, self.vocab_size, bias=False)

    def forward(self, token_ids: torch.Tensor) -> torch.Tensor:
        """
        Compute Markov-based next token logits.

        Args:
            token_ids: Input token IDs [batch_size, seq_len]

        Returns:
            logits: Next token logits [batch_size, seq_len, vocab_size]
        """
        # Embed tokens
        embed = self.markov_w1(token_ids)  # [batch_size, seq_len, markov_rank]

        # Project to vocabulary space
        logits = self.markov_w2(embed)  # [batch_size, seq_len, vocab_size]

        return logits


class DSparkConfidenceHead(nn.Module):
    """
    DSpark Confidence Head for scoring prediction confidence.
    """

    def __init__(self, config):
        super().__init__()
        hidden_size = getattr(config, 'hidden_size', 4096)
        markov_rank = getattr(config, 'dspark_markov_rank', 256)
        input_dim = hidden_size + markov_rank

        self.proj = nn.Linear(input_dim, 1, bias=False)

    def forward(self, hidden: torch.Tensor, markov_embed: torch.Tensor) -> torch.Tensor:
        """
        Compute confidence score.

        Args:
            hidden: Hidden states from model [batch_size, seq_len, hidden_size]
            markov_embed: Markov embeddings [batch_size, seq_len, markov_rank]

        Returns:
            confidence: Confidence scores [batch_size, seq_len]
        """
        # Concatenate hidden states and Markov embeddings
        combined = torch.cat([hidden, markov_embed], dim=-1)  # [batch_size, seq_len, hidden_size + markov_rank]

        # Project to single dimension
        confidence = self.proj(combined).squeeze(-1)  # [batch_size, seq_len]

        return confidence


def sample(logits, temperature: float = 1.0):
    """
    Sample from logits using Gumbel-max trick.
    """
    if temperature == 0:
        return logits.argmax(dim=-1)
    logits = logits / max(temperature, 1e-5)
    probs = torch.softmax(logits, dim=-1, dtype=torch.float32)
    return probs.div_(torch.empty_like(probs).exponential_(1)).argmax(dim=-1)