Skip to content

Commit 47430c9

Browse files
committed
Update (base update)
[ghstack-poisoned]
1 parent 7013c8d commit 47430c9

112 files changed

Lines changed: 11471 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backends/vulkan/custom_ops_lib.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,61 @@ def linear_dq8ca_q4gsw(
289289
lib.impl(name, linear_q4gsw, "CompositeExplicitAutograd")
290290
linear_qc4w_op = getattr(getattr(torch.ops, namespace), name)
291291

292+
293+
# Backward of linear_q4gsw wrt input (for on-device LoRA training through a frozen
294+
# 4-bit base): d_x = d_out @ dequant(W). Reference impl extracts dequant(W) via the
295+
# forward on an identity so it is layout-agnostic; the runtime dispatches this op to
296+
# the tiled q4gsw_backward WGSL kernel (contracts over N).
297+
def linear_q4gsw_backward_impl(
298+
d_out: torch.Tensor,
299+
weights: torch.Tensor,
300+
weight_scales: torch.Tensor,
301+
group_size: int,
302+
) -> torch.Tensor:
303+
in_features = int(weights.shape[1]) * 2
304+
eye = torch.eye(in_features, dtype=d_out.dtype, device=d_out.device)
305+
w_t = linear_q4gsw(eye, weights, weight_scales, group_size) # [in, out]
306+
return d_out @ w_t.t() # [M, out] @ [out, in] = [M, in]
307+
308+
309+
def linear_q4gsw_backward_meta(
310+
d_out: torch.Tensor,
311+
weights: torch.Tensor,
312+
weight_scales: torch.Tensor,
313+
group_size: int,
314+
) -> torch.Tensor:
315+
return d_out.new_empty(d_out.shape[:-1] + (int(weights.shape[1]) * 2,))
316+
317+
318+
name = "linear_q4gsw_backward"
319+
lib.define(
320+
f"{name}(Tensor d_out, Tensor weights, Tensor weight_scales, int group_size) -> Tensor"
321+
)
322+
lib.impl(name, linear_q4gsw_backward_impl, "CompositeExplicitAutograd")
323+
lib.impl(name, linear_q4gsw_backward_meta, "Meta")
324+
linear_q4gsw_backward_op = getattr(getattr(torch.ops, namespace), name)
325+
326+
327+
def linear_q4gsw_setup_context(ctx, inputs, output) -> None:
328+
_x, weights, weight_scales, group_size, _bias = inputs
329+
ctx.save_for_backward(weights, weight_scales)
330+
ctx.group_size = group_size
331+
332+
333+
def linear_q4gsw_backward(ctx, grad_out):
334+
weights, weight_scales = ctx.saved_tensors
335+
d_x = torch.ops.et_vk.linear_q4gsw_backward(
336+
grad_out, weights, weight_scales, ctx.group_size
337+
)
338+
return d_x, None, None, None, None # grads for (x, weights, scales, group_size, bias)
339+
340+
341+
torch.library.register_autograd(
342+
f"{namespace}::linear_q4gsw",
343+
linear_q4gsw_backward,
344+
setup_context=linear_q4gsw_setup_context,
345+
)
346+
292347
name = "linear_dq8ca_q4gsw"
293348
lib.define(
294349
f"""
@@ -1090,3 +1145,173 @@ def rms_norm_impl(
10901145
lib.define(f"{name}(Tensor x, Tensor weight, float eps) -> Tensor")
10911146
lib.impl(name, rms_norm_impl, "CompositeExplicitAutograd")
10921147
rms_norm_op = getattr(getattr(torch.ops, namespace), name)
1148+
1149+
1150+
########################
1151+
## fused_ce (training) ##
1152+
########################
1153+
1154+
1155+
def fused_ce_impl(
1156+
logits: torch.Tensor,
1157+
labels: torch.Tensor,
1158+
n_valid: float,
1159+
) -> tuple[torch.Tensor, torch.Tensor]:
1160+
mask = labels >= 0
1161+
safe = labels.clamp(min=0).long()
1162+
lse = torch.logsumexp(logits, dim=-1)
1163+
picked = logits.gather(-1, safe[:, None]).squeeze(-1)
1164+
loss = torch.where(mask, (lse - picked) / n_valid, torch.zeros_like(lse)).sum()
1165+
softmax = torch.softmax(logits, dim=-1)
1166+
onehot = torch.nn.functional.one_hot(safe, logits.shape[-1]).to(logits.dtype)
1167+
dlogits = torch.where(
1168+
mask[:, None], (softmax - onehot) / n_valid, torch.zeros_like(softmax)
1169+
)
1170+
return loss, dlogits
1171+
1172+
1173+
def fused_ce_meta(
1174+
logits: torch.Tensor,
1175+
labels: torch.Tensor,
1176+
n_valid: float,
1177+
) -> tuple[torch.Tensor, torch.Tensor]:
1178+
return logits.new_empty([]), torch.empty_like(logits)
1179+
1180+
1181+
def fused_ce_setup_context(ctx, inputs, output) -> None:
1182+
ctx.save_for_backward(output[1])
1183+
1184+
1185+
def fused_ce_backward(ctx, grad_loss, grad_dlogits):
1186+
(dlogits,) = ctx.saved_tensors
1187+
return grad_loss * dlogits, None, None
1188+
1189+
1190+
name = "fused_ce"
1191+
lib.define(f"{name}(Tensor logits, Tensor labels, float n_valid) -> (Tensor, Tensor)")
1192+
lib.impl(name, fused_ce_impl, "CompositeExplicitAutograd")
1193+
lib.impl(name, fused_ce_meta, "Meta")
1194+
torch.library.register_autograd(
1195+
f"{namespace}::{name}", fused_ce_backward, setup_context=fused_ce_setup_context
1196+
)
1197+
fused_ce_op = getattr(getattr(torch.ops, namespace), name)
1198+
1199+
1200+
1201+
###########################
1202+
## adamw_step (training) ##
1203+
###########################
1204+
1205+
1206+
def adamw_step_impl(
1207+
param: torch.Tensor,
1208+
m: torch.Tensor,
1209+
v: torch.Tensor,
1210+
grad: torch.Tensor,
1211+
lr: float,
1212+
beta1: float,
1213+
beta2: float,
1214+
eps: float,
1215+
weight_decay: float,
1216+
bias_correction1: float,
1217+
bias_correction2: float,
1218+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
1219+
param.mul_(1.0 - lr * weight_decay)
1220+
m.mul_(beta1).add_(grad, alpha=1.0 - beta1)
1221+
v.mul_(beta2).addcmul_(grad, grad, value=1.0 - beta2)
1222+
mhat = m / bias_correction1
1223+
denom = (v / bias_correction2).sqrt() + eps
1224+
param.addcdiv_(mhat, denom, value=-lr)
1225+
return param, m, v
1226+
1227+
1228+
def adamw_step_meta(
1229+
param: torch.Tensor,
1230+
m: torch.Tensor,
1231+
v: torch.Tensor,
1232+
grad: torch.Tensor,
1233+
lr: float,
1234+
beta1: float,
1235+
beta2: float,
1236+
eps: float,
1237+
weight_decay: float,
1238+
bias_correction1: float,
1239+
bias_correction2: float,
1240+
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
1241+
return param, m, v
1242+
1243+
1244+
name = "adamw_step"
1245+
lib.define(
1246+
f"{name}(Tensor(a!) param, Tensor(b!) m, Tensor(c!) v, Tensor grad, float lr, float beta1, float beta2, float eps, float weight_decay, float bias_correction1, float bias_correction2) -> (Tensor(a!), Tensor(b!), Tensor(c!))"
1247+
)
1248+
lib.impl(name, adamw_step_impl, "CompositeExplicitAutograd")
1249+
lib.impl(name, adamw_step_meta, "Meta")
1250+
adamw_step_op = getattr(getattr(torch.ops, namespace), name)
1251+
1252+
1253+
# STE weight gradient d_out^T @ x through the frozen 4-bit linear_q4gsw base.
1254+
def linear_q4gsw_dw_impl(
1255+
d_out: torch.Tensor,
1256+
x: torch.Tensor,
1257+
) -> torch.Tensor:
1258+
return d_out.reshape(-1, d_out.shape[-1]).t() @ x.reshape(-1, x.shape[-1])
1259+
1260+
1261+
def linear_q4gsw_dw_meta(
1262+
d_out: torch.Tensor,
1263+
x: torch.Tensor,
1264+
) -> torch.Tensor:
1265+
return d_out.new_empty((d_out.shape[-1], x.shape[-1]))
1266+
1267+
1268+
name = "linear_q4gsw_dw"
1269+
lib.define(f"{name}(Tensor d_out, Tensor x) -> Tensor")
1270+
lib.impl(name, linear_q4gsw_dw_impl, "CompositeExplicitAutograd")
1271+
lib.impl(name, linear_q4gsw_dw_meta, "Meta")
1272+
linear_q4gsw_dw_op = getattr(getattr(torch.ops, namespace), name)
1273+
1274+
1275+
1276+
##################
1277+
## q4gsw_requant ##
1278+
##################
1279+
1280+
1281+
# STE re-quant of fp32 latent weights into the frozen-scale 4-bit codes.
1282+
def q4gsw_requant_impl(
1283+
latent: torch.Tensor,
1284+
scales: torch.Tensor,
1285+
group_size: int,
1286+
) -> torch.Tensor:
1287+
n, k = latent.shape
1288+
group_idx = torch.arange(k, device=latent.device) // group_size
1289+
scale_full = scales.t()[:, group_idx] # [N, K]: scales[k // group_size, n]
1290+
nonzero = scale_full != 0
1291+
safe = torch.where(nonzero, scale_full, torch.ones_like(scale_full))
1292+
q = torch.round(latent / safe)
1293+
q = torch.where(nonzero, q, torch.zeros_like(q))
1294+
codes = (torch.clamp(q, -8, 7).to(torch.int32) + 8) & 0xF # [N, K] in 0..15
1295+
k_packed = (k + 1) // 2
1296+
packed = torch.zeros((n, k_packed), dtype=torch.uint8, device=latent.device)
1297+
packed[:, :] = codes[:, 0::2].to(torch.uint8)
1298+
if k > 1:
1299+
high = codes[:, 1::2].to(torch.uint8)
1300+
packed[:, : high.shape[1]] |= high << 4
1301+
return packed
1302+
1303+
1304+
def q4gsw_requant_meta(
1305+
latent: torch.Tensor,
1306+
scales: torch.Tensor,
1307+
group_size: int,
1308+
) -> torch.Tensor:
1309+
n, k = latent.shape
1310+
return latent.new_empty((n, (k + 1) // 2), dtype=torch.uint8)
1311+
1312+
1313+
name = "q4gsw_requant"
1314+
lib.define(f"{name}(Tensor latent, Tensor scales, int group_size) -> Tensor")
1315+
lib.impl(name, q4gsw_requant_impl, "CompositeExplicitAutograd")
1316+
lib.impl(name, q4gsw_requant_meta, "Meta")
1317+
q4gsw_requant_op = getattr(getattr(torch.ops, namespace), name)

backends/vulkan/op_registry.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,15 @@ def register_quantizedlinear_cpp_ops():
462462
)
463463

464464

465+
@update_features(exir_ops.edge.et_vk.linear_q4gsw_backward.default)
466+
def register_linear_q4gsw_backward():
467+
return OpFeatures(
468+
inputs_storage=utils.CONTIGUOUS_ANY,
469+
inputs_dtypes=utils.FP_T,
470+
supports_prepacking=True,
471+
)
472+
473+
465474
@update_features(exir_ops.edge.et_vk.linear_dq8ca_q4gsw.default)
466475
def register_linear_dq8ca_q4gsw():
467476
return OpFeatures(
@@ -1746,6 +1755,80 @@ def register_rms_norm():
17461755
)
17471756

17481757

1758+
# =============================================================================
1759+
# FusedCe.cpp (training)
1760+
# =============================================================================
1761+
1762+
1763+
@update_features(exir_ops.edge.et_vk.fused_ce.default)
1764+
def register_fused_ce():
1765+
return OpFeatures(
1766+
inputs_storage=utils.CONTIGUOUS_ANY,
1767+
inputs_dtypes=[utils.FP_T, utils.INT_T, utils.NONE_T],
1768+
outputs_dtypes=[utils.FP_T, utils.FP_T],
1769+
)
1770+
1771+
1772+
1773+
1774+
@update_features(
1775+
[
1776+
exir_ops.edge.aten.ne.Scalar,
1777+
exir_ops.edge.aten.lt.Scalar,
1778+
exir_ops.edge.aten.le.Scalar,
1779+
exir_ops.edge.aten.ge.Scalar,
1780+
]
1781+
)
1782+
def register_compare_scalar_ops():
1783+
return OpFeatures(
1784+
inputs_storage=utils.ANY_STORAGE,
1785+
inputs_dtypes=utils.FP_INT_T,
1786+
outputs_dtypes=utils.BOOL_T,
1787+
supports_resize=True,
1788+
supports_highdim=True,
1789+
)
1790+
1791+
1792+
1793+
1794+
@update_features(exir_ops.edge.aten.logical_not.default)
1795+
def register_logical_not():
1796+
return OpFeatures(
1797+
inputs_storage=utils.ANY_STORAGE,
1798+
inputs_dtypes=utils.BOOL_T,
1799+
supports_resize=True,
1800+
supports_highdim=True,
1801+
)
1802+
1803+
1804+
1805+
@update_features("et_vk::adamw_step")
1806+
def register_adamw_step():
1807+
return OpFeatures(
1808+
inputs_storage=utils.CONTIGUOUS_ANY,
1809+
inputs_dtypes=utils.FP_T,
1810+
)
1811+
1812+
1813+
@update_features(exir_ops.edge.et_vk.linear_q4gsw_dw.default)
1814+
def register_linear_q4gsw_dw():
1815+
return OpFeatures(
1816+
inputs_storage=utils.CONTIGUOUS_ANY,
1817+
inputs_dtypes=utils.FP_T,
1818+
supports_prepacking=True,
1819+
)
1820+
1821+
1822+
1823+
1824+
@update_features(exir_ops.edge.et_vk.q4gsw_requant.default)
1825+
def register_q4gsw_requant():
1826+
return OpFeatures(
1827+
inputs_storage=utils.CONTIGUOUS_ANY,
1828+
inputs_dtypes=utils.FP_T,
1829+
)
1830+
1831+
17491832
#######################
17501833
## Utility functions ##
17511834
#######################
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#version 450 core
10+
11+
#define PRECISION ${PRECISION}
12+
13+
#define T ${buffer_scalar_type(DTYPE)}
14+
15+
${define_active_storage_type(STORAGE)}
16+
17+
${define_required_extensions(STORAGE, DTYPE)}
18+
19+
layout(std430) buffer;
20+
21+
${layout_declare_tensor(B, "rw", "t_param", DTYPE, STORAGE)}
22+
${layout_declare_tensor(B, "rw", "t_m", DTYPE, STORAGE)}
23+
${layout_declare_tensor(B, "rw", "t_v", DTYPE, STORAGE)}
24+
${layout_declare_tensor(B, "r", "t_grad", DTYPE, STORAGE)}
25+
26+
layout(push_constant) uniform restrict Block {
27+
int numel;
28+
float lr;
29+
float beta1;
30+
float beta2;
31+
float eps;
32+
float weight_decay;
33+
float bias_correction1;
34+
float bias_correction2;
35+
};
36+
37+
layout(local_size_x_id = 0, local_size_y_id = 1, local_size_z_id = 2) in;
38+
39+
void main() {
40+
const int i = int(gl_GlobalInvocationID.x);
41+
if (i >= numel) {
42+
return;
43+
}
44+
T g = t_grad[i];
45+
T p = t_param[i];
46+
p = p - lr * weight_decay * p;
47+
T m = beta1 * t_m[i] + (1.0 - beta1) * g;
48+
T v = beta2 * t_v[i] + (1.0 - beta2) * g * g;
49+
t_m[i] = m;
50+
t_v[i] = v;
51+
T mhat = m / bias_correction1;
52+
T vhat = v / bias_correction2;
53+
t_param[i] = p - lr * mhat / (sqrt(vhat) + eps);
54+
}

0 commit comments

Comments
 (0)