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

203 lines
6.6 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
import paddle.nn.functional as F
from paddle.device import core
from paddle.nn.functional import scaled_dot_product_attention
from paddle.nn.functional.flash_attention import (
flash_attention,
)
def attention_naive(q, k, v, causal=False):
qt = paddle.transpose(q, [0, 2, 1, 3])
kt = paddle.transpose(k, [0, 2, 1, 3])
vt = paddle.transpose(v, [0, 2, 1, 3])
scale = 1.0 / np.sqrt(q.shape[-1])
s = paddle.matmul(qt * scale, paddle.transpose(kt, [0, 1, 3, 2]))
p = (
paddle.incubate.softmax_mask_fuse_upper_triangle(s)
if causal
else F.softmax(s)
)
o = paddle.matmul(p, vt)
return paddle.transpose(o, [0, 2, 1, 3])
is_sm80 = (
(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_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_sm90 = (
(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_sm_supported = is_sm8x or is_sm90
@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 TestFlashAttentionAPIFlag(unittest.TestCase):
def setUp(self):
self.place = get_device_place()
self.shape = (2, 128, 8, 16)
self.dtype = 'float16'
self.dropout = 0.0
self.causal = False
self.return_softmax = False
self.use_sdp_kernel = False
self.use_sdp_api = False
def flash_attn_compute(self, query, key, value):
# test dynamic
paddle.disable_static()
q = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
k = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
v = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
q_ = paddle.to_tensor(
query, place=self.place, dtype=self.dtype, stop_gradient=False
)
k_ = paddle.to_tensor(
key, place=self.place, dtype=self.dtype, stop_gradient=False
)
v_ = paddle.to_tensor(
value, place=self.place, dtype=self.dtype, stop_gradient=False
)
if self.use_sdp_kernel:
with paddle.nn.functional.sdp_kernel(
enable_math=self.enable_math,
enable_flash=self.enable_flash,
enable_mem_efficient=self.enable_mem_efficient,
):
if self.use_sdp_api:
out = scaled_dot_product_attention(
q, k, v, None, self.dropout, self.causal
)
else:
out, _ = flash_attention(
q, k, v, self.dropout, self.causal, self.return_softmax
)
else:
out, _ = flash_attention(
q, k, v, self.dropout, self.causal, self.return_softmax
)
out_ = attention_naive(q_, k_, v_, self.causal)
out.backward()
out_.backward()
self.assertEqual(q.grad.shape, q.shape)
self.assertEqual(q_.grad.shape, q.shape)
np.testing.assert_allclose(
q.grad.numpy(), q_.grad.numpy(), rtol=5e-03, atol=2e-03
)
return out, out_, q.grad.numpy(), k.grad.numpy(), v.grad.numpy()
def test_all_flag(self):
paddle.set_flags({'FLAGS_cudnn_deterministic': 1})
query = np.random.random(self.shape)
key = np.random.random(self.shape)
value = np.random.random(self.shape)
out1, out1_, q_grad1, k_grad1, v_grad1 = self.flash_attn_compute(
query, key, value
)
np.testing.assert_allclose(out1.numpy(), out1_, rtol=5e-03, atol=1e-03)
out2, out2_, q_grad2, k_grad2, v_grad2 = self.flash_attn_compute(
query, key, value
)
self.assertTrue(np.equal(out1.numpy(), out2.numpy()).all())
self.assertTrue(np.equal(q_grad1, q_grad2).all())
self.assertTrue(np.equal(k_grad1, k_grad2).all())
self.assertTrue(np.equal(v_grad1, v_grad2).all())
paddle.set_flags({'FLAGS_cudnn_deterministic': 0})
class TestFlashAttentionAPIFlagTest1(TestFlashAttentionAPIFlag):
def setUp(self):
self.place = get_device_place()
self.shape = (2, 128, 8, 16)
self.dtype = paddle.float16
self.dropout = 0.0
self.causal = False
self.return_softmax = False
self.use_sdp_kernel = False
class TestFlashAttentionAPIFlagTest2(TestFlashAttentionAPIFlag):
def setUp(self):
self.place = get_device_place()
# Flash attention backward kernel only supports SM80 or SM90 for head dimension > 192
self.shape = (
(8, 1024, 16, 256) if (is_sm80 or is_sm90) else (8, 1024, 16, 192)
)
self.dtype = paddle.float16
self.dropout = 0.0
self.causal = False
self.return_softmax = False
self.use_sdp_kernel = False
class TestSDPAttentionAPIFlagTest(TestFlashAttentionAPIFlag):
def setUp(self):
self.place = get_device_place()
self.shape = (8, 1024, 16, 128)
self.dtype = paddle.float16
self.dropout = 0.0
self.causal = False
self.return_softmax = False
self.use_sdp_kernel = True
self.use_sdp_api = True
self.enable_math = True
self.enable_flash = False
self.enable_mem_efficient = False
if __name__ == '__main__':
unittest.main()