MNN

MNN è il motore di inferenza leggero di Alibaba. LibreYOLO esporta un grafo ONNX statico, lo converte con lo strumento mnnconvert incluso nel pacchetto MNN e scrive un sidecar JSON che registra i nomi di input e output, la forma di input fissa e i nomi delle classi.

Flag
export(format="mnn")
Scrive
Un file .mnn più un sidecar di metadati .mnn.json
Extra
pip install "libreyolo[mnn]"
Si ricarica con
LibreYOLO("weights/LibreYOLO9t.mnn")
Forme
NCHW fissa. dynamic=True viene rifiutato.
Precisione
Solo FP32, solo CPU.
Task
Solo rilevamento in questa versione

Installazione

Installazione
# L'extra include libreyolo[onnx]: MNN converte da un intermedio ONNX.pip install "libreyolo[mnn]"
Verificare che il convertitore sia nel PATH
mnnconvert --version

L'extra include libreyolo[onnx], perché la conversione passa per un intermedio ONNX. Porta con sé anche l'eseguibile mnnconvert, che l'exporter cerca prima accanto all'interprete Python attivo e poi nel PATH. Se il convertitore manca viene sollevato un ImportError che indica il comando di installazione, invece di fallire a metà conversione.

Esportazione

Python
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9t.pt") # Scrive weights/LibreYOLO9t.mnn e weights/LibreYOLO9t.mnn.jsonpath = model.export(format="mnn", imgsz=640)print(path)
CLI
libreyolo export --model LibreYOLO9t.pt --format mnn --imgsz 640
Argomenti
model.export(    format="mnn",    imgsz=640,        # int, oppure (altezza, larghezza)    batch=1,          # incorporato nell'artefatto    simplify=True,    # onnxsim sull'intermedio ONNX    output_path=None, # None scrive weights/<stem>.mnn    verbose=False,    # True mostra il log di mnnconvert) # dynamic=True solleva ValueError. half=True e int8=True vengono rifiutati.

Prima di passare il grafo, l'exporter legge il contratto di input ONNX e rifiuta tutto ciò che non può esprimere: più di un input immagine, oppure una forma di input con una dimensione simbolica. In questa versione MNN richiede una forma NCHW completamente fissa, e batch viene incorporato nell'artefatto anziché negoziato al caricamento.

Il sidecar non è contabilità opzionale. weights/LibreYOLO9t.mnn.json registra i nomi di input e output, la forma di input fissa, il batch, i nomi delle classi, la versione di MNN usata e il backend per cui l'artefatto è stato costruito, e il runtime valida ognuno di questi campi al caricamento.

Su Windows, MNN 3.6.1 a volte completa la conversione e poi termina durante la chiusura del processo con una violazione di accesso o uno stato di fail-fast. L'exporter riconosce quegli specifici codici di uscita e considera riuscita la conversione quando il file di output è presente.

Eseguire l'artefatto

Tramite LibreYOLO
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("weights/LibreYOLO9t.mnn")result = model.predict(SAMPLE_IMAGE)print(result.boxes.xyxy[:3])
MNN puro
import json import MNNimport numpy as np meta = json.load(open("weights/LibreYOLO9t.mnn.json"))print(meta["mnn_input_names"], meta["mnn_output_names"], meta["mnn_input_shape"]) runtime = MNN.nn.create_runtime_manager(    ({"backend": 0, "precision": 1, "numThread": 4},))module = MNN.nn.load_module_from_file(    "weights/LibreYOLO9t.mnn",    meta["mnn_input_names"],    meta["mnn_output_names"],    runtime_manager=runtime,    dynamic=False,    shape_mutable=False,) blob = np.zeros(meta["mnn_input_shape"], dtype=np.float32)input_var = MNN.expr.const(    blob, list(blob.shape), MNN.expr.NCHW, MNN.expr.float)outputs = module.forward([input_var])for out in outputs:    print(np.array(MNN.expr.convert(out, MNN.expr.NCHW).read()).shape) # Il preprocessing e il postprocessing sono a tuo carico su questo percorso.

LibreYOLO() smista in base al suffisso .mnn e restituisce lo stesso oggetto Results del checkpoint. Il caricamento è severo per scelta: il sidecar deve dichiarare format=mnn, mnn_backend=cpu, dynamic=false, precision=fp32, una dimensione, un task di rilevamento, una forma NCHW fissa e positiva che concordi con la dimensione immagine registrata, e nomi di classe che coprano ogni indice da 0 a nc - 1. Qualsiasi discrepanza solleva un errore invece di tirare a indovinare.

Anche fare una predizione con un imgsz diverso da quello per cui l'artefatto è stato costruito solleva un errore, e device viene ignorato con un avviso, perché qui le esportazioni MNN girano su CPU.

Il secondo snippet è il percorso a runtime puro. Lì il preprocessing, il decoding, l'NMS e il riscalamento delle coordinate diventano a tuo carico, e i nomi di input e output arrivano dal sidecar perché il module loader di MNN li vuole espliciti.

Vincoli

Solo rilevamento. Il backend rifiuta qualsiasi altro task al caricamento, e il lato esportazione fa lo stesso: al di fuori delle combinazioni registrate, il preflight solleva un errore con "MNN v1 has no implemented runtime contract for this family and task."

FP32, CPU, forma fissa. dynamic=True solleva ValueError, e half=True e int8=True vengono rifiutati durante la validazione.

Le famiglie di rilevamento validate sono YOLO9, YOLO9-E2E, YOLO9-P2, RF-DETR, EC, RT-DETR, RT-DETRv2, RT-DETRv4, D-FINE, DEIM e YOLO-NAS, ciascuna coperta da conversione, ricaricamento di un artefatto appena creato, esecuzione MNN su CPU, controlli sui metadati e parità di rilevamento post-NMS con corrispondenza rispetto al modello PyTorch. DEIMv2 converte, si ricarica, viene eseguito e preserva i rilevamenti post-NMS, ma il suo percorso ONNX intermedio ha una parità di punteggio a livello di query incompleta, quindi è registrato come disponibile anziché validato.

Per la griglia completa di famiglie e task, vedi la matrice di esportazione. Per una singola combinazione:

Controllare una famiglia e un task prima di esportare
libreyolo formats --family yolo9 --task detect

Letto da libreyolo/export/mnn.py, libreyolo/export/exporter.py, libreyolo/export/support.py, libreyolo/backends/mnn.py e pyproject.toml sul branch dev.