|
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