|
3 | 3 | import copy
|
4 | 4 | import json
|
5 | 5 | import os
|
6 |
| -import shutil |
7 | 6 | import sys
|
8 | 7 | import time
|
9 | 8 | import zipfile
|
10 | 9 | from typing import TYPE_CHECKING, Optional, Union
|
11 | 10 |
|
12 | 11 | import requests
|
13 |
| -import yaml |
14 | 12 | from dotenv import load_dotenv
|
15 | 13 | from tqdm import tqdm
|
16 | 14 |
|
|
34 | 32 | from roboflow.models.semantic_segmentation import SemanticSegmentationModel
|
35 | 33 | from roboflow.util.annotations import amend_data_yaml
|
36 | 34 | 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 |
38 | 37 |
|
39 | 38 | if TYPE_CHECKING:
|
40 | 39 | import numpy as np
|
@@ -478,294 +477,14 @@ def deploy(self, model_type: str, model_path: str, filename: str = "weights/best
|
478 | 477 | model_path (str): File path to the model weights to be uploaded.
|
479 | 478 | filename (str, optional): The name of the weights file. Defaults to "weights/best.pt".
|
480 | 479 | """
|
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) |
567 | 481 |
|
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") |
765 | 484 |
|
766 |
| - self.upload_zip(model_type, model_path) |
| 485 | + self._upload_zip(model_type, model_path, zip_file_name) |
767 | 486 |
|
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): |
769 | 488 | res = requests.get(
|
770 | 489 | f"{API_URL}/{self.workspace}/{self.project}/{self.version}"
|
771 | 490 | f"/uploadModel?api_key={self.__api_key}&modelType={model_type}&nocache=true"
|
|
0 commit comments