Uploaded using `kernel-builder`.
Browse files- build/torch211-cxx11-cu126-x86_64-linux/__init__.py +114 -0
- build/torch211-cxx11-cu126-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch211-cxx11-cu126-x86_64-linux/_ops.py +9 -0
- build/torch211-cxx11-cu126-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch211-cxx11-cu126-x86_64-linux/metadata.json +36 -0
- build/torch211-cxx11-cu128-x86_64-linux/__init__.py +114 -0
- build/torch211-cxx11-cu128-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch211-cxx11-cu128-x86_64-linux/_ops.py +9 -0
- build/torch211-cxx11-cu128-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch211-cxx11-cu128-x86_64-linux/metadata.json +38 -0
- build/torch211-cxx11-cu130-x86_64-linux/__init__.py +114 -0
- build/torch211-cxx11-cu130-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch211-cxx11-cu130-x86_64-linux/_ops.py +9 -0
- build/torch211-cxx11-cu130-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch211-cxx11-cu130-x86_64-linux/metadata.json +38 -0
- build/torch212-cxx11-cu126-x86_64-linux/__init__.py +114 -0
- build/torch212-cxx11-cu126-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch212-cxx11-cu126-x86_64-linux/_ops.py +9 -0
- build/torch212-cxx11-cu126-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch212-cxx11-cu126-x86_64-linux/metadata.json +36 -0
- build/torch212-cxx11-cu130-x86_64-linux/__init__.py +114 -0
- build/torch212-cxx11-cu130-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch212-cxx11-cu130-x86_64-linux/_ops.py +9 -0
- build/torch212-cxx11-cu130-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch212-cxx11-cu130-x86_64-linux/metadata.json +38 -0
- build/torch212-cxx11-cu132-x86_64-linux/__init__.py +114 -0
- build/torch212-cxx11-cu132-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch212-cxx11-cu132-x86_64-linux/_ops.py +9 -0
- build/torch212-cxx11-cu132-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch212-cxx11-cu132-x86_64-linux/metadata.json +38 -0
- build/torch213-cxx11-cu126-x86_64-linux/__init__.py +114 -0
- build/torch213-cxx11-cu126-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch213-cxx11-cu126-x86_64-linux/_ops.py +9 -0
- build/torch213-cxx11-cu126-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch213-cxx11-cu126-x86_64-linux/metadata.json +36 -0
- build/torch213-cxx11-cu130-x86_64-linux/__init__.py +114 -0
- build/torch213-cxx11-cu130-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch213-cxx11-cu130-x86_64-linux/_ops.py +9 -0
- build/torch213-cxx11-cu130-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch213-cxx11-cu130-x86_64-linux/metadata.json +38 -0
- build/torch213-cxx11-cu132-x86_64-linux/__init__.py +114 -0
- build/torch213-cxx11-cu132-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so +3 -0
- build/torch213-cxx11-cu132-x86_64-linux/_ops.py +9 -0
- build/torch213-cxx11-cu132-x86_64-linux/binary_gemm/__init__.py +26 -0
- build/torch213-cxx11-cu132-x86_64-linux/metadata.json +38 -0
build/torch211-cxx11-cu126-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch211-cxx11-cu126-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b0726f6e858bd28e6f7afec48f47ccb1b74cf5e0213fd015036b1b5bdf9d03d7
|
| 3 |
+
size 433992
|
build/torch211-cxx11-cu126-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch211-cxx11-cu126-x86_64-linux/binary_gemm/__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/torch211-cxx11-cu126-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"8.0",
|
| 11 |
+
"8.6",
|
| 12 |
+
"8.9",
|
| 13 |
+
"9.0"
|
| 14 |
+
]
|
| 15 |
+
},
|
| 16 |
+
"digest": {
|
| 17 |
+
"algorithm": "sha256",
|
| 18 |
+
"files": {
|
| 19 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 20 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "sHJvboWL0o5vev7Ej0fMsbdM9eAhP9AVA2sbW9+dA9c=",
|
| 21 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 22 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
"provenance": {
|
| 26 |
+
"kernel-builder": {
|
| 27 |
+
"version": "0.17.0-dev0",
|
| 28 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 29 |
+
"dirty": false
|
| 30 |
+
},
|
| 31 |
+
"kernel": {
|
| 32 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 33 |
+
"dirty": false
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
}
|
build/torch211-cxx11-cu128-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch211-cxx11-cu128-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1d94c2e858f5285c82375e55ffeabe003db5b49f3c0f27bc178aff911554010b
|
| 3 |
+
size 655336
|
build/torch211-cxx11-cu128-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch211-cxx11-cu128-x86_64-linux/binary_gemm/__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/torch211-cxx11-cu128-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"10.0",
|
| 11 |
+
"12.0",
|
| 12 |
+
"8.0",
|
| 13 |
+
"8.6",
|
| 14 |
+
"8.9",
|
| 15 |
+
"9.0"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"digest": {
|
| 19 |
+
"algorithm": "sha256",
|
| 20 |
+
"files": {
|
| 21 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 22 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "HZTC6Fj1KFyCN15V/+q+AD21tJ88Dye8F4r/kRVUAQs=",
|
| 23 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 24 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"provenance": {
|
| 28 |
+
"kernel-builder": {
|
| 29 |
+
"version": "0.17.0-dev0",
|
| 30 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 31 |
+
"dirty": false
|
| 32 |
+
},
|
| 33 |
+
"kernel": {
|
| 34 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 35 |
+
"dirty": false
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
build/torch211-cxx11-cu130-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch211-cxx11-cu130-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:73836d8e9c2e4fd31428ec170ee045ac2488cddf0a4dcebb2abe7c2a22fbf7f6
|
| 3 |
+
size 682200
|
build/torch211-cxx11-cu130-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch211-cxx11-cu130-x86_64-linux/binary_gemm/__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/torch211-cxx11-cu130-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"10.0",
|
| 11 |
+
"12.0",
|
| 12 |
+
"8.0",
|
| 13 |
+
"8.6",
|
| 14 |
+
"8.9",
|
| 15 |
+
"9.0"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"digest": {
|
| 19 |
+
"algorithm": "sha256",
|
| 20 |
+
"files": {
|
| 21 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 22 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "c4NtjpwuT9MUKOwXDuBFrCSIzd8KTc67Kr58KiL79/Y=",
|
| 23 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 24 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"provenance": {
|
| 28 |
+
"kernel-builder": {
|
| 29 |
+
"version": "0.17.0-dev0",
|
| 30 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 31 |
+
"dirty": false
|
| 32 |
+
},
|
| 33 |
+
"kernel": {
|
| 34 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 35 |
+
"dirty": false
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
build/torch212-cxx11-cu126-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch212-cxx11-cu126-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cfc06dfc659837c4190ea30bb4b6296a6bb8513b9fc4e3376fe649f79b6dd91a
|
| 3 |
+
size 439624
|
build/torch212-cxx11-cu126-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch212-cxx11-cu126-x86_64-linux/binary_gemm/__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/torch212-cxx11-cu126-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"8.0",
|
| 11 |
+
"8.6",
|
| 12 |
+
"8.9",
|
| 13 |
+
"9.0"
|
| 14 |
+
]
|
| 15 |
+
},
|
| 16 |
+
"digest": {
|
| 17 |
+
"algorithm": "sha256",
|
| 18 |
+
"files": {
|
| 19 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 20 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "z8Bt/GWYN8QZDqMLtLYpamu4UTufxOM3b+ZJ95tt2Ro=",
|
| 21 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 22 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
"provenance": {
|
| 26 |
+
"kernel-builder": {
|
| 27 |
+
"version": "0.17.0-dev0",
|
| 28 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 29 |
+
"dirty": false
|
| 30 |
+
},
|
| 31 |
+
"kernel": {
|
| 32 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 33 |
+
"dirty": false
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
}
|
build/torch212-cxx11-cu130-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch212-cxx11-cu130-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d35024ab6c50dcf278e73b13bb43933e1136403d8b9a3b8f91c0564312895ebf
|
| 3 |
+
size 687272
|
build/torch212-cxx11-cu130-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch212-cxx11-cu130-x86_64-linux/binary_gemm/__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/torch212-cxx11-cu130-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"10.0",
|
| 11 |
+
"12.0",
|
| 12 |
+
"8.0",
|
| 13 |
+
"8.6",
|
| 14 |
+
"8.9",
|
| 15 |
+
"9.0"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"digest": {
|
| 19 |
+
"algorithm": "sha256",
|
| 20 |
+
"files": {
|
| 21 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 22 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "01Akq2xQ3PJ45zsTu0OTPhE2QD2LmjuPkcBWQxKJXr8=",
|
| 23 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 24 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"provenance": {
|
| 28 |
+
"kernel-builder": {
|
| 29 |
+
"version": "0.17.0-dev0",
|
| 30 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 31 |
+
"dirty": false
|
| 32 |
+
},
|
| 33 |
+
"kernel": {
|
| 34 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 35 |
+
"dirty": false
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
build/torch212-cxx11-cu132-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch212-cxx11-cu132-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cd3c83131d08f6d8ea35a82715caa2f9c918be096e0ccc408189d22a7cd6818a
|
| 3 |
+
size 726192
|
build/torch212-cxx11-cu132-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch212-cxx11-cu132-x86_64-linux/binary_gemm/__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/torch212-cxx11-cu132-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"10.0",
|
| 11 |
+
"12.0",
|
| 12 |
+
"8.0",
|
| 13 |
+
"8.6",
|
| 14 |
+
"8.9",
|
| 15 |
+
"9.0"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"digest": {
|
| 19 |
+
"algorithm": "sha256",
|
| 20 |
+
"files": {
|
| 21 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 22 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "zTyDEx0I9tjqNagnFcqi+ckYvgluDMxAgYnSKnzWgYo=",
|
| 23 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 24 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"provenance": {
|
| 28 |
+
"kernel-builder": {
|
| 29 |
+
"version": "0.17.0-dev0",
|
| 30 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 31 |
+
"dirty": false
|
| 32 |
+
},
|
| 33 |
+
"kernel": {
|
| 34 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 35 |
+
"dirty": false
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
build/torch213-cxx11-cu126-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch213-cxx11-cu126-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:65b2374bfd12bf73d357aa8b65328b19085187c6b1ca37d1dfedc8f9d2b86a4f
|
| 3 |
+
size 439464
|
build/torch213-cxx11-cu126-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch213-cxx11-cu126-x86_64-linux/binary_gemm/__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/torch213-cxx11-cu126-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"8.0",
|
| 11 |
+
"8.6",
|
| 12 |
+
"8.9",
|
| 13 |
+
"9.0"
|
| 14 |
+
]
|
| 15 |
+
},
|
| 16 |
+
"digest": {
|
| 17 |
+
"algorithm": "sha256",
|
| 18 |
+
"files": {
|
| 19 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 20 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "ZbI3S/0Sv3PTV6qLZTKLGQhRh8axyjfR3+3I+dK4ak8=",
|
| 21 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 22 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
"provenance": {
|
| 26 |
+
"kernel-builder": {
|
| 27 |
+
"version": "0.17.0-dev0",
|
| 28 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 29 |
+
"dirty": false
|
| 30 |
+
},
|
| 31 |
+
"kernel": {
|
| 32 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 33 |
+
"dirty": false
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
}
|
build/torch213-cxx11-cu130-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch213-cxx11-cu130-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9a16adc9320c70979199f783d70716c648d7a4471ce5f926f584dc17283c0f05
|
| 3 |
+
size 687120
|
build/torch213-cxx11-cu130-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch213-cxx11-cu130-x86_64-linux/binary_gemm/__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/torch213-cxx11-cu130-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"10.0",
|
| 11 |
+
"12.0",
|
| 12 |
+
"8.0",
|
| 13 |
+
"8.6",
|
| 14 |
+
"8.9",
|
| 15 |
+
"9.0"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"digest": {
|
| 19 |
+
"algorithm": "sha256",
|
| 20 |
+
"files": {
|
| 21 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 22 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "mhatyTIMcJeRmfeD1wcWxkjXpEcc5fkm9YTcFyg8DwU=",
|
| 23 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 24 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"provenance": {
|
| 28 |
+
"kernel-builder": {
|
| 29 |
+
"version": "0.17.0-dev0",
|
| 30 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 31 |
+
"dirty": false
|
| 32 |
+
},
|
| 33 |
+
"kernel": {
|
| 34 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 35 |
+
"dirty": false
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
build/torch213-cxx11-cu132-x86_64-linux/__init__.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""binary-gemm: W1A8 matrix products for binary-weight language models.
|
| 2 |
+
|
| 3 |
+
Each weight is one bit meaning +1 or -1, with one fp16 scale per group of G
|
| 4 |
+
weights: at G = 128 that is 1.125 effective bits, against 2 bits for a ternary
|
| 5 |
+
layer and 16 for bf16. Activations are quantized per token to int8.
|
| 6 |
+
|
| 7 |
+
Binary weights are not a special case of ternary. Writing the stored bit as
|
| 8 |
+
b = (w + 1) / 2, the inner product is 2 * sum_{b_i = 1} a_i - sum_i a_i, so the
|
| 9 |
+
weight is never materialized as a number. The kernel resolves four weights at a
|
| 10 |
+
time through a nibble table feeding __dp4a.
|
| 11 |
+
|
| 12 |
+
Ternary companions on the Hub: phanerozoic/bitnet-tc (CUDA),
|
| 13 |
+
phanerozoic/bitnet-cpu (CPU).
|
| 14 |
+
"""
|
| 15 |
+
from typing import Optional, Tuple
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ._ops import ops
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"pack_weights", "unpack_weights", "quantize_activation", "binary_gemm",
|
| 23 |
+
"binary_linear", "BinaryLinear",
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
GROUP = 128
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def pack_weights(w: torch.Tensor) -> torch.Tensor:
|
| 30 |
+
"""Pack a ``{-1, +1}`` int8 ``[N, K]`` matrix to ``[N, K//32]`` int32.
|
| 31 |
+
|
| 32 |
+
Bit j of word i holds weight ``32*i + j``: set means +1, clear means -1.
|
| 33 |
+
Zeros are treated as -1, so a ternary tensor will not round-trip.
|
| 34 |
+
"""
|
| 35 |
+
return ops.pack_weights(w.contiguous().to(torch.int8))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unpack_weights(wq: torch.Tensor, K: int) -> torch.Tensor:
|
| 39 |
+
"""Inverse of :func:`pack_weights`, for tests and for a reference path."""
|
| 40 |
+
bits = wq.to(torch.int32)
|
| 41 |
+
idx = torch.arange(32, device=wq.device, dtype=torch.int32)
|
| 42 |
+
out = ((bits.unsqueeze(-1) >> idx) & 1).to(torch.int8)
|
| 43 |
+
return (out.reshape(wq.shape[0], -1)[:, :K] * 2 - 1)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def quantize_activation(x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 47 |
+
"""bf16 ``[..., K]`` -> (int8 ``[M, K]``, fp32 per-token scale ``[M]``)."""
|
| 48 |
+
q, s = ops.quantize_activation(x.contiguous().to(torch.bfloat16))
|
| 49 |
+
return q, s
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def binary_gemm(act_q: torch.Tensor, act_scale: torch.Tensor, wq: torch.Tensor,
|
| 53 |
+
wscale: torch.Tensor, bias: Optional[torch.Tensor] = None,
|
| 54 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 55 |
+
"""int8 activations times packed binary weights -> bf16 ``[M, N]``."""
|
| 56 |
+
return ops.binary_gemm(act_q, act_scale, wq, wscale, bias, group_size)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def binary_linear(x: torch.Tensor, wq: torch.Tensor, wscale: torch.Tensor,
|
| 60 |
+
bias: Optional[torch.Tensor] = None,
|
| 61 |
+
group_size: int = GROUP) -> torch.Tensor:
|
| 62 |
+
"""One-shot forward: quantize the activation, then the packed product.
|
| 63 |
+
Trailing batch dimensions are preserved."""
|
| 64 |
+
shape = x.shape[:-1]
|
| 65 |
+
q, s = quantize_activation(x)
|
| 66 |
+
out = binary_gemm(q, s, wq, wscale, bias, group_size)
|
| 67 |
+
return out.reshape(*shape, out.shape[-1])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class BinaryLinear(torch.nn.Module):
|
| 71 |
+
"""Drop-in for a binary-weight ``nn.Linear``. Holds packed weights and their
|
| 72 |
+
group scales; nothing is stored at higher precision."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, in_features: int, out_features: int, bias: bool = False,
|
| 75 |
+
group_size: int = GROUP, device=None):
|
| 76 |
+
super().__init__()
|
| 77 |
+
assert in_features % 32 == 0, "in_features must be a multiple of 32"
|
| 78 |
+
assert in_features % group_size == 0, "in_features must be a multiple of group_size"
|
| 79 |
+
self.in_features = in_features
|
| 80 |
+
self.out_features = out_features
|
| 81 |
+
self.group_size = group_size
|
| 82 |
+
self.register_buffer(
|
| 83 |
+
"wq", torch.zeros(out_features, in_features // 32, dtype=torch.int32,
|
| 84 |
+
device=device))
|
| 85 |
+
self.register_buffer(
|
| 86 |
+
"wscale", torch.ones(out_features, in_features // group_size,
|
| 87 |
+
dtype=torch.float16, device=device))
|
| 88 |
+
self.bias = torch.nn.Parameter(
|
| 89 |
+
torch.zeros(out_features, device=device)) if bias else None
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_dense(cls, lin: torch.nn.Linear, group_size: int = GROUP) -> "BinaryLinear":
|
| 93 |
+
"""Binarize an existing linear by sign, with the group scale set to the
|
| 94 |
+
group's mean absolute weight, which is the scale that minimizes squared
|
| 95 |
+
error for a sign-quantized group."""
|
| 96 |
+
W = lin.weight.data
|
| 97 |
+
N, K = W.shape
|
| 98 |
+
m = cls(K, N, bias=lin.bias is not None, group_size=group_size,
|
| 99 |
+
device=W.device)
|
| 100 |
+
signs = torch.where(W >= 0, 1, -1).to(torch.int8)
|
| 101 |
+
m.wq.copy_(pack_weights(signs))
|
| 102 |
+
m.wscale.copy_(W.reshape(N, K // group_size, group_size).abs().mean(-1)
|
| 103 |
+
.to(torch.float16))
|
| 104 |
+
if lin.bias is not None:
|
| 105 |
+
m.bias.data.copy_(lin.bias.data)
|
| 106 |
+
return m
|
| 107 |
+
|
| 108 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
return binary_linear(x, self.wq, self.wscale, self.bias, self.group_size)
|
| 110 |
+
|
| 111 |
+
def extra_repr(self) -> str:
|
| 112 |
+
bits = 1 + 16 / self.group_size
|
| 113 |
+
return (f"in_features={self.in_features}, out_features={self.out_features}, "
|
| 114 |
+
f"group_size={self.group_size}, effective_bits={bits:.3f}")
|
build/torch213-cxx11-cu132-x86_64-linux/_binary_gemm_cuda_31ce78e.abi3.so
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c81218433482ffc1f9565ba27445822bd9e765fda89e15a6313ac62355416309
|
| 3 |
+
size 726032
|
build/torch213-cxx11-cu132-x86_64-linux/_ops.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from . import _binary_gemm_cuda_31ce78e
|
| 3 |
+
ops = torch.ops._binary_gemm_cuda_31ce78e
|
| 4 |
+
|
| 5 |
+
def add_op_namespace_prefix(op_name: str):
|
| 6 |
+
"""
|
| 7 |
+
Prefix op by namespace.
|
| 8 |
+
"""
|
| 9 |
+
return f"_binary_gemm_cuda_31ce78e::{op_name}"
|
build/torch213-cxx11-cu132-x86_64-linux/binary_gemm/__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/torch213-cxx11-cu132-x86_64-linux/metadata.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "binary-gemm",
|
| 3 |
+
"id": "_binary_gemm_cuda_31ce78e",
|
| 4 |
+
"version": 1,
|
| 5 |
+
"license": "Apache-2.0",
|
| 6 |
+
"python-depends": [],
|
| 7 |
+
"backend": {
|
| 8 |
+
"type": "cuda",
|
| 9 |
+
"archs": [
|
| 10 |
+
"10.0",
|
| 11 |
+
"12.0",
|
| 12 |
+
"8.0",
|
| 13 |
+
"8.6",
|
| 14 |
+
"8.9",
|
| 15 |
+
"9.0"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"digest": {
|
| 19 |
+
"algorithm": "sha256",
|
| 20 |
+
"files": {
|
| 21 |
+
"__init__.py": "fwn5WUojHrxLQwNl70+IAx7FZfHJQI9O+Zp5h4F+n7Y=",
|
| 22 |
+
"_binary_gemm_cuda_31ce78e.abi3.so": "yBIYQzSC/8H5VluidEWCK9nnZf2onhWmMTrGI1VBYwk=",
|
| 23 |
+
"_ops.py": "cGdYjoXrblpZs8DIqLlPArXWztTZ+wWrveEjvYq+5/0=",
|
| 24 |
+
"binary_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
|
| 25 |
+
}
|
| 26 |
+
},
|
| 27 |
+
"provenance": {
|
| 28 |
+
"kernel-builder": {
|
| 29 |
+
"version": "0.17.0-dev0",
|
| 30 |
+
"sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
|
| 31 |
+
"dirty": false
|
| 32 |
+
},
|
| 33 |
+
"kernel": {
|
| 34 |
+
"sha": "31ce78ecfe5b53cd413fd76785b351410c1f610a",
|
| 35 |
+
"dirty": false
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|