Kernels:
Trusted publisher
Uploaded using `kernel-builder`.
Browse files- build/torch-xpu/__init__.py +9 -0
- build/torch-xpu/_ops.py +38 -0
- build/torch-xpu/cross_entropy.py +558 -0
- build/torch-xpu/dyt.py +164 -0
- build/torch-xpu/fused_linear_cross_entropy.py +400 -0
- build/torch-xpu/geglu.py +143 -0
- build/torch-xpu/group_norm.py +311 -0
- build/torch-xpu/jsd.py +201 -0
- build/torch-xpu/kl_div.py +259 -0
- build/torch-xpu/layer_norm.py +320 -0
- build/torch-xpu/layers.py +264 -0
- build/torch-xpu/liger_kernels/__init__.py +26 -0
- build/torch-xpu/metadata.json +10 -0
- build/torch-xpu/metadata.json.sigstore +1 -0
- build/torch-xpu/qwen2vl_mrope.py +222 -0
- build/torch-xpu/rms_norm.py +654 -0
- build/torch-xpu/rope.py +239 -0
- build/torch-xpu/swiglu.py +176 -0
- build/torch-xpu/tiled_mlp.py +139 -0
- build/torch-xpu/tvd.py +218 -0
- build/torch-xpu/utils.py +176 -0
build/torch-xpu/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import layers
|
| 2 |
+
from .layers import LigerForCausalLMLoss, liger_rotary_pos_emb
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
"layers",
|
| 7 |
+
"LigerForCausalLMLoss",
|
| 8 |
+
"liger_rotary_pos_emb",
|
| 9 |
+
]
|
build/torch-xpu/_ops.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
|
| 3 |
+
def get_backend() -> str:
|
| 4 |
+
"""Detect the backend by inspecting torch."""
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
if hasattr(torch, "neuron"):
|
| 8 |
+
# Needs to be sorted before specific Torch builds, since Neuron
|
| 9 |
+
# extension can be loaded into e.g. CUDA Torch builds.
|
| 10 |
+
return "neuron"
|
| 11 |
+
elif torch.version.cuda is not None:
|
| 12 |
+
return "cuda"
|
| 13 |
+
elif torch.version.hip is not None:
|
| 14 |
+
return "rocm"
|
| 15 |
+
elif torch.backends.mps.is_available():
|
| 16 |
+
return "metal"
|
| 17 |
+
elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
|
| 18 |
+
return "xpu"
|
| 19 |
+
else:
|
| 20 |
+
return "cpu"
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _find_ops_name() -> str:
|
| 24 |
+
kernel_name = "liger_kernels"
|
| 25 |
+
unique_id = "afb82fe"
|
| 26 |
+
backend = get_backend()
|
| 27 |
+
return f"_{kernel_name}_{backend}_{unique_id}"
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
_OPS_NAME = _find_ops_name()
|
| 31 |
+
|
| 32 |
+
ops = getattr(torch.ops, _OPS_NAME)
|
| 33 |
+
|
| 34 |
+
def add_op_namespace_prefix(op_name: str) -> str:
|
| 35 |
+
"""
|
| 36 |
+
Prefix op by namespace.
|
| 37 |
+
"""
|
| 38 |
+
return f"{_OPS_NAME}::{op_name}"
|
build/torch-xpu/cross_entropy.py
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import operator
|
| 2 |
+
|
| 3 |
+
from typing import Optional
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import triton
|
| 7 |
+
import triton.language as tl
|
| 8 |
+
|
| 9 |
+
from .utils import compare_version
|
| 10 |
+
from .utils import element_mul_kernel
|
| 11 |
+
from .utils import is_hip
|
| 12 |
+
from .utils import infer_device
|
| 13 |
+
from .utils import is_npu_available
|
| 14 |
+
|
| 15 |
+
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
|
| 16 |
+
try:
|
| 17 |
+
# typical import path with dispatch available
|
| 18 |
+
from triton.language.extra.libdevice import tanh
|
| 19 |
+
except ModuleNotFoundError:
|
| 20 |
+
# for working with NGC containers
|
| 21 |
+
from triton.language.extra.cuda.libdevice import tanh
|
| 22 |
+
else:
|
| 23 |
+
from triton.language.math import tanh
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@triton.jit
|
| 27 |
+
def liger_cross_entropy_kernel(
|
| 28 |
+
X_ptr,
|
| 29 |
+
X_stride,
|
| 30 |
+
Y_ptr,
|
| 31 |
+
Y_stride,
|
| 32 |
+
weight_ptr,
|
| 33 |
+
loss_ptr,
|
| 34 |
+
z_loss_ptr,
|
| 35 |
+
loss_stride,
|
| 36 |
+
token_accuracy_ptr,
|
| 37 |
+
token_accuracy_stride,
|
| 38 |
+
predicted_tokens_ptr,
|
| 39 |
+
predicted_tokens_stride,
|
| 40 |
+
n_cols,
|
| 41 |
+
n_non_ignore,
|
| 42 |
+
sum_non_ignore_weight,
|
| 43 |
+
weight_sum,
|
| 44 |
+
ignore_index,
|
| 45 |
+
lse_square_scale: tl.constexpr,
|
| 46 |
+
label_smoothing: tl.constexpr,
|
| 47 |
+
reduction: tl.constexpr, # set it as constexpr since reduction is always known at compile time
|
| 48 |
+
softcap,
|
| 49 |
+
RETURN_Z_LOSS: tl.constexpr,
|
| 50 |
+
RETURN_TOKEN_ACCURACY: tl.constexpr,
|
| 51 |
+
RETURN_PREDICTED_TOKENS: tl.constexpr,
|
| 52 |
+
BLOCK_SIZE: tl.constexpr,
|
| 53 |
+
HAS_WEIGHT: tl.constexpr,
|
| 54 |
+
HAS_SOFTCAPPING: tl.constexpr,
|
| 55 |
+
HAS_GRADIENTS: tl.constexpr,
|
| 56 |
+
):
|
| 57 |
+
"""
|
| 58 |
+
This kernel computes both cross entropy loss and the gradient of the input.
|
| 59 |
+
We only consider hard label + mean reduction for now. Please refer to https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html for the math.
|
| 60 |
+
|
| 61 |
+
Parameters:
|
| 62 |
+
X_ptr: Pointer to input tensor.
|
| 63 |
+
X_stride (int): The stride of the input tensor.
|
| 64 |
+
Y_ptr: Pointer to target tensor.
|
| 65 |
+
Y_stride (int): The stride of the target tensor.
|
| 66 |
+
weight_ptr: Pointer to weight tensor.
|
| 67 |
+
loss_ptr: Pointer to tensor to store the loss.
|
| 68 |
+
z_loss_ptr: Pointer to tensor to store the z loss. No operation if RETURN_Z_LOSS is 0.
|
| 69 |
+
loss_stride (int): The stride of the loss tensor.
|
| 70 |
+
token_accuracy_ptr: Pointer to tensor to store the per-token accuracy. No operation if RETURN_TOKEN_ACCURACY is 0.
|
| 71 |
+
token_accuracy_stride (int): The stride of the token accuracy tensor.
|
| 72 |
+
n_cols (int): The number of columns in the input tensor.
|
| 73 |
+
n_non_ignore (float): The number of non-ignored elements in the batch.
|
| 74 |
+
sum_non_ignore_weight (float): The sum of non-ignored target's weights in the batch.
|
| 75 |
+
weight_sum (float): The sum of weight tensor.
|
| 76 |
+
ignore_index (int): The index to ignore in the target.
|
| 77 |
+
label_smoothing (float): The amount of smoothing when computing the loss, where 0.0 means no smoothing.
|
| 78 |
+
lse_square_scale (float): The scaler of (logsumexp(_input)) ^ 2 adding to the loss for the stability of training.
|
| 79 |
+
reduction (str): The string for the reduction to apply
|
| 80 |
+
softcap (float): The upper threshold for scaling logits to the range (-softcap, +softcap).
|
| 81 |
+
RETURN_Z_LOSS (int): The boolean value to decide whether to store z loss to z_loss_ptr or not. It must be 0 or 1.
|
| 82 |
+
RETURN_TOKEN_ACCURACY (int): The boolean value to decide whether to store per-token accuracy to token_accuracy_ptr or not. It must be 0 or 1.
|
| 83 |
+
BLOCK_SIZE (int): The block size for Triton operations.
|
| 84 |
+
HAS_WEIGHT (bool): The boolean value to determine whether assigning weight to each of the classes.
|
| 85 |
+
HAS_SOFTCAPPING (bool): The boolean value to determine whether applying soft-capping or not.
|
| 86 |
+
HAS_GRADIENTS (bool): The boolean value to determine whether calculating gradients in forward pass.
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
# https://github.com/triton-lang/triton/issues/1058
|
| 90 |
+
# If B*T*V is too large, program_id * stride will overflow out of int32, so we convert to int64
|
| 91 |
+
program_id = tl.program_id(0).to(tl.int64)
|
| 92 |
+
|
| 93 |
+
# 1. Load Y_ptr first because if the target is ignore_index, we can return right away
|
| 94 |
+
Y_ptr += program_id * Y_stride
|
| 95 |
+
y = tl.load(Y_ptr)
|
| 96 |
+
|
| 97 |
+
# 2. locate the start index
|
| 98 |
+
X_ptr += program_id * X_stride
|
| 99 |
+
|
| 100 |
+
if y == ignore_index:
|
| 101 |
+
# set all X_ptr as 0
|
| 102 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 103 |
+
X_offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 104 |
+
tl.store(X_ptr + X_offsets, 0.0, mask=X_offsets < n_cols)
|
| 105 |
+
# For ignored tokens, set token accuracy to 0
|
| 106 |
+
if RETURN_TOKEN_ACCURACY:
|
| 107 |
+
token_accuracy_ptr += program_id * token_accuracy_stride
|
| 108 |
+
tl.store(token_accuracy_ptr, 0.0)
|
| 109 |
+
if RETURN_PREDICTED_TOKENS:
|
| 110 |
+
predicted_tokens_ptr += program_id * predicted_tokens_stride
|
| 111 |
+
tl.store(predicted_tokens_ptr, -1)
|
| 112 |
+
return
|
| 113 |
+
|
| 114 |
+
loss_ptr += program_id * loss_stride
|
| 115 |
+
if RETURN_Z_LOSS:
|
| 116 |
+
z_loss_ptr += program_id * loss_stride
|
| 117 |
+
if RETURN_TOKEN_ACCURACY:
|
| 118 |
+
token_accuracy_ptr += program_id * token_accuracy_stride
|
| 119 |
+
if RETURN_PREDICTED_TOKENS:
|
| 120 |
+
predicted_tokens_ptr += program_id * predicted_tokens_stride
|
| 121 |
+
|
| 122 |
+
if HAS_WEIGHT:
|
| 123 |
+
weight_y = tl.load(weight_ptr + y).cast(tl.float32)
|
| 124 |
+
|
| 125 |
+
# Online softmax: 2 loads + 1 store (compared with 3 loads + 1 store for the safe softmax)
|
| 126 |
+
# Refer to Algorithm 3 in the paper: https://arxiv.org/pdf/1805.02867
|
| 127 |
+
|
| 128 |
+
# 3. [Online softmax] first pass: find max + sum
|
| 129 |
+
m = float("-inf") # m is the max value. use the notation from the paper
|
| 130 |
+
d = 0.0 # d is the sum. use the notation from the paper
|
| 131 |
+
argmax_idx = 0 # Track the index of the maximum value for token accuracy / predicted tokens computation
|
| 132 |
+
ori_X_y = tl.load(X_ptr + y).cast(tl.float32) # we need to store the original value of X_y for the loss calculation
|
| 133 |
+
if HAS_SOFTCAPPING:
|
| 134 |
+
ori_X_y = softcap * tanh(ori_X_y / softcap)
|
| 135 |
+
|
| 136 |
+
# Label smoothing is a general case of normal cross entropy
|
| 137 |
+
# See the full derivation at https://github.com/linkedin/Liger-Kernel/pull/198#issue-2503665310
|
| 138 |
+
scaled_x_sum = 0.0
|
| 139 |
+
eps = label_smoothing / n_cols
|
| 140 |
+
|
| 141 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 142 |
+
X_offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 143 |
+
X_block = tl.load(
|
| 144 |
+
X_ptr + X_offsets,
|
| 145 |
+
mask=X_offsets < n_cols,
|
| 146 |
+
other=float("-inf"),
|
| 147 |
+
# Ensure float32 precision for softmax calculation
|
| 148 |
+
).cast(tl.float32)
|
| 149 |
+
if HAS_SOFTCAPPING:
|
| 150 |
+
X_block = softcap * tanh(X_block / softcap)
|
| 151 |
+
block_max = tl.max(X_block)
|
| 152 |
+
|
| 153 |
+
# Track argmax for accuracy / predicted tokens computation
|
| 154 |
+
if RETURN_TOKEN_ACCURACY or RETURN_PREDICTED_TOKENS:
|
| 155 |
+
# Find the index of the maximum value in this block
|
| 156 |
+
is_max_mask = X_block == block_max
|
| 157 |
+
# Mask out invalid indices with a value larger than n_cols
|
| 158 |
+
masked_offsets = tl.where(is_max_mask, X_offsets, n_cols)
|
| 159 |
+
# Get the first (smallest) index where max occurs
|
| 160 |
+
current_block_argmax_idx = tl.min(masked_offsets)
|
| 161 |
+
|
| 162 |
+
is_new_max = block_max > m
|
| 163 |
+
argmax_idx = tl.where(is_new_max, current_block_argmax_idx, argmax_idx)
|
| 164 |
+
|
| 165 |
+
if label_smoothing > 0:
|
| 166 |
+
# scale X beforehand to avoid overflow
|
| 167 |
+
if HAS_WEIGHT:
|
| 168 |
+
weight_block = tl.load(weight_ptr + X_offsets, mask=X_offsets < n_cols)
|
| 169 |
+
scaled_x_sum += tl.sum(tl.where(X_offsets < n_cols, -eps * X_block * weight_block, 0.0))
|
| 170 |
+
else:
|
| 171 |
+
scaled_x_sum += tl.sum(tl.where(X_offsets < n_cols, -eps * X_block, 0.0))
|
| 172 |
+
m_new = tl.maximum(m, block_max)
|
| 173 |
+
d = d * tl.exp(m - m_new) + tl.sum(tl.exp(X_block - m_new))
|
| 174 |
+
m = m_new
|
| 175 |
+
|
| 176 |
+
# log (sum(e^(X_i))) = log (sum(e ^ (max(X) * e ^ (X_i - max(X)))))
|
| 177 |
+
# = log (e^(max(X)) * sum(e ^ (X_i - max(X))))
|
| 178 |
+
# = max(X) + log (sum(e ^ (X_i - max(X)))) = m + log d
|
| 179 |
+
lse = m + tl.log(d)
|
| 180 |
+
|
| 181 |
+
# 4. [Online Softmax] Second pass: compute gradients
|
| 182 |
+
# For 'mean' reduction, gradients are normalized by number of non-ignored elements (N)
|
| 183 |
+
# dx_y = (softmax(x_y) - 1) / N
|
| 184 |
+
# dx_i = softmax(x_i) / N, i != y
|
| 185 |
+
# For label smoothing:
|
| 186 |
+
# dx_i = (softmax(x_i) - label_smoothing / V) / N, V = n_cols, i != y
|
| 187 |
+
# dx_y = (softmax(x_y) - label_smoothing / V - (1 - label_smoothing)) / N
|
| 188 |
+
# = dx_i - (1 - label_smoothing) / N
|
| 189 |
+
# With Z loss:
|
| 190 |
+
# dx_i = ((1 + 2 * lse_square_scale * lse) * softmax(x_i) - label_smoothing / V) / N, i != y
|
| 191 |
+
# dx_y = dx_i - (1 - label_smoothing) / N
|
| 192 |
+
# For 'sum' reduction, no normalization is applied:
|
| 193 |
+
# dx_y = softmax(x_y) - 1
|
| 194 |
+
# dx_i = softmax(x_i), for i ≠ y
|
| 195 |
+
if HAS_GRADIENTS:
|
| 196 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 197 |
+
X_offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 198 |
+
X_block = tl.load(
|
| 199 |
+
X_ptr + X_offsets,
|
| 200 |
+
mask=X_offsets < n_cols,
|
| 201 |
+
other=float("-inf"),
|
| 202 |
+
# Ensure float32 precision for softmax calculation
|
| 203 |
+
).cast(tl.float32)
|
| 204 |
+
if HAS_SOFTCAPPING:
|
| 205 |
+
intermediate = tanh(X_block / softcap)
|
| 206 |
+
X_block = softcap * intermediate
|
| 207 |
+
|
| 208 |
+
if not HAS_WEIGHT:
|
| 209 |
+
# softmax(x_i)
|
| 210 |
+
X_block = tl.exp(X_block - m) / d
|
| 211 |
+
# derivative of z-loss: 2 * lse_square_scale * lse * softmax(x_i)
|
| 212 |
+
X_block += 2 * lse_square_scale * lse * X_block
|
| 213 |
+
# smoothing term
|
| 214 |
+
X_block += -eps
|
| 215 |
+
# special handle dx_y
|
| 216 |
+
X_block = tl.where(X_offsets != y, X_block, X_block - (1 - label_smoothing))
|
| 217 |
+
# reduction scale
|
| 218 |
+
if reduction == "mean":
|
| 219 |
+
X_block = X_block / n_non_ignore
|
| 220 |
+
else:
|
| 221 |
+
weight_block = tl.load(weight_ptr + X_offsets, mask=X_offsets < n_cols)
|
| 222 |
+
softmax_X = tl.exp(X_block - m) / d
|
| 223 |
+
# derivative of original_loss
|
| 224 |
+
dloss_ori = (1 - label_smoothing) * softmax_X
|
| 225 |
+
# specially handle dx_y
|
| 226 |
+
dloss_ori = tl.where(X_offsets != y, dloss_ori, dloss_ori - (1 - label_smoothing))
|
| 227 |
+
dloss_ori = dloss_ori * weight_y
|
| 228 |
+
# derivative of smooth_loss
|
| 229 |
+
dloss_smooth = eps * (-weight_block + softmax_X * weight_sum)
|
| 230 |
+
# derivative of z-loss
|
| 231 |
+
dz_loss = 2 * lse_square_scale * lse * softmax_X
|
| 232 |
+
# reduction scale
|
| 233 |
+
if reduction == "mean":
|
| 234 |
+
dloss_ori = dloss_ori / sum_non_ignore_weight
|
| 235 |
+
dloss_smooth = dloss_smooth / sum_non_ignore_weight
|
| 236 |
+
# TODO: Implement weighted z_loss. Currently, z_loss is not scaled by weight.
|
| 237 |
+
dz_loss = dz_loss / n_non_ignore
|
| 238 |
+
# derivative of total_loss
|
| 239 |
+
X_block = dloss_ori + dloss_smooth + dz_loss
|
| 240 |
+
|
| 241 |
+
# chain rule softcapping
|
| 242 |
+
# d(softcap * tanh(x / softcap)) = (1 - tanh^2(x / softcap))
|
| 243 |
+
if HAS_SOFTCAPPING:
|
| 244 |
+
X_block = X_block * (1 - intermediate * intermediate)
|
| 245 |
+
|
| 246 |
+
tl.store(X_ptr + X_offsets, X_block, mask=X_offsets < n_cols)
|
| 247 |
+
|
| 248 |
+
# We need tl.debug_barrier() to ensure the new result of X_ptr is written as mentioned in
|
| 249 |
+
# https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/ops/cross_entropy.py#L34
|
| 250 |
+
tl.debug_barrier()
|
| 251 |
+
|
| 252 |
+
# 5. Calculate the loss
|
| 253 |
+
|
| 254 |
+
# loss = log (softmax(X_y)) = log ((e ^ (X_y - max(X)) / sum(e ^ (X - max(X))))
|
| 255 |
+
# = (X_y - max(X)) - log(sum(e ^ (X - max(X))))
|
| 256 |
+
# = X_y - m - log d = X_y - lse
|
| 257 |
+
# sum(e ^ (X - max(X))) must >= 1 because the max term is e ^ 0 = 1
|
| 258 |
+
# So we can safely calculate log (softmax(X_y)) without overflow
|
| 259 |
+
loss = lse - ori_X_y
|
| 260 |
+
if HAS_WEIGHT:
|
| 261 |
+
loss = weight_y * loss
|
| 262 |
+
|
| 263 |
+
# Original loss = H(q, p), with label smoothing regularization = H(q', p) and (label_smoothing / V) = eps
|
| 264 |
+
# H(q', p) = (1 - label_smoothing) * H(q, p) + label_smoothing * H(u, p)
|
| 265 |
+
# = (1 - label_smoothing) * H(q, p) + eps * sum(logsoftmax(x_i))
|
| 266 |
+
# By using m (global max of xi) and d (sum of e^(xi-m)), we can simplify as:
|
| 267 |
+
# = (1 - label_smoothing) * H(q, p) + (sum(-eps * x_i) + label_smoothing * (m + logd))
|
| 268 |
+
# Refer to H(q', p) in section 7 of the paper: https://arxiv.org/pdf/1512.00567
|
| 269 |
+
# pytorch: https://github.com/pytorch/pytorch/blob/2981534f54d49fa3a9755c9b0855e7929c2527f0/aten/src/ATen/native/LossNLL.cpp#L516
|
| 270 |
+
# See full derivation at https://github.com/linkedin/Liger-Kernel/pull/198#issuecomment-2333753087
|
| 271 |
+
if label_smoothing > 0:
|
| 272 |
+
if HAS_WEIGHT:
|
| 273 |
+
smooth_loss = scaled_x_sum + eps * lse * weight_sum
|
| 274 |
+
else:
|
| 275 |
+
smooth_loss = scaled_x_sum + label_smoothing * lse
|
| 276 |
+
loss = loss * (1 - label_smoothing) + smooth_loss
|
| 277 |
+
|
| 278 |
+
# An auxiliary loss, z_loss
|
| 279 |
+
# Refer to Page14 Loss function section in the paper PaLM: https://www.jmlr.org/papers/v24/22-1144.html
|
| 280 |
+
z_loss = lse_square_scale * lse * lse
|
| 281 |
+
# Normalize the loss by the number of non-ignored elements if reduction is "mean"
|
| 282 |
+
if reduction == "mean":
|
| 283 |
+
if HAS_WEIGHT:
|
| 284 |
+
loss = loss / sum_non_ignore_weight
|
| 285 |
+
else:
|
| 286 |
+
loss = loss / n_non_ignore
|
| 287 |
+
# TODO: Implement weighted z_loss. Currently, z_loss is not scaled by weight.
|
| 288 |
+
z_loss = z_loss / n_non_ignore
|
| 289 |
+
loss += z_loss
|
| 290 |
+
|
| 291 |
+
tl.store(loss_ptr, loss)
|
| 292 |
+
if RETURN_Z_LOSS:
|
| 293 |
+
tl.store(z_loss_ptr, z_loss)
|
| 294 |
+
if RETURN_TOKEN_ACCURACY:
|
| 295 |
+
# Store 1.0 if prediction is correct, 0.0 otherwise
|
| 296 |
+
is_correct = 1.0 if argmax_idx == y else 0.0
|
| 297 |
+
tl.store(token_accuracy_ptr, is_correct)
|
| 298 |
+
if RETURN_PREDICTED_TOKENS:
|
| 299 |
+
tl.store(predicted_tokens_ptr, argmax_idx)
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
# The hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576 https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
|
| 303 |
+
# However, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
|
| 304 |
+
# The optimal maximum block size depends on your hardware, your kernel, and your dtype
|
| 305 |
+
# the best size we found by manually tuning on xpu and npu.
|
| 306 |
+
if infer_device() == "xpu":
|
| 307 |
+
MAX_FUSED_SIZE = 4096
|
| 308 |
+
elif infer_device() == "npu":
|
| 309 |
+
MAX_FUSED_SIZE = 2048
|
| 310 |
+
else:
|
| 311 |
+
MAX_FUSED_SIZE = 65536 // 2
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def cross_entropy_forward(
|
| 315 |
+
_input,
|
| 316 |
+
target,
|
| 317 |
+
weight,
|
| 318 |
+
ignore_index,
|
| 319 |
+
lse_square_scale,
|
| 320 |
+
label_smoothing,
|
| 321 |
+
reduction,
|
| 322 |
+
softcap,
|
| 323 |
+
return_z_loss,
|
| 324 |
+
return_token_accuracy=False,
|
| 325 |
+
return_predicted_tokens=False,
|
| 326 |
+
):
|
| 327 |
+
assert isinstance(return_z_loss, bool), f"return_z_loss must be True or False. Got: {return_z_loss}"
|
| 328 |
+
assert isinstance(return_token_accuracy, bool), (
|
| 329 |
+
f"return_token_accuracy must be True or False. Got: {return_token_accuracy}"
|
| 330 |
+
)
|
| 331 |
+
assert isinstance(return_predicted_tokens, bool), (
|
| 332 |
+
f"return_predicted_tokens must be True or False. Got: {return_predicted_tokens}"
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
BT, V = _input.shape
|
| 336 |
+
n_rows = BT
|
| 337 |
+
|
| 338 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 339 |
+
|
| 340 |
+
# unreduced loss
|
| 341 |
+
loss_1d = torch.zeros(n_rows, dtype=_input.dtype, device=_input.device)
|
| 342 |
+
z_loss_1d = torch.zeros(n_rows, dtype=_input.dtype, device=_input.device) if return_z_loss else None
|
| 343 |
+
token_accuracy_1d = (
|
| 344 |
+
torch.zeros(n_rows, dtype=torch.float32, device=_input.device) if return_token_accuracy else None
|
| 345 |
+
)
|
| 346 |
+
predicted_tokens_1d = (
|
| 347 |
+
torch.full((n_rows,), -1, dtype=torch.int64, device=_input.device) if return_predicted_tokens else None
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
target_mask = target != ignore_index
|
| 351 |
+
n_non_ignore = target_mask.sum().item()
|
| 352 |
+
assert (target * target_mask).max() < _input.shape[-1], (
|
| 353 |
+
f"Target {target.max()} is out of bounds. Expected < {_input.shape[-1]}"
|
| 354 |
+
)
|
| 355 |
+
assert (target * target_mask).min() >= 0, f"Target {target.min()} is out of bounds. Expected >= 0"
|
| 356 |
+
sum_non_ignore_weight = n_non_ignore
|
| 357 |
+
weight_sum = 0.0
|
| 358 |
+
if weight is not None:
|
| 359 |
+
assert weight.shape[0] == V, f"If given, weight has to be a Tensor of size V. Got: {weight.shape}"
|
| 360 |
+
assert torch.is_floating_point(weight), (
|
| 361 |
+
f"If given, weight has to be a Tensor of floating point dtype. Got: {weight.dtype}"
|
| 362 |
+
)
|
| 363 |
+
sum_non_ignore_weight = torch.gather(weight, dim=0, index=target.masked_select(target_mask)).sum().item()
|
| 364 |
+
weight_sum = weight.sum().item()
|
| 365 |
+
# ensure weight is contiguous
|
| 366 |
+
if weight.stride(-1) != 1:
|
| 367 |
+
weight = weight.contiguous()
|
| 368 |
+
|
| 369 |
+
# ensure _input and target are contiguous in the last dimension
|
| 370 |
+
if _input.stride(-1) != 1:
|
| 371 |
+
_input = _input.contiguous()
|
| 372 |
+
if target.stride(-1) != 1:
|
| 373 |
+
target = target.contiguous()
|
| 374 |
+
|
| 375 |
+
# Here we use a trick to store X_ptr gradient in X_ptr so we can save memory
|
| 376 |
+
liger_cross_entropy_kernel[(n_rows,)](
|
| 377 |
+
X_ptr=_input,
|
| 378 |
+
X_stride=_input.stride(-2),
|
| 379 |
+
Y_ptr=target,
|
| 380 |
+
Y_stride=target.stride(-1), # always 1
|
| 381 |
+
weight_ptr=weight, # dummy if None
|
| 382 |
+
loss_ptr=loss_1d,
|
| 383 |
+
z_loss_ptr=z_loss_1d,
|
| 384 |
+
loss_stride=loss_1d.stride(-1), # always 1
|
| 385 |
+
token_accuracy_ptr=token_accuracy_1d,
|
| 386 |
+
token_accuracy_stride=token_accuracy_1d.stride(-1)
|
| 387 |
+
if return_token_accuracy
|
| 388 |
+
else 0, # always 1 if accuracy is enabled
|
| 389 |
+
predicted_tokens_ptr=predicted_tokens_1d,
|
| 390 |
+
predicted_tokens_stride=predicted_tokens_1d.stride(-1)
|
| 391 |
+
if return_predicted_tokens
|
| 392 |
+
else 0, # always 1 if predicted tokens is enabled
|
| 393 |
+
n_cols=V,
|
| 394 |
+
n_non_ignore=n_non_ignore,
|
| 395 |
+
sum_non_ignore_weight=sum_non_ignore_weight,
|
| 396 |
+
ignore_index=ignore_index,
|
| 397 |
+
weight_sum=weight_sum,
|
| 398 |
+
lse_square_scale=lse_square_scale,
|
| 399 |
+
label_smoothing=label_smoothing,
|
| 400 |
+
reduction=reduction,
|
| 401 |
+
softcap=softcap,
|
| 402 |
+
RETURN_Z_LOSS=return_z_loss,
|
| 403 |
+
RETURN_TOKEN_ACCURACY=return_token_accuracy,
|
| 404 |
+
RETURN_PREDICTED_TOKENS=return_predicted_tokens,
|
| 405 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 406 |
+
HAS_WEIGHT=True if weight is not None else False,
|
| 407 |
+
HAS_SOFTCAPPING=True if softcap is not None else False,
|
| 408 |
+
HAS_GRADIENTS=_input.requires_grad,
|
| 409 |
+
# TODO: 32 seems to give the best performance
|
| 410 |
+
# Performance is quite sensitive to num_warps
|
| 411 |
+
num_warps=32 if not is_hip() else 16,
|
| 412 |
+
)
|
| 413 |
+
|
| 414 |
+
if reduction == "none":
|
| 415 |
+
loss = loss_1d
|
| 416 |
+
z_loss = z_loss_1d if return_z_loss else None
|
| 417 |
+
token_accuracy = token_accuracy_1d if return_token_accuracy else None
|
| 418 |
+
else:
|
| 419 |
+
loss = torch.sum(loss_1d)
|
| 420 |
+
z_loss = torch.sum(z_loss_1d) if return_z_loss else None
|
| 421 |
+
# For accuracy, we compute the mean across all non-ignored tokens
|
| 422 |
+
token_accuracy = torch.sum(token_accuracy_1d) / n_non_ignore if return_token_accuracy else None
|
| 423 |
+
|
| 424 |
+
predicted_tokens = predicted_tokens_1d if return_predicted_tokens else None
|
| 425 |
+
|
| 426 |
+
return loss, z_loss, token_accuracy, predicted_tokens, _input
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
def cross_entropy_backward(_input, grad_output):
|
| 430 |
+
# If cross entropy is the last layer, grad_output is 1.0. Skip the mul to save time
|
| 431 |
+
if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
|
| 432 |
+
pass
|
| 433 |
+
# If reduction is 'none'
|
| 434 |
+
elif grad_output.ndim > 0:
|
| 435 |
+
_input = _input * grad_output.unsqueeze(dim=1)
|
| 436 |
+
# If reduction is ['mean', 'sum'], grad_output is just a scalar
|
| 437 |
+
# We use a Triton kernel instead of a PyTorch operation because modifying inputs in-place
|
| 438 |
+
# for gradient storage and backward multiple times causes anomalies with PyTorch but not with Triton.
|
| 439 |
+
else:
|
| 440 |
+
BT, V = _input.shape
|
| 441 |
+
n_rows = BT
|
| 442 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 443 |
+
|
| 444 |
+
element_mul_kernel[(n_rows,)](
|
| 445 |
+
_input,
|
| 446 |
+
_input.stride(-2),
|
| 447 |
+
grad_output,
|
| 448 |
+
V,
|
| 449 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 450 |
+
num_warps=32 if not is_hip() else 16,
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
return _input
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
class LigerCrossEntropyFunction(torch.autograd.Function):
|
| 457 |
+
"""
|
| 458 |
+
This class implements a custom autograd function for the Liger Cross Entropy loss.
|
| 459 |
+
It overrides the forward and backward methods of the torch.autograd.Function class.
|
| 460 |
+
"""
|
| 461 |
+
|
| 462 |
+
@staticmethod
|
| 463 |
+
def forward(
|
| 464 |
+
ctx,
|
| 465 |
+
_input: torch.Tensor,
|
| 466 |
+
target: torch.Tensor,
|
| 467 |
+
weight: Optional[torch.FloatTensor],
|
| 468 |
+
ignore_index: int = -100,
|
| 469 |
+
lse_square_scale: float = 0.0,
|
| 470 |
+
label_smoothing: float = 0.0,
|
| 471 |
+
reduction: str = "mean",
|
| 472 |
+
softcap: Optional[float] = None,
|
| 473 |
+
return_z_loss: bool = False,
|
| 474 |
+
return_token_accuracy: bool = False,
|
| 475 |
+
return_predicted_tokens: bool = False,
|
| 476 |
+
):
|
| 477 |
+
"""
|
| 478 |
+
The forward pass of the Liger Cross Entropy loss.
|
| 479 |
+
|
| 480 |
+
Parameters:
|
| 481 |
+
ctx : The context object.
|
| 482 |
+
_input (tensor): The input tensor of shape (BT, V) where B is batch size, T is sequence length, V is vocab size.
|
| 483 |
+
target (tensor): The target tensor of shape (BT) where each value is in [0, V-1].
|
| 484 |
+
weight(Tensor, optional): a manual rescaling weight given to each class. If given, has to be a Tensor of size V and floating point dtype
|
| 485 |
+
ignore_index (int): The index to ignore in the target.
|
| 486 |
+
lse_square_scale (float): The scaler of (logsumexp(_input)) ^ 2 adding to the loss for the stability of training.
|
| 487 |
+
label_smoothing (float): The amount of smoothing when computing the loss, where 0.0 means no smoothing.
|
| 488 |
+
reduction (str): The reduction to apply to the output: "none" | "mean | "sum".
|
| 489 |
+
softcap (Optional[float]): The upper threshold for scaling logits to the range (-softcap, +softcap).
|
| 490 |
+
return_z_loss (bool): When `return_z_loss` is `True`, returns (loss, z_loss, token_accuracy, predicted_tokens) instead of (loss, None, None, None). Default: `False`
|
| 491 |
+
return_token_accuracy (bool): When `return_token_accuracy` is `True`, computes and returns per-token accuracy without materializing logits. Default: `False`
|
| 492 |
+
return_predicted_tokens (bool): When `return_predicted_tokens` is `True`, returns per-token predicted class indices (argmax) without materializing logits. Default: `False`
|
| 493 |
+
|
| 494 |
+
Returns:
|
| 495 |
+
tuple: A tuple with the computed losses, accuracy, and predicted tokens: (loss, z_loss, token_accuracy, predicted_tokens). z_loss, token_accuracy, and predicted_tokens are None if not requested.
|
| 496 |
+
"""
|
| 497 |
+
input_requires_grad = _input.requires_grad
|
| 498 |
+
|
| 499 |
+
loss, z_loss, token_accuracy, predicted_tokens, _input = cross_entropy_forward(
|
| 500 |
+
_input,
|
| 501 |
+
target,
|
| 502 |
+
weight,
|
| 503 |
+
ignore_index,
|
| 504 |
+
lse_square_scale,
|
| 505 |
+
label_smoothing,
|
| 506 |
+
reduction,
|
| 507 |
+
softcap,
|
| 508 |
+
return_z_loss,
|
| 509 |
+
return_token_accuracy,
|
| 510 |
+
return_predicted_tokens,
|
| 511 |
+
)
|
| 512 |
+
# TODO: investigation
|
| 513 |
+
# If we don't detach the _input tensor, the memory will double
|
| 514 |
+
# Not sure why but seems that there will be a time both grad and value exist but in different location
|
| 515 |
+
if input_requires_grad:
|
| 516 |
+
ctx.save_for_backward(_input.detach())
|
| 517 |
+
ctx.return_z_loss = return_z_loss
|
| 518 |
+
ctx.return_token_accuracy = return_token_accuracy
|
| 519 |
+
ctx.return_predicted_tokens = return_predicted_tokens
|
| 520 |
+
|
| 521 |
+
return loss, z_loss, token_accuracy, predicted_tokens
|
| 522 |
+
|
| 523 |
+
@staticmethod
|
| 524 |
+
def backward(ctx, grad_output, grad_output2, grad_output3, grad_output4):
|
| 525 |
+
"""
|
| 526 |
+
The backward pass of the Liger Cross Entropy loss.
|
| 527 |
+
|
| 528 |
+
Parameters:
|
| 529 |
+
ctx : The context object with saved tensors.
|
| 530 |
+
grad_output (tensor): The tensor containing the gradient of the loss with respect to the output.
|
| 531 |
+
grad_output2 (tensor): No use. Gradient for z_loss (not used as z_loss is only for logging).
|
| 532 |
+
grad_output3 (tensor): No use. Gradient for token_accuracy (not used as token_accuracy is only for metrics).
|
| 533 |
+
grad_output4 (tensor): No use. Gradient for predicted_tokens (not used as predicted_tokens is only for metrics).
|
| 534 |
+
Returns:
|
| 535 |
+
tuple: A tuple with the gradients with respect to the inputs. The elements are tensors or None.
|
| 536 |
+
"""
|
| 537 |
+
if ctx.return_z_loss:
|
| 538 |
+
del grad_output2 # z_loss is only for logging
|
| 539 |
+
if ctx.return_token_accuracy:
|
| 540 |
+
del grad_output3 # token_accuracy is only for metrics
|
| 541 |
+
if ctx.return_predicted_tokens:
|
| 542 |
+
del grad_output4 # predicted_tokens is only for metrics
|
| 543 |
+
|
| 544 |
+
(_input,) = ctx.saved_tensors
|
| 545 |
+
_input = cross_entropy_backward(_input, grad_output)
|
| 546 |
+
return (
|
| 547 |
+
_input,
|
| 548 |
+
None,
|
| 549 |
+
None,
|
| 550 |
+
None,
|
| 551 |
+
None,
|
| 552 |
+
None,
|
| 553 |
+
None,
|
| 554 |
+
None,
|
| 555 |
+
None,
|
| 556 |
+
None,
|
| 557 |
+
None,
|
| 558 |
+
)
|
build/torch-xpu/dyt.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import operator
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from .utils import compare_version
|
| 8 |
+
from .utils import ensure_contiguous
|
| 9 |
+
from .utils import get_npu_core_count
|
| 10 |
+
from .utils import infer_device
|
| 11 |
+
from .utils import is_npu_available
|
| 12 |
+
|
| 13 |
+
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
|
| 14 |
+
try:
|
| 15 |
+
# typical import path with dispatch available
|
| 16 |
+
from triton.language.extra.libdevice import tanh
|
| 17 |
+
except ModuleNotFoundError:
|
| 18 |
+
# for working with NGC containers
|
| 19 |
+
from triton.language.extra.cuda.libdevice import tanh
|
| 20 |
+
else:
|
| 21 |
+
from triton.language.math import tanh
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@triton.autotune(
|
| 25 |
+
configs=[
|
| 26 |
+
triton.Config({"BLOCK_N": bn}, num_stages=ns, num_warps=nw)
|
| 27 |
+
for bn in [1024, 2048, 4096]
|
| 28 |
+
for ns in [1, 2]
|
| 29 |
+
for nw in [4, 8, 16]
|
| 30 |
+
],
|
| 31 |
+
key=["N"],
|
| 32 |
+
)
|
| 33 |
+
@triton.jit
|
| 34 |
+
def _dyt_fwd_kernel(X, Y, Alpha, Gamma, Beta, HAVE_BETA: tl.constexpr, N: tl.constexpr, BLOCK_N: tl.constexpr):
|
| 35 |
+
col = tl.cast(tl.program_id(0), tl.int64) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 36 |
+
mask = col < N
|
| 37 |
+
row_id = tl.cast(tl.program_id(1), tl.int64)
|
| 38 |
+
|
| 39 |
+
X += row_id * N
|
| 40 |
+
Y += row_id * N
|
| 41 |
+
alpha = tl.load(Alpha).to(tl.float32)
|
| 42 |
+
|
| 43 |
+
gamma = tl.load(Gamma + col, mask=mask, other=0.0).to(tl.float32)
|
| 44 |
+
|
| 45 |
+
x = tl.load(X + col, mask=mask, other=0.0).to(tl.float32)
|
| 46 |
+
|
| 47 |
+
tanh_x = tanh(alpha * x)
|
| 48 |
+
y = tanh_x * gamma
|
| 49 |
+
if HAVE_BETA:
|
| 50 |
+
beta = tl.load(Beta + col, mask=mask, other=0.0).to(tl.float32)
|
| 51 |
+
y += beta
|
| 52 |
+
tl.store(Y + col, y, mask=mask)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
@triton.autotune(
|
| 56 |
+
configs=[
|
| 57 |
+
triton.Config({"BLOCK_N": bn}, num_stages=ns, num_warps=nw)
|
| 58 |
+
for bn in [1024, 2048, 4096]
|
| 59 |
+
for ns in [1, 2]
|
| 60 |
+
for nw in [4, 8, 16]
|
| 61 |
+
],
|
| 62 |
+
key=["N"],
|
| 63 |
+
# DA is indexed by program_id(0), so different BLOCK_N configs write to
|
| 64 |
+
# different slot counts per SM. Autotune trials don't zero outputs between
|
| 65 |
+
# runs, so stale slots from a prior trial would leak into da.sum(). Reset
|
| 66 |
+
# DA between trials to isolate each config's writes.
|
| 67 |
+
reset_to_zero=["DA"],
|
| 68 |
+
)
|
| 69 |
+
@triton.jit
|
| 70 |
+
def _dyt_bwd_kernel(
|
| 71 |
+
DY, DX, DA, DG, DB, X, Alpha, Gamma, HAVE_BETA: tl.constexpr, M, N: tl.constexpr, BLOCK_N: tl.constexpr
|
| 72 |
+
):
|
| 73 |
+
col = tl.cast(tl.program_id(0), tl.int64) * BLOCK_N + tl.arange(0, BLOCK_N)
|
| 74 |
+
mask = col < N
|
| 75 |
+
start_row_id = tl.cast(tl.program_id(1), tl.int64)
|
| 76 |
+
|
| 77 |
+
alpha = tl.load(Alpha).to(tl.float32)
|
| 78 |
+
da = 0.0
|
| 79 |
+
gamma = tl.load(Gamma + col, mask=mask, other=0.0).to(tl.float32)
|
| 80 |
+
dg = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 81 |
+
if HAVE_BETA:
|
| 82 |
+
db = tl.zeros((BLOCK_N,), dtype=tl.float32)
|
| 83 |
+
for row_id in range(start_row_id, M, tl.num_programs(1)):
|
| 84 |
+
x = tl.load(X + row_id * N + col, mask=mask, other=0.0).to(tl.float32)
|
| 85 |
+
dy = tl.load(DY + row_id * N + col, mask=mask, other=0.0).to(tl.float32)
|
| 86 |
+
tanh_x = tanh(alpha * x)
|
| 87 |
+
if HAVE_BETA:
|
| 88 |
+
db += dy
|
| 89 |
+
dg += dy * tanh_x
|
| 90 |
+
tmp = (1 - tanh_x * tanh_x) * dy * gamma
|
| 91 |
+
da += tl.sum(x * tmp, 0)
|
| 92 |
+
dx = alpha * tmp
|
| 93 |
+
tl.store(DX + row_id * N + col, dx, mask=mask)
|
| 94 |
+
|
| 95 |
+
tl.store(DG + start_row_id * N + col, dg, mask=mask)
|
| 96 |
+
if HAVE_BETA:
|
| 97 |
+
tl.store(DB + start_row_id * N + col, db, mask=mask)
|
| 98 |
+
tl.store(DA + start_row_id * tl.cdiv(N, 512) + tl.program_id(0), da)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def liger_dyt_fwd(x, alpha, gamma, beta):
|
| 102 |
+
assert x.is_contiguous()
|
| 103 |
+
HAVE_BETA = True if beta is not None else False
|
| 104 |
+
input_shape = x.shape
|
| 105 |
+
x = x.view(-1, input_shape[-1])
|
| 106 |
+
M, N = x.shape
|
| 107 |
+
|
| 108 |
+
y = torch.empty_like(x)
|
| 109 |
+
|
| 110 |
+
grid = lambda meta: (triton.cdiv(N, meta["BLOCK_N"]), M)
|
| 111 |
+
_dyt_fwd_kernel[grid](
|
| 112 |
+
x,
|
| 113 |
+
y,
|
| 114 |
+
alpha,
|
| 115 |
+
gamma,
|
| 116 |
+
beta,
|
| 117 |
+
HAVE_BETA,
|
| 118 |
+
N,
|
| 119 |
+
)
|
| 120 |
+
return y.view(input_shape)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def liger_dyt_bwd(dy, x, alpha, gamma, beta):
|
| 124 |
+
assert dy.is_contiguous()
|
| 125 |
+
input_shape = x.shape
|
| 126 |
+
x = x.view(-1, input_shape[-1])
|
| 127 |
+
M, N = x.shape
|
| 128 |
+
HAVE_BETA = True if beta is not None else False
|
| 129 |
+
|
| 130 |
+
device = infer_device()
|
| 131 |
+
if device == "cuda":
|
| 132 |
+
NUM_SMS = torch.cuda.get_device_properties(x.device).multi_processor_count
|
| 133 |
+
elif device == "xpu":
|
| 134 |
+
NUM_SMS = torch.xpu.get_device_properties(x.device).gpu_subslice_count
|
| 135 |
+
elif device == "npu":
|
| 136 |
+
NUM_SMS = get_npu_core_count()
|
| 137 |
+
da = torch.zeros(NUM_SMS, triton.cdiv(N, 512), dtype=torch.float32, device=x.device)
|
| 138 |
+
dg = torch.empty(NUM_SMS, N, dtype=torch.float32, device=x.device)
|
| 139 |
+
db = torch.empty(NUM_SMS, N, dtype=torch.float32, device=x.device) if HAVE_BETA else None
|
| 140 |
+
dx = torch.empty_like(dy)
|
| 141 |
+
|
| 142 |
+
grid = lambda meta: (triton.cdiv(N, meta["BLOCK_N"]), NUM_SMS)
|
| 143 |
+
_dyt_bwd_kernel[grid](dy, dx, da, dg, db, x, alpha, gamma, HAVE_BETA, M, N)
|
| 144 |
+
if HAVE_BETA:
|
| 145 |
+
db = db.sum(0).to(x.dtype)
|
| 146 |
+
dg = dg.sum(0).to(gamma.dtype)
|
| 147 |
+
da = da.sum().to(x.dtype).unsqueeze(0)
|
| 148 |
+
return dx.view(input_shape), da, dg, db
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class LigerDyTFunction(torch.autograd.Function):
|
| 152 |
+
@staticmethod
|
| 153 |
+
@ensure_contiguous
|
| 154 |
+
def forward(ctx, x, alpha, gamma, beta):
|
| 155 |
+
y = liger_dyt_fwd(x, alpha, gamma, beta)
|
| 156 |
+
ctx.save_for_backward(x, alpha, gamma, beta)
|
| 157 |
+
return y
|
| 158 |
+
|
| 159 |
+
@staticmethod
|
| 160 |
+
@ensure_contiguous
|
| 161 |
+
def backward(ctx, dy):
|
| 162 |
+
x, alpha, gamma, beta = ctx.saved_tensors
|
| 163 |
+
dx, dalpha, dgamma, dbeta = liger_dyt_bwd(dy, x, alpha, gamma, beta)
|
| 164 |
+
return dx, dalpha, dgamma, dbeta
|
build/torch-xpu/fused_linear_cross_entropy.py
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
|
| 4 |
+
from .cross_entropy import liger_cross_entropy_kernel
|
| 5 |
+
from .utils import amp_custom_bwd
|
| 6 |
+
from .utils import amp_custom_fwd
|
| 7 |
+
from .utils import element_mul_kernel
|
| 8 |
+
from .utils import is_hip
|
| 9 |
+
from .utils import infer_device
|
| 10 |
+
|
| 11 |
+
# The hard limit of TRITON_MAX_TENSOR_NUMEL is 1048576 https://github.com/triton-lang/triton/blob/ba42a5c68fd0505f8c42f4202d53be0f8d9a5fe0/python/triton/language/core.py#L19
|
| 12 |
+
# However, setting limit as 65536 as in LayerNorm tutorial is faster because of less register spilling
|
| 13 |
+
# The optimal maximum block size depends on your hardware, your kernel, and your dtype
|
| 14 |
+
MAX_FUSED_SIZE = 2048 if infer_device() == "npu" else 65536 // 2
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def fused_linear_cross_entropy_forward(
|
| 18 |
+
_input,
|
| 19 |
+
weight,
|
| 20 |
+
target,
|
| 21 |
+
ce_weight=None,
|
| 22 |
+
bias=None,
|
| 23 |
+
ignore_index=-100,
|
| 24 |
+
lse_square_scale=0.0,
|
| 25 |
+
label_smoothing=0.0,
|
| 26 |
+
reduction="mean",
|
| 27 |
+
softcap=None,
|
| 28 |
+
return_z_loss=False,
|
| 29 |
+
accum_dtype=None,
|
| 30 |
+
use_token_scaling=False,
|
| 31 |
+
return_token_accuracy=False,
|
| 32 |
+
return_predicted_tokens=False,
|
| 33 |
+
):
|
| 34 |
+
assert isinstance(return_z_loss, bool), f"return_z_loss must be True or False. Got: {return_z_loss}"
|
| 35 |
+
assert isinstance(return_token_accuracy, bool), (
|
| 36 |
+
f"return_token_accuracy must be True or False. Got: {return_token_accuracy}"
|
| 37 |
+
)
|
| 38 |
+
assert isinstance(return_predicted_tokens, bool), (
|
| 39 |
+
f"return_predicted_tokens must be True or False. Got: {return_predicted_tokens}"
|
| 40 |
+
)
|
| 41 |
+
device = _input.device
|
| 42 |
+
|
| 43 |
+
input_requires_grad = _input.requires_grad
|
| 44 |
+
|
| 45 |
+
# inputs have shape: BT x H
|
| 46 |
+
# materialized activations will have shape: BT x V
|
| 47 |
+
# the increase in memory = BT x V
|
| 48 |
+
# reduction can be achieved by partitioning the number of tokens BT into smaller chunks.
|
| 49 |
+
# for ex: if we were to achieve the same memory consumption as BT x H, then the chunk size should be:
|
| 50 |
+
# inc_factor = (V+H-1)//H, chunk_size = (BT + inc_factor - 1)//inc_factor
|
| 51 |
+
# for ex: BT = 4096*4, V = 32000, H = 4096 ==> inc_factor = 8, chunk_size = 2048
|
| 52 |
+
BT, H = _input.shape
|
| 53 |
+
V = weight.shape[0]
|
| 54 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 55 |
+
|
| 56 |
+
inc_factor = triton.cdiv(V, H) # (V + H - 1) // H
|
| 57 |
+
chunk_size = triton.next_power_of_2(triton.cdiv(BT, inc_factor)) # (BT + inc_factor - 1) // inc_factor
|
| 58 |
+
num_chunks = triton.cdiv(BT, chunk_size) # (BT + chunk_size - 1) // chunk_size
|
| 59 |
+
|
| 60 |
+
grad_input = torch.zeros_like(_input, device=device)
|
| 61 |
+
|
| 62 |
+
# we use fp32 for loss and gradients accumulator
|
| 63 |
+
if input_requires_grad:
|
| 64 |
+
if accum_dtype is None:
|
| 65 |
+
grad_weight = torch.zeros_like(weight, device=device) if weight.requires_grad else None
|
| 66 |
+
grad_bias = torch.zeros_like(bias, device=device) if bias is not None else None
|
| 67 |
+
else:
|
| 68 |
+
grad_weight = torch.zeros_like(weight, dtype=accum_dtype, device=device) if weight.requires_grad else None
|
| 69 |
+
grad_bias = torch.zeros_like(bias, dtype=accum_dtype, device=device) if bias is not None else None
|
| 70 |
+
else:
|
| 71 |
+
grad_weight = None
|
| 72 |
+
grad_bias = None
|
| 73 |
+
|
| 74 |
+
loss_1d = torch.zeros(BT, dtype=torch.float32, device=device)
|
| 75 |
+
z_loss_1d = torch.zeros(BT, dtype=_input.dtype, device=_input.device) if return_z_loss else None
|
| 76 |
+
token_accuracy_1d = torch.zeros(BT, dtype=torch.float32, device=device) if return_token_accuracy else None
|
| 77 |
+
predicted_tokens_1d = torch.full((BT,), -1, dtype=torch.int64, device=device) if return_predicted_tokens else None
|
| 78 |
+
|
| 79 |
+
# TODO: evaluate how CUDA synchronization caused by .item() affects the speed
|
| 80 |
+
target_mask = target != ignore_index
|
| 81 |
+
total_n_non_ignore = target_mask.sum().item()
|
| 82 |
+
total_sum_non_ignore_ce_weight = total_n_non_ignore
|
| 83 |
+
ce_weight_sum = 0.0
|
| 84 |
+
if ce_weight is not None:
|
| 85 |
+
assert ce_weight.shape[0] == V, f"If given, weight has to be a Tensor of size V. Got: {ce_weight.shape}"
|
| 86 |
+
assert torch.is_floating_point(ce_weight), (
|
| 87 |
+
f"If given, weight has to be a Tensor of floating point dtype. Got: {ce_weight.dtype}"
|
| 88 |
+
)
|
| 89 |
+
total_sum_non_ignore_ce_weight = (
|
| 90 |
+
torch.gather(ce_weight, dim=0, index=target.masked_select(target_mask)).sum().item()
|
| 91 |
+
)
|
| 92 |
+
ce_weight_sum = ce_weight.sum().item()
|
| 93 |
+
if ce_weight.stride(-1) != 1:
|
| 94 |
+
ce_weight = ce_weight.contiguous()
|
| 95 |
+
|
| 96 |
+
for chunk_id in range(num_chunks):
|
| 97 |
+
start_idx = chunk_id * chunk_size
|
| 98 |
+
end_idx = min((chunk_id + 1) * chunk_size, BT)
|
| 99 |
+
_input_chunk = _input[start_idx:end_idx] # chunk_size x H
|
| 100 |
+
|
| 101 |
+
# when doing matmul, use the original precision
|
| 102 |
+
logits_chunk = _input_chunk @ weight.t() # chunk_size x V
|
| 103 |
+
if bias is not None:
|
| 104 |
+
logits_chunk = logits_chunk + bias
|
| 105 |
+
|
| 106 |
+
target_chunk = target[start_idx:end_idx] # chunk_size,
|
| 107 |
+
|
| 108 |
+
n_rows = logits_chunk.shape[0]
|
| 109 |
+
|
| 110 |
+
# Compute predicted probabilities for token scaling if needed
|
| 111 |
+
if use_token_scaling:
|
| 112 |
+
# Compute softmax probabilities for scaling
|
| 113 |
+
# We need to compute this before the cross entropy kernel modifies logits_chunk
|
| 114 |
+
logits_for_softmax = logits_chunk.detach().clone() # Detach to avoid gradient flow
|
| 115 |
+
if softcap is not None:
|
| 116 |
+
logits_for_softmax = softcap * torch.tanh(logits_for_softmax / softcap)
|
| 117 |
+
|
| 118 |
+
# Compute softmax to get predicted probabilities
|
| 119 |
+
probs = torch.softmax(logits_for_softmax, dim=-1)
|
| 120 |
+
|
| 121 |
+
# Get predicted probabilities for token scaling, handling ignored targets
|
| 122 |
+
valid_target_mask = target_chunk != ignore_index
|
| 123 |
+
valid_targets = target_chunk[valid_target_mask]
|
| 124 |
+
|
| 125 |
+
if len(valid_targets) > 0:
|
| 126 |
+
# Gather probabilities only for valid targets
|
| 127 |
+
valid_probs = probs[valid_target_mask]
|
| 128 |
+
pred_probs_valid = torch.gather(valid_probs, -1, valid_targets.unsqueeze(-1)).squeeze(-1)
|
| 129 |
+
|
| 130 |
+
# Create full tensor with zeros for ignored targets
|
| 131 |
+
pred_probs = torch.zeros_like(target_chunk, dtype=probs.dtype, device=probs.device)
|
| 132 |
+
pred_probs[valid_target_mask] = pred_probs_valid
|
| 133 |
+
else:
|
| 134 |
+
# All targets are ignored
|
| 135 |
+
pred_probs = torch.zeros_like(target_chunk, dtype=probs.dtype, device=probs.device)
|
| 136 |
+
|
| 137 |
+
# Store the scaling factors
|
| 138 |
+
scaling_factors = pred_probs.detach() # Detach to ensure no gradient flow
|
| 139 |
+
|
| 140 |
+
# unreduced loss
|
| 141 |
+
loss_1d_slice = loss_1d[start_idx:end_idx] # chunk_size,
|
| 142 |
+
z_loss_1d_slice = z_loss_1d[start_idx:end_idx] if return_z_loss else None
|
| 143 |
+
token_accuracy_1d_slice = token_accuracy_1d[start_idx:end_idx] if return_token_accuracy else None
|
| 144 |
+
predicted_tokens_1d_slice = predicted_tokens_1d[start_idx:end_idx] if return_predicted_tokens else None
|
| 145 |
+
|
| 146 |
+
# ensure _input and target are contiguous
|
| 147 |
+
logits_chunk = logits_chunk.contiguous()
|
| 148 |
+
target_chunk = target_chunk.contiguous()
|
| 149 |
+
|
| 150 |
+
# Here we calculate the gradient of logits_chunk in place so we can save memory.
|
| 151 |
+
liger_cross_entropy_kernel[(n_rows,)](
|
| 152 |
+
X_ptr=logits_chunk,
|
| 153 |
+
X_stride=logits_chunk.stride(-2),
|
| 154 |
+
Y_ptr=target_chunk,
|
| 155 |
+
Y_stride=target_chunk.stride(-1), # always 1
|
| 156 |
+
weight_ptr=ce_weight,
|
| 157 |
+
loss_ptr=loss_1d_slice,
|
| 158 |
+
z_loss_ptr=z_loss_1d_slice,
|
| 159 |
+
loss_stride=loss_1d_slice.stride(-1), # always 1
|
| 160 |
+
token_accuracy_ptr=token_accuracy_1d_slice,
|
| 161 |
+
token_accuracy_stride=token_accuracy_1d_slice.stride(-1)
|
| 162 |
+
if return_token_accuracy
|
| 163 |
+
else 0, # always 1 if accuracy is enabled
|
| 164 |
+
predicted_tokens_ptr=predicted_tokens_1d_slice,
|
| 165 |
+
predicted_tokens_stride=predicted_tokens_1d_slice.stride(-1)
|
| 166 |
+
if return_predicted_tokens
|
| 167 |
+
else 0, # always 1 if predicted tokens is enabled
|
| 168 |
+
n_cols=V,
|
| 169 |
+
n_non_ignore=total_n_non_ignore,
|
| 170 |
+
sum_non_ignore_weight=total_sum_non_ignore_ce_weight,
|
| 171 |
+
weight_sum=ce_weight_sum,
|
| 172 |
+
ignore_index=ignore_index,
|
| 173 |
+
lse_square_scale=lse_square_scale,
|
| 174 |
+
label_smoothing=label_smoothing,
|
| 175 |
+
reduction=reduction,
|
| 176 |
+
softcap=softcap,
|
| 177 |
+
RETURN_Z_LOSS=return_z_loss,
|
| 178 |
+
RETURN_TOKEN_ACCURACY=return_token_accuracy,
|
| 179 |
+
RETURN_PREDICTED_TOKENS=return_predicted_tokens,
|
| 180 |
+
HAS_WEIGHT=True if ce_weight is not None else False,
|
| 181 |
+
HAS_SOFTCAPPING=True if softcap is not None else False,
|
| 182 |
+
HAS_GRADIENTS=input_requires_grad,
|
| 183 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 184 |
+
num_warps=32 if not is_hip() else 16,
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
# Apply token scaling if requested
|
| 188 |
+
if use_token_scaling:
|
| 189 |
+
loss_1d_slice = loss_1d_slice * scaling_factors
|
| 190 |
+
if return_z_loss:
|
| 191 |
+
z_loss_1d_slice = z_loss_1d_slice * scaling_factors
|
| 192 |
+
|
| 193 |
+
loss_1d[start_idx:end_idx] = loss_1d_slice
|
| 194 |
+
if return_z_loss:
|
| 195 |
+
z_loss_1d[start_idx:end_idx] = z_loss_1d_slice
|
| 196 |
+
if return_token_accuracy:
|
| 197 |
+
token_accuracy_1d[start_idx:end_idx] = token_accuracy_1d_slice
|
| 198 |
+
if return_predicted_tokens:
|
| 199 |
+
predicted_tokens_1d[start_idx:end_idx] = predicted_tokens_1d_slice
|
| 200 |
+
grad_logits_chunk = logits_chunk # chunk_size x V
|
| 201 |
+
|
| 202 |
+
# Apply token scaling to gradients if requested
|
| 203 |
+
if use_token_scaling:
|
| 204 |
+
# Expand scaling factors to match gradient dimensions
|
| 205 |
+
scaling_factors_expanded = scaling_factors.unsqueeze(-1) # chunk_size x 1
|
| 206 |
+
grad_logits_chunk = grad_logits_chunk * scaling_factors_expanded
|
| 207 |
+
|
| 208 |
+
if input_requires_grad:
|
| 209 |
+
grad_input[start_idx:end_idx] = grad_logits_chunk @ weight
|
| 210 |
+
|
| 211 |
+
if grad_weight is not None and input_requires_grad:
|
| 212 |
+
grad_weight += torch.mm(grad_logits_chunk.t(), _input_chunk).float()
|
| 213 |
+
|
| 214 |
+
if bias is not None and input_requires_grad:
|
| 215 |
+
torch.add(
|
| 216 |
+
input=grad_bias,
|
| 217 |
+
other=grad_logits_chunk.sum(dim=0),
|
| 218 |
+
out=grad_bias,
|
| 219 |
+
alpha=1.0,
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
# Need extra calculations for backward if reduction=='none'. Not supporting reduction='none' now.
|
| 223 |
+
# if reduction == "none":
|
| 224 |
+
# loss = loss_1d
|
| 225 |
+
# z_loss = z_loss_1d if return_z_loss else None
|
| 226 |
+
|
| 227 |
+
if reduction == "none":
|
| 228 |
+
# Return per-token losses
|
| 229 |
+
loss = loss_1d
|
| 230 |
+
z_loss = z_loss_1d if return_z_loss else None
|
| 231 |
+
token_accuracy = token_accuracy_1d if return_token_accuracy else None
|
| 232 |
+
else:
|
| 233 |
+
loss = torch.sum(loss_1d)
|
| 234 |
+
z_loss = torch.sum(z_loss_1d) if return_z_loss else None
|
| 235 |
+
# For accuracy, we compute the mean across all non-ignored tokens
|
| 236 |
+
token_accuracy = torch.sum(token_accuracy_1d) / total_n_non_ignore if return_token_accuracy else None
|
| 237 |
+
|
| 238 |
+
predicted_tokens = predicted_tokens_1d if return_predicted_tokens else None
|
| 239 |
+
|
| 240 |
+
# Cast back to original dtype
|
| 241 |
+
grad_weight = grad_weight.to(weight.dtype) if grad_weight is not None else None
|
| 242 |
+
grad_bias = grad_bias.to(bias.dtype) if grad_bias is not None else None
|
| 243 |
+
|
| 244 |
+
return loss, z_loss, token_accuracy, predicted_tokens, grad_input, grad_weight, grad_bias
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def fused_linear_cross_entropy_backward(grad_output, grad_input, grad_weight, grad_bias):
|
| 248 |
+
# If cross entropy is the last layer, grad_output is 1.0. Skip the mul to save time
|
| 249 |
+
if not torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
|
| 250 |
+
# We use a Triton kernel instead of a PyTorch operation because modifying inputs in-place
|
| 251 |
+
# for gradient storage and backward multiple times causes anomalies with PyTorch but not with Triton.
|
| 252 |
+
BT, H = grad_input.shape
|
| 253 |
+
n_rows = BT
|
| 254 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(H))
|
| 255 |
+
|
| 256 |
+
element_mul_kernel[(n_rows,)](
|
| 257 |
+
grad_input,
|
| 258 |
+
grad_input.stride(-2),
|
| 259 |
+
grad_output,
|
| 260 |
+
H,
|
| 261 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 262 |
+
num_warps=32 if not is_hip() else 16,
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
# handle grad_weight
|
| 266 |
+
if grad_weight is not None:
|
| 267 |
+
V, H = grad_weight.shape
|
| 268 |
+
n_rows = V
|
| 269 |
+
|
| 270 |
+
element_mul_kernel[(n_rows,)](
|
| 271 |
+
grad_weight,
|
| 272 |
+
grad_weight.stride(-2),
|
| 273 |
+
grad_output,
|
| 274 |
+
H,
|
| 275 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 276 |
+
num_warps=32 if not is_hip() else 16,
|
| 277 |
+
)
|
| 278 |
+
|
| 279 |
+
if grad_bias is not None:
|
| 280 |
+
V = grad_bias.shape[0]
|
| 281 |
+
n_rows = V
|
| 282 |
+
|
| 283 |
+
element_mul_kernel[(n_rows,)](
|
| 284 |
+
grad_bias,
|
| 285 |
+
grad_bias.stride(-1),
|
| 286 |
+
grad_output,
|
| 287 |
+
1,
|
| 288 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 289 |
+
num_warps=32 if not is_hip() else 16,
|
| 290 |
+
)
|
| 291 |
+
return grad_input, grad_weight, grad_bias
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
class LigerFusedLinearCrossEntropyFunction(torch.autograd.Function):
|
| 295 |
+
@staticmethod
|
| 296 |
+
@amp_custom_fwd
|
| 297 |
+
def forward(
|
| 298 |
+
ctx,
|
| 299 |
+
_input,
|
| 300 |
+
weight,
|
| 301 |
+
target,
|
| 302 |
+
bias=None,
|
| 303 |
+
ce_weight=None,
|
| 304 |
+
ignore_index=-100,
|
| 305 |
+
lse_square_scale=0.0,
|
| 306 |
+
label_smoothing=0.0,
|
| 307 |
+
reduction="mean",
|
| 308 |
+
softcap=None,
|
| 309 |
+
return_z_loss: bool = False,
|
| 310 |
+
accum_dtype=None,
|
| 311 |
+
use_token_scaling: bool = False,
|
| 312 |
+
return_token_accuracy: bool = False,
|
| 313 |
+
return_predicted_tokens: bool = False,
|
| 314 |
+
):
|
| 315 |
+
"""
|
| 316 |
+
Fusing the last linear layer with cross-entropy loss
|
| 317 |
+
Reference: https://github.com/mgmalek/efficient_cross_entropy
|
| 318 |
+
|
| 319 |
+
Handle the forward and backward pass of the final linear layer via cross-entropy loss by avoiding
|
| 320 |
+
the materialization of the large logits tensor. Since Cross Entropy Loss is the last layer, we can
|
| 321 |
+
compute the gradient at the forward pass. By doing so, we don't have to store the _input and target
|
| 322 |
+
for the backward pass.
|
| 323 |
+
|
| 324 |
+
_input: (B*T, H) where B is batch size, T is sequence length, H is hidden dimension.
|
| 325 |
+
target: (B*T) where each value is in [0, V-1]
|
| 326 |
+
weight: (V, H) where V is the number of classes
|
| 327 |
+
bias: (V) where V is the number of classes
|
| 328 |
+
ce_weight: a manual rescaling weight given to each class. If given, has to be a Tensor of size V and floating point dtype
|
| 329 |
+
ignore_index: the index to ignore in the target
|
| 330 |
+
label_smoothing (float): The amount of smoothing when computing the loss, where 0.0 means no smoothing.
|
| 331 |
+
reduction: reduction to apply
|
| 332 |
+
accum_dtype (torch.dtype): the dtype of intermediate result buffers for weight and bias gradient accumulations.
|
| 333 |
+
Recommended to set `accum_dtype` to higher precision, e.g. `torch.float32`, if the training is unstable with original dtype. Default: `None`, performing accumulations in original dtype
|
| 334 |
+
use_token_scaling (bool): whether to scale each token's loss by its predicted probability (detached).
|
| 335 |
+
When True, each token's loss is multiplied by the model's predicted probability for that token's true class.
|
| 336 |
+
Default: False.
|
| 337 |
+
return_token_accuracy (bool): When `return_token_accuracy` is `True`, computes and returns per-token accuracy without materializing logits. Default: `False`
|
| 338 |
+
return_predicted_tokens (bool): When `return_predicted_tokens` is `True`, returns per-token predicted class indices (argmax) without materializing logits. Default: `False`
|
| 339 |
+
"""
|
| 340 |
+
|
| 341 |
+
loss, z_loss, token_accuracy, predicted_tokens, grad_input, grad_weight, grad_bias = (
|
| 342 |
+
fused_linear_cross_entropy_forward(
|
| 343 |
+
_input=_input,
|
| 344 |
+
weight=weight,
|
| 345 |
+
target=target,
|
| 346 |
+
bias=bias,
|
| 347 |
+
ce_weight=ce_weight,
|
| 348 |
+
ignore_index=ignore_index,
|
| 349 |
+
lse_square_scale=lse_square_scale,
|
| 350 |
+
label_smoothing=label_smoothing,
|
| 351 |
+
reduction=reduction,
|
| 352 |
+
softcap=softcap,
|
| 353 |
+
return_z_loss=return_z_loss,
|
| 354 |
+
accum_dtype=accum_dtype,
|
| 355 |
+
use_token_scaling=use_token_scaling,
|
| 356 |
+
return_token_accuracy=return_token_accuracy,
|
| 357 |
+
return_predicted_tokens=return_predicted_tokens,
|
| 358 |
+
)
|
| 359 |
+
)
|
| 360 |
+
# downcast to dtype and store for backward
|
| 361 |
+
ctx.save_for_backward(
|
| 362 |
+
grad_input.detach(),
|
| 363 |
+
grad_weight.detach() if grad_weight is not None else None,
|
| 364 |
+
grad_bias.detach() if grad_bias is not None else None,
|
| 365 |
+
)
|
| 366 |
+
ctx.return_z_loss = return_z_loss
|
| 367 |
+
ctx.return_token_accuracy = return_token_accuracy
|
| 368 |
+
ctx.return_predicted_tokens = return_predicted_tokens
|
| 369 |
+
return loss, z_loss, token_accuracy, predicted_tokens
|
| 370 |
+
|
| 371 |
+
@staticmethod
|
| 372 |
+
@amp_custom_bwd
|
| 373 |
+
def backward(ctx, grad_output, grad_output2, grad_output3, grad_output4):
|
| 374 |
+
if ctx.return_z_loss:
|
| 375 |
+
del grad_output2 # z_loss is only for logging
|
| 376 |
+
if ctx.return_token_accuracy:
|
| 377 |
+
del grad_output3 # token_accuracy is only for metrics
|
| 378 |
+
if ctx.return_predicted_tokens:
|
| 379 |
+
del grad_output4 # predicted_tokens is only for metrics
|
| 380 |
+
(grad_input, grad_weight, grad_bias) = ctx.saved_tensors
|
| 381 |
+
grad_input, grad_weight, grad_bias = fused_linear_cross_entropy_backward(
|
| 382 |
+
grad_output, grad_input, grad_weight, grad_bias
|
| 383 |
+
)
|
| 384 |
+
return (
|
| 385 |
+
grad_input,
|
| 386 |
+
grad_weight,
|
| 387 |
+
None,
|
| 388 |
+
grad_bias,
|
| 389 |
+
None,
|
| 390 |
+
None,
|
| 391 |
+
None,
|
| 392 |
+
None,
|
| 393 |
+
None,
|
| 394 |
+
None,
|
| 395 |
+
None,
|
| 396 |
+
None,
|
| 397 |
+
None, # use_token_scaling
|
| 398 |
+
None, # return_token_accuracy
|
| 399 |
+
None, # return_predicted_tokens
|
| 400 |
+
)
|
build/torch-xpu/geglu.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import operator
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from .utils import calculate_settings
|
| 8 |
+
from .utils import compare_version
|
| 9 |
+
from .utils import ensure_contiguous
|
| 10 |
+
from .utils import is_npu_available
|
| 11 |
+
|
| 12 |
+
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
|
| 13 |
+
try:
|
| 14 |
+
# typical import path with dispatch available
|
| 15 |
+
from triton.language.extra.libdevice import tanh
|
| 16 |
+
except ModuleNotFoundError:
|
| 17 |
+
# for working with NGC containers
|
| 18 |
+
from triton.language.extra.cuda.libdevice import tanh
|
| 19 |
+
else:
|
| 20 |
+
from triton.language.math import tanh
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@triton.jit
|
| 24 |
+
def _geglu_tanh_forward_kernel(a, b, c, stride, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr):
|
| 25 |
+
program_id = tl.program_id(0).to(tl.int64)
|
| 26 |
+
|
| 27 |
+
# locate start index
|
| 28 |
+
a += program_id * stride
|
| 29 |
+
b += program_id * stride
|
| 30 |
+
c += program_id * stride
|
| 31 |
+
|
| 32 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 33 |
+
mask = col_offsets < n_cols
|
| 34 |
+
a_row = tl.load(a + col_offsets, mask=mask, other=0).to(tl.float32)
|
| 35 |
+
b_row = tl.load(b + col_offsets, mask=mask, other=0)
|
| 36 |
+
|
| 37 |
+
# tanh approximation form of GELU is computed with:
|
| 38 |
+
# 0.5 * a * (1 + tanh(sqrt(2 / pi) * (a + 0.044715 * a^3)))
|
| 39 |
+
sqrt_2_over_pi = 0.7978845608028654 # sqrt(2 / pi)
|
| 40 |
+
a_cubed = a_row * a_row * a_row
|
| 41 |
+
tanh_arg = sqrt_2_over_pi * (a_row + 0.044715 * a_cubed)
|
| 42 |
+
tanh_result = tanh(tanh_arg)
|
| 43 |
+
geglu_a = 0.5 * a_row * (1 + tanh_result)
|
| 44 |
+
c_row = geglu_a.cast(b_row.dtype) * b_row
|
| 45 |
+
tl.store(c + col_offsets, c_row, mask=mask)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@triton.jit
|
| 49 |
+
def _geglu_tanh_backward_kernel(dc, a, b, stride, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr):
|
| 50 |
+
program_id = tl.program_id(0).to(tl.int64)
|
| 51 |
+
|
| 52 |
+
# locate start index
|
| 53 |
+
dc += program_id * stride
|
| 54 |
+
a += program_id * stride
|
| 55 |
+
b += program_id * stride
|
| 56 |
+
|
| 57 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 58 |
+
mask = col_offsets < n_cols
|
| 59 |
+
|
| 60 |
+
dc_row = tl.load(dc + col_offsets, mask=mask, other=0)
|
| 61 |
+
a_row = tl.load(a + col_offsets, mask=mask, other=0).to(tl.float32)
|
| 62 |
+
b_row = tl.load(b + col_offsets, mask=mask, other=0)
|
| 63 |
+
|
| 64 |
+
# recomputation to save memory
|
| 65 |
+
sqrt_2_over_pi = 0.7978845608028654 # sqrt(2 / pi)
|
| 66 |
+
a_cubed = a_row * a_row * a_row
|
| 67 |
+
tanh_arg = sqrt_2_over_pi * (a_row + 0.044715 * a_cubed)
|
| 68 |
+
tanh_result = tanh(tanh_arg)
|
| 69 |
+
geglu_a = 0.5 * a_row * (1 + tanh_result)
|
| 70 |
+
geglu_a = geglu_a.to(dc_row.dtype).to(tl.float32)
|
| 71 |
+
|
| 72 |
+
db_row = dc_row.cast(tl.float32) * geglu_a
|
| 73 |
+
|
| 74 |
+
# Gradient w.r.t. a can be computed with:
|
| 75 |
+
# b * (0.5 * (1 + tanh(z)) + 0.5 * a * (1 - tanh(z)^2) * (sqrt(2/pi) * (1 + 3 * 0.044715 * a^2)))
|
| 76 |
+
# where z = sqrt(2/pi) * (a + 0.044715 * a^3)
|
| 77 |
+
term1 = 0.5 * (1 + tanh_result)
|
| 78 |
+
tanh_sq = tanh_result * tanh_result
|
| 79 |
+
term2 = 0.5 * a_row * (1 - tanh_sq) * (sqrt_2_over_pi * (1 + 3 * 0.044715 * a_row * a_row))
|
| 80 |
+
da_row = dc_row * b_row * (term1 + term2)
|
| 81 |
+
|
| 82 |
+
tl.store(a + col_offsets, da_row, mask=mask)
|
| 83 |
+
tl.store(b + col_offsets, db_row.to(dc_row.dtype), mask=mask)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def geglu_forward(a, b):
|
| 87 |
+
ori_shape = a.shape
|
| 88 |
+
|
| 89 |
+
n_cols = ori_shape[-1]
|
| 90 |
+
a = a.view(-1, n_cols)
|
| 91 |
+
b = b.view(-1, n_cols)
|
| 92 |
+
c = torch.empty_like(a)
|
| 93 |
+
n_rows = a.shape[0]
|
| 94 |
+
|
| 95 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 96 |
+
|
| 97 |
+
_geglu_tanh_forward_kernel[(n_rows,)](
|
| 98 |
+
a,
|
| 99 |
+
b,
|
| 100 |
+
c,
|
| 101 |
+
c.stride(-2),
|
| 102 |
+
n_cols=n_cols,
|
| 103 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 104 |
+
num_warps=num_warps,
|
| 105 |
+
)
|
| 106 |
+
return a, b, c.view(*ori_shape)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def geglu_backward(a, b, dc):
|
| 110 |
+
ori_shape = dc.shape
|
| 111 |
+
n_cols = ori_shape[-1]
|
| 112 |
+
dc = dc.view(-1, n_cols)
|
| 113 |
+
n_rows = dc.shape[0]
|
| 114 |
+
|
| 115 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 116 |
+
|
| 117 |
+
_geglu_tanh_backward_kernel[(n_rows,)](
|
| 118 |
+
dc,
|
| 119 |
+
a,
|
| 120 |
+
b,
|
| 121 |
+
dc.stride(-2),
|
| 122 |
+
n_cols=n_cols,
|
| 123 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 124 |
+
num_warps=num_warps,
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
return a.view(*ori_shape), b.view(*ori_shape)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
class LigerGELUMulFunction(torch.autograd.Function):
|
| 131 |
+
@staticmethod
|
| 132 |
+
@ensure_contiguous
|
| 133 |
+
def forward(ctx, a, b):
|
| 134 |
+
a, b, c = geglu_forward(a, b)
|
| 135 |
+
ctx.save_for_backward(a, b)
|
| 136 |
+
return c
|
| 137 |
+
|
| 138 |
+
@staticmethod
|
| 139 |
+
@ensure_contiguous
|
| 140 |
+
def backward(ctx, dc):
|
| 141 |
+
a, b = ctx.saved_tensors
|
| 142 |
+
a, b = geglu_backward(a, b, dc)
|
| 143 |
+
return a, b
|
build/torch-xpu/group_norm.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import operator
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from .utils import compare_version
|
| 8 |
+
from .utils import ensure_contiguous
|
| 9 |
+
from .utils import infer_device
|
| 10 |
+
from .utils import is_npu_available
|
| 11 |
+
|
| 12 |
+
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
|
| 13 |
+
try:
|
| 14 |
+
# typical import path with dispatch available
|
| 15 |
+
from triton.language.extra.libdevice import rsqrt
|
| 16 |
+
except ModuleNotFoundError:
|
| 17 |
+
# for working with NGC containers
|
| 18 |
+
from triton.language.extra.cuda.libdevice import rsqrt
|
| 19 |
+
else:
|
| 20 |
+
from triton.language.math import rsqrt
|
| 21 |
+
|
| 22 |
+
if infer_device() == "npu":
|
| 23 |
+
MAX_FUSED_SIZE = 16384 # 8192
|
| 24 |
+
else:
|
| 25 |
+
MAX_FUSED_SIZE = 65536
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@triton.jit
|
| 29 |
+
def _group_norm_forward_kernel(
|
| 30 |
+
Y_ptr, # pointer to output, shape (n_rows, n_groups, hidden_size)
|
| 31 |
+
Y_row_stride, # stride of each row in output
|
| 32 |
+
Y_col_stride, # stride of each column in output
|
| 33 |
+
X_ptr, # pointer to input, shape (n_rows, n_groups, hidden_size)
|
| 34 |
+
X_row_stride, # stride of each row in input
|
| 35 |
+
X_col_stride, # stride of each column in input
|
| 36 |
+
Mean_ptr, # pointer to mean, shape (n_rows, n_groups)
|
| 37 |
+
Mean_row_stride, # stride of each row in mean
|
| 38 |
+
Mean_col_stride, # stride of each column in mean
|
| 39 |
+
RSTD_ptr, # pointer to rstd, shape (n_rows, n_groups)
|
| 40 |
+
RSTD_row_stride, # stride of each row in rstd
|
| 41 |
+
RSTD_col_stride, # stride of each column in rstd
|
| 42 |
+
W_ptr, # pointer to W
|
| 43 |
+
B_ptr, # pointer to B
|
| 44 |
+
hidden_size, # hidden size of X
|
| 45 |
+
channels_per_group, # the number of channels per group
|
| 46 |
+
eps,
|
| 47 |
+
BLOCK_SIZE: tl.constexpr,
|
| 48 |
+
):
|
| 49 |
+
"""
|
| 50 |
+
References:
|
| 51 |
+
https://nn.labml.ai/normalization/group_norm/index.html
|
| 52 |
+
"""
|
| 53 |
+
batch_idx = tl.program_id(0)
|
| 54 |
+
group_idx = tl.program_id(1)
|
| 55 |
+
|
| 56 |
+
X_ptr += batch_idx * X_row_stride + group_idx * X_col_stride
|
| 57 |
+
Y_ptr += batch_idx * Y_row_stride + group_idx * Y_col_stride
|
| 58 |
+
|
| 59 |
+
block_range = tl.arange(0, BLOCK_SIZE)
|
| 60 |
+
|
| 61 |
+
# Compute mean and variance using the online algorithm
|
| 62 |
+
s = 0.0
|
| 63 |
+
squared_sum = 0.0
|
| 64 |
+
for i in tl.range(0, hidden_size, BLOCK_SIZE):
|
| 65 |
+
hidden_size_offsets = i + block_range
|
| 66 |
+
mask = hidden_size_offsets < hidden_size
|
| 67 |
+
X = tl.load(X_ptr + hidden_size_offsets, mask=mask, other=0.0)
|
| 68 |
+
s += tl.sum(X)
|
| 69 |
+
# X**2
|
| 70 |
+
squared_sum += tl.sum(X * X)
|
| 71 |
+
|
| 72 |
+
m = s / hidden_size
|
| 73 |
+
|
| 74 |
+
# variance = E[X**2] - E[X]**2
|
| 75 |
+
variance = (squared_sum / hidden_size) - (m * m)
|
| 76 |
+
|
| 77 |
+
# 1/std
|
| 78 |
+
rstd = rsqrt(variance + eps)
|
| 79 |
+
|
| 80 |
+
# Normalize — flat loop over full hidden_size (not per-channel)
|
| 81 |
+
# This avoids the nested channel × per_channel_hidden loop where
|
| 82 |
+
# BLOCK_SIZE >> hidden_size_per_channel causes massive padding waste.
|
| 83 |
+
hidden_size_per_channel = hidden_size // channels_per_group
|
| 84 |
+
for i in tl.range(0, hidden_size, BLOCK_SIZE):
|
| 85 |
+
hidden_size_offsets = i + block_range
|
| 86 |
+
mask = hidden_size_offsets < hidden_size
|
| 87 |
+
X = tl.load(X_ptr + hidden_size_offsets, mask=mask, other=m)
|
| 88 |
+
# Determine which channel each element belongs to, then load W/B
|
| 89 |
+
local_channel = hidden_size_offsets // hidden_size_per_channel
|
| 90 |
+
global_channel = group_idx * channels_per_group + local_channel
|
| 91 |
+
W = tl.load(W_ptr + global_channel, mask=mask)
|
| 92 |
+
B = tl.load(B_ptr + global_channel, mask=mask)
|
| 93 |
+
Y = (X - m) * rstd * W + B
|
| 94 |
+
tl.store(Y_ptr + hidden_size_offsets, Y, mask=mask)
|
| 95 |
+
|
| 96 |
+
tl.store(Mean_ptr + batch_idx * Mean_row_stride + group_idx * Mean_col_stride, m)
|
| 97 |
+
tl.store(RSTD_ptr + batch_idx * RSTD_row_stride + group_idx * RSTD_col_stride, rstd)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
@triton.jit
|
| 101 |
+
def _group_norm_backward_kernel(
|
| 102 |
+
X_ptr, # pointer to input, shape (n_rows, n_channels, hidden_size)
|
| 103 |
+
X_row_stride, # stride of each row in input
|
| 104 |
+
X_col_stride, # stride of each column in input
|
| 105 |
+
W_ptr, # pointer to weights, shape (n_channels)
|
| 106 |
+
Mean_ptr, # pointer to mean, shape (n_rows, n_groups)
|
| 107 |
+
Mean_ptr_row_stride, # stride of each column in mean
|
| 108 |
+
Mean_ptr_col_stride, # stride of each column in mean
|
| 109 |
+
RSTD_ptr, # pointer to rstd, shape (n_rows, n_groups)
|
| 110 |
+
DX_ptr, # pointer to input grad, shape (n_rows, n_groups, hidden_size)
|
| 111 |
+
DW_ptr, # pointer to weights grad, shape (n_channels)
|
| 112 |
+
DB_ptr, # pointer to bias grad, shape (n_channels)
|
| 113 |
+
UPSTREAM_ptr, # pointer to output grad, shape (n_rows, n_channels, hidden_size)
|
| 114 |
+
hidden_size: tl.constexpr, # hidden size
|
| 115 |
+
channels_per_group: tl.constexpr, # number of groups in group norm
|
| 116 |
+
BLOCK_SIZE: tl.constexpr,
|
| 117 |
+
dtype: tl.constexpr,
|
| 118 |
+
):
|
| 119 |
+
"""
|
| 120 |
+
References:
|
| 121 |
+
https://nn.labml.ai/normalization/group_norm/index.html
|
| 122 |
+
https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
|
| 123 |
+
|
| 124 |
+
The backprop equations are the same for group_norm and layer_norm
|
| 125 |
+
the only difference here is that we load the Mean, Rstd corresponding to the
|
| 126 |
+
group we're computing gradients for and the mean and rstd are computed over n-channels
|
| 127 |
+
so the total number of elements we compute the mean over is num_channels_per_group * hidden_size
|
| 128 |
+
|
| 129 |
+
We also need to load the Weights corresponding to the current channel to compute the gradients.
|
| 130 |
+
"""
|
| 131 |
+
batch_idx = tl.program_id(0)
|
| 132 |
+
group_idx = tl.program_id(1)
|
| 133 |
+
|
| 134 |
+
# Move the pointers to the correct batch
|
| 135 |
+
X_ptr += batch_idx * X_row_stride
|
| 136 |
+
DX_ptr += batch_idx * X_row_stride
|
| 137 |
+
UPSTREAM_ptr += batch_idx * X_row_stride
|
| 138 |
+
|
| 139 |
+
# Mean and rstd are the same shape so have the same strides
|
| 140 |
+
mean = tl.load(Mean_ptr + batch_idx * Mean_ptr_row_stride + group_idx * Mean_ptr_col_stride)
|
| 141 |
+
rstd = tl.load(RSTD_ptr + batch_idx * Mean_ptr_row_stride + group_idx * Mean_ptr_col_stride)
|
| 142 |
+
|
| 143 |
+
c1 = 0.0
|
| 144 |
+
c2 = 0.0
|
| 145 |
+
block_range = tl.arange(0, BLOCK_SIZE)
|
| 146 |
+
|
| 147 |
+
# We need to compute the sum terms of the backprop equations across all channels in the group
|
| 148 |
+
for channel_idx in range(group_idx * channels_per_group, (group_idx + 1) * channels_per_group):
|
| 149 |
+
dW = 0.0
|
| 150 |
+
dB = 0.0
|
| 151 |
+
# Move the pointers to the correct channel
|
| 152 |
+
W = tl.load(W_ptr + channel_idx)
|
| 153 |
+
for i in tl.range(0, hidden_size, BLOCK_SIZE):
|
| 154 |
+
hidden_size_offsets = i + block_range
|
| 155 |
+
mask = hidden_size_offsets < hidden_size
|
| 156 |
+
X = tl.load(
|
| 157 |
+
X_ptr + channel_idx * X_col_stride + hidden_size_offsets,
|
| 158 |
+
mask=mask,
|
| 159 |
+
other=0.0,
|
| 160 |
+
)
|
| 161 |
+
UPSTREAM_grad = tl.load(
|
| 162 |
+
UPSTREAM_ptr + channel_idx * X_col_stride + hidden_size_offsets,
|
| 163 |
+
mask=mask,
|
| 164 |
+
other=0.0,
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
x_hat = (X - mean) * rstd
|
| 168 |
+
dW += tl.sum(UPSTREAM_grad * x_hat)
|
| 169 |
+
dB += tl.sum(UPSTREAM_grad)
|
| 170 |
+
|
| 171 |
+
wdy = W * UPSTREAM_grad
|
| 172 |
+
c1 += tl.sum(x_hat * wdy)
|
| 173 |
+
c2 += tl.sum(wdy)
|
| 174 |
+
|
| 175 |
+
# Need to ensure additions to the same channel are atomic
|
| 176 |
+
tl.atomic_add(DW_ptr + channel_idx, dW.to(dtype))
|
| 177 |
+
tl.atomic_add(DB_ptr + channel_idx, dB.to(dtype))
|
| 178 |
+
|
| 179 |
+
N = hidden_size * channels_per_group
|
| 180 |
+
c1 = c1 / N
|
| 181 |
+
c2 = c2 / N
|
| 182 |
+
|
| 183 |
+
for channel_idx in tl.range(group_idx * channels_per_group, (group_idx + 1) * channels_per_group):
|
| 184 |
+
# Move the pointers to the correct channel
|
| 185 |
+
W = tl.load(W_ptr + channel_idx)
|
| 186 |
+
for i in range(0, hidden_size, BLOCK_SIZE):
|
| 187 |
+
hidden_size_offsets = i + block_range
|
| 188 |
+
mask = hidden_size_offsets < hidden_size
|
| 189 |
+
X = tl.load(
|
| 190 |
+
X_ptr + channel_idx * X_col_stride + hidden_size_offsets,
|
| 191 |
+
mask=mask,
|
| 192 |
+
other=0.0,
|
| 193 |
+
)
|
| 194 |
+
UPSTREAM_grad = tl.load(
|
| 195 |
+
UPSTREAM_ptr + channel_idx * X_col_stride + hidden_size_offsets,
|
| 196 |
+
mask=mask,
|
| 197 |
+
other=0.0,
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
x_hat = (X - mean) * rstd
|
| 201 |
+
wdy = W * UPSTREAM_grad
|
| 202 |
+
dx = (wdy - (x_hat * c1 + c2)) * rstd
|
| 203 |
+
tl.store(DX_ptr + channel_idx * X_col_stride + hidden_size_offsets, dx, mask=mask)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def group_norm_forward(X, num_channels, num_groups, W, B, eps):
|
| 207 |
+
shape = X.shape
|
| 208 |
+
batch_size = shape[0]
|
| 209 |
+
channels_per_group = num_channels // num_groups
|
| 210 |
+
# Reshape X so that the mean and std are computed across the groups
|
| 211 |
+
X = X.view(batch_size, num_groups, -1).contiguous()
|
| 212 |
+
hidden_size = X.shape[-1]
|
| 213 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(hidden_size))
|
| 214 |
+
Y = torch.empty((batch_size, num_groups, hidden_size), dtype=X.dtype, device=X.device)
|
| 215 |
+
Mean = torch.zeros((batch_size, num_groups), dtype=X.dtype, device=X.device)
|
| 216 |
+
RSTD = torch.zeros((batch_size, num_groups), dtype=X.dtype, device=X.device)
|
| 217 |
+
|
| 218 |
+
_group_norm_forward_kernel[(batch_size, num_groups)](
|
| 219 |
+
Y,
|
| 220 |
+
Y.stride(0),
|
| 221 |
+
Y.stride(1),
|
| 222 |
+
X,
|
| 223 |
+
X.stride(0),
|
| 224 |
+
X.stride(1),
|
| 225 |
+
Mean,
|
| 226 |
+
Mean.stride(0),
|
| 227 |
+
Mean.stride(1),
|
| 228 |
+
RSTD,
|
| 229 |
+
RSTD.stride(0),
|
| 230 |
+
RSTD.stride(1),
|
| 231 |
+
W,
|
| 232 |
+
B,
|
| 233 |
+
hidden_size,
|
| 234 |
+
channels_per_group,
|
| 235 |
+
eps,
|
| 236 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 237 |
+
)
|
| 238 |
+
# Return tensors in the original shape
|
| 239 |
+
return Y.view(*shape), X.view(*shape), Mean, RSTD, BLOCK_SIZE
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def group_norm_backward(dY, X, W, B, Mean, RSTD, num_channels, num_groups):
|
| 243 |
+
shape = dY.shape
|
| 244 |
+
batch_size = shape[0]
|
| 245 |
+
hidden_size = dY.shape[-1]
|
| 246 |
+
channels_per_group = num_channels // num_groups
|
| 247 |
+
dY = dY.view(batch_size, num_groups, -1)
|
| 248 |
+
DX = torch.empty(
|
| 249 |
+
(batch_size, num_groups, hidden_size * channels_per_group),
|
| 250 |
+
dtype=X.dtype,
|
| 251 |
+
device=X.device,
|
| 252 |
+
)
|
| 253 |
+
DW = torch.zeros((num_channels), dtype=W.dtype, device=W.device)
|
| 254 |
+
DB = torch.zeros((num_channels), dtype=B.dtype, device=B.device)
|
| 255 |
+
triton_dtype = tl.float32 if X.dtype == torch.float32 else tl.bfloat16
|
| 256 |
+
|
| 257 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(hidden_size))
|
| 258 |
+
_group_norm_backward_kernel[(batch_size, num_groups)](
|
| 259 |
+
X,
|
| 260 |
+
X.stride(0),
|
| 261 |
+
X.stride(1),
|
| 262 |
+
W,
|
| 263 |
+
Mean,
|
| 264 |
+
Mean.stride(0),
|
| 265 |
+
Mean.stride(1),
|
| 266 |
+
RSTD,
|
| 267 |
+
DX,
|
| 268 |
+
DW,
|
| 269 |
+
DB,
|
| 270 |
+
dY,
|
| 271 |
+
hidden_size,
|
| 272 |
+
channels_per_group,
|
| 273 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 274 |
+
dtype=triton_dtype,
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
# Return tensors in the original shape
|
| 278 |
+
return DX.view(*shape), DW, DB
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
class LigerGroupNormFunction(torch.autograd.Function):
|
| 282 |
+
@staticmethod
|
| 283 |
+
@ensure_contiguous
|
| 284 |
+
def forward(
|
| 285 |
+
ctx,
|
| 286 |
+
X,
|
| 287 |
+
affine_scaling_weight,
|
| 288 |
+
affine_shifting_bias,
|
| 289 |
+
num_channels,
|
| 290 |
+
num_groups,
|
| 291 |
+
eps,
|
| 292 |
+
):
|
| 293 |
+
Y, X, Mean, RSTD, BLOCK_SIZE = group_norm_forward(
|
| 294 |
+
X,
|
| 295 |
+
num_channels,
|
| 296 |
+
num_groups,
|
| 297 |
+
affine_scaling_weight,
|
| 298 |
+
affine_shifting_bias,
|
| 299 |
+
eps,
|
| 300 |
+
)
|
| 301 |
+
ctx.num_channels = num_channels
|
| 302 |
+
ctx.num_groups = num_groups
|
| 303 |
+
ctx.save_for_backward(X, affine_scaling_weight, affine_shifting_bias, Mean, RSTD)
|
| 304 |
+
return Y
|
| 305 |
+
|
| 306 |
+
@staticmethod
|
| 307 |
+
@ensure_contiguous
|
| 308 |
+
def backward(ctx, dY):
|
| 309 |
+
X, W, B, Mean, RSTD = ctx.saved_tensors
|
| 310 |
+
DX, DW, DB = group_norm_backward(dY, X, W, B, Mean, RSTD, ctx.num_channels, ctx.num_groups)
|
| 311 |
+
return DX, DW, DB, None, None, None
|
build/torch-xpu/jsd.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from .utils import ensure_contiguous
|
| 8 |
+
from .utils import infer_device
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@triton.jit
|
| 12 |
+
def _jsd_kernel(
|
| 13 |
+
X_ptr, # input in logspace, X = log Q
|
| 14 |
+
X_stride,
|
| 15 |
+
Y_ptr, # ground truth in logspace, Y = log P
|
| 16 |
+
Y_stride,
|
| 17 |
+
loss_ptr,
|
| 18 |
+
loss_stride,
|
| 19 |
+
dX_ptr,
|
| 20 |
+
dX_stride,
|
| 21 |
+
label_ptr,
|
| 22 |
+
beta: tl.constexpr,
|
| 23 |
+
n_non_ignore: int,
|
| 24 |
+
ignore_index: tl.constexpr,
|
| 25 |
+
n_cols,
|
| 26 |
+
BLOCK_SIZE: tl.constexpr,
|
| 27 |
+
HAS_LABEL: tl.constexpr,
|
| 28 |
+
):
|
| 29 |
+
# JSD(P || Q) = (KL(P || M) + KL(Q || M)) / 2, M = (1/2) * (P + Q) = (1/2) * (e ^ Y + e ^ X)
|
| 30 |
+
# = sum(P * log P + Q * log Q - 2 * M * log M) / 2
|
| 31 |
+
# = sum(e ^ Y * Y + e ^ X * X - 2 * M * log M) / 2
|
| 32 |
+
# grad_x_i = 0.5 * Q * (X - log_M)
|
| 33 |
+
pid = tl.program_id(0).to(tl.int64)
|
| 34 |
+
X_ptr += pid * X_stride
|
| 35 |
+
dX_ptr += pid * dX_stride
|
| 36 |
+
Y_ptr += pid * Y_stride
|
| 37 |
+
loss_ptr += pid * loss_stride
|
| 38 |
+
label_ptr += pid
|
| 39 |
+
|
| 40 |
+
if HAS_LABEL:
|
| 41 |
+
label = tl.load(label_ptr)
|
| 42 |
+
if label == ignore_index:
|
| 43 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 44 |
+
offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 45 |
+
tl.store(dX_ptr + offsets, 0.0, mask=offsets < n_cols)
|
| 46 |
+
return
|
| 47 |
+
|
| 48 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 49 |
+
offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 50 |
+
mask = offsets < n_cols
|
| 51 |
+
X = tl.load(X_ptr + offsets, mask=mask, other=float("-inf")).to(tl.float32)
|
| 52 |
+
Y = tl.load(Y_ptr + offsets, mask=mask, other=float("-inf")).to(tl.float32)
|
| 53 |
+
|
| 54 |
+
if beta == 0.0: # forward KL
|
| 55 |
+
Y_max = tl.max(Y, axis=0)
|
| 56 |
+
Y_shifted = Y - Y_max
|
| 57 |
+
Y_prob = tl.exp(Y_shifted) * tl.exp(Y_max) # Compensate for the shift
|
| 58 |
+
loss = Y_prob * (Y - X)
|
| 59 |
+
dX = -Y_prob
|
| 60 |
+
elif beta == 1.0: # reverse KL
|
| 61 |
+
X_max = tl.max(X, axis=0)
|
| 62 |
+
X_shifted = X - X_max
|
| 63 |
+
X_prob = tl.exp(X_shifted) * tl.exp(X_max) # Compensate for the shift
|
| 64 |
+
loss = X_prob * (X - Y)
|
| 65 |
+
dX = loss + X_prob
|
| 66 |
+
else:
|
| 67 |
+
max_val = tl.maximum(tl.max(X, axis=0), tl.max(Y, axis=0))
|
| 68 |
+
X_shifted = X - max_val
|
| 69 |
+
Y_shifted = Y - max_val
|
| 70 |
+
|
| 71 |
+
# Pre-compute exp(max_val) since it's used twice
|
| 72 |
+
exp_max = tl.exp(max_val)
|
| 73 |
+
|
| 74 |
+
# Compute exp terms with compensation
|
| 75 |
+
Q = tl.exp(X_shifted) * exp_max # = exp(X)
|
| 76 |
+
P = tl.exp(Y_shifted) * exp_max # = exp(Y)
|
| 77 |
+
|
| 78 |
+
# Pre-compute common terms
|
| 79 |
+
beta_P = beta * P
|
| 80 |
+
one_minus_beta_Q = (1 - beta) * Q
|
| 81 |
+
M = beta_P + one_minus_beta_Q
|
| 82 |
+
log_M = tl.log(M) # No need to compensate as M is already in original scale
|
| 83 |
+
|
| 84 |
+
loss = beta_P * Y + one_minus_beta_Q * X - M * log_M
|
| 85 |
+
dX = one_minus_beta_Q * (X - log_M)
|
| 86 |
+
|
| 87 |
+
# Pre-compute scaling factor
|
| 88 |
+
scale = 1.0 / n_non_ignore
|
| 89 |
+
loss = loss * scale
|
| 90 |
+
dX = dX * scale
|
| 91 |
+
|
| 92 |
+
tl.store(loss_ptr + offsets, loss, mask=mask)
|
| 93 |
+
tl.store(dX_ptr + offsets, dX, mask=mask)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
MAX_FUSED_SIZE = 4096 if infer_device() == "xpu" else 65536
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def jsd_forward(_input, target, shift_labels, beta, ignore_index, has_label):
|
| 100 |
+
BT, V = _input.shape
|
| 101 |
+
n_rows = BT
|
| 102 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 103 |
+
# non reduction loss
|
| 104 |
+
loss = torch.zeros(_input.shape, dtype=torch.float32, device=_input.device)
|
| 105 |
+
dX = torch.empty_like(_input)
|
| 106 |
+
|
| 107 |
+
if has_label:
|
| 108 |
+
n_non_ignore = (shift_labels != ignore_index).sum().item()
|
| 109 |
+
else:
|
| 110 |
+
n_non_ignore = BT
|
| 111 |
+
|
| 112 |
+
_jsd_kernel[(n_rows,)](
|
| 113 |
+
X_ptr=_input, # input in logspace, X = log Q
|
| 114 |
+
X_stride=_input.stride(-2),
|
| 115 |
+
Y_ptr=target, # ground truth in logspace, Y = log P
|
| 116 |
+
Y_stride=target.stride(-2),
|
| 117 |
+
loss_ptr=loss,
|
| 118 |
+
loss_stride=loss.stride(-2),
|
| 119 |
+
dX_ptr=dX,
|
| 120 |
+
dX_stride=dX.stride(-2),
|
| 121 |
+
label_ptr=(shift_labels if has_label else torch.empty(1, device=_input.device)), # dummy ptr if no label
|
| 122 |
+
beta=beta,
|
| 123 |
+
n_non_ignore=n_non_ignore,
|
| 124 |
+
ignore_index=ignore_index,
|
| 125 |
+
n_cols=V,
|
| 126 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 127 |
+
HAS_LABEL=has_label,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
loss = torch.sum(loss)
|
| 131 |
+
return loss.to(_input.dtype), dX
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def jsd_backward(dX, grad_output):
|
| 135 |
+
# If jsd is the last layer, grad_output is 1.0. Skip the mul to save time
|
| 136 |
+
if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
|
| 137 |
+
return dX
|
| 138 |
+
else:
|
| 139 |
+
return grad_output * dX
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class LigerJSDFunction(torch.autograd.Function):
|
| 143 |
+
r"""
|
| 144 |
+
This class implements the forward and backward pass for the generalized Jensen-Shannon Divergence.
|
| 145 |
+
.. math::
|
| 146 |
+
JSD(\beta)(P || Q)
|
| 147 |
+
= \beta * KLDiv(P || (\beta * P + (1 - \beta) * Q)) + (1 - \beta) * KLDiv(Q || (\beta * P + (1 - \beta) * Q))
|
| 148 |
+
|
| 149 |
+
.. note::
|
| 150 |
+
As all the other losses in PyTorch, this function expects the first argument,
|
| 151 |
+
:attr:`_input`, to be the predictions, the output of the student model, in log-space
|
| 152 |
+
and the second, :attr:`target`, to be the observations, the output of the teacher model, in log-space.
|
| 153 |
+
This differs from the standard mathematical notation :math:`JSD(P || Q)` where
|
| 154 |
+
:math:`P` denotes the teacher model and :math:`Q` denotes the student model.
|
| 155 |
+
"""
|
| 156 |
+
|
| 157 |
+
@staticmethod
|
| 158 |
+
@ensure_contiguous
|
| 159 |
+
def forward(
|
| 160 |
+
ctx,
|
| 161 |
+
_input: torch.Tensor,
|
| 162 |
+
target: torch.Tensor,
|
| 163 |
+
shift_labels: Optional[torch.Tensor] = None,
|
| 164 |
+
beta: float = 0.5,
|
| 165 |
+
ignore_index: int = -100,
|
| 166 |
+
) -> torch.Tensor:
|
| 167 |
+
"""
|
| 168 |
+
Args:
|
| 169 |
+
_input (torch.Tensor): predict values with shape (BT, V) in logspace
|
| 170 |
+
target (torch.Tensor): ground truth values with shape (BT, V) in logspace
|
| 171 |
+
shift_labels (Optional[torch.LongTensor]): indicator of next predicted vocab with shape (BT) where each value is in [0, V-1].
|
| 172 |
+
beta (float): coefficient beta of generalized JSD in the interval [0, 1]. It implements forward/reverse KL when beta equals 0 and 1 respectively. Default: `0.5`
|
| 173 |
+
ignore_index (int): the index to ignore. Default: -100
|
| 174 |
+
|
| 175 |
+
Returns:
|
| 176 |
+
loss (torch.Tensor): generalized JSD
|
| 177 |
+
"""
|
| 178 |
+
has_label = False
|
| 179 |
+
if shift_labels is not None:
|
| 180 |
+
assert shift_labels.shape == (_input.shape[0],), (
|
| 181 |
+
f"the shape of shift_labels must be (BT,). Got: {shift_labels.shape}"
|
| 182 |
+
)
|
| 183 |
+
shift_labels = shift_labels.contiguous()
|
| 184 |
+
has_label = True
|
| 185 |
+
|
| 186 |
+
loss, dX = jsd_forward(_input, target, shift_labels, beta, ignore_index, has_label)
|
| 187 |
+
ctx.save_for_backward(dX)
|
| 188 |
+
return loss
|
| 189 |
+
|
| 190 |
+
@staticmethod
|
| 191 |
+
@ensure_contiguous
|
| 192 |
+
def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
|
| 193 |
+
(dX,) = ctx.saved_tensors
|
| 194 |
+
dX = jsd_backward(dX, grad_output)
|
| 195 |
+
return (
|
| 196 |
+
dX,
|
| 197 |
+
None,
|
| 198 |
+
None,
|
| 199 |
+
None,
|
| 200 |
+
None,
|
| 201 |
+
)
|
build/torch-xpu/kl_div.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Literal
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import triton
|
| 5 |
+
import triton.language as tl
|
| 6 |
+
|
| 7 |
+
from .utils import ensure_contiguous
|
| 8 |
+
from .utils import is_hip
|
| 9 |
+
from .utils import infer_device
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def get_num_warps(BLOCK_SIZE):
|
| 13 |
+
num_warps = 4
|
| 14 |
+
if BLOCK_SIZE >= 32768:
|
| 15 |
+
num_warps = 32 if not is_hip() else 16
|
| 16 |
+
elif BLOCK_SIZE >= 8192:
|
| 17 |
+
num_warps = 16
|
| 18 |
+
elif BLOCK_SIZE >= 2048:
|
| 19 |
+
num_warps = 8
|
| 20 |
+
|
| 21 |
+
return num_warps
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
if infer_device() == "xpu":
|
| 25 |
+
MAX_FUSED_SIZE = 8192
|
| 26 |
+
elif infer_device() == "npu":
|
| 27 |
+
MAX_FUSED_SIZE = 8192
|
| 28 |
+
else:
|
| 29 |
+
MAX_FUSED_SIZE = 65536 // 4 # 65536 // 4 or 8 works the best
|
| 30 |
+
|
| 31 |
+
REDUCTION_LITERAL = Literal["none", "sum", "mean", "batchmean"]
|
| 32 |
+
|
| 33 |
+
_REDUCTION_MODE_NONE: tl.constexpr = tl.constexpr(0)
|
| 34 |
+
_REDUCTION_MODE_SUM: tl.constexpr = tl.constexpr(1)
|
| 35 |
+
_REDUCTION_MODE_MEAN: tl.constexpr = tl.constexpr(2)
|
| 36 |
+
_REDUCTION_MODE_BATCHMEAN: tl.constexpr = tl.constexpr(3)
|
| 37 |
+
|
| 38 |
+
_str_to_reduction_mode = {
|
| 39 |
+
"none": _REDUCTION_MODE_NONE.value,
|
| 40 |
+
"sum": _REDUCTION_MODE_SUM.value,
|
| 41 |
+
"mean": _REDUCTION_MODE_MEAN.value,
|
| 42 |
+
"batchmean": _REDUCTION_MODE_BATCHMEAN.value,
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
@triton.jit
|
| 47 |
+
def _kldiv_kernel_forward(
|
| 48 |
+
y_ptr, # [B, S], prediction ptr, the kernel expects the prediction in log-space
|
| 49 |
+
y_stride, # int, prediction stride
|
| 50 |
+
gt_ptr, # [B, S], ground truth ptr
|
| 51 |
+
gt_stride, # int, ground truth stride
|
| 52 |
+
loss_ptr, # [B] or [B, S] if reduction == _REDUCTION_MODE_NONE, output ptr
|
| 53 |
+
loss_stride, # int, output stride
|
| 54 |
+
n_cols, # int, number of columns in the input tensor
|
| 55 |
+
eps,
|
| 56 |
+
BLOCK_SIZE: tl.constexpr,
|
| 57 |
+
log_target: tl.constexpr = False,
|
| 58 |
+
reduction: tl.constexpr = _REDUCTION_MODE_BATCHMEAN,
|
| 59 |
+
):
|
| 60 |
+
pid = tl.program_id(0).to(tl.int64)
|
| 61 |
+
y_ptr += pid * y_stride
|
| 62 |
+
gt_ptr += pid * gt_stride
|
| 63 |
+
loss_ptr += pid * loss_stride
|
| 64 |
+
|
| 65 |
+
base_offsets = tl.arange(0, BLOCK_SIZE)
|
| 66 |
+
|
| 67 |
+
loss_sum = 0.0
|
| 68 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 69 |
+
offsets = i + base_offsets
|
| 70 |
+
mask = offsets < n_cols
|
| 71 |
+
y = tl.load(y_ptr + offsets, mask=mask, other=0.0)
|
| 72 |
+
y_true = tl.load(gt_ptr + offsets, mask=mask, other=0.0)
|
| 73 |
+
|
| 74 |
+
# KL(y_true || y) = y_true * (log(y_true) - log(y))
|
| 75 |
+
# We compute KL(y_true || y) with y in the log-space
|
| 76 |
+
if not log_target:
|
| 77 |
+
loss = y_true * (tl.log(tl.maximum(y_true, eps)) - y)
|
| 78 |
+
else:
|
| 79 |
+
loss = tl.exp(y_true) * (y_true - y)
|
| 80 |
+
|
| 81 |
+
if reduction == _REDUCTION_MODE_NONE:
|
| 82 |
+
tl.store(loss_ptr + offsets, loss, mask=mask)
|
| 83 |
+
else:
|
| 84 |
+
loss_sum += tl.sum(loss, axis=0)
|
| 85 |
+
|
| 86 |
+
if reduction != _REDUCTION_MODE_NONE:
|
| 87 |
+
tl.store(loss_ptr, loss_sum)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
@triton.jit
|
| 91 |
+
def _kldiv_kernel_backward(
|
| 92 |
+
target_ptr,
|
| 93 |
+
target_stride,
|
| 94 |
+
new_grads_ptr,
|
| 95 |
+
new_grads_stride,
|
| 96 |
+
n_cols,
|
| 97 |
+
BLOCK_SIZE: tl.constexpr,
|
| 98 |
+
log_target: tl.constexpr = False,
|
| 99 |
+
):
|
| 100 |
+
pid = tl.program_id(0).to(tl.int64)
|
| 101 |
+
|
| 102 |
+
target_ptr += pid * target_stride
|
| 103 |
+
new_grads_ptr += pid * new_grads_stride
|
| 104 |
+
|
| 105 |
+
offsets = tl.arange(0, BLOCK_SIZE)
|
| 106 |
+
mask = offsets < n_cols
|
| 107 |
+
|
| 108 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 109 |
+
offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 110 |
+
mask = offsets < n_cols
|
| 111 |
+
|
| 112 |
+
target = tl.load(target_ptr + offsets, mask=mask, other=0.0)
|
| 113 |
+
|
| 114 |
+
if not log_target:
|
| 115 |
+
res = target * -1
|
| 116 |
+
else:
|
| 117 |
+
res = -tl.exp(target)
|
| 118 |
+
|
| 119 |
+
tl.store(new_grads_ptr + offsets, res, mask=mask)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def kldiv_forward_triton(y_pred, y_true, log_target, reduction, eps): # [BT, V]
|
| 123 |
+
BT, V = y_pred.shape
|
| 124 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 125 |
+
num_warps = 32 if infer_device() == "xpu" else get_num_warps(BLOCK_SIZE)
|
| 126 |
+
|
| 127 |
+
grid = (BT,)
|
| 128 |
+
reduction = _str_to_reduction_mode[reduction]
|
| 129 |
+
|
| 130 |
+
out_size = (BT, V) if reduction == _REDUCTION_MODE_NONE.value else (BT,)
|
| 131 |
+
output_tensor = torch.zeros(out_size, device=y_pred.device, dtype=torch.float32)
|
| 132 |
+
|
| 133 |
+
_kldiv_kernel_forward[grid](
|
| 134 |
+
y_pred,
|
| 135 |
+
y_pred.stride(0),
|
| 136 |
+
y_true,
|
| 137 |
+
y_true.stride(0),
|
| 138 |
+
output_tensor,
|
| 139 |
+
output_tensor.stride(0),
|
| 140 |
+
V,
|
| 141 |
+
eps=eps,
|
| 142 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 143 |
+
num_warps=num_warps,
|
| 144 |
+
log_target=log_target,
|
| 145 |
+
reduction=reduction,
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
# calculated according to the reduction mode same as in Pytorch. In the later versions, `mean` will be changed to the same behavior as `batchmean`
|
| 149 |
+
# https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html
|
| 150 |
+
# https://github.com/pytorch/pytorch/blob/d7b57c4d63edb42e1deeeba9497fcb5f1f748ff2/torch/nn/functional.py#L3372
|
| 151 |
+
if reduction == _REDUCTION_MODE_BATCHMEAN.value:
|
| 152 |
+
return output_tensor.sum() / BT
|
| 153 |
+
elif reduction == _REDUCTION_MODE_SUM.value:
|
| 154 |
+
return output_tensor.sum(dim=0)
|
| 155 |
+
elif reduction == _REDUCTION_MODE_MEAN.value:
|
| 156 |
+
return output_tensor.sum() / (BT * V)
|
| 157 |
+
else:
|
| 158 |
+
return output_tensor
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def kldiv_backward_triton(target, grad_output, new_grads, log_target):
|
| 162 |
+
BT, V = target.shape
|
| 163 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 164 |
+
num_warps = 32 if infer_device() == "xpu" else get_num_warps(BLOCK_SIZE)
|
| 165 |
+
|
| 166 |
+
grid = (BT,)
|
| 167 |
+
|
| 168 |
+
# We store the gradients in-place in the input tensor
|
| 169 |
+
_kldiv_kernel_backward[grid](
|
| 170 |
+
target,
|
| 171 |
+
target.stride(0),
|
| 172 |
+
new_grads,
|
| 173 |
+
new_grads.stride(0),
|
| 174 |
+
V,
|
| 175 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 176 |
+
num_warps=num_warps,
|
| 177 |
+
log_target=log_target,
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
# If cross entropy is the last layer, grad_output is 1.0. Skip the mul then.
|
| 181 |
+
if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
|
| 182 |
+
return new_grads
|
| 183 |
+
|
| 184 |
+
return new_grads * grad_output
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
class LigerKLDivLossFunction(torch.autograd.Function):
|
| 188 |
+
"""
|
| 189 |
+
Class implementing the forward and backward pass for the KL Divergence Loss using Triton, as defined by the following formula:
|
| 190 |
+
```python
|
| 191 |
+
if log_target:
|
| 192 |
+
loss = target.exp() * (target - input)
|
| 193 |
+
else:
|
| 194 |
+
loss = target * (target.log() - input)
|
| 195 |
+
```,
|
| 196 |
+
then the loss is reduced according to the `reduction` parameter.
|
| 197 |
+
as defined in the PyTorch documentation: https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html
|
| 198 |
+
"""
|
| 199 |
+
|
| 200 |
+
@staticmethod
|
| 201 |
+
@ensure_contiguous
|
| 202 |
+
def forward(
|
| 203 |
+
ctx,
|
| 204 |
+
y_pred: torch.Tensor,
|
| 205 |
+
y_true: torch.Tensor,
|
| 206 |
+
reduction: REDUCTION_LITERAL = "batchmean",
|
| 207 |
+
log_target: bool = False,
|
| 208 |
+
eps: float = 1e-10,
|
| 209 |
+
) -> torch.Tensor:
|
| 210 |
+
"""A forward pass for the KL Divergence Loss.
|
| 211 |
+
|
| 212 |
+
Args:
|
| 213 |
+
ctx: Torch autograd context
|
| 214 |
+
y_pred (torch.Tensor): A tensor of shape (BT, V) containing the predicted values, expected to be log-probabilities.
|
| 215 |
+
y_true (torch.Tensor): A tensor of shape (BT, V) containing the target values, expected to be either probabilities or log-probabilities, depending on the value of `log_target`.
|
| 216 |
+
reduction (REDUCTION_LITERAL, optional): Reduction to be used. Defaults to "batchmean".
|
| 217 |
+
log_target (bool, optional): If set to true, expects the ground truth to already be log-probabilities. Defaults to False.
|
| 218 |
+
eps: (float, optional): A small value to avoid division by zero. Defaults to 1e-10.
|
| 219 |
+
|
| 220 |
+
Returns:
|
| 221 |
+
torch.Tensor: The computed KL Divergence Loss, with shape (BT, V) if `reduction` is "none", else a scalar.
|
| 222 |
+
"""
|
| 223 |
+
ctx.save_for_backward(y_true)
|
| 224 |
+
ctx.reduction = reduction
|
| 225 |
+
ctx.log_target = log_target
|
| 226 |
+
return kldiv_forward_triton(y_pred, y_true, log_target=log_target, reduction=reduction, eps=eps)
|
| 227 |
+
|
| 228 |
+
@staticmethod
|
| 229 |
+
@ensure_contiguous
|
| 230 |
+
def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
|
| 231 |
+
"""A backward pass for the KL Divergence Loss.
|
| 232 |
+
|
| 233 |
+
Args:
|
| 234 |
+
ctx: Torch autograd context
|
| 235 |
+
grad_output (torch.Tensor): The gradient of the loss with respect to the output.
|
| 236 |
+
|
| 237 |
+
Returns:
|
| 238 |
+
tuple[torch.Tensor, None, None, None, None]: The gradient of the loss with respect to the inputs and None for the other arguments of the forward method.
|
| 239 |
+
"""
|
| 240 |
+
(y_true,) = ctx.saved_tensors
|
| 241 |
+
|
| 242 |
+
new_grads = torch.empty_like(y_true)
|
| 243 |
+
|
| 244 |
+
derivative = kldiv_backward_triton(y_true, grad_output, new_grads, ctx.log_target)
|
| 245 |
+
|
| 246 |
+
if ctx.reduction == "batchmean":
|
| 247 |
+
derivative = derivative / y_true.shape[0]
|
| 248 |
+
elif ctx.reduction == "sum" or ctx.reduction == "none":
|
| 249 |
+
pass
|
| 250 |
+
elif ctx.reduction == "mean":
|
| 251 |
+
derivative = derivative / (y_true.shape[0] * y_true.shape[1])
|
| 252 |
+
|
| 253 |
+
return (
|
| 254 |
+
derivative,
|
| 255 |
+
None,
|
| 256 |
+
None,
|
| 257 |
+
None,
|
| 258 |
+
None,
|
| 259 |
+
)
|
build/torch-xpu/layer_norm.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import operator
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
from .utils import calculate_settings
|
| 9 |
+
from .utils import compare_version
|
| 10 |
+
from .utils import ensure_contiguous
|
| 11 |
+
from .utils import get_npu_core_count
|
| 12 |
+
from .utils import set_large_grf_mode
|
| 13 |
+
from .utils import is_npu_available
|
| 14 |
+
|
| 15 |
+
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
|
| 16 |
+
try:
|
| 17 |
+
# typical import path with dispatch available
|
| 18 |
+
from triton.language.extra.libdevice import rsqrt
|
| 19 |
+
except ModuleNotFoundError:
|
| 20 |
+
# for working with NGC containers
|
| 21 |
+
from triton.language.extra.cuda.libdevice import rsqrt
|
| 22 |
+
else:
|
| 23 |
+
from triton.language.math import rsqrt
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@triton.jit
|
| 27 |
+
def _layer_norm_forward_kernel(
|
| 28 |
+
Y_ptr, # pointer to output, shape (n_rows, n_cols)
|
| 29 |
+
Y_row_stride, # stride of each row in output
|
| 30 |
+
X_ptr, # pointer to input, shape (n_rows, n_cols)
|
| 31 |
+
X_row_stride, # stride of each row in input
|
| 32 |
+
W_ptr, # pointer to weights, shape (n_cols,)
|
| 33 |
+
W_row_stride, # stride of each row in weights
|
| 34 |
+
B_ptr, # pointer to bias, shape (n_cols,)
|
| 35 |
+
B_row_stride, # stride of each row in bias
|
| 36 |
+
Mean_ptr, # pointer to mean, shape (n_rows,)
|
| 37 |
+
Mean_row_stride, # stride of each row in mean
|
| 38 |
+
RSTD_ptr, # pointer to rstd, shape (n_rows,)
|
| 39 |
+
RSTD_row_stride, # stride of each row in rstd
|
| 40 |
+
n_cols,
|
| 41 |
+
eps,
|
| 42 |
+
BLOCK_SIZE: tl.constexpr,
|
| 43 |
+
):
|
| 44 |
+
"""
|
| 45 |
+
References:
|
| 46 |
+
https://arxiv.org/abs/1607.06450
|
| 47 |
+
https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
|
| 48 |
+
"""
|
| 49 |
+
row_idx = tl.program_id(0).to(tl.int64)
|
| 50 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 51 |
+
mask = col_offsets < n_cols
|
| 52 |
+
|
| 53 |
+
# Pre-load weights and bias in fp32 to avoid repeated conversions
|
| 54 |
+
W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0.0)
|
| 55 |
+
B_row = tl.load(B_ptr + col_offsets, mask=mask, other=0.0)
|
| 56 |
+
W_f32 = W_row.to(tl.float32)
|
| 57 |
+
B_f32 = B_row.to(tl.float32)
|
| 58 |
+
|
| 59 |
+
# Calculate pointers for this row
|
| 60 |
+
row_X_ptr = X_ptr + row_idx * X_row_stride
|
| 61 |
+
row_Y_ptr = Y_ptr + row_idx * Y_row_stride
|
| 62 |
+
row_Mean_ptr = Mean_ptr + row_idx * Mean_row_stride
|
| 63 |
+
row_RSTD_ptr = RSTD_ptr + row_idx * RSTD_row_stride
|
| 64 |
+
|
| 65 |
+
# Load input data and convert to fp32 for numerical stability
|
| 66 |
+
X_row = tl.load(row_X_ptr + col_offsets, mask=mask, other=0.0)
|
| 67 |
+
X_f32 = X_row.to(tl.float32)
|
| 68 |
+
|
| 69 |
+
# Compute statistics in fp32 for numerical stability
|
| 70 |
+
mean = tl.sum(X_f32, axis=0) / n_cols
|
| 71 |
+
X_centered = X_f32 - mean
|
| 72 |
+
# Apply mask to variance calculation to exclude contributions from masked elements
|
| 73 |
+
X_centered_masked = tl.where(mask, X_centered, 0.0)
|
| 74 |
+
var = tl.sum(X_centered_masked * X_centered_masked, axis=0) / n_cols
|
| 75 |
+
rstd = rsqrt(var + eps)
|
| 76 |
+
|
| 77 |
+
# Store statistics (convert back to original dtype only once)
|
| 78 |
+
tl.store(row_Mean_ptr, mean.to(X_row.dtype))
|
| 79 |
+
tl.store(row_RSTD_ptr, rstd.to(X_row.dtype))
|
| 80 |
+
|
| 81 |
+
# Fused normalization and affine transformation
|
| 82 |
+
# Y = (X - mean) * rstd * W + B = X_centered * rstd * W + B
|
| 83 |
+
Y_f32 = X_centered * rstd * W_f32 + B_f32
|
| 84 |
+
|
| 85 |
+
# Store output (single conversion back to original dtype)
|
| 86 |
+
tl.store(row_Y_ptr + col_offsets, Y_f32.to(X_row.dtype), mask=mask)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@triton.jit
|
| 90 |
+
def _layer_norm_backward_kernel(
|
| 91 |
+
X_ptr, # pointer to input, shape (n_rows, n_cols)
|
| 92 |
+
stride_x, # stride of each row in input
|
| 93 |
+
W_ptr, # pointer to weights, shape (n_cols,)
|
| 94 |
+
Mean_ptr, # pointer to mean, shape (n_rows,)
|
| 95 |
+
stride_mean, # stride of each row in mean
|
| 96 |
+
RSTD_ptr, # pointer to rstd, shape (n_rows,)
|
| 97 |
+
stride_rstd, # stride of each row in rstd
|
| 98 |
+
DX_ptr, # pointer to input grad, shape (n_rows, n_cols)
|
| 99 |
+
stride_dx, # stride of each row in input grad
|
| 100 |
+
DW_ptr, # pointer to weights grad, shape (n_cols,)
|
| 101 |
+
stride_dw, # stride of each row in weights grad
|
| 102 |
+
DB_ptr, # pointer to bias grad, shape (n_cols,)
|
| 103 |
+
stride_db, # stride of each row in bias grad
|
| 104 |
+
DY_ptr, # pointer to output grad, shape (n_rows, n_cols)
|
| 105 |
+
stride_dy, # stride of each row in output grad
|
| 106 |
+
n_rows,
|
| 107 |
+
n_cols,
|
| 108 |
+
rows_per_program: tl.constexpr,
|
| 109 |
+
BLOCK_SIZE: tl.constexpr,
|
| 110 |
+
):
|
| 111 |
+
"""
|
| 112 |
+
References:
|
| 113 |
+
https://arxiv.org/abs/1607.06450
|
| 114 |
+
https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
|
| 115 |
+
"""
|
| 116 |
+
row_block_id = tl.program_id(0).to(tl.int64)
|
| 117 |
+
row_start = row_block_id * rows_per_program
|
| 118 |
+
row_end = min((row_block_id + 1) * rows_per_program, n_rows)
|
| 119 |
+
cols = tl.arange(0, BLOCK_SIZE)
|
| 120 |
+
mask = cols < n_cols
|
| 121 |
+
|
| 122 |
+
dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
|
| 123 |
+
db_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
|
| 124 |
+
|
| 125 |
+
# Pre-load weights once (same optimization as forward pass)
|
| 126 |
+
w = tl.load(W_ptr + cols, mask=mask, other=0.0)
|
| 127 |
+
w_f32 = w.to(tl.float32)
|
| 128 |
+
|
| 129 |
+
for row_idx in range(row_start, row_end):
|
| 130 |
+
# Calculate pointers for this specific row
|
| 131 |
+
row_X_ptr = X_ptr + row_idx * stride_x
|
| 132 |
+
row_DX_ptr = DX_ptr + row_idx * stride_dx
|
| 133 |
+
row_DY_ptr = DY_ptr + row_idx * stride_dy
|
| 134 |
+
row_Mean_ptr = Mean_ptr + row_idx * stride_mean
|
| 135 |
+
row_RSTD_ptr = RSTD_ptr + row_idx * stride_rstd
|
| 136 |
+
|
| 137 |
+
# Load data for this row
|
| 138 |
+
x = tl.load(row_X_ptr + cols, mask=mask, other=0.0)
|
| 139 |
+
dy = tl.load(row_DY_ptr + cols, mask=mask, other=0.0)
|
| 140 |
+
mean = tl.load(row_Mean_ptr)
|
| 141 |
+
rstd = tl.load(row_RSTD_ptr)
|
| 142 |
+
|
| 143 |
+
# Convert to fp32 for numerical stability
|
| 144 |
+
x_f32 = x.to(tl.float32)
|
| 145 |
+
dy_f32 = dy.to(tl.float32)
|
| 146 |
+
mean_f32 = mean.to(tl.float32)
|
| 147 |
+
rstd_f32 = rstd.to(tl.float32)
|
| 148 |
+
|
| 149 |
+
# Compute backward pass for this row
|
| 150 |
+
x_hat = (x_f32 - mean_f32) * rstd_f32
|
| 151 |
+
wdy = w_f32 * dy_f32
|
| 152 |
+
c1 = tl.sum(x_hat * wdy, axis=0) / n_cols
|
| 153 |
+
c2 = tl.sum(wdy, axis=0) / n_cols
|
| 154 |
+
dx = (wdy - (x_hat * c1 + c2)) * rstd_f32
|
| 155 |
+
|
| 156 |
+
# Store input gradient
|
| 157 |
+
tl.store(row_DX_ptr + cols, dx, mask=mask)
|
| 158 |
+
|
| 159 |
+
# Accumulate weight and bias gradients for this thread block's assigned rows
|
| 160 |
+
dw = dy_f32 * x_hat
|
| 161 |
+
db = dy_f32
|
| 162 |
+
dW_row += dw
|
| 163 |
+
db_row += db
|
| 164 |
+
|
| 165 |
+
tl.store(DW_ptr + row_block_id * stride_dw + cols, dW_row, mask=mask)
|
| 166 |
+
tl.store(DB_ptr + row_block_id * stride_db + cols, db_row, mask=mask)
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def layer_norm_forward(X, W, B, eps):
|
| 170 |
+
"""
|
| 171 |
+
Args:
|
| 172 |
+
X: Input tensor of shape (..., hidden_size)
|
| 173 |
+
W: Weight tensor of shape (hidden_size,)
|
| 174 |
+
B: Bias tensor of shape (hidden_size,)
|
| 175 |
+
eps: Small constant for numerical stability
|
| 176 |
+
|
| 177 |
+
Returns:
|
| 178 |
+
Tuple of (output, input, mean, rstd, block_size, num_warps)
|
| 179 |
+
"""
|
| 180 |
+
shape = X.shape
|
| 181 |
+
dim = shape[-1]
|
| 182 |
+
X = X.view(-1, dim)
|
| 183 |
+
n_rows, n_cols = X.shape
|
| 184 |
+
|
| 185 |
+
# Calculate optimal block size and warp configuration
|
| 186 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 187 |
+
|
| 188 |
+
# Allocate output tensors
|
| 189 |
+
Y = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
|
| 190 |
+
Mean = torch.empty(n_rows, dtype=X.dtype, device=X.device)
|
| 191 |
+
RSTD = torch.empty(n_rows, dtype=X.dtype, device=X.device)
|
| 192 |
+
|
| 193 |
+
# Validate input dimensions
|
| 194 |
+
if X.shape[1] != W.shape[0]:
|
| 195 |
+
raise ValueError(
|
| 196 |
+
f"Incompatible dimensions: input feature size (X.shape[1]={X.shape[1]}) "
|
| 197 |
+
f"must match weight size (W.shape[0]={W.shape[0]})"
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
# XPU-specific optimization
|
| 201 |
+
kernel_args = {}
|
| 202 |
+
if X.device.type == "xpu":
|
| 203 |
+
set_large_grf_mode(kernel_args)
|
| 204 |
+
|
| 205 |
+
# Launch kernel with one thread block per row for optimal performance
|
| 206 |
+
grid = (n_rows,)
|
| 207 |
+
_layer_norm_forward_kernel[grid](
|
| 208 |
+
Y,
|
| 209 |
+
Y.stride(0),
|
| 210 |
+
X,
|
| 211 |
+
X.stride(0),
|
| 212 |
+
W,
|
| 213 |
+
W.stride(0),
|
| 214 |
+
B,
|
| 215 |
+
B.stride(0),
|
| 216 |
+
Mean,
|
| 217 |
+
Mean.stride(0),
|
| 218 |
+
RSTD,
|
| 219 |
+
RSTD.stride(0),
|
| 220 |
+
n_cols,
|
| 221 |
+
eps,
|
| 222 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 223 |
+
num_warps=num_warps,
|
| 224 |
+
**kernel_args,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
return Y.view(*shape), X, Mean, RSTD, BLOCK_SIZE, num_warps
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def layer_norm_backward(dY, X, W, B, Mean, RSTD):
|
| 231 |
+
"""
|
| 232 |
+
Args:
|
| 233 |
+
dY: Gradient of output
|
| 234 |
+
X: Input tensor
|
| 235 |
+
W: Weight tensor
|
| 236 |
+
B: Bias tensor
|
| 237 |
+
Mean: Pre-computed mean
|
| 238 |
+
RSTD: Pre-computed reciprocal standard deviation
|
| 239 |
+
|
| 240 |
+
Returns:
|
| 241 |
+
Tuple of (input_grad, weight_grad, bias_grad)
|
| 242 |
+
"""
|
| 243 |
+
shape = dY.shape
|
| 244 |
+
dim = shape[-1]
|
| 245 |
+
dY = dY.view(-1, dim)
|
| 246 |
+
n_rows, n_cols = dY.shape
|
| 247 |
+
|
| 248 |
+
sm_count = 1
|
| 249 |
+
if X.device.type == "cuda":
|
| 250 |
+
sm_count = torch.cuda.get_device_properties(X.device).multi_processor_count
|
| 251 |
+
elif X.device.type == "xpu":
|
| 252 |
+
sm_count = torch.xpu.get_device_properties(X.device).gpu_eu_count
|
| 253 |
+
elif X.device.type == "npu":
|
| 254 |
+
sm_count = get_npu_core_count()
|
| 255 |
+
|
| 256 |
+
# fp32 for numerical stability especially.
|
| 257 |
+
_DW = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
|
| 258 |
+
_DB = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
|
| 259 |
+
|
| 260 |
+
# Calculate optimal block size and warp configuration
|
| 261 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 262 |
+
if n_cols > BLOCK_SIZE:
|
| 263 |
+
raise RuntimeError(f"Feature dimension {n_cols} exceeds maximum supported size of {BLOCK_SIZE}.")
|
| 264 |
+
rows_per_program = math.ceil(n_rows / sm_count)
|
| 265 |
+
grid = (sm_count,)
|
| 266 |
+
|
| 267 |
+
# Allocate gradient tensors
|
| 268 |
+
DX = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
|
| 269 |
+
|
| 270 |
+
kernel_args = {"num_warps": num_warps}
|
| 271 |
+
# XPU-specific optimization
|
| 272 |
+
if X.device.type == "xpu":
|
| 273 |
+
kernel_args.update({"num_warps": 32, "num_stages": 4})
|
| 274 |
+
set_large_grf_mode(kernel_args)
|
| 275 |
+
|
| 276 |
+
# Launch kernel with one thread block per row for optimal performance
|
| 277 |
+
_layer_norm_backward_kernel[grid](
|
| 278 |
+
X,
|
| 279 |
+
X.stride(0),
|
| 280 |
+
W,
|
| 281 |
+
Mean,
|
| 282 |
+
Mean.stride(0),
|
| 283 |
+
RSTD,
|
| 284 |
+
RSTD.stride(0),
|
| 285 |
+
DX,
|
| 286 |
+
DX.stride(0),
|
| 287 |
+
_DW,
|
| 288 |
+
_DW.stride(0),
|
| 289 |
+
_DB,
|
| 290 |
+
_DB.stride(0),
|
| 291 |
+
dY,
|
| 292 |
+
dY.stride(0),
|
| 293 |
+
n_rows,
|
| 294 |
+
n_cols,
|
| 295 |
+
rows_per_program=rows_per_program,
|
| 296 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 297 |
+
**kernel_args,
|
| 298 |
+
)
|
| 299 |
+
|
| 300 |
+
DX = DX.view(*shape)
|
| 301 |
+
DW = _DW.sum(dim=0).to(W.dtype)
|
| 302 |
+
DB = _DB.sum(dim=0).to(B.dtype)
|
| 303 |
+
|
| 304 |
+
return DX, DW, DB
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
class LigerLayerNormFunction(torch.autograd.Function):
|
| 308 |
+
@staticmethod
|
| 309 |
+
@ensure_contiguous
|
| 310 |
+
def forward(ctx, X, W, B, eps):
|
| 311 |
+
Y, X, Mean, RSTD, BLOCK_SIZE, num_warps = layer_norm_forward(X, W, B, eps)
|
| 312 |
+
ctx.save_for_backward(X, W, B, Mean, RSTD)
|
| 313 |
+
return Y
|
| 314 |
+
|
| 315 |
+
@staticmethod
|
| 316 |
+
@ensure_contiguous
|
| 317 |
+
def backward(ctx, dY):
|
| 318 |
+
X, W, B, Mean, RSTD = ctx.saved_tensors
|
| 319 |
+
DX, DW, DB = layer_norm_backward(dY, X, W, B, Mean, RSTD)
|
| 320 |
+
return DX, DW, DB, None
|
build/torch-xpu/layers.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import inspect
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
from typing import Optional
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
|
| 8 |
+
from .fused_linear_cross_entropy import LigerFusedLinearCrossEntropyFunction
|
| 9 |
+
from .geglu import LigerGELUMulFunction
|
| 10 |
+
from .rms_norm import LigerRMSNormFunction
|
| 11 |
+
from .rope import LigerRopeFunction
|
| 12 |
+
from .swiglu import LigerSiLUMulFunction
|
| 13 |
+
from .tiled_mlp import apply_tiled_mlp
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# NOTE: Not compile-friendly --> large deviations to the original implementation under compile
|
| 17 |
+
class LigerRMSNorm(nn.Module):
|
| 18 |
+
weight: nn.Parameter
|
| 19 |
+
variance_epsilon: float
|
| 20 |
+
|
| 21 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 22 |
+
return LigerRMSNormFunction.apply(
|
| 23 |
+
hidden_states,
|
| 24 |
+
self.weight,
|
| 25 |
+
self.variance_epsilon,
|
| 26 |
+
0,
|
| 27 |
+
"llama",
|
| 28 |
+
True,
|
| 29 |
+
None,
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
def extra_repr(self):
|
| 33 |
+
return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class LigerLinear(nn.Module):
|
| 37 |
+
weight: nn.Parameter
|
| 38 |
+
bias: nn.Parameter | None
|
| 39 |
+
|
| 40 |
+
can_torch_compile = True
|
| 41 |
+
|
| 42 |
+
def forward(self, input: torch.Tensor) -> torch.Tensor:
|
| 43 |
+
def _forward(module, x):
|
| 44 |
+
return nn.functional.linear(x, module.weight, module.bias)
|
| 45 |
+
|
| 46 |
+
compute_params = [p for p in self.parameters() if p.requires_grad]
|
| 47 |
+
|
| 48 |
+
return apply_tiled_mlp(
|
| 49 |
+
fn=_forward,
|
| 50 |
+
mlp_module=self,
|
| 51 |
+
x=input,
|
| 52 |
+
num_shards=None,
|
| 53 |
+
compute_params=compute_params,
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class LigerSwiGLUMLP(nn.Module):
|
| 58 |
+
gate_proj: nn.Linear
|
| 59 |
+
up_proj: nn.Linear
|
| 60 |
+
down_proj: nn.Linear
|
| 61 |
+
|
| 62 |
+
can_torch_compile = True
|
| 63 |
+
|
| 64 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 65 |
+
return self.down_proj(LigerSiLUMulFunction.apply(self.gate_proj(x), self.up_proj(x)))
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class LigerGEGLUMLP(nn.Module):
|
| 69 |
+
gate_proj: nn.Linear
|
| 70 |
+
up_proj: nn.Linear
|
| 71 |
+
down_proj: nn.Linear
|
| 72 |
+
|
| 73 |
+
can_torch_compile = True
|
| 74 |
+
|
| 75 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 76 |
+
return self.down_proj(LigerGELUMulFunction.apply(self.gate_proj(x), self.up_proj(x)))
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class LigerTiledSwiGLUMLP(nn.Module):
|
| 80 |
+
gate_proj: nn.Linear
|
| 81 |
+
up_proj: nn.Linear
|
| 82 |
+
down_proj: nn.Linear
|
| 83 |
+
|
| 84 |
+
can_torch_compile = True
|
| 85 |
+
|
| 86 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 87 |
+
def _mlp_forward(module, x):
|
| 88 |
+
"""Internal MLP forward function for tiled computation."""
|
| 89 |
+
gate = module.gate_proj(x)
|
| 90 |
+
up = module.up_proj(x)
|
| 91 |
+
return module.down_proj(LigerSiLUMulFunction.apply(gate, up))
|
| 92 |
+
|
| 93 |
+
compute_params = [p for p in self.parameters() if p.requires_grad]
|
| 94 |
+
|
| 95 |
+
return apply_tiled_mlp(
|
| 96 |
+
fn=_mlp_forward,
|
| 97 |
+
mlp_module=self,
|
| 98 |
+
x=x,
|
| 99 |
+
num_shards=None,
|
| 100 |
+
compute_params=compute_params,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class LigerTiledGEGLUMLP(nn.Module):
|
| 105 |
+
gate_proj: nn.Linear
|
| 106 |
+
up_proj: nn.Linear
|
| 107 |
+
down_proj: nn.Linear
|
| 108 |
+
|
| 109 |
+
can_torch_compile = True
|
| 110 |
+
|
| 111 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 112 |
+
def _mlp_forward(module, x):
|
| 113 |
+
"""Internal MLP forward function for tiled computation."""
|
| 114 |
+
gate = module.gate_proj(x)
|
| 115 |
+
up = module.up_proj(x)
|
| 116 |
+
return module.down_proj(LigerGELUMulFunction.apply(gate, up))
|
| 117 |
+
|
| 118 |
+
compute_params = [p for p in self.parameters() if p.requires_grad]
|
| 119 |
+
|
| 120 |
+
return apply_tiled_mlp(
|
| 121 |
+
fn=_mlp_forward,
|
| 122 |
+
mlp_module=self,
|
| 123 |
+
x=x,
|
| 124 |
+
num_shards=None,
|
| 125 |
+
compute_params=compute_params,
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def liger_rotary_pos_emb(
|
| 130 |
+
q: torch.Tensor,
|
| 131 |
+
k: torch.Tensor,
|
| 132 |
+
cos: torch.Tensor,
|
| 133 |
+
sin: torch.Tensor,
|
| 134 |
+
unsqueeze_dim: int = 1,
|
| 135 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 136 |
+
"""Apply standard rotary positional embedding to ``q`` and ``k``."""
|
| 137 |
+
return LigerRopeFunction.apply(q, k, cos, sin, None, unsqueeze_dim)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
@dataclass
|
| 141 |
+
class CrossEntropyOutput:
|
| 142 |
+
loss: torch.Tensor
|
| 143 |
+
z_loss: Optional[torch.Tensor] = None
|
| 144 |
+
token_accuracy: Optional[torch.Tensor] = None
|
| 145 |
+
predicted_tokens: Optional[torch.Tensor] = None
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def liger_fused_linear_cross_entropy(
|
| 149 |
+
input: torch.Tensor,
|
| 150 |
+
weight: torch.Tensor,
|
| 151 |
+
target: torch.Tensor,
|
| 152 |
+
bias: Optional[torch.Tensor] = None,
|
| 153 |
+
ce_weight: Optional[torch.Tensor] = None,
|
| 154 |
+
ignore_index: int = -100,
|
| 155 |
+
lse_square_scale: float = 0.0,
|
| 156 |
+
label_smoothing: float = 0.0,
|
| 157 |
+
reduction: str = "mean",
|
| 158 |
+
softcap: Optional[float] = None,
|
| 159 |
+
return_z_loss: bool = False,
|
| 160 |
+
accum_dtype: Optional[torch.dtype] = None,
|
| 161 |
+
use_token_scaling: bool = False,
|
| 162 |
+
return_token_accuracy: bool = False,
|
| 163 |
+
return_predicted_tokens: bool = False,
|
| 164 |
+
):
|
| 165 |
+
loss, z_loss, token_accuracy, predicted_tokens = LigerFusedLinearCrossEntropyFunction.apply(
|
| 166 |
+
input,
|
| 167 |
+
weight,
|
| 168 |
+
target,
|
| 169 |
+
bias,
|
| 170 |
+
ce_weight,
|
| 171 |
+
ignore_index,
|
| 172 |
+
lse_square_scale,
|
| 173 |
+
label_smoothing,
|
| 174 |
+
reduction,
|
| 175 |
+
softcap,
|
| 176 |
+
return_z_loss,
|
| 177 |
+
accum_dtype,
|
| 178 |
+
use_token_scaling,
|
| 179 |
+
return_token_accuracy,
|
| 180 |
+
return_predicted_tokens,
|
| 181 |
+
)
|
| 182 |
+
if not return_z_loss and not return_token_accuracy and not return_predicted_tokens:
|
| 183 |
+
return loss
|
| 184 |
+
return CrossEntropyOutput(
|
| 185 |
+
loss=loss,
|
| 186 |
+
z_loss=z_loss,
|
| 187 |
+
token_accuracy=token_accuracy,
|
| 188 |
+
predicted_tokens=predicted_tokens,
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
# NOTE: We have this intentional graph break as we encounter issues such as IMAs and Cublas errors.
|
| 193 |
+
# We know that this is an optimized kernel already so there is less ways to
|
| 194 |
+
# fuse it either way; we rely on torch compile to go through the base model to optimize.
|
| 195 |
+
@torch.compiler.disable
|
| 196 |
+
def LigerForCausalLMLoss(
|
| 197 |
+
logits: None, # to match transformers signature
|
| 198 |
+
labels: torch.Tensor,
|
| 199 |
+
vocab_size: int, # to match transformers signature
|
| 200 |
+
num_items_in_batch: Optional[int] = None,
|
| 201 |
+
ignore_index: int = -100,
|
| 202 |
+
shift_labels: Optional[torch.Tensor] = None,
|
| 203 |
+
hidden_states: torch.Tensor | None = None,
|
| 204 |
+
lm_head_weight: torch.Tensor | None = None,
|
| 205 |
+
lm_head_bias: torch.Tensor | None = None,
|
| 206 |
+
**kwargs,
|
| 207 |
+
):
|
| 208 |
+
# To match signature we hide these behind the kwargs but we expect a few kwargs to exist
|
| 209 |
+
hidden_size = kwargs.pop("hidden_size", None)
|
| 210 |
+
final_logit_softcapping = kwargs.pop("final_logit_softcapping", None)
|
| 211 |
+
|
| 212 |
+
if hidden_size is None or hidden_states is None or lm_head_weight is None:
|
| 213 |
+
raise ValueError(
|
| 214 |
+
f"`LigerForCausalLMLoss` requires the LLM's weight (found `{lm_head_weight is not None}`),"
|
| 215 |
+
f"the last hidden state (found `{hidden_states is not None}`), and the `hidden_size`"
|
| 216 |
+
f"(found `{hidden_size is not None}`). Please make sure to pass the necessary kwargs."
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
applicable_params = inspect.signature(liger_fused_linear_cross_entropy).parameters
|
| 220 |
+
kwargs = {k: v for k, v in kwargs.items() if k in applicable_params}
|
| 221 |
+
|
| 222 |
+
if shift_labels is None:
|
| 223 |
+
labels = nn.functional.pad(labels, (0, 1), value=ignore_index)
|
| 224 |
+
shift_labels = labels[..., 1:].contiguous()
|
| 225 |
+
|
| 226 |
+
hidden_states = hidden_states.view(-1, hidden_size)
|
| 227 |
+
shift_labels = shift_labels.view(-1).to(hidden_states.device)
|
| 228 |
+
|
| 229 |
+
reduction = "sum" if num_items_in_batch is not None else "mean"
|
| 230 |
+
loss = liger_fused_linear_cross_entropy(
|
| 231 |
+
hidden_states,
|
| 232 |
+
lm_head_weight,
|
| 233 |
+
shift_labels,
|
| 234 |
+
bias=lm_head_bias,
|
| 235 |
+
reduction=reduction,
|
| 236 |
+
ignore_index=ignore_index,
|
| 237 |
+
softcap=final_logit_softcapping,
|
| 238 |
+
return_token_accuracy=False,
|
| 239 |
+
return_predicted_tokens=False,
|
| 240 |
+
**kwargs,
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
if reduction == "sum":
|
| 244 |
+
loss = loss / num_items_in_batch
|
| 245 |
+
|
| 246 |
+
return loss
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
# Add torch compile support for functions - in this case it's to allow this to be used
|
| 250 |
+
# but the function itself will not be compiled (see the note at the function)
|
| 251 |
+
liger_rotary_pos_emb.can_torch_compile = True
|
| 252 |
+
LigerForCausalLMLoss.can_torch_compile = True
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
__all__ = [
|
| 256 |
+
"LigerRMSNorm",
|
| 257 |
+
"LigerLinear",
|
| 258 |
+
"LigerSwiGLUMLP",
|
| 259 |
+
"LigerGEGLUMLP",
|
| 260 |
+
"LigerTiledSwiGLUMLP",
|
| 261 |
+
"LigerTiledGEGLUMLP",
|
| 262 |
+
"liger_rotary_pos_emb",
|
| 263 |
+
"LigerForCausalLMLoss",
|
| 264 |
+
]
|
build/torch-xpu/liger_kernels/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ctypes
|
| 2 |
+
import importlib.util
|
| 3 |
+
import sys
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from types import ModuleType
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def _import_from_path(file_path: Path) -> ModuleType:
|
| 9 |
+
# We cannot use the module name as-is, after adding it to `sys.modules`,
|
| 10 |
+
# it would also be used for other imports. So, we make a module name that
|
| 11 |
+
# depends on the path for it to be unique using the hex-encoded hash of
|
| 12 |
+
# the path.
|
| 13 |
+
path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
|
| 14 |
+
module_name = path_hash
|
| 15 |
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
| 16 |
+
if spec is None:
|
| 17 |
+
raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
|
| 18 |
+
module = importlib.util.module_from_spec(spec)
|
| 19 |
+
if module is None:
|
| 20 |
+
raise ImportError(f"Cannot load module {module_name} from spec")
|
| 21 |
+
sys.modules[module_name] = module
|
| 22 |
+
spec.loader.exec_module(module) # type: ignore
|
| 23 |
+
return module
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
|
build/torch-xpu/metadata.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "liger-kernels",
|
| 3 |
+
"id": "_liger_kernels_xpu_afb82fe",
|
| 4 |
+
"version": 2,
|
| 5 |
+
"license": "BSD-2-Clause",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "xpu"
|
| 9 |
+
}
|
| 10 |
+
}
|
build/torch-xpu/metadata.json.sigstore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json","verificationMaterial":{"certificate":{"rawBytes":"MIIHSzCCBtKgAwIBAgIUNYsKdoVscGuY8AOg/YXOY7TJNgQwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwNjEyMTAyMzI2WhcNMjYwNjEyMTAzMzI2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwL6cgzB4MXkXvEoxoA8Niog8qPBuIo2l66jZuJJooZ14fOoj/FmKDPXaiCOcQD8/DJ+onqzoAMPLYV3eVd/yu6OCBfEwggXtMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUI+4luUNNMEbXbA3VwIXr/8vqXZIwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wawYDVR0RAQH/BGEwX4ZdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoYWZiODJmZWU3OGEwODFjZWEwYjQ5ZGFiOWE0NGRhYzIwMzY2ZGYzYzATBgorBgEEAYO/MAEEBAVCdWlsZDArBgorBgEEAYO/MAEFBB1odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMG0GCisGAQQBg78wAQkEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoYWZiODJmZWU3OGEwODFjZWEwYjQ5ZGFiOWE0NGRhYzIwMzY2ZGYzYzAbBgorBgEEAYO/MAELBA0MC3NlbGYtaG9zdGVkMEAGCisGAQQBg78wAQwEMgwwaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MDgGCisGAQQBg78wAQ0EKgwoYWZiODJmZWU3OGEwODFjZWEwYjQ5ZGFiOWE0NGRhYzIwMzY2ZGYzYzAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAaBgorBgEEAYO/MAEPBAwMCjEwNzE0NzU1MjkwLgYKKwYBBAGDvzABEAQgDB5odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2UwGAYKKwYBBAGDvzABEQQKDAgyNTcyMDc0MzBtBgorBgEEAYO/MAESBF8MXWh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS8uZ2l0aHViL3dvcmtmbG93cy9idWlsZC55YW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAETBCoMKGFmYjgyZmVlNzhhMDgxY2VhMGI0OWRhYjlhNDRkYWMyMDM2NmRmM2MwIQYKKwYBBAGDvzABFAQTDBF3b3JrZmxvd19kaXNwYXRjaDBkBgorBgEEAYO/MAEVBFYMVGh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS9hY3Rpb25zL3J1bnMvMjc0MDk3MDAwNjkvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzBGBgorBgEEAYO/MAEYBDgMNnJlcG86aHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHk6cmVmOnJlZnMvaGVhZHMvbWFpbjCBiwYKKwYBBAHWeQIEAgR9BHsAeQB3AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABnrtbpW4AAAQDAEgwRgIhALRO/Yxh+kX7lF4Fc8leqsAbWvJh6aUwWZeE/KjbTIqIAiEA2U+uwjZShcChh4ANGWz1hiHYfKFzKSvQ4rGIyR12Jd0wCgYIKoZIzj0EAwMDZwAwZAIxAOwAQp3PkAz11rb3IlIB+x9P3oujPssjgutBbdOI1PKlQMs4MESfl55b47itcZcM+QIva6hW1q5PM35WXFMQ5zOEd40W+wLCMiTxnmNZj11RksrnzQbaWPOHt5cu8SPTBb4="},"tlogEntries":[{"logIndex":"1801786239","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"hashedrekord","version":"0.0.1"},"integratedTime":"1781259806","inclusionPromise":{"signedEntryTimestamp":"MEUCIQCHqGZ14vXsDNNIOy3K2Wc3mf4wEb/bQy73T3B2zoZq7wIgCaaXICvLQyBnY2GejVYIBwlz6VO0wZPaib5IdCF22UU="},"inclusionProof":{"logIndex":"1679881977","rootHash":"Pf/D4eAo8H1S8Bn9/tyHOEHGXs29Nu4Fb4CfRPG/A6U=","treeSize":"1679882071","hashes":["cp8H6zQf02EzMUsznEytKusSSAr3ni9dP5bMjWIOOPg=","UCbg2pSeTyXhD/XbzywhCYY3z6ahhlDqNaA0nNqJMZ0=","VLg4WdqM69USqsB8er7C4s993pK5Ti3KFNrt2ZXMjWE=","VMsuT/+OZrb6ekIeWGFsoywA57FGjOK+0pDr5lsIh7g=","rV1f3r5vik186SF7kl40BQGUg68uoBRetDyHtAgaRE8=","uu40hE0P9ZIi/agZ9vwESWaHZpe84r9LltxkBqCZ/0Q=","e//Rtkj6VrK452qT7oFiN+zyUEQw++ys7MzKPYQqtDA=","sJ9J60hxa7frilk1j9XNZA1fWSXNznU4lyqlQbI4TYI=","eaD5hPwDC5J8S06fg4+T1fxT5zzscOUJ5vNY6WqXSHE=","BxjQrs56pFR6o4LnLB9DwBKJ9li9Q0ovwEpxXRCNtso=","AMFlZap06R4Lm803t0WznfQuWcHvDKpht8oBK2GFnSw=","CITr9CfG5CX3u+1OC78Hb/Lciz6zJo9uwibRQ7NmqKo=","CqceEtcR4cD/BKPcQtUzBX2jNslbaExgbziymRTgMP0=","kuXKJNIHwI2DBdz9DroxF+DjzudB0c6QoTMGZfOOCOE=","1jn1ESjNaXF3WIMzN7zLvKm9W9GCtKCFh/D3gyEvJ3A=","xkhaNB/WnIm8ph8OnY2kY2XMhw3TOt8z60JK9RmoStA=","lYGQ9ibwC8+smMkPQ6TchJm3H9Nc/aTYLfdRacGFChw=","daxmZaajRpZV+JxHiOYZhJBiSKN5ucqjh2WnGbHhirw=","DOCeoSMovIvLExkhIvisow9AuNXgeWs4ECkyR6EcqYU="],"checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n1679882071\nPf/D4eAo8H1S8Bn9/tyHOEHGXs29Nu4Fb4CfRPG/A6U=\n\n— rekor.sigstore.dev wNI9ajBEAiBcN5G1+YoqyaZXZ5hQT7lHLSGHg3l+B0FwJsKZjEx6WwIgU496iPyX/p3ZwQdJQj0FAb3QHkS9KjbRcNyLh9fRaic=\n"}},"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyNDNjZDViOThkOGQ1YzE0YTFkYTM1NDdiZGI1MmRlODMyMDBjMzU4ZWJlMDU3OGYzNWFiNDE5NjQzMTAxMDM3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJRFZCUmtSWXhFbFBjdkNSTzJJcEdTOWh1MklYd1YwYVJ3UU1rTXNySlJRckFpRUF3Wlg5a0Y3UGdiV3oza3ZjMEx1M1pFVUppRTQ0ZVN5bjh5dVdibzR0b25FPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaFRla05EUW5STFowRjNTVUpCWjBsVlRsbHpTMlJ2Vm5OalIzVlpPRUZQWnk5WldFOVpOMVJLVG1kUmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDVxUlhsTlZFRjVUWHBKTWxkb1kwNU5hbGwzVG1wRmVVMVVRWHBOZWtreVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVYzVERaalozcENORTFZYTFoMlJXOTRiMEU0VG1sdlp6aHhVRUoxU1c4eWJEWTJhbG9LZFVwS2IyOWFNVFJtVDI5cUwwWnRTMFJRV0dGcFEwOWpVVVE0TDBSS0syOXVjWHB2UVUxUVRGbFdNMlZXWkM5NWRUWlBRMEptUlhkbloxaDBUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZKS3pSc0NuVlZUazVOUldKWVlrRXpWbmRKV0hJdk9IWnhXRnBKZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJGM1dVUldVakJTUVZGSUwwSkhSWGRZTkZwa1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKb01Wb3laSEJpYldSdFdWZE9iQXBNTW5Sc1kyMDFiR0pJVFhSWk1qbDBZbGhXZFdGWVVqVk1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1rb3hZVmQ0YTB4dWJHaGlWM2hCQ21OdFZtMWplVGx2V2xkR2EyTjVPWFJaVjJ4MVRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpBS1lWYzVkV041Tlc1aFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NIZFpTMHQzV1VKQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWN3cGlNMlJtV2tkc2VtTkhSakJaTW1kM1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOVpWMXBwVDBSS2JWcFhWVE5QUjBWM1QwUkdhbHBYUlhkWmFsRTFDbHBIUm1sUFYwVXdUa2RTYUZsNlNYZE5lbGt5V2tkWmVsbDZRVlJDWjI5eVFtZEZSVUZaVHk5TlFVVkZRa0ZXUTJSWGJITmFSRUZ5UW1kdmNrSm5SVVVLUVZsUEwwMUJSVVpDUWpGdlpGZGtibUZYTlc1YWJVWnFXbE01Y2xwWVNuVmFWM2g2VEZkT2RtSlhNVEZpYld3d1pWUkJaRUpuYjNKQ1owVkZRVmxQTHdwTlFVVkhRa0U1ZVZwWFducE1NbWhzV1ZkU2Vrd3lNV2hoVnpSM1QzZFpTMHQzV1VKQ1FVZEVkbnBCUWtOQlVYUkVRM1J2WkVoU2QyTjZiM1pNTTFKMkNtRXlWblZNYlVacVpFZHNkbUp1VFhWYU1td3dZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVY3dSME5wYzBkQlVWRkNaemM0ZDBGUmEwVUtXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5YURGYU1tUndZbTFrYlZsWFRteE1NblJzWTIwMWJHSklUWFJaTWpsMFlsaFdkUXBoV0ZJMVRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2VERKS01XRlhlR3RNYm14b1lsZDRRV050Vm0xamVUbHZXbGRHYTJONU9YUlpWMngxQ2sxRVowZERhWE5IUVZGUlFtYzNPSGRCVVc5RlMyZDNiMWxYV21sUFJFcHRXbGRWTTA5SFJYZFBSRVpxV2xkRmQxbHFVVFZhUjBacFQxZEZNRTVIVW1nS1dYcEpkMDE2V1RKYVIxbDZXWHBCWWtKbmIzSkNaMFZGUVZsUEwwMUJSVXhDUVRCTlF6Tk9iR0pIV1hSaFJ6bDZaRWRXYTAxRlFVZERhWE5IUVZGUlFncG5OemgzUVZGM1JVMW5kM2RoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlUxRVowZERhWE5IUVZGUlFtYzNPSGRCVVRCRlMyZDNiMWxYV21sUFJFcHRXbGRWTTA5SFJYZFBSRVpxV2xkRmQxbHFVVFVLV2tkR2FVOVhSVEJPUjFKb1dYcEpkMDE2V1RKYVIxbDZXWHBCWmtKbmIzSkNaMFZGUVZsUEwwMUJSVTlDUWtWTlJETktiRnB1VFhaaFIxWm9Xa2hOZGdwaVYwWndZbXBCWVVKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhkTlEycEZkMDU2UlRCT2VsVXhUV3ByZDB4bldVdExkMWxDUWtGSFJIWjZRVUpGUVZGbkNrUkNOVzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWM1IwRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVc0tSRUZuZVU1VVkzbE5SR013VFhwQ2RFSm5iM0pDWjBWRlFWbFBMMDFCUlZOQ1JqaE5XRmRvTUdSSVFucFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVid3BrVjJSdVlWYzFibHB0Um1wYVV6bHlXbGhLZFZwWGVIcE1WMDUyWWxjeE1XSnRiREJsVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxwQ21SWGJITmFRelUxV1ZjeGMxRklTbXhhYmsxMllVZFdhRnBJVFhaaVYwWndZbXBCTkVKbmIzSkNaMFZGUVZsUEwwMUJSVlJDUTI5TlMwZEdiVmxxWjNrS1dtMVdiRTU2YUdoTlJHZDRXVEpXYUUxSFNUQlBWMUpvV1dwc2FFNUVVbXRaVjAxNVRVUk5NazV0VW0xTk1rMTNTVkZaUzB0M1dVSkNRVWRFZG5wQlFncEdRVkZVUkVKR00ySXpTbkphYlhoMlpERTVhMkZZVG5kWldGSnFZVVJDYTBKbmIzSkNaMFZGUVZsUEwwMUJSVlpDUmxsTlZrZG9NR1JJUW5wUGFUaDJDbG95YkRCaFNGWnBURzFPZG1KVE9XOWtWMlJ1WVZjMWJscHRSbXBhVXpseVdsaEtkVnBYZUhwTVYwNTJZbGN4TVdKdGJEQmxVemxvV1ROU2NHSXlOWG9LVEROS01XSnVUWFpOYW1Nd1RVUnJNMDFFUVhkT2FtdDJXVmhTTUZwWE1YZGtTRTEyVFZSQlYwSm5iM0pDWjBWRlFWbFBMMDFCUlZkQ1FXZE5RbTVDTVFwWmJYaHdXWHBDUjBKbmIzSkNaMFZGUVZsUEwwMUJSVmxDUkdkTlRtNUtiR05IT0RaaFNGWnVXakpzZFZveVdtaFpNbFYyWVRKV2VXSnRWbk5qZVRGcUNtSXlNWFJrVnpWd1pFaHJObU50Vm0xUGJrcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRMEpwZDFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT1VKSWMwRUtaVkZDTTBGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtNXlkR0p3VnpSQlFVRlJSQXBCUldkM1VtZEphRUZNVWs4dldYaG9LMnRZTjJ4R05FWmpPR3hsY1hOQllsZDJTbWcyWVZWM1YxcGxSUzlMYW1KVVNYRkpRV2xGUVRKVkszVjNhbHBUQ21oalEyaG9ORUZPUjFkNk1XaHBTRmxtUzBaNlMxTjJVVFJ5UjBsNVVqRXlTbVF3ZDBObldVbExiMXBKZW1vd1JVRjNUVVJhZDBGM1drRkplRUZQZDBFS1VYQXpVR3RCZWpFeGNtSXpTV3hKUWl0NE9WQXpiM1ZxVUhOemFtZDFkRUppWkU5Sk1WQkxiRkZOY3pSTlJWTm1iRFUxWWpRM2FYUmpXbU5OSzFGSmRncGhObWhYTVhFMVVFMHpOVmRZUmsxUk5YcFBSV1EwTUZjcmQweERUV2xVZUc1dFRscHFNVEZTYTNOeWJucFJZbUZYVUU5SWREVmpkVGhUVUZSQ1lqUTlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ=="}],"timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyTADAgEAMIICwAYJKoZIhvcNAQcCoIICsTCCAq0CAQMxDTALBglghkgBZQMEAgEwgbgGCyqGSIb3DQEJEAEEoIGoBIGlMIGiAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgyw72kjAa0/agVgM99pf0BSx/YmHn9R3peKnmbETm0/UCFQCDlNlEd+s3CQ88r415t0lF06b1OBgPMjAyNjA2MTIxMDIzMjZaMAMCAQGgMqQwMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhoAAxggHaMIIB1gIBATBRMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAsGCWCGSAFlAwQCAaCB/DAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDYxMjEwMjMyNlowLwYJKoZIhvcNAQkEMSIEIB9BRQeh87N0jqz1HsdxE81aPNjrTeN9a69PG4Ei+KV1MIGOBgsqhkiG9w0BCRACLzF/MH0wezB5BCCF+Se8B6tiysO0Q1bBDvyBssaIP9p6uebYcNnROs0FtzBVMD2kOzA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDAKBggqhkjOPQQDAgRmMGQCMAFuarl2h9/1NOjYkbIUpKmUfVbrEfoVbIB1/d3yJNffh61lRgL0X8mvHvJJfdtq9wIwX4L5gVRafVJiFnDLrvhIOewGaiWHEloss1Ozr/RrHDvRWvsbeVBDsfj0Nlz8ERou"}]}},"messageSignature":{"messageDigest":{"algorithm":"SHA2_256","digest":"JDzVuY2NXBSh2jVHvbUt6DIAw1jr4FePNatBlkMQEDc="},"signature":"MEUCIDVBRkRYxElPcvCRO2IpGS9hu2IXwV0aRwQMkMsrJRQrAiEAwZX9kF7PgbWz3kvc0Lu3ZEUJiE44eSyn8yuWbo4tonE="}}
|
build/torch-xpu/qwen2vl_mrope.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@triton.jit
|
| 7 |
+
def _triton_qwen2vl_mrope(
|
| 8 |
+
q_ptr,
|
| 9 |
+
k_ptr,
|
| 10 |
+
cos,
|
| 11 |
+
sin,
|
| 12 |
+
sl,
|
| 13 |
+
bs: tl.constexpr,
|
| 14 |
+
n_qh: tl.constexpr,
|
| 15 |
+
n_kh: tl.constexpr,
|
| 16 |
+
hd: tl.constexpr,
|
| 17 |
+
pad_n_qh: tl.constexpr,
|
| 18 |
+
pad_n_kh: tl.constexpr,
|
| 19 |
+
pad_hd: tl.constexpr,
|
| 20 |
+
mrope_section_t: tl.constexpr,
|
| 21 |
+
mrope_section_h: tl.constexpr,
|
| 22 |
+
BLOCK_SIZE: tl.constexpr,
|
| 23 |
+
BACKWARD_PASS: tl.constexpr = False,
|
| 24 |
+
):
|
| 25 |
+
pid = tl.program_id(0)
|
| 26 |
+
|
| 27 |
+
# locate start address
|
| 28 |
+
q_ptr = q_ptr + pid * (n_qh * hd)
|
| 29 |
+
k_ptr = k_ptr + pid * (n_kh * hd)
|
| 30 |
+
|
| 31 |
+
# ####################################################################
|
| 32 |
+
# get the cos(mθ_{i...d/2}) and sin(mθ_{i...d/2}) for token position
|
| 33 |
+
# m of this program instance
|
| 34 |
+
# ####################################################################
|
| 35 |
+
|
| 36 |
+
# 1. program instances are laid out in a 1D vector of size bsz * seq_len, which
|
| 37 |
+
# effectively represents a 2D grid of size [bsz, seq_len] with seq_len dimension
|
| 38 |
+
# being the fastest changing dimension. Thus we can simply do pid // sl to get the batch index
|
| 39 |
+
# and pid % sl to get the sequence index.
|
| 40 |
+
# 2. We only need the left half of cos and sin matrix because the right half is just
|
| 41 |
+
# a clone of the left half.
|
| 42 |
+
t_end = mrope_section_t
|
| 43 |
+
h_end = t_end + mrope_section_h
|
| 44 |
+
|
| 45 |
+
t_cos = cos + pid * hd
|
| 46 |
+
h_cos = t_cos + bs * sl * hd
|
| 47 |
+
w_cos = h_cos + bs * sl * hd
|
| 48 |
+
t_sin = sin + pid * hd
|
| 49 |
+
h_sin = t_sin + bs * sl * hd
|
| 50 |
+
w_sin = h_sin + bs * sl * hd
|
| 51 |
+
|
| 52 |
+
cos_offsets = tl.arange(0, pad_hd // 2)
|
| 53 |
+
t_mask = cos_offsets < t_end
|
| 54 |
+
h_mask = (t_end <= cos_offsets) & (cos_offsets < h_end)
|
| 55 |
+
w_mask = (h_end <= cos_offsets) & (cos_offsets < hd // 2)
|
| 56 |
+
t_cos_row = tl.load(t_cos + cos_offsets, mask=t_mask, other=0)
|
| 57 |
+
h_cos_row = tl.load(h_cos + cos_offsets, mask=h_mask, other=0)
|
| 58 |
+
w_cos_row = tl.load(w_cos + cos_offsets, mask=w_mask, other=0)
|
| 59 |
+
t_sin_row = tl.load(t_sin + cos_offsets, mask=t_mask, other=0)
|
| 60 |
+
h_sin_row = tl.load(h_sin + cos_offsets, mask=h_mask, other=0)
|
| 61 |
+
w_sin_row = tl.load(w_sin + cos_offsets, mask=w_mask, other=0)
|
| 62 |
+
cos_row = t_cos_row + h_cos_row + w_cos_row
|
| 63 |
+
sin_row = t_sin_row + h_sin_row + w_sin_row
|
| 64 |
+
|
| 65 |
+
# ####################################################################
|
| 66 |
+
# Load the left and right half of q and k for the current
|
| 67 |
+
# program instance (i.e. for the current token) separately
|
| 68 |
+
# ####################################################################
|
| 69 |
+
# left half of the head
|
| 70 |
+
first_half_q_offsets = tl.arange(0, pad_n_qh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
|
| 71 |
+
first_half_k_offsets = tl.arange(0, pad_n_kh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
|
| 72 |
+
first_q_mask = (tl.arange(0, pad_n_qh)[:, None] < n_qh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
|
| 73 |
+
first_k_mask = (tl.arange(0, pad_n_kh)[:, None] < n_kh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
|
| 74 |
+
q_tile_1 = tl.load(q_ptr + first_half_q_offsets, mask=first_q_mask, other=0).to(sin_row.dtype)
|
| 75 |
+
k_tile_1 = tl.load(k_ptr + first_half_k_offsets, mask=first_k_mask, other=0).to(sin_row.dtype)
|
| 76 |
+
|
| 77 |
+
# right half of the head
|
| 78 |
+
second_half_q_offsets = first_half_q_offsets + (hd // 2)
|
| 79 |
+
second_half_k_offsets = first_half_k_offsets + (hd // 2)
|
| 80 |
+
second_q_mask = first_q_mask
|
| 81 |
+
second_k_mask = first_k_mask
|
| 82 |
+
q_tile_2 = tl.load(q_ptr + second_half_q_offsets, mask=second_q_mask, other=0).to(sin_row.dtype)
|
| 83 |
+
k_tile_2 = tl.load(k_ptr + second_half_k_offsets, mask=second_k_mask, other=0).to(sin_row.dtype)
|
| 84 |
+
|
| 85 |
+
if not BACKWARD_PASS:
|
| 86 |
+
# y = [x1, x2] * [cos, cos] + [-x2, x1] * [sin, sin]
|
| 87 |
+
new_q_tile_1 = q_tile_1 * cos_row - q_tile_2 * sin_row
|
| 88 |
+
tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
|
| 89 |
+
new_q_tile_2 = q_tile_2 * cos_row + q_tile_1 * sin_row
|
| 90 |
+
tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
|
| 91 |
+
|
| 92 |
+
new_k_tile_1 = k_tile_1 * cos_row - k_tile_2 * sin_row
|
| 93 |
+
tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
|
| 94 |
+
new_k_tile_2 = k_tile_2 * cos_row + k_tile_1 * sin_row
|
| 95 |
+
tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
|
| 96 |
+
else:
|
| 97 |
+
# with some math, we can get:
|
| 98 |
+
# dy = [dx1, dx2] * [cos, cos] + [-dx2, dx1] * [-sin, -sin]
|
| 99 |
+
new_q_tile_1 = q_tile_1 * cos_row + q_tile_2 * sin_row
|
| 100 |
+
tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
|
| 101 |
+
new_q_tile_2 = q_tile_2 * cos_row - q_tile_1 * sin_row
|
| 102 |
+
tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
|
| 103 |
+
|
| 104 |
+
new_k_tile_1 = k_tile_1 * cos_row + k_tile_2 * sin_row
|
| 105 |
+
tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
|
| 106 |
+
new_k_tile_2 = k_tile_2 * cos_row - k_tile_1 * sin_row
|
| 107 |
+
tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def qwen2vl_mrope_forward(q, k, cos, sin, mrope_section):
|
| 111 |
+
# transpose it back to the physical shape because Triton looks at the physical storage
|
| 112 |
+
# note: q and k are incontiguous before the transformation and will become contiguous after transpose
|
| 113 |
+
q = q.transpose(1, 2)
|
| 114 |
+
k = k.transpose(1, 2)
|
| 115 |
+
|
| 116 |
+
batch_size, seq_len, n_q_head, head_dim = q.shape
|
| 117 |
+
n_kv_head = k.shape[2]
|
| 118 |
+
pad_hd = triton.next_power_of_2(head_dim)
|
| 119 |
+
pad_n_q_head = triton.next_power_of_2(n_q_head)
|
| 120 |
+
pad_n_kv_head = triton.next_power_of_2(n_kv_head)
|
| 121 |
+
BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
|
| 122 |
+
|
| 123 |
+
n_row = batch_size * seq_len
|
| 124 |
+
|
| 125 |
+
# ensure tensors passed into the kernel are contiguous. It will be no-op if they are already contiguous
|
| 126 |
+
q = q.contiguous()
|
| 127 |
+
k = k.contiguous()
|
| 128 |
+
cos = cos.contiguous()
|
| 129 |
+
sin = sin.contiguous()
|
| 130 |
+
|
| 131 |
+
_triton_qwen2vl_mrope[(n_row,)](
|
| 132 |
+
q,
|
| 133 |
+
k,
|
| 134 |
+
cos,
|
| 135 |
+
sin,
|
| 136 |
+
seq_len,
|
| 137 |
+
batch_size,
|
| 138 |
+
n_q_head,
|
| 139 |
+
n_kv_head,
|
| 140 |
+
head_dim,
|
| 141 |
+
pad_n_q_head,
|
| 142 |
+
pad_n_kv_head,
|
| 143 |
+
pad_hd,
|
| 144 |
+
mrope_section[0],
|
| 145 |
+
mrope_section[1],
|
| 146 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 147 |
+
BACKWARD_PASS=False,
|
| 148 |
+
)
|
| 149 |
+
return q.transpose(1, 2), k.transpose(1, 2), cos, sin
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def qwen2vl_mrope_backward(dq, dk, cos, sin, mrope_section):
|
| 153 |
+
dq = dq.transpose(1, 2)
|
| 154 |
+
dk = dk.transpose(1, 2)
|
| 155 |
+
|
| 156 |
+
batch_size, seq_len, n_q_head, head_dim = dq.shape
|
| 157 |
+
n_kv_head = dk.shape[2]
|
| 158 |
+
pad_hd = triton.next_power_of_2(head_dim)
|
| 159 |
+
pad_n_q_head = triton.next_power_of_2(n_q_head)
|
| 160 |
+
pad_n_kv_head = triton.next_power_of_2(n_kv_head)
|
| 161 |
+
BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
|
| 162 |
+
|
| 163 |
+
n_row = batch_size * seq_len
|
| 164 |
+
|
| 165 |
+
# ensure dq and dk are contiguous
|
| 166 |
+
dq = dq.contiguous()
|
| 167 |
+
dk = dk.contiguous()
|
| 168 |
+
|
| 169 |
+
# backward is similar to forward except swapping few ops
|
| 170 |
+
_triton_qwen2vl_mrope[(n_row,)](
|
| 171 |
+
dq,
|
| 172 |
+
dk,
|
| 173 |
+
cos,
|
| 174 |
+
sin,
|
| 175 |
+
seq_len,
|
| 176 |
+
batch_size,
|
| 177 |
+
n_q_head,
|
| 178 |
+
n_kv_head,
|
| 179 |
+
head_dim,
|
| 180 |
+
pad_n_q_head,
|
| 181 |
+
pad_n_kv_head,
|
| 182 |
+
pad_hd,
|
| 183 |
+
mrope_section[0],
|
| 184 |
+
mrope_section[1],
|
| 185 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 186 |
+
BACKWARD_PASS=True,
|
| 187 |
+
)
|
| 188 |
+
return dq.transpose(1, 2), dk.transpose(1, 2)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
class LigerQwen2VLMRopeFunction(torch.autograd.Function):
|
| 192 |
+
"""
|
| 193 |
+
Triton implementation of the Qwen2VL Multimodal Rotary Positional Embedding (M-RoPE) operation.
|
| 194 |
+
|
| 195 |
+
Please find the corresponding HuggingFace implementation here:
|
| 196 |
+
https://github.com/huggingface/transformers/blob/main/src/transformers/models/qwen2_vl/modeling_qwen2_vl.py
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
@staticmethod
|
| 200 |
+
def forward(ctx, q, k, cos, sin, mrope_section, unsqueeze_dim=1):
|
| 201 |
+
"""
|
| 202 |
+
q size: (bsz, n_q_head, seq_len, head_dim)
|
| 203 |
+
k size: (bsz, n_kv_head, seq_len, head_dim)
|
| 204 |
+
cos size: (3, bsz, seq_len, head_dim)
|
| 205 |
+
sin size: (3, bsz, seq_len, head_dim)
|
| 206 |
+
"""
|
| 207 |
+
q, k, cos, sin = qwen2vl_mrope_forward(q, k, cos, sin, mrope_section)
|
| 208 |
+
ctx.save_for_backward(cos, sin)
|
| 209 |
+
ctx.mrope_section = mrope_section
|
| 210 |
+
return q, k
|
| 211 |
+
|
| 212 |
+
def backward(ctx, dq, dk):
|
| 213 |
+
"""
|
| 214 |
+
dq size: (bsz, n_q_head, seq_len, head_dim)
|
| 215 |
+
dk size: (bsz, n_kv_head, seq_len, head_dim)
|
| 216 |
+
cos size: (3, bsz, seq_len, head_dim)
|
| 217 |
+
sin size: (3, bsz, seq_len, head_dim)
|
| 218 |
+
"""
|
| 219 |
+
cos, sin = ctx.saved_tensors
|
| 220 |
+
mrope_section = ctx.mrope_section
|
| 221 |
+
dq, dk = qwen2vl_mrope_backward(dq, dk, cos, sin, mrope_section)
|
| 222 |
+
return dq, dk, None, None, None, None
|
build/torch-xpu/rms_norm.py
ADDED
|
@@ -0,0 +1,654 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file incorporates code from Unsloth licensed under the Apache License, Version 2.0.
|
| 3 |
+
See the original Unsloth repository at https://github.com/unslothai/unsloth.
|
| 4 |
+
|
| 5 |
+
The following line
|
| 6 |
+
https://github.com/linkedin/Liger-Kernel/blob/7382a8761f9af679482b968f9348013d933947c7/src/liger_kernel/ops/rms_norm.py#L30
|
| 7 |
+
is based on code from Unsloth, located at:
|
| 8 |
+
https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/rms_layernorm.py#L22
|
| 9 |
+
|
| 10 |
+
Modifications made by Yanning Chen, 2024.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import math
|
| 14 |
+
import operator
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
import triton
|
| 18 |
+
import triton.language as tl
|
| 19 |
+
|
| 20 |
+
from .utils import calculate_settings
|
| 21 |
+
from .utils import compare_version
|
| 22 |
+
from .utils import ensure_contiguous
|
| 23 |
+
from .utils import get_npu_core_count
|
| 24 |
+
from .utils import set_large_grf_mode
|
| 25 |
+
from .utils import torch_to_triton_dtype
|
| 26 |
+
from .utils import is_npu_available
|
| 27 |
+
|
| 28 |
+
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
|
| 29 |
+
try:
|
| 30 |
+
# typical import path with dispatch available
|
| 31 |
+
from triton.language.extra.libdevice import rsqrt
|
| 32 |
+
except ModuleNotFoundError:
|
| 33 |
+
# for working with NGC containers
|
| 34 |
+
from triton.language.extra.cuda.libdevice import rsqrt
|
| 35 |
+
else:
|
| 36 |
+
from triton.language.math import rsqrt
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
_CASTING_MODE_NONE: tl.constexpr = tl.constexpr(-1)
|
| 40 |
+
_CASTING_MODE_LLAMA: tl.constexpr = tl.constexpr(0)
|
| 41 |
+
_CASTING_MODE_GEMMA: tl.constexpr = tl.constexpr(1)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@triton.jit
|
| 45 |
+
def _rms_norm_forward_kernel(
|
| 46 |
+
Y_ptr,
|
| 47 |
+
Y_row_stride,
|
| 48 |
+
X_ptr,
|
| 49 |
+
X_row_stride,
|
| 50 |
+
W_ptr,
|
| 51 |
+
W_row_stride,
|
| 52 |
+
RSTD_ptr,
|
| 53 |
+
RSTD_row_stride,
|
| 54 |
+
n_cols,
|
| 55 |
+
eps,
|
| 56 |
+
offset,
|
| 57 |
+
casting_mode: tl.constexpr, # constexpr so the `if` blocks can be optimized out
|
| 58 |
+
elementwise_affine: tl.constexpr,
|
| 59 |
+
BLOCK_SIZE: tl.constexpr,
|
| 60 |
+
):
|
| 61 |
+
"""
|
| 62 |
+
y_i = (x_i / (RMS)) * (offset + wi), RMS = sqrt(sum(x_i^2) / N)
|
| 63 |
+
|
| 64 |
+
Reference:
|
| 65 |
+
1. https://triton-lang.org/main/getting-started/tutorials/05-layer-norm.html
|
| 66 |
+
2. https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/rms_layernorm.py#L22
|
| 67 |
+
3. https://arxiv.org/pdf/1910.07467
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
row_idx = tl.program_id(0).to(tl.int64)
|
| 71 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 72 |
+
mask = col_offsets < n_cols
|
| 73 |
+
|
| 74 |
+
y_base = Y_ptr + row_idx * Y_row_stride
|
| 75 |
+
x_base = X_ptr + row_idx * X_row_stride
|
| 76 |
+
rstd_base = RSTD_ptr + row_idx * RSTD_row_stride
|
| 77 |
+
|
| 78 |
+
X_row = tl.load(x_base + col_offsets, mask=mask, other=0)
|
| 79 |
+
X_row_dtype = X_row.dtype
|
| 80 |
+
if elementwise_affine:
|
| 81 |
+
W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0)
|
| 82 |
+
|
| 83 |
+
# On Llama, only rstd is computed on fp32
|
| 84 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 85 |
+
X_row = X_row.to(tl.float32)
|
| 86 |
+
|
| 87 |
+
# Gemma computes everything on fp32, and then casts back the output to the original dtype
|
| 88 |
+
if casting_mode == _CASTING_MODE_GEMMA:
|
| 89 |
+
if elementwise_affine:
|
| 90 |
+
W_row = W_row.to(tl.float32)
|
| 91 |
+
X_row = X_row.to(tl.float32)
|
| 92 |
+
|
| 93 |
+
if casting_mode == _CASTING_MODE_NONE:
|
| 94 |
+
eps = eps.to(X_row_dtype)
|
| 95 |
+
offset = offset.to(X_row_dtype)
|
| 96 |
+
|
| 97 |
+
mean_square = tl.sum(X_row * X_row, axis=0) / n_cols
|
| 98 |
+
rstd = rsqrt(mean_square + eps)
|
| 99 |
+
|
| 100 |
+
# We can save time by caching rms with minimal memory overhead
|
| 101 |
+
# because rms is much smaller compared to X_row, as rms is for each row.
|
| 102 |
+
# However, on the computation side, it can save 4 operations (*, sum, /, sqrt).
|
| 103 |
+
tl.store(rstd_base, rstd)
|
| 104 |
+
|
| 105 |
+
X_row = X_row * rstd
|
| 106 |
+
|
| 107 |
+
# On Llama, the multiplication with the weight is done on the original dtype
|
| 108 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 109 |
+
X_row = X_row.to(X_row_dtype)
|
| 110 |
+
|
| 111 |
+
if elementwise_affine:
|
| 112 |
+
Y_row = X_row * (offset + W_row)
|
| 113 |
+
else:
|
| 114 |
+
Y_row = X_row
|
| 115 |
+
|
| 116 |
+
if casting_mode == _CASTING_MODE_GEMMA:
|
| 117 |
+
Y_row = Y_row.to(X_row_dtype)
|
| 118 |
+
|
| 119 |
+
tl.store(y_base + col_offsets, Y_row, mask=mask)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
@triton.jit
|
| 123 |
+
def _rms_norm_backward_kernel(
|
| 124 |
+
dY_ptr,
|
| 125 |
+
dY_row_stride,
|
| 126 |
+
dX_ptr,
|
| 127 |
+
dX_row_stride,
|
| 128 |
+
X_ptr,
|
| 129 |
+
X_row_stride,
|
| 130 |
+
X_dtype: tl.constexpr,
|
| 131 |
+
W_ptr,
|
| 132 |
+
W_row_stride,
|
| 133 |
+
RSTD_ptr,
|
| 134 |
+
RSTD_row_stride,
|
| 135 |
+
dW_ptr,
|
| 136 |
+
dW_row_stride,
|
| 137 |
+
n_rows,
|
| 138 |
+
n_cols,
|
| 139 |
+
offset,
|
| 140 |
+
rows_per_program,
|
| 141 |
+
casting_mode: tl.constexpr,
|
| 142 |
+
elementwise_affine: tl.constexpr,
|
| 143 |
+
BLOCK_SIZE: tl.constexpr,
|
| 144 |
+
):
|
| 145 |
+
"""
|
| 146 |
+
dx = (1 / RMS) * [dy * (w + offset - (1 / N) * (1 / RMS^2) * ((dy * (w + offset)) dot x) * x]. * means element-wise multiplication, whileas dot means dot product
|
| 147 |
+
dw = sum(dy * (x / RMS)). summation over BxT dimension
|
| 148 |
+
"""
|
| 149 |
+
|
| 150 |
+
row_block_id = tl.program_id(0).to(tl.int64)
|
| 151 |
+
row_start = row_block_id * rows_per_program
|
| 152 |
+
row_end = min((row_block_id + 1) * rows_per_program, n_rows)
|
| 153 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 154 |
+
mask = col_offsets < n_cols
|
| 155 |
+
|
| 156 |
+
if elementwise_affine:
|
| 157 |
+
dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
|
| 158 |
+
|
| 159 |
+
if elementwise_affine:
|
| 160 |
+
W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0.0)
|
| 161 |
+
W_row = W_row + offset
|
| 162 |
+
|
| 163 |
+
for row_idx in range(row_start, row_end):
|
| 164 |
+
dy_base = dY_ptr + row_idx * dY_row_stride
|
| 165 |
+
dx_base = dX_ptr + row_idx * dX_row_stride
|
| 166 |
+
|
| 167 |
+
x_base = X_ptr + row_idx * X_row_stride
|
| 168 |
+
rstd_base = RSTD_ptr + row_idx * RSTD_row_stride
|
| 169 |
+
|
| 170 |
+
dY_row = tl.load(dy_base + col_offsets, mask=mask, other=0.0)
|
| 171 |
+
X_row = tl.load(x_base + col_offsets, mask=mask, other=0.0)
|
| 172 |
+
|
| 173 |
+
# Get cached rms
|
| 174 |
+
rstd_row = tl.load(rstd_base)
|
| 175 |
+
|
| 176 |
+
X_row = X_row.to(tl.float32)
|
| 177 |
+
|
| 178 |
+
# Different bacward graphs for different casting modes
|
| 179 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 180 |
+
if elementwise_affine:
|
| 181 |
+
m = (dY_row * W_row).to(tl.float32)
|
| 182 |
+
else:
|
| 183 |
+
m = dY_row.to(tl.float32)
|
| 184 |
+
|
| 185 |
+
elif casting_mode == _CASTING_MODE_GEMMA:
|
| 186 |
+
dY_row = dY_row.to(tl.float32)
|
| 187 |
+
if elementwise_affine:
|
| 188 |
+
m = dY_row * W_row
|
| 189 |
+
else:
|
| 190 |
+
m = dY_row
|
| 191 |
+
else:
|
| 192 |
+
if elementwise_affine:
|
| 193 |
+
m = dY_row * W_row
|
| 194 |
+
else:
|
| 195 |
+
m = dY_row
|
| 196 |
+
|
| 197 |
+
dX_row = rstd_row * m
|
| 198 |
+
|
| 199 |
+
dX_row += (rstd_row) * (-(1 / n_cols) * rstd_row * rstd_row * tl.sum(m * X_row, axis=0) * X_row)
|
| 200 |
+
|
| 201 |
+
if elementwise_affine:
|
| 202 |
+
# calculate the gradient of W
|
| 203 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 204 |
+
dW_row += dY_row * (X_row * rstd_row).to(X_dtype)
|
| 205 |
+
else:
|
| 206 |
+
# here X_row is already in fp32 (see previous if block)
|
| 207 |
+
dW_row += dY_row * (X_row * rstd_row)
|
| 208 |
+
|
| 209 |
+
tl.store(dx_base + col_offsets, dX_row.to(X_dtype), mask=mask)
|
| 210 |
+
|
| 211 |
+
if elementwise_affine:
|
| 212 |
+
tl.store(dW_ptr + row_block_id * dW_row_stride + col_offsets, dW_row, mask=mask)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
@triton.jit
|
| 216 |
+
def _block_rms_norm_forward_kernel(
|
| 217 |
+
Y_ptr,
|
| 218 |
+
Y_row_stride,
|
| 219 |
+
X_ptr,
|
| 220 |
+
X_row_stride,
|
| 221 |
+
W_ptr,
|
| 222 |
+
W_row_stride,
|
| 223 |
+
RSTD_ptr,
|
| 224 |
+
RSTD_row_stride,
|
| 225 |
+
n_rows,
|
| 226 |
+
n_cols,
|
| 227 |
+
eps,
|
| 228 |
+
offset,
|
| 229 |
+
casting_mode: tl.constexpr, # constexpr so the `if` blocks can be optimized out
|
| 230 |
+
elementwise_affine: tl.constexpr,
|
| 231 |
+
BLOCK_SIZE: tl.constexpr,
|
| 232 |
+
BLOCK_ROW: tl.constexpr,
|
| 233 |
+
):
|
| 234 |
+
"""
|
| 235 |
+
y_i = (x_i / (RMS)) * (offset + wi), RMS = sqrt(sum(x_i^2) / N)
|
| 236 |
+
|
| 237 |
+
Reference:
|
| 238 |
+
1. https://triton-lang.org/main/getting-started/tutorials/05-layer-norm.html
|
| 239 |
+
2. https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/rms_layernorm.py#L22
|
| 240 |
+
3. https://arxiv.org/pdf/1910.07467
|
| 241 |
+
"""
|
| 242 |
+
|
| 243 |
+
row_idx = tl.program_id(0) * BLOCK_ROW + tl.arange(0, BLOCK_ROW)
|
| 244 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 245 |
+
row_mask = row_idx < n_rows
|
| 246 |
+
col_mask = col_offsets < n_cols
|
| 247 |
+
|
| 248 |
+
X_row = tl.load(
|
| 249 |
+
X_ptr + row_idx[:, None] * X_row_stride + col_offsets[None, :],
|
| 250 |
+
mask=row_mask[:, None] & col_mask[None, :],
|
| 251 |
+
other=0,
|
| 252 |
+
)
|
| 253 |
+
X_row_dtype = X_row.dtype
|
| 254 |
+
if elementwise_affine:
|
| 255 |
+
W_row = tl.load(W_ptr + col_offsets, mask=col_mask, other=0)
|
| 256 |
+
|
| 257 |
+
# On Llama, only rstd is computed on fp32
|
| 258 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 259 |
+
X_row = X_row.to(tl.float32)
|
| 260 |
+
|
| 261 |
+
# Gemma computes everything on fp32, and then casts back the output to the original dtype
|
| 262 |
+
if casting_mode == _CASTING_MODE_GEMMA:
|
| 263 |
+
if elementwise_affine:
|
| 264 |
+
W_row = W_row.to(tl.float32)
|
| 265 |
+
X_row = X_row.to(tl.float32)
|
| 266 |
+
|
| 267 |
+
if casting_mode == _CASTING_MODE_NONE:
|
| 268 |
+
eps = eps.to(X_row_dtype)
|
| 269 |
+
offset = offset.to(X_row_dtype)
|
| 270 |
+
|
| 271 |
+
mean_square = tl.sum(X_row * X_row, axis=1) / n_cols
|
| 272 |
+
rstd = rsqrt(mean_square + eps)
|
| 273 |
+
|
| 274 |
+
# We can save time by caching rms with minimal memory overhead
|
| 275 |
+
# because rms is much smaller compared to X_row, as rms is for each row.
|
| 276 |
+
# However, on the computation side, it can save 4 operations (*, sum, /, sqrt).
|
| 277 |
+
tl.store(RSTD_ptr + row_idx * RSTD_row_stride, rstd, row_mask)
|
| 278 |
+
|
| 279 |
+
X_row = X_row * rstd[:, None]
|
| 280 |
+
|
| 281 |
+
# On Llama, the multiplication with the weight is done on the original dtype
|
| 282 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 283 |
+
X_row = X_row.to(X_row_dtype)
|
| 284 |
+
|
| 285 |
+
if elementwise_affine:
|
| 286 |
+
Y_row = X_row * (offset + W_row)[None, :]
|
| 287 |
+
else:
|
| 288 |
+
Y_row = X_row
|
| 289 |
+
|
| 290 |
+
if casting_mode == _CASTING_MODE_GEMMA:
|
| 291 |
+
Y_row = Y_row.to(X_row_dtype)
|
| 292 |
+
|
| 293 |
+
tl.store(
|
| 294 |
+
Y_ptr + row_idx[:, None] * Y_row_stride + col_offsets[None, :],
|
| 295 |
+
Y_row,
|
| 296 |
+
mask=row_mask[:, None] & col_mask[None, :],
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
@triton.jit
|
| 301 |
+
def _block_rms_norm_backward_kernel(
|
| 302 |
+
dY_ptr,
|
| 303 |
+
dY_row_stride,
|
| 304 |
+
dX_ptr,
|
| 305 |
+
dX_row_stride,
|
| 306 |
+
X_ptr,
|
| 307 |
+
X_row_stride,
|
| 308 |
+
X_dtype: tl.constexpr,
|
| 309 |
+
W_ptr,
|
| 310 |
+
W_row_stride,
|
| 311 |
+
RSTD_ptr,
|
| 312 |
+
RSTD_row_stride,
|
| 313 |
+
dW_ptr,
|
| 314 |
+
dW_row_stride,
|
| 315 |
+
n_rows,
|
| 316 |
+
n_cols,
|
| 317 |
+
offset,
|
| 318 |
+
casting_mode: tl.constexpr,
|
| 319 |
+
elementwise_affine: tl.constexpr,
|
| 320 |
+
BLOCK_SIZE: tl.constexpr,
|
| 321 |
+
BLOCK_ROW: tl.constexpr,
|
| 322 |
+
):
|
| 323 |
+
"""
|
| 324 |
+
dx = (1 / RMS) * [dy * (w + offset - (1 / N) * (1 / RMS^2) * ((dy * (w + offset)) dot x) * x]. * means element-wise multiplication, whileas dot means dot product
|
| 325 |
+
dw = sum(dy * (x / RMS)). summation over BxT dimension
|
| 326 |
+
"""
|
| 327 |
+
|
| 328 |
+
pid = tl.program_id(0).cast(tl.int64)
|
| 329 |
+
NUM_SMS = tl.num_programs(0)
|
| 330 |
+
|
| 331 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 332 |
+
col_mask = col_offsets < n_cols
|
| 333 |
+
|
| 334 |
+
if elementwise_affine:
|
| 335 |
+
dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
|
| 336 |
+
|
| 337 |
+
W_row = tl.load(W_ptr + col_offsets, mask=col_mask, other=0.0)
|
| 338 |
+
W_row = W_row + offset
|
| 339 |
+
|
| 340 |
+
for start in range(pid * BLOCK_ROW, n_rows, NUM_SMS * BLOCK_ROW):
|
| 341 |
+
row_idx = start + tl.arange(0, BLOCK_ROW)
|
| 342 |
+
row_mask = row_idx < n_rows
|
| 343 |
+
dY_row = tl.load(
|
| 344 |
+
dY_ptr + row_idx[:, None] * dY_row_stride + col_offsets[None, :],
|
| 345 |
+
mask=row_mask[:, None] & col_mask[None, :],
|
| 346 |
+
other=0.0,
|
| 347 |
+
)
|
| 348 |
+
X_row = tl.load(
|
| 349 |
+
X_ptr + row_idx[:, None] * X_row_stride + col_offsets[None, :],
|
| 350 |
+
mask=row_mask[:, None] & col_mask[None, :],
|
| 351 |
+
other=0.0,
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
# Get cached rms
|
| 355 |
+
rstd_row = tl.load(RSTD_ptr + row_idx * RSTD_row_stride, row_mask)
|
| 356 |
+
|
| 357 |
+
X_row = X_row.to(tl.float32)
|
| 358 |
+
|
| 359 |
+
# Different bacward graphs for different casting modes
|
| 360 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 361 |
+
if elementwise_affine:
|
| 362 |
+
m = (dY_row * W_row[None, :]).to(tl.float32)
|
| 363 |
+
else:
|
| 364 |
+
m = dY_row.to(tl.float32)
|
| 365 |
+
|
| 366 |
+
elif casting_mode == _CASTING_MODE_GEMMA:
|
| 367 |
+
dY_row = dY_row.to(tl.float32)
|
| 368 |
+
if elementwise_affine:
|
| 369 |
+
m = dY_row * W_row[None, :]
|
| 370 |
+
else:
|
| 371 |
+
m = dY_row
|
| 372 |
+
else:
|
| 373 |
+
if elementwise_affine:
|
| 374 |
+
m = dY_row * W_row[None, :]
|
| 375 |
+
else:
|
| 376 |
+
m = dY_row
|
| 377 |
+
|
| 378 |
+
dX_row = rstd_row[:, None] * m
|
| 379 |
+
|
| 380 |
+
dX_row += (rstd_row[:, None]) * (
|
| 381 |
+
-(1 / n_cols) * (rstd_row * rstd_row * tl.sum(m * X_row, axis=1))[:, None] * X_row
|
| 382 |
+
)
|
| 383 |
+
|
| 384 |
+
if elementwise_affine:
|
| 385 |
+
if casting_mode == _CASTING_MODE_LLAMA:
|
| 386 |
+
# TODO(tcc): use tl.sum(..., dtype=tl.float32) once we upgrade to triton>=3.3.0
|
| 387 |
+
dW_row += tl.sum((dY_row * (X_row * rstd_row[:, None]).to(X_dtype)).to(tl.float32), 0)
|
| 388 |
+
else:
|
| 389 |
+
# here X_row is already in fp32 (see previous if block)
|
| 390 |
+
dW_row += tl.sum(dY_row * (X_row * rstd_row[:, None]), 0)
|
| 391 |
+
|
| 392 |
+
tl.store(
|
| 393 |
+
dX_ptr + row_idx[:, None] * dX_row_stride + col_offsets[None, :],
|
| 394 |
+
dX_row,
|
| 395 |
+
mask=row_mask[:, None] & col_mask[None, :],
|
| 396 |
+
)
|
| 397 |
+
|
| 398 |
+
if elementwise_affine:
|
| 399 |
+
tl.store(dW_ptr + pid * dW_row_stride + col_offsets, dW_row, mask=col_mask)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
_str_to_casting_mode = {
|
| 403 |
+
"llama": _CASTING_MODE_LLAMA.value,
|
| 404 |
+
"gemma": _CASTING_MODE_GEMMA.value,
|
| 405 |
+
"none": _CASTING_MODE_NONE.value,
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
def rms_norm_forward(X, W, eps, offset, casting_mode, row_mode):
|
| 410 |
+
if not isinstance(casting_mode, int):
|
| 411 |
+
assert casting_mode in _str_to_casting_mode, f"Invalid casting mode: {casting_mode}"
|
| 412 |
+
casting_mode = _str_to_casting_mode[casting_mode]
|
| 413 |
+
else:
|
| 414 |
+
assert casting_mode in _str_to_casting_mode.values(), f"Invalid casting mode: {casting_mode}"
|
| 415 |
+
|
| 416 |
+
shape = X.shape
|
| 417 |
+
dim = shape[-1]
|
| 418 |
+
X = X.view(-1, dim)
|
| 419 |
+
n_rows, n_cols = X.shape
|
| 420 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 421 |
+
|
| 422 |
+
Y = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
|
| 423 |
+
# RSTD is to cache rstd for each row
|
| 424 |
+
# RSTD is always computed/stored in fp32 if we are using Llama or Gemma casting mode
|
| 425 |
+
rstd_dtype = torch.float32 if casting_mode in (_CASTING_MODE_LLAMA.value, _CASTING_MODE_GEMMA.value) else X.dtype
|
| 426 |
+
RSTD = torch.empty(n_rows, dtype=rstd_dtype, device=X.device)
|
| 427 |
+
|
| 428 |
+
if W is not None:
|
| 429 |
+
# Check constraints.
|
| 430 |
+
assert X.shape[1] == W.shape[0], (
|
| 431 |
+
"Incompatible hidden size dimension between tensor1.shape[1] and tensor2.shape[0]"
|
| 432 |
+
)
|
| 433 |
+
elementwise_affine = True
|
| 434 |
+
else:
|
| 435 |
+
elementwise_affine = False
|
| 436 |
+
|
| 437 |
+
# XPU-specific optimization
|
| 438 |
+
kernel_args = {}
|
| 439 |
+
if X.device.type == "xpu":
|
| 440 |
+
set_large_grf_mode(kernel_args)
|
| 441 |
+
if BLOCK_SIZE > 256 or n_rows < 4096 * 8 or row_mode:
|
| 442 |
+
_rms_norm_forward_kernel[(n_rows,)](
|
| 443 |
+
Y,
|
| 444 |
+
Y.stride(0),
|
| 445 |
+
X,
|
| 446 |
+
X.stride(0),
|
| 447 |
+
W,
|
| 448 |
+
W.stride(0) if elementwise_affine else 0,
|
| 449 |
+
RSTD,
|
| 450 |
+
RSTD.stride(0),
|
| 451 |
+
n_cols,
|
| 452 |
+
eps,
|
| 453 |
+
offset,
|
| 454 |
+
casting_mode,
|
| 455 |
+
elementwise_affine=elementwise_affine,
|
| 456 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 457 |
+
num_warps=num_warps,
|
| 458 |
+
**kernel_args, # XPU-specific optimization
|
| 459 |
+
)
|
| 460 |
+
else:
|
| 461 |
+
BLOCK_ROW = 16
|
| 462 |
+
kernel_args["BLOCK_ROW"] = BLOCK_ROW
|
| 463 |
+
_block_rms_norm_forward_kernel[(triton.cdiv(n_rows, BLOCK_ROW),)](
|
| 464 |
+
Y,
|
| 465 |
+
Y.stride(0),
|
| 466 |
+
X,
|
| 467 |
+
X.stride(0),
|
| 468 |
+
W,
|
| 469 |
+
W.stride(0) if elementwise_affine else 0,
|
| 470 |
+
RSTD,
|
| 471 |
+
RSTD.stride(0),
|
| 472 |
+
n_rows,
|
| 473 |
+
n_cols,
|
| 474 |
+
eps,
|
| 475 |
+
offset,
|
| 476 |
+
casting_mode,
|
| 477 |
+
elementwise_affine=elementwise_affine,
|
| 478 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 479 |
+
num_warps=num_warps,
|
| 480 |
+
**kernel_args, # XPU-specific optimization
|
| 481 |
+
)
|
| 482 |
+
return Y.view(*shape), X, RSTD, BLOCK_SIZE, num_warps, casting_mode
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
def rms_norm_backward(dY, X, W, RSTD, offset, casting_mode, BLOCK_SIZE, num_warps, in_place, row_mode):
|
| 486 |
+
shape = dY.shape
|
| 487 |
+
dim = shape[-1]
|
| 488 |
+
dY = dY.view(-1, dim)
|
| 489 |
+
n_rows, n_cols = dY.shape
|
| 490 |
+
|
| 491 |
+
sm_count = 1
|
| 492 |
+
if X.device.type == "cuda":
|
| 493 |
+
sm_count = torch.cuda.get_device_properties(X.device).multi_processor_count
|
| 494 |
+
elif X.device.type == "xpu":
|
| 495 |
+
sm_count = torch.xpu.get_device_properties(X.device).gpu_eu_count
|
| 496 |
+
elif X.device.type == "npu":
|
| 497 |
+
sm_count = get_npu_core_count()
|
| 498 |
+
|
| 499 |
+
if W is not None:
|
| 500 |
+
# fp32 for numerical stability especially.
|
| 501 |
+
_dW = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
|
| 502 |
+
elementwise_affine = True
|
| 503 |
+
else:
|
| 504 |
+
_dW = None
|
| 505 |
+
elementwise_affine = False
|
| 506 |
+
|
| 507 |
+
if n_cols > BLOCK_SIZE:
|
| 508 |
+
raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
|
| 509 |
+
rows_per_program = math.ceil(n_rows / sm_count)
|
| 510 |
+
grid = (sm_count,)
|
| 511 |
+
|
| 512 |
+
if in_place is True:
|
| 513 |
+
dX = dY
|
| 514 |
+
else:
|
| 515 |
+
dX = torch.zeros_like(dY)
|
| 516 |
+
|
| 517 |
+
# XPU-specific optimization
|
| 518 |
+
kernel_args = {}
|
| 519 |
+
if X.device.type == "xpu":
|
| 520 |
+
set_large_grf_mode(kernel_args)
|
| 521 |
+
|
| 522 |
+
if BLOCK_SIZE > 256 or n_rows < 4096 * 8 or row_mode:
|
| 523 |
+
_rms_norm_backward_kernel[grid](
|
| 524 |
+
dY,
|
| 525 |
+
dY.stride(0),
|
| 526 |
+
dX,
|
| 527 |
+
dX.stride(0),
|
| 528 |
+
X,
|
| 529 |
+
X.stride(0),
|
| 530 |
+
torch_to_triton_dtype[X.dtype],
|
| 531 |
+
W,
|
| 532 |
+
W.stride(0) if elementwise_affine else 0,
|
| 533 |
+
RSTD,
|
| 534 |
+
RSTD.stride(0),
|
| 535 |
+
_dW,
|
| 536 |
+
_dW.stride(0) if elementwise_affine else 0,
|
| 537 |
+
n_rows,
|
| 538 |
+
n_cols,
|
| 539 |
+
offset,
|
| 540 |
+
rows_per_program,
|
| 541 |
+
casting_mode,
|
| 542 |
+
elementwise_affine=elementwise_affine,
|
| 543 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 544 |
+
num_warps=num_warps,
|
| 545 |
+
**kernel_args, # XPU-specific optimization
|
| 546 |
+
)
|
| 547 |
+
else:
|
| 548 |
+
BLOCK_ROW = 16
|
| 549 |
+
kernel_args["BLOCK_ROW"] = BLOCK_ROW
|
| 550 |
+
_block_rms_norm_backward_kernel[grid](
|
| 551 |
+
dY,
|
| 552 |
+
dY.stride(0),
|
| 553 |
+
dX,
|
| 554 |
+
dX.stride(0),
|
| 555 |
+
X,
|
| 556 |
+
X.stride(0),
|
| 557 |
+
torch_to_triton_dtype[X.dtype],
|
| 558 |
+
W,
|
| 559 |
+
W.stride(0) if elementwise_affine else 0,
|
| 560 |
+
RSTD,
|
| 561 |
+
RSTD.stride(0),
|
| 562 |
+
_dW,
|
| 563 |
+
_dW.stride(0) if elementwise_affine else 0,
|
| 564 |
+
n_rows,
|
| 565 |
+
n_cols,
|
| 566 |
+
offset,
|
| 567 |
+
casting_mode,
|
| 568 |
+
elementwise_affine=elementwise_affine,
|
| 569 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 570 |
+
num_warps=num_warps,
|
| 571 |
+
**kernel_args, # XPU-specific optimization
|
| 572 |
+
)
|
| 573 |
+
dX = dX.view(*shape)
|
| 574 |
+
|
| 575 |
+
if elementwise_affine:
|
| 576 |
+
dW = _dW.sum(dim=0).to(W.dtype)
|
| 577 |
+
else:
|
| 578 |
+
dW = None
|
| 579 |
+
|
| 580 |
+
return dX, dW
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
class LigerRMSNormFunction(torch.autograd.Function):
|
| 584 |
+
"""
|
| 585 |
+
Performs RMSNorm (Root Mean Square Normalization), which normalizes the input tensor `X` using the
|
| 586 |
+
weight tensor `W`, with an optional offset and casting mode.
|
| 587 |
+
|
| 588 |
+
Some models use an 'offset' to shift the weight tensor `W` by a constant value. For example, Gemma
|
| 589 |
+
uses an offset of 1.0, so the computation becomes `(X / RMS(X)) * (W + 1.0)` instead of the usual
|
| 590 |
+
`(X / RMS(X)) * W`. You can pass the offset value as an argument to the forward function.
|
| 591 |
+
|
| 592 |
+
In addition, different models cast their inputs at different places during RMSNorm computation. For
|
| 593 |
+
example, Gemma casts everything to fp32 nefore starting the computation, while Llama casts only the
|
| 594 |
+
inverse RMS to fp32. You can specify the casting mode using the `casting_mode` argument. We currently
|
| 595 |
+
support the following casting modes (they match HuggingFace Transformers' implementations):
|
| 596 |
+
- 'llama': matches the Llama implementation, where only the inverse RMS is computed on fp32.
|
| 597 |
+
- 'gemma': matches the Gemma implementation, where everything is cast to fp32, then computed, then cast back to the original dtype.
|
| 598 |
+
- 'none': no casting is done. The computation is done in the original dtype. This saves memory and is slightly faster, but has more error w.r.t. the original implementation.
|
| 599 |
+
|
| 600 |
+
`in_place` option means whether to in_place modify dY to store dX. This is default to `True` to save memory. However, under certain cases, it can produce incorrect inputs.
|
| 601 |
+
For example, gemma2 uses two rmsnorm sequentially with residual in between. The resesidual part needs dY so it cannot be modified in-place.
|
| 602 |
+
Therefore, for the patching of RMSNorm in gemma2, we set `in_place` to `False`
|
| 603 |
+
"""
|
| 604 |
+
|
| 605 |
+
@staticmethod
|
| 606 |
+
@ensure_contiguous
|
| 607 |
+
def forward(ctx, X, W, eps, offset=0.0, casting_mode="llama", in_place=True, row_mode=None):
|
| 608 |
+
"""
|
| 609 |
+
X: (B, T, H) or (BxT, H)
|
| 610 |
+
W: (H,)
|
| 611 |
+
"""
|
| 612 |
+
if isinstance(X, torch.distributed.tensor.DTensor):
|
| 613 |
+
# Input tensor is output of a tensor parallel module and
|
| 614 |
+
# needs to be gathered to a local tensor to compute
|
| 615 |
+
# RMSE layer norm on each TP worker.
|
| 616 |
+
# TODO: support CP.
|
| 617 |
+
X = X.full_tensor()
|
| 618 |
+
|
| 619 |
+
Y, X, RSTD, BLOCK_SIZE, num_warps, casting_mode = rms_norm_forward(X, W, eps, offset, casting_mode, row_mode)
|
| 620 |
+
ctx.offset = offset
|
| 621 |
+
ctx.casting_mode = casting_mode
|
| 622 |
+
ctx.in_place = in_place
|
| 623 |
+
ctx.row_mode = row_mode
|
| 624 |
+
ctx.BLOCK_SIZE = BLOCK_SIZE
|
| 625 |
+
ctx.num_warps = num_warps
|
| 626 |
+
ctx.elementwise_affine = W is not None
|
| 627 |
+
if W is not None:
|
| 628 |
+
ctx.save_for_backward(X, W, RSTD)
|
| 629 |
+
else:
|
| 630 |
+
ctx.save_for_backward(X, RSTD)
|
| 631 |
+
return Y
|
| 632 |
+
|
| 633 |
+
@staticmethod
|
| 634 |
+
@ensure_contiguous
|
| 635 |
+
def backward(ctx, dY):
|
| 636 |
+
"""
|
| 637 |
+
Y: (B, T, H) or (BxT, H)
|
| 638 |
+
"""
|
| 639 |
+
if ctx.elementwise_affine:
|
| 640 |
+
X, W, RSTD = ctx.saved_tensors
|
| 641 |
+
else:
|
| 642 |
+
X, RSTD = ctx.saved_tensors
|
| 643 |
+
W = None
|
| 644 |
+
|
| 645 |
+
if isinstance(dY, torch.distributed.tensor.DTensor):
|
| 646 |
+
# Gradients are output of a tensor parallel module and
|
| 647 |
+
# needs to be gathered to a local tensor for computing RMSE layer.
|
| 648 |
+
# TODO: support CP.
|
| 649 |
+
dY = dY.full_tensor()
|
| 650 |
+
|
| 651 |
+
dX, dW = rms_norm_backward(
|
| 652 |
+
dY, X, W, RSTD, ctx.offset, ctx.casting_mode, ctx.BLOCK_SIZE, ctx.num_warps, ctx.in_place, ctx.row_mode
|
| 653 |
+
)
|
| 654 |
+
return dX, dW, None, None, None, None, None
|
build/torch-xpu/rope.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
@triton.jit
|
| 7 |
+
def _triton_rope(
|
| 8 |
+
q_ptr,
|
| 9 |
+
q_row_stride,
|
| 10 |
+
k_ptr,
|
| 11 |
+
k_row_stride,
|
| 12 |
+
cos,
|
| 13 |
+
cos_row_stride,
|
| 14 |
+
sin,
|
| 15 |
+
sin_row_stride,
|
| 16 |
+
sl,
|
| 17 |
+
bs: tl.constexpr,
|
| 18 |
+
cos_bs: tl.constexpr,
|
| 19 |
+
n_qh: tl.constexpr,
|
| 20 |
+
n_kh: tl.constexpr,
|
| 21 |
+
hd: tl.constexpr,
|
| 22 |
+
pad_n_qh: tl.constexpr,
|
| 23 |
+
pad_n_kh: tl.constexpr,
|
| 24 |
+
pad_hd: tl.constexpr,
|
| 25 |
+
BLOCK_SIZE: tl.constexpr,
|
| 26 |
+
BACKWARD_PASS: tl.constexpr = False,
|
| 27 |
+
):
|
| 28 |
+
# q size: (bsz, seq_len, num_q_heads, head_dim)
|
| 29 |
+
# q stride: (seq_len * num_q_heads * head_dim, num_q_heads * head_dim, head_dim, 1)
|
| 30 |
+
# k size: (bsz, seq_len, num_kv_heads, head_dim)
|
| 31 |
+
# k stride: (seq_len * num_kv_heads * head_dim, num_kv_heads * head_dim, head_dim, 1)
|
| 32 |
+
|
| 33 |
+
# cos size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
|
| 34 |
+
# stride: (seq_len * head_dim, head_dim, 1)
|
| 35 |
+
pid = tl.program_id(0).to(tl.int64)
|
| 36 |
+
|
| 37 |
+
# locate start address
|
| 38 |
+
q_ptr = q_ptr + pid * q_row_stride
|
| 39 |
+
k_ptr = k_ptr + pid * k_row_stride
|
| 40 |
+
|
| 41 |
+
# ####################################################################
|
| 42 |
+
# get the cos(mθ_{i...d/2}) and sin(mθ_{i...d/2}) for token position
|
| 43 |
+
# m of this program instance
|
| 44 |
+
# ####################################################################
|
| 45 |
+
|
| 46 |
+
# 1. program instances are laid out in a 1D vector of size bsz * seq_len, which
|
| 47 |
+
# effectively represents a 2D grid of size [bsz, seq_len] with seq_len dimension
|
| 48 |
+
# being the fastest changing dimension. Thus we can simply do pid // sl to get the batch index
|
| 49 |
+
# and pid % sl to get the sequence index.
|
| 50 |
+
# 2. We only need the left half of cos and sin matrix because the right half is just
|
| 51 |
+
# a clone of the left half.
|
| 52 |
+
batch_idx = pid // sl
|
| 53 |
+
cos_row_idx = pid % sl
|
| 54 |
+
cos = cos + tl.where(
|
| 55 |
+
cos_bs == 1,
|
| 56 |
+
cos_row_idx * cos_row_stride,
|
| 57 |
+
batch_idx * (sl * cos_row_stride) + cos_row_idx * cos_row_stride,
|
| 58 |
+
)
|
| 59 |
+
sin = sin + tl.where(
|
| 60 |
+
cos_bs == 1,
|
| 61 |
+
cos_row_idx * sin_row_stride,
|
| 62 |
+
batch_idx * (sl * sin_row_stride) + cos_row_idx * sin_row_stride,
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
cos_offsets = tl.arange(0, pad_hd // 2)
|
| 66 |
+
cos_mask = cos_offsets < hd // 2
|
| 67 |
+
cos_row = tl.load(cos + cos_offsets, mask=cos_mask, other=0)
|
| 68 |
+
sin_row = tl.load(sin + cos_offsets, mask=cos_mask, other=0)
|
| 69 |
+
|
| 70 |
+
# ####################################################################
|
| 71 |
+
# Load the left and right half of q and k for the current
|
| 72 |
+
# program instance (i.e. for the current token) separately
|
| 73 |
+
# ####################################################################
|
| 74 |
+
# left half of the head
|
| 75 |
+
first_half_q_offsets = tl.arange(0, pad_n_qh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
|
| 76 |
+
first_half_k_offsets = tl.arange(0, pad_n_kh)[:, None] * hd + tl.arange(0, pad_hd // 2)[None, :]
|
| 77 |
+
first_q_mask = (tl.arange(0, pad_n_qh)[:, None] < n_qh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
|
| 78 |
+
first_k_mask = (tl.arange(0, pad_n_kh)[:, None] < n_kh) & (tl.arange(0, pad_hd // 2)[None, :] < hd // 2)
|
| 79 |
+
q_tile_1 = tl.load(q_ptr + first_half_q_offsets, mask=first_q_mask, other=0).to(sin_row.dtype)
|
| 80 |
+
k_tile_1 = tl.load(k_ptr + first_half_k_offsets, mask=first_k_mask, other=0).to(sin_row.dtype)
|
| 81 |
+
|
| 82 |
+
# right half of the head
|
| 83 |
+
second_half_q_offsets = first_half_q_offsets + (hd // 2)
|
| 84 |
+
second_half_k_offsets = first_half_k_offsets + (hd // 2)
|
| 85 |
+
second_q_mask = first_q_mask
|
| 86 |
+
second_k_mask = first_k_mask
|
| 87 |
+
q_tile_2 = tl.load(q_ptr + second_half_q_offsets, mask=second_q_mask, other=0).to(sin_row.dtype)
|
| 88 |
+
k_tile_2 = tl.load(k_ptr + second_half_k_offsets, mask=second_k_mask, other=0).to(sin_row.dtype)
|
| 89 |
+
|
| 90 |
+
if not BACKWARD_PASS:
|
| 91 |
+
# y = [x1, x2] * [cos, cos] + [-x2, x1] * [sin, sin]
|
| 92 |
+
new_q_tile_1 = q_tile_1 * cos_row - q_tile_2 * sin_row
|
| 93 |
+
tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
|
| 94 |
+
new_q_tile_2 = q_tile_2 * cos_row + q_tile_1 * sin_row
|
| 95 |
+
tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
|
| 96 |
+
|
| 97 |
+
new_k_tile_1 = k_tile_1 * cos_row - k_tile_2 * sin_row
|
| 98 |
+
tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
|
| 99 |
+
new_k_tile_2 = k_tile_2 * cos_row + k_tile_1 * sin_row
|
| 100 |
+
tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
|
| 101 |
+
else:
|
| 102 |
+
# with some math, we can get:
|
| 103 |
+
# dy = [dx1, dx2] * [cos, cos] + [-dx2, dx1] * [-sin, -sin]
|
| 104 |
+
new_q_tile_1 = q_tile_1 * cos_row + q_tile_2 * sin_row
|
| 105 |
+
tl.store(q_ptr + first_half_q_offsets, new_q_tile_1, mask=first_q_mask)
|
| 106 |
+
new_q_tile_2 = q_tile_2 * cos_row - q_tile_1 * sin_row
|
| 107 |
+
tl.store(q_ptr + second_half_q_offsets, new_q_tile_2, mask=second_q_mask)
|
| 108 |
+
|
| 109 |
+
new_k_tile_1 = k_tile_1 * cos_row + k_tile_2 * sin_row
|
| 110 |
+
tl.store(k_ptr + first_half_k_offsets, new_k_tile_1, mask=first_k_mask)
|
| 111 |
+
new_k_tile_2 = k_tile_2 * cos_row - k_tile_1 * sin_row
|
| 112 |
+
tl.store(k_ptr + second_half_k_offsets, new_k_tile_2, mask=second_k_mask)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def rope_forward(q, k, cos, sin):
|
| 116 |
+
# transpose it back to the physical shape because Triton looks at the physical storage
|
| 117 |
+
# note: q and k are incontiguous before the transformation and will become contiguous after transpose
|
| 118 |
+
q = q.transpose(1, 2)
|
| 119 |
+
k = k.transpose(1, 2)
|
| 120 |
+
|
| 121 |
+
batch_size, seq_len, n_q_head, head_dim = q.shape
|
| 122 |
+
n_kv_head = k.shape[2]
|
| 123 |
+
pad_hd = triton.next_power_of_2(head_dim)
|
| 124 |
+
pad_n_q_head = triton.next_power_of_2(n_q_head)
|
| 125 |
+
pad_n_kv_head = triton.next_power_of_2(n_kv_head)
|
| 126 |
+
BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
|
| 127 |
+
|
| 128 |
+
n_row = batch_size * seq_len
|
| 129 |
+
|
| 130 |
+
# ensure tensors passed into the kernel are contiguous. It will be no-op if they are already contiguous
|
| 131 |
+
q = q.contiguous()
|
| 132 |
+
k = k.contiguous()
|
| 133 |
+
cos = cos.contiguous()
|
| 134 |
+
sin = sin.contiguous()
|
| 135 |
+
cos_batch_size = cos.shape[0]
|
| 136 |
+
|
| 137 |
+
_triton_rope[(n_row,)](
|
| 138 |
+
q,
|
| 139 |
+
q.stride(1),
|
| 140 |
+
k,
|
| 141 |
+
k.stride(1),
|
| 142 |
+
cos,
|
| 143 |
+
cos.stride(-2),
|
| 144 |
+
sin,
|
| 145 |
+
sin.stride(-2),
|
| 146 |
+
seq_len,
|
| 147 |
+
batch_size,
|
| 148 |
+
cos_batch_size,
|
| 149 |
+
n_q_head,
|
| 150 |
+
n_kv_head,
|
| 151 |
+
head_dim,
|
| 152 |
+
pad_n_q_head,
|
| 153 |
+
pad_n_kv_head,
|
| 154 |
+
pad_hd,
|
| 155 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 156 |
+
BACKWARD_PASS=False,
|
| 157 |
+
)
|
| 158 |
+
return q.transpose(1, 2), k.transpose(1, 2), cos, sin
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def rope_backward(dq, dk, cos, sin):
|
| 162 |
+
dq = dq.transpose(1, 2)
|
| 163 |
+
dk = dk.transpose(1, 2)
|
| 164 |
+
|
| 165 |
+
batch_size, seq_len, n_q_head, head_dim = dq.shape
|
| 166 |
+
cos_batch_size = cos.shape[0]
|
| 167 |
+
n_kv_head = dk.shape[2]
|
| 168 |
+
pad_hd = triton.next_power_of_2(head_dim)
|
| 169 |
+
pad_n_q_head = triton.next_power_of_2(n_q_head)
|
| 170 |
+
pad_n_kv_head = triton.next_power_of_2(n_kv_head)
|
| 171 |
+
BLOCK_SIZE = max(pad_n_q_head, pad_n_kv_head)
|
| 172 |
+
|
| 173 |
+
n_row = batch_size * seq_len
|
| 174 |
+
|
| 175 |
+
# ensure dq and dk are contiguous
|
| 176 |
+
dq = dq.contiguous()
|
| 177 |
+
dk = dk.contiguous()
|
| 178 |
+
|
| 179 |
+
# backward is similar to forward except swapping few ops
|
| 180 |
+
_triton_rope[(n_row,)](
|
| 181 |
+
dq,
|
| 182 |
+
dq.stride(1),
|
| 183 |
+
dk,
|
| 184 |
+
dk.stride(1),
|
| 185 |
+
cos,
|
| 186 |
+
cos.stride(-2),
|
| 187 |
+
sin,
|
| 188 |
+
sin.stride(-2),
|
| 189 |
+
seq_len,
|
| 190 |
+
batch_size,
|
| 191 |
+
cos_batch_size,
|
| 192 |
+
n_q_head,
|
| 193 |
+
n_kv_head,
|
| 194 |
+
head_dim,
|
| 195 |
+
pad_n_q_head,
|
| 196 |
+
pad_n_kv_head,
|
| 197 |
+
pad_hd,
|
| 198 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 199 |
+
BACKWARD_PASS=True,
|
| 200 |
+
)
|
| 201 |
+
return dq.transpose(1, 2), dk.transpose(1, 2)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
class LigerRopeFunction(torch.autograd.Function):
|
| 205 |
+
"""
|
| 206 |
+
Triton implementation of the Rotary Positional Embedding (RoPE) operation. Please note that
|
| 207 |
+
this implements the HuggingFace Llama & Mistral version, whose rotation matrix is slightly different
|
| 208 |
+
than the original RoPE paper.
|
| 209 |
+
|
| 210 |
+
Please find the corresponding HuggingFace implementation here:
|
| 211 |
+
https://github.com/huggingface/transformers/blob/v4.40.2/src/transformers/models/llama/modeling_llama.py#L184
|
| 212 |
+
|
| 213 |
+
For more details about the rotation matrix used here, please refer to:
|
| 214 |
+
https://discuss.huggingface.co/t/is-llama-rotary-embedding-implementation-correct/44509/2
|
| 215 |
+
"""
|
| 216 |
+
|
| 217 |
+
@staticmethod
|
| 218 |
+
def forward(ctx, q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
|
| 219 |
+
"""
|
| 220 |
+
q size: (bsz, n_q_head, seq_len, head_dim)
|
| 221 |
+
k size: (bsz, n_kv_head, seq_len, head_dim)
|
| 222 |
+
cos size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
|
| 223 |
+
sin size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
|
| 224 |
+
"""
|
| 225 |
+
q, k, cos, sin = rope_forward(q, k, cos, sin)
|
| 226 |
+
ctx.save_for_backward(cos, sin)
|
| 227 |
+
return q, k
|
| 228 |
+
|
| 229 |
+
def backward(ctx, dq, dk):
|
| 230 |
+
"""
|
| 231 |
+
dq size: (bsz, n_q_head, seq_len, head_dim)
|
| 232 |
+
dk size: (bsz, n_kv_head, seq_len, head_dim)
|
| 233 |
+
cos size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
|
| 234 |
+
sin size: (1, seq_len, head_dim) or (bsz, seq_len, head_dim)
|
| 235 |
+
"""
|
| 236 |
+
|
| 237 |
+
cos, sin = ctx.saved_tensors
|
| 238 |
+
dq, dk = rope_backward(dq, dk, cos, sin)
|
| 239 |
+
return dq, dk, None, None, None, None
|
build/torch-xpu/swiglu.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import triton
|
| 3 |
+
import triton.language as tl
|
| 4 |
+
|
| 5 |
+
from .utils import calculate_settings
|
| 6 |
+
from .utils import ensure_contiguous
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@triton.jit
|
| 10 |
+
def silu(x):
|
| 11 |
+
return x * tl.sigmoid(x)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@triton.jit
|
| 15 |
+
def _swiglu_forward_kernel(
|
| 16 |
+
a_ptr, b_ptr, c_ptr, stride, gate_multiplier, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr
|
| 17 |
+
):
|
| 18 |
+
program_id = tl.program_id(0).to(tl.int64)
|
| 19 |
+
|
| 20 |
+
# locate start index
|
| 21 |
+
a_ptr += program_id * stride
|
| 22 |
+
b_ptr += program_id * stride
|
| 23 |
+
c_ptr += program_id * stride
|
| 24 |
+
|
| 25 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 26 |
+
mask = col_offsets < n_cols
|
| 27 |
+
|
| 28 |
+
# sigmoid requires type float32
|
| 29 |
+
a_row = tl.load(a_ptr + col_offsets, mask=mask, other=0).to(tl.float32) * gate_multiplier
|
| 30 |
+
b_row = tl.load(b_ptr + col_offsets, mask=mask, other=0)
|
| 31 |
+
c_row = silu(a_row).cast(b_row.dtype) * b_row
|
| 32 |
+
tl.store(c_ptr + col_offsets, c_row, mask=mask)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@triton.jit
|
| 36 |
+
def _swiglu_backward_kernel(
|
| 37 |
+
dc_ptr, a_ptr, b_ptr, stride, gate_multiplier, n_cols: tl.constexpr, BLOCK_SIZE: tl.constexpr
|
| 38 |
+
):
|
| 39 |
+
program_id = tl.program_id(0).to(tl.int64)
|
| 40 |
+
|
| 41 |
+
# locate start index
|
| 42 |
+
dc_ptr += program_id * stride
|
| 43 |
+
a_ptr += program_id * stride
|
| 44 |
+
b_ptr += program_id * stride
|
| 45 |
+
|
| 46 |
+
col_offsets = tl.arange(0, BLOCK_SIZE)
|
| 47 |
+
mask = col_offsets < n_cols
|
| 48 |
+
|
| 49 |
+
dc_row = tl.load(dc_ptr + col_offsets, mask=mask, other=0)
|
| 50 |
+
# sigmoid requires type float32
|
| 51 |
+
a_row = tl.load(a_ptr + col_offsets, mask=mask, other=0).to(tl.float32) * gate_multiplier
|
| 52 |
+
b_row = tl.load(b_ptr + col_offsets, mask=mask, other=0)
|
| 53 |
+
|
| 54 |
+
# recomputation to save memory. a_row already holds a * gate_multiplier.
|
| 55 |
+
sig_a = tl.sigmoid(a_row)
|
| 56 |
+
silu_a = a_row * sig_a
|
| 57 |
+
db_row = dc_row * silu_a
|
| 58 |
+
# chain rule pulls an extra factor of gate_multiplier through the pre-activation scaling
|
| 59 |
+
da_row = dc_row * (silu_a * (1 - sig_a) + sig_a) * b_row * gate_multiplier
|
| 60 |
+
|
| 61 |
+
tl.store(a_ptr + col_offsets, da_row, mask=mask)
|
| 62 |
+
tl.store(b_ptr + col_offsets, db_row, mask=mask)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def swiglu_forward(a, b, gate_multiplier: float = 1.0):
|
| 66 |
+
ori_shape = a.shape
|
| 67 |
+
|
| 68 |
+
n_cols = ori_shape[-1]
|
| 69 |
+
a = a.view(-1, n_cols)
|
| 70 |
+
b = b.view(-1, n_cols)
|
| 71 |
+
c = torch.empty_like(a)
|
| 72 |
+
n_rows = a.shape[0]
|
| 73 |
+
|
| 74 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 75 |
+
|
| 76 |
+
_swiglu_forward_kernel[(n_rows,)](
|
| 77 |
+
a,
|
| 78 |
+
b,
|
| 79 |
+
c,
|
| 80 |
+
c.stride(-2),
|
| 81 |
+
float(gate_multiplier),
|
| 82 |
+
n_cols=n_cols,
|
| 83 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 84 |
+
num_warps=num_warps,
|
| 85 |
+
)
|
| 86 |
+
return a, b, c.view(*ori_shape)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def swiglu_backward(a, b, dc, gate_multiplier: float = 1.0):
|
| 90 |
+
ori_shape = dc.shape
|
| 91 |
+
n_cols = ori_shape[-1]
|
| 92 |
+
dc = dc.view(-1, n_cols)
|
| 93 |
+
n_rows = dc.shape[0]
|
| 94 |
+
|
| 95 |
+
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
|
| 96 |
+
|
| 97 |
+
_swiglu_backward_kernel[(n_rows,)](
|
| 98 |
+
dc,
|
| 99 |
+
a,
|
| 100 |
+
b,
|
| 101 |
+
dc.stride(-2),
|
| 102 |
+
float(gate_multiplier),
|
| 103 |
+
n_cols=n_cols,
|
| 104 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 105 |
+
num_warps=num_warps,
|
| 106 |
+
)
|
| 107 |
+
return a.view(*ori_shape), b.view(*ori_shape)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class LigerSiLUMulFunction(torch.autograd.Function):
|
| 111 |
+
@staticmethod
|
| 112 |
+
@ensure_contiguous
|
| 113 |
+
def forward(ctx, a, b, gate_multiplier: float = 1.0, down_multiplier: float = 1.0):
|
| 114 |
+
gate_multiplier = float(gate_multiplier)
|
| 115 |
+
down_multiplier = float(down_multiplier)
|
| 116 |
+
ctx.gate_multiplier = gate_multiplier
|
| 117 |
+
ctx.down_multiplier = down_multiplier
|
| 118 |
+
|
| 119 |
+
if isinstance(a, torch.distributed.tensor.DTensor) or isinstance(b, torch.distributed.tensor.DTensor):
|
| 120 |
+
device_mesh, placements = (
|
| 121 |
+
(a.device_mesh, a.placements)
|
| 122 |
+
if isinstance(a, torch.distributed.tensor.DTensor)
|
| 123 |
+
else (b.device_mesh, b.placements)
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
# Assume that full tensors are gathered before and identical across
|
| 127 |
+
# the associated process groups.
|
| 128 |
+
if not isinstance(a, torch.distributed.tensor.DTensor):
|
| 129 |
+
a = torch.distributed.tensor.distribute_tensor(a, device_mesh=device_mesh, placements=placements)
|
| 130 |
+
if not isinstance(b, torch.distributed.tensor.DTensor):
|
| 131 |
+
b = torch.distributed.tensor.distribute_tensor(b, device_mesh=device_mesh, placements=placements)
|
| 132 |
+
a_local, b_local, c_local = swiglu_forward(a.to_local(), b.to_local(), gate_multiplier)
|
| 133 |
+
if down_multiplier != 1.0:
|
| 134 |
+
c_local = c_local * down_multiplier
|
| 135 |
+
ctx.save_for_backward(a_local, b_local)
|
| 136 |
+
ctx.dtensor_metadata = (device_mesh, placements)
|
| 137 |
+
return torch.distributed.tensor.DTensor.from_local(c_local, device_mesh, placements)
|
| 138 |
+
else:
|
| 139 |
+
a, b, c = swiglu_forward(a, b, gate_multiplier)
|
| 140 |
+
if down_multiplier != 1.0:
|
| 141 |
+
c = c * down_multiplier
|
| 142 |
+
ctx.save_for_backward(a, b)
|
| 143 |
+
ctx.dtensor_metadata = None
|
| 144 |
+
return c
|
| 145 |
+
|
| 146 |
+
@staticmethod
|
| 147 |
+
@ensure_contiguous
|
| 148 |
+
def backward(ctx, dc):
|
| 149 |
+
a, b = ctx.saved_tensors
|
| 150 |
+
gate_multiplier = ctx.gate_multiplier
|
| 151 |
+
down_multiplier = ctx.down_multiplier
|
| 152 |
+
|
| 153 |
+
if ctx.dtensor_metadata is not None:
|
| 154 |
+
device_mesh, placements = ctx.dtensor_metadata
|
| 155 |
+
|
| 156 |
+
# Assume that full tensors are gathered before and identical across
|
| 157 |
+
# the associated process groups.
|
| 158 |
+
dc_local = (
|
| 159 |
+
dc.to_local()
|
| 160 |
+
if isinstance(dc, torch.distributed.tensor.DTensor)
|
| 161 |
+
else torch.distributed.tensor.distribute_tensor(dc, device_mesh=device_mesh, placements=placements)
|
| 162 |
+
)
|
| 163 |
+
if down_multiplier != 1.0:
|
| 164 |
+
dc_local = dc_local * down_multiplier
|
| 165 |
+
a_local, b_local = swiglu_backward(a, b, dc_local, gate_multiplier)
|
| 166 |
+
return (
|
| 167 |
+
torch.distributed.tensor.DTensor.from_local(a_local, device_mesh, placements),
|
| 168 |
+
torch.distributed.tensor.DTensor.from_local(b_local, device_mesh, placements),
|
| 169 |
+
None,
|
| 170 |
+
None,
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
if down_multiplier != 1.0:
|
| 174 |
+
dc = dc * down_multiplier
|
| 175 |
+
a, b = swiglu_backward(a, b, dc, gate_multiplier)
|
| 176 |
+
return a, b, None, None
|
build/torch-xpu/tiled_mlp.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
|
| 3 |
+
from typing import Callable
|
| 4 |
+
from typing import List
|
| 5 |
+
from typing import Optional
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
from .utils import ensure_contiguous
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class LigerTiledMLPFunction(torch.autograd.Function):
|
| 13 |
+
"""
|
| 14 |
+
Based on DeepSpeed's TiledMLP:
|
| 15 |
+
https://github.com/deepspeedai/DeepSpeed/blob/v0.18.2/deepspeed/runtime/sequence_parallel/ulysses_sp.py#L838
|
| 16 |
+
|
| 17 |
+
Perform a tiled MLP computation to massively reduce memory usage needed to compute MLP
|
| 18 |
+
when using very long sequence lengths.
|
| 19 |
+
|
| 20 |
+
This module re-computes `forward` in the `backward`. So the `forward` occurs twice each iteration.
|
| 21 |
+
And if you're using activation checkpointing it then occurs thrice.
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
fn: the function to call on sharded inputs (e.g., mlp.forward)
|
| 25 |
+
mlp_module: the MLP nn.Module object
|
| 26 |
+
x: the input to MLP.forward (hidden_states)
|
| 27 |
+
shards: how many shards to use
|
| 28 |
+
compute_params: a list of weights engaged in the compute
|
| 29 |
+
|
| 30 |
+
Returns:
|
| 31 |
+
the computed hidden_states
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
@staticmethod
|
| 35 |
+
@ensure_contiguous
|
| 36 |
+
def forward(
|
| 37 |
+
ctx,
|
| 38 |
+
fn: Callable,
|
| 39 |
+
mlp_module: torch.nn.Module,
|
| 40 |
+
x: torch.Tensor,
|
| 41 |
+
shards: int,
|
| 42 |
+
compute_params: Optional[List[torch.nn.Parameter]] = None,
|
| 43 |
+
) -> torch.Tensor:
|
| 44 |
+
ctx.fn = fn
|
| 45 |
+
ctx.mlp_module = mlp_module
|
| 46 |
+
ctx.shards = shards
|
| 47 |
+
ctx.save_for_backward(x)
|
| 48 |
+
|
| 49 |
+
# x.shape could be [bs, seqlen, hidden_size] or [seqlen, hidden_size] (moe experts)
|
| 50 |
+
x_shards = list(torch.chunk(x, chunks=shards, dim=-2))
|
| 51 |
+
with torch.no_grad():
|
| 52 |
+
output_shards = [fn(mlp_module, x_shard) for x_shard in x_shards]
|
| 53 |
+
output_unsharded = torch.cat(output_shards, dim=-2)
|
| 54 |
+
|
| 55 |
+
return output_unsharded
|
| 56 |
+
|
| 57 |
+
@staticmethod
|
| 58 |
+
@ensure_contiguous
|
| 59 |
+
def backward(ctx, *grads) -> tuple:
|
| 60 |
+
fn = ctx.fn
|
| 61 |
+
(x,) = ctx.saved_tensors
|
| 62 |
+
mlp_module = ctx.mlp_module
|
| 63 |
+
shards = ctx.shards
|
| 64 |
+
|
| 65 |
+
grad_output = grads[0]
|
| 66 |
+
|
| 67 |
+
x_requires_grad = x.requires_grad
|
| 68 |
+
x = x.detach()
|
| 69 |
+
# detach() unsets x.requires_grad, so restore it
|
| 70 |
+
x.requires_grad_(x_requires_grad)
|
| 71 |
+
|
| 72 |
+
# x.shape could be [bs, seqlen, in_features] or [seqlen, in_features] (moe experts)
|
| 73 |
+
in_features = x.shape[-1]
|
| 74 |
+
out_features = grad_output.shape[-1]
|
| 75 |
+
x_shape_orig = x.shape
|
| 76 |
+
|
| 77 |
+
# flatten bs+seqlen to avoid having stride issues when narrowing into seqlen w/ bs>1
|
| 78 |
+
# NOTE: input and output feature dimensions may differ, e.g.
|
| 79 |
+
# Linear(in_features, out_features), so flatten x and grad_output with their own last dims.
|
| 80 |
+
x = x.view(-1, in_features)
|
| 81 |
+
incoming_grad = grad_output.view(-1, out_features)
|
| 82 |
+
x_grad = torch.zeros_like(x)
|
| 83 |
+
|
| 84 |
+
x_shards = list(torch.chunk(x, chunks=shards, dim=0))
|
| 85 |
+
incoming_grad_shards = list(torch.chunk(incoming_grad, chunks=shards, dim=0))
|
| 86 |
+
|
| 87 |
+
for i, x_shard in enumerate(x_shards):
|
| 88 |
+
x_shard.requires_grad_(x_requires_grad)
|
| 89 |
+
|
| 90 |
+
# if seqlen is not exactly divisible by shards the last step will be shorter than shard_step
|
| 91 |
+
shard_step = x_shards[i].shape[0]
|
| 92 |
+
shard_offset = i * x_shards[0].shape[0]
|
| 93 |
+
|
| 94 |
+
x_shard.grad = x_grad.narrow(0, shard_offset, shard_step).view_as(x_shard)
|
| 95 |
+
incoming_grad_shard = incoming_grad_shards[i]
|
| 96 |
+
|
| 97 |
+
with torch.enable_grad():
|
| 98 |
+
output = fn(mlp_module, x_shard)
|
| 99 |
+
torch.autograd.backward(output, incoming_grad_shard)
|
| 100 |
+
|
| 101 |
+
# unflatten
|
| 102 |
+
x_grad = x_grad.view(x_shape_orig)
|
| 103 |
+
|
| 104 |
+
return (None, None, x_grad, None, None)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def apply_tiled_mlp(
|
| 108 |
+
fn: Callable,
|
| 109 |
+
mlp_module: torch.nn.Module,
|
| 110 |
+
x: torch.Tensor,
|
| 111 |
+
num_shards: Optional[int] = None,
|
| 112 |
+
compute_params: Optional[List[torch.nn.Parameter]] = None,
|
| 113 |
+
) -> torch.Tensor:
|
| 114 |
+
"""
|
| 115 |
+
Apply tiled MLP computation for memory efficiency.
|
| 116 |
+
|
| 117 |
+
Args:
|
| 118 |
+
fn: the function to call on sharded inputs (e.g., lambda module, x: module(x))
|
| 119 |
+
mlp_module: the MLP nn.Module object
|
| 120 |
+
x: the input tensor with shape [bs, seqlen, hidden_size] or [seqlen, hidden_size]
|
| 121 |
+
num_shards: number of shards to use. If None, automatically calculated as ceil(seqlen / hidden_size)
|
| 122 |
+
compute_params: list of parameters for DeepSpeed ZeRO optimization
|
| 123 |
+
"""
|
| 124 |
+
if num_shards is None:
|
| 125 |
+
# x.shape could be [bs, seqlen, hidden_size] or [seqlen, hidden_size]
|
| 126 |
+
hidden_size = x.shape[-1]
|
| 127 |
+
seqlen = x.shape[-2]
|
| 128 |
+
num_shards = math.ceil(seqlen / hidden_size)
|
| 129 |
+
|
| 130 |
+
# Ensure num_shards is at least 1
|
| 131 |
+
num_shards = max(1, num_shards)
|
| 132 |
+
|
| 133 |
+
return LigerTiledMLPFunction.apply(
|
| 134 |
+
fn,
|
| 135 |
+
mlp_module,
|
| 136 |
+
x,
|
| 137 |
+
num_shards,
|
| 138 |
+
compute_params,
|
| 139 |
+
)
|
build/torch-xpu/tvd.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Literal
|
| 2 |
+
from typing import Optional
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import triton
|
| 6 |
+
import triton.language as tl
|
| 7 |
+
|
| 8 |
+
from .utils import ensure_contiguous
|
| 9 |
+
|
| 10 |
+
MAX_FUSED_SIZE = 65536 // 4
|
| 11 |
+
|
| 12 |
+
REDUCTION_LITERAL = Literal["none", "sum", "mean", "batchmean"]
|
| 13 |
+
|
| 14 |
+
_REDUCTION_MODE_NONE = tl.constexpr(0)
|
| 15 |
+
_REDUCTION_MODE_SUM = tl.constexpr(1)
|
| 16 |
+
_REDUCTION_MODE_MEAN = tl.constexpr(2)
|
| 17 |
+
_REDUCTION_MODE_BATCHMEAN = tl.constexpr(3)
|
| 18 |
+
|
| 19 |
+
_str_to_reduction_mode = {
|
| 20 |
+
"none": _REDUCTION_MODE_NONE.value,
|
| 21 |
+
"sum": _REDUCTION_MODE_SUM.value,
|
| 22 |
+
"mean": _REDUCTION_MODE_MEAN.value,
|
| 23 |
+
"batchmean": _REDUCTION_MODE_BATCHMEAN.value,
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def get_num_warps(BLOCK_SIZE):
|
| 28 |
+
num_warps = 4
|
| 29 |
+
if BLOCK_SIZE >= 32768:
|
| 30 |
+
num_warps = 32
|
| 31 |
+
elif BLOCK_SIZE >= 8192:
|
| 32 |
+
num_warps = 16
|
| 33 |
+
elif BLOCK_SIZE >= 2048:
|
| 34 |
+
num_warps = 8
|
| 35 |
+
|
| 36 |
+
return num_warps
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@triton.jit
|
| 40 |
+
def _tv_distance_kernel(
|
| 41 |
+
p_ptr,
|
| 42 |
+
p_stride,
|
| 43 |
+
q_ptr,
|
| 44 |
+
q_stride,
|
| 45 |
+
loss_ptr,
|
| 46 |
+
loss_stride,
|
| 47 |
+
grads_ptr,
|
| 48 |
+
grads_stride,
|
| 49 |
+
label_ptr,
|
| 50 |
+
ignore_index: tl.constexpr,
|
| 51 |
+
n_cols,
|
| 52 |
+
scale, # pre-computed reduction scale for gradients (fused into kernel)
|
| 53 |
+
BLOCK_SIZE: tl.constexpr,
|
| 54 |
+
HAS_LABEL: tl.constexpr,
|
| 55 |
+
reduction: tl.constexpr = _REDUCTION_MODE_BATCHMEAN,
|
| 56 |
+
):
|
| 57 |
+
pid = tl.program_id(0).to(tl.int64)
|
| 58 |
+
p_ptr += pid * p_stride
|
| 59 |
+
q_ptr += pid * q_stride
|
| 60 |
+
loss_ptr += pid * loss_stride
|
| 61 |
+
grads_ptr += pid * grads_stride
|
| 62 |
+
label_ptr += pid
|
| 63 |
+
|
| 64 |
+
base_offsets = tl.arange(0, BLOCK_SIZE)
|
| 65 |
+
|
| 66 |
+
if HAS_LABEL:
|
| 67 |
+
label = tl.load(label_ptr)
|
| 68 |
+
if label == ignore_index:
|
| 69 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 70 |
+
offsets = i + base_offsets
|
| 71 |
+
mask = offsets < n_cols
|
| 72 |
+
tl.store(grads_ptr + offsets, 0.0, mask=mask)
|
| 73 |
+
if reduction == _REDUCTION_MODE_NONE:
|
| 74 |
+
tl.store(loss_ptr + offsets, 0.0, mask=mask)
|
| 75 |
+
return
|
| 76 |
+
|
| 77 |
+
loss_sum = 0.0
|
| 78 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 79 |
+
offsets = i + base_offsets
|
| 80 |
+
mask = offsets < n_cols
|
| 81 |
+
|
| 82 |
+
p = tl.load(p_ptr + offsets, mask=mask, other=0.0)
|
| 83 |
+
q = tl.load(q_ptr + offsets, mask=mask, other=0.0)
|
| 84 |
+
|
| 85 |
+
# TVD(P || Q) = 0.5 * |P - Q|
|
| 86 |
+
tv_loss = 0.5 * tl.abs(p - q)
|
| 87 |
+
|
| 88 |
+
# Fuse reduction scaling into gradient computation (eliminates separate Python division)
|
| 89 |
+
grad_res = tl.where(p > q, 0.5 * scale, -0.5 * scale)
|
| 90 |
+
|
| 91 |
+
tl.store(grads_ptr + offsets, grad_res, mask=mask)
|
| 92 |
+
|
| 93 |
+
if reduction == _REDUCTION_MODE_NONE:
|
| 94 |
+
tl.store(loss_ptr + offsets, tv_loss, mask=mask)
|
| 95 |
+
else:
|
| 96 |
+
loss_sum += tl.sum(tv_loss, axis=0)
|
| 97 |
+
|
| 98 |
+
if reduction != _REDUCTION_MODE_NONE:
|
| 99 |
+
# Fuse reduction scaling into loss (same scale as gradients; avoids Python division)
|
| 100 |
+
tl.store(loss_ptr, loss_sum * scale)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def tv_distance_forward_triton(p, q, shift_labels, reduction, ignore_index, has_label):
|
| 104 |
+
BT, V = p.shape
|
| 105 |
+
|
| 106 |
+
BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(V))
|
| 107 |
+
num_warps = get_num_warps(BLOCK_SIZE)
|
| 108 |
+
|
| 109 |
+
grid = (BT,)
|
| 110 |
+
|
| 111 |
+
reduction = _str_to_reduction_mode[reduction]
|
| 112 |
+
|
| 113 |
+
out_size = (BT, V) if reduction == _REDUCTION_MODE_NONE.value else (BT,)
|
| 114 |
+
output_tensor = torch.zeros(out_size, device=p.device, dtype=torch.float32)
|
| 115 |
+
grads = torch.empty_like(p)
|
| 116 |
+
|
| 117 |
+
n_non_ignore = (shift_labels != ignore_index).sum().item() if has_label else BT
|
| 118 |
+
|
| 119 |
+
# Pre-compute gradient scale factor (fused into kernel to avoid separate division)
|
| 120 |
+
if reduction == _REDUCTION_MODE_BATCHMEAN.value:
|
| 121 |
+
scale = 1.0 / n_non_ignore
|
| 122 |
+
elif reduction == _REDUCTION_MODE_MEAN.value:
|
| 123 |
+
scale = 1.0 / (n_non_ignore * V)
|
| 124 |
+
else:
|
| 125 |
+
scale = 1.0
|
| 126 |
+
|
| 127 |
+
_tv_distance_kernel[grid](
|
| 128 |
+
p,
|
| 129 |
+
p.stride(0),
|
| 130 |
+
q,
|
| 131 |
+
q.stride(0),
|
| 132 |
+
output_tensor,
|
| 133 |
+
output_tensor.stride(0),
|
| 134 |
+
grads,
|
| 135 |
+
grads.stride(0),
|
| 136 |
+
shift_labels if has_label else torch.empty(1, device=p.device),
|
| 137 |
+
ignore_index,
|
| 138 |
+
V,
|
| 139 |
+
scale,
|
| 140 |
+
BLOCK_SIZE=BLOCK_SIZE,
|
| 141 |
+
HAS_LABEL=has_label,
|
| 142 |
+
num_warps=num_warps,
|
| 143 |
+
reduction=reduction,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
# Loss and gradients are already scaled inside the kernel — no separate division needed
|
| 147 |
+
if reduction in (_REDUCTION_MODE_BATCHMEAN.value, _REDUCTION_MODE_MEAN.value):
|
| 148 |
+
return output_tensor.sum(), grads
|
| 149 |
+
elif reduction == _REDUCTION_MODE_SUM.value:
|
| 150 |
+
return output_tensor.sum(dim=0), grads
|
| 151 |
+
else:
|
| 152 |
+
return output_tensor, grads
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def tvd_backward_triton(grad_output, grads):
|
| 156 |
+
# If cross entropy is the last layer, grad_output is 1.0. Skip the mul then.
|
| 157 |
+
if torch.equal(grad_output, torch.tensor(1.0, device=grad_output.device)):
|
| 158 |
+
return grads
|
| 159 |
+
|
| 160 |
+
return grads * grad_output
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class LigerTVDLossFunction(torch.autograd.Function):
|
| 164 |
+
"""
|
| 165 |
+
Class implementing the forward and backward pass for the Total Variation Distance Loss using Triton.
|
| 166 |
+
"""
|
| 167 |
+
|
| 168 |
+
@staticmethod
|
| 169 |
+
@ensure_contiguous
|
| 170 |
+
def forward(
|
| 171 |
+
ctx,
|
| 172 |
+
p: torch.Tensor,
|
| 173 |
+
q: torch.Tensor,
|
| 174 |
+
shift_labels: Optional[torch.Tensor] = None,
|
| 175 |
+
reduction: REDUCTION_LITERAL = "batchmean",
|
| 176 |
+
ignore_index: int = -100,
|
| 177 |
+
) -> torch.Tensor:
|
| 178 |
+
"""A forward pass for the Total Variation Distance Loss.
|
| 179 |
+
|
| 180 |
+
Args:
|
| 181 |
+
ctx: Torch autograd context
|
| 182 |
+
p (torch.Tensor): A tensor of shape (BT, V) containing the first distribution.
|
| 183 |
+
q (torch.Tensor): A tensor of shape (BT, V) containing the second distribution.
|
| 184 |
+
shift_labels (Optional[torch.Tensor]): A tensor of shape (BT,) containing the labels.
|
| 185 |
+
reduction (REDUCTION_LITERAL, optional): The reduction method to be applied. Defaults to "batchmean".
|
| 186 |
+
ignore_index (int, optional): The index to ignore during loss calculation. Defaults to -100.
|
| 187 |
+
|
| 188 |
+
Returns:
|
| 189 |
+
torch.Tensor: The computed Total Variation Distance Loss.
|
| 190 |
+
"""
|
| 191 |
+
has_label = False
|
| 192 |
+
if shift_labels is not None:
|
| 193 |
+
assert shift_labels.shape == (p.shape[0],), (
|
| 194 |
+
f"the shape of shift_labels must be (BT,). Got: {shift_labels.shape}"
|
| 195 |
+
)
|
| 196 |
+
shift_labels = shift_labels.contiguous()
|
| 197 |
+
has_label = True
|
| 198 |
+
|
| 199 |
+
loss, grads = tv_distance_forward_triton(p, q, shift_labels, reduction, ignore_index, has_label)
|
| 200 |
+
ctx.save_for_backward(grads)
|
| 201 |
+
return loss
|
| 202 |
+
|
| 203 |
+
@staticmethod
|
| 204 |
+
@ensure_contiguous
|
| 205 |
+
def backward(ctx, grad_output: torch.Tensor) -> torch.Tensor:
|
| 206 |
+
"""A backward pass for the Total Variation Distance Loss.
|
| 207 |
+
|
| 208 |
+
Args:
|
| 209 |
+
ctx: Torch autograd context
|
| 210 |
+
grad_output (torch.Tensor): The gradient of the loss with respect to the output.
|
| 211 |
+
|
| 212 |
+
Returns:
|
| 213 |
+
tuple[torch.Tensor, None, None, None, None]: The gradient of the loss with respect to the inputs.
|
| 214 |
+
"""
|
| 215 |
+
(grads,) = ctx.saved_tensors
|
| 216 |
+
grads = tvd_backward_triton(grad_output, grads)
|
| 217 |
+
|
| 218 |
+
return grads, None, None, None, None
|
build/torch-xpu/utils.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This file incorporates code from Unsloth licensed under the Apache License, Version 2.0.
|
| 3 |
+
See the original Unsloth repository at https://github.com/unslothai/unsloth.
|
| 4 |
+
|
| 5 |
+
The following line
|
| 6 |
+
https://github.com/linkedin/Liger-Kernel/blob/7382a8761f9af679482b968f9348013d933947c7/src/liger_kernel/ops/utils.py#L23
|
| 7 |
+
is based on code from Unsloth, located at:
|
| 8 |
+
https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/utils.py#L43
|
| 9 |
+
|
| 10 |
+
Modifications made by Yanning Chen, 2024.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import functools
|
| 14 |
+
import importlib
|
| 15 |
+
import operator
|
| 16 |
+
|
| 17 |
+
from typing import Callable
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
import triton
|
| 21 |
+
import triton.language as tl
|
| 22 |
+
|
| 23 |
+
from packaging.version import Version
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def is_npu_available() -> bool:
|
| 27 |
+
"""Detect Ascend NPU availability."""
|
| 28 |
+
try:
|
| 29 |
+
from transformers.utils import is_torch_npu_available
|
| 30 |
+
|
| 31 |
+
return is_torch_npu_available()
|
| 32 |
+
except Exception:
|
| 33 |
+
return False
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def infer_device():
|
| 37 |
+
"""
|
| 38 |
+
Get current device name based on available devices
|
| 39 |
+
"""
|
| 40 |
+
if torch.cuda.is_available(): # Works for both Nvidia and AMD
|
| 41 |
+
return "cuda"
|
| 42 |
+
# Use Ascend NPU if available (torch.npu)
|
| 43 |
+
elif is_npu_available():
|
| 44 |
+
return "npu"
|
| 45 |
+
# XPU (Intel) if available
|
| 46 |
+
elif torch.xpu.is_available():
|
| 47 |
+
return "xpu"
|
| 48 |
+
else:
|
| 49 |
+
return "cpu"
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def is_hip() -> bool:
|
| 53 |
+
return torch.version.hip is not None
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def ensure_contiguous(fn):
|
| 57 |
+
@functools.wraps(fn)
|
| 58 |
+
def wrapper(ctx, *args, **kwargs):
|
| 59 |
+
def maybe_to_contiguous(x):
|
| 60 |
+
return x.contiguous() if isinstance(x, torch.Tensor) else x
|
| 61 |
+
|
| 62 |
+
args = [maybe_to_contiguous(arg) for arg in args]
|
| 63 |
+
kwargs = {k: maybe_to_contiguous(v) for k, v in kwargs.items()}
|
| 64 |
+
return fn(ctx, *args, **kwargs)
|
| 65 |
+
|
| 66 |
+
return wrapper
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def calculate_settings(n):
|
| 70 |
+
# reference: https://github.com/unslothai/unsloth/blob/fd753fed99ed5f10ef8a9b7139588d9de9ddecfb/unsloth/kernels/utils.py#L43
|
| 71 |
+
|
| 72 |
+
MAX_FUSED_SIZE = 65536
|
| 73 |
+
BLOCK_SIZE = triton.next_power_of_2(n)
|
| 74 |
+
if BLOCK_SIZE > MAX_FUSED_SIZE:
|
| 75 |
+
raise RuntimeError(
|
| 76 |
+
f"Cannot launch Triton kernel since n = {n} exceeds the recommended Triton blocksize = {MAX_FUSED_SIZE}."
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
num_warps = 4
|
| 80 |
+
if BLOCK_SIZE >= 32768:
|
| 81 |
+
num_warps = 32 if not is_hip() else 16
|
| 82 |
+
elif BLOCK_SIZE >= 8192:
|
| 83 |
+
num_warps = 16
|
| 84 |
+
elif BLOCK_SIZE >= 2048:
|
| 85 |
+
num_warps = 8
|
| 86 |
+
return BLOCK_SIZE, num_warps
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def compare_version(package: str, operator: Callable, target: str):
|
| 90 |
+
try:
|
| 91 |
+
pkg = importlib.import_module(package)
|
| 92 |
+
except ImportError:
|
| 93 |
+
return False
|
| 94 |
+
pkg_version = Version(pkg.__version__)
|
| 95 |
+
return operator(pkg_version, Version(target))
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def get_amp_custom_fwd_bwd() -> Callable:
|
| 99 |
+
device = infer_device()
|
| 100 |
+
if compare_version("torch", operator.ge, "2.4.0"):
|
| 101 |
+
return (
|
| 102 |
+
functools.partial(torch.amp.custom_fwd, device_type=device),
|
| 103 |
+
functools.partial(torch.amp.custom_bwd, device_type=device),
|
| 104 |
+
)
|
| 105 |
+
if hasattr(torch, "npu") and getattr(torch.npu, "amp", None) is not None:
|
| 106 |
+
return torch.npu.amp.custom_fwd, torch.npu.amp.custom_bwd
|
| 107 |
+
return torch.cuda.amp.custom_fwd, torch.cuda.amp.custom_bwd
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
amp_custom_fwd, amp_custom_bwd = get_amp_custom_fwd_bwd()
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
torch_to_triton_dtype = {
|
| 114 |
+
torch.float32: tl.float32,
|
| 115 |
+
torch.float16: tl.float16,
|
| 116 |
+
torch.bfloat16: tl.bfloat16,
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
@triton.jit
|
| 121 |
+
def element_mul_kernel(
|
| 122 |
+
X_ptr,
|
| 123 |
+
X_stride,
|
| 124 |
+
grad_output_ptr,
|
| 125 |
+
n_cols,
|
| 126 |
+
BLOCK_SIZE: tl.constexpr,
|
| 127 |
+
):
|
| 128 |
+
"""
|
| 129 |
+
This function multiplies each element of the tensor pointed by X_ptr with the value pointed by grad_output_ptr.
|
| 130 |
+
The multiplication is performed in-place on the tensor pointed by X_ptr.
|
| 131 |
+
|
| 132 |
+
Parameters:
|
| 133 |
+
X_ptr: Pointer to the input tensor.
|
| 134 |
+
X_stride (int): The stride of the input tensor.
|
| 135 |
+
grad_output_ptr: Pointer to the gradient output value.
|
| 136 |
+
n_cols (int): The number of columns in the input tensor.
|
| 137 |
+
BLOCK_SIZE (int): The block size for Triton operations.
|
| 138 |
+
"""
|
| 139 |
+
|
| 140 |
+
# Get the program ID and convert it to int64 to avoid overflow
|
| 141 |
+
program_id = tl.program_id(0).to(tl.int64)
|
| 142 |
+
|
| 143 |
+
# Locate the start index
|
| 144 |
+
X_ptr += program_id * X_stride
|
| 145 |
+
|
| 146 |
+
# Load the gradient output value
|
| 147 |
+
grad_output = tl.load(grad_output_ptr)
|
| 148 |
+
|
| 149 |
+
# Perform the element-wise multiplication
|
| 150 |
+
for i in range(0, n_cols, BLOCK_SIZE):
|
| 151 |
+
X_offsets = i + tl.arange(0, BLOCK_SIZE)
|
| 152 |
+
X_block = tl.load(X_ptr + X_offsets, mask=X_offsets < n_cols)
|
| 153 |
+
tl.store(X_ptr + X_offsets, X_block * grad_output, mask=X_offsets < n_cols)
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def get_npu_core_count(default: int = 20) -> int:
|
| 157 |
+
"""Return NPU vector core count.
|
| 158 |
+
Fallback to `default` if Triton runtime or NPU device is unavailable.
|
| 159 |
+
"""
|
| 160 |
+
try:
|
| 161 |
+
utils = triton.runtime.driver.active.utils
|
| 162 |
+
props = utils.get_device_properties(0)
|
| 163 |
+
return int(props.get("num_vectorcore", default))
|
| 164 |
+
except Exception:
|
| 165 |
+
return default
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def set_large_grf_mode(kernel_args: dict):
|
| 169 |
+
"""Set large GRF mode for XPU devices."""
|
| 170 |
+
# On XPU triton installed along with pytorch-xpu will be called `pytorch-triton-xpu`,
|
| 171 |
+
# triton XPU installed from source will be called `triton`.
|
| 172 |
+
if compare_version("pytorch-triton-xpu", operator.ge, "3.6.0") or compare_version("triton", operator.ge, "3.6.0"):
|
| 173 |
+
kernel_args["grf_mode"] = "256"
|
| 174 |
+
else:
|
| 175 |
+
# API was changed in https://github.com/intel/intel-xpu-backend-for-triton/pull/5430
|
| 176 |
+
kernel_args["grf_mode"] = "large"
|