Skip to content

Commit 3b49a55

Browse files
committed
Added support to GQA (Grouped Query Attention) in the flash-attention implementation by projecting the key/value heads.
Also adjusted the VJP implementation to reduce-sum the resulting VJP back to the original key/value heads.
1 parent 02719c1 commit 3b49a55

1 file changed

Lines changed: 112 additions & 5 deletions

File tree

compute/xla/flash.go

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)