File size: 17,072 Bytes
73072fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# METHODOLOGY: W4A8 Quantization Pipeline

This document details the technical steps, tools, and configurations used to generate the 12 quantized models optimized for the **Snapdragon X Elite** NPU.

---

## 1. Architectural Overview
The target models are:
- **MobileNet V4-S (Scale)**: Lightweight model optimized for high-speed metrology.
- **MobileNet V4-L (Rocks)**: Large model for higher geological precision.
- **MobileNet V5-300M (Rocks)**: Latest MobileNet architecture utilizing GELU activations and optimized bottleneck layers (analogous to the Gemma 3 Nano vision encoder).

---

## 2. Quantization Formats & Zero-FP32 Policy
Two specific formats were produced for each architecture to support the tiered deployment strategy, governed by a strict **Zero-FP32 Hardware Constraint**:
1.  **INT8 Full Integer (A8W8)**: 8-bit Activations, 8-bit Weights. Internal operations are pure integer kernels (`QLinearConv`, `QLinearMul`, `QLinearAdd`). Inputs and Outputs forced to INT8.
2.  **W4A8 Full Integer (A8W4)**: 8-bit Activations, 4-bit Weights (packed sub-byte integers). Inputs and Outputs forced to INT8 with 0 dynamic float32 dequantization layers.

> [!IMPORTANT]
> **Zero-FP32 Qualcomm Hexagon HTP Requirement**:
> Qualcomm Hexagon HTP accelerators immediately reject or fracture graphs containing mixed floating-point tensors in the convolutional backbone. We enforce:
> - **ONNX**: Exporté via `onnxruntime.quantization.quantize_static` avec `QuantFormat.QDQ` (`QuantType.QInt8` / `QuantType.QUInt4`), combiné à la transformation post-traitement `force_full_integer_io`. Cette dernière élimine les nœuds d'entrée/sortie `QuantizeLinear` et `DequantizeLinear` pour exposer un graphe dont toutes les entrées/sorties et tenseurs intermédiaires sont strictement des entiers (`int8_t`). Ce format QDQ élagué est directement compilé en nœuds matériels natifs sans casting flottant.

> - **TFLite**: Full Integer compilation where `supported_ops = [TFLITE_BUILTINS_INT8]` and all depthwise/convolutional layers are bound to integer kernels with 0 FP32 buffers (0.0% float tensors).

---

## 3. Mathematical Foundations

The quantization uses **linear symmetric/asymmetric** mapping. The relationship between a floating-point value $q_f$ and its quantized integer value $q_i$ is:

$$q_f = \text{scale} \times (q_i - \text{zero\_point})$$



Where:

- **Scale**: Floating-point scaling factor.

- **Zero Point**: Integer offset (typically 0 or -128 for INT8).



For **W4A8** models:

- **Weights (W4)**: Weights are grouped and quantized block-wise to minimize precision loss in deep convolutional layers.

- **Activations (A8)**: Activations use static calibration (observed min-max) to minimize saturation of outlier values.



### MobileNet V4 (S & L) Pipeline:

- **Tool**: `ai-edge-quantizer` (Google).

- **Process**: 

  - Conversion from `SavedModel` to FP32 TFLite with Signatures.

  - Static calibration on 100 representative geological samples.

  - Export with `Full Integer` flag to ensure zero floating-point operators.

- **I/O Optimization**: Used the `ai-edge-quantizer` API to specify `input_type=int8` and `output_type=int8`.



### MobileNet V5 Specifics:

- **Challenge**: Presence of `GELU` (Erf) operations and memory-intensive tracking of 300M parameters.

- **NPU Solution**: Implemented **Surgical Graph Optimization** to replace `Erf` with a polynomial approximation ($x \times \sigma(1.702 \times x)$). 

- **Graph Freezing**: To ensure the model is fully compatible with LiteRT without triggering `_DictWrapper` or `untracked resource` errors during conversion, all 294M variables were **frozen into constants** directly in the graph definition.
- **4-bit Quantization**: Executed via the `ai-edge-quantizer` compression API, allowing weight reduction to 4-bits while maintaining 8-bit activations for NPU compatibility. This results in a "No-Flex" artifact where 100% of the operators are natively supported by the NPU.

---

## 4. ONNX Quantization Details (Static QDQ & Stripped Full Integer I/O)

### General Pipeline:
`FP32 ONNX` -> `ONNX Runtime Static Quantization (QuantFormat.QDQ)` -> `force_full_integer_io (Stripping FP32 Casts)`.

The quantization workflow uses `onnxruntime.quantization.quantize_static` with `QuantFormat.QDQ`:
- **QuantFormat.QDQ Pipeline**: Activations and weights are quantized using `QuantizeLinear` and `DequantizeLinear` pairs configured for `QuantType.QInt8` (or `QUInt4` for 4-bit weights).
- **Post-Processing (Full Integer I/O Stripping)**: Standard QDQ models expose FP32 inputs and outputs. To eliminate CPU-bound float conversion layers on edge devices, the custom routine `force_full_integer_io` rewrites the model graph by removing boundary quantize/dequantize nodes and updating graph signatures to native `int8_t` inputs/outputs.
- **NPU Compatibility**: The resulting graph presents pure integer boundaries directly consumable by execution providers (such as QNN Execution Provider) with zero partition breaks.

### Calibration Data:
- **Scale**: `calib_echelle.npy` (100 samples, NCHW [1, 3, 1024, 1024]).
- **Rocks V4**: `calib_roches_244.npy` (100 samples, NCHW [1, 3, 244, 244]).
- **Rocks V5**: `calib_roches_256.npy` (100 samples, NCHW [1, 3, 256, 256], transposed to NHWC).

### Forcing Full Integer I/O:
Standard ONNX tools insert `QuantizeLinear` (input) and `DequantizeLinear` (output) nodes that expect floats. To eliminate CPU-bound type casting:
- A custom post-processing script stripped these boundary nodes.
- Graph metadata was manually adjusted to declare `INT8` as the native I/O type.

### Calibration Dataset Preparation
To ensure bit-accurate min-max profiling, we prepared a calibration dataset of 100 representative samples. The following script was used to generate the `.npy` tensors:

```python

import os, glob

import numpy as np

from PIL import Image



def prepare_calib(image_dir, output_path, size=(256, 256), num_samples=100):

    all_files = []

    for ext in ['*.jpg', '*.png', '*.jpeg']:

        all_files.extend(glob.glob(os.path.join(image_dir, "**", ext), recursive=True))

    

    np.random.seed(42)

    np.random.shuffle(all_files)

    selected = all_files[:num_samples]

    

    images = []

    for f in selected:

        img = Image.open(f).convert('RGB').resize(size, Image.Resampling.LANCZOS)

        img_data = np.array(img).astype(np.float32) / 255.0

        # NCHW format

        img_data = np.transpose(img_data, (2, 0, 1)) 

        images.append(img_data)

            

    np.save(output_path, np.stack(images))



# Usage for MobileNetV5 (256x256)

# prepare_calib("path/to/images", "calib_roches_256.npy", size=(256, 256))

```

---

## 5. Calibration & Preprocessing

### Data Selection:
We selected **100 images** per model from the validation datasets.
- **Representativeness**: Images cover various lighting conditions and rock textures to ensure the `min/max` calculation during calibration is robust (minimizing KL Divergence).
- **Source Format**: `.npy` (Numpy) files containing normalized tensors.

| Parameter | Scale V4-S | Rocks V4-L | Rocks V5 |
| :--- | :--- | :--- | :--- |
| **Resolution** | **1024 x 1024** | 244 x 244 | 256 x 256 |
| **ONNX Format** | NCHW (1,3,1024,1024) | NCHW (1,3,244,244) | **NHWC** (1,256,256,3) |
| **Normalization** | [0, 1] | [0, 1] | [0, 1] |

---

## 6. Edge Hardware Optimization & Portability

While benchmarks were conducted on the Snapdragon X Elite, the optimization strategy was designed for **maximum cross-platform portability**:
- **W4A8 Format Selection**: The **W4A8** format was chosen over more specialized formats (like W4A16) to ensure the widest possible compatibility across various Edge AI runtimes (LiteRT, ONNX Runtime) and hardware accelerators (NPUs from multiple vendors, modern GPUs). This ensures a consistent performance baseline across the Windows ARM64 Copilot+ ecosystem.
- **Full Integer I/O**: Without this, the CPU would perform a "Casting" overhead for every frame. By forcing INT8, the data flow remains entirely in the integer domain from the sensor to the final prediction, significantly reducing thermal throttling risks on mobile devices.

---

## 7. Troubleshooting

### A. Erf / GELU (MobileNet V5)
`Erf` is not natively supported by most 8-bit accelerators.
- **Solution**: Removed `SELECT_TF_OPS` in favor of a native subgraph approximation. This ensures that the Qualcomm Hexagon NPU can execute the entire model without falling back to the CPU for activation kernels.

### B. Mismatch in Dimensions
V4 and V5 models use different conventions (NCHW vs NHWC).
- **Solution**: Implemented a dynamic `transpose=(0, 2, 3, 1)` in the `CalibrationDataReader` to adapt on-the-fly without modifying source `.npy` files.

---

## 8. Technical Implementation (Python API)

### A. TFLite Quantization (LiteRT)
Using `ai-edge-quantizer`:

#### 1. Rock Classification (MobileNetV5 - Gemma 3 Nano)
```python

import tensorflow as tf

from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2



def export_to_noflex_tflite(keras_model, output_path):

    # 1. Capture model in a concrete function

    @tf.function(input_signature=[tf.TensorSpec([1, 256, 256, 3], tf.float32, name="input_1")])

    def serve(x):

        return {"output_0": keras_model(x, training=False)}



    # 2. Freeze 300M variables into constants (No-Flex requirement)

    frozen_func = convert_variables_to_constants_v2(serve.get_concrete_function())

    

    # 3. Convert via SavedModel with explicit signatures

    temp_sm = "temp_sm"

    tf.saved_model.save(tf.Module(), temp_sm, signatures={"serving_default": frozen_func})

    

    converter = tf.lite.TFLiteConverter.from_saved_model(temp_sm)

    converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]

    tflite_model = converter.convert()

    

    with open(output_path, "wb") as f:

        f.write(tflite_model)

```

#### 2. W4A8 Compression (Weights 4-bit)
Using the `ai_edge_quantizer` API for high-density compression:
```python

compression_config = ai_edge_quantizer.CompressionConfig(

    weight_bits=4,

    activation_bits=8,

    asymmetric=True

)

quantizer.export(

    output_path,

    compression_config=compression_config,

    input_type=ai_edge_quantizer.DType.INT8,

    output_type=ai_edge_quantizer.DType.INT8

)

```

---

### B. Custom Graph Stripping (ONNX Full Integer)
Python logic used to prune dequantization artifacts and force INT8 I/O:

```python

import onnx



def force_full_integer_io(model_path):

    m = onnx.load(model_path)

    g = m.graph

    # Input Node Processing

    inp = g.input[0]

    for n in list(g.node):

        if n.op_type == 'QuantizeLinear' and inp.name in n.input:

            out_name = n.output[0]

            g.node.remove(n)

            for c in g.node:

                for i, in_c in enumerate(c.input):

                    if in_c == out_name: c.input[i] = inp.name

            inp.type.tensor_type.elem_type = 3 # INT8

            break

    # Output Node Processing

    out = g.output[0]

    for n in list(g.node):

        if n.op_type == 'DequantizeLinear' and out.name in n.output:

            in_name = n.input[0]

            g.node.remove(n)

            for p in g.node:

                for i, out_p in enumerate(p.output):

                    if out_p == in_name: p.output[i] = out.name

            out.type.tensor_type.elem_type = 3 # INT8

            break

    onnx.save(m, model_path)

```

---

## 9. Final I/O Specifications
To ensure bit-accurate inference on edge accelerators, all models adhere to the following I/O contracts.

| Model | Format | Input Shape | Output Shape | Axis Order |
| :--- | :--- | :--- | :--- | :--- |
| **Scale V4-S** | TFLite | `[1, 1024, 1024, 3]` | `[1, 1]` | NHWC |
| **Scale V4-S** | ONNX | `[1, 3, 1024, 1024]` | `[1, 1]` | NCHW |
| **Rocks V4-L** | TFLite | `[1, 256, 256, 3]` | `[1, 104]` | NHWC |
| **Rocks V4-L** | ONNX | `[1, 3, 256, 256]` | `[1, 104]` | NCHW |
| **Rocks V5** | TFLite | `[1, 256, 256, 3]` | `[1, 104]` | **NHWC** |
| **Rocks V5** | ONNX | `[1, 256, 256, 3]` | `[1, 104]` | NHWC |

### Input Normalization
All inputs are expected as **Signed INT8** tensors in the range `[-128, 127]`.
```python

# Formula for bit-accurate preprocessing (NumPy)

# Maps [0, 1] range to [-128, 127] signed INT8

input_int8 = (pixel_float * 255 - 128).astype(np.int8)

```

---

## 10. Advanced Inference: 21-Pass Multi-Scale Tiling (21-MST)
To prevent the loss of micro-mineralogical information (like small crystals) during downscaling to 256x256, the Lithotheque engine implements a **21-Pass Multi-Scale Tiling** strategy. 

Instead of a single prediction, the model evaluates 21 distinct crops of the high-resolution input:
- **1 Global Pass**: Full resize (20% weight).
- **4 Medium Passes**: 2x2 grid crops (30% total weight).
- **16 Fine Passes**: 4x4 grid crops (50% total weight).

**[Reference Python Implementation (scripts/litho_reference_engine.py)](scripts/litho_reference_engine.py)**

---

## 11. Confidence Calibration: Super-Selectivity Matrix

To ensure consistent behavior across diverse hardware tiers and quantization formats, the inference engine applies a dynamic **Super-Selectivity Matrix** during post-processing. This matrix adjusts the Softmax temperature (multiplier) based on the model's `outputScale`, compensating for precision loss in highly compressed (Legacy/Balanced) or ultra-precise (Premium) artifacts.

| Tier / Model Profile | Scale Threshold | Multiplier | Rationale |
| :--- | :--- | :---: | :--- |
| **Premium / App Internal** | `< 0.0003` | **1.500f** | High precision, slight sharpening of peaks. |
| **Standard Rocks** | `< 0.0050` | **1.200f** | Balanced calibration for INT8. |
| **Probe INT8** | `< 0.0100` | **1.000f** | Identity (raw model selectivity). |
| **Balanced Rocks** | `< 0.0240` | **0.800f** | Compensates for INT4 quantization noise. |
| **Legacy Rocks** | `< 0.0300` | **0.700f** | Maximum smoothing for entry-level devices. |
| **Premium Scale** | `< 0.0540` | **5.000f** | High selectivity for geometric metrology. |
| **Std/Legacy Scale** | `< 0.0600` | **4.500f** | Metric precision for fallback tiers. |
| **Default / Unknown** | `else` | **4.000f** | Safety net for new/unidentified models. |

---

## 12. Technical Reproduction Scripts

> [!NOTE]
> **Methodological Blueprint & Adaptability**
> The scripts provided in the `scripts/` directory serve as a technical blueprint. The specific resolutions (e.g., 256x256, 1024x1024), axis orderings, and normalization logic used are **arbitrary examples** based on the Lithotheque engine. Researchers are expected to **adapt these scripts** (specifically input dimensions and normalization layers) to match their own specific models and datasets.

For full scientific reproducibility, the following specialized automation scripts are provided in the `scripts/` directory:

### TFLite / LiteRT (ai-edge-quantizer)
- **MobileNetV5 Rocks**: `tflite_v5_rocks_int8.py` / `tflite_v5_rocks_int4.py`
- **MobileNetV4-L Rocks**: `tflite_v4l_rocks_int8.py` / `tflite_v4l_rocks_int4.py`
- **MobileNetV4-S Scale**: `tflite_v4s_scale_int8.py` / `tflite_v4s_scale_int4.py`

### ONNX (onnxruntime-quantization)
- **MobileNetV5 Rocks**: `onnx_v5_rocks_int8.py` / `onnx_v5_rocks_int4.py`
- **MobileNetV4-L Rocks**: `onnx_v4l_rocks_int8.py` / `onnx_v4l_rocks_int4.py`
- **MobileNetV4-S Scale**: `onnx_v4s_scale_int8.py` / `onnx_v4s_scale_int4.py`

### Multi-Vendor AOT Compilation & Benchmarking
- **AOT Compilers**: `compile_qairt_aot.py`, `compile_tensor_aot.py`, `compile_mediatek_aot.py`, `compile_intel_aot.py`
- **NPU Benchmark Harnesses**: `benchmark_npu_onnx.py`, `benchmark_npu_qairt.py`, `benchmark_snapdragon_native_binaries.py`

---

## 13. Replication Environment

### Software Stack
- **Python**: Python 3.11+ (x64 & ARM64)
- **Frameworks**: `tensorflow`, `ai-edge-quantizer`, `ai-edge-litert`, `onnxruntime-quantization`, `torch`, `torchao`.
- **Compiler / Runtimes**: MSVC, CUDA Toolkit, cuDNN, Qualcomm QAIRT SDK.

### Hardware (Generation & Quantization Workstation)
- **GPU**: NVIDIA Blackwell Architecture GPU (Compute Capability `sm_120`, GDDR7)
- **CPU**: Multi-core x86_64 Host Processor (AVX2)

- **RAM**: High-capacity System Memory (32 GB+)

- **OS**: 64-bit Desktop Environment (Windows 11 / Linux WSL2 for AOT Compilers)



### Target Edge Benchmark Hardware (Snapdragon Edge Deployment Platform)

- **Platform**: Qualcomm Snapdragon® X Series Reference Platform (ARM64)

- **NPU**: Qualcomm® Hexagon™ HTP NPU (45 TOPS INT8)

- **RAM**: Unified LPDDR5x High-Bandwidth Memory

- **OS**: Windows 11 ARM64 Environment



---

*This document certifies the technical reproducibility of the models across NVIDIA Blackwell and Qualcomm Snapdragon platforms.*