159 lines
6.7 KiB
Python
159 lines
6.7 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.
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
from paddle import _C_ops
|
|
from paddle.base.layer_helper import LayerHelper
|
|
from paddle.framework import in_dynamic_or_pir_mode
|
|
|
|
if TYPE_CHECKING:
|
|
from paddle import Tensor
|
|
|
|
|
|
def fused_rotary_position_embedding(
|
|
q: Tensor,
|
|
k: Tensor | None = None,
|
|
v: Tensor | None = None,
|
|
sin: Tensor | None = None,
|
|
cos: Tensor | None = None,
|
|
position_ids: Tensor | None = None,
|
|
use_neox_rotary_style: bool = True,
|
|
time_major: bool = False,
|
|
rotary_emb_base: float = 10000.0,
|
|
) -> tuple[Tensor, Tensor, Tensor]:
|
|
r"""
|
|
Fused rotary position embedding.
|
|
|
|
Args:
|
|
q (Tensor): The input tensor. The data type is bfloat16, float16, float32 or float64. The shape of q must be [batch_size, seq_len, num_heads, head_dim] or [seq_len, batch_size, num_heads, head_dim] and head_dim must be a multiple of 2.
|
|
k (Tensor, optional): The input tensor. The data type is bfloat16, float16, float32 or float64. The shape of k must be [batch_size, seq_len, num_heads, head_dim] or [seq_len, batch_size, num_heads, head_dim] and head_dim must be a multiple of 2.
|
|
v (Tensor, optional): The input tensor. The data type is bfloat16, float16, float32 or float64. The shape of v must be [batch_size, seq_len, num_heads, head_dim] or [seq_len, batch_size, num_heads, head_dim] and head_dim must be a multiple of 2.
|
|
sin (Tensor, optional): The input tensor. The data type is bfloat16, float16, float32 or float64. The shape of sin must be [seq_len, head_dim] or [1, seq_len, 1, head_dim] and head_dim must be a multiple of 2.
|
|
cos (Tensor, optional): The input tensor. The data type is bfloat16, float16, float32 or float64. The shape of cos must be [seq_len, head_dim] or [1, seq_len, 1, head_dim] and head_dim must be a multiple of 2.
|
|
position_ids (Tensor, optional): The input tensor. The data type is int64. The shape of position_ids must be [batch_size, seq_len].
|
|
use_neox_rotary_style(optional|bool): When the use_neox_rotary_style is True, every two adjacent numbers are calculated. When the use_neox_rotary_style is False, the numbers corresponding to the positions of the front half and back half segments are calculated. Default True.
|
|
time_major(optional|bool): Whether the first dimension of the q, k, v input means the time steps. If time_major is True, the shape of Tensor is [seq_len, batch_size, num_heads, head_dim], otherwise [batch_size, seq_len, num_heads, head_dime]. Defaults to False. `time_steps` means the length of input sequence.
|
|
rotary_emb_base(optional|float): the base of the rotary embedding. Default 10000.
|
|
|
|
Returns:
|
|
out_q/out_k/out_v Tensor representing the fused rotary position embedding, has same shape and data type as `q` .
|
|
|
|
|
|
Examples:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> # doctest: +REQUIRES(env:GPU)
|
|
>>> import paddle
|
|
>>> from paddle.incubate.nn.functional import fused_rotary_position_embedding
|
|
|
|
>>> paddle.set_device('gpu')
|
|
|
|
>>> # batch_size = 2
|
|
>>> # seq_len = 2
|
|
>>> # num_heads = 2
|
|
>>> # head_dim = 2
|
|
|
|
>>> paddle.seed(1204)
|
|
|
|
>>> # q, k, v: [batch_size, seq_len, num_heads, head_dim]
|
|
>>> q = paddle.randn([2, 2, 2, 2], dtype='float16')
|
|
>>> k = paddle.randn([2, 2, 2, 2], dtype='float16')
|
|
>>> v = paddle.randn([2, 2, 2, 2], dtype='float16')
|
|
|
|
>>> # sin, cos: [1, seq_len, 1, head_dim]
|
|
>>> x = paddle.randn([1, 2, 1, 2], dtype='float16')
|
|
>>> y = paddle.randn([1, 2, 1, 2], dtype='float16')
|
|
>>> sin = paddle.sin(x)
|
|
>>> cos = paddle.cos(y)
|
|
|
|
>>> # position_ids: [batch_size, seq_len]
|
|
>>> position_ids = paddle.randint(high=2, size=[2, 2], dtype='int64')
|
|
|
|
>>> # out_q, out_k, out_v: [batch_size, seq_len, num_heads, head_dim]
|
|
>>> out_q, out_k, out_v = fused_rotary_position_embedding(
|
|
... q, k, v, sin=sin, cos=cos, position_ids=position_ids, use_neox_rotary_style=False
|
|
... )
|
|
>>> print(out_q)
|
|
>>> # doctest: +SKIP("Random output")
|
|
Tensor(shape=[2, 2, 2, 2], dtype=float16, place=Place(gpu:0), stop_gradient=True,
|
|
[[[[-0.54931641, 0.64990234],
|
|
[-1.08691406, 1.18261719]],
|
|
[[ 0.57812500, 0.11749268],
|
|
[-0.63281250, 0.15551758]]],
|
|
[[[-0.77050781, 0.07733154],
|
|
[-0.73730469, -0.16735840]],
|
|
[[ 0.07116699, -0.90966797],
|
|
[-0.03628540, -0.20202637]]]])
|
|
>>> # doctest: -SKIP
|
|
"""
|
|
if (sin is None) or (cos is None):
|
|
assert position_ids is None, (
|
|
"position_ids without sin/cos is not correctly supported now."
|
|
)
|
|
assert use_neox_rotary_style, (
|
|
"rotate_half without sin/cos is not correctly supported now."
|
|
)
|
|
|
|
if in_dynamic_or_pir_mode():
|
|
return _C_ops.fused_rotary_position_embedding(
|
|
q,
|
|
k,
|
|
v,
|
|
sin,
|
|
cos,
|
|
position_ids,
|
|
use_neox_rotary_style,
|
|
time_major,
|
|
rotary_emb_base,
|
|
)
|
|
|
|
helper = LayerHelper('fused_rotary_position_embedding', **locals())
|
|
out_q = helper.create_variable_for_type_inference(dtype=q.dtype)
|
|
out_k = (
|
|
helper.create_variable_for_type_inference(dtype=k.dtype) if k else None
|
|
)
|
|
out_v = (
|
|
helper.create_variable_for_type_inference(dtype=v.dtype) if v else None
|
|
)
|
|
|
|
outputs = {'out_q': out_q}
|
|
if out_k:
|
|
outputs.update({'out_k': out_k})
|
|
if out_v:
|
|
outputs.update({'out_v': out_v})
|
|
|
|
helper.append_op(
|
|
type='fused_rotary_position_embedding',
|
|
inputs={
|
|
'q': q,
|
|
'k': k,
|
|
'v': v,
|
|
'sin': sin,
|
|
'cos': cos,
|
|
'position_ids': position_ids,
|
|
},
|
|
outputs=outputs,
|
|
attrs={
|
|
'use_neox_rotary_style': use_neox_rotary_style,
|
|
'time_major': time_major,
|
|
'rotary_emb_base': rotary_emb_base,
|
|
},
|
|
)
|
|
|
|
return out_q, out_k, out_v
|