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

3197 lines
105 KiB
Python

# Copyright (c) 2023 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_cuda_version, get_device_place, is_custom_device
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(2023)
np.random.seed(2023)
is_sm8x = (
(core.is_compiled_with_cuda() or is_custom_device())
and paddle.device.cuda.get_device_capability()[0] == 8
and paddle.device.cuda.get_device_capability()[1] >= 0
)
is_sm9x = (
(core.is_compiled_with_cuda() or is_custom_device())
and paddle.device.cuda.get_device_capability()[0] == 9
and paddle.device.cuda.get_device_capability()[1] >= 0
)
is_sm7x = (
(core.is_compiled_with_cuda() or is_custom_device())
and paddle.device.cuda.get_device_capability()[0] == 7
and paddle.device.cuda.get_device_capability()[1] >= 0
)
is_sm_supported = is_sm8x or is_sm9x or is_sm7x
def create_attn_mask(
mask_type,
batch_size,
seq_lens,
pre_cache_length=0,
):
max_seq_len = max(seq_lens)
mask = paddle.zeros(
[batch_size, 1, max_seq_len, max_seq_len + pre_cache_length],
dtype=mask_type,
)
mask[:, :, :, :pre_cache_length] = 1
for i in range(batch_size):
seq_len = seq_lens[i]
mask[i, 0, :seq_len, :seq_len] = (
paddle.tril(paddle.ones(shape=(seq_len, seq_len), dtype=mask_type))
- 1
) * 1e4
return mask
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:
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)
key = paddle.concat([dequant_cache_k, key], axis=2)
else:
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:
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)
value = paddle.concat([dequant_cache_v, value], axis=2)
else:
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
def get_padding_offset(bsz, max_seq_len, seq_lens_this_time):
cum_offsets_now = paddle.cumsum(max_seq_len - seq_lens_this_time)
cum_offsets = paddle.zeros(shape=(bsz + 1), dtype="int32")
cum_offsets[1:] = cum_offsets_now
token_num = paddle.sum(seq_lens_this_time)
padding_offsets = paddle.zeros(shape=(token_num), dtype="int32")
cu_seqlens_q = paddle.zeros(shape=(bsz + 1), dtype="int32")
cu_seqlens_k = paddle.zeros(shape=(bsz + 1), dtype="int32")
for i in range(bsz):
seq_len_now = seq_lens_this_time[i]
cum_offset = cum_offsets[i]
for j in range(seq_len_now):
padding_offsets[i * max_seq_len - cum_offset + j] = cum_offset
cum_seq_len = (i + 1) * max_seq_len - cum_offsets[i + 1]
cu_seqlens_q[i + 1] = cum_seq_len
cu_seqlens_k[i + 1] = cum_seq_len
return padding_offsets, cum_offsets[:-1], cu_seqlens_q, cu_seqlens_k
class RopeEmbedding:
def _rotary_position_embedding(self, seq_len, head_dim, dtype):
pos_seq = paddle.arange(0, seq_len, 1, dtype=dtype)
indices = paddle.arange(0, head_dim, 2, dtype=dtype)
indices = 1 / 10000 ** (indices / head_dim)
sinusoid_inp = pos_seq.unsqueeze(1) * indices.unsqueeze(0)
pos_emb = paddle.concat(
[paddle.sin(sinusoid_inp), paddle.cos(sinusoid_inp)], axis=-1
)
pos_emb = paddle.reshape(pos_emb, (1, 1, seq_len, head_dim))
pos_emb.stop_gradient = True
return pos_emb
def _apply_rope(self, rp, q, k, v=None):
# sin [sequence_length, embed_size_per_head//2]
# cos [sequence_length, embed_size_per_head//2]
sin, cos = paddle.chunk(rp, 2, axis=-1)
# sin [θ0,θ1,θ2......θd/2-1] -> sin_pos [θ0,θ0,θ1,θ1,θ2,θ2......θd/2-1,θd/2-1]
sin_pos = paddle.reshape(paddle.stack([sin, sin], axis=-1), rp.shape)
# cos [θ0,θ1,θ2......θd/2-1] -> cos_pos [θ0,θ0,θ1,θ1,θ2,θ2......θd/2-1,θd/2-1]
cos_pos = paddle.reshape(paddle.stack([cos, cos], axis=-1), rp.shape)
# rotate_half_query_layer [-q1,q0,-q3,q2......,-qd-1,qd-2]
rotate_half_q = paddle.reshape(
paddle.stack([-q[:, :, :, 1::2], q[:, :, :, 0::2]], axis=-1),
paddle.shape(q),
)
query = paddle.add(
paddle.multiply(q, cos_pos), paddle.multiply(rotate_half_q, sin_pos)
)
# rotate_half_key_layer [-k1,k0,-k3,k2......,-kd-1,kd-2]
rotate_half_k = paddle.reshape(
paddle.stack([-k[:, :, :, 1::2], k[:, :, :, 0::2]], axis=-1),
paddle.shape(k),
)
key = paddle.add(
paddle.multiply(k, cos_pos), paddle.multiply(rotate_half_k, sin_pos)
)
if v is not None:
# rotate_half_value_layer [-v1,v0,-v3,v2......,-vd-1,vd-2]
rotate_half_v = paddle.reshape(
paddle.stack([-v[:, :, :, 1::2], v[:, :, :, 0::2]], axis=-1),
paddle.shape(v),
)
value = paddle.add(
paddle.multiply(v, cos_pos),
paddle.multiply(rotate_half_v, sin_pos),
)
return query, key, value
return query, key
def remove_padding(seq_lens, cu_seq_lens, inputs, token_num):
bsz, num_head, seq_len, dim_head = inputs.shape
output = paddle.zeros(
shape=[token_num, num_head * dim_head], dtype=inputs.dtype
)
inputs = inputs.transpose([0, 2, 1, 3]).reshape([bsz, seq_len, -1])
for i in range(bsz):
seq_len_now = seq_lens[i]
start_idx = cu_seq_lens[i]
end_idx = cu_seq_lens[i + 1]
output[start_idx:end_idx, :] = inputs[i, :seq_len_now, :]
return output
def block_cache_to_naive_cache(
cache_k, cache_v, bsz, block_tables, cache_seq_len
):
_, num_head, blocksize, dim_head = cache_k.shape
out_cache_k = paddle.zeros(
shape=[bsz, num_head, cache_seq_len, dim_head], dtype=cache_k.dtype
)
out_cache_v = paddle.zeros(
shape=[bsz, num_head, cache_seq_len, dim_head], dtype=cache_v.dtype
)
for i in range(bsz):
for j in range(cache_seq_len):
out_cache_k[i, :, j, :] = cache_k[
block_tables[i, j // blocksize], :, j % blocksize, :
]
out_cache_v[i, :, j, :] = cache_v[
block_tables[i, j // blocksize], :, j % blocksize, :
]
return out_cache_k, out_cache_v
@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 TestBlockMultiHeadAttnEncDec(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.num_head, 1, self.seq_len + 1],
dtype=self.dtype,
)
self.scale = 1.0 / np.sqrt(self.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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=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 TestBlockMultiHeadAttnEncDecSkipGetMaxLen(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDecSkipGetMaxLen"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.num_head, 1, self.seq_len + 1],
dtype=self.dtype,
)
self.scale = 1.0 / np.sqrt(self.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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 TestBlockMultiHeadAttnRoPE(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnRoPE"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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=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 TestBlockMultiHeadAttnPreCache(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnPreCacbe"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.num_head * self.dim_head
self.blocksize = 64
self.pre_cache_length = 64
self.max_seq_len = self.seq_len + self.pre_cache_length
self.block_num_per_seq = (
self.max_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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.num_head,
self.blocksize,
self.dim_head,
)
self.pre_cache_shape = (
self.batch_size,
self.num_head,
self.pre_cache_length,
self.dim_head,
)
self.dtype = 'float16'
self.attention_mask = create_attn_mask(
self.dtype,
self.batch_size,
[
self.seq_len,
]
* self.batch_size,
self.pre_cache_length,
)
self.scale = 1.0 / np.sqrt(self.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.pre_cache_k = paddle.randn(
shape=self.pre_cache_shape, dtype=self.dtype
)
self.pre_cache_v = paddle.randn(
shape=self.pre_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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
out_ = naive_attention_impl(
q,
k,
v,
None,
None,
self.pre_cache_k,
self.pre_cache_v,
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,
self.pre_cache_k, # pre_key_cache
self.pre_cache_v, # 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
self.attention_mask, # 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.pre_cache_length,
)
self.seq_lens_decoder[:] = self.seq_lens_encoder
self.seq_lens_encoder[:] = 0
self.seq_lens_this_time[:] = 1
self.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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])
)
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,
self.pre_cache_k, # pre_key_cache
self.pre_cache_v, # 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
self.attention_mask, # 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=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 TestBlockMultiHeadAttnEncStatic(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncStatic"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.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.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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, 3 * self.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 TestBlockMultiHeadAttnEncDecPTQDequant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.token_num, self.hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape([self.token_num, self.hid_dim])
v = v.transpose([0, 2, 1, 3]).reshape([self.token_num, self.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.hid_dim], dtype=self.dtype)
k_bias = paddle.ones([self.hid_dim], dtype=self.dtype)
v_bias = paddle.ones([self.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.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, self.seq_len, self.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, self.seq_len, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.randint(-65535, 65535, self.shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.hid_dim])
v = v.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.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.hid_dim], dtype=self.dtype) * 0.1
k_bias = paddle.ones([self.hid_dim], dtype=self.dtype) * 0.1
v_bias = paddle.ones([self.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.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, 1, self.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, 1, self.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 TestBlockMultiHeadAttnEncDecPTQDequantQuantShiftSmooth(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
],
axis=1,
).reshape([self.token_num, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.token_num, self.hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape([self.token_num, self.hid_dim])
v = v.transpose([0, 2, 1, 3]).reshape([self.token_num, self.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.hid_dim], dtype=self.dtype)
k_bias = paddle.ones([self.hid_dim], dtype=self.dtype)
v_bias = paddle.ones([self.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.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, self.seq_len, self.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, self.seq_len, self.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.num_head * self.dim_head])
shift = paddle.to_tensor(shift, dtype=self.dtype, place=self.place)
smooth = np.random.random([self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.randint(-65535, 65535, self.shape, 'int32')
q = paddle.to_tensor(
query, place=self.place, dtype='int32', stop_gradient=False
)
key = np.random.randint(-65535, 65535, self.shape, 'int32')
k = paddle.to_tensor(
key, place=self.place, dtype='int32', stop_gradient=False
)
value = np.random.randint(-65535, 65535, self.shape, 'int32')
v = paddle.to_tensor(
value, place=self.place, dtype='int32', stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
],
axis=1,
).reshape([self.batch_size, -1])
q = q.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.hid_dim])
k = k.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.hid_dim])
v = v.transpose([0, 2, 1, 3]).reshape([self.batch_size, self.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.hid_dim], dtype=self.dtype) * 0.1
k_bias = paddle.ones([self.hid_dim], dtype=self.dtype) * 0.1
v_bias = paddle.ones([self.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.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
k = k.reshape(
[self.batch_size, 1, self.num_head, self.dim_head]
).transpose([0, 2, 1, 3])
v = v.reshape(
[self.batch_size, 1, self.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.num_head * self.dim_head])
shift = paddle.to_tensor(shift, dtype=self.dtype, place=self.place)
smooth = np.random.random([self.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 TestBlockMultiHeadAttnEncDecQuant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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 TestBlockMultiHeadAttnEncDecCacheKVDynamicQuant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.num_head], dtype='float32'
)
self.cache_v_quant_scales = paddle.zeros(
shape=[self.batch_size, self.num_head], dtype='float32'
)
self.cache_k_dequant_scales = paddle.zeros(
shape=[self.batch_size, self.num_head], dtype='float32'
)
self.cache_v_dequant_scales = paddle.zeros(
shape=[self.batch_size, self.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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 TestBlockMultiHeadAttnEncDecCacheKVStaticQuant(unittest.TestCase):
def setUp(self):
paddle.disable_static()
self.name = "TestBlockMultiHeadAttnEncDec"
self.place = get_device_place()
self.batch_size = 2
self.num_head = 8
self.seq_len = 64
self.max_dec_len = 64
self.dim_head = 64
self.hid_dim = self.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.shape = (
self.batch_size,
self.num_head,
self.seq_len,
self.dim_head,
)
self.cache_shape = (
self.max_block_num,
self.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.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.num_head], dtype='float32'
)
self.cache_v_quant_scales = paddle.zeros(
shape=[self.num_head], dtype='float32'
)
self.cache_k_dequant_scales = paddle.zeros(
shape=[self.num_head], dtype='float32'
)
self.cache_v_dequant_scales = paddle.zeros(
shape=[self.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.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.token_num, self.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.shape = (
self.batch_size,
self.num_head,
1,
self.dim_head,
)
query = np.random.random(self.shape)
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
key = np.random.random(self.shape)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
value = np.random.random(self.shape)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
qkv = paddle.stack(
[
q.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
k.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.hid_dim]
),
v.transpose([0, 2, 1, 3]).reshape(
[self.batch_size, self.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()