Skip to content

Commit b5ed55b

Browse files
authored
Merge pull request #355 from roboflow/lean/model-processor
Model processor
2 parents 38e0ad5 + d217962 commit b5ed55b

File tree

4 files changed

+331
-291
lines changed

4 files changed

+331
-291
lines changed

roboflow/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from roboflow.models import CLIPModel, GazeModel # noqa: F401
1616
from roboflow.util.general import write_line
1717

18-
__version__ = "1.1.51"
18+
__version__ = "1.1.52"
1919

2020

2121
def check_key(api_key, model, notebook, num_retries=0):

roboflow/core/version.py

Lines changed: 7 additions & 288 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
import copy
44
import json
55
import os
6-
import shutil
76
import sys
87
import time
98
import zipfile
109
from typing import TYPE_CHECKING, Optional, Union
1110

1211
import requests
13-
import yaml
1412
from dotenv import load_dotenv
1513
from tqdm import tqdm
1614

@@ -34,7 +32,8 @@
3432
from roboflow.models.semantic_segmentation import SemanticSegmentationModel
3533
from roboflow.util.annotations import amend_data_yaml
3634
from roboflow.util.general import write_line
37-
from roboflow.util.versions import get_wrong_dependencies_versions, print_warn_for_wrong_dependencies_versions
35+
from roboflow.util.model_processor import process
36+
from roboflow.util.versions import get_wrong_dependencies_versions
3837

3938
if TYPE_CHECKING:
4039
import numpy as np
@@ -478,294 +477,14 @@ def deploy(self, model_type: str, model_path: str, filename: str = "weights/best
478477
model_path (str): File path to the model weights to be uploaded.
479478
filename (str, optional): The name of the weights file. Defaults to "weights/best.pt".
480479
"""
481-
if model_type.startswith("yolo11"):
482-
model_type = model_type.replace("yolo11", "yolov11")
483-
484-
supported_models = [
485-
"yolov5",
486-
"yolov7-seg",
487-
"yolov8",
488-
"yolov9",
489-
"yolonas",
490-
"paligemma",
491-
"paligemma2",
492-
"yolov10",
493-
"florence-2",
494-
"yolov11",
495-
]
496-
497-
if not any(supported_model in model_type for supported_model in supported_models):
498-
raise (ValueError(f"Model type {model_type} not supported. Supported models are {supported_models}"))
499-
500-
if model_type.startswith(("paligemma", "paligemma2", "florence-2")):
501-
if any(model in model_type for model in ["paligemma", "paligemma2", "florence-2"]):
502-
supported_hf_types = [
503-
"florence-2-base",
504-
"florence-2-large",
505-
"paligemma-3b-pt-224",
506-
"paligemma-3b-pt-448",
507-
"paligemma-3b-pt-896",
508-
"paligemma2-3b-pt-224",
509-
"paligemma2-3b-pt-448",
510-
"paligemma2-3b-pt-896",
511-
]
512-
if model_type not in supported_hf_types:
513-
raise RuntimeError(
514-
f"{model_type} not supported for this type of upload."
515-
f"Supported upload types are {supported_hf_types}"
516-
)
517-
self.deploy_huggingface(model_type, model_path, filename)
518-
return
519-
520-
if "yolonas" in model_type:
521-
self.deploy_yolonas(model_type, model_path, filename)
522-
return
523-
524-
if "yolov8" in model_type:
525-
try:
526-
import torch
527-
import ultralytics
528-
529-
except ImportError:
530-
raise RuntimeError(
531-
"The ultralytics python package is required to deploy yolov8"
532-
" models. Please install it with `pip install ultralytics`"
533-
)
534-
535-
print_warn_for_wrong_dependencies_versions([("ultralytics", "==", "8.0.196")], ask_to_continue=True)
536-
537-
elif "yolov10" in model_type:
538-
try:
539-
import torch
540-
import ultralytics
541-
542-
except ImportError:
543-
raise RuntimeError(
544-
"The ultralytics python package is required to deploy yolov10"
545-
" models. Please install it with `pip install ultralytics`"
546-
)
547-
548-
elif "yolov5" in model_type or "yolov7" in model_type or "yolov9" in model_type:
549-
try:
550-
import torch
551-
except ImportError:
552-
raise RuntimeError(
553-
"The torch python package is required to deploy yolov5 models."
554-
" Please install it with `pip install torch`"
555-
)
556-
557-
elif "yolov11" in model_type:
558-
try:
559-
import torch
560-
import ultralytics
561-
562-
except ImportError:
563-
raise RuntimeError(
564-
"The ultralytics python package is required to deploy yolov10"
565-
" models. Please install it with `pip install ultralytics`"
566-
)
480+
zip_file_name = process(model_type, model_path, filename)
567481

568-
print_warn_for_wrong_dependencies_versions([("ultralytics", ">=", "8.3.0")], ask_to_continue=True)
569-
570-
model = torch.load(os.path.join(model_path, filename))
571-
572-
if isinstance(model["model"].names, list):
573-
class_names = model["model"].names
574-
else:
575-
class_names = []
576-
for i, val in enumerate(model["model"].names):
577-
class_names.append((val, model["model"].names[val]))
578-
class_names.sort(key=lambda x: x[0])
579-
class_names = [x[1] for x in class_names]
580-
581-
if "yolov8" in model_type or "yolov10" in model_type or "yolov11" in model_type:
582-
# try except for backwards compatibility with older versions of ultralytics
583-
if "-cls" in model_type or model_type.startswith("yolov10") or model_type.startswith("yolov11"):
584-
nc = model["model"].yaml["nc"]
585-
args = model["train_args"]
586-
else:
587-
nc = model["model"].nc
588-
args = model["model"].args
589-
try:
590-
model_artifacts = {
591-
"names": class_names,
592-
"yaml": model["model"].yaml,
593-
"nc": nc,
594-
"args": {k: val for k, val in args.items() if ((k == "model") or (k == "imgsz") or (k == "batch"))},
595-
"ultralytics_version": ultralytics.__version__,
596-
"model_type": model_type,
597-
}
598-
except Exception:
599-
model_artifacts = {
600-
"names": class_names,
601-
"yaml": model["model"].yaml,
602-
"nc": nc,
603-
"args": {
604-
k: val
605-
for k, val in args.__dict__.items()
606-
if ((k == "model") or (k == "imgsz") or (k == "batch"))
607-
},
608-
"ultralytics_version": ultralytics.__version__,
609-
"model_type": model_type,
610-
}
611-
elif "yolov5" in model_type or "yolov7" in model_type or "yolov9" in model_type:
612-
# parse from yaml for yolov5
613-
614-
with open(os.path.join(model_path, "opt.yaml")) as stream:
615-
opts = yaml.safe_load(stream)
616-
617-
model_artifacts = {
618-
"names": class_names,
619-
"nc": model["model"].nc,
620-
"args": {
621-
"imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"],
622-
"batch": opts["batch_size"],
623-
},
624-
"model_type": model_type,
625-
}
626-
if hasattr(model["model"], "yaml"):
627-
model_artifacts["yaml"] = model["model"].yaml
628-
629-
with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp:
630-
json.dump(model_artifacts, fp)
631-
632-
torch.save(model["model"].state_dict(), os.path.join(model_path, "state_dict.pt"))
633-
634-
list_files = [
635-
"results.csv",
636-
"results.png",
637-
"model_artifacts.json",
638-
"state_dict.pt",
639-
]
640-
641-
with zipfile.ZipFile(os.path.join(model_path, "roboflow_deploy.zip"), "w") as zipMe:
642-
for file in list_files:
643-
if os.path.exists(os.path.join(model_path, file)):
644-
zipMe.write(
645-
os.path.join(model_path, file),
646-
arcname=file,
647-
compress_type=zipfile.ZIP_DEFLATED,
648-
)
649-
else:
650-
if file in ["model_artifacts.json", "state_dict.pt"]:
651-
raise (ValueError(f"File {file} not found. Please make sure to provide a valid model path."))
652-
653-
self.upload_zip(model_type, model_path)
654-
655-
def deploy_huggingface(
656-
self, model_type: str, model_path: str, filename: str = "fine-tuned-paligemma-3b-pt-224.f16.npz"
657-
) -> None:
658-
# Check if model_path exists
659-
if not os.path.exists(model_path):
660-
raise FileNotFoundError(f"Model path {model_path} does not exist.")
661-
model_files = os.listdir(model_path)
662-
print(f"Model files found in {model_path}: {model_files}")
663-
664-
files_to_deploy = []
665-
666-
# Find first .npz file in model_path
667-
npz_filename = next((file for file in model_files if file.endswith(".npz")), None)
668-
if any([file.endswith(".safetensors") for file in model_files]):
669-
print(f"Found .safetensors file in model path. Deploying PyTorch {model_type} model.")
670-
necessary_files = [
671-
"preprocessor_config.json",
672-
"special_tokens_map.json",
673-
"tokenizer_config.json",
674-
"tokenizer.json",
675-
]
676-
for file in necessary_files:
677-
if file not in model_files:
678-
print("Missing necessary file", file)
679-
res = input("Do you want to continue? (y/n)")
680-
if res.lower() != "y":
681-
exit(1)
682-
for file in model_files:
683-
files_to_deploy.append(file)
684-
elif npz_filename is not None:
685-
print(f"Found .npz file {npz_filename} in model path. Deploying JAX PaliGemma model.")
686-
files_to_deploy.append(npz_filename)
687-
else:
688-
raise FileNotFoundError(f"No .npz or .safetensors file found in model path {model_path}.")
689-
690-
if len(files_to_deploy) == 0:
691-
raise FileNotFoundError(f"No valid files found in model path {model_path}.")
692-
print(f"Zipping files for deploy: {files_to_deploy}")
693-
694-
import tarfile
695-
696-
with tarfile.open(os.path.join(model_path, "roboflow_deploy.tar"), "w") as tar:
697-
for file in files_to_deploy:
698-
tar.add(os.path.join(model_path, file), arcname=file)
699-
700-
print("Uploading to Roboflow... May take several minutes.")
701-
self.upload_zip(model_type, model_path, "roboflow_deploy.tar")
702-
703-
def deploy_yolonas(self, model_type: str, model_path: str, filename: str = "weights/best.pt") -> None:
704-
try:
705-
import torch
706-
except ImportError:
707-
raise RuntimeError(
708-
"The torch python package is required to deploy yolonas models."
709-
" Please install it with `pip install torch`"
710-
)
711-
712-
model = torch.load(os.path.join(model_path, filename), map_location="cpu")
713-
class_names = model["processing_params"]["class_names"]
714-
715-
opt_path = os.path.join(model_path, "opt.yaml")
716-
if not os.path.exists(opt_path):
717-
raise RuntimeError(
718-
f"You must create an opt.yaml file at {os.path.join(model_path, '')} of the format:\n"
719-
f"imgsz: <resolution of model>\n"
720-
f"batch_size: <batch size of inference model>\n"
721-
f"architecture: <one of [yolo_nas_s, yolo_nas_m, yolo_nas_l]."
722-
f"s, m, l refer to small, medium, large architecture sizes, respectively>\n"
723-
)
724-
with open(os.path.join(model_path, "opt.yaml")) as stream:
725-
opts = yaml.safe_load(stream)
726-
required_keys = ["imgsz", "batch_size", "architecture"]
727-
for key in required_keys:
728-
if key not in opts:
729-
raise RuntimeError(f"{opt_path} lacks required key {key}. Required keys: {required_keys}")
730-
731-
model_artifacts = {
732-
"names": class_names,
733-
"nc": len(class_names),
734-
"args": {
735-
"imgsz": opts["imgsz"] if "imgsz" in opts else opts["img_size"],
736-
"batch": opts["batch_size"],
737-
"architecture": opts["architecture"],
738-
},
739-
"model_type": model_type,
740-
}
741-
742-
with open(os.path.join(model_path, "model_artifacts.json"), "w") as fp:
743-
json.dump(model_artifacts, fp)
744-
745-
shutil.copy(os.path.join(model_path, filename), os.path.join(model_path, "state_dict.pt"))
746-
747-
list_files = [
748-
"results.json",
749-
"results.png",
750-
"model_artifacts.json",
751-
"state_dict.pt",
752-
]
753-
754-
with zipfile.ZipFile(os.path.join(model_path, "roboflow_deploy.zip"), "w") as zipMe:
755-
for file in list_files:
756-
if os.path.exists(os.path.join(model_path, file)):
757-
zipMe.write(
758-
os.path.join(model_path, file),
759-
arcname=file,
760-
compress_type=zipfile.ZIP_DEFLATED,
761-
)
762-
else:
763-
if file in ["model_artifacts.json", filename]:
764-
raise (ValueError(f"File {file} not found. Please make sure to provide a valid model path."))
482+
if zip_file_name is None:
483+
raise RuntimeError("Failed to process model")
765484

766-
self.upload_zip(model_type, model_path)
485+
self._upload_zip(model_type, model_path, zip_file_name)
767486

768-
def upload_zip(self, model_type: str, model_path: str, model_file_name: str = "roboflow_deploy.zip"):
487+
def _upload_zip(self, model_type: str, model_path: str, model_file_name: str):
769488
res = requests.get(
770489
f"{API_URL}/{self.workspace}/{self.project}/{self.version}"
771490
f"/uploadModel?api_key={self.__api_key}&modelType={model_type}&nocache=true"

roboflow/roboflowpy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717

1818
def login(args):
19-
roboflow.login()
19+
roboflow.login(force=args.force)
2020

2121

2222
def _parse_url(url):
@@ -78,9 +78,10 @@ def upload_image(args):
7878
def upload_model(args):
7979
rf = roboflow.Roboflow(args.api_key)
8080
workspace = rf.workspace(args.workspace)
81+
82+
# Deploy to specific version
8183
project = workspace.project(args.project)
8284
version = project.version(args.version_number)
83-
print(args.model_type, args.model_path, args.filename)
8485
version.deploy(str(args.model_type), str(args.model_path), str(args.filename))
8586

8687

@@ -536,6 +537,12 @@ def _add_get_workspace_project_version_parser(subparsers):
536537

537538
def _add_login_parser(subparsers):
538539
login_parser = subparsers.add_parser("login", help="Log in to Roboflow")
540+
login_parser.add_argument(
541+
"-f",
542+
dest="force",
543+
help="force login",
544+
action="store_true",
545+
)
539546
login_parser.set_defaults(func=login)
540547

541548

0 commit comments

Comments
 (0)