Instructions to use replicate/liger-kernels with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Kernels
How to use replicate/liger-kernels with Kernels:
# !pip install kernels from kernels import get_kernel # a version (or an explicit revision) is required; see the "Files and versions" tab for the available ones kernel = get_kernel("replicate/liger-kernels", version=1) - Notebooks
- Google Colab
- Kaggle
File size: 10,586 Bytes
e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 e5b00e0 3bbbd55 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | import math
import operator
import torch
import triton
import triton.language as tl
from .utils import calculate_settings
from .utils import compare_version
from .utils import ensure_contiguous
from .utils import get_npu_core_count
from .utils import set_large_grf_mode
from .utils import is_npu_available
if compare_version("triton", operator.ge, "3.0.0") and not is_npu_available():
try:
# typical import path with dispatch available
from triton.language.extra.libdevice import rsqrt
except ModuleNotFoundError:
# for working with NGC containers
from triton.language.extra.cuda.libdevice import rsqrt
else:
from triton.language.math import rsqrt
@triton.jit
def _layer_norm_forward_kernel(
Y_ptr, # pointer to output, shape (n_rows, n_cols)
Y_row_stride, # stride of each row in output
X_ptr, # pointer to input, shape (n_rows, n_cols)
X_row_stride, # stride of each row in input
W_ptr, # pointer to weights, shape (n_cols,)
W_row_stride, # stride of each row in weights
B_ptr, # pointer to bias, shape (n_cols,)
B_row_stride, # stride of each row in bias
Mean_ptr, # pointer to mean, shape (n_rows,)
Mean_row_stride, # stride of each row in mean
RSTD_ptr, # pointer to rstd, shape (n_rows,)
RSTD_row_stride, # stride of each row in rstd
n_cols,
eps,
BLOCK_SIZE: tl.constexpr,
):
"""
References:
https://arxiv.org/abs/1607.06450
https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
"""
row_idx = tl.program_id(0).to(tl.int64)
col_offsets = tl.arange(0, BLOCK_SIZE)
mask = col_offsets < n_cols
# Pre-load weights and bias in fp32 to avoid repeated conversions
W_row = tl.load(W_ptr + col_offsets, mask=mask, other=0.0)
B_row = tl.load(B_ptr + col_offsets, mask=mask, other=0.0)
W_f32 = W_row.to(tl.float32)
B_f32 = B_row.to(tl.float32)
# Calculate pointers for this row
row_X_ptr = X_ptr + row_idx * X_row_stride
row_Y_ptr = Y_ptr + row_idx * Y_row_stride
row_Mean_ptr = Mean_ptr + row_idx * Mean_row_stride
row_RSTD_ptr = RSTD_ptr + row_idx * RSTD_row_stride
# Load input data and convert to fp32 for numerical stability
X_row = tl.load(row_X_ptr + col_offsets, mask=mask, other=0.0)
X_f32 = X_row.to(tl.float32)
# Compute statistics in fp32 for numerical stability
mean = tl.sum(X_f32, axis=0) / n_cols
X_centered = X_f32 - mean
# Apply mask to variance calculation to exclude contributions from masked elements
X_centered_masked = tl.where(mask, X_centered, 0.0)
var = tl.sum(X_centered_masked * X_centered_masked, axis=0) / n_cols
rstd = rsqrt(var + eps)
# Store statistics (convert back to original dtype only once)
tl.store(row_Mean_ptr, mean.to(X_row.dtype))
tl.store(row_RSTD_ptr, rstd.to(X_row.dtype))
# Fused normalization and affine transformation
# Y = (X - mean) * rstd * W + B = X_centered * rstd * W + B
Y_f32 = X_centered * rstd * W_f32 + B_f32
# Store output (single conversion back to original dtype)
tl.store(row_Y_ptr + col_offsets, Y_f32.to(X_row.dtype), mask=mask)
@triton.jit
def _layer_norm_backward_kernel(
X_ptr, # pointer to input, shape (n_rows, n_cols)
stride_x, # stride of each row in input
W_ptr, # pointer to weights, shape (n_cols,)
Mean_ptr, # pointer to mean, shape (n_rows,)
stride_mean, # stride of each row in mean
RSTD_ptr, # pointer to rstd, shape (n_rows,)
stride_rstd, # stride of each row in rstd
DX_ptr, # pointer to input grad, shape (n_rows, n_cols)
stride_dx, # stride of each row in input grad
DW_ptr, # pointer to weights grad, shape (n_cols,)
stride_dw, # stride of each row in weights grad
DB_ptr, # pointer to bias grad, shape (n_cols,)
stride_db, # stride of each row in bias grad
DY_ptr, # pointer to output grad, shape (n_rows, n_cols)
stride_dy, # stride of each row in output grad
n_rows,
n_cols,
rows_per_program: tl.constexpr,
BLOCK_SIZE: tl.constexpr,
):
"""
References:
https://arxiv.org/abs/1607.06450
https://github.com/karpathy/llm.c/blob/master/doc/layernorm/layernorm.md
"""
row_block_id = tl.program_id(0).to(tl.int64)
row_start = row_block_id * rows_per_program
row_end = min((row_block_id + 1) * rows_per_program, n_rows)
cols = tl.arange(0, BLOCK_SIZE)
mask = cols < n_cols
dW_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
db_row = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
# Pre-load weights once (same optimization as forward pass)
w = tl.load(W_ptr + cols, mask=mask, other=0.0)
w_f32 = w.to(tl.float32)
for row_idx in range(row_start, row_end):
# Calculate pointers for this specific row
row_X_ptr = X_ptr + row_idx * stride_x
row_DX_ptr = DX_ptr + row_idx * stride_dx
row_DY_ptr = DY_ptr + row_idx * stride_dy
row_Mean_ptr = Mean_ptr + row_idx * stride_mean
row_RSTD_ptr = RSTD_ptr + row_idx * stride_rstd
# Load data for this row
x = tl.load(row_X_ptr + cols, mask=mask, other=0.0)
dy = tl.load(row_DY_ptr + cols, mask=mask, other=0.0)
mean = tl.load(row_Mean_ptr)
rstd = tl.load(row_RSTD_ptr)
# Convert to fp32 for numerical stability
x_f32 = x.to(tl.float32)
dy_f32 = dy.to(tl.float32)
mean_f32 = mean.to(tl.float32)
rstd_f32 = rstd.to(tl.float32)
# Compute backward pass for this row
x_hat = (x_f32 - mean_f32) * rstd_f32
wdy = w_f32 * dy_f32
c1 = tl.sum(x_hat * wdy, axis=0) / n_cols
c2 = tl.sum(wdy, axis=0) / n_cols
dx = (wdy - (x_hat * c1 + c2)) * rstd_f32
# Store input gradient
tl.store(row_DX_ptr + cols, dx, mask=mask)
# Accumulate weight and bias gradients for this thread block's assigned rows
dw = dy_f32 * x_hat
db = dy_f32
dW_row += dw
db_row += db
tl.store(DW_ptr + row_block_id * stride_dw + cols, dW_row, mask=mask)
tl.store(DB_ptr + row_block_id * stride_db + cols, db_row, mask=mask)
def layer_norm_forward(X, W, B, eps):
"""
Args:
X: Input tensor of shape (..., hidden_size)
W: Weight tensor of shape (hidden_size,)
B: Bias tensor of shape (hidden_size,)
eps: Small constant for numerical stability
Returns:
Tuple of (output, input, mean, rstd, block_size, num_warps)
"""
shape = X.shape
dim = shape[-1]
X = X.view(-1, dim)
n_rows, n_cols = X.shape
# Calculate optimal block size and warp configuration
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
# Allocate output tensors
Y = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
Mean = torch.empty(n_rows, dtype=X.dtype, device=X.device)
RSTD = torch.empty(n_rows, dtype=X.dtype, device=X.device)
# Validate input dimensions
if X.shape[1] != W.shape[0]:
raise ValueError(
f"Incompatible dimensions: input feature size (X.shape[1]={X.shape[1]}) "
f"must match weight size (W.shape[0]={W.shape[0]})"
)
# XPU-specific optimization
kernel_args = {}
if X.device.type == "xpu":
set_large_grf_mode(kernel_args)
# Launch kernel with one thread block per row for optimal performance
grid = (n_rows,)
_layer_norm_forward_kernel[grid](
Y,
Y.stride(0),
X,
X.stride(0),
W,
W.stride(0),
B,
B.stride(0),
Mean,
Mean.stride(0),
RSTD,
RSTD.stride(0),
n_cols,
eps,
BLOCK_SIZE=BLOCK_SIZE,
num_warps=num_warps,
**kernel_args,
)
return Y.view(*shape), X, Mean, RSTD, BLOCK_SIZE, num_warps
def layer_norm_backward(dY, X, W, B, Mean, RSTD):
"""
Args:
dY: Gradient of output
X: Input tensor
W: Weight tensor
B: Bias tensor
Mean: Pre-computed mean
RSTD: Pre-computed reciprocal standard deviation
Returns:
Tuple of (input_grad, weight_grad, bias_grad)
"""
shape = dY.shape
dim = shape[-1]
dY = dY.view(-1, dim)
n_rows, n_cols = dY.shape
sm_count = 1
if X.device.type == "cuda":
sm_count = torch.cuda.get_device_properties(X.device).multi_processor_count
elif X.device.type == "xpu":
sm_count = torch.xpu.get_device_properties(X.device).gpu_eu_count
elif X.device.type == "npu":
sm_count = get_npu_core_count()
# fp32 for numerical stability especially.
_DW = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
_DB = torch.empty((sm_count, n_cols), dtype=torch.float32, device=W.device)
# Calculate optimal block size and warp configuration
BLOCK_SIZE, num_warps = calculate_settings(n_cols)
if n_cols > BLOCK_SIZE:
raise RuntimeError(f"Feature dimension {n_cols} exceeds maximum supported size of {BLOCK_SIZE}.")
rows_per_program = math.ceil(n_rows / sm_count)
grid = (sm_count,)
# Allocate gradient tensors
DX = torch.empty((n_rows, n_cols), dtype=X.dtype, device=X.device)
kernel_args = {"num_warps": num_warps}
# XPU-specific optimization
if X.device.type == "xpu":
kernel_args.update({"num_warps": 32, "num_stages": 4})
set_large_grf_mode(kernel_args)
# Launch kernel with one thread block per row for optimal performance
_layer_norm_backward_kernel[grid](
X,
X.stride(0),
W,
Mean,
Mean.stride(0),
RSTD,
RSTD.stride(0),
DX,
DX.stride(0),
_DW,
_DW.stride(0),
_DB,
_DB.stride(0),
dY,
dY.stride(0),
n_rows,
n_cols,
rows_per_program=rows_per_program,
BLOCK_SIZE=BLOCK_SIZE,
**kernel_args,
)
DX = DX.view(*shape)
DW = _DW.sum(dim=0).to(W.dtype)
DB = _DB.sum(dim=0).to(B.dtype)
return DX, DW, DB
class LigerLayerNormFunction(torch.autograd.Function):
@staticmethod
@ensure_contiguous
def forward(ctx, X, W, B, eps):
Y, X, Mean, RSTD, BLOCK_SIZE, num_warps = layer_norm_forward(X, W, B, eps)
ctx.save_for_backward(X, W, B, Mean, RSTD)
return Y
@staticmethod
@ensure_contiguous
def backward(ctx, dY):
X, W, B, Mean, RSTD = ctx.saved_tensors
DX, DW, DB = layer_norm_backward(dY, X, W, B, Mean, RSTD)
return DX, DW, DB, None
|