This is an automated email from the ASF dual-hosted git repository.

masahi pushed a commit to branch unity
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/unity by this push:
     new dd02791e5a [Uniy][Op] Expand support of attention bias layout (#14737)
dd02791e5a is described below

commit dd02791e5a18715997ef68ff1d755770b6bee63f
Author: Yaxing Cai <[email protected]>
AuthorDate: Fri Apr 28 05:11:37 2023 -0700

    [Uniy][Op] Expand support of attention bias layout (#14737)
    
    This PR expands the support of attention bias layout. In the past, we only 
support 3 types of layout, with `BNSS'`, `BSS'`, `BS'`, e.g. `BNSS'` for 
(batch_size, num_head, seq_len, seq_len_kv).  However, cutlass accepts 8 types 
of layout, as long as the shape ends with `S` and is broadcastable to `BNSS'`. 
So in this PR, we fixed the ndim of `bias` to 4 and accepts all 8 types of 
layout as cutlass.
---
 python/tvm/contrib/cutlass/attention_operation.py | 30 +++---------
 python/tvm/contrib/cutlass/gen_tensor_op.py       | 20 ++++++--
 python/tvm/relax/op/nn/nn.py                      |  4 +-
 python/tvm/relax/transform/legalize_ops/nn.py     |  4 --
 src/relax/op/nn/attention.cc                      | 29 ++++++------
 tests/python/relax/test_codegen_cutlass.py        | 57 ++++++++++++-----------
 6 files changed, 67 insertions(+), 77 deletions(-)

diff --git a/python/tvm/contrib/cutlass/attention_operation.py 
b/python/tvm/contrib/cutlass/attention_operation.py
index 1921edb397..0e507d3be1 100644
--- a/python/tvm/contrib/cutlass/attention_operation.py
+++ b/python/tvm/contrib/cutlass/attention_operation.py
@@ -23,32 +23,14 @@ def instantiate_attention_template(attrs):
     """Return CUTLASS host code for fused multi head attention
     based on a template and the provided attribute map."""
 
-    bias_template = {
-        "B11S'": """
-  CHECK(${bias}->ndim == 2); // B, 1, 1, S'
-
-  p.attn_bias_ptr = reinterpret_cast<T *>(${bias}->data);
-  p.bias_strideM = 0; // 0
-  p.bias_strideH = 0; // 0
-  p.bias_strideB = p.num_keys; // S'
-""",
-        "B1SS'": """
-  CHECK(${bias}->ndim == 3); // B, 1, S, S'
-
-  p.attn_bias_ptr = reinterpret_cast<T *>(${bias}->data);
-  p.bias_strideM = p.num_keys; // S'
-  p.bias_strideH = 0; // 0
-  p.bias_strideB = p.bias_strideM * p.num_queries; // S' * S
-""",
-        "BNSS'": """
+    bias_template = """
   CHECK(${bias}->ndim == 4); // B, N, S, S'
 
   p.attn_bias_ptr = reinterpret_cast<T *>(${bias}->data);
-  p.bias_strideM = p.num_keys; // S'
-  p.bias_strideH = p.bias_strideM * p.num_queries; // S' * S
-  p.bias_strideB = p.bias_strideH * p.num_heads; // S' * S * N
-""",
-    }
+  p.bias_strideM = ${bias_strideM};
+  p.bias_strideH = ${bias_strideH};
+  p.bias_strideB = ${bias_strideB};
+"""
 
     qkv_template = {
         "default": """
@@ -159,7 +141,7 @@ def instantiate_attention_template(attrs):
         template,
         {
             "qkv_template": qkv_template[attrs["qkv_layout"]],
-            "bias_template": bias_template[attrs["bias_layout"]] if 
"bias_layout" in attrs else "",
+            "bias_template": bias_template if "bias" in attrs else "",
         },
     )
 
diff --git a/python/tvm/contrib/cutlass/gen_tensor_op.py 
b/python/tvm/contrib/cutlass/gen_tensor_op.py
index bb4d224329..392a7bf7e2 100644
--- a/python/tvm/contrib/cutlass/gen_tensor_op.py
+++ b/python/tvm/contrib/cutlass/gen_tensor_op.py
@@ -741,11 +741,21 @@ def instantiate_template(func_name, annotations, 
func_args):
         if "bias" in attrs:
             attrs["kSupportsBias"] = True
             if len(annotations["bias_shape"]) == 4:
-                attrs["bias_layout"] = "BNSS'"
-            elif len(annotations["bias_shape"]) == 3:
-                attrs["bias_layout"] = "B1SS'"
-            elif len(annotations["bias_shape"]) == 2:
-                attrs["bias_layout"] = "B11S'"
+                strides = "p.num_keys"
+                if annotations["bias_shape"][2] == 1:
+                    attrs["bias_strideM"] = 0
+                else:
+                    attrs["bias_strideM"] = strides
+                    strides = f"p.num_queries * {strides}"
+                if annotations["bias_shape"][1] == 1:
+                    attrs["bias_strideH"] = 0
+                else:
+                    attrs["bias_strideH"] = strides
+                    strides = f"p.num_heads * {strides}"
+                if annotations["bias_shape"][0] == 1:
+                    attrs["bias_strideB"] = 0
+                else:
+                    attrs["bias_strideB"] = strides
             else:
                 raise NotImplementedError()
         else:
diff --git a/python/tvm/relax/op/nn/nn.py b/python/tvm/relax/op/nn/nn.py
index 5483e7c5ee..fb5e0736ff 100644
--- a/python/tvm/relax/op/nn/nn.py
+++ b/python/tvm/relax/op/nn/nn.py
@@ -1009,8 +1009,8 @@ def attention(
 
     bias: Optional[Expr]
         The optional attention bias to the operator. The layout of the 
attention bias should be
-        (batch_size, num_head, seq_len, seq_len_kv),
-        (batch_size, seq_len, seq_len_kv) or (batch_size, seq_len_kv).
+        a 4-D tensor ending with seq_len_kv, and broadcastable to
+        (batch_size, num_head, seq_len, seq_len_kv).
 
     scale: Optional[FloatImm]
         The custom scale applied before the softmax. The default value is 1 / 
sqrt(head_dim).
diff --git a/python/tvm/relax/transform/legalize_ops/nn.py 
b/python/tvm/relax/transform/legalize_ops/nn.py
index efed7b586b..9c98682e32 100644
--- a/python/tvm/relax/transform/legalize_ops/nn.py
+++ b/python/tvm/relax/transform/legalize_ops/nn.py
@@ -336,10 +336,6 @@ def _te_attention(
         p = topi.divide(p, tir.sqrt(tir.Cast(p.dtype, head_dim)))
     if bias is not None:
         p = topi.reshape(p, [batch_size, num_head, seq_len, seq_len_kv])
-        if len(bias.shape) == 2:
-            bias = topi.reshape(bias, [batch_size, 1, 1, seq_len_kv])
-        elif len(bias.shape) == 3:
-            bias = topi.reshape(bias, [batch_size, 1, seq_len, seq_len_kv])
         p = topi.add(p, bias)
         p = topi.reshape(p, [batch_size * num_head, seq_len, seq_len_kv])
     s = topi.nn.softmax(p)
diff --git a/src/relax/op/nn/attention.cc b/src/relax/op/nn/attention.cc
index 7f0a1d3aea..56e5a04e12 100644
--- a/src/relax/op/nn/attention.cc
+++ b/src/relax/op/nn/attention.cc
@@ -85,23 +85,24 @@ StructInfo InferStructInfoAttention(const Call& call, const 
BlockBuilder& ctx) {
   if (input_sinfo.size() == 4) {
     TensorStructInfo bias_sinfo = input_sinfo[3];
     const ShapeExprNode* bias_shape = bias_sinfo->shape.as<ShapeExprNode>();
-    if (bias_sinfo->ndim == 4) {
-      diag_equal(num_batches, bias_shape->values[0], "query", "bias", "batch 
size");
-      diag_equal(num_heads, bias_shape->values[1], "query", "bias", "number of 
heads");
-      diag_equal(num_queries, bias_shape->values[2], "query", "bias", 
"sequence length");
-      diag_equal(num_keys, bias_shape->values[3], "key", "bias", "sequence 
length");
-    } else if (bias_sinfo->ndim == 3) {
-      diag_equal(num_batches, bias_shape->values[0], "query", "bias", "batch 
size");
-      diag_equal(num_queries, bias_shape->values[1], "query", "bias", 
"sequence length");
-      diag_equal(num_keys, bias_shape->values[2], "key", "bias", "sequence 
length");
-    } else if (bias_sinfo->ndim == 2) {
-      diag_equal(num_batches, bias_shape->values[0], "query", "bias", "batch 
size");
-      diag_equal(num_keys, bias_shape->values[1], "key", "bias", "sequence 
length");
-    } else {
+    if (bias_sinfo->ndim != 4) {
       ctx->ReportFatal(Diagnostic::Error(call)
-                       << "The bias should have 2, 3 or 4 dimensions."
+                       << "The bias should have 4 dimensions."
                        << "However, the bias input has " << bias_sinfo->ndim 
<< " dimensions.");
     }
+    auto diag_equal_or_broadcast = [&](PrimExpr v1, PrimExpr v2, String m1, 
String m2, String dim) {
+      if (analyzer->CanProve(v1 != v2) && !tir::is_one(v2)) {
+        ctx->ReportFatal(Diagnostic::Error(call)
+                         << "The " << m1 << " " << dim << " and the " << m2 << 
" " << dim
+                         << " should be the same or broadcastable. However, 
the " << dim << " of "
+                         << m1 << " is " << v1 << " while the " << dim << " of 
" << m2 << " is "
+                         << v2);
+      }
+    };
+    diag_equal_or_broadcast(num_batches, bias_shape->values[0], "query", 
"bias", "batch size");
+    diag_equal_or_broadcast(num_heads, bias_shape->values[1], "query", "bias", 
"number of heads");
+    diag_equal_or_broadcast(num_queries, bias_shape->values[2], "query", 
"bias", "sequence length");
+    diag_equal(num_keys, bias_shape->values[3], "key", "bias", "sequence 
length");
   }
 
   Array<PrimExpr> output_shape = {num_batches, num_queries, num_heads, 
head_dim_value};
diff --git a/tests/python/relax/test_codegen_cutlass.py 
b/tests/python/relax/test_codegen_cutlass.py
index 3d0cc3a54c..d5d3142cab 100644
--- a/tests/python/relax/test_codegen_cutlass.py
+++ b/tests/python/relax/test_codegen_cutlass.py
@@ -549,7 +549,7 @@ def get_relax_attention_module(q, k, v, bias=None, 
qk_scale=None):
 
 
 @memoize("topi.tests.test_codegen_cutlass.test_attention_offload")
-def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, bias_shape, bias_reshape, 
qk_scale, dtype):
+def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, bias_shape, qk_scale, 
dtype):
     q = np.random.randn(b, s, n, h).astype(dtype)
     k = np.random.randn(b, s_kv, n, h).astype(dtype)
     v = np.random.randn(b, s_kv, n, h_v).astype(dtype)
@@ -561,7 +561,7 @@ def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, 
bias_shape, bias_reshape, qk_
         score = qt @ kt / np.sqrt(q.shape[-1])  # b, n, s, s_kv
     if not bias_shape == "none":
         bias = np.random.randn(*bias_shape).astype(dtype)
-        score = score + bias.reshape(*bias_reshape)  # b, n, s, s_kv
+        score = score + bias  # b, n, s, s_kv
     else:
         bias = None
     attn = tvm.topi.testing.softmax_python(score, -1)
@@ -573,7 +573,7 @@ def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, 
bias_shape, bias_reshape, qk_
 def test_attention_offload(attention_size, attention_dtype):
     b, (s, s_kv), n, (h, h_v) = attention_size
     q, k, v, _, ref = get_numpy_attention_ref(
-        b, s, s_kv, n, h, h_v, "none", "none", "none", attention_dtype
+        b, s, s_kv, n, h, h_v, "none", "none", attention_dtype
     )
 
     mod = get_relax_attention_module(q, k, v)
@@ -584,10 +584,15 @@ def test_attention_offload(attention_size, 
attention_dtype):
 
 @pytest.fixture(
     params=[
-        # B, S, N, H, bias_shape, bias_reshape
-        (4, (16, 8), 32, (8, 16), (4, 32, 16, 8), (4, 32, 16, 8)),
-        (4, (16, 8), 32, (8, 16), (4, 16, 8), (4, 1, 16, 8)),
-        (4, (16, 8), 32, (8, 16), (4, 8), (4, 1, 1, 8)),
+        # B, S, N, H, bias_shape
+        (4, (16, 8), 32, (8, 16), (4, 32, 16, 8)),
+        (4, (16, 8), 32, (8, 16), (4, 1, 16, 8)),
+        (4, (16, 8), 32, (8, 16), (4, 32, 1, 8)),
+        (4, (16, 8), 32, (8, 16), (4, 1, 1, 8)),
+        (4, (16, 8), 32, (8, 16), (1, 32, 16, 8)),
+        (4, (16, 8), 32, (8, 16), (1, 1, 16, 8)),
+        (4, (16, 8), 32, (8, 16), (1, 32, 1, 8)),
+        (4, (16, 8), 32, (8, 16), (1, 1, 1, 8)),
     ]
 )
 def attention_bias_size(request):
@@ -595,9 +600,9 @@ def attention_bias_size(request):
 
 
 def test_attention_bias_offload(attention_bias_size):
-    b, (s, s_kv), n, (h, h_v), bias_shape, bias_reshape = attention_bias_size
+    b, (s, s_kv), n, (h, h_v), bias_shape = attention_bias_size
     q, k, v, bias, ref = get_numpy_attention_ref(
-        b, s, s_kv, n, h, h_v, bias_shape, bias_reshape, "none", "float32"
+        b, s, s_kv, n, h, h_v, bias_shape, "none", "float32"
     )
 
     mod = get_relax_attention_module(q, k, v, bias)
@@ -608,9 +613,9 @@ def test_attention_bias_offload(attention_bias_size):
 
 @pytest.fixture(
     params=[
-        # B, S, N, H, bias_shape, bias_reshape
-        (4, (16, 8), 32, (8, 16), (4, 32, 16, 8), (4, 32, 16, 8)),
-        (4, (16, 8), 32, (8, 16), "none", "none"),
+        # B, S, N, H, bias_shape
+        (4, (16, 8), 32, (8, 16), (4, 32, 16, 8)),
+        (4, (16, 8), 32, (8, 16), "none"),
     ]
 )
 def attention_scale_size(request):
@@ -623,9 +628,9 @@ def attention_scale(request):
 
 
 def test_attention_scale_offload(attention_scale_size, attention_scale):
-    b, (s, s_kv), n, (h, h_v), bias_shape, bias_reshape = attention_scale_size
+    b, (s, s_kv), n, (h, h_v), bias_shape = attention_scale_size
     q, k, v, bias, ref = get_numpy_attention_ref(
-        b, s, s_kv, n, h, h_v, bias_shape, bias_reshape, attention_scale, 
"float32"
+        b, s, s_kv, n, h, h_v, bias_shape, attention_scale, "float32"
     )
 
     mod = get_relax_attention_module(q, k, v, bias, attention_scale)
@@ -637,7 +642,7 @@ def test_attention_scale_offload(attention_scale_size, 
attention_scale):
 
 
 @memoize("topi.tests.test_codegen_cutlass.test_stacked_attention_offload")
-def get_numpy_stacked_attention_ref(b, s, n, h, h_v, bias_shape, bias_reshape, 
qk_scale, dtype):
+def get_numpy_stacked_attention_ref(b, s, n, h, h_v, bias_shape, qk_scale, 
dtype):
     qkv = np.random.randn(b, s, n * h + n * h + n * h_v).astype(dtype)
     split_qkv = np.split(qkv, [n * h, n * h * 2], axis=2)
     q = np.reshape(split_qkv[0], (b, s, n, h))
@@ -651,7 +656,7 @@ def get_numpy_stacked_attention_ref(b, s, n, h, h_v, 
bias_shape, bias_reshape, q
         score = qt @ kt / np.sqrt(q.shape[-1])  # b, n, s, s
     if not bias_shape == "none":
         bias = np.random.randn(*bias_shape).astype(dtype)
-        score = score + bias.reshape(*bias_reshape)  # b, n, s, s
+        score = score + bias  # b, n, s, s
     else:
         bias = None
     attn = tvm.topi.testing.softmax_python(score, -1)
@@ -709,10 +714,10 @@ def get_relax_stacked_attention_module(
 
 @pytest.fixture(
     params=[
-        # B, S, N, H, bias_shape, bias_reshape, scale, single_shape
-        (4, 8, 32, (64, 32), "none", "none", "none", False),
-        (4, 8, 32, (64, 32), (4, 32, 8, 8), (4, 32, 8, 8), 0.5, False),
-        (4, 8, 32, (64, 64), "none", "none", "none", True),
+        # B, S, N, H, bias_shape, scale, single_shape
+        (4, 8, 32, (64, 32), "none", "none", False),
+        (4, 8, 32, (64, 32), (4, 32, 8, 8), 0.5, False),
+        (4, 8, 32, (64, 64), "none", "none", True),
     ]
 )
 def stacked_attention_size(request):
@@ -720,10 +725,8 @@ def stacked_attention_size(request):
 
 
 def test_stacked_attention_split_offload(stacked_attention_size):
-    b, s, n, (h, h_v), bias_shape, bias_reshape, scale, single_shape = 
stacked_attention_size
-    qkv, bias, ref = get_numpy_stacked_attention_ref(
-        b, s, n, h, h_v, bias_shape, bias_reshape, scale, "float32"
-    )
+    b, s, n, (h, h_v), bias_shape, scale, single_shape = stacked_attention_size
+    qkv, bias, ref = get_numpy_stacked_attention_ref(b, s, n, h, h_v, 
bias_shape, scale, "float32")
     if scale == "none":
         mod = get_relax_stacked_attention_module(
             qkv, b, s, n, h, h_v, "split", bias, single_shape=single_shape
@@ -741,10 +744,8 @@ def 
test_stacked_attention_split_offload(stacked_attention_size):
 
 
 def test_stacked_attention_strided_slice_offload(stacked_attention_size):
-    b, s, n, (h, h_v), bias_shape, bias_reshape, scale, single_shape = 
stacked_attention_size
-    qkv, bias, ref = get_numpy_stacked_attention_ref(
-        b, s, n, h, h_v, bias_shape, bias_reshape, scale, "float32"
-    )
+    b, s, n, (h, h_v), bias_shape, scale, single_shape = stacked_attention_size
+    qkv, bias, ref = get_numpy_stacked_attention_ref(b, s, n, h, h_v, 
bias_shape, scale, "float32")
     if scale == "none":
         mod = get_relax_stacked_attention_module(
             qkv, b, s, n, h, h_v, "strided_slice", bias, 
single_shape=single_shape

Reply via email to