The .tflite reports inputs[0].shape == [1, 224, 224, 3] but the
preprocessing was producing 256x256x3 buffers, so 196608 floats were
written into a tensor sized for 150528. Inference still completed
(no allocator check on the JS side) but ran on shifted, decadred
data — predictions were effectively random.
Also: the v3 react-native-fast-tflite binding rejects raw TypedArray
views with "TfliteModel.runSync(...): Object \"<element dump>\"" and
only accepts the underlying ArrayBuffer. We now pass `input.buffer`
as the primary path and keep the TypedArray as a fallback.
Bonus:
- Read input dtype from the model and dispatch preprocess accordingly
(float32, uint8, int8) instead of hard-coding float32. Future-proof
for a quantized re-export.
- Dequantize uint8/int8 outputs to floats so argmax stays consistent.
- Log model.inputs and model.outputs at load time — invaluable when
re-exporting the .tflite and discovering shape mismatches.
Validated on device (Samsung S23): preprocess 700ms + inference 39ms,
no fallback chain. Still ~25% accuracy because the model itself is
overfit (see docs/audit_report.md), but the inference plumbing is
finally honest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>