Stochastic Rounding Simulations for Ternary Quantization

This notebook provides a pure numeric simulation mapping out how Stochastic Rounding alters the transfer threshold boundary curves of ultra-low bitwidth Ternary Quantizers.

Core Algorithmic Concepts Shown in This Example

  • Ternary State Quantization Thresholds: Standard ternary operators compress full-precision continuous weights down to exactly three discrete numerical choices: ${-1, 0, 1}$ by establishing a hard threshold barrier ($T$). Values falling between $[-T, T]$ are aggressively zeroed out, while external values are clamped to their respective polar signs.

  • Stochastic Boundary Smoothing: Instead of using rigid deterministic cutoff lines, this algorithm samples from a continuous uniform random distribution np.random.uniform(0, 1). Near the quantization decision threshold boundaries, weights have a statistical probability of rounding up or down depending on how close they are to the barrier. Running this over multiple iterations transforms rough, step-like step transformations into a smooth, continuous probabilistic curve, preserving small updates during training.

1. Environment Setup & Dependency Installation

Uncomment the cell below if you need to install the required libraries inside your runtime environment.

# !pip install qkeras-v3 keras tensorflow matplotlib

2. Configure Visualization Backend and Rounding Functions

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import keras.ops.numpy as knp

def _stochastic_rounding(x, precision, resolution, delta):
    """Performs probabilistic stochastic rounding transitions along boundary regions."""
    delta_left = delta - precision
    delta_right = delta + precision
    scale = 1 / resolution
    scale_delta_left = delta_left * scale
    scale_delta_right = delta_right * scale
    scale_2_delta = scale_delta_right - scale_delta_left
    scale_x = x * scale
    fraction = scale_x - scale_delta_left

    # Generate probabilistic uniform array mapping
    random_selector = np.random.uniform(0, 1, size=x.shape) * scale_2_delta

    result = knp.where(
        fraction < random_selector, scale_delta_left / scale, scale_delta_right / scale
    )
    return result

def _ternary(x, sto=False):
    """Maps values to ternary values {-1, 0, 1}, optionally applying stochastic boundaries."""
    m = knp.amax(knp.abs(x), keepdims=True)
    scale = 2 * m / 3.0
    thres = scale / 2.0

    if sto:
        sign_bit = knp.sign(x)
        x = knp.abs(x)
        x = (
            sign_bit
            * scale
            * _stochastic_rounding(
                x / scale,
                precision=0.3,
                resolution=0.01,
                delta=thres / scale,
            )
        )
    return knp.where(knp.abs(x) < thres, knp.zeros_like(x), knp.sign(x))
/Users/mariuskoppel/cms/qkeras/venv/lib/python3.11/site-packages/keras/src/export/tf2onnx_lib.py:8: FutureWarning: In the future `np.object` will be defined as the corresponding NumPy scalar.
  if not hasattr(np, "object"):

3. Run Monte Carlo Statistical Simulations & View Curves

# Generate uniformly distributed test values
x = np.random.uniform(-10.0, 10.0, size=1000)
x = knp.sort(x)

tr = knp.zeros_like(x)
t = knp.zeros_like(x)
iter_count = 500

print(f"Simulating {iter_count} sampling iterations over step distributions...")
for _ in range(iter_count):
    y = _ternary(x, sto=False)
    yr = _ternary(x, sto=True)
    t = t + y
    tr = tr + yr

# Display plot mappings
plt.figure(figsize=(10, 6))
plt.plot(x, t / iter_count, label="Standard Deterministic Ternary", linewidth=2)
plt.plot(x, tr / iter_count, label="Stochastic Rounded Ternary", linestyle="--", linewidth=2)
plt.xlabel("Input Feature Range ($x$)")
plt.ylabel(f"Mean Expected Response Value ({iter_count} samples)")
plt.title("Ternary Quantization Transition Profile")
plt.grid(True, alpha=0.4)
plt.legend(loc="upper left")
plt.show()
Simulating 500 sampling iterations over step distributions...
../_images/f9c85c28a2cab399105f2371e9c539b4c7579cdbaa7945afbe8b5493b1c29d99.png