Files
paddlepaddle--paddle/test/legacy_test/test_block_multihead_attention_gqa.py
T
2026-07-13 12:40:42 +08:00

2930 lines
96 KiB
Python

# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
import numpy as np
from op_test import get_device_place, is_custom_device
from test_block_multihead_attention import (
RopeEmbedding,
block_cache_to_naive_cache,
create_attn_mask,
get_cuda_version,
get_padding_offset,
is_sm_supported,
remove_padding,
)
import paddle
from paddle import base
from paddle.framework import core
from paddle.incubate.nn.functional import block_multihead_attention
from paddle.static import Program, program_guard
paddle.seed(2024)
np.random.seed(2024)
def naive_attention_impl(
query,
key,
value,
cache_k=None,
cache_v=None,
pre_cache_k=None,
pre_cache_v=None,
mask=None,
scale=1.0,
cache_k_dequant_scales=None,
cache_v_dequant_scales=None,
use_cachekv_int8="None",
):
batch = query.shape[0]
heads = query.shape[1]
seq_len = query.shape[2]
head_dim = query.shape[3]
kv_head = key.shape[1]
key = key.reshape([batch, kv_head, 1, seq_len, head_dim])
key = paddle.tile(key, [1, 1, heads // kv_head, 1, 1])
key = key.reshape([batch, heads, seq_len, head_dim])
if use_cachekv_int8 == "dynamic":
unsqueeze_shape = [2, 3]
elif use_cachekv_int8 == "static":
unsqueeze_shape = [0, 2, 3]
if pre_cache_k is not None:
pre_cache_k = pre_cache_k.reshape([batch, kv_head, 1, -1, head_dim])
pre_cache_k = paddle.tile(pre_cache_k, [1, 1, heads // kv_head, 1, 1])
pre_cache_k = pre_cache_k.reshape([batch, heads, -1, head_dim])
key = paddle.concat([pre_cache_k, key], axis=2)
if cache_k is not None:
if cache_k_dequant_scales is not None:
dequant_cache_k = (
(cache_k.astype('float32') - 128.0)
* cache_k_dequant_scales.unsqueeze(unsqueeze_shape)
).astype(key.dtype)
dequant_cache_k = dequant_cache_k.reshape(
[batch, kv_head, 1, -1, head_dim]
)
dequant_cache_k = paddle.tile(
dequant_cache_k, [1, 1, heads // kv_head, 1, 1]
)
dequant_cache_k = dequant_cache_k.reshape(
[batch, heads, -1, head_dim]
)
key = paddle.concat([dequant_cache_k, key], axis=2)
else:
cache_k = cache_k.reshape([batch, kv_head, 1, -1, head_dim])
cache_k = paddle.tile(cache_k, [1, 1, heads // kv_head, 1, 1])
cache_k = cache_k.reshape([batch, heads, -1, head_dim])
key = paddle.concat([cache_k, key], axis=2)
value = value.reshape([batch, kv_head, 1, seq_len, head_dim])
value = paddle.tile(value, [1, 1, heads // kv_head, 1, 1])
value = value.reshape([batch, heads, seq_len, head_dim])
if pre_cache_v is not None:
pre_cache_v = pre_cache_v.reshape([batch, kv_head, 1, -1, head_dim])
pre_cache_v = paddle.tile(pre_cache_v, [1, 1, heads // kv_head, 1, 1])
pre_cache_v = pre_cache_v.reshape([batch, heads, -1, head_dim])
value = paddle.concat([pre_cache_v, value], axis=2)
if cache_v is not None:
if cache_v_dequant_scales is not None:
dequant_cache_v = (
(cache_v.astype('float32') - 128.0)
* cache_v_dequant_scales.unsqueeze(unsqueeze_shape)
).astype(value.dtype)
dequant_cache_v = dequant_cache_v.reshape(
[batch, kv_head, 1, -1, head_dim]
)
dequant_cache_v = paddle.tile(
dequant_cache_v, [1, 1, heads // kv_head, 1, 1]
)
dequant_cache_v = dequant_cache_v.reshape(
[batch, heads, -1, head_dim]
)
value = paddle.concat([dequant_cache_v, value], axis=2)
else:
cache_v = cache_v.reshape([batch, kv_head, 1, -1, head_dim])
cache_v = paddle.tile(cache_v, [1, 1, heads // kv_head, 1, 1])
cache_v = cache_v.reshape([batch, heads, -1, head_dim])
value = paddle.concat([cache_v, value], axis=2)
qk_res = paddle.matmul(query, key, transpose_y=True)
attention = qk_res * scale
if mask is not None:
attention = attention + mask
softmax_result = paddle.nn.functional.softmax(attention, -1)
result = paddle.matmul(softmax_result, value)
return result
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnEncDec(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.tgt_mask = paddle.randn(
[self.batch_size, self.q_num_head, 1, self.seq_len + 1],
dtype=self.dtype,
)
# self.tgt_mask = None
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=5e-03,
atol=1e-03,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
self.tgt_mask,
self.scale,
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
self.tgt_mask, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=7e-02,
atol=7e-02,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnEncDecSkipGetMaxLen(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDecSkipGetMaxLen"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.max_enc_len_this_time = paddle.to_tensor(
[self.seq_len], "int32"
).cpu()
self.max_dec_len_this_time = paddle.to_tensor([0], "int32").cpu()
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.tgt_mask = paddle.randn(
[self.batch_size, self.q_num_head, 1, self.seq_len + 1],
dtype=self.dtype,
)
# self.tgt_mask = None
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
self.max_enc_len_this_time, # max_enc_len_this_time
self.max_dec_len_this_time, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=5e-03,
atol=1e-03,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.max_enc_len_this_time = paddle.to_tensor([0], "int32").cpu()
self.max_dec_len_this_time = paddle.to_tensor(
[self.seq_len], "int32"
).cpu()
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
self.tgt_mask,
self.scale,
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
self.max_enc_len_this_time, # max_enc_len_this_time
self.max_dec_len_this_time, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
self.tgt_mask, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=5e-02,
atol=5e-02,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnRoPE(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnRoPE"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.rope = RopeEmbedding()
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def get_rotary_position_embedding(self, position_ids, head_dim):
bsz, max_seq_len = position_ids.shape[:2]
rot_emb = paddle.zeros(
(2, bsz, max_seq_len, 1, head_dim // 2), dtype="float32"
)
inv_freq = 10000 ** (
-paddle.arange(0, head_dim, 2, dtype="float32") / head_dim
)
# shape: [B, S, D/2]
freqs = paddle.einsum(
"ij,k->ijk", position_ids.cast("float32"), inv_freq
)
# shape: [B, S, D]
# emb = paddle.stack([freqs, freqs], axis=-1).reshape((bsz, max_seq_len, head_dim))
emb = paddle.stack([freqs], axis=-1).reshape(
(bsz, max_seq_len, head_dim // 2)
)
# shape: [B, S, 1, D]
emb = paddle.unsqueeze(emb, 2)
rot_emb[0] = paddle.cos(emb)
rot_emb[1] = paddle.sin(emb)
return rot_emb
def test_all(self):
paddle.disable_static()
tmp_position_ids = paddle.arange(
self.seq_len + self.max_dec_len
).reshape((1, -1))
self.rope_emb = self.get_rotary_position_embedding(
tmp_position_ids, self.dim_head
)
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
sinusoidal_pos = self.rope._rotary_position_embedding(
self.seq_len, self.dim_head, self.dtype
)
q, k = self.rope._apply_rope(sinusoidal_pos, q, k)
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
self.rope_emb, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=5e-03,
atol=1e-03,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
sinusoidal_pos = self.rope._rotary_position_embedding(
self.seq_len + 1, self.dim_head, self.dtype
)[:, :, -1:, :]
q, k = self.rope._apply_rope(sinusoidal_pos, q, k)
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
None,
self.scale,
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
self.rope_emb, # rotary_embs
None, # attn_mask
None, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=7e-02,
atol=7e-02,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnEncStatic(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncStatic"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
qkv_numpy = (
paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
)
.reshape([self.token_num, -1])
.numpy()
)
paddle.enable_static()
with program_guard(Program(), Program()):
qkv = paddle.static.data(
name="qkv",
shape=(self.token_num, 2 * self.kv_hid_dim + self.q_hid_dim),
dtype=self.dtype,
)
cache_k = paddle.static.data(
name="cache_k", shape=self.cache_shape, dtype=self.dtype
)
cache_v = paddle.static.data(
name="cache_v", shape=self.cache_shape, dtype=self.dtype
)
seq_lens_encoder = paddle.static.data(
name="seq_lens_encoder", shape=(self.batch_size,), dtype='int32'
)
seq_lens_decoder = paddle.static.data(
name="seq_lens_decoder", shape=(self.batch_size,), dtype='int32'
)
seq_lens_this_time = paddle.static.data(
name="seq_lens_this_time",
shape=(self.batch_size,),
dtype='int32',
)
cu_seqlens_q = paddle.static.data(
name="cu_seqlens_q", shape=(self.batch_size + 1,), dtype='int32'
)
cu_seqlens_k = paddle.static.data(
name="cu_seqlens_k", shape=(self.batch_size + 1,), dtype='int32'
)
padding_offsets = paddle.static.data(
name="padding_offset", shape=(self.token_num,), dtype='int32'
)
cum_offsets = paddle.static.data(
name="cum_offset", shape=(self.batch_size,), dtype='int32'
)
block_tables = paddle.static.data(
name="block_tables",
shape=(self.batch_size, self.block_num_per_seq),
dtype='int32',
)
out = block_multihead_attention(
qkv,
cache_k,
cache_v,
seq_lens_encoder,
seq_lens_decoder,
seq_lens_this_time,
padding_offsets,
cum_offsets,
cu_seqlens_q,
cu_seqlens_k,
block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style
)
exe = base.Executor()
res = exe.run(
feed={
"qkv": qkv_numpy,
"cache_k": self.cache_k.numpy(),
"cache_v": self.cache_v.numpy(),
"seq_lens_encoder": self.seq_lens_encoder.numpy(),
"seq_lens_decoder": self.seq_lens_decoder.numpy(),
"seq_lens_this_time": self.seq_lens_this_time.numpy(),
"cu_seqlens_q": self.cu_seqlens_q.numpy(),
"cu_seqlens_k": self.cu_seqlens_k.numpy(),
"padding_offset": self.padding_offset.numpy(),
"cum_offset": self.cum_offset.numpy(),
"block_tables": self.block_tables.numpy(),
},
fetch_list=[out],
)
paddle.disable_static()
np.testing.assert_allclose(
res[0],
out_.numpy(),
rtol=5e-03,
atol=1e-03,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnEncDecPTQDequant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDecPTQDequant"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.randint(-65535, 65535, self.q_shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.token_num, self.q_hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape([self.token_num, self.kv_hid_dim])
v = v.transpose([0, 2, 1, 3]).reshape([self.token_num, self.kv_hid_dim])
q_out_scale = 10.0 / paddle.max(q, axis=0).astype('float32')
k_out_scale = 10.0 / paddle.max(k, axis=0).astype('float32')
v_out_scale = 10.0 / paddle.max(v, axis=0).astype('float32')
qkv_out_scale = paddle.concat(
[q_out_scale, k_out_scale, v_out_scale], axis=0
)
q_bias = paddle.ones([self.q_hid_dim], dtype=self.dtype)
k_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype)
v_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype)
qkv_bias = paddle.concat([q_bias, k_bias, v_bias], axis=-1)
# dequant
q = (q.astype('float32') * q_out_scale).astype(self.dtype)
k = (k.astype('float32') * k_out_scale).astype(self.dtype)
v = (v.astype('float32') * v_out_scale).astype(self.dtype)
# add bias
q = q + q_bias
k = k + k_bias
v = v + v_bias
# transpose to origin
q = q.reshape(
[self.batch_size, self.seq_len, self.q_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, self.seq_len, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, self.seq_len, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
qkv_out_scale, # qkv_out_scale
qkv_bias, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
compute_dtype="fp16",
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=100,
atol=1,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.randint(-65535, 65535, self.q_shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.q_hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
)
v = v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
)
q_out_scale = 1.0 / paddle.max(q, axis=0).astype('float32')
k_out_scale = 1.0 / paddle.max(k, axis=0).astype('float32')
v_out_scale = 1.0 / paddle.max(v, axis=0).astype('float32')
qkv_out_scale = paddle.concat(
[q_out_scale, k_out_scale, v_out_scale], axis=0
)
q_bias = paddle.ones([self.q_hid_dim], dtype=self.dtype) * 0.1
k_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype) * 0.1
v_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype) * 0.1
qkv_bias = paddle.concat([q_bias, k_bias, v_bias], axis=-1)
# dequant
q = (q.astype('float32') * q_out_scale).astype(self.dtype)
k = (k.astype('float32') * k_out_scale).astype(self.dtype)
v = (v.astype('float32') * v_out_scale).astype(self.dtype)
# add bias
q = q + q_bias
k = k + k_bias
v = v + v_bias
# transpose to origin
q = q.reshape(
[self.batch_size, 1, self.q_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, 1, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, 1, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
None,
self.scale,
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
qkv_out_scale, # qkv_out_scale
qkv_bias, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
compute_dtype="fp16",
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=100,
atol=1,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
" and device's compute capability must be 7.x, 8.x or 9.x",
)
class TestBlockGroupQueryAttnEncDecPTQDequantQuantShiftSmooth(
unittest.TestCase
):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDecPTQDequantQuantShiftSmooth"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.randint(-65535, 65535, self.q_shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.token_num, self.q_hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape([self.token_num, self.kv_hid_dim])
v = v.transpose([0, 2, 1, 3]).reshape([self.token_num, self.kv_hid_dim])
q_out_scale = 1.0 / paddle.max(q, axis=0).astype('float32')
k_out_scale = 1.0 / paddle.max(k, axis=0).astype('float32')
v_out_scale = 1.0 / paddle.max(v, axis=0).astype('float32')
qkv_out_scale = paddle.concat(
[q_out_scale, k_out_scale, v_out_scale], axis=0
)
q_bias = paddle.ones([self.q_hid_dim], dtype=self.dtype)
k_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype)
v_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype)
qkv_bias = paddle.concat([q_bias, k_bias, v_bias], axis=-1)
# dequant
q = (q.astype('float32') * q_out_scale).astype(self.dtype)
k = (k.astype('float32') * k_out_scale).astype(self.dtype)
v = (v.astype('float32') * v_out_scale).astype(self.dtype)
# add bias
q = q + q_bias
k = k + k_bias
v = v + v_bias
# transpose to origin
q = q.reshape(
[self.batch_size, self.seq_len, self.q_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, self.seq_len, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, self.seq_len, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
# shift smooth
shift = np.random.random([self.q_num_head * self.dim_head])
shift = paddle.to_tensor(shift, dtype=self.dtype, place=self.place)
smooth = np.random.random([self.q_num_head * self.dim_head])
smooth = paddle.to_tensor(smooth, dtype=self.dtype, place=self.place)
out_ = (out_ + shift) * smooth
# quant
out_ *= 127.0
out_ = paddle.where(out_ <= -127, paddle.full_like(out_, -127), out_)
out_ = paddle.where(out_ >= 127, paddle.full_like(out_, 127), out_)
out_ = paddle.round(out_).astype('int8')
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
qkv_out_scale, # qkv_out_scale
qkv_bias, # qkv_bias
shift, # out_shift
smooth, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
compute_dtype="fp16",
out_scale=1.0,
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=1,
atol=1,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.randint(-65535, 65535, self.q_shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.kv_shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.q_hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
)
v = v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
)
q_out_scale = 1.0 / paddle.max(q, axis=0).astype('float32')
k_out_scale = 1.0 / paddle.max(k, axis=0).astype('float32')
v_out_scale = 1.0 / paddle.max(v, axis=0).astype('float32')
qkv_out_scale = paddle.concat(
[q_out_scale, k_out_scale, v_out_scale], axis=0
)
q_bias = paddle.ones([self.q_hid_dim], dtype=self.dtype) * 0.1
k_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype) * 0.1
v_bias = paddle.ones([self.kv_hid_dim], dtype=self.dtype) * 0.1
qkv_bias = paddle.concat([q_bias, k_bias, v_bias], axis=-1)
# dequant
q = (q.astype('float32') * q_out_scale).astype(self.dtype)
k = (k.astype('float32') * k_out_scale).astype(self.dtype)
v = (v.astype('float32') * v_out_scale).astype(self.dtype)
# add bias
q = q + q_bias
k = k + k_bias
v = v + v_bias
# transpose to origin
q = q.reshape(
[self.batch_size, 1, self.q_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, 1, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, 1, self.kv_num_head, self.dim_head]
).transpose([0, 2, 1, 3])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
None,
self.scale,
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
# shift smooth
shift = np.random.random([self.q_num_head * self.dim_head])
shift = paddle.to_tensor(shift, dtype=self.dtype, place=self.place)
smooth = np.random.random([self.q_num_head * self.dim_head])
smooth = paddle.to_tensor(smooth, dtype=self.dtype, place=self.place)
out_ = (out_ + shift) * smooth
# quant
out_ *= 127.0
out_ = paddle.where(out_ <= -127, paddle.full_like(out_, -127), out_)
out_ = paddle.where(out_ >= 127, paddle.full_like(out_, 127), out_)
out_ = paddle.round(out_).astype('int8')
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
qkv_out_scale, # qkv_out_scale
qkv_bias, # qkv_bias
shift, # out_shift
smooth, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
compute_dtype="fp16",
out_scale=1.0,
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=20,
atol=57,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
" and device's compute capability must be 7.x, 8.x or 9.x",
)
class TestBlockGroupQueryAttnEncDecQuant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDecQuant"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype=self.dtype)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
# batch_size * seq_len
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
# quant
out_ *= 127.0
out_ = paddle.where(out_ <= -127, paddle.full_like(out_, -127), out_)
out_ = paddle.where(out_ >= 127, paddle.full_like(out_, 127), out_)
out_ = paddle.round(out_).astype('int8')
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
out_scale=1.0,
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=0.1,
atol=1,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
None,
self.scale,
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
# quant
out_ *= 127.0
out_ = paddle.where(out_ <= -127, paddle.full_like(out_, -127), out_)
out_ = paddle.where(out_ >= 127, paddle.full_like(out_, 127), out_)
out_ = paddle.round(out_).astype('int8')
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
None, # cache_k_quant_scales
None, # cache_v_quant_scales
None, # cache_k_dequant_scales
None, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
out_scale=1.0,
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=0.1,
atol=1,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnEncDecCacheKVDynamicQuant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDecCacheKVDynamicQuant"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype='uint8')
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype='uint8')
self.cache_k_quant_scales = paddle.zeros(
shape=[self.batch_size, self.kv_num_head], dtype='float32'
)
self.cache_v_quant_scales = paddle.zeros(
shape=[self.batch_size, self.kv_num_head], dtype='float32'
)
self.cache_k_dequant_scales = paddle.zeros(
shape=[self.batch_size, self.kv_num_head], dtype='float32'
)
self.cache_v_dequant_scales = paddle.zeros(
shape=[self.batch_size, self.kv_num_head], dtype='float32'
)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
self.cache_k_quant_scales, # cache_k_quant_scales
self.cache_v_quant_scales, # cache_v_quant_scales
self.cache_k_dequant_scales, # cache_k_dequant_scales
self.cache_v_dequant_scales, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
use_dynamic_cachekv_quant=True,
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=0.1,
atol=1,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
None,
self.scale,
cache_k_dequant_scales=self.cache_k_dequant_scales,
cache_v_dequant_scales=self.cache_v_dequant_scales,
use_cachekv_int8="dynamic",
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
# quant
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
self.cache_k_quant_scales, # cache_k_quant_scales
self.cache_v_quant_scales, # cache_v_quant_scales
self.cache_k_dequant_scales, # cache_k_dequant_scales
self.cache_v_dequant_scales, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
use_dynamic_cachekv_quant=True,
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=0.1,
atol=1,
)
@unittest.skipIf(
not (core.is_compiled_with_cuda() or is_custom_device())
or get_cuda_version() < 11040
or not is_sm_supported,
"core is not compiled with CUDA and cuda version need larger than or equal to 11.4"
"and device's compute capability must be 8.x or 90",
)
class TestBlockGroupQueryAttnEncDecCacheKVStaticQuant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockGroupQueryAttnEncDecCacheKVStaticQuant"
self.place = get_device_place()
self.batch_size = 2
self.q_num_head = 8
self.kv_num_head = 2
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.q_hid_dim = self.q_num_head * self.dim_head
self.kv_hid_dim = self.kv_num_head * self.dim_head
self.blocksize = 64
self.block_num_per_seq = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
self.max_block_num = self.block_num_per_seq * self.batch_size
self.free_list = list(range(self.max_block_num - 1, -1, -1))
self.seq_lens_encoder = paddle.to_tensor(
[
self.seq_len,
]
* self.batch_size,
"int32",
)
self.seq_lens_decoder = paddle.to_tensor(
[
0,
]
* self.batch_size,
"int32",
)
self.seq_lens_this_time = self.seq_lens_encoder
self.q_shape = (
self.batch_size,
self.q_num_head,
self.seq_len,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.kv_num_head,
self.blocksize,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
)
self.scale = 1.0 / np.sqrt(self.q_shape[-1])
self.cache_k = paddle.zeros(shape=self.cache_shape, dtype='uint8')
self.cache_v = paddle.zeros(shape=self.cache_shape, dtype='uint8')
self.cache_k_quant_scales = paddle.zeros(
shape=[self.kv_num_head], dtype='float32'
)
self.cache_v_quant_scales = paddle.zeros(
shape=[self.kv_num_head], dtype='float32'
)
self.cache_k_dequant_scales = paddle.zeros(
shape=[self.kv_num_head], dtype='float32'
)
self.cache_v_dequant_scales = paddle.zeros(
shape=[self.kv_num_head], dtype='float32'
)
self.block_tables = paddle.zeros(
shape=(self.batch_size, self.block_num_per_seq), dtype="int32"
)
for i in range(self.batch_size):
need_block_num = (
self.seq_len + self.max_dec_len + self.blocksize - 1
) // self.blocksize
for j in range(need_block_num):
self.block_tables[i, j] = self.free_list.pop()
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(
self.batch_size, self.seq_len, self.seq_lens_this_time
)
self.token_num = self.padding_offset.shape[0]
def test_all(self):
paddle.disable_static()
# encoder
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
out_ = naive_attention_impl(
q, k, v, None, None, None, None, self.attention_mask, self.scale
)
out_ = remove_padding(
self.seq_lens_this_time, self.cu_seqlens_q, out_, self.token_num
)
self.cache_k_quant_scales = (
127.0 / paddle.max(k, axis=[0, 2, 3])
).astype("float32")
self.cache_v_quant_scales = (
127.0 / paddle.max(k, axis=[0, 2, 3])
).astype("float32")
self.cache_k_dequant_scales = 1.0 / self.cache_k_quant_scales
self.cache_v_dequant_scales = 1.0 / self.cache_v_quant_scales
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
self.cache_k_quant_scales, # cache_k_quant_scales
self.cache_v_quant_scales, # cache_v_quant_scales
self.cache_k_dequant_scales, # cache_k_dequant_scales
self.cache_v_dequant_scales, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
self.seq_len,
self.blocksize,
False, # use_neox_rotary_style,
use_dynamic_cachekv_quant=False,
)[0]
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=0.1,
atol=1,
)
# decoder
naive_cache_k, naive_cache_v = block_cache_to_naive_cache(
self.cache_k,
self.cache_v,
self.batch_size,
self.block_tables,
self.seq_len,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.q_shape = (
self.batch_size,
self.q_num_head,
1,
self.dim_head,
)
self.kv_shape = (
self.batch_size,
self.kv_num_head,
1,
self.dim_head,
)
query = np.random.random(self.q_shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.kv_shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.kv_shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.concat(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.q_hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.kv_hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
(
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
) = get_padding_offset(self.batch_size, 1, self.seq_lens_this_time)
out_ = (
naive_attention_impl(
q,
k,
v,
naive_cache_k,
naive_cache_v,
None,
None,
None,
self.scale,
cache_k_dequant_scales=self.cache_k_dequant_scales,
cache_v_dequant_scales=self.cache_v_dequant_scales,
use_cachekv_int8="static",
)
.transpose([0, 2, 1, 3])
.reshape([self.batch_size, -1])
)
out = block_multihead_attention(
qkv,
self.cache_k,
self.cache_v,
self.seq_lens_encoder,
self.seq_lens_decoder,
self.seq_lens_this_time,
self.padding_offset,
self.cum_offset,
self.cu_seqlens_q,
self.cu_seqlens_k,
self.block_tables,
None, # pre_key_cache
None, # pre_value_cache
self.cache_k_quant_scales, # cache_k_quant_scales
self.cache_v_quant_scales, # cache_v_quant_scales
self.cache_k_dequant_scales, # cache_k_dequant_scales
self.cache_v_dequant_scales, # cache_v_dequant_scales
None, # qkv_out_scale
None, # qkv_bias
None, # out_shift
None, # out_smooth
None, # max_enc_len_this_time
None, # max_dec_len_this_time
None, # rotary_embs
None, # attn_mask
None, # tgt_mask
1, # seq_len,
self.blocksize,
False, # use_neox_rotary_style
use_dynamic_cachekv_quant=False,
)[0]
# NOTE: The diff of decoder is a little big
np.testing.assert_allclose(
out.numpy(),
out_.numpy(),
rtol=0.1,
atol=1,
)
if __name__ == '__main__':
unittest.main()