@ -9,7 +9,7 @@ TorchScript | `torchscript` | yolov8n.torchscript
ONNX | ` onnx ` | yolov8n . onnx
OpenVINO | ` openvino ` | yolov8n_openvino_model /
TensorRT | ` engine ` | yolov8n . engine
CoreML | ` coreml ` | yolov8n . mlmodel
CoreML | ` coreml ` | yolov8n . mlpackage
TensorFlow SavedModel | ` saved_model ` | yolov8n_saved_model /
TensorFlow GraphDef | ` pb ` | yolov8n . pb
TensorFlow Lite | ` tflite ` | yolov8n . tflite
@ -35,7 +35,7 @@ Inference:
yolov8n . onnx # ONNX Runtime or OpenCV DNN with dnn=True
yolov8n_openvino_model # OpenVINO
yolov8n . engine # TensorRT
yolov8n . mlmodel # CoreML (macOS-only)
yolov8n . mlpackage # CoreML (macOS-only)
yolov8n_saved_model # TensorFlow SavedModel
yolov8n . pb # TensorFlow GraphDef
yolov8n . tflite # TensorFlow Lite
@ -82,7 +82,7 @@ def export_formats():
[ ' ONNX ' , ' onnx ' , ' .onnx ' , True , True ] ,
[ ' OpenVINO ' , ' openvino ' , ' _openvino_model ' , True , False ] ,
[ ' TensorRT ' , ' engine ' , ' .engine ' , False , True ] ,
[ ' CoreML ' , ' coreml ' , ' .mlmodel ' , True , False ] ,
[ ' CoreML ' , ' coreml ' , ' .mlpackage ' , True , False ] ,
[ ' TensorFlow SavedModel ' , ' saved_model ' , ' _saved_model ' , True , True ] ,
[ ' TensorFlow GraphDef ' , ' pb ' , ' .pb ' , True , True ] ,
[ ' TensorFlow Lite ' , ' tflite ' , ' .tflite ' , True , False ] ,
@ -149,8 +149,10 @@ class Exporter:
self . run_callbacks ( ' on_export_start ' )
t = time . time ( )
format = self . args . format . lower ( ) # to lowercase
if format in ( ' tensorrt ' , ' trt ' ) : # engine aliases
if format in ( ' tensorrt ' , ' trt ' ) : # ' engine' aliases
format = ' engine '
if format in ( ' mlmodel ' , ' mlpackage ' , ' mlprogram ' , ' apple ' , ' ios ' ) : # 'coreml' aliases
format = ' coreml '
fmts = tuple ( export_formats ( ) [ ' Argument ' ] [ 1 : ] ) # available export formats
flags = [ x == format for x in fmts ]
if sum ( flags ) != 1 :
@ -319,7 +321,7 @@ class Exporter:
dynamic [ ' output0 ' ] = { 0 : ' batch ' , 2 : ' anchors ' } # shape(1, 84, 8400)
torch . onnx . export (
self . model . cpu ( ) if dynamic else self . model , # -- dynamic only compatible with cpu
self . model . cpu ( ) if dynamic else self . model , # dynamic=True only compatible with cpu
self . im . cpu ( ) if dynamic else self . im ,
f ,
verbose = False ,
@ -461,14 +463,16 @@ class Exporter:
yaml_save ( f / ' metadata.yaml ' , self . metadata ) # add metadata.yaml
return str ( f ) , None
@try_export
def export_coreml ( self , prefix = colorstr ( ' CoreML: ' ) ) :
""" YOLOv8 CoreML export. """
check_requirements ( ' coremltools>=6.0,<=6.2 ' )
mlmodel = self . args . format . lower ( ) == ' mlmodel ' # legacy *.mlmodel export format requested
check_requirements ( ' coremltools>=6.0,<=6.2 ' if mlmodel else ' coremltools>=7.0.b1 ' )
import coremltools as ct # noqa
LOGGER . info ( f ' \n { prefix } starting export with coremltools { ct . __version__ } ... ' )
f = self . file . with_suffix ( ' .mlmodel ' )
f = self . file . with_suffix ( ' .mlmodel ' if mlmodel else ' .mlpackage ' )
if f . is_dir ( ) :
shutil . rmtree ( f )
bias = [ 0.0 , 0.0 , 0.0 ]
scale = 1 / 255
@ -479,20 +483,38 @@ class Exporter:
elif self . model . task == ' detect ' :
model = iOSDetectModel ( self . model , self . im ) if self . args . nms else self . model
else :
# TODO CoreML Segment and Pose model pipelining
if self . args . nms :
LOGGER . warning ( f " { prefix } WARNING ⚠️ ' nms=True ' is only available for Detect models like ' yolov8n.pt ' . " )
# TODO CoreML Segment and Pose model pipelining
model = self . model
ts = torch . jit . trace ( model . eval ( ) , self . im , strict = False ) # TorchScript model
ct_model = ct . convert ( ts ,
inputs = [ ct . ImageType ( ' image ' , shape = self . im . shape , scale = scale , bias = bias ) ] ,
classifier_config = classifier_config )
bits , mode = ( 8 , ' kmeans_lut ' ) if self . args . int8 else ( 16 , ' linear ' ) if self . args . half else ( 32 , None )
classifier_config = classifier_config ,
convert_to = ' neuralnetwork ' if mlmodel else ' mlprogram ' )
bits , mode = ( 8 , ' kmeans ' ) if self . args . int8 else ( 16 , ' linear ' ) if self . args . half else ( 32 , None )
if bits < 32 :
if ' kmeans ' in mode :
check_requirements ( ' scikit-learn ' ) # scikit-learn package required for k-means quantization
ct_model = ct . models . neural_network . quantization_utils . quantize_weights ( ct_model , bits , mode )
if mlmodel :
ct_model = ct . models . neural_network . quantization_utils . quantize_weights ( ct_model , bits , mode )
else :
import coremltools . optimize . coreml as cto
op_config = cto . OpPalettizerConfig ( mode = mode , nbits = bits , weight_threshold = 512 )
config = cto . OptimizationConfig ( global_config = op_config )
ct_model = cto . palettize_weights ( ct_model , config = config )
if self . args . nms and self . model . task == ' detect ' :
ct_model = self . _pipeline_coreml ( ct_model )
if mlmodel :
import platform
# coremltools<=6.2 NMS export requires Python<3.11
check_version ( platform . python_version ( ) , ' <3.11 ' , name = ' Python ' , hard = True )
weights_dir = None
else :
ct_model . save ( str ( f ) ) # save otherwise weights_dir does not exist
weights_dir = str ( f / ' Data/com.apple.CoreML/weights ' )
ct_model = self . _pipeline_coreml ( ct_model , weights_dir = weights_dir )
m = self . metadata # metadata dict
ct_model . short_description = m . pop ( ' description ' )
@ -500,7 +522,14 @@ class Exporter:
ct_model . license = m . pop ( ' license ' )
ct_model . version = m . pop ( ' version ' )
ct_model . user_defined_metadata . update ( { k : str ( v ) for k , v in m . items ( ) } )
ct_model . save ( str ( f ) )
try :
ct_model . save ( str ( f ) ) # save *.mlpackage
except Exception as e :
LOGGER . warning (
f ' { prefix } WARNING ⚠️ CoreML export to *.mlpackage failed ( { e } ), reverting to *.mlmodel export. '
f ' Known coremltools Python 3.11 and Windows bugs https://github.com/apple/coremltools/issues/1928. ' )
f = f . with_suffix ( ' .mlmodel ' )
ct_model . save ( str ( f ) )
return f , ct_model
@try_export
@ -546,7 +575,7 @@ class Exporter:
if self . args . dynamic :
shape = self . im . shape
if shape [ 0 ] < = 1 :
LOGGER . warning ( f ' { prefix } WARNING ⚠️ --dynamic model requires maximum --batch-size argument ' )
LOGGER . warning ( f " { prefix } WARNING ⚠️ ' dynamic=True ' model requires max batch size, i.e. ' batch=16 ' " )
profile = builder . create_optimization_profile ( )
for inp in inputs :
profile . set_shape ( inp . name , ( 1 , * shape [ 1 : ] ) , ( max ( 1 , shape [ 0 ] / / 2 ) , * shape [ 1 : ] ) , shape )
@ -805,7 +834,7 @@ class Exporter:
populator . populate ( )
tmp_file . unlink ( )
def _pipeline_coreml ( self , model , prefix = colorstr ( ' CoreML Pipeline: ' ) ) :
def _pipeline_coreml ( self , model , weights_dir = None , prefix = colorstr ( ' CoreML Pipeline: ' ) ) :
""" YOLOv8 CoreML pipeline. """
import coremltools as ct # noqa
@ -853,7 +882,7 @@ class Exporter:
# print(spec.description)
# Model from spec
model = ct . models . MLModel ( spec )
model = ct . models . MLModel ( spec , weights_dir = weights_dir )
# 3. Create NMS protobuf
nms_spec = ct . proto . Model_pb2 . Model ( )
@ -912,7 +941,7 @@ class Exporter:
' Confidence threshold ' : str ( nms . confidenceThreshold ) } )
# Save the model
model = ct . models . MLModel ( pipeline . spec )
model = ct . models . MLModel ( pipeline . spec , weights_dir = weights_dir )
model . input_description [ ' image ' ] = ' Input image '
model . input_description [ ' iouThreshold ' ] = f ' (optional) IOU threshold override (default: { nms . iouThreshold } ) '
model . input_description [ ' confidenceThreshold ' ] = \