latest (dev)
Copy
Latest development documentation · Updated 2026-10-15
Source code for tensorplay.ao.nn.quantized.activation
"""Quantized activation modules.
Each module computes its activation on the dequantized values of an affine
Int8 tensor and requantizes into declared output qparams, except relu-style
ops whose grid is order-preserving, so the output inherits the input qparams.
"""
from __future__ import annotations
import tensorplay
from tensorplay import nn
from tensorplay import _C
from tensorplay._C import (
quantized_elu as _quantized_elu,
quantized_hardswish as _quantized_hardswish,
quantized_hardsigmoid as _quantized_hardsigmoid,
quantized_leaky_relu as _quantized_leaky_relu,
quantized_prelu as _quantized_prelu,
quantized_relu as _quantized_relu,
quantized_relu6 as _quantized_relu6,
quantized_sigmoid as _quantized_sigmoid,
quantized_softmax as _quantized_softmax,
quantized_tanh as _quantized_tanh,
)
__all__ = [
"ReLU",
"ReLU6",
"ELU",
"LeakyReLU",
"Hardswish",
"Hardsigmoid",
"Sigmoid",
"Tanh",
"Softmax",
"PReLU",
]
def _activation_qparams(mod, scale, zero_point):
"""Resolve (scale, zero_point) from explicit values or a calibrated
``activation_post_process`` attribute on ``mod``."""
if scale is not None and zero_point is not None:
return float(scale), int(zero_point)
post_process = getattr(mod, "activation_post_process", None)
if post_process is None:
raise ValueError(
f"{type(mod).__name__} requires scale and zero_point, or a "
"calibrated activation_post_process")
s, z = post_process.calculate_qparams()
return float(s), int(z)
[docs]
class ReLU(nn.ReLU):
"""Rectified linear unit on a quantized tensor.
The output carries the input scale and zero point: the negative
half-space of the affine grid is exactly the code segment below the zero
point, so the op is an integer maximum.
"""
def forward(self, input):
return _quantized_relu(input)
def _get_name(self):
return "QuantizedReLU"
@staticmethod
def from_float(mod, use_precomputed_fake_quant=False):
return ReLU(mod.inplace)
[docs]
class ReLU6(nn.ReLU6):
"""Rectified linear unit clamped at 6 on a quantized tensor.
The output carries the input scale and zero point; the upper bound is the
grid position of the real value 6.
"""
def forward(self, input):
return _quantized_relu6(input)
def _get_name(self):
return "QuantizedReLU6"
@staticmethod
def from_float(mod, use_precomputed_fake_quant=False):
return ReLU6(mod.inplace)
[docs]
class ELU(nn.ELU):
"""Exponential linear unit on a quantized tensor.
Args:
scale: quantization scale of the output tensor
zero_point: quantization zero point of the output tensor
alpha: the alpha constant
"""
def __init__(self, scale, zero_point, alpha=1.0):
super().__init__(alpha)
self.scale = scale
self.zero_point = zero_point
self.alpha = alpha
def forward(self, input):
scale, zero_point = _activation_qparams(self, self.scale, self.zero_point)
return _quantized_elu(
input, scale, zero_point, alpha=self.alpha)
def _get_name(self):
return "QuantizedELU"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = mod.activation_post_process.calculate_qparams()
return cls(float(scale), int(zero_point), mod.alpha)
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(float(scale), int(zero_point), mod.alpha)
[docs]
class LeakyReLU(nn.LeakyReLU):
"""Leaky rectified linear unit on a quantized tensor.
Values below zero are multiplied by ``negative_slope``; the result is
requantized into the declared output qparams.
Args:
scale: quantization scale of the output tensor
zero_point: quantization zero point of the output tensor
negative_slope: slope applied to negative values
inplace: accepted for interface compatibility; the computation is
always out-of-place
"""
def __init__(self, scale, zero_point, negative_slope=1e-2, inplace=False):
super().__init__(negative_slope, inplace)
self.scale = scale
self.zero_point = zero_point
def forward(self, input):
scale, zero_point = _activation_qparams(self, self.scale, self.zero_point)
return _quantized_leaky_relu(input, self.negative_slope, scale, zero_point)
def _get_name(self):
return "QuantizedLeakyReLU"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = mod.activation_post_process.calculate_qparams()
return cls(float(scale), int(zero_point), mod.negative_slope, mod.inplace)
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(float(scale), int(zero_point), mod.negative_slope, mod.inplace)
[docs]
class Hardswish(nn.Hardswish):
"""HardSwish activation on a quantized tensor.
Computes ``x * clamp(x + 3, 0, 6) / 6`` in the dequantized domain and
requantizes into the declared output qparams.
Args:
scale: quantization scale of the output tensor
zero_point: quantization zero point of the output tensor
"""
def __init__(self, scale, zero_point):
super().__init__()
self.scale = scale
self.zero_point = zero_point
def forward(self, input):
scale, zero_point = _activation_qparams(self, self.scale, self.zero_point)
return _quantized_hardswish(input, scale, zero_point)
def _get_name(self):
return "QuantizedHardswish"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = mod.activation_post_process.calculate_qparams()
return cls(float(scale), int(zero_point))
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(float(scale), int(zero_point))
[docs]
class Hardsigmoid(nn.Hardsigmoid):
"""HardSigmoid activation on a quantized tensor.
Computes ``clamp(x / 6 + 1 / 2, 0, 1)`` in the dequantized domain and
requantizes into the declared output qparams.
Args:
scale: quantization scale of the output tensor
zero_point: quantization zero point of the output tensor
"""
def __init__(self, scale, zero_point):
super().__init__()
self.scale = scale
self.zero_point = zero_point
def forward(self, input):
scale, zero_point = _activation_qparams(self, self.scale, self.zero_point)
return _quantized_hardsigmoid(input, scale, zero_point)
def _get_name(self):
return "QuantizedHardSigmoid"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = mod.activation_post_process.calculate_qparams()
return cls(float(scale), int(zero_point))
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(float(scale), int(zero_point))
[docs]
class Sigmoid(nn.Sigmoid):
"""Logistic sigmoid on a quantized tensor.
Args:
scale: quantization scale of the output tensor
zero_point: quantization zero point of the output tensor
"""
def __init__(self, scale, zero_point):
super().__init__()
self.scale = scale
self.zero_point = zero_point
def forward(self, input):
scale, zero_point = _activation_qparams(self, self.scale, self.zero_point)
return _quantized_sigmoid(input, scale, zero_point)
def _get_name(self):
return "QuantizedSigmoid"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = mod.activation_post_process.calculate_qparams()
return cls(float(scale), int(zero_point))
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(float(scale), int(zero_point))
[docs]
class Tanh(nn.Tanh):
"""Hyperbolic tangent on a quantized tensor.
Args:
scale: quantization scale of the output tensor
zero_point: quantization zero point of the output tensor
"""
def __init__(self, scale, zero_point):
super().__init__()
self.scale = scale
self.zero_point = zero_point
def forward(self, input):
scale, zero_point = _activation_qparams(self, self.scale, self.zero_point)
return _quantized_tanh(input, scale, zero_point)
def _get_name(self):
return "QuantizedTanh"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = mod.activation_post_process.calculate_qparams()
return cls(float(scale), int(zero_point))
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(float(scale), int(zero_point))
class Softmax(nn.Softmax):
"""Softmax on a quantized tensor, requantized to declared qparams."""
def __init__(self, dim=None, scale=1.0, zero_point=0):
super().__init__(dim=dim)
self.scale = scale
self.zero_point = zero_point
def forward(self, input):
dim = self.dim
if dim is None:
dim = input.dim() - 1
return _quantized_softmax(input, dim, self.scale, self.zero_point)
def _get_name(self):
return "QuantizedSoftmax"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = _activation_qparams(mod, None, None)
return cls(mod.dim, scale, zero_point)
@classmethod
def from_reference(cls, mod, scale, zero_point):
return cls(mod.dim, float(scale), int(zero_point))
class PReLU(nn.Module):
"""Parametric rectifier on a quantized tensor with a quantized slope."""
def __init__(self, output_scale, output_zero_point, num_parameters=1):
super().__init__()
self.num_parameters = num_parameters
self.scale = output_scale
self.zero_point = output_zero_point
w = tensorplay.randn(num_parameters, dtype=tensorplay.float32)
qw = tensorplay.quantize_per_tensor(w, 1.0, 0, tensorplay.quint8)
self.set_weight(qw)
def set_weight(self, w):
self.weight = w
def forward(self, input):
return _quantized_prelu(input, self.weight, self.scale, self.zero_point)
def _get_name(self):
return "QuantizedPReLU"
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
scale, zero_point = _activation_qparams(mod, None, None)
result = cls(float(scale), int(zero_point), mod.num_parameters)
float_wt = mod.weight.float()
observer = mod.qconfig.weight()
observer(float_wt)
wt_scale, wt_zp = observer.calculate_qparams()
qweight = tensorplay.quantize_per_tensor(float_wt, float(wt_scale), int(wt_zp), tensorplay.quint8)
result.set_weight(qweight)
return result
@classmethod
def from_reference(cls, mod, scale, zero_point):
result = cls(float(scale), int(zero_point), mod.num_parameters)
float_wt = mod.weight.float()
observer = mod.qconfig.weight()
observer(float_wt)
wt_scale, wt_zp = observer.calculate_qparams()
qweight = tensorplay.quantize_per_tensor(float_wt, float(wt_scale), int(wt_zp), tensorplay.quint8)
result.set_weight(qweight)
return result
class MultiheadAttention(nn.Module):
"""Quantized multihead attention converted from an observed module.
The projection layers are native quantized linear modules; the attention
output is requantized with the module's declared output qparams.
"""
_FLOAT_MODULE = None
def __init__(self, linear_Q, linear_K, linear_V, out_proj, scale, zero_point,
batch_first=False):
super().__init__()
self.linear_Q = linear_Q
self.linear_K = linear_K
self.linear_V = linear_V
self.out_proj = out_proj
self.scale = scale
self.zero_point = zero_point
self.embed_dim = linear_Q.out_features
self.num_heads = getattr(linear_Q, "_num_heads", 1)
self.batch_first = batch_first
def _get_name(self):
return "QuantizedMultiheadAttention"
def forward(self, query, key, value, key_padding_mask=None, need_weights=True,
attn_mask=None, average_attn_weights=True, is_causal=False):
if is_causal:
raise AssertionError("causal mask not supported by AO MHA module")
if self.training:
raise AssertionError("quantized MHA can only be used in eval mode")
def run_linear(linear, x):
if not x.is_quantized():
x = _C.quantize_per_tensor(x, 1.0, 0, tensorplay.qint8)
return linear(x).dequantize()
q = run_linear(self.linear_Q, query)
k = run_linear(self.linear_K, key)
v = run_linear(self.linear_V, value)
# Normalize to (N, L, E).
if not self.batch_first:
q = q.transpose(0, 1)
k = k.transpose(0, 1)
v = v.transpose(0, 1)
head_dim = self.embed_dim // self.num_heads
scaling = float(head_dim) ** -0.5
# Reshape to (N, H, L, D).
q = q.reshape(q.size(0), q.size(1), self.num_heads, head_dim).transpose(1, 2)
k = k.reshape(k.size(0), k.size(1), self.num_heads, head_dim).transpose(1, 2)
v = v.reshape(v.size(0), v.size(1), self.num_heads, head_dim).transpose(1, 2)
attn = tensorplay.matmul(q, k.transpose(-2, -1)) * scaling
if attn_mask is not None:
if attn_mask.dim() == 2:
attn_mask = attn_mask.unsqueeze(0).unsqueeze(0)
attn = attn + attn_mask
if key_padding_mask is not None:
attn = attn.masked_fill(key_padding_mask.unsqueeze(1).unsqueeze(2), float("-inf"))
attn = tensorplay.softmax(attn, dim=-1)
attn_output = tensorplay.matmul(attn, v).transpose(1, 2).reshape(q.size(0), q.size(2), self.embed_dim)
from tensorplay.nn import functional as F
out_w = self.out_proj.weight().dequantize() if self.out_proj.weight().is_quantized() else self.out_proj.weight()
attn_output = F.linear(attn_output, out_w, self.out_proj.bias)
attn_output = _C.quantize_per_tensor(attn_output, self.scale, self.zero_point, tensorplay.qint8)
if need_weights:
attn_weights = attn.mean(dim=1) if average_attn_weights else attn
return attn_output, attn_weights
return attn_output, None
@classmethod
def from_float(cls, mod, use_precomputed_fake_quant=False):
raise NotImplementedError(
"It looks like you are trying to convert a non-observed MHA module. "
"Please use the quantizable MHA with the eager quantization workflow."
)
@classmethod
def from_observed(cls, other):
from tensorplay.ao.quantization import convert
converted = convert(other, inplace=False, remove_qconfig=True)
post = getattr(other, "activation_post_process", None)
if post is None:
post = getattr(other.out_proj, "activation_post_process", None)
if post is None:
scale, zero_point = 1.0, 0
else:
scale, zero_point = post.calculate_qparams()
result = cls(converted.linear_Q, converted.linear_K, converted.linear_V,
converted.out_proj, float(scale), int(zero_point),
batch_first=getattr(other, "batch_first", False))
result.eval()
return resultHelp improve this page
Found an error, an unclear step, or a missing example?
Was this page helpful?

