@@ -284,10 +284,10 @@ func (f *Function) flashSupported(op string, qkvDType dtypes.DType, mask compute
284284 return errors .Wrapf (compute .ErrNotImplemented ,
285285 "%s: cuDNN flash path supports only BSHD or BHSD layouts (got layout %v)" , op , axesLayout )
286286 }
287- if numKVHeads != numHeads {
287+ if numHeads % numKVHeads != 0 {
288288 return errors .Wrapf (compute .ErrNotImplemented ,
289- "%s: cuDNN flash path requires equal number of q/ kv heads (got layout=%v heads=%d/ %d)" ,
290- op , axesLayout , numHeads , numKVHeads )
289+ "%s: cuDNN flash path requires query heads (%d) to be a multiple of kv heads (%d)" ,
290+ op , numHeads , numKVHeads )
291291 }
292292 // One of QuerySeqLen/KeyValueSeqLen set without the other is ambiguous.
293293 if options != nil && (options .QuerySeqLen != nil ) != (options .KeyValueSeqLen != nil ) {
@@ -407,9 +407,21 @@ func (f *Function) FusedScaledDotProductAttention(query, key, value compute.Valu
407407 if scale == 0 {
408408 scale = 1.0 / math .Sqrt (float64 (featureDim ))
409409 }
410+ broadcastedKey := key
411+ broadcastedValue := value
412+ if numHeads != numKVHeads {
413+ broadcastedKey , err = f .broadcastGQA (key , numHeads , numKVHeads , axesLayout )
414+ if err != nil {
415+ return nil , nil , err
416+ }
417+ broadcastedValue , err = f .broadcastGQA (value , numHeads , numKVHeads , axesLayout )
418+ if err != nil {
419+ return nil , nil , err
420+ }
421+ }
410422 // Operand order cuDNN expects: q, k, v, [bias], [seqQ, seqKV]. Bias goes before seqlens
411423 // (bias and seqlens are mutually exclusive here; see selectFMHAVariant).
412- operands := []compute.Value {query , key , value }
424+ operands := []compute.Value {query , broadcastedKey , broadcastedValue }
413425 operandLayouts := [][]int {{3 , 2 , 1 , 0 }, {3 , 2 , 1 , 0 }, {3 , 2 , 1 , 0 }}
414426 if variant .hasBias {
415427 if err = validateBias ("Bias" , options .Bias , qDType , batchSize , numHeads , seqLen , seqLen ); err != nil {
@@ -494,9 +506,21 @@ func (f *Function) FusedScaledDotProductAttentionVJP(query, key, value compute.V
494506 if scale == 0 {
495507 scale = 1.0 / math .Sqrt (float64 (featureDim ))
496508 }
509+ broadcastedKey := key
510+ broadcastedValue := value
511+ if numHeads != numKVHeads {
512+ broadcastedKey , err = f .broadcastGQA (key , numHeads , numKVHeads , axesLayout )
513+ if err != nil {
514+ return nil , nil , nil , err
515+ }
516+ broadcastedValue , err = f .broadcastGQA (value , numHeads , numKVHeads , axesLayout )
517+ if err != nil {
518+ return nil , nil , nil , err
519+ }
520+ }
497521 // Operand order cuDNN expects: q, k, v, softmax_sum, dO, [bias], O. Bias goes at index 5,
498522 // before O (bias and seqlens are mutually exclusive; see selectFMHAVariant).
499- operands := []compute.Value {query , key , value , softmaxStats , dOutput }
523+ operands := []compute.Value {query , broadcastedKey , broadcastedValue , softmaxStats , dOutput }
500524 operandLayouts := [][]int {{3 , 2 , 1 , 0 }, {3 , 2 , 1 , 0 }, {3 , 2 , 1 , 0 }, {2 , 1 , 0 }, {3 , 2 , 1 , 0 }}
501525 if variant .hasBias {
502526 if err = validateBias ("Bias" , options .Bias , qDType , batchSize , numHeads , seqLen , seqLen ); err != nil {
@@ -555,5 +579,88 @@ func (f *Function) FusedScaledDotProductAttentionVJP(query, key, value compute.V
555579 dKey = grads [1 ]
556580 dValue = grads [2 ]
557581 }
582+ if numHeads != numKVHeads {
583+ dKey , err = f .reduceSumGQA (dKey , numHeads , numKVHeads , axesLayout )
584+ if err != nil {
585+ return nil , nil , nil , err
586+ }
587+ dValue , err = f .reduceSumGQA (dValue , numHeads , numKVHeads , axesLayout )
588+ if err != nil {
589+ return nil , nil , nil , err
590+ }
591+ }
558592 return dQuery , dKey , dValue , nil
559593}
594+
595+ func (f * Function ) broadcastGQA (x compute.Value , numQueryHeads , numKVHeads int , layout compute.AxesLayout ) (compute.Value , error ) {
596+ groupSize := numQueryHeads / numKVHeads
597+ if groupSize == 1 {
598+ return x , nil
599+ }
600+ shape , err := f .Shape (x )
601+ if err != nil {
602+ return nil , err
603+ }
604+ dtype := shape .DType
605+ dims := shape .Dimensions
606+ if len (dims ) != 4 {
607+ return nil , errors .Errorf ("broadcastGQA: expected rank-4 tensor, got rank-%d (shape %v)" , len (dims ), dims )
608+ }
609+
610+ if layout == compute .AxesLayoutBSHD {
611+ b , s , _ , d := dims [0 ], dims [1 ], dims [2 ], dims [3 ]
612+ reshaped , err := f .Reshape (x , b , s , numKVHeads , 1 , d )
613+ if err != nil {
614+ return nil , err
615+ }
616+ broadcastShape := shapes .Make (dtype , b , s , numKVHeads , groupSize , d )
617+ broadcasted , err := f .BroadcastInDim (reshaped , broadcastShape , []int {0 , 1 , 2 , 3 , 4 })
618+ if err != nil {
619+ return nil , err
620+ }
621+ return f .Reshape (broadcasted , b , s , numQueryHeads , d )
622+ } else {
623+ b , _ , s , d := dims [0 ], dims [1 ], dims [2 ], dims [3 ]
624+ reshaped , err := f .Reshape (x , b , numKVHeads , 1 , s , d )
625+ if err != nil {
626+ return nil , err
627+ }
628+ broadcastShape := shapes .Make (dtype , b , numKVHeads , groupSize , s , d )
629+ broadcasted , err := f .BroadcastInDim (reshaped , broadcastShape , []int {0 , 1 , 2 , 3 , 4 })
630+ if err != nil {
631+ return nil , err
632+ }
633+ return f .Reshape (broadcasted , b , numQueryHeads , s , d )
634+ }
635+ }
636+
637+ func (f * Function ) reduceSumGQA (dx compute.Value , numQueryHeads , numKVHeads int , layout compute.AxesLayout ) (compute.Value , error ) {
638+ groupSize := numQueryHeads / numKVHeads
639+ if groupSize == 1 {
640+ return dx , nil
641+ }
642+ shape , err := f .Shape (dx )
643+ if err != nil {
644+ return nil , err
645+ }
646+ dims := shape .Dimensions
647+ if len (dims ) != 4 {
648+ return nil , errors .Errorf ("reduceSumGQA: expected rank-4 tensor, got rank-%d (shape %v)" , len (dims ), dims )
649+ }
650+
651+ if layout == compute .AxesLayoutBSHD {
652+ b , s , _ , d := dims [0 ], dims [1 ], dims [2 ], dims [3 ]
653+ reshaped , err := f .Reshape (dx , b , s , numKVHeads , groupSize , d )
654+ if err != nil {
655+ return nil , err
656+ }
657+ return f .ReduceSum (reshaped , 3 )
658+ } else {
659+ b , _ , s , d := dims [0 ], dims [1 ], dims [2 ], dims [3 ]
660+ reshaped , err := f .Reshape (dx , b , numKVHeads , groupSize , s , d )
661+ if err != nil {
662+ return nil , err
663+ }
664+ return f .ReduceSum (reshaped , 2 )
665+ }
666+ }
0 commit comments