Files
2026-07-13 13:18:33 +08:00

308 lines
15 KiB
Python

# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
# DeepSpeed Team
from enum import IntEnum
from .builder import NPUOpBuilder
try:
import torch
import torch_npu
except ImportError as e:
pass
class ActivationFuncType(IntEnum):
UNKNOWN = 0
GELU = 1
ReLU = 2
GATED_GELU = 3
GATED_SILU = 4
class InferenceContext:
_workspace = None
_seed = 42
_curr_offset = 0
_stream = 0
_free_memory_size = 0
_num_tokens = 1
_attention_unfused_workspace_offset = 0
_workSpaceSize = 0
workSpaceSize = 0
kv_caches = None
@staticmethod
def reset_tokens(initial_tokens=1):
InferenceContext._num_tokens = initial_tokens
@staticmethod
def current_tokens():
return InferenceContext._num_tokens
@staticmethod
def GetWorkSpace():
return InferenceContext._workspace
class NPUInference:
@staticmethod
def layer_norm(inputs, gamma, beta, epsilon):
return torch.nn.functional.layer_norm(inputs, [inputs.shape[-1]], gamma, beta, eps=epsilon)
@staticmethod
def _qkv_gemm(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose):
inp_norm = torch.nn.functional.layer_norm(inputs, (inputs.shape[2], ), gamma, beta, eps)
weight = weight.t() if transpose else weight
tmp = torch.matmul(inp_norm, weight)
if add_bias:
tmp += bias
output = [tmp, inp_norm]
return output
@staticmethod
def qkv_gemm_fp16(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose):
return NPUInference._qkv_gemm(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose)
@staticmethod
def qkv_gemm_bf16(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose):
return NPUInference._qkv_gemm(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose)
@staticmethod
def qkv_gemm_fp32(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose):
return NPUInference._qkv_gemm(inputs, weight, q_scale, bias, gamma, beta, eps, add_bias, q_int8, transpose)
@staticmethod
def _bias_add_transform_0213(vals, bias, hidden_dim, seq_length, seq_offset, heads, num_kv, rotary_dim,
rotate_half, rotate_every_two, rope_theta):
bsz, _, _ = vals.shape
q = vals[..., :hidden_dim].reshape(bsz, seq_length, heads, -1)
k = vals[..., hidden_dim:hidden_dim + num_kv * (hidden_dim // heads)].reshape(bsz, seq_length, num_kv, -1)
v = vals[..., hidden_dim + num_kv * (hidden_dim // heads):]
if rotary_dim > 0 and rotate_every_two:
# sin, cos may use cache
seq_id = torch.arange(0, seq_length).to("npu")
inv_freq = torch.arange(0, rotary_dim, 2) / rotary_dim
inv_freq = inv_freq.to("npu")
inv_freq = 1.0 / torch.pow(rope_theta, inv_freq)
inv_freq = torch.outer(seq_id, inv_freq)
sin = inv_freq.sin()
cos = inv_freq.cos()
# shape: [bsz=1, seq_len, heads=1, rotary_dim]
sin = sin.view(-1, seq_length, 1, rotary_dim // 2).repeat_interleave(2, dim=-1)
cos = cos.view(-1, seq_length, 1, rotary_dim // 2).repeat_interleave(2, dim=-1)
q_pos, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
k_pos, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
q_pos = torch_npu.npu_rotary_mul(q_pos, cos, sin)
q = torch.cat([q_pos, q_pass], dim=-1)
k_pos = torch_npu.npu_rotary_mul(k_pos, cos, sin)
k = torch.cat([k_pos, k_pass], dim=-1)
output = q.reshape(bsz, seq_length, -1).contiguous() # [b, s, H]
k_cache = k.reshape(bsz, seq_length, heads, -1).transpose(1, 2).contiguous() # [b, n, s, d]
v_cache = v.reshape(bsz, seq_length, heads, -1).transpose(1, 2).contiguous() # [b, n, s, d]
return output, k_cache, v_cache
@staticmethod
def _softmax_context(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two, heads, num_kv,
norm_factor, triangular_masking, local_attention, window_size, no_masking, layer_id,
num_layers, alibi, rope_theta):
bsz, seq_len, k = query_key_value.size()
k = k // (heads + 2 * (num_kv if num_kv > 0 else heads))
hidden_dim = heads * k
is_promt = seq_len > 1
if not InferenceContext.kv_caches:
InferenceContext.kv_caches = [[None, None] for _ in range(num_layers)]
if is_promt:
InferenceContext.reset_tokens(seq_len)
InferenceContext.kv_caches[layer_id] = [None, None]
soft_len = InferenceContext.current_tokens()
workspace = InferenceContext.GetWorkSpace()
seq_offset = 0 if is_promt else soft_len - 1
q, k, v = NPUInference._bias_add_transform_0213(vals=query_key_value,
bias=None,
hidden_dim=hidden_dim,
seq_length=seq_len,
seq_offset=seq_offset,
heads=heads,
num_kv=num_kv if num_kv > 0 else heads,
rotary_dim=rotary_dim,
rotate_half=rotate_half,
rotate_every_two=rotate_every_two,
rope_theta=rope_theta)
if not is_promt:
k_cache, v_cache = InferenceContext.kv_caches[layer_id]
if k_cache is not None:
k = torch.cat([k_cache, k], dim=2)
v = torch.cat([v_cache, v], dim=2)
InferenceContext.kv_caches[layer_id] = [k, v]
seq_len = k.shape[2]
layer_scale = max(1, layer_id) if len(alibi.size()) > 1 else 1.0
alpha = norm_factor * norm_factor / layer_scale
output = torch_npu.npu_fusion_attention(q,
k.transpose(1, 2).reshape(bsz, seq_len, -1).contiguous(),
v.transpose(1, 2).reshape(bsz, seq_len, -1).contiguous(),
heads,
"BSH",
pse=None,
padding_mask=None,
atten_mask=attn_mask.bool(),
scale=alpha,
pre_tockens=65536,
next_tockens=65536,
keep_prob=1,
inner_precise=0)[0]
return output, k, v
@staticmethod
def softmax_context_fp16(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two, heads, num_kv,
norm_factor, triangular_masking, local_attention, window_size, no_masking, layer_id,
num_layers, alibi, rope_theta):
return NPUInference._softmax_context(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two,
heads, num_kv, norm_factor, triangular_masking, local_attention,
window_size, no_masking, layer_id, num_layers, alibi, rope_theta)
@staticmethod
def softmax_context_bf16(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two, heads, num_kv,
norm_factor, triangular_masking, local_attention, window_size, no_masking, layer_id,
num_layers, alibi, rope_theta):
return NPUInference._softmax_context(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two,
heads, num_kv, norm_factor, triangular_masking, local_attention,
window_size, no_masking, layer_id, num_layers, alibi, rope_theta)
@staticmethod
def softmax_context_fp32(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two, heads, num_kv,
norm_factor, triangular_masking, local_attention, window_size, no_masking, layer_id,
num_layers, alibi, rope_theta):
return NPUInference._softmax_context(query_key_value, attn_mask, rotary_dim, rotate_half, rotate_every_two,
heads, num_kv, norm_factor, triangular_masking, local_attention,
window_size, no_masking, layer_id, num_layers, alibi, rope_theta)
@staticmethod
def _vector_matmul(input, weight, async_op, q_scale, q_int8, transposed_mode):
if transposed_mode:
return torch.matmul(input, weight.t())
return torch.matmul(input, weight)
@staticmethod
def vector_matmul_fp16(input, weight, async_op, q_scale, q_int8, transposed_mode):
return NPUInference._vector_matmul(input, weight, async_op, q_scale, q_int8, transposed_mode)
@staticmethod
def vector_matmul_bf16(input, weight, async_op, q_scale, q_int8, transposed_mode):
return NPUInference._vector_matmul(input, weight, async_op, q_scale, q_int8, transposed_mode)
@staticmethod
def vector_matmul_fp32(input, weight, async_op, q_scale, q_int8, transposed_mode):
return NPUInference._vector_matmul(input, weight, async_op, q_scale, q_int8, transposed_mode)
@staticmethod
def _mlp_gemm(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps, pre_layer_norm,
mlp_after_attn, interm_scale, out_scale, dtype, mlp_act_func_type, transpose):
if mlp_after_attn:
residual_add = torch.nn.functional.layer_norm(input + residual + input_bias, (input.shape[-1], ), gamma,
beta, eps)
else:
residual_add = torch.nn.functional.layer_norm(input, (input.shape[-1], ), gamma, beta, eps)
weight_interm = weight_interm.t() if transpose else weight_interm
tmp = torch.matmul(residual_add, weight_interm)
if mlp_act_func_type == ActivationFuncType.GELU:
tmp = torch.nn.functional.gelu(tmp + bias)
elif mlp_act_func_type == ActivationFuncType.ReLU:
tmp = torch.nn.functional.relu(tmp + bias)
else:
raise Exception('Unsupported ActivationFuncType {}'.format(mlp_act_func_type))
output = torch.matmul(tmp, weight_out.t())
return output, residual_add
@staticmethod
def mlp_gemm_fp16(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps, pre_layer_norm,
mlp_after_attn, interm_scale, out_scale, dtype, mlp_act_func_type, transpose):
return NPUInference._mlp_gemm(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps,
pre_layer_norm, mlp_after_attn, interm_scale, out_scale, dtype,
mlp_act_func_type, transpose)
@staticmethod
def mlp_gemm_bf16(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps, pre_layer_norm,
mlp_after_attn, interm_scale, out_scale, dtype, mlp_act_func_type, transpose):
return NPUInference._mlp_gemm(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps,
pre_layer_norm, mlp_after_attn, interm_scale, out_scale, dtype,
mlp_act_func_type, transpose)
@staticmethod
def mlp_gemm_fp32(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps, pre_layer_norm,
mlp_after_attn, interm_scale, out_scale, dtype, mlp_act_func_type, transpose):
return NPUInference._mlp_gemm(input, residual, input_bias, weight_interm, weight_out, bias, gamma, beta, eps,
pre_layer_norm, mlp_after_attn, interm_scale, out_scale, dtype,
mlp_act_func_type, transpose)
@staticmethod
def _residual_add_bias(hidden_state, residual, attention_output, attention_bias, final_bias, mp_size,
mlp_after_attn, add_bias, pre_layer_norm):
if mlp_after_attn:
if pre_layer_norm:
tmp = (residual.float() + attention_output.float() + attention_bias.float() +
final_bias.float()) / mp_size + hidden_state.float()
else:
tmp = residual.float() + hidden_state.float() + final_bias.float()
else:
if add_bias:
residual += attention_bias.float()
tmp = hidden_state.float() + attention_output.float() + (residual.float() + final_bias.float()) / mp_size
input_dtype = hidden_state.dtype
residual.set_(tmp.to(input_dtype))
@staticmethod
def residual_add_bias_fp16(hidden_state, residual, attention_output, attention_bias, final_bias, mp_size,
mlp_after_attn, add_bias, pre_layer_norm):
return NPUInference._residual_add_bias(hidden_state, residual, attention_output, attention_bias, final_bias,
mp_size, mlp_after_attn, add_bias, pre_layer_norm)
@staticmethod
def residual_add_bias_bf16(hidden_state, residual, attention_output, attention_bias, final_bias, mp_size,
mlp_after_attn, add_bias, pre_layer_norm):
return NPUInference._residual_add_bias(hidden_state, residual, attention_output, attention_bias, final_bias,
mp_size, mlp_after_attn, add_bias, pre_layer_norm)
@staticmethod
def residual_add_bias_fp32(hidden_state, residual, attention_output, attention_bias, final_bias, mp_size,
mlp_after_attn, add_bias, pre_layer_norm):
return NPUInference._residual_add_bias(hidden_state, residual, attention_output, attention_bias, final_bias,
mp_size, mlp_after_attn, add_bias, pre_layer_norm)
class InferenceBuilder(NPUOpBuilder):
BUILD_VAR = "DS_BUILD_TRANSFORMER_INFERENCE"
NAME = "transformer_inference"
def __init__(self):
super().__init__(name=self.NAME)
def absolute_name(self):
return f'deepspeed.ops.transformer.inference.{self.NAME}_op'
def sources(self):
return []
def include_paths(self):
return []
def load(self, verbose=True):
return NPUInference