ultralytics 8.3.125__py3-none-any.whl → 8.3.126__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tests/test_cuda.py +71 -66
 - ultralytics/__init__.py +1 -1
 - ultralytics/cfg/default.yaml +1 -1
 - ultralytics/engine/trainer.py +1 -0
 - ultralytics/utils/__init__.py +4 -0
 - ultralytics/utils/autodevice.py +175 -0
 - ultralytics/utils/benchmarks.py +2 -2
 - ultralytics/utils/checks.py +2 -2
 - ultralytics/utils/torch_utils.py +18 -5
 - {ultralytics-8.3.125.dist-info → ultralytics-8.3.126.dist-info}/METADATA +1 -1
 - {ultralytics-8.3.125.dist-info → ultralytics-8.3.126.dist-info}/RECORD +15 -14
 - {ultralytics-8.3.125.dist-info → ultralytics-8.3.126.dist-info}/WHEEL +0 -0
 - {ultralytics-8.3.125.dist-info → ultralytics-8.3.126.dist-info}/entry_points.txt +0 -0
 - {ultralytics-8.3.125.dist-info → ultralytics-8.3.126.dist-info}/licenses/LICENSE +0 -0
 - {ultralytics-8.3.125.dist-info → ultralytics-8.3.126.dist-info}/top_level.txt +0 -0
 
    
        tests/test_cuda.py
    CHANGED
    
    | 
         @@ -10,8 +10,18 @@ from tests import CUDA_DEVICE_COUNT, CUDA_IS_AVAILABLE, MODEL, SOURCE 
     | 
|
| 
       10 
10 
     | 
    
         
             
            from ultralytics import YOLO
         
     | 
| 
       11 
11 
     | 
    
         
             
            from ultralytics.cfg import TASK2DATA, TASK2MODEL, TASKS
         
     | 
| 
       12 
12 
     | 
    
         
             
            from ultralytics.utils import ASSETS, WEIGHTS_DIR
         
     | 
| 
      
 13 
     | 
    
         
            +
            from ultralytics.utils.autodevice import GPUInfo
         
     | 
| 
       13 
14 
     | 
    
         
             
            from ultralytics.utils.checks import check_amp
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
      
 16 
     | 
    
         
            +
            # Try to find idle devices if CUDA is available
         
     | 
| 
      
 17 
     | 
    
         
            +
            DEVICES = []
         
     | 
| 
      
 18 
     | 
    
         
            +
            if CUDA_IS_AVAILABLE:
         
     | 
| 
      
 19 
     | 
    
         
            +
                gpu_info = GPUInfo()
         
     | 
| 
      
 20 
     | 
    
         
            +
                gpu_info.print_status()
         
     | 
| 
      
 21 
     | 
    
         
            +
                idle_gpus = gpu_info.select_idle_gpu(count=2, min_memory_mb=2048)
         
     | 
| 
      
 22 
     | 
    
         
            +
                if idle_gpus:
         
     | 
| 
      
 23 
     | 
    
         
            +
                    DEVICES = idle_gpus
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       15 
25 
     | 
    
         | 
| 
       16 
26 
     | 
    
         
             
            def test_checks():
         
     | 
| 
       17 
27 
     | 
    
         
             
                """Validate CUDA settings against torch CUDA functions."""
         
     | 
| 
         @@ -19,16 +29,16 @@ def test_checks(): 
     | 
|
| 
       19 
29 
     | 
    
         
             
                assert torch.cuda.device_count() == CUDA_DEVICE_COUNT
         
     | 
| 
       20 
30 
     | 
    
         | 
| 
       21 
31 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 32 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       23 
33 
     | 
    
         
             
            def test_amp():
         
     | 
| 
       24 
34 
     | 
    
         
             
                """Test AMP training checks."""
         
     | 
| 
       25 
     | 
    
         
            -
                model = YOLO("yolo11n.pt").model.cuda 
     | 
| 
      
 35 
     | 
    
         
            +
                model = YOLO("yolo11n.pt").model.to(f"cuda:{DEVICES[0]}")
         
     | 
| 
       26 
36 
     | 
    
         
             
                assert check_amp(model)
         
     | 
| 
       27 
37 
     | 
    
         | 
| 
       28 
38 
     | 
    
         | 
| 
       29 
39 
     | 
    
         
             
            @pytest.mark.slow
         
     | 
| 
       30 
40 
     | 
    
         
             
            @pytest.mark.skipif(True, reason="CUDA export tests disabled pending additional Ultralytics GPU server availability")
         
     | 
| 
       31 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 41 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       32 
42 
     | 
    
         
             
            @pytest.mark.parametrize(
         
     | 
| 
       33 
43 
     | 
    
         
             
                "task, dynamic, int8, half, batch",
         
     | 
| 
       34 
44 
     | 
    
         
             
                [  # generate all combinations but exclude those where both int8 and half are True
         
     | 
| 
         @@ -40,16 +50,7 @@ def test_amp(): 
     | 
|
| 
       40 
50 
     | 
    
         
             
                ],
         
     | 
| 
       41 
51 
     | 
    
         
             
            )
         
     | 
| 
       42 
52 
     | 
    
         
             
            def test_export_engine_matrix(task, dynamic, int8, half, batch):
         
     | 
| 
       43 
     | 
    
         
            -
                """
         
     | 
| 
       44 
     | 
    
         
            -
                Test YOLO model export to TensorRT format for various configurations and run inference.
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                Args:
         
     | 
| 
       47 
     | 
    
         
            -
                    task (str): Task type like 'detect', 'segment', etc.
         
     | 
| 
       48 
     | 
    
         
            -
                    dynamic (bool): Whether to use dynamic input size.
         
     | 
| 
       49 
     | 
    
         
            -
                    int8 (bool): Whether to use INT8 precision.
         
     | 
| 
       50 
     | 
    
         
            -
                    half (bool): Whether to use FP16 precision.
         
     | 
| 
       51 
     | 
    
         
            -
                    batch (int): Batch size for export.
         
     | 
| 
       52 
     | 
    
         
            -
                """
         
     | 
| 
      
 53 
     | 
    
         
            +
                """Test YOLO model export to TensorRT format for various configurations and run inference."""
         
     | 
| 
       53 
54 
     | 
    
         
             
                file = YOLO(TASK2MODEL[task]).export(
         
     | 
| 
       54 
55 
     | 
    
         
             
                    format="engine",
         
     | 
| 
       55 
56 
     | 
    
         
             
                    imgsz=32,
         
     | 
| 
         @@ -60,105 +61,109 @@ def test_export_engine_matrix(task, dynamic, int8, half, batch): 
     | 
|
| 
       60 
61 
     | 
    
         
             
                    data=TASK2DATA[task],
         
     | 
| 
       61 
62 
     | 
    
         
             
                    workspace=1,  # reduce workspace GB for less resource utilization during testing
         
     | 
| 
       62 
63 
     | 
    
         
             
                    simplify=True,  # use 'onnxslim'
         
     | 
| 
      
 64 
     | 
    
         
            +
                    device=DEVICES[0],
         
     | 
| 
       63 
65 
     | 
    
         
             
                )
         
     | 
| 
       64 
     | 
    
         
            -
                YOLO(file)([SOURCE] * batch, imgsz=64 if dynamic else 32)  # exported model inference
         
     | 
| 
      
 66 
     | 
    
         
            +
                YOLO(file)([SOURCE] * batch, imgsz=64 if dynamic else 32, device=DEVICES[0])  # exported model inference
         
     | 
| 
       65 
67 
     | 
    
         
             
                Path(file).unlink()  # cleanup
         
     | 
| 
       66 
68 
     | 
    
         
             
                Path(file).with_suffix(".cache").unlink() if int8 else None  # cleanup INT8 cache
         
     | 
| 
       67 
69 
     | 
    
         | 
| 
       68 
70 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 71 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       70 
72 
     | 
    
         
             
            def test_train():
         
     | 
| 
       71 
73 
     | 
    
         
             
                """Test model training on a minimal dataset using available CUDA devices."""
         
     | 
| 
       72 
     | 
    
         
            -
                device =  
     | 
| 
      
 74 
     | 
    
         
            +
                device = DEVICES if len(DEVICES) > 1 else DEVICES[0]
         
     | 
| 
       73 
75 
     | 
    
         
             
                YOLO(MODEL).train(data="coco8.yaml", imgsz=64, epochs=1, device=device)  # requires imgsz>=64
         
     | 
| 
       74 
76 
     | 
    
         | 
| 
       75 
77 
     | 
    
         | 
| 
       76 
78 
     | 
    
         
             
            @pytest.mark.slow
         
     | 
| 
       77 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 79 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       78 
80 
     | 
    
         
             
            def test_predict_multiple_devices():
         
     | 
| 
       79 
81 
     | 
    
         
             
                """Validate model prediction consistency across CPU and CUDA devices."""
         
     | 
| 
       80 
82 
     | 
    
         
             
                model = YOLO("yolo11n.pt")
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                # Test CPU
         
     | 
| 
       81 
85 
     | 
    
         
             
                model = model.cpu()
         
     | 
| 
       82 
86 
     | 
    
         
             
                assert str(model.device) == "cpu"
         
     | 
| 
       83 
     | 
    
         
            -
                _ = model(SOURCE) 
     | 
| 
      
 87 
     | 
    
         
            +
                _ = model(SOURCE)
         
     | 
| 
       84 
88 
     | 
    
         
             
                assert str(model.device) == "cpu"
         
     | 
| 
       85 
89 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
                 
     | 
| 
       87 
     | 
    
         
            -
                 
     | 
| 
       88 
     | 
    
         
            -
                 
     | 
| 
       89 
     | 
    
         
            -
                assert str(model.device) ==  
     | 
| 
      
 90 
     | 
    
         
            +
                # Test CUDA
         
     | 
| 
      
 91 
     | 
    
         
            +
                cuda_device = f"cuda:{DEVICES[0]}"
         
     | 
| 
      
 92 
     | 
    
         
            +
                model = model.to(cuda_device)
         
     | 
| 
      
 93 
     | 
    
         
            +
                assert str(model.device) == cuda_device
         
     | 
| 
      
 94 
     | 
    
         
            +
                _ = model(SOURCE)
         
     | 
| 
      
 95 
     | 
    
         
            +
                assert str(model.device) == cuda_device
         
     | 
| 
       90 
96 
     | 
    
         | 
| 
      
 97 
     | 
    
         
            +
                # Test CPU again
         
     | 
| 
       91 
98 
     | 
    
         
             
                model = model.cpu()
         
     | 
| 
       92 
99 
     | 
    
         
             
                assert str(model.device) == "cpu"
         
     | 
| 
       93 
     | 
    
         
            -
                _ = model(SOURCE) 
     | 
| 
      
 100 
     | 
    
         
            +
                _ = model(SOURCE)
         
     | 
| 
       94 
101 
     | 
    
         
             
                assert str(model.device) == "cpu"
         
     | 
| 
       95 
102 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                 
     | 
| 
       97 
     | 
    
         
            -
                 
     | 
| 
       98 
     | 
    
         
            -
                 
     | 
| 
       99 
     | 
    
         
            -
                 
     | 
| 
      
 103 
     | 
    
         
            +
                # Test CUDA again
         
     | 
| 
      
 104 
     | 
    
         
            +
                model = model.to(cuda_device)
         
     | 
| 
      
 105 
     | 
    
         
            +
                assert str(model.device) == cuda_device
         
     | 
| 
      
 106 
     | 
    
         
            +
                _ = model(SOURCE)
         
     | 
| 
      
 107 
     | 
    
         
            +
                assert str(model.device) == cuda_device
         
     | 
| 
       100 
108 
     | 
    
         | 
| 
       101 
109 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 110 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       103 
111 
     | 
    
         
             
            def test_autobatch():
         
     | 
| 
       104 
112 
     | 
    
         
             
                """Check optimal batch size for YOLO model training using autobatch utility."""
         
     | 
| 
       105 
113 
     | 
    
         
             
                from ultralytics.utils.autobatch import check_train_batch_size
         
     | 
| 
       106 
114 
     | 
    
         | 
| 
       107 
     | 
    
         
            -
                check_train_batch_size(YOLO(MODEL).model.cuda 
     | 
| 
      
 115 
     | 
    
         
            +
                check_train_batch_size(YOLO(MODEL).model.to(f"cuda:{DEVICES[0]}"), imgsz=128, amp=True)
         
     | 
| 
       108 
116 
     | 
    
         | 
| 
       109 
117 
     | 
    
         | 
| 
       110 
118 
     | 
    
         
             
            @pytest.mark.slow
         
     | 
| 
       111 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 119 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       112 
120 
     | 
    
         
             
            def test_utils_benchmarks():
         
     | 
| 
       113 
121 
     | 
    
         
             
                """Profile YOLO models for performance benchmarks."""
         
     | 
| 
       114 
122 
     | 
    
         
             
                from ultralytics.utils.benchmarks import ProfileModels
         
     | 
| 
       115 
123 
     | 
    
         | 
| 
       116 
124 
     | 
    
         
             
                # Pre-export a dynamic engine model to use dynamic inference
         
     | 
| 
       117 
     | 
    
         
            -
                YOLO(MODEL).export(format="engine", imgsz=32, dynamic=True, batch=1)
         
     | 
| 
       118 
     | 
    
         
            -
                ProfileModels( 
     | 
| 
      
 125 
     | 
    
         
            +
                YOLO(MODEL).export(format="engine", imgsz=32, dynamic=True, batch=1, device=DEVICES[0])
         
     | 
| 
      
 126 
     | 
    
         
            +
                ProfileModels(
         
     | 
| 
      
 127 
     | 
    
         
            +
                    [MODEL],
         
     | 
| 
      
 128 
     | 
    
         
            +
                    imgsz=32,
         
     | 
| 
      
 129 
     | 
    
         
            +
                    half=False,
         
     | 
| 
      
 130 
     | 
    
         
            +
                    min_time=1,
         
     | 
| 
      
 131 
     | 
    
         
            +
                    num_timed_runs=3,
         
     | 
| 
      
 132 
     | 
    
         
            +
                    num_warmup_runs=1,
         
     | 
| 
      
 133 
     | 
    
         
            +
                    device=DEVICES[0],
         
     | 
| 
      
 134 
     | 
    
         
            +
                ).run()
         
     | 
| 
       119 
135 
     | 
    
         | 
| 
       120 
136 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
            @pytest.mark.skipif(not  
     | 
| 
      
 137 
     | 
    
         
            +
            @pytest.mark.skipif(not DEVICES, reason="No CUDA devices available")
         
     | 
| 
       122 
138 
     | 
    
         
             
            def test_predict_sam():
         
     | 
| 
       123 
     | 
    
         
            -
                """Test SAM model predictions using different prompts 
     | 
| 
      
 139 
     | 
    
         
            +
                """Test SAM model predictions using different prompts."""
         
     | 
| 
       124 
140 
     | 
    
         
             
                from ultralytics import SAM
         
     | 
| 
       125 
141 
     | 
    
         
             
                from ultralytics.models.sam import Predictor as SAMPredictor
         
     | 
| 
       126 
142 
     | 
    
         | 
| 
       127 
     | 
    
         
            -
                # Load a model
         
     | 
| 
       128 
143 
     | 
    
         
             
                model = SAM(WEIGHTS_DIR / "sam2.1_b.pt")
         
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
                # Display model information (optional)
         
     | 
| 
       131 
144 
     | 
    
         
             
                model.info()
         
     | 
| 
       132 
145 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
                # Run inference
         
     | 
| 
       134 
     | 
    
         
            -
                model(SOURCE, device=0)
         
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
       136 
     | 
    
         
            -
                 
     | 
| 
       137 
     | 
    
         
            -
                model( 
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                 
     | 
| 
       140 
     | 
    
         
            -
                model(ASSETS / "zidane.jpg", points=[900, 370], device=0)
         
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
                #  
     | 
| 
       143 
     | 
    
         
            -
                 
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
                 
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
                # Create SAMPredictor
         
     | 
| 
       155 
     | 
    
         
            -
                overrides = dict(conf=0.25, task="segment", mode="predict", imgsz=1024, model=WEIGHTS_DIR / "mobile_sam.pt")
         
     | 
| 
       156 
     | 
    
         
            -
                predictor = SAMPredictor(overrides=overrides)
         
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
                # Set image
         
     | 
| 
       159 
     | 
    
         
            -
                predictor.set_image(ASSETS / "zidane.jpg")  # set with image file
         
     | 
| 
      
 146 
     | 
    
         
            +
                # Run inference with various prompts
         
     | 
| 
      
 147 
     | 
    
         
            +
                model(SOURCE, device=DEVICES[0])
         
     | 
| 
      
 148 
     | 
    
         
            +
                model(SOURCE, bboxes=[439, 437, 524, 709], device=DEVICES[0])
         
     | 
| 
      
 149 
     | 
    
         
            +
                model(ASSETS / "zidane.jpg", points=[900, 370], device=DEVICES[0])
         
     | 
| 
      
 150 
     | 
    
         
            +
                model(ASSETS / "zidane.jpg", points=[900, 370], labels=[1], device=DEVICES[0])
         
     | 
| 
      
 151 
     | 
    
         
            +
                model(ASSETS / "zidane.jpg", points=[[900, 370]], labels=[1], device=DEVICES[0])
         
     | 
| 
      
 152 
     | 
    
         
            +
                model(ASSETS / "zidane.jpg", points=[[400, 370], [900, 370]], labels=[1, 1], device=DEVICES[0])
         
     | 
| 
      
 153 
     | 
    
         
            +
                model(ASSETS / "zidane.jpg", points=[[[900, 370], [1000, 100]]], labels=[[1, 1]], device=DEVICES[0])
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                # Test predictor
         
     | 
| 
      
 156 
     | 
    
         
            +
                predictor = SAMPredictor(
         
     | 
| 
      
 157 
     | 
    
         
            +
                    overrides=dict(
         
     | 
| 
      
 158 
     | 
    
         
            +
                        conf=0.25,
         
     | 
| 
      
 159 
     | 
    
         
            +
                        task="segment",
         
     | 
| 
      
 160 
     | 
    
         
            +
                        mode="predict",
         
     | 
| 
      
 161 
     | 
    
         
            +
                        imgsz=1024,
         
     | 
| 
      
 162 
     | 
    
         
            +
                        model=WEIGHTS_DIR / "mobile_sam.pt",
         
     | 
| 
      
 163 
     | 
    
         
            +
                        device=DEVICES[0],
         
     | 
| 
      
 164 
     | 
    
         
            +
                    )
         
     | 
| 
      
 165 
     | 
    
         
            +
                )
         
     | 
| 
      
 166 
     | 
    
         
            +
                predictor.set_image(ASSETS / "zidane.jpg")
         
     | 
| 
       160 
167 
     | 
    
         
             
                # predictor(bboxes=[439, 437, 524, 709])
         
     | 
| 
       161 
168 
     | 
    
         
             
                # predictor(points=[900, 370], labels=[1])
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                # Reset image
         
     | 
| 
       164 
169 
     | 
    
         
             
                predictor.reset_image()
         
     | 
    
        ultralytics/__init__.py
    CHANGED
    
    
    
        ultralytics/cfg/default.yaml
    CHANGED
    
    | 
         @@ -17,7 +17,7 @@ imgsz: 640 # (int | list) input images size as int for train and val modes, or l 
     | 
|
| 
       17 
17 
     | 
    
         
             
            save: True # (bool) save train checkpoints and predict results
         
     | 
| 
       18 
18 
     | 
    
         
             
            save_period: -1 # (int) Save checkpoint every x epochs (disabled if < 1)
         
     | 
| 
       19 
19 
     | 
    
         
             
            cache: False # (bool) True/ram, disk or False. Use cache for data loading
         
     | 
| 
       20 
     | 
    
         
            -
            device: # (int | str | list 
     | 
| 
      
 20 
     | 
    
         
            +
            device: # (int | str | list) device: CUDA device=0 or [0,1,2,3] or "cpu/mps" or -1 or [-1,-1] to auto-select idle GPUs
         
     | 
| 
       21 
21 
     | 
    
         
             
            workers: 8 # (int) number of worker threads for data loading (per RANK if DDP)
         
     | 
| 
       22 
22 
     | 
    
         
             
            project: # (str, optional) project name
         
     | 
| 
       23 
23 
     | 
    
         
             
            name: # (str, optional) experiment name, results saved to 'project/name' directory
         
     | 
    
        ultralytics/engine/trainer.py
    CHANGED
    
    | 
         @@ -105,6 +105,7 @@ class BaseTrainer: 
     | 
|
| 
       105 
105 
     | 
    
         
             
                    self.args = get_cfg(cfg, overrides)
         
     | 
| 
       106 
106 
     | 
    
         
             
                    self.check_resume(overrides)
         
     | 
| 
       107 
107 
     | 
    
         
             
                    self.device = select_device(self.args.device, self.args.batch)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    self.args.device = str(self.device)  # ensure -1 is updated to selected CUDA device
         
     | 
| 
       108 
109 
     | 
    
         
             
                    self.validator = None
         
     | 
| 
       109 
110 
     | 
    
         
             
                    self.metrics = None
         
     | 
| 
       110 
111 
     | 
    
         
             
                    self.plots = {}
         
     | 
    
        ultralytics/utils/__init__.py
    CHANGED
    
    | 
         @@ -182,6 +182,10 @@ class TQDM(rich.tqdm if TQDM_RICH else tqdm.tqdm): 
     | 
|
| 
       182 
182 
     | 
    
         
             
                    kwargs.setdefault("bar_format", TQDM_BAR_FORMAT)  # override default value if passed
         
     | 
| 
       183 
183 
     | 
    
         
             
                    super().__init__(*args, **kwargs)
         
     | 
| 
       184 
184 
     | 
    
         | 
| 
      
 185 
     | 
    
         
            +
                def __iter__(self):
         
     | 
| 
      
 186 
     | 
    
         
            +
                    """Return self as iterator to satisfy Iterable interface."""
         
     | 
| 
      
 187 
     | 
    
         
            +
                    return super().__iter__()
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
       185 
189 
     | 
    
         | 
| 
       186 
190 
     | 
    
         
             
            class SimpleClass:
         
     | 
| 
       187 
191 
     | 
    
         
             
                """
         
     | 
| 
         @@ -0,0 +1,175 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            from ultralytics.utils import LOGGER
         
     | 
| 
      
 5 
     | 
    
         
            +
            from ultralytics.utils.checks import check_requirements
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            class GPUInfo:
         
     | 
| 
      
 9 
     | 
    
         
            +
                """
         
     | 
| 
      
 10 
     | 
    
         
            +
                Manages NVIDIA GPU information via pynvml with robust error handling.
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                Provides methods to query detailed GPU statistics (utilization, memory, temp, power) and select the most idle
         
     | 
| 
      
 13 
     | 
    
         
            +
                GPUs based on configurable criteria. It safely handles the absence or initialization failure of the pynvml
         
     | 
| 
      
 14 
     | 
    
         
            +
                library by logging warnings and disabling related features, preventing application crashes.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                Includes fallback logic using `torch.cuda` for basic device counting if NVML is unavailable during GPU
         
     | 
| 
      
 17 
     | 
    
         
            +
                selection. Manages NVML initialization and shutdown internally.
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                Attributes:
         
     | 
| 
      
 20 
     | 
    
         
            +
                    pynvml (module | None): The `pynvml` module if successfully imported and initialized, otherwise `None`.
         
     | 
| 
      
 21 
     | 
    
         
            +
                    nvml_available (bool): Indicates if `pynvml` is ready for use. True if import and `nvmlInit()` succeeded,
         
     | 
| 
      
 22 
     | 
    
         
            +
                        False otherwise.
         
     | 
| 
      
 23 
     | 
    
         
            +
                    gpu_stats (list[dict]): A list of dictionaries, each holding stats for one GPU. Populated on initialization
         
     | 
| 
      
 24 
     | 
    
         
            +
                        and by `refresh_stats()`. Keys include: 'index', 'name', 'utilization' (%), 'memory_used' (MiB),
         
     | 
| 
      
 25 
     | 
    
         
            +
                        'memory_total' (MiB), 'memory_free' (MiB), 'temperature' (C), 'power_draw' (W),
         
     | 
| 
      
 26 
     | 
    
         
            +
                        'power_limit' (W or 'N/A'). Empty if NVML is unavailable or queries fail.
         
     | 
| 
      
 27 
     | 
    
         
            +
                """
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def __init__(self):
         
     | 
| 
      
 30 
     | 
    
         
            +
                    """Initializes GPUInfo, attempting to import and initialize pynvml."""
         
     | 
| 
      
 31 
     | 
    
         
            +
                    self.pynvml = None
         
     | 
| 
      
 32 
     | 
    
         
            +
                    self.nvml_available = False
         
     | 
| 
      
 33 
     | 
    
         
            +
                    self.gpu_stats = []
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 36 
     | 
    
         
            +
                        check_requirements("pynvml>=12.0.0")
         
     | 
| 
      
 37 
     | 
    
         
            +
                        self.pynvml = __import__("pynvml")
         
     | 
| 
      
 38 
     | 
    
         
            +
                        self.pynvml.nvmlInit()
         
     | 
| 
      
 39 
     | 
    
         
            +
                        self.nvml_available = True
         
     | 
| 
      
 40 
     | 
    
         
            +
                        self.refresh_stats()
         
     | 
| 
      
 41 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 42 
     | 
    
         
            +
                        LOGGER.warning(f"Failed to initialize pynvml, GPU stats disabled: {e}")
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def __del__(self):
         
     | 
| 
      
 45 
     | 
    
         
            +
                    """Ensures NVML is shut down when the object is garbage collected."""
         
     | 
| 
      
 46 
     | 
    
         
            +
                    self.shutdown()
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def shutdown(self):
         
     | 
| 
      
 49 
     | 
    
         
            +
                    """Shuts down NVML if it was initialized."""
         
     | 
| 
      
 50 
     | 
    
         
            +
                    if self.nvml_available and self.pynvml:
         
     | 
| 
      
 51 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 52 
     | 
    
         
            +
                            self.pynvml.nvmlShutdown()
         
     | 
| 
      
 53 
     | 
    
         
            +
                        except Exception:
         
     | 
| 
      
 54 
     | 
    
         
            +
                            pass
         
     | 
| 
      
 55 
     | 
    
         
            +
                        self.nvml_available = False
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def refresh_stats(self):
         
     | 
| 
      
 58 
     | 
    
         
            +
                    """Refreshes the internal gpu_stats list by querying NVML."""
         
     | 
| 
      
 59 
     | 
    
         
            +
                    self.gpu_stats = []
         
     | 
| 
      
 60 
     | 
    
         
            +
                    if not self.nvml_available or not self.pynvml:
         
     | 
| 
      
 61 
     | 
    
         
            +
                        return
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 64 
     | 
    
         
            +
                        device_count = self.pynvml.nvmlDeviceGetCount()
         
     | 
| 
      
 65 
     | 
    
         
            +
                        for i in range(device_count):
         
     | 
| 
      
 66 
     | 
    
         
            +
                            self.gpu_stats.append(self._get_device_stats(i))
         
     | 
| 
      
 67 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 68 
     | 
    
         
            +
                        LOGGER.warning(f"Error during device query: {e}")
         
     | 
| 
      
 69 
     | 
    
         
            +
                        self.gpu_stats = []
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def _get_device_stats(self, index):
         
     | 
| 
      
 72 
     | 
    
         
            +
                    """Gets stats for a single GPU device."""
         
     | 
| 
      
 73 
     | 
    
         
            +
                    handle = self.pynvml.nvmlDeviceGetHandleByIndex(index)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    memory = self.pynvml.nvmlDeviceGetMemoryInfo(handle)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    util = self.pynvml.nvmlDeviceGetUtilizationRates(handle)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    def safe_get(func, *args, default=-1, divisor=1):
         
     | 
| 
      
 78 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 79 
     | 
    
         
            +
                            val = func(*args)
         
     | 
| 
      
 80 
     | 
    
         
            +
                            return val // divisor if divisor != 1 and isinstance(val, (int, float)) else val
         
     | 
| 
      
 81 
     | 
    
         
            +
                        except Exception:
         
     | 
| 
      
 82 
     | 
    
         
            +
                            return default
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    temp_type = getattr(self.pynvml, "NVML_TEMPERATURE_GPU", -1)
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    return {
         
     | 
| 
      
 87 
     | 
    
         
            +
                        "index": index,
         
     | 
| 
      
 88 
     | 
    
         
            +
                        "name": self.pynvml.nvmlDeviceGetName(handle),
         
     | 
| 
      
 89 
     | 
    
         
            +
                        "utilization": util.gpu if util else -1,
         
     | 
| 
      
 90 
     | 
    
         
            +
                        "memory_used": memory.used >> 20 if memory else -1,
         
     | 
| 
      
 91 
     | 
    
         
            +
                        "memory_total": memory.total >> 20 if memory else -1,
         
     | 
| 
      
 92 
     | 
    
         
            +
                        "memory_free": memory.free >> 20 if memory else -1,
         
     | 
| 
      
 93 
     | 
    
         
            +
                        "temperature": safe_get(self.pynvml.nvmlDeviceGetTemperature, handle, temp_type),
         
     | 
| 
      
 94 
     | 
    
         
            +
                        "power_draw": safe_get(self.pynvml.nvmlDeviceGetPowerUsage, handle, divisor=1000),
         
     | 
| 
      
 95 
     | 
    
         
            +
                        "power_limit": safe_get(self.pynvml.nvmlDeviceGetEnforcedPowerLimit, handle, divisor=1000),
         
     | 
| 
      
 96 
     | 
    
         
            +
                    }
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def print_status(self):
         
     | 
| 
      
 99 
     | 
    
         
            +
                    """Prints GPU status in a compact table format using current stats."""
         
     | 
| 
      
 100 
     | 
    
         
            +
                    self.refresh_stats()
         
     | 
| 
      
 101 
     | 
    
         
            +
                    if not self.gpu_stats:
         
     | 
| 
      
 102 
     | 
    
         
            +
                        LOGGER.warning("No GPU stats available.")
         
     | 
| 
      
 103 
     | 
    
         
            +
                        return
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    stats = self.gpu_stats
         
     | 
| 
      
 106 
     | 
    
         
            +
                    name_len = max(len(gpu.get("name", "N/A")) for gpu in stats)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    hdr = f"{'Idx':<3} {'Name':<{name_len}} {'Util':>6} {'Mem (MiB)':>15} {'Temp':>5} {'Pwr (W)':>10}"
         
     | 
| 
      
 108 
     | 
    
         
            +
                    LOGGER.info(f"\n--- GPU Status ---\n{hdr}\n{'-' * len(hdr)}")
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    for gpu in stats:
         
     | 
| 
      
 111 
     | 
    
         
            +
                        u = f"{gpu['utilization']:>5}%" if gpu["utilization"] >= 0 else " N/A "
         
     | 
| 
      
 112 
     | 
    
         
            +
                        m = f"{gpu['memory_used']:>6}/{gpu['memory_total']:<6}" if gpu["memory_used"] >= 0 else " N/A / N/A "
         
     | 
| 
      
 113 
     | 
    
         
            +
                        t = f"{gpu['temperature']}C" if gpu["temperature"] >= 0 else " N/A "
         
     | 
| 
      
 114 
     | 
    
         
            +
                        p = f"{gpu['power_draw']:>3}/{gpu['power_limit']:<3}" if gpu["power_draw"] >= 0 else " N/A "
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                        LOGGER.info(f"{gpu.get('index'):<3d} {gpu.get('name', 'N/A'):<{name_len}} {u:>6} {m:>15} {t:>5} {p:>10}")
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                    LOGGER.info(f"{'-' * len(hdr)}\n")
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def select_idle_gpu(self, count=1, min_memory_mb=0):
         
     | 
| 
      
 121 
     | 
    
         
            +
                    """
         
     | 
| 
      
 122 
     | 
    
         
            +
                    Selects the 'count' most idle GPUs based on utilization and free memory.
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 125 
     | 
    
         
            +
                        count (int): The number of idle GPUs to select. Defaults to 1.
         
     | 
| 
      
 126 
     | 
    
         
            +
                        min_memory_mb (int): Minimum free memory required (MiB). Defaults to 0.
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 129 
     | 
    
         
            +
                        (list[int]): Indices of the selected GPUs, sorted by idleness.
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                    Notes:
         
     | 
| 
      
 132 
     | 
    
         
            +
                         Returns fewer than 'count' if not enough qualify or exist.
         
     | 
| 
      
 133 
     | 
    
         
            +
                         Returns basic CUDA indices if NVML fails. Empty list if no GPUs found.
         
     | 
| 
      
 134 
     | 
    
         
            +
                    """
         
     | 
| 
      
 135 
     | 
    
         
            +
                    LOGGER.info(f"Searching for {count} idle GPUs with >= {min_memory_mb} MiB free memory...")
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                    if count <= 0:
         
     | 
| 
      
 138 
     | 
    
         
            +
                        return []
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                    self.refresh_stats()
         
     | 
| 
      
 141 
     | 
    
         
            +
                    if not self.gpu_stats:
         
     | 
| 
      
 142 
     | 
    
         
            +
                        LOGGER.warning("NVML stats unavailable.")
         
     | 
| 
      
 143 
     | 
    
         
            +
                        return []
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                    # Filter and sort eligible GPUs
         
     | 
| 
      
 146 
     | 
    
         
            +
                    eligible_gpus = [
         
     | 
| 
      
 147 
     | 
    
         
            +
                        gpu
         
     | 
| 
      
 148 
     | 
    
         
            +
                        for gpu in self.gpu_stats
         
     | 
| 
      
 149 
     | 
    
         
            +
                        if gpu.get("memory_free", -1) >= min_memory_mb and gpu.get("utilization", -1) != -1
         
     | 
| 
      
 150 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 151 
     | 
    
         
            +
                    eligible_gpus.sort(key=lambda x: (x.get("utilization", 101), -x.get("memory_free", 0)))
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    # Select top 'count' indices
         
     | 
| 
      
 154 
     | 
    
         
            +
                    selected = [gpu["index"] for gpu in eligible_gpus[:count]]
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                    if selected:
         
     | 
| 
      
 157 
     | 
    
         
            +
                        LOGGER.info(f"Selected idle CUDA devices {selected}")
         
     | 
| 
      
 158 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 159 
     | 
    
         
            +
                        LOGGER.warning(f"No GPUs met criteria (Util != -1, Free Mem >= {min_memory_mb} MiB).")
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                    return selected
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
            if __name__ == "__main__":
         
     | 
| 
      
 165 
     | 
    
         
            +
                required_free_mem = 2048  # Require 2GB free VRAM
         
     | 
| 
      
 166 
     | 
    
         
            +
                num_gpus_to_select = 1
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                gpu_info = GPUInfo()
         
     | 
| 
      
 169 
     | 
    
         
            +
                gpu_info.print_status()
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                selected = gpu_info.select_idle_gpu(count=num_gpus_to_select, min_memory_mb=required_free_mem)
         
     | 
| 
      
 172 
     | 
    
         
            +
                if selected:
         
     | 
| 
      
 173 
     | 
    
         
            +
                    print(f"\n==> Using selected GPU indices: {selected}")
         
     | 
| 
      
 174 
     | 
    
         
            +
                    devices = [f"cuda:{idx}" for idx in selected]
         
     | 
| 
      
 175 
     | 
    
         
            +
                    print(f"    Target devices: {devices}")
         
     | 
    
        ultralytics/utils/benchmarks.py
    CHANGED
    
    | 
         @@ -399,7 +399,7 @@ class ProfileModels: 
     | 
|
| 
       399 
399 
     | 
    
         
             
                        imgsz (int): Size of the image used during profiling.
         
     | 
| 
       400 
400 
     | 
    
         
             
                        half (bool): Flag to indicate whether to use FP16 half-precision for TensorRT profiling.
         
     | 
| 
       401 
401 
     | 
    
         
             
                        trt (bool): Flag to indicate whether to profile using TensorRT.
         
     | 
| 
       402 
     | 
    
         
            -
                        device (torch.device | None): Device used for profiling. If None, it is determined automatically.
         
     | 
| 
      
 402 
     | 
    
         
            +
                        device (torch.device | str | None): Device used for profiling. If None, it is determined automatically.
         
     | 
| 
       403 
403 
     | 
    
         | 
| 
       404 
404 
     | 
    
         
             
                    Notes:
         
     | 
| 
       405 
405 
     | 
    
         
             
                        FP16 'half' argument option removed for ONNX as slower on CPU than FP32.
         
     | 
| 
         @@ -417,7 +417,7 @@ class ProfileModels: 
     | 
|
| 
       417 
417 
     | 
    
         
             
                    self.imgsz = imgsz
         
     | 
| 
       418 
418 
     | 
    
         
             
                    self.half = half
         
     | 
| 
       419 
419 
     | 
    
         
             
                    self.trt = trt  # run TensorRT profiling
         
     | 
| 
       420 
     | 
    
         
            -
                    self.device = device  
     | 
| 
      
 420 
     | 
    
         
            +
                    self.device = device if isinstance(device, torch.device) else select_device(device)
         
     | 
| 
       421 
421 
     | 
    
         | 
| 
       422 
422 
     | 
    
         
             
                def run(self):
         
     | 
| 
       423 
423 
     | 
    
         
             
                    """
         
     | 
    
        ultralytics/utils/checks.py
    CHANGED
    
    | 
         @@ -608,7 +608,7 @@ def check_yolo(verbose=True, device=""): 
     | 
|
| 
       608 
608 
     | 
    
         | 
| 
       609 
609 
     | 
    
         
             
                Args:
         
     | 
| 
       610 
610 
     | 
    
         
             
                    verbose (bool): Whether to print verbose information.
         
     | 
| 
       611 
     | 
    
         
            -
                    device (str): Device to use for YOLO.
         
     | 
| 
      
 611 
     | 
    
         
            +
                    device (str | torch.device): Device to use for YOLO.
         
     | 
| 
       612 
612 
     | 
    
         
             
                """
         
     | 
| 
       613 
613 
     | 
    
         
             
                import psutil
         
     | 
| 
       614 
614 
     | 
    
         | 
| 
         @@ -810,7 +810,7 @@ def print_args(args: Optional[dict] = None, show_file=True, show_func=False): 
     | 
|
| 
       810 
810 
     | 
    
         
             
                except ValueError:
         
     | 
| 
       811 
811 
     | 
    
         
             
                    file = Path(file).stem
         
     | 
| 
       812 
812 
     | 
    
         
             
                s = (f"{file}: " if show_file else "") + (f"{func}: " if show_func else "")
         
     | 
| 
       813 
     | 
    
         
            -
                LOGGER.info(colorstr(s) + ", ".join(f"{k}={strip_auth(v)}" for k, v in args.items()))
         
     | 
| 
      
 813 
     | 
    
         
            +
                LOGGER.info(colorstr(s) + ", ".join(f"{k}={strip_auth(v)}" for k, v in sorted(args.items())))
         
     | 
| 
       814 
814 
     | 
    
         | 
| 
       815 
815 
     | 
    
         | 
| 
       816 
816 
     | 
    
         
             
            def cuda_device_count() -> int:
         
     | 
    
        ultralytics/utils/torch_utils.py
    CHANGED
    
    | 
         @@ -136,9 +136,9 @@ def select_device(device="", batch=0, newline=False, verbose=True): 
     | 
|
| 
       136 
136 
     | 
    
         
             
                    device (str | torch.device, optional): Device string or torch.device object.
         
     | 
| 
       137 
137 
     | 
    
         
             
                        Options are 'None', 'cpu', or 'cuda', or '0' or '0,1,2,3'. Defaults to an empty string, which auto-selects
         
     | 
| 
       138 
138 
     | 
    
         
             
                        the first available GPU, or CPU if no GPU is available.
         
     | 
| 
       139 
     | 
    
         
            -
                    batch (int, optional): Batch size being used in your model. 
     | 
| 
       140 
     | 
    
         
            -
                    newline (bool, optional): If True, adds a newline at the end of the log string. 
     | 
| 
       141 
     | 
    
         
            -
                    verbose (bool, optional): If True, logs the device information. 
     | 
| 
      
 139 
     | 
    
         
            +
                    batch (int, optional): Batch size being used in your model.
         
     | 
| 
      
 140 
     | 
    
         
            +
                    newline (bool, optional): If True, adds a newline at the end of the log string.
         
     | 
| 
      
 141 
     | 
    
         
            +
                    verbose (bool, optional): If True, logs the device information.
         
     | 
| 
       142 
142 
     | 
    
         | 
| 
       143 
143 
     | 
    
         
             
                Returns:
         
     | 
| 
       144 
144 
     | 
    
         
             
                    (torch.device): Selected device.
         
     | 
| 
         @@ -157,13 +157,26 @@ def select_device(device="", batch=0, newline=False, verbose=True): 
     | 
|
| 
       157 
157 
     | 
    
         
             
                Note:
         
     | 
| 
       158 
158 
     | 
    
         
             
                    Sets the 'CUDA_VISIBLE_DEVICES' environment variable for specifying which GPUs to use.
         
     | 
| 
       159 
159 
     | 
    
         
             
                """
         
     | 
| 
       160 
     | 
    
         
            -
                if isinstance(device, torch.device) or str(device).startswith("tpu" 
     | 
| 
      
 160 
     | 
    
         
            +
                if isinstance(device, torch.device) or str(device).startswith(("tpu", "intel")):
         
     | 
| 
       161 
161 
     | 
    
         
             
                    return device
         
     | 
| 
       162 
162 
     | 
    
         | 
| 
       163 
163 
     | 
    
         
             
                s = f"Ultralytics {__version__} 🚀 Python-{PYTHON_VERSION} torch-{torch.__version__} "
         
     | 
| 
       164 
164 
     | 
    
         
             
                device = str(device).lower()
         
     | 
| 
       165 
165 
     | 
    
         
             
                for remove in "cuda:", "none", "(", ")", "[", "]", "'", " ":
         
     | 
| 
       166 
166 
     | 
    
         
             
                    device = device.replace(remove, "")  # to string, 'cuda:0' -> '0' and '(0, 1)' -> '0,1'
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                # Auto-select GPUs
         
     | 
| 
      
 169 
     | 
    
         
            +
                if "-1" in device:
         
     | 
| 
      
 170 
     | 
    
         
            +
                    from ultralytics.utils.autodevice import GPUInfo
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                    # Replace each -1 with a selected GPU or remove it
         
     | 
| 
      
 173 
     | 
    
         
            +
                    parts = device.split(",")
         
     | 
| 
      
 174 
     | 
    
         
            +
                    selected = GPUInfo().select_idle_gpu(count=parts.count("-1"), min_memory_mb=2048)
         
     | 
| 
      
 175 
     | 
    
         
            +
                    for i in range(len(parts)):
         
     | 
| 
      
 176 
     | 
    
         
            +
                        if parts[i] == "-1":
         
     | 
| 
      
 177 
     | 
    
         
            +
                            parts[i] = str(selected.pop(0)) if selected else ""
         
     | 
| 
      
 178 
     | 
    
         
            +
                    device = ",".join(p for p in parts if p)
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
       167 
180 
     | 
    
         
             
                cpu = device == "cpu"
         
     | 
| 
       168 
181 
     | 
    
         
             
                mps = device in {"mps", "mps:0"}  # Apple Metal Performance Shaders (MPS)
         
     | 
| 
       169 
182 
     | 
    
         
             
                if cpu or mps:
         
     | 
| 
         @@ -200,7 +213,7 @@ def select_device(device="", batch=0, newline=False, verbose=True): 
     | 
|
| 
       200 
213 
     | 
    
         
             
                        if batch < 1:
         
     | 
| 
       201 
214 
     | 
    
         
             
                            raise ValueError(
         
     | 
| 
       202 
215 
     | 
    
         
             
                                "AutoBatch with batch<1 not supported for Multi-GPU training, "
         
     | 
| 
       203 
     | 
    
         
            -
                                "please specify a valid batch size, i.e. batch= 
     | 
| 
      
 216 
     | 
    
         
            +
                                f"please specify a valid batch size multiple of GPU count {n}, i.e. batch={n * 8}."
         
     | 
| 
       204 
217 
     | 
    
         
             
                            )
         
     | 
| 
       205 
218 
     | 
    
         
             
                        if batch >= 0 and batch % n != 0:  # check batch_size is divisible by device_count
         
     | 
| 
       206 
219 
     | 
    
         
             
                            raise ValueError(
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.4
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: ultralytics
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 8.3. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 8.3.126
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: Ultralytics YOLO 🚀 for SOTA object detection, multi-object tracking, instance segmentation, pose estimation and image classification.
         
     | 
| 
       5 
5 
     | 
    
         
             
            Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>, Jing Qiu <jing.qiu@ultralytics.com>
         
     | 
| 
       6 
6 
     | 
    
         
             
            Maintainer-email: Ultralytics <hello@ultralytics.com>
         
     | 
| 
         @@ -1,17 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            tests/__init__.py,sha256=xnMhv3O_DF1YrW4zk__ZywQzAaoTDjPKPoiI1Ktss1w,670
         
     | 
| 
       2 
2 
     | 
    
         
             
            tests/conftest.py,sha256=rsIAipRKfrVNoTaJ1LdpYue8AbcJ_fr3d3WIlM_6uXY,2982
         
     | 
| 
       3 
3 
     | 
    
         
             
            tests/test_cli.py,sha256=PtMFl5Lp_6ygBbYDJ1ndofz2k7ZYupMPEAiZw6aZVm8,5450
         
     | 
| 
       4 
     | 
    
         
            -
            tests/test_cuda.py,sha256= 
     | 
| 
      
 4 
     | 
    
         
            +
            tests/test_cuda.py,sha256=vMjegc23QlEzMdpzav2JEjXR1n8W-lYZ-KLGiLiwLok,6167
         
     | 
| 
       5 
5 
     | 
    
         
             
            tests/test_engine.py,sha256=aGqZ8P7QO5C_nOa1b4FOyk92Ysdk5WiP-ST310Vyxys,4962
         
     | 
| 
       6 
6 
     | 
    
         
             
            tests/test_exports.py,sha256=dhZn86LdbapW15RthQF870LGxDjC1MUZhlGdBgPmgIQ,9716
         
     | 
| 
       7 
7 
     | 
    
         
             
            tests/test_integrations.py,sha256=dQteeRsRVuT_p5-T88-7jqT65Zm9iAXkyKg-KQ1_TQ8,6341
         
     | 
| 
       8 
8 
     | 
    
         
             
            tests/test_python.py,sha256=hkOJc0Ejin3Bywyw0BT4pPex5hwwfbmw0K5ChRtvdvw,25398
         
     | 
| 
       9 
9 
     | 
    
         
             
            tests/test_solutions.py,sha256=BIvg9zW0a_ggEmrPKgB_Y0MncveH-eYuN5KlqdJ6nHs,5726
         
     | 
| 
       10 
     | 
    
         
            -
            ultralytics/__init__.py,sha256= 
     | 
| 
      
 10 
     | 
    
         
            +
            ultralytics/__init__.py,sha256=HfUbgU67v26Obhd8l6aWhYnTG8W_ycy0mBgszTdxe6E,730
         
     | 
| 
       11 
11 
     | 
    
         
             
            ultralytics/assets/bus.jpg,sha256=wCAZxJecGR63Od3ZRERe9Aja1Weayrb9Ug751DS_vGM,137419
         
     | 
| 
       12 
12 
     | 
    
         
             
            ultralytics/assets/zidane.jpg,sha256=Ftc4aeMmen1O0A3o6GCDO9FlfBslLpTAw0gnetx7bts,50427
         
     | 
| 
       13 
13 
     | 
    
         
             
            ultralytics/cfg/__init__.py,sha256=We3ti0mvUQrGRmUPcufDGboW0YAO3nSRYuoWxGagk3M,39462
         
     | 
| 
       14 
     | 
    
         
            -
            ultralytics/cfg/default.yaml,sha256= 
     | 
| 
      
 14 
     | 
    
         
            +
            ultralytics/cfg/default.yaml,sha256=oFG6llJO-Py5H-cR9qs-7FieJamroDLwpbrkhmfROOM,8307
         
     | 
| 
       15 
15 
     | 
    
         
             
            ultralytics/cfg/datasets/Argoverse.yaml,sha256=_xlEDIJ9XkUo0v_iNL7FW079BoSeZtKSuLteKTtGbA8,3275
         
     | 
| 
       16 
16 
     | 
    
         
             
            ultralytics/cfg/datasets/DOTAv1.5.yaml,sha256=SHND_CFkojxw5iQD5Mcgju2kCZIl0gW2ajuzv1cqoL0,1224
         
     | 
| 
       17 
17 
     | 
    
         
             
            ultralytics/cfg/datasets/DOTAv1.yaml,sha256=j_DvXVQzZ4dQmf8I7oPX4v9xO3WZXztxV4Xo9VhUTsM,1194
         
     | 
| 
         @@ -121,7 +121,7 @@ ultralytics/engine/exporter.py,sha256=aaZ_-np1q0klWtDXp6CxVjyiZ0DDXx-8Pqg4jZSByu 
     | 
|
| 
       121 
121 
     | 
    
         
             
            ultralytics/engine/model.py,sha256=37qGh6aqqPTUyMfpsvBQMaZ1Av7eJDe6mfRl9GvlfKg,52860
         
     | 
| 
       122 
122 
     | 
    
         
             
            ultralytics/engine/predictor.py,sha256=YJ5l-0qIpr6JAJxowswtZ0IqmXBqVTvAA9vR40v0sCM,21752
         
     | 
| 
       123 
123 
     | 
    
         
             
            ultralytics/engine/results.py,sha256=-JPBn_YMyZv6HhdlyhjRIZCcMf41LTyWID7JrEP64rc,79632
         
     | 
| 
       124 
     | 
    
         
            -
            ultralytics/engine/trainer.py,sha256= 
     | 
| 
      
 124 
     | 
    
         
            +
            ultralytics/engine/trainer.py,sha256=sQCtjCI7_qOvXp4z-OPIQB1Nnqgeoi8YAIJAiCs_OOY,38951
         
     | 
| 
       125 
125 
     | 
    
         
             
            ultralytics/engine/tuner.py,sha256=zEW1UpLlZ6N4xbvS7MxICkshRlaFgLNfuADA0VfRpao,12629
         
     | 
| 
       126 
126 
     | 
    
         
             
            ultralytics/engine/validator.py,sha256=jfV81wuFDgrVVXEcPzgOpxAPrAZn-1LgpKwu9l_1-ts,17050
         
     | 
| 
       127 
127 
     | 
    
         
             
            ultralytics/hub/__init__.py,sha256=wDtAUKdfqob95tfFHgDJFXcsNSDSdoIQkJTm-CfIUTI,6616
         
     | 
| 
         @@ -230,10 +230,11 @@ ultralytics/trackers/utils/__init__.py,sha256=lm6MckFYCPTbqIoX7w0s_daxdjNeBeKW6D 
     | 
|
| 
       230 
230 
     | 
    
         
             
            ultralytics/trackers/utils/gmc.py,sha256=dz3I5LbIv7h1__Xg7rGHecQFE32VFTe54tUnxb8F0Z8,14466
         
     | 
| 
       231 
231 
     | 
    
         
             
            ultralytics/trackers/utils/kalman_filter.py,sha256=A0CqOnnaKH6kr0XwuHzyHmIU6aJAjJYxF9jVlNBKZHo,21326
         
     | 
| 
       232 
232 
     | 
    
         
             
            ultralytics/trackers/utils/matching.py,sha256=7eIufSdeN7cXuFMjvcfvz0Ldq84m4YKZl5IGxBR8IIo,7169
         
     | 
| 
       233 
     | 
    
         
            -
            ultralytics/utils/__init__.py,sha256= 
     | 
| 
      
 233 
     | 
    
         
            +
            ultralytics/utils/__init__.py,sha256=YSBOQcgak2v6l03EHPjkpzH-ZtjVXrg2_4o0BF1cqDQ,52807
         
     | 
| 
       234 
234 
     | 
    
         
             
            ultralytics/utils/autobatch.py,sha256=kg05q2qKg74y_Uq2vvr01i3KhLfpVR7sT0IXBt3_kyI,4921
         
     | 
| 
       235 
     | 
    
         
            -
            ultralytics/utils/ 
     | 
| 
       236 
     | 
    
         
            -
            ultralytics/utils/ 
     | 
| 
      
 235 
     | 
    
         
            +
            ultralytics/utils/autodevice.py,sha256=OrLSk34UpW0I5ndxnkQEIWBxL--CvAON_W9Qw51zOGA,7233
         
     | 
| 
      
 236 
     | 
    
         
            +
            ultralytics/utils/benchmarks.py,sha256=1Y6R1DxdSOzeHRsKKgMOab_bdtEWF9z32HOU2hqgzss,30172
         
     | 
| 
      
 237 
     | 
    
         
            +
            ultralytics/utils/checks.py,sha256=Z87AuJ3C5JcTVYdhAn31BFErmF48bRyMc4_WZ9ku5-E,32711
         
     | 
| 
       237 
238 
     | 
    
         
             
            ultralytics/utils/dist.py,sha256=aytW0JEkcA5ZTZucV92ot7Bn-apiej8aLk3QNWicjAc,4103
         
     | 
| 
       238 
239 
     | 
    
         
             
            ultralytics/utils/downloads.py,sha256=Rn8xDwn2bzgBqiYz3Xn0rm3MWjk4T-QUd2Ajlu1EpQ4,22312
         
     | 
| 
       239 
240 
     | 
    
         
             
            ultralytics/utils/errors.py,sha256=vY9h2evFSrHnZdHJVVrmm8Zzw4qVDLyo9DeYW5g0dFk,1573
         
     | 
| 
         @@ -246,7 +247,7 @@ ultralytics/utils/ops.py,sha256=YFwPrKlPcgEmgAWqnJVR0Ccx5NQgp5e3P-YYHwVSP0k,3477 
     | 
|
| 
       246 
247 
     | 
    
         
             
            ultralytics/utils/patches.py,sha256=6rVT-l8WDp_Py3O-gZdv9t3PnrYRRkrX_lF3mZ1XS8c,4928
         
     | 
| 
       247 
248 
     | 
    
         
             
            ultralytics/utils/plotting.py,sha256=8n9G1RvFAv4fk09iqZt7D-VXUqfAHoOTBcGXE7BHEE0,46807
         
     | 
| 
       248 
249 
     | 
    
         
             
            ultralytics/utils/tal.py,sha256=P5nPoR9qNnFuDIda0fsn8WP6m1V8r7EbvXUuhNRFFTA,20805
         
     | 
| 
       249 
     | 
    
         
            -
            ultralytics/utils/torch_utils.py,sha256= 
     | 
| 
      
 250 
     | 
    
         
            +
            ultralytics/utils/torch_utils.py,sha256=2SJxxg8Qr0YqOoQ-8qAYn6VrzZdQMObqiw3CJZ-rAY0,39611
         
     | 
| 
       250 
251 
     | 
    
         
             
            ultralytics/utils/triton.py,sha256=xK9Db_ZUVDnIK1u76S2G-6ulIBsLfj9HN_YOaSrnMuU,5304
         
     | 
| 
       251 
252 
     | 
    
         
             
            ultralytics/utils/tuner.py,sha256=0Bp7l5dWZe1RzdvAIa11wQoX6eoAaoNRcA-EAnpofbk,6755
         
     | 
| 
       252 
253 
     | 
    
         
             
            ultralytics/utils/callbacks/__init__.py,sha256=hzL63Rce6VkZhP4Lcim9LKjadixaQG86nKqPhk7IkS0,242
         
     | 
| 
         @@ -260,9 +261,9 @@ ultralytics/utils/callbacks/neptune.py,sha256=JaI95Cj2kIjUhlEEOiDN0-Drc-fDelLhNI 
     | 
|
| 
       260 
261 
     | 
    
         
             
            ultralytics/utils/callbacks/raytune.py,sha256=A8amUGpux7dYES-L1iSeMoMXBySGWCD1aUqT7vcG-pU,1284
         
     | 
| 
       261 
262 
     | 
    
         
             
            ultralytics/utils/callbacks/tensorboard.py,sha256=jgYnym3cUQFAgN1GzTyO7l3jINtfAh8zhrllDvnLuVQ,5339
         
     | 
| 
       262 
263 
     | 
    
         
             
            ultralytics/utils/callbacks/wb.py,sha256=iDRFXI4IIDm8R5OI89DMTmjs8aHLo1HRCLkOFKdaMG4,7507
         
     | 
| 
       263 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       264 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       265 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       266 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       267 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
       268 
     | 
    
         
            -
            ultralytics-8.3. 
     | 
| 
      
 264 
     | 
    
         
            +
            ultralytics-8.3.126.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
         
     | 
| 
      
 265 
     | 
    
         
            +
            ultralytics-8.3.126.dist-info/METADATA,sha256=zkgsqHwnXIUCbSDzK_gK_HunHRJAQvvvIgix7NOzXgw,37180
         
     | 
| 
      
 266 
     | 
    
         
            +
            ultralytics-8.3.126.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
         
     | 
| 
      
 267 
     | 
    
         
            +
            ultralytics-8.3.126.dist-info/entry_points.txt,sha256=YM_wiKyTe9yRrsEfqvYolNO5ngwfoL4-NwgKzc8_7sI,93
         
     | 
| 
      
 268 
     | 
    
         
            +
            ultralytics-8.3.126.dist-info/top_level.txt,sha256=XP49TwiMw4QGsvTLSYiJhz1xF_k7ev5mQ8jJXaXi45Q,12
         
     | 
| 
      
 269 
     | 
    
         
            +
            ultralytics-8.3.126.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |