Risrit1121's picture
Upload folder using huggingface_hub
283ba4a verified
Raw
History Blame Contribute Delete
5.82 kB
# Copyright © 2023 Apple Inc.
import math
from typing import Any, Optional
import mlx.core as mx
from mlx.nn.layers.base import Module
from mlx.nn.layers.quantized import QQLinear, QuantizedLinear
class Identity(Module):
r"""A placeholder identity operator that is argument-insensitive.
Args:
args: any argument (unused)
kwargs: any keyword argument (unused)
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__()
def __call__(self, x: mx.array) -> mx.array:
return x
class Linear(Module):
r"""Applies an affine transformation to the input.
Concretely:
.. math::
y = x W^\top + b
where:
where :math:`W` has shape ``[output_dims, input_dims]`` and :math:`b` has shape ``[output_dims]``.
The values are initialized from the uniform distribution :math:`\mathcal{U}(-{k}, {k})`,
where :math:`k = \frac{1}{\sqrt{D_i}}` and :math:`D_i` is equal to ``input_dims``.
Args:
input_dims (int): The dimensionality of the input features
output_dims (int): The dimensionality of the output features
bias (bool, optional): If set to ``False`` then the layer will
not use a bias. Default is ``True``.
"""
def __init__(self, input_dims: int, output_dims: int, bias: bool = True) -> None:
super().__init__()
scale = math.sqrt(1.0 / input_dims)
self.weight = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims, input_dims),
)
if bias:
self.bias = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims,),
)
def _extra_repr(self) -> str:
return f"input_dims={self.weight.shape[1]}, output_dims={self.weight.shape[0]}, bias={'bias' in self}"
def __call__(self, x: mx.array) -> mx.array:
if "bias" in self:
x = mx.addmm(self["bias"], x, self["weight"].T)
else:
x = x @ self["weight"].T
return x
def to_quantized(
self,
group_size: Optional[int] = None,
bits: Optional[int] = None,
mode: str = "affine",
quantize_input: bool = False,
):
"""Return a quantized approximation of this layer.
If ``quantize_input`` is ``False``, returns a :obj:`QuantizedLinear`
(weights are quantized). If ``quantize_input`` is ``True``, returns
a :obj:`QQLinear` (weights and activations are quantized).
Args:
group_size (Optional[int]): The quantization group size (see
:func:`mlx.core.quantize`). Default: ``None``.
bits (Optional[int]): The number of bits per parameter (see
:func:`mlx.core.quantize`). Default: ``None``.
mode (str): The quantization method to use (see
:func:`mlx.core.quantize`). Default: ``"affine"``.
quantize_input (bool): Whether to quantize input. Default: ``False``.
Returns:
QuantizedLinear or QQLinear: A quantized version of this layer.
Notes:
Quantized input is only supported for ``"nvfp4"`` and ``"mxfp8"``
modes.
"""
if quantize_input:
if mode not in ["nvfp4", "mxfp8"]:
raise ValueError(
f"Quantized activations are only supported for 'nvfp4' and 'mxfp8' modes, got {mode}."
)
return QQLinear.from_linear(self, group_size, bits, mode)
return QuantizedLinear.from_linear(self, group_size, bits, mode)
class Bilinear(Module):
r"""Applies a bilinear transformation to the inputs.
Concretely:
.. math::
y_i = x_1^\top W_i x_2 + b_i
where:
:math:`W` has shape ``[output_dims, input1_dims, input2_dims]``, :math:`b` has shape ``[output_dims ]``,
and :math:`i` indexes the output dimension.
The values are initialized from the uniform distribution :math:`\mathcal{U}(-{k}, {k})`,
where :math:`k = \frac{1}{\sqrt{D_1}}` and :math:`D_1` is ``input1_dims``.
Args:
input1_dims (int): The dimensionality of the input1 features
input2_dims (int): The dimensionality of the input2 features
output_dims (int): The dimensionality of the output features
bias (bool, optional): If set to ``False`` then the layer will
not use a bias. Default is ``True``.
"""
def __init__(
self, input1_dims: int, input2_dims: int, output_dims: int, bias: bool = True
) -> None:
super().__init__()
scale = math.sqrt(1.0 / input1_dims)
self.weight = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims, input2_dims, input1_dims),
)
if bias:
self.bias = mx.random.uniform(
low=-scale,
high=scale,
shape=(output_dims,),
)
def _extra_repr(self) -> str:
out, in2, in1 = self.weight.shape
return (
f"input1_dims={in1}, input2_dims={in2}, output_dims={out}, "
f"bias={'bias' in self}"
)
def __call__(self, x1: mx.array, x2: mx.array) -> mx.array:
# Normalize shapes
out, in2, in1 = self.weight.shape
xshape = x1.shape[:-1]
x1 = x1.reshape(-1, in1)
x2 = x2.reshape(-1, 1, in2)
# Perform the bilinear transformation
w = self.weight.reshape(out * in2, in1)
y = x1 @ w.T
y = y.reshape(-1, out, in2).swapaxes(-2, -1)
y = x2 @ y
y = y.squeeze(1)
# Reset the shape
y = y.reshape(*xshape, out)
# Apply the bias
if "bias" in self:
y = y + self.bias
return y