Model Metadata
This document describes the metadata schema embedded in EdgeFirst model files. Model metadata provides complete traceability for MLOps workflows and contains all information needed to decode model outputs for inference.
Overview
EdgeFirst models embed metadata that enables:
- Full Traceability: Link any deployed model back to its training session, dataset, and configuration in EdgeFirst Studio
- Self-Describing Models: Models contain all information needed for inference without external configuration files
- Cross-Platform Compatibility: Consistent schema across TFLite and ONNX formats
- Third-Party Integration: Any training framework can produce EdgeFirst-compatible models by following this schema
Supported Formats
EdgeFirst models from the Model Zoo (including ModelPack and Ultralytics) embed metadata in format-specific locations:
| Format | Metadata Location | Config Format | Labels |
|---|---|---|---|
| TFLite | ZIP archive (associated files) | edgefirst.yaml |
labels.txt |
| ONNX | Custom metadata properties | edgefirst (JSON) |
labels (JSON array) |
Supported Training Frameworks
| Framework | Decoder | Architecture | Use Case |
|---|---|---|---|
| ModelPack | modelpack |
Anchor-based YOLO | Semantic segmentation, detection |
| Ultralytics | ultralytics |
Anchor-free DFL (YOLOv5/v8/v11) | Instance segmentation, detection |
Note
These metadata fields are automatically read and handled by edgefirst-validator and the EdgeFirst Perception Middleware. In most cases, developers don't need to worry about these details — the EdgeFirst ecosystem "Just Works." This documentation exists so developers understand what's happening under the hood when needed.
Traceability for Production MLOps
One of the most critical aspects of production ML systems is traceability — the ability to answer questions like:
- Where was this model trained?
- What dataset was used?
- What were the training parameters?
- Can I reproduce this model?
EdgeFirst metadata provides complete traceability through these key fields:
| Field | Location | Purpose |
|---|---|---|
studio_server |
host.studio_server |
Full hostname of EdgeFirst Studio instance (e.g., test.edgefirst.studio) |
project_id |
host.project_id |
Project ID for constructing Studio URLs |
session_id |
host.session |
Training session ID for accessing logs, metrics, artifacts |
dataset_id |
dataset.id |
Dataset identifier for reproducing training data |
dataset |
dataset.name |
Human-readable dataset name |
Example Traceability Workflow
Given a deployed model, you can trace back to its origins:
# Extract metadata from deployed model
metadata = get_edgefirst_metadata(model_path)
# Construct EdgeFirst Studio URLs
studio_server = metadata['host']['studio_server'] # e.g., 'test.edgefirst.studio'
project_id = metadata['host']['project_id'] # e.g., '1123'
session = metadata['host']['session'] # e.g., 't-2110'
dataset_id = metadata['dataset']['id'] # e.g., 'ds-1c8'
# Note: Studio URL parameters require integer IDs. Metadata stores hex values
# with prefixes (t-, ds-). Convert by stripping the prefix and parsing as hex:
# 't-2110' -> int('2110', 16) -> 8464
# 'ds-1c8' -> int('1c8', 16) -> 456
# Access training session: https://{studio_server}/{project_id}/experiment/training/details?train_session_id={session_int}
# Example: https://test.edgefirst.studio/1123/experiment/training/details?train_session_id=8464
# Access dataset: https://{studio_server}/{project_id}/datasets/gallery/main?dataset={dataset_int}
# Example: https://test.edgefirst.studio/1123/datasets/gallery/main?dataset=456
# View training logs, metrics, and original configuration
This enables:
- Audit trails for regulatory compliance
- Debugging production issues by examining training data
- Reproducibility by re-running training with identical configuration
- Version control of model lineage through Model Experiments
Reading Metadata
TFLite Models
TFLite models are ZIP-format files containing embedded edgefirst.yaml and labels.txt:
import zipfile
import yaml
import json
from typing import Optional, List
def get_edgefirst_metadata(model_path: str) -> Optional[dict]:
"""Extract EdgeFirst metadata from a TFLite model."""
if not zipfile.is_zipfile(model_path):
return None
with zipfile.ZipFile(model_path) as zf:
# Try JSON first (preferred), then YAML fallback
for filename in ['edgefirst.json', 'edgefirst.yaml']:
if filename in zf.namelist():
with zf.open(filename) as f:
content = f.read().decode('utf-8')
if filename.endswith('.json'):
return json.loads(content)
else:
return yaml.safe_load(content)
return None
def get_labels(model_path: str) -> List[str]:
"""Extract class labels from a TFLite model."""
if not zipfile.is_zipfile(model_path):
return []
with zipfile.ZipFile(model_path) as zf:
if 'labels.txt' in zf.namelist():
with zf.open('labels.txt') as f:
content = f.read().decode('utf-8').strip()
return [line for line in content.splitlines()
if line.strip()]
return []
ONNX Models
ONNX models store metadata directly in the model's custom properties:
import onnx
import json
from typing import Optional, List
def get_edgefirst_metadata(model_path: str) -> Optional[dict]:
"""Extract EdgeFirst metadata from an ONNX model."""
model = onnx.load(model_path)
for prop in model.metadata_props:
if prop.key == 'edgefirst':
return json.loads(prop.value)
return None
def get_labels(model_path: str) -> List[str]:
"""Extract class labels from an ONNX model."""
model = onnx.load(model_path)
for prop in model.metadata_props:
if prop.key == 'labels':
return json.loads(prop.value)
return []
def get_quick_metadata(model_path: str) -> dict:
"""Get commonly-used fields without parsing full config."""
model = onnx.load(model_path)
result = {}
quick_fields = ['name', 'description', 'author', 'studio_server',
'session_id', 'dataset', 'dataset_id']
for prop in model.metadata_props:
if prop.key in quick_fields:
result[prop.key] = prop.value
elif prop.key == 'labels':
result['labels'] = json.loads(prop.value)
return result
ONNX Runtime Access
For inference applications using ONNX Runtime:
import onnxruntime as ort
import json
session = ort.InferenceSession(model_path)
metadata = session.get_modelmeta()
# Access custom metadata
custom = metadata.custom_metadata_map
edgefirst_config = json.loads(custom.get('edgefirst', '{}'))
labels = json.loads(custom.get('labels', '[]'))
# Access official ONNX fields
print(f"Producer: {metadata.producer_name}") # 'EdgeFirst ModelPack'
print(f"Graph: {metadata.graph_name}")
print(f"Description: {metadata.description}")
Metadata Schema
The EdgeFirst metadata schema is organized into logical sections. All sections are optional — third-party integrations can include only the sections relevant to their use case.
Complete Schema Structure
# Traceability & Identification
host:
studio_server: string # Full EdgeFirst Studio hostname (e.g., test.edgefirst.studio)
project_id: string # Project ID for Studio URLs
session: string # Training session ID
username: string # User who initiated training
dataset:
name: string # Human-readable dataset name
id: string # Dataset identifier
classes: [string] # List of class labels
# Model Identification (from training session)
name: string # Model/session name
description: string # Model description
author: string # Organization (typically "Au-Zone Technologies")
# Model Configuration (see ModelPack and Ultralytics documentation)
input:
shape: [int] # Input tensor shape (NCHW or NHWC depending on model)
color_adaptor: string # Color format (rgb, rgba, yuyv)
model:
backbone: string # Backbone architecture (e.g., cspdarknet19, cspdarknet53)
model_size: string # Size variant (nano, small, medium, large)
activation: string # Activation function (relu, relu6, silu)
detection: boolean # Detection task enabled
segmentation: boolean # Segmentation task enabled
classification: boolean # Classification task enabled
split_decoder: boolean # Whether decoder is external (see Split Decoder section)
anchors: [[[int, int]]] # Anchor boxes per output level
# ... additional model-specific parameters
# Training Configuration
trainer:
epochs: int # Number of training epochs
batch_size: int # Training batch size
weights: string # Pretrained weights source
checkpoint_path: string # Where checkpoints were saved
optimizer:
optimizer: string # Optimizer type (adam, adamw, sgd)
learning_rate: float # Base learning rate
weight_decay: float # L2 regularization strength
# ... additional optimizer parameters
augmentation: # See Vision Augmentations documentation
random_hflip: int # Horizontal flip probability (0-100)
random_mosaic: int # Mosaic augmentation probability
# ... additional augmentation parameters
validation:
iou: float # NMS IoU threshold
score: float # NMS score threshold
normalization: string # Input normalization (unsigned, signed)
preprocessing: string # Preprocessing method (resize, letterbox)
skip_validation_steps: int # Steps to skip between validations
export: # See Quantization documentation for ModelPack and Ultralytics
export: boolean # Whether model was quantized
export_input_type: string # Input quantization type
export_output_type: string # Output quantization type
calibration_samples: int # Samples used for calibration
# Output Specification (Critical for Inference)
outputs:
- name: string # Output tensor name
index: int # Tensor index
output_index: int # Output order
shape: [int] # Tensor shape
dshape: # Named dimensions as ordered array (see dshape section)
- batch: int
- height: int # For spatial outputs
- width: int # For spatial outputs
- num_features: int # For detection outputs
- num_boxes: int # For detection outputs
- num_protos: int # For instance segmentation
- num_classes: int # For semantic segmentation
dtype: string # Data type (float32, uint8, int8)
type: string # Semantic type (detection, segmentation, boxes, scores, masks, protos)
decode: boolean # Whether decoding is required
decoder: string # Decoder type: 'modelpack' or 'ultralytics'
quantization: [float, int] # [scale, zero_point] for quantized models
stride: [int, int] # Spatial stride for this output (ModelPack)
anchors: [[[float, float]]] # Normalized anchors for this output level (ModelPack only)
Output Specification
The outputs section is critical for inference — it tells the runtime how to interpret model outputs.
Output Types
| Type | Description | Typical Shape | Framework |
|---|---|---|---|
detection |
Raw detection output (needs decoding) | [1, num_features, num_boxes] |
Both |
boxes |
Decoded bounding boxes | [1, N, 4] |
ModelPack |
scores |
Decoded class scores | [1, N, classes] |
ModelPack |
segmentation |
Semantic segmentation output | [1, H, W, classes] |
ModelPack |
masks |
Semantic segmentation masks | [1, H, W] or [1, H, W, classes] |
ModelPack |
protos |
Instance segmentation prototypes | [1, num_protos, H, W] (NCHW) |
Ultralytics |
Segmentation Types
EdgeFirst supports two distinct segmentation approaches:
Semantic Segmentation (ModelPack)
Per-pixel classification without object instances. Each pixel is assigned a class label, but individual objects are not distinguished.
Use cases:
- Drivable surface detection
- Lane segmentation
- Sky/ground separation
- Terrain classification
Output structure:
outputs:
- name: "segmentation_output"
type: segmentation
shape: [1, 480, 640, 5] # [batch, H, W, num_classes]
dshape:
- batch: 1
- height: 480
- width: 640
- num_classes: 5
decoder: modelpack
Instance Segmentation (Ultralytics)
Per-pixel classification with object instances. Each detected object gets its own mask, enabling fine-grained object boundaries beyond bounding boxes.
Use cases:
- Individual person segmentation
- Vehicle instance masks
- Product segmentation
- Fine-grained object detection
Output structure:
# Detection output with mask coefficients
outputs:
- name: "detection_output"
type: detection
shape: [1, 116, 8400] # [batch, 4+nc+32, num_boxes] - includes 32 mask coefficients
dshape:
- batch: 1
- num_features: 116 # 4 box coords + 80 classes + 32 mask coefficients
- num_boxes: 8400
decoder: ultralytics
# Prototype masks for instance computation
- name: "protos_output"
type: protos
shape: [1, 32, 160, 160] # [batch, num_protos, H, W] NCHW
dshape:
- batch: 1
- num_protos: 32
- height: 160
- width: 160
decoder: ultralytics
Final mask computation:
# For each detected object with mask_coefficients [32]:
instance_mask = sigmoid(mask_coefficients @ protos) # [32] @ [32, H, W] → [H, W]
# Crop to bounding box region for final instance mask
The dshape Field
The dshape field provides named dimensions for easier interpretation of tensor shapes. This is especially useful when shapes vary between data layouts (NCHW vs NHWC).
outputs:
- name: "output_0"
shape: [1, 84, 8400] # Raw shape
dshape: # Named dimensions as ordered array
- batch: 1
- num_features: 84 # 4 box coords + 80 classes
- num_boxes: 8400
Standard dimension names:
| Name | Description |
|---|---|
batch |
Batch size (typically 1 for inference) |
height |
Spatial height |
width |
Spatial width |
num_classes |
Number of classification classes |
num_features |
Feature dimension (box coords + classes + mask coefficients) |
num_boxes |
Number of detection boxes/anchors |
num_protos |
Number of prototype masks (instance segmentation) |
num_anchors_x_features |
Combined anchor and feature dimension for ModelPack grid outputs (anchors × features per anchor) |
Decoding Information
For outputs with decode: true, the metadata provides all information needed to decode:
outputs:
- name: "detection_output_0"
type: detection
decode: true
decoder: modelpack
shape: [1, 40, 40, 54] # Grid output
stride: [16, 16] # Downsampling factor
anchors: # Normalized anchor boxes
- [0.054, 0.065]
- [0.089, 0.139]
- [0.195, 0.196]
quantization: [0.176, 198] # For dequantization
Quantization Parameters
For quantized models (TFLite INT8), each output includes quantization parameters:
# Dequantize output
scale, zero_point = output_spec['quantization']
float_output = (quantized_output - zero_point) * scale
Data Layout (NCHW vs NHWC)
Deep learning frameworks use different memory layouts for tensor data. The metadata accurately reflects each format's native layout:
| Format | Data Layout | Shape Convention | Example (batch=1, 640x640, RGB) |
|---|---|---|---|
| TFLite | NHWC | [batch, height, width, channels] |
[1, 640, 640, 3] |
| ONNX | NCHW | [batch, channels, height, width] |
[1, 3, 640, 640] |
Why This Matters
- TFLite (TensorFlow): Uses channels-last (NHWC) which is optimized for CPU and mobile inference
- ONNX (PyTorch-derived): Uses channels-first (NCHW) which is optimized for GPU and NPU inference
The metadata's outputs section reports shapes in the model's native format. When integrating with inference runtimes, ensure your input preprocessing matches the expected layout.
Metadata Fields
input:
shape: [1, 640, 640, 3] # Input tensor shape (layout varies by model)
color_adaptor: rgb # Channel order (rgb, bgr, yuyv)
# Common layouts:
# - NHWC: [batch, height, width, channels] e.g., [1, 640, 640, 3]
# - NCHW: [batch, channels, height, width] e.g., [1, 3, 640, 640]
outputs:
- name: "output_0"
shape: [1, 640, 640, 3] # TFLite: NHWC
# shape: [1, 3, 640, 640] # ONNX: NCHW
Input Preprocessing
EdgeFirst models expect specific input preprocessing. The metadata documents these requirements so inference pipelines can prepare data correctly.
Image Resizing
Models expect input images at the resolution specified in metadata. How images are resized depends on the training approach:
input:
shape: [1, 640, 640, 3] # NHWC example: [batch, height, width, channels]
# shape: [1, 3, 640, 640] # NCHW example: [batch, channels, height, width]
color_adaptor: rgb # Expected color format
Native Aspect Ratio (typical for purpose-built datasets):
- ModelPack models are often trained at the camera's native aspect ratio
- Images are directly resized to target dimensions without padding
- Best accuracy when deployment camera matches training data
Letterbox (typical for diverse datasets like COCO):
- Used when training on images from diverse cameras and aspect ratios
- Image is scaled to fit within target size while maintaining aspect ratio
- Gray padding (value 114) added to reach exact dimensions
- Inference must apply same letterbox transform and account for padding offset in output coordinates
Example: A 1920x1080 image letterboxed to 640x640:
- Scaled to 640x360 (maintains 16:9 ratio)
- 140 pixels of padding added to top and bottom
- Output box coordinates must be adjusted to remove padding offset
Pixel Normalization
Input pixels are normalized from [0, 255] to [0.0, 1.0]:
# Standard normalization
normalized = pixels.astype(np.float32) / 255.0
For quantized models (INT8), the quantization parameters handle the scaling internally — raw uint8 pixel values can often be used directly.
Color Format
The color_adaptor field specifies the expected channel format:
| Value | Description | Channel Order |
|---|---|---|
rgb |
Standard RGB | Red, Green, Blue |
bgr |
OpenCV default | Blue, Green, Red |
rgba |
RGB with alpha | Red, Green, Blue, Alpha |
yuyv |
YUV 4:2:2 packed | For direct camera sensor input |
Post-Processing & Split Decoder
What is Split Decoder?
The split_decoder field indicates whether the model's detection outputs require external decoding:
model:
split_decoder: true # Outputs need external anchor-based decoding
split_decoder: false # Outputs are fully decoded (ready-to-use boxes)
Why Split Decoder Exists
For quantized INT8 models, ModelPack uses a "split decoder" architecture where:
- Model outputs raw grid features — not decoded bounding boxes
- Decoding happens after dequantization — in float32 precision
- Anchor-based box calculation — uses the anchors specified in metadata
The reason: Quantization introduces precision loss. For small objects or high-resolution inputs, applying anchor calculations in INT8 would compound rounding errors, leading to inaccurate bounding boxes. By deferring decoding until after dequantization, we preserve box accuracy.
Decoding Process
When split_decoder: true, the inference pipeline must:
- Run model inference → Get quantized grid outputs
- Dequantize outputs → Convert INT8 to float32 using scale/zero_point
- Apply anchor decoding → Transform grid predictions to bounding boxes
- Run NMS → Filter overlapping detections
# Example decoding flow for split_decoder models
for output_spec in metadata['outputs']:
if output_spec.get('decode', False):
# Dequantize first
scale, zp = output_spec['quantization']
grid_float = (grid_int8.astype(np.float32) - zp) * scale
# Then decode with anchors
anchors = output_spec['anchors']
stride = output_spec['stride']
boxes = decode_yolo_grid(grid_float, anchors, stride)
Output Types with Split Decoder
split_decoder |
Output Type | Description |
|---|---|---|
true |
detection |
Raw grid features, requires anchor decoding |
false |
boxes, scores |
Decoded boxes ready for NMS |
Decoder Field
The decoder field specifies which decoding algorithm to use:
outputs:
- name: "detection_output_0"
type: detection
decode: true
decoder: modelpack # Use ModelPack YOLO-style grid decoding
Supported Decoders
modelpack — Anchor-Based YOLO Decoder
Used by ModelPack models. Traditional YOLO-style grid decoding with pre-defined anchor boxes.
Characteristics:
- Anchor-based: Uses pre-defined anchor boxes per output level (3 anchors × 3 scales typical)
- Grid outputs: Raw features from detection grid cells
- Sigmoid activations: Applied to xy, wh, objectness, and class predictions
Decoding formula:
xy = (sigmoid(xy) * 2.0 + grid - 0.5) * stride
wh = (sigmoid(wh) * 2) ** 2 * anchors * stride * 0.5
xyxy = concat([xy - wh, xy + wh]) / input_dims # normalized xyxy
Required metadata fields:
outputs:
- decoder: modelpack
anchors: # Required - normalized anchor boxes
- [0.054, 0.065]
- [0.089, 0.139]
stride: [16, 16] # Required - spatial stride
ultralytics — Anchor-Free DFL Decoder
Used by Ultralytics models (YOLOv5, YOLOv8, YOLO11). Modern anchor-free detection using Distribution Focal Loss (DFL).
Characteristics:
- Anchor-free: Uses anchor points (grid centers) instead of pre-defined boxes
- DFL regression: Converts 16-bin distribution to box coordinates
- Unified architecture: Same decoder for YOLOv5, YOLOv8, and YOLO11
Decoding formula:
# DFL converts 16-bin distribution to coordinate value
box = dfl(raw_box) # [batch, 64, anchors] → [batch, 4, anchors]
# dist2bbox converts LTRB distances to boxes
x1y1 = anchor_points - lt
x2y2 = anchor_points + rb
# Returns xywh or xyxy in pixel coordinates
Metadata structure:
outputs:
- decoder: ultralytics
anchors: null # Not used - anchor-free
# Strides are implicit: [8, 16, 32] for P3/P4/P5 outputs
Version differences:
All Ultralytics versions use the same anchor-free Detect class. Differences are in backbone architecture:
| Version | Backbone Blocks | Classification Head |
|---|---|---|
| YOLOv5 | C3 | Conv→Conv→Conv2d |
| YOLOv8 | C2f | Conv→Conv→Conv2d |
| YOLO11 | C3k2, C2PSA | DWConv→Conv (efficient) |
ONNX-Specific Metadata
ONNX models exported from ModelPack or Ultralytics include additional official metadata fields:
| Field | ModelPack Value | Ultralytics Value | Purpose |
|---|---|---|---|
producer_name |
"EdgeFirst ModelPack" | "EdgeFirst Ultralytics" | Identifies producing framework |
producer_version |
Package version | Package version | Version tracking |
graph.name |
Model name | Model name | Graph identification |
doc_string |
Description | Description | Human-readable description |
Custom metadata properties (all string values):
| Key | Content | Purpose |
|---|---|---|
edgefirst |
Full config as JSON | Complete configuration |
name |
Model name | Quick access (no JSON parsing) |
description |
Model description | Quick access |
author |
Author/organization | Quick access |
studio_server |
Full hostname | Quick access for traceability |
project_id |
Project ID | Quick access for traceability |
session_id |
Session ID | Quick access for traceability |
dataset |
Dataset name | Quick access |
dataset_id |
Dataset ID | Quick access for traceability |
labels |
JSON array of labels | Class labels |
Third-Party Integration
Any training framework can produce EdgeFirst-compatible models by embedding the appropriate metadata.
Minimum Required Fields
For basic EdgeFirst Perception stack compatibility:
input:
shape: [1, 640, 640, 3] # Input tensor shape (NHWC or NCHW)
color_adaptor: rgb
model:
detection: true
segmentation: false
split_decoder: true # or false if decoder is built-in
outputs:
- name: "output_0"
shape: [1, 8400, 84]
dtype: float32
type: boxes # or detection if needs decoding
decode: false
dataset:
classes:
- class1
- class2
Full Traceability (Recommended)
For production MLOps integration with EdgeFirst Studio:
host:
studio_server: test.edgefirst.studio
project_id: "1123"
session: t-2110 # Hex value, convert to int for URLs
dataset:
name: "My Dataset"
id: ds-xyz789
classes: [...]
name: "my-model-v1" # Model/session name
description: "Model for production deployment"
author: "My Organization"
Embedding Metadata in TFLite
Dependencies
This example requires the tflite-support and pyyaml packages:
pip install tflite-support pyyaml
from tensorflow_lite_support.metadata.python.metadata_writers import metadata_writer, writer_utils
from tensorflow_lite_support.metadata import metadata_schema_py_generated as schema
import yaml
from typing import List
import tempfile
import os
def add_edgefirst_metadata(tflite_path: str, config: dict, labels: List[str]):
"""Add EdgeFirst metadata to a TFLite model."""
# Write config and labels to temp files in a cross-platform way
with tempfile.TemporaryDirectory() as tmpdir:
config_path = os.path.join(tmpdir, 'edgefirst.yaml')
labels_path = os.path.join(tmpdir, 'labels.txt')
with open(config_path, 'w') as f:
yaml.dump(config, f)
with open(labels_path, 'w') as f:
f.write('\n'.join(labels))
# Create model metadata
model_meta = schema.ModelMetadataT()
model_meta.name = config.get('name', '')
model_meta.description = config.get('description', '')
model_meta.author = config.get('author', '')
# Load and populate
tflite_buffer = writer_utils.load_file(tflite_path)
writer = metadata_writer.MetadataWriter.create_from_metadata(
model_buffer=tflite_buffer,
model_metadata=model_meta,
associated_files=[labels_path, config_path]
)
writer_utils.save_file(writer.populate(), tflite_path)
Embedding Metadata in ONNX
Dependencies
This example requires the onnx package:
pip install onnx
import onnx
import json
from typing import List
def add_edgefirst_metadata(onnx_path: str, config: dict, labels: List[str]):
"""Add EdgeFirst metadata to an ONNX model."""
model = onnx.load(onnx_path)
# Set official ONNX fields
model.producer_name = 'My Training Framework'
model.producer_version = '1.0.0'
if config.get('name'):
model.graph.name = config['name']
if config.get('description'):
model.doc_string = config['description']
# Add custom metadata
metadata = {
'edgefirst': json.dumps(config),
'labels': json.dumps(labels),
'name': config.get('name', ''),
'description': config.get('description', ''),
'author': config.get('author', ''),
'studio_server': config.get('host', {}).get('studio_server', ''),
'project_id': str(config.get('host', {}).get('project_id', '')),
'session_id': config.get('host', {}).get('session', ''),
'dataset': config.get('dataset', {}).get('name', ''),
'dataset_id': str(config.get('dataset', {}).get('id', '')),
}
for key, value in metadata.items():
if value:
prop = model.metadata_props.add()
prop.key = key
prop.value = str(value)
onnx.save(model, onnx_path)
Updating Metadata
Updating TFLite Metadata
Since TFLite models are ZIP archives, you can update embedded files:
zip command
The zip command is available on most platforms but may need to be installed:
# Update edgefirst.yaml
zip -u mymodel.tflite edgefirst.yaml
# Update labels
zip -u mymodel.tflite labels.txt
# Add new files
zip mymodel.tflite edgefirst.json
Updating ONNX Metadata
import onnx
import json
model = onnx.load('mymodel.onnx')
# Update existing metadata
for prop in model.metadata_props:
if prop.key == 'description':
prop.value = 'Updated description'
# Add new metadata
prop = model.metadata_props.add()
prop.key = 'custom_field'
prop.value = 'custom_value'
onnx.save(model, 'mymodel.onnx')
Schema Reference
Host Section
The host section identifies the EdgeFirst Studio instance and training session that produced the model.
host:
studio_server: test.edgefirst.studio # Full EdgeFirst Studio hostname
project_id: "1123" # Project ID for Studio URLs
session: t-2110 # Training session ID (hex, prefix t-)
username: john.doe # User who initiated training
Converting IDs for Studio URLs
Session and dataset IDs in metadata use hexadecimal values with prefixes (t- for training sessions, ds- for datasets). To construct Studio URLs, strip the prefix and convert from hex to decimal:
t-2110→int('2110', 16)→8464ds-1c8→int('1c8', 16)→456
Dataset Section
The dataset section references the dataset used for training. See the Dataset Zoo for available datasets and Dataset Structure for format details.
dataset:
name: "COCO 2017" # Human-readable name
id: ds-abc123 # Dataset ID (prefix: ds-)
classes: # Ordered list of class labels
- background
- person
- car
Model Identification
Top-level fields for model identification, populated from the training session name and description.
name: "coffeecup-detection" # Model/session name (used in filename)
description: "Object detection model for coffee cups"
author: "Au-Zone Technologies" # Organization
Input Section
The input section specifies image preprocessing requirements. See Vision Augmentations for training-time augmentation configuration.
input:
shape: [1, 640, 640, 3] # Input tensor shape
color_adaptor: rgb # rgb, rgba, yuyv, bgr
Data Layout
The shape field uses the model's native tensor layout. This can be either NHWC [batch, height, width, channels] or NCHW [batch, channels, height, width] depending on how the model was exported. While TFLite typically uses NHWC and ONNX typically uses NCHW, both formats can support either layout — always check the actual shape values.
Model Section
The model section captures architecture configuration. These parameters can be configured during training session setup in EdgeFirst Studio. See the ModelPack and Ultralytics documentation for detailed parameter descriptions.
# ModelPack model configuration
model:
backbone: cspdarknet19
model_size: nano # nano, small, medium, large
activation: relu6 # relu, relu6, silu, mish
detection: true
segmentation: false
classification: false
split_decoder: true # true = outputs need anchor decoding after dequantization
# false = outputs are fully decoded boxes
# See "Post-Processing & Split Decoder" section for details
anchors: # Per-level anchor boxes (pixels at input resolution)
- [[35, 42], [57, 89], [125, 126]]
- [[125, 126], [208, 260], [529, 491]]
# Ultralytics model configuration
model:
model_version: v8 # v5, v8, v11
model_task: detect # detect, segment
model_size: n # n (nano), s (small), m (medium), l (large), x (xlarge)
detection: true
segmentation: false
split_decoder: false # Ultralytics models have decoder built-in
Outputs Section
# ModelPack detection output example
outputs:
- name: "output_0"
index: 0
output_index: 0
shape: [1, 40, 40, 54]
dshape:
- batch: 1
- height: 40
- width: 40
- num_anchors_x_features: 54 # 3 anchors × (5 + 13 classes)
dtype: float32
type: detection
decode: true
decoder: modelpack
quantization: [0.176, 198]
stride: [16, 16]
anchors:
- [0.054, 0.065]
- [0.089, 0.139]
- [0.195, 0.196]
# Ultralytics detection output example
outputs:
- name: "output0"
index: 0
output_index: 0
shape: [1, 84, 8400] # NCHW: [batch, 4+nc, num_boxes]
dshape:
- batch: 1
- num_features: 84 # 4 box coords + 80 classes
- num_boxes: 8400
dtype: float32
type: detection
decode: true
decoder: ultralytics
quantization: null # Float model
anchors: null # Anchor-free
# Ultralytics instance segmentation protos example
- name: "output1"
index: 1
output_index: 1
shape: [1, 32, 160, 160] # NCHW: [batch, protos, H, W]
dshape:
- batch: 1
- num_protos: 32
- height: 160
- width: 160
dtype: float32
type: protos
decode: true
decoder: ultralytics
quantization: null
anchors: null
Related Articles
- ModelPack Overview - Architecture details and training parameters
- Ultralytics Integration - YOLOv8/v11 training and deployment
- Training Vision Models - Step-by-step training workflow
- On Cloud Validation - Managed validation sessions
- On Target Validation - User-managed validation with
edgefirst-validator - ModelPack Quantization - Converting ONNX to quantized TFLite
- Deploying to Embedded Targets - Model deployment workflow
- EdgeFirst Perception Middleware - Runtime inference stack
- Dataset Zoo - Available datasets for training
- Model Experiments Dashboard - Managing training and validation sessions