TypeScript commited on
Commit
a09624d
·
verified ·
1 Parent(s): 0905286

Optimize JointSchemaHead (Ordinal-KL + multi-task calibration distillation), SGLang fast FlashAttention kernel in joint_schema_model.py, and sync GGUF builds

Browse files
FastBrain-9B-BF16.gguf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:ea3c2beb04acf2b6697fd47c4d02d7f53b01d6ce08755bacca4f2ef91e0561b0
3
- size 18164488352
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ef9d5ec64502466ed71933f4dfd89156ff1aafaa4e2c36c8af5a3f62dc091a2e
3
+ size 18164488768
FastBrain-9B-Q4_K_M.gguf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:6c5005c680a842b11639b0b6e5bebfc9a0bbd8e4b8a1aceb96248d24f038045a
3
- size 6732781728
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a40f122e5c7dac5d901a4a0c9993e953c1e4b0ebb974722459020ad801d54c6b
3
+ size 6732782144
FastBrain-9B-Q8_0.gguf CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:1449a7ec20aea71fcac6ff89a29571dccb4188c348e8fc0db6cdea8e183104a8
3
- size 9657260192
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:901e34c4ac710b2c52c4d1c56da85bc8b230f70a514ad5420b019ce27037bfa7
3
+ size 9657260608
joint_head.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:5dc14a970326c99b98f32ff3e54c4c0144a2096bd86825790dac40a27df90cc6
3
  size 243537984
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ce8517deeb83948b29c4e24428171bbd403af220555ddf8c063792c332752b14
3
  size 243537984
joint_schema_model.py CHANGED
@@ -267,6 +267,41 @@ def collate_records(
267
  }
268
 
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  class EvidenceRoutingLayer(torch.nn.Module):
271
  def __init__(
272
  self,
@@ -369,7 +404,234 @@ class JointSchemaHead(torch.nn.Module):
369
  start, end = span
370
  return values[start:end].mean(dim=0)
371
 
372
- def forward(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  self,
374
  hidden_states: torch.Tensor,
375
  input_ids: torch.Tensor,
@@ -486,6 +748,38 @@ class JointSchemaHead(torch.nn.Module):
486
  results.append(record_logits)
487
  return results
488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
 
490
  class ClefModel(torch.nn.Module):
491
  def __init__(self, language_model: Any, head: JointSchemaHead) -> None:
@@ -1053,4 +1347,3 @@ def _apply_schema_aware_calibration(request: dict[str, Any], answers: dict[str,
1053
 
1054
 
1055
  apply_schema_aware_calibration = _apply_schema_aware_calibration
1056
-
 
267
  }
268
 
269
 
270
+ def _packed_flash_attention(
271
+ module: torch.nn.MultiheadAttention,
272
+ queries: torch.Tensor,
273
+ keys: torch.Tensor | None,
274
+ query_bounds: tuple[torch.Tensor, int],
275
+ key_bounds: tuple[torch.Tensor, int],
276
+ ) -> torch.Tensor:
277
+ """Variable-length FlashAttention over packed sequences within each request."""
278
+ width = queries.shape[-1]
279
+ heads = module.num_heads
280
+ if keys is None:
281
+ query, key, value = functional.linear(
282
+ queries, module.in_proj_weight, module.in_proj_bias
283
+ ).chunk(3, dim=-1)
284
+ else:
285
+ query_weight, key_value_weight = module.in_proj_weight.split([width, 2 * width])
286
+ query_bias, key_value_bias = module.in_proj_bias.split([width, 2 * width])
287
+ query = functional.linear(queries, query_weight, query_bias)
288
+ key, value = functional.linear(keys, key_value_weight, key_value_bias).chunk(2, dim=-1)
289
+ head_dim = width // heads
290
+ output = torch.ops.aten._flash_attention_forward(
291
+ query.reshape(-1, heads, head_dim),
292
+ key.reshape(-1, heads, head_dim),
293
+ value.reshape(-1, heads, head_dim),
294
+ query_bounds[0],
295
+ key_bounds[0],
296
+ query_bounds[1],
297
+ key_bounds[1],
298
+ 0.0,
299
+ False,
300
+ False,
301
+ )[0]
302
+ return module.out_proj(output.reshape(-1, width))
303
+
304
+
305
  class EvidenceRoutingLayer(torch.nn.Module):
306
  def __init__(
307
  self,
 
404
  start, end = span
405
  return values[start:end].mean(dim=0)
406
 
407
+ def _forward_fast_cuda(
408
+ self,
409
+ hidden_states: torch.Tensor,
410
+ input_ids: torch.Tensor,
411
+ attention_mask: torch.Tensor,
412
+ records: list[EncodedRecord],
413
+ output_embedding_weight: torch.Tensor,
414
+ ) -> list[list[torch.Tensor]]:
415
+ """Vectorized prefix-sum span pooling + packed varlen FlashAttention (SGLang dev-clef kernel)."""
416
+ device, dtype = hidden_states.device, hidden_states.dtype
417
+ batch = len(records)
418
+ lengths = [int(attention_mask[i].sum().item()) for i in range(batch)]
419
+ if batch == 1:
420
+ hidden = hidden_states[0, : lengths[0]]
421
+ flat_ids = input_ids[0, : lengths[0]]
422
+ else:
423
+ hidden = torch.cat([hidden_states[i, : lengths[i]] for i in range(batch)], dim=0)
424
+ flat_ids = torch.cat([input_ids[i, : lengths[i]] for i in range(batch)], dim=0)
425
+
426
+ question_bounds: list[int] = []
427
+ option_bounds: list[int] = []
428
+ type_ids: list[int] = []
429
+ owner: list[int] = []
430
+ slot: list[int] = []
431
+ question_request: list[int] = []
432
+ last_tokens: list[int] = []
433
+ regions: list[tuple[int, int]] = []
434
+ region_offsets = [0]
435
+ token_cu = [0]
436
+ question_cu = [0]
437
+ option_cu = [0]
438
+ option_counts: list[int] = []
439
+ per_req_q_counts: list[list[int]] = []
440
+
441
+ for request, (length, record) in enumerate(zip(lengths, records)):
442
+ offset = token_cu[-1]
443
+ questions = record.questions
444
+ low = min(min(q.question_span[0], q.option_spans[0][0]) for q in questions)
445
+ high = max(
446
+ max(q.question_span[1], *(e for _, e in q.option_spans))
447
+ for q in questions
448
+ )
449
+ base = region_offsets[-1] + request - low
450
+ regions.append((offset + low, offset + high))
451
+ request_options = 0
452
+ q_opt_counts: list[int] = []
453
+ for question in questions:
454
+ count = len(question.option_spans)
455
+ q_opt_counts.append(count)
456
+ question_bounds += (
457
+ base + question.question_span[0],
458
+ base + question.question_span[1],
459
+ )
460
+ for option_slot, (option_start, option_end) in enumerate(
461
+ question.option_spans
462
+ ):
463
+ option_bounds += (base + option_start, base + option_end)
464
+ owner.append(len(type_ids))
465
+ slot.append(option_slot)
466
+ type_ids.append(question.question_type)
467
+ question_request.append(request)
468
+ request_options += count
469
+ per_req_q_counts.append(q_opt_counts)
470
+ last_tokens.append(offset + length - 1)
471
+ token_cu.append(offset + length)
472
+ region_offsets.append(region_offsets[-1] + high - low)
473
+ question_cu.append(len(type_ids))
474
+ option_cu.append(option_cu[-1] + request_options)
475
+ option_counts.append(request_options)
476
+
477
+ num_questions = len(type_ids)
478
+ widest = max(len(q.option_spans) for r in records for q in r.questions)
479
+ sizes = [
480
+ 2 * num_questions,
481
+ len(option_bounds),
482
+ num_questions,
483
+ len(owner),
484
+ len(slot),
485
+ num_questions,
486
+ batch,
487
+ 3 * (batch + 1),
488
+ ]
489
+ packed = torch.tensor(
490
+ question_bounds
491
+ + option_bounds
492
+ + type_ids
493
+ + owner
494
+ + slot
495
+ + question_request
496
+ + last_tokens
497
+ + token_cu
498
+ + question_cu
499
+ + option_cu,
500
+ dtype=torch.long,
501
+ device=device,
502
+ )
503
+ (
504
+ question_bounds_t,
505
+ option_bounds_t,
506
+ type_ids_t,
507
+ owner_t,
508
+ slot_t,
509
+ question_request_t,
510
+ last_tokens_t,
511
+ cu_t,
512
+ ) = torch.split(packed, sizes)
513
+ question_bounds_t = question_bounds_t.view(-1, 2)
514
+ option_bounds_t = option_bounds_t.view(-1, 2)
515
+ token_cu_t, question_cu_t, option_cu_t = (
516
+ cu_t.to(torch.int32).view(3, batch + 1).unbind(0)
517
+ )
518
+ token_bounds = (token_cu_t, max(lengths))
519
+ question_bounds_cu = (question_cu_t, max(len(r.questions) for r in records))
520
+ option_bounds_cu = (option_cu_t, max(option_counts))
521
+
522
+ normalized = self.hidden_norm(hidden)
523
+ region_total = region_offsets[-1] + batch
524
+ hidden_prefix = torch.zeros(
525
+ region_total, normalized.shape[-1], dtype=torch.float32, device=device
526
+ )
527
+ lexical_prefix = torch.zeros(
528
+ region_total,
529
+ output_embedding_weight.shape[-1],
530
+ dtype=torch.float32,
531
+ device=device,
532
+ )
533
+ for request, (low, high) in enumerate(regions):
534
+ row = region_offsets[request] + request + 1
535
+ torch.cumsum(
536
+ normalized[low:high].float(),
537
+ dim=0,
538
+ out=hidden_prefix[row : row + high - low],
539
+ )
540
+ torch.cumsum(
541
+ output_embedding_weight[flat_ids[low:high]].float(),
542
+ dim=0,
543
+ out=lexical_prefix[row : row + high - low],
544
+ )
545
+
546
+ def span_means(prefix: torch.Tensor, bounds: torch.Tensor) -> torch.Tensor:
547
+ sums = prefix[bounds[:, 1]] - prefix[bounds[:, 0]]
548
+ return (sums / (bounds[:, 1] - bounds[:, 0]).unsqueeze(-1)).to(dtype)
549
+
550
+ memory = self.memory_projection(normalized)
551
+ global_vectors = normalized[last_tokens_t]
552
+ question_vectors = span_means(hidden_prefix, question_bounds_t)
553
+ option_contexts = span_means(hidden_prefix, option_bounds_t)
554
+ lexical = span_means(lexical_prefix, option_bounds_t)
555
+
556
+ routed = (
557
+ self.option_context_projection(option_contexts)
558
+ + self.option_lexical_projection(lexical)
559
+ + self.option_question_projection(question_vectors)[owner_t]
560
+ )
561
+ for layer in self.evidence_layers:
562
+ routed = routed + _packed_flash_attention(
563
+ layer.attention,
564
+ layer.query_norm(routed),
565
+ layer.memory_norm(memory),
566
+ option_bounds_cu,
567
+ token_bounds,
568
+ )
569
+ routed = routed + layer.feedforward(layer.feedforward_norm(routed))
570
+
571
+ base_fields = self.question_projection(question_vectors)
572
+ scores = torch.matmul(
573
+ routed.unsqueeze(1), base_fields[owner_t].unsqueeze(2)
574
+ ).view(-1) / math.sqrt(routed.shape[-1])
575
+ padded_scores = torch.full(
576
+ (num_questions, widest), float("-inf"), dtype=torch.float32, device=device
577
+ )
578
+ padded_scores[owner_t, slot_t] = scores.float()
579
+ weights = torch.softmax(padded_scores, dim=1).to(dtype)
580
+ padded_options = routed.new_zeros(num_questions, widest, routed.shape[-1])
581
+ padded_options[owner_t, slot_t] = routed
582
+ summaries = torch.sum(weights.unsqueeze(-1) * padded_options, dim=1)
583
+ fields = (
584
+ base_fields
585
+ + self.option_summary_norm(summaries)
586
+ + self.global_projection(global_vectors)[question_request_t]
587
+ + self.type_embedding(type_ids_t)
588
+ )
589
+ for layer in self.layers:
590
+ fields = fields + _packed_flash_attention(
591
+ layer.self_attn,
592
+ layer.norm1(fields),
593
+ None,
594
+ question_bounds_cu,
595
+ question_bounds_cu,
596
+ )
597
+ fields = fields + _packed_flash_attention(
598
+ layer.multihead_attn,
599
+ layer.norm2(fields),
600
+ memory,
601
+ question_bounds_cu,
602
+ token_bounds,
603
+ )
604
+ fields = fields + layer.linear2(
605
+ layer.activation(layer.linear1(layer.norm3(fields)))
606
+ )
607
+ fields = self.field_norm(fields)
608
+
609
+ prior_scale = self.prior_logit_scale.clamp(max=math.log(100.0)).exp()
610
+ joint_scale = self.joint_logit_scale.clamp(max=math.log(100.0)).exp()
611
+ anchor = functional.normalize(
612
+ question_vectors + global_vectors[question_request_t], dim=-1
613
+ )
614
+ prior = prior_scale * torch.matmul(
615
+ functional.normalize(lexical, dim=-1).unsqueeze(1), anchor[owner_t].unsqueeze(2)
616
+ ).view(-1)
617
+ options = self.option_norm(routed)
618
+ repeated = fields[owner_t]
619
+ cosine = functional.cosine_similarity(repeated, options, dim=-1)
620
+ features = torch.cat(
621
+ [repeated, options, repeated * options, torch.abs(repeated - options)],
622
+ dim=-1,
623
+ )
624
+ residual = self.residual_scorer(features).squeeze(-1)
625
+ logits = prior + torch.sigmoid(self.residual_gate) * (
626
+ joint_scale * cosine + residual
627
+ )
628
+ per_req_flat = torch.split(logits, option_counts)
629
+ return [
630
+ list(torch.split(req_logits, q_counts))
631
+ for req_logits, q_counts in zip(per_req_flat, per_req_q_counts)
632
+ ]
633
+
634
+ def _forward_reference(
635
  self,
636
  hidden_states: torch.Tensor,
637
  input_ids: torch.Tensor,
 
748
  results.append(record_logits)
749
  return results
750
 
751
+ def forward(
752
+ self,
753
+ hidden_states: torch.Tensor,
754
+ input_ids: torch.Tensor,
755
+ attention_mask: torch.Tensor,
756
+ records: list[EncodedRecord],
757
+ output_embedding_weight: torch.Tensor,
758
+ ) -> list[list[torch.Tensor]]:
759
+ if (
760
+ not torch.is_grad_enabled()
761
+ and hidden_states.is_cuda
762
+ and hidden_states.dtype in (torch.float16, torch.bfloat16)
763
+ and hasattr(torch.ops.aten, "_flash_attention_forward")
764
+ ):
765
+ try:
766
+ return self._forward_fast_cuda(
767
+ hidden_states,
768
+ input_ids,
769
+ attention_mask,
770
+ records,
771
+ output_embedding_weight,
772
+ )
773
+ except Exception:
774
+ pass
775
+ return self._forward_reference(
776
+ hidden_states,
777
+ input_ids,
778
+ attention_mask,
779
+ records,
780
+ output_embedding_weight,
781
+ )
782
+
783
 
784
  class ClefModel(torch.nn.Module):
785
  def __init__(self, language_model: Any, head: JointSchemaHead) -> None:
 
1347
 
1348
 
1349
  apply_schema_aware_calibration = _apply_schema_aware_calibration