From 17bf48113600360a916d070a640d99006a700aaf Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 19:14:20 -0300 Subject: [PATCH 01/11] extract the yolov12 state_dict before upload --- roboflow/util/model_processor.py | 78 +++++++++----------------------- 1 file changed, 22 insertions(+), 56 deletions(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index b7c6a16e..234d5542 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -57,9 +57,6 @@ def _get_processor_function(model_type: str) -> Callable: if "yolonas" in model_type: return _process_yolonas - if "yolov12" in model_type: - return _process_yolov12 - return _process_yolo @@ -110,29 +107,38 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) + + elif "yolov12" in model_type: + print( + "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " + "or through the Roboflow platform" + ) + model = torch.load(os.path.join(model_path, filename)) - if isinstance(model["model"].names, list): - class_names = model["model"].names + model_instance = model["model"] if "model" in model and model["model"] is not None else model["ema"] + + if isinstance(model_instance.names, list): + class_names = model_instance.names else: class_names = [] - for i, val in enumerate(model["model"].names): - class_names.append((val, model["model"].names[val])) + for i, val in enumerate(model_instance.names): + class_names.append((val, model_instance.names[val])) class_names.sort(key=lambda x: x[0]) class_names = [x[1] for x in class_names] if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: # try except for backwards compatibility with older versions of ultralytics if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): - nc = model["model"].yaml["nc"] + nc = model_instance.yaml["nc"] args = model["train_args"] else: - nc = model["model"].nc - args = model["model"].args + nc = model_instance.nc + args = model_instance.args try: model_artifacts = { "names": class_names, - "yaml": model["model"].yaml, + "yaml": model_instance.yaml, "nc": nc, "args": {k: val for k, val in args.items() if ((k == "model") or (k == "imgsz") or (k == "batch"))}, "ultralytics_version": ultralytics.__version__, @@ -141,7 +147,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: except Exception: model_artifacts = { "names": class_names, - "yaml": model["model"].yaml, + "yaml": model_instance.yaml, "nc": nc, "args": { k: val for k, val in args.__dict__.items() if ((k == "model") or (k == "imgsz") or (k == "batch")) @@ -157,20 +163,20 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: model_artifacts = { "names": class_names, - "nc": model["model"].nc, + "nc": model_instance.nc, "args": { "imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"], "batch": opts["batch_size"], }, "model_type": model_type, } - if hasattr(model["model"], "yaml"): - model_artifacts["yaml"] = model["model"].yaml + if hasattr(model_instance, "yaml"): + model_artifacts["yaml"] = model_instance.yaml with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp: json.dump(model_artifacts, fp) - torch.save(model["model"].state_dict(), os.path.join(model_path, "state_dict.pt")) + torch.save(model_instance.state_dict(), os.path.join(model_path, "state_dict.pt")) list_files = [ "results.csv", @@ -196,46 +202,6 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: return zip_file_name -def _process_yolov12(model_type: str, model_path: str, filename: str) -> str: - # For YOLOv12, since it uses a special Ultralytics version, - # state dict extraction and model artifacts are handled during model conversion - - print( - "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " - "or through the Roboflow platform" - ) - - # Check if model_path exists - if not os.path.exists(model_path): - raise FileNotFoundError(f"Model path {model_path} does not exist.") - - # Find any .pt file in model path - model_files = os.listdir(model_path) - pt_file = next((f for f in model_files if f.endswith(".pt")), None) - - if pt_file is None: - raise RuntimeError("No .pt model file found in the provided path") - - # Copy the .pt file to weights.pt if not already named weights.pt - if pt_file != "weights.pt": - shutil.copy(os.path.join(model_path, pt_file), os.path.join(model_path, "weights.pt")) - - required_files = ["weights.pt"] - - optional_files = ["results.csv", "results.png", "model_artifacts.json"] - - zip_file_name = "roboflow_deploy.zip" - with zipfile.ZipFile(os.path.join(model_path, zip_file_name), "w") as zipMe: - for file in required_files: - zipMe.write(os.path.join(model_path, file), arcname=file, compress_type=zipfile.ZIP_DEFLATED) - - for file in optional_files: - if os.path.exists(os.path.join(model_path, file)): - zipMe.write(os.path.join(model_path, file), arcname=file, compress_type=zipfile.ZIP_DEFLATED) - - return zip_file_name - - def _process_huggingface( model_type: str, model_path: str, filename: str = "fine-tuned-paligemma-3b-pt-224.f16.npz" ) -> str: From 7e86dc70443fd0ef653f7715da2839d367101ff8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:15:38 +0000 Subject: [PATCH 02/11] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/util/model_processor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 234d5542..ed0e2f53 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -107,7 +107,6 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) - elif "yolov12" in model_type: print( "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " From f58bbdb9eebd128865d190d63a9ad5b6de81f1e3 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 19:16:22 -0300 Subject: [PATCH 03/11] bump version --- roboflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/__init__.py b/roboflow/__init__.py index ce9289d5..ad24004d 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -15,7 +15,7 @@ from roboflow.models import CLIPModel, GazeModel # noqa: F401 from roboflow.util.general import write_line -__version__ = "1.1.58" +__version__ = "1.1.59" def check_key(api_key, model, notebook, num_retries=0): From 976af370bc8c85428ff212abffae9e83ec24b779 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 20:32:54 -0300 Subject: [PATCH 04/11] load torch and ultralytics --- roboflow/util/model_processor.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index ed0e2f53..92579489 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -108,6 +108,16 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True) elif "yolov12" in model_type: + try: + import torch + import ultralytics + + except ImportError: + raise RuntimeError( + "The ultralytics python package is required to deploy yolov12" + " models. Please install it with `pip install ultralytics`" + ) + print( "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " "or through the Roboflow platform" From 054ca0da68342d782dab9df73b9996e56fa7a16f Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 20:33:08 -0300 Subject: [PATCH 05/11] process yolov12 --- roboflow/util/model_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 92579489..8cc035dc 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -136,7 +136,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: class_names.sort(key=lambda x: x[0]) class_names = [x[1] for x in class_names] - if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type: + if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type or "yolov12" in model_type: # try except for backwards compatibility with older versions of ultralytics if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): nc = model_instance.yaml["nc"] From 1d59cfb3037dfcd29141e3f3a18b170fc8edfd8a Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 20:33:51 -0300 Subject: [PATCH 06/11] weights_only=False because torch by default set it to True on 2.6+ --- roboflow/util/model_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 8cc035dc..ef2c34ae 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -123,7 +123,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: "or through the Roboflow platform" ) - model = torch.load(os.path.join(model_path, filename)) + model = torch.load(os.path.join(model_path, filename), map_location="cpu", weights_only=False) model_instance = model["model"] if "model" in model and model["model"] is not None else model["ema"] From 1ed67dd02bfc120071d54c217a99e3a842a94273 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 23:34:04 +0000 Subject: [PATCH 07/11] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/util/model_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index ef2c34ae..0cf34673 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -117,7 +117,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: "The ultralytics python package is required to deploy yolov12" " models. Please install it with `pip install ultralytics`" ) - + print( "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " "or through the Roboflow platform" From 564a7a244f11704e1513cb1a4217cd7022647239 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 22:19:43 -0300 Subject: [PATCH 08/11] . --- roboflow/util/model_processor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index 0cf34673..ef572a3d 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -123,7 +123,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: "or through the Roboflow platform" ) - model = torch.load(os.path.join(model_path, filename), map_location="cpu", weights_only=False) + model = torch.load(os.path.join(model_path, filename), weights_only=False) model_instance = model["model"] if "model" in model and model["model"] is not None else model["ema"] @@ -138,7 +138,7 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type or "yolov12" in model_type: # try except for backwards compatibility with older versions of ultralytics - if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"): + if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11") or model_type.startswith("yolov12"): nc = model_instance.yaml["nc"] args = model["train_args"] else: From 8f06e286ad05093c03f96b6134a280d65f348291 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 01:21:48 +0000 Subject: [PATCH 09/11] =?UTF-8?q?fix(pre=5Fcommit):=20=F0=9F=8E=A8=20auto?= =?UTF-8?q?=20format=20pre-commit=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- roboflow/util/model_processor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index ef572a3d..fd26c84e 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -138,7 +138,12 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type or "yolov12" in model_type: # try except for backwards compatibility with older versions of ultralytics - if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11") or model_type.startswith("yolov12"): + if ( + "-cls" in model_type + or model_type.startswith("yolov10") + or model_type.startswith("yolov11") + or model_type.startswith("yolov12") + ): nc = model_instance.yaml["nc"] args = model["train_args"] else: From 113de54c7ea02afb3925a1a922c490914c19cbe2 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Wed, 2 Apr 2025 22:29:31 -0300 Subject: [PATCH 10/11] disclaimer --- roboflow/util/model_processor.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/roboflow/util/model_processor.py b/roboflow/util/model_processor.py index ef572a3d..8faf6707 100644 --- a/roboflow/util/model_processor.py +++ b/roboflow/util/model_processor.py @@ -115,14 +115,18 @@ def _process_yolo(model_type: str, model_path: str, filename: str) -> str: except ImportError: raise RuntimeError( "The ultralytics python package is required to deploy yolov12" - " models. Please install it with `pip install ultralytics`" + " models. Please install it from `https://github.com/sunsmarterjie/yolov12`" ) print( - "Note: Model must be trained using ultralytics from https://github.com/sunsmarterjie/yolov12 " - "or through the Roboflow platform" + "\n!!! ATTENTION !!!\n" + "Model must be trained and uploaded using ultralytics from https://github.com/sunsmarterjie/yolov12\n" + "or through the Roboflow platform\n" + "!!! ATTENTION !!!\n" ) + print_warn_for_wrong_dependencies_versions([("ultralytics", "==", "8.3.63")], ask_to_continue=True) + model = torch.load(os.path.join(model_path, filename), weights_only=False) model_instance = model["model"] if "model" in model and model["model"] is not None else model["ema"] From 85f44078cad9788d94471f99b3346fd0eda17fa4 Mon Sep 17 00:00:00 2001 From: Leandro Rosemberg Date: Thu, 3 Apr 2025 13:11:50 -0300 Subject: [PATCH 11/11] bump version --- roboflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roboflow/__init__.py b/roboflow/__init__.py index ad24004d..e754cf0f 100644 --- a/roboflow/__init__.py +++ b/roboflow/__init__.py @@ -15,7 +15,7 @@ from roboflow.models import CLIPModel, GazeModel # noqa: F401 from roboflow.util.general import write_line -__version__ = "1.1.59" +__version__ = "1.1.60" def check_key(api_key, model, notebook, num_retries=0):