Ultralytics Code Refactor https://ultralytics.com/actions (#13152)
Refactor code for speed and clarity
This commit is contained in:
1 parent
05e59bdec1
commit
b9019671f4
13 files changed
+29
-5
No files matched your search
@@ -180,6 +180,7 @@ def train(opt, device):
|
|||||||
|
|
||||||
# lf = lambda x: ((1 + math.cos(x * math.pi / epochs)) / 2) * (1 - lrf) + lrf # cosine
|
# lf = lambda x: ((1 + math.cos(x * math.pi / epochs)) / 2) * (1 - lrf) + lrf # cosine
|
||||||
def lf(x):
|
def lf(x):
|
||||||
|
"""Linear learning rate scheduler function, scaling learning rate from initial value to `lrf` over `epochs`."""
|
||||||
return (1 - x / epochs) * (1 - lrf) + lrf # linear
|
return (1 - x / epochs) * (1 - lrf) + lrf # linear
|
||||||
|
|
||||||
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
|
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ def try_export(inner_func):
|
|||||||
inner_args = get_default_args(inner_func)
|
inner_args = get_default_args(inner_func)
|
||||||
|
|
||||||
def outer_func(*args, **kwargs):
|
def outer_func(*args, **kwargs):
|
||||||
|
"""Logs success/failure and execution details of model export functions wrapped with @try_export decorator."""
|
||||||
prefix = inner_args["prefix"]
|
prefix = inner_args["prefix"]
|
||||||
try:
|
try:
|
||||||
with Profile() as dt:
|
with Profile() as dt:
|
||||||
@@ -224,7 +225,7 @@ def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr("ONNX
|
|||||||
|
|
||||||
@try_export
|
@try_export
|
||||||
def export_openvino(file, metadata, half, int8, data, prefix=colorstr("OpenVINO:")):
|
def export_openvino(file, metadata, half, int8, data, prefix=colorstr("OpenVINO:")):
|
||||||
# YOLOv5 OpenVINO export
|
"""Exports a YOLOv5 model to OpenVINO format with optional FP16 and INT8 quantization; see https://pypi.org/project/openvino-dev/."""
|
||||||
check_requirements("openvino-dev>=2023.0") # requires openvino-dev: https://pypi.org/project/openvino-dev/
|
check_requirements("openvino-dev>=2023.0") # requires openvino-dev: https://pypi.org/project/openvino-dev/
|
||||||
import openvino.runtime as ov # noqa
|
import openvino.runtime as ov # noqa
|
||||||
from openvino.tools import mo # noqa
|
from openvino.tools import mo # noqa
|
||||||
@@ -244,6 +245,7 @@ def export_openvino(file, metadata, half, int8, data, prefix=colorstr("OpenVINO:
|
|||||||
from utils.dataloaders import create_dataloader
|
from utils.dataloaders import create_dataloader
|
||||||
|
|
||||||
def gen_dataloader(yaml_path, task="train", imgsz=640, workers=4):
|
def gen_dataloader(yaml_path, task="train", imgsz=640, workers=4):
|
||||||
|
"""Generates a DataLoader for model training or validation based on the given YAML dataset configuration."""
|
||||||
data_yaml = check_yaml(yaml_path)
|
data_yaml = check_yaml(yaml_path)
|
||||||
data = check_dataset(data_yaml)
|
data = check_dataset(data_yaml)
|
||||||
dataloader = create_dataloader(
|
dataloader = create_dataloader(
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ def train(hyp, opt, device, callbacks):
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
def lf(x):
|
def lf(x):
|
||||||
|
"""Linear learning rate scheduler decreasing from 1 to hyp['lrf'] over 'epochs'."""
|
||||||
return (1 - x / epochs) * (1.0 - hyp["lrf"]) + hyp["lrf"] # linear
|
return (1 - x / epochs) * (1.0 - hyp["lrf"]) + hyp["lrf"] # linear
|
||||||
|
|
||||||
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)
|
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ def save_one_json(predn, jdict, path, class_map, pred_masks):
|
|||||||
from pycocotools.mask import encode
|
from pycocotools.mask import encode
|
||||||
|
|
||||||
def single_encode(x):
|
def single_encode(x):
|
||||||
|
"""Encodes binary mask arrays into RLE (Run-Length Encoding) format for JSON serialization."""
|
||||||
rle = encode(np.asarray(x[:, :, None], order="F", dtype="uint8"))[0]
|
rle = encode(np.asarray(x[:, :, None], order="F", dtype="uint8"))[0]
|
||||||
rle["counts"] = rle["counts"].decode("utf-8")
|
rle["counts"] = rle["counts"].decode("utf-8")
|
||||||
return rle
|
return rle
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ def train(hyp, opt, device, callbacks):
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
def lf(x):
|
def lf(x):
|
||||||
|
"""Linear learning rate scheduler function with decay calculated by epoch proportion."""
|
||||||
return (1 - x / epochs) * (1.0 - hyp["lrf"]) + hyp["lrf"] # linear
|
return (1 - x / epochs) * (1.0 - hyp["lrf"]) + hyp["lrf"] # linear
|
||||||
|
|
||||||
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)
|
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ def threaded(func):
|
|||||||
"""Decorator @threaded to run a function in a separate thread, returning the thread instance."""
|
"""Decorator @threaded to run a function in a separate thread, returning the thread instance."""
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
"""Runs the decorated function in a separate daemon thread and returns the thread instance."""
|
||||||
thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
|
thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
return thread
|
return thread
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ def check_anchors(dataset, model, thr=4.0, imgsz=640):
|
|||||||
wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float() # wh
|
wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float() # wh
|
||||||
|
|
||||||
def metric(k): # compute metric
|
def metric(k): # compute metric
|
||||||
|
"""Computes ratio metric, anchors above threshold, and best possible recall for YOLOv5 anchor evaluation."""
|
||||||
r = wh[:, None] / k[None]
|
r = wh[:, None] / k[None]
|
||||||
x = torch.min(r, 1 / r).min(2)[0] # ratio metric
|
x = torch.min(r, 1 / r).min(2)[0] # ratio metric
|
||||||
best = x.max(1)[0] # best_x
|
best = x.max(1)[0] # best_x
|
||||||
@@ -86,16 +87,19 @@ def kmean_anchors(dataset="./data/coco128.yaml", n=9, img_size=640, thr=4.0, gen
|
|||||||
thr = 1 / thr
|
thr = 1 / thr
|
||||||
|
|
||||||
def metric(k, wh): # compute metrics
|
def metric(k, wh): # compute metrics
|
||||||
|
"""Computes ratio metric, anchors above threshold, and best possible recall for YOLOv5 anchor evaluation."""
|
||||||
r = wh[:, None] / k[None]
|
r = wh[:, None] / k[None]
|
||||||
x = torch.min(r, 1 / r).min(2)[0] # ratio metric
|
x = torch.min(r, 1 / r).min(2)[0] # ratio metric
|
||||||
# x = wh_iou(wh, torch.tensor(k)) # iou metric
|
# x = wh_iou(wh, torch.tensor(k)) # iou metric
|
||||||
return x, x.max(1)[0] # x, best_x
|
return x, x.max(1)[0] # x, best_x
|
||||||
|
|
||||||
def anchor_fitness(k): # mutation fitness
|
def anchor_fitness(k): # mutation fitness
|
||||||
|
"""Evaluates fitness of YOLOv5 anchors by computing recall and ratio metrics for an anchor evolution process."""
|
||||||
_, best = metric(torch.tensor(k, dtype=torch.float32), wh)
|
_, best = metric(torch.tensor(k, dtype=torch.float32), wh)
|
||||||
return (best * (best > thr).float()).mean() # fitness
|
return (best * (best > thr).float()).mean() # fitness
|
||||||
|
|
||||||
def print_results(k, verbose=True):
|
def print_results(k, verbose=True):
|
||||||
|
"""Sorts and logs kmeans-evolved anchor metrics and best possible recall values for YOLOv5 anchor evaluation."""
|
||||||
k = k[np.argsort(k.prod(1))] # sort small to large
|
k = k[np.argsort(k.prod(1))] # sort small to large
|
||||||
x, best = metric(k, wh0)
|
x, best = metric(k, wh0)
|
||||||
bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr
|
bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ def attempt_download(file, repo="ultralytics/yolov5", release="v7.0"):
|
|||||||
from utils.general import LOGGER
|
from utils.general import LOGGER
|
||||||
|
|
||||||
def github_assets(repository, version="latest"):
|
def github_assets(repository, version="latest"):
|
||||||
# Return GitHub repo tag (i.e. 'v7.0') and assets (i.e. ['yolov5s.pt', 'yolov5m.pt', ...])
|
"""Fetches GitHub repository release tag and asset names using the GitHub API."""
|
||||||
if version != "latest":
|
if version != "latest":
|
||||||
version = f"tags/{version}" # i.e. tags/v7.0
|
version = f"tags/{version}" # i.e. tags/v7.0
|
||||||
response = requests.get(f"https://api.github.com/repos/{repository}/releases/{version}").json() # github api
|
response = requests.get(f"https://api.github.com/repos/{repository}/releases/{version}").json() # github api
|
||||||
|
|||||||
+3
-3
@@ -343,7 +343,7 @@ def check_online():
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
def run_once():
|
def run_once():
|
||||||
# Check once
|
"""Checks internet connectivity by attempting to create a connection to "1.1.1.1" on port 443."""
|
||||||
try:
|
try:
|
||||||
socket.create_connection(("1.1.1.1", 443), 5) # check host accessibility
|
socket.create_connection(("1.1.1.1", 443), 5) # check host accessibility
|
||||||
return True
|
return True
|
||||||
@@ -587,7 +587,7 @@ def check_amp(model):
|
|||||||
from models.common import AutoShape, DetectMultiBackend
|
from models.common import AutoShape, DetectMultiBackend
|
||||||
|
|
||||||
def amp_allclose(model, im):
|
def amp_allclose(model, im):
|
||||||
# All close FP32 vs AMP results
|
"""Compares FP32 and AMP model inference outputs, ensuring they are close within a 10% absolute tolerance."""
|
||||||
m = AutoShape(model, verbose=False) # model
|
m = AutoShape(model, verbose=False) # model
|
||||||
a = m(im).xywhn[0] # FP32 inference
|
a = m(im).xywhn[0] # FP32 inference
|
||||||
m.amp = True
|
m.amp = True
|
||||||
@@ -652,7 +652,7 @@ def download(url, dir=".", unzip=True, delete=True, curl=False, threads=1, retry
|
|||||||
"""Downloads and optionally unzips files concurrently, supporting retries and curl fallback."""
|
"""Downloads and optionally unzips files concurrently, supporting retries and curl fallback."""
|
||||||
|
|
||||||
def download_one(url, dir):
|
def download_one(url, dir):
|
||||||
# Download 1 file
|
"""Downloads a single file from `url` to `dir`, with retry support and optional curl fallback."""
|
||||||
success = True
|
success = True
|
||||||
if os.path.isfile(url):
|
if os.path.isfile(url):
|
||||||
f = Path(url) # filename
|
f = Path(url) # filename
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class CometLogger:
|
|||||||
"""Log metrics, parameters, source code, models and much more with Comet."""
|
"""Log metrics, parameters, source code, models and much more with Comet."""
|
||||||
|
|
||||||
def __init__(self, opt, hyp, run_id=None, job_type="Training", **experiment_kwargs) -> None:
|
def __init__(self, opt, hyp, run_id=None, job_type="Training", **experiment_kwargs) -> None:
|
||||||
|
"""Initializes CometLogger with given options, hyperparameters, run ID, job type, and additional experiment
|
||||||
|
arguments.
|
||||||
|
"""
|
||||||
self.job_type = job_type
|
self.job_type = job_type
|
||||||
self.opt = opt
|
self.opt = opt
|
||||||
self.hyp = hyp
|
self.hyp = hyp
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ def butter_lowpass_filtfilt(data, cutoff=1500, fs=50000, order=5):
|
|||||||
|
|
||||||
# https://stackoverflow.com/questions/28536191/how-to-filter-smooth-with-scipy-numpy
|
# https://stackoverflow.com/questions/28536191/how-to-filter-smooth-with-scipy-numpy
|
||||||
def butter_lowpass(cutoff, fs, order):
|
def butter_lowpass(cutoff, fs, order):
|
||||||
|
"""Applies a low-pass Butterworth filter to a signal with specified cutoff frequency, sample rate, and filter
|
||||||
|
order.
|
||||||
|
"""
|
||||||
nyq = 0.5 * fs
|
nyq = 0.5 * fs
|
||||||
normal_cutoff = cutoff / nyq
|
normal_cutoff = cutoff / nyq
|
||||||
return butter(order, normal_cutoff, btype="low", analog=False)
|
return butter(order, normal_cutoff, btype="low", analog=False)
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ def ap_per_class_box_and_mask(
|
|||||||
|
|
||||||
class Metric:
|
class Metric:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
"""Initializes performance metric attributes for precision, recall, F1 score, average precision, and class
|
||||||
|
indices.
|
||||||
|
"""
|
||||||
self.p = [] # (nc, )
|
self.p = [] # (nc, )
|
||||||
self.r = [] # (nc, )
|
self.r = [] # (nc, )
|
||||||
self.f1 = [] # (nc, )
|
self.f1 = [] # (nc, )
|
||||||
@@ -151,6 +154,9 @@ class Metrics:
|
|||||||
"""Metric for boxes and masks."""
|
"""Metric for boxes and masks."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
"""Initializes Metric objects for bounding boxes and masks to compute performance metrics in the Metrics
|
||||||
|
class.
|
||||||
|
"""
|
||||||
self.metric_box = Metric()
|
self.metric_box = Metric()
|
||||||
self.metric_mask = Metric()
|
self.metric_mask = Metric()
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ def smart_inference_mode(torch_1_9=check_version(torch.__version__, "1.9.0")):
|
|||||||
"""Applies torch.inference_mode() if torch>=1.9.0, else torch.no_grad() as a decorator for functions."""
|
"""Applies torch.inference_mode() if torch>=1.9.0, else torch.no_grad() as a decorator for functions."""
|
||||||
|
|
||||||
def decorate(fn):
|
def decorate(fn):
|
||||||
|
"""Applies torch.inference_mode() if torch>=1.9.0, else torch.no_grad() to the decorated function."""
|
||||||
return (torch.inference_mode if torch_1_9 else torch.no_grad)()(fn)
|
return (torch.inference_mode if torch_1_9 else torch.no_grad)()(fn)
|
||||||
|
|
||||||
return decorate
|
return decorate
|
||||||
|
|||||||
Reference in new issue
Block a user