`ultralytics 8.2.61` fix `num_threads` for CPU training (#14544)

Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com>
Co-authored-by: UltralyticsAssistant <web@ultralytics.com>
Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>
pull/14566/head v8.2.61
Laughing 9 months ago committed by GitHub
parent 8094df3c47
commit 47ff2b4a76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 39
      docs/build_docs.py
  2. 3
      mkdocs.yml
  3. 2
      ultralytics/__init__.py
  4. 310
      ultralytics/cfg/__init__.py
  5. 3
      ultralytics/utils/torch_utils.py

@ -30,6 +30,7 @@ import shutil
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from bs4 import BeautifulSoup
from tqdm import tqdm from tqdm import tqdm
os.environ["JUPYTER_PLATFORM_DIRS"] = "1" # fix DeprecationWarning: Jupyter is migrating to use standard platformdirs os.environ["JUPYTER_PLATFORM_DIRS"] = "1" # fix DeprecationWarning: Jupyter is migrating to use standard platformdirs
@ -96,8 +97,6 @@ def update_html_head(script=""):
def update_subdir_edit_links(subdir="", docs_url=""): def update_subdir_edit_links(subdir="", docs_url=""):
"""Update the HTML head section of each file.""" """Update the HTML head section of each file."""
from bs4 import BeautifulSoup
if str(subdir[0]) == "/": if str(subdir[0]) == "/":
subdir = str(subdir[0])[1:] subdir = str(subdir[0])[1:]
html_files = (SITE / subdir).rglob("*.html") html_files = (SITE / subdir).rglob("*.html")
@ -153,7 +152,7 @@ def update_markdown_files(md_filepath: Path):
def update_docs_html(): def update_docs_html():
"""Updates titles, edit links and head sections of HTML documentation for improved accessibility and relevance.""" """Updates titles, edit links, head sections, and converts plaintext links in HTML documentation."""
update_page_title(SITE / "404.html", new_title="Ultralytics Docs - Not Found") update_page_title(SITE / "404.html", new_title="Ultralytics Docs - Not Found")
# Update edit links # Update edit links
@ -162,12 +161,46 @@ def update_docs_html():
docs_url="https://github.com/ultralytics/hub-sdk/tree/main/docs/", docs_url="https://github.com/ultralytics/hub-sdk/tree/main/docs/",
) )
# Convert plaintext links to HTML hyperlinks
files_modified = 0
for html_file in tqdm(SITE.rglob("*.html"), desc="Converting plaintext links"):
with open(html_file, "r", encoding="utf-8") as file:
content = file.read()
updated_content = convert_plaintext_links_to_html(content)
if updated_content != content:
with open(html_file, "w", encoding="utf-8") as file:
file.write(updated_content)
files_modified += 1
print(f"Modified plaintext links in {files_modified} files.")
# Update HTML file head section # Update HTML file head section
script = "" script = ""
if any(script): if any(script):
update_html_head(script) update_html_head(script)
def convert_plaintext_links_to_html(content):
"""Convert plaintext links to HTML hyperlinks in the main content area only."""
soup = BeautifulSoup(content, "html.parser")
# Find the main content area (adjust this selector based on your HTML structure)
main_content = soup.find("main") or soup.find("div", class_="md-content")
if not main_content:
return content # Return original content if main content area not found
modified = False
for paragraph in main_content.find_all(["p", "li"]): # Focus on paragraphs and list items
for text_node in paragraph.find_all(string=True, recursive=False):
if text_node.parent.name not in {"a", "code"}: # Ignore links and code blocks
new_text = re.sub(r"(https?://\S+)", r'<a href="\1">\1</a>', str(text_node)) # note: reject http?
if "<a" in new_text:
new_soup = BeautifulSoup(new_text, "html.parser")
text_node.replace_with(new_soup)
modified = True
return str(soup) if modified else content
def main(): def main():
"""Builds docs, updates titles and edit links, and prints local server command.""" """Builds docs, updates titles and edit links, and prints local server command."""
prepare_docs_markdown() prepare_docs_markdown()

@ -614,6 +614,9 @@ plugins:
handlers: handlers:
python: python:
options: options:
docstring_options:
ignore_init_summary: true
merge_init_into_class: true
docstring_style: google docstring_style: google
show_root_heading: true show_root_heading: true
show_source: true show_source: true

@ -1,6 +1,6 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license # Ultralytics YOLO 🚀, AGPL-3.0 license
__version__ = "8.2.60" __version__ = "8.2.61"
import os import os

@ -190,27 +190,23 @@ def cfg2dict(cfg):
Convert a configuration object to a dictionary, whether it is a file path, a string, or a SimpleNamespace object. Convert a configuration object to a dictionary, whether it is a file path, a string, or a SimpleNamespace object.
Args: Args:
cfg (str | Path | dict | SimpleNamespace): Configuration object to be converted to a dictionary. This may be a cfg (str | Path | Dict | SimpleNamespace): Configuration object to be converted to a dictionary. This may be a
path to a configuration file, a dictionary, or a SimpleNamespace object. path to a configuration file, a dictionary, or a SimpleNamespace object.
Returns: Returns:
(dict): Configuration object in dictionary format. (Dict): Configuration object in dictionary format.
Example: Examples:
```python Convert a YAML file path to a dictionary:
from ultralytics.cfg import cfg2dict >>> config_dict = cfg2dict('config.yaml')
from types import SimpleNamespace
# Example usage with a file path Convert a SimpleNamespace to a dictionary:
config_dict = cfg2dict('config.yaml') >>> from types import SimpleNamespace
>>> config_sn = SimpleNamespace(param1='value1', param2='value2')
>>> config_dict = cfg2dict(config_sn)
# Example usage with a SimpleNamespace Pass through an already existing dictionary:
config_sn = SimpleNamespace(param1='value1', param2='value2') >>> config_dict = cfg2dict({'param1': 'value1', 'param2': 'value2'})
config_dict = cfg2dict(config_sn)
# Example usage with a dictionary (returns the same dictionary)
config_dict = cfg2dict({'param1': 'value1', 'param2': 'value2'})
```
Notes: Notes:
- If `cfg` is a path or a string, it will be loaded as YAML and converted to a dictionary. - If `cfg` is a path or a string, it will be loaded as YAML and converted to a dictionary.
@ -228,9 +224,8 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
Load and merge configuration data from a file or dictionary, with optional overrides. Load and merge configuration data from a file or dictionary, with optional overrides.
Args: Args:
cfg (str | Path | dict | SimpleNamespace, optional): Configuration data source. Defaults to `DEFAULT_CFG_DICT`. cfg (str | Path | Dict | SimpleNamespace): Configuration data source.
overrides (dict | None, optional): Dictionary containing key-value pairs to override the base configuration. overrides (Dict | None): Dictionary containing key-value pairs to override the base configuration.
Defaults to None.
Returns: Returns:
(SimpleNamespace): Namespace containing the merged training arguments. (SimpleNamespace): Namespace containing the merged training arguments.
@ -238,23 +233,15 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
Notes: Notes:
- If both `cfg` and `overrides` are provided, the values in `overrides` will take precedence. - If both `cfg` and `overrides` are provided, the values in `overrides` will take precedence.
- Special handling ensures alignment and correctness of the configuration, such as converting numeric `project` - Special handling ensures alignment and correctness of the configuration, such as converting numeric `project`
and `name` to strings and validating the configuration keys and values. and `name` to strings and validating configuration keys and values.
Example:
```python
from ultralytics.cfg import get_cfg
# Load default configuration Examples:
config = get_cfg() Load default configuration:
>>> from ultralytics import get_cfg
# Load from a custom file with overrides >>> config = get_cfg()
config = get_cfg('path/to/config.yaml', overrides={'epochs': 50, 'batch_size': 16})
```
Configuration dictionary merged with overrides: Load from a custom file with overrides:
```python >>> config = get_cfg('path/to/config.yaml', overrides={'epochs': 50, 'batch_size': 16})
{'epochs': 50, 'batch_size': 16, ...}
```
""" """
cfg = cfg2dict(cfg) cfg = cfg2dict(cfg)
@ -282,7 +269,26 @@ def get_cfg(cfg: Union[str, Path, Dict, SimpleNamespace] = DEFAULT_CFG_DICT, ove
def check_cfg(cfg, hard=True): def check_cfg(cfg, hard=True):
"""Validate Ultralytics configuration argument types and values, converting them if necessary.""" """
Checks configuration argument types and values for the Ultralytics library, ensuring correctness and converting them
if necessary.
Args:
cfg (Dict): Configuration dictionary to validate.
hard (bool): If True, raises exceptions for invalid types and values; if False, attempts to convert them.
Examples:
Validate a configuration with a mix of valid and invalid values:
>>> config = {
... 'epochs': 50, # valid integer
... 'lr0': 0.01, # valid float
... 'momentum': 1.2, # invalid float (out of 0.0-1.0 range)
... 'save': 'true', # invalid bool
... }
>>> check_cfg(config, hard=False)
>>> print(config)
{'epochs': 50, 'lr0': 0.01, 'momentum': 1.2, 'save': False} # corrected 'save' key and retained other values
"""
for k, v in cfg.items(): for k, v in cfg.items():
if v is not None: # None values may be from optional args if v is not None: # None values may be from optional args
if k in CFG_FLOAT_KEYS and not isinstance(v, (int, float)): if k in CFG_FLOAT_KEYS and not isinstance(v, (int, float)):
@ -318,7 +324,26 @@ def check_cfg(cfg, hard=True):
def get_save_dir(args, name=None): def get_save_dir(args, name=None):
"""Returns the directory path for saving outputs, derived from arguments or default settings.""" """
Returns the directory path for saving outputs, derived from arguments or default settings.
Args:
args (SimpleNamespace): Namespace object containing configurations such as 'project', 'name', 'task', 'mode', and
'save_dir'.
name (str | None): Optional name for the output directory. If not provided, it defaults to 'args.name' or the
'args.mode'.
Returns:
(Path): Directory path where outputs should be saved.
Examples:
Generate a save directory using provided arguments
>>> from types import SimpleNamespace
>>> args = SimpleNamespace(project='my_project', task='detect', mode='train', exist_ok=True)
>>> save_dir = get_save_dir(args)
>>> print(save_dir)
my_project/detect/train
"""
if getattr(args, "save_dir", None): if getattr(args, "save_dir", None):
save_dir = args.save_dir save_dir = args.save_dir
@ -333,7 +358,18 @@ def get_save_dir(args, name=None):
def _handle_deprecation(custom): def _handle_deprecation(custom):
"""Handles deprecated configuration keys by mapping them to current equivalents with deprecation warnings.""" """
Handles deprecated configuration keys by mapping them to current equivalents with deprecation warnings.
Args:
custom (Dict): Configuration dictionary potentially containing deprecated keys.
Examples:
>>> custom_config = {"boxes": True, "hide_labels": "False", "line_thickness": 2}
>>> _handle_deprecation(custom_config)
>>> print(custom_config)
{'show_boxes': True, 'show_labels': True, 'line_width': 2}
"""
for key in custom.copy().keys(): for key in custom.copy().keys():
if key == "boxes": if key == "boxes":
@ -354,35 +390,32 @@ def _handle_deprecation(custom):
def check_dict_alignment(base: Dict, custom: Dict, e=None): def check_dict_alignment(base: Dict, custom: Dict, e=None):
""" """
Check for key alignment between custom and base configuration dictionaries, catering for deprecated keys and Check for key alignment between custom and base configuration dictionaries, handling deprecated keys and providing
providing informative error messages for mismatched keys. informative error messages for mismatched keys.
Args: Args:
base (dict): The base configuration dictionary containing valid keys. base (Dict): The base configuration dictionary containing valid keys.
custom (dict): The custom configuration dictionary to be checked for alignment. custom (Dict): The custom configuration dictionary to be checked for alignment.
e (Exception, optional): An optional error instance passed by the calling function. Default is None. e (Exception | None): Optional error instance passed by the calling function. Default is None.
Raises: Raises:
SystemExit: Terminates the program execution if mismatched keys are found. SystemExit: Terminates the program execution if mismatched keys are found.
Notes: Notes:
- The function provides suggestions for mismatched keys based on their similarity to valid keys in the - The function suggests corrections for mismatched keys based on similarity to valid keys.
base configuration. - Deprecated keys in the custom configuration are automatically replaced with their updated equivalents.
- Deprecated keys in the custom configuration are automatically handled and replaced with their updated - Detailed error messages are printed for each mismatched key to help users identify and correct their custom
equivalents. configurations.
- A detailed error message is printed for each mismatched key, helping users to quickly identify and correct
their custom configurations. Examples:
>>> base_cfg = {'epochs': 50, 'lr0': 0.01, 'batch_size': 16}
Example: >>> custom_cfg = {'epoch': 100, 'lr': 0.02, 'batch_size': 32}
```python
base_cfg = {'epochs': 50, 'lr0': 0.01, 'batch_size': 16} >>> try:
custom_cfg = {'epoch': 100, 'lr': 0.02, 'batch_size': 32} ... check_dict_alignment(base_cfg, custom_cfg)
... except SystemExit:
try: ... # Handle the error or correct the configuration
check_dict_alignment(base_cfg, custom_cfg) ... pass
except SystemExit:
# Handle the error or correct the configuration
```
""" """
custom = _handle_deprecation(custom) custom = _handle_deprecation(custom)
base_keys, custom_keys = (set(x.keys()) for x in (base, custom)) base_keys, custom_keys = (set(x.keys()) for x in (base, custom))
@ -401,30 +434,29 @@ def check_dict_alignment(base: Dict, custom: Dict, e=None):
def merge_equals_args(args: List[str]) -> List[str]: def merge_equals_args(args: List[str]) -> List[str]:
""" """
Merges arguments around isolated '=' args in a list of strings. The function considers cases where the first Merges arguments around isolated '=' in a list of strings.
argument ends with '=' or the second starts with '=', as well as when the middle one is an equals sign.
Args: Args:
args (List[str]): A list of strings where each element is an argument. args (List[str]): A list of strings where each element represents an argument.
Returns: Returns:
(List[str]): A list of strings where the arguments around isolated '=' are merged. (List[str]): A list of strings where the arguments around isolated '=' are merged.
Example: Examples:
The function modifies the argument list as follows: Merge arguments where equals sign is separated:
```python >>> args = ["arg1", "=", "value"]
args = ["arg1", "=", "value"] >>> merge_equals_args(args)
new_args = merge_equals_args(args) ["arg1=value"]
print(new_args) # Output: ["arg1=value"]
Merge arguments where equals sign is at the end of the first argument:
args = ["arg1=", "value"] >>> args = ["arg1=", "value"]
new_args = merge_equals_args(args) >>> merge_equals_args(args)
print(new_args) # Output: ["arg1=value"] ["arg1=value"]
args = ["arg1", "=value"] Merge arguments where equals sign is at the beginning of the second argument:
new_args = merge_equals_args(args) >>> args = ["arg1", "=value"]
print(new_args) # Output: ["arg1=value"] >>> merge_equals_args(args)
``` ["arg1=value"]
""" """
new_args = [] new_args = []
for i, arg in enumerate(args): for i, arg in enumerate(args):
@ -445,16 +477,13 @@ def handle_yolo_hub(args: List[str]) -> None:
""" """
Handle Ultralytics HUB command-line interface (CLI) commands. Handle Ultralytics HUB command-line interface (CLI) commands.
This function processes Ultralytics HUB CLI commands such as login and logout. It should be called when executing This function processes Ultralytics HUB CLI commands such as login and logout. It should be called when executing a
a script with arguments related to HUB authentication. script with arguments related to HUB authentication.
Args: Args:
args (List[str]): A list of command line arguments. args (List[str]): A list of command line arguments.
Returns: Examples:
None
Example:
```bash ```bash
yolo hub login YOUR_API_KEY yolo hub login YOUR_API_KEY
``` ```
@ -480,13 +509,9 @@ def handle_yolo_settings(args: List[str]) -> None:
Args: Args:
args (List[str]): A list of command line arguments for YOLO settings management. args (List[str]): A list of command line arguments for YOLO settings management.
Returns: Examples:
None Reset YOLO settings:
>>> yolo settings reset
Example:
```bash
yolo settings reset
```
Notes: Notes:
For more information on handling YOLO settings, visit: For more information on handling YOLO settings, visit:
@ -511,21 +536,58 @@ def handle_yolo_settings(args: List[str]) -> None:
def handle_explorer(): def handle_explorer():
"""Open the Ultralytics Explorer GUI for dataset exploration and analysis.""" """
Open the Ultralytics Explorer GUI for dataset exploration and analysis.
This function launches a graphical user interface that provides tools for interacting with and analyzing datasets
using the Ultralytics Explorer API.
Examples:
Start the Ultralytics Explorer:
>>> handle_explorer()
"""
checks.check_requirements("streamlit>=1.29.0") checks.check_requirements("streamlit>=1.29.0")
LOGGER.info("💡 Loading Explorer dashboard...") LOGGER.info("💡 Loading Explorer dashboard...")
subprocess.run(["streamlit", "run", ROOT / "data/explorer/gui/dash.py", "--server.maxMessageSize", "2048"]) subprocess.run(["streamlit", "run", ROOT / "data/explorer/gui/dash.py", "--server.maxMessageSize", "2048"])
def handle_streamlit_inference(): def handle_streamlit_inference():
"""Open the Ultralytics Live Inference streamlit app for real time object detection.""" """
Open the Ultralytics Live Inference streamlit app for real-time object detection.
This function initializes and runs a Streamlit application designed for performing live object detection using
Ultralytics models.
References:
- Streamlit documentation: https://docs.streamlit.io/
- Ultralytics: https://docs.ultralytics.com
Examples:
To run the live inference Streamlit app, execute:
>>> handle_streamlit_inference()
"""
checks.check_requirements("streamlit>=1.29.0") checks.check_requirements("streamlit>=1.29.0")
LOGGER.info("💡 Loading Ultralytics Live Inference app...") LOGGER.info("💡 Loading Ultralytics Live Inference app...")
subprocess.run(["streamlit", "run", ROOT / "solutions/streamlit_inference.py", "--server.headless", "true"]) subprocess.run(["streamlit", "run", ROOT / "solutions/streamlit_inference.py", "--server.headless", "true"])
def parse_key_value_pair(pair): def parse_key_value_pair(pair):
"""Parse one 'key=value' pair and return key and value.""" """
Parse a 'key=value' pair and return the key and value.
Args:
pair (str): The 'key=value' string to be parsed.
Returns:
(tuple[str, str]): A tuple containing the key and value as separate strings.
Examples:
>>> key, value = parse_key_value_pair("model=yolov8n.pt")
>>> key
'model'
>>> value
'yolov8n.pt
"""
k, v = pair.split("=", 1) # split on first '=' sign k, v = pair.split("=", 1) # split on first '=' sign
k, v = k.strip(), v.strip() # remove spaces k, v = k.strip(), v.strip() # remove spaces
assert v, f"missing '{k}' value" assert v, f"missing '{k}' value"
@ -533,7 +595,29 @@ def parse_key_value_pair(pair):
def smart_value(v): def smart_value(v):
"""Convert a string to its appropriate type (int, float, bool, None, etc.).""" """
Convert a string representation of a value into its appropriate Python type (int, float, bool, None, etc.).
Args:
v (str): String representation of the value to be converted.
Returns:
(Any): The converted value, which can be of type int, float, bool, None, or the original string if no conversion
is applicable.
Examples:
Convert a string to various types:
>>> smart_value("42")
42
>>> smart_value("3.14")
3.14
>>> smart_value("True")
True
>>> smart_value("None")
None
>>> smart_value("some_string")
'some_string'
"""
v_lower = v.lower() v_lower = v.lower()
if v_lower == "none": if v_lower == "none":
return None return None
@ -551,31 +635,26 @@ def entrypoint(debug=""):
""" """
Ultralytics entrypoint function for parsing and executing command-line arguments. Ultralytics entrypoint function for parsing and executing command-line arguments.
This function serves as the main entry point for the Ultralytics CLI, parsing command-line arguments and This function serves as the main entry point for the Ultralytics CLI, parsing command-line arguments and
executing the corresponding tasks such as training, validation, prediction, exporting models, and more. executing the corresponding tasks such as training, validation, prediction, exporting models, and more.
Args: Args:
debug (str, optional): Space-separated string of command-line arguments for debugging purposes. Default is "". debug (str, optional): Space-separated string of command-line arguments for debugging purposes.
Returns: Examples:
(None): This function does not return any value. Train a detection model for 10 epochs with an initial learning_rate of 0.01:
>>> entrypoint("train data=coco8.yaml model=yolov8n.pt epochs=10 lr0=0.01")
Predict a YouTube video using a pretrained segmentation model at image size 320:
>>> entrypoint("predict model=yolov8n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320")
Validate a pretrained detection model at batch-size 1 and image size 640:
>>> entrypoint("val model=yolov8n.pt data=coco8.yaml batch=1 imgsz=640")
Notes: Notes:
- For a list of all available commands and their arguments, see the provided help messages and the Ultralytics - For a list of all available commands and their arguments, see the provided help messages and the Ultralytics
documentation at https://docs.ultralytics.com. documentation at https://docs.ultralytics.com.
- If no arguments are passed, the function will display the usage help message. - If no arguments are passed, the function will display the usage help message.
Example:
```python
# Train a detection model for 10 epochs with an initial learning_rate of 0.01
entrypoint("train data=coco8.yaml model=yolov8n.pt epochs=10 lr0=0.01")
# Predict a YouTube video using a pretrained segmentation model at image size 320
entrypoint("predict model=yolov8n-seg.pt source='https://youtu.be/LNwODJXcvt4' imgsz=320")
# Validate a pretrained detection model at batch-size 1 and image size 640
entrypoint("val model=yolov8n.pt data=coco8.yaml batch=1 imgsz=640")
```
""" """
args = (debug.split(" ") if debug else ARGV)[1:] args = (debug.split(" ") if debug else ARGV)[1:]
if not args: # no arguments passed if not args: # no arguments passed
@ -713,7 +792,18 @@ def entrypoint(debug=""):
# Special modes -------------------------------------------------------------------------------------------------------- # Special modes --------------------------------------------------------------------------------------------------------
def copy_default_cfg(): def copy_default_cfg():
"""Copy and create a new default configuration file with '_copy' appended to its name, providing usage example.""" """
Copy and create a new default configuration file with '_copy' appended to its name, providing a usage example.
This function duplicates the existing default configuration file and appends '_copy' to its name in the current
working directory.
Examples:
Copy the default configuration file and use it in a YOLO command:
>>> copy_default_cfg()
>>> # Example YOLO command with this new custom cfg:
>>> # yolo cfg='default_copy.yaml' imgsz=320 batch=8
"""
new_file = Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml") new_file = Path.cwd() / DEFAULT_CFG_PATH.name.replace(".yaml", "_copy.yaml")
shutil.copy2(DEFAULT_CFG_PATH, new_file) shutil.copy2(DEFAULT_CFG_PATH, new_file)
LOGGER.info( LOGGER.info(

@ -21,6 +21,7 @@ from ultralytics.utils import (
DEFAULT_CFG_DICT, DEFAULT_CFG_DICT,
DEFAULT_CFG_KEYS, DEFAULT_CFG_KEYS,
LOGGER, LOGGER,
NUM_THREADS,
PYTHON_VERSION, PYTHON_VERSION,
TORCHVISION_VERSION, TORCHVISION_VERSION,
__version__, __version__,
@ -172,6 +173,8 @@ def select_device(device="", batch=0, newline=False, verbose=True):
s += f"CPU ({get_cpu_info()})\n" s += f"CPU ({get_cpu_info()})\n"
arg = "cpu" arg = "cpu"
if arg in {"cpu", "mps"}:
torch.set_num_threads(NUM_THREADS) # reset OMP_NUM_THREADS for cpu training
if verbose: if verbose:
LOGGER.info(s if newline else s.rstrip()) LOGGER.info(s if newline else s.rstrip())
return torch.device(arg) return torch.device(arg)

Loading…
Cancel
Save