Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/pip/jaxlib-lte-0.4.23
Browse files Browse the repository at this point in the history
  • Loading branch information
glenn-jocher authored Jan 16, 2024
2 parents d309581 + 7d9a117 commit 4049ad0
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 252 deletions.
38 changes: 20 additions & 18 deletions .github/workflows/links.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ jobs:
retry_wait_seconds: 60
max_attempts: 3
command: |
lychee
--accept 403,429,500,502,999
--exclude-loopback
--exclude 'https?://(www\.)?(linkedin\.com|twitter\.com|instagram\.com|kaggle\.com|fonts\.gstatic\.com|url\.com)'
--exclude-path '**/ci.yaml'
--github-token ${{ secrets.GITHUB_TOKEN }}
'./**/*.md'
lychee \
--scheme 'https' \
--accept 403,429,500,502,999 \
--exclude-loopback \
--exclude 'https?://(www\.)?(linkedin\.com|twitter\.com|instagram\.com|kaggle\.com|fonts\.gstatic\.com|url\.com)' \
--exclude-path '**/ci.yaml' \
--github-token ${{ secrets.GITHUB_TOKEN }} \
'./**/*.md' \
'./**/*.html'
- name: Test Markdown, HTML, YAML, Python and Notebook links with retry
Expand All @@ -51,15 +52,16 @@ jobs:
retry_wait_seconds: 60
max_attempts: 3
command: |
lychee
--accept 429,999
--exclude-loopback
--exclude 'https?://(www\.)?(linkedin\.com|twitter\.com|instagram\.com|kaggle\.com|fonts\.gstatic\.com|url\.com)'
--exclude-path '**/ci.yaml'
--github-token ${{ secrets.GITHUB_TOKEN }}
'./**/*.md'
'./**/*.html'
'./**/*.yml'
'./**/*.yaml'
'./**/*.py'
lychee \
--scheme 'https' \
--accept 429,999 \
--exclude-loopback \
--exclude 'https?://(www\.)?(linkedin\.com|twitter\.com|instagram\.com|kaggle\.com|fonts\.gstatic\.com|url\.com)' \
--exclude-path '**/ci.yaml' \
--github-token ${{ secrets.GITHUB_TOKEN }} \
'./**/*.md' \
'./**/*.html' \
'./**/*.yml' \
'./**/*.yaml' \
'./**/*.py' \
'./**/*.ipynb'
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div align="center">
<p>
<a href="https://yolovision.ultralytics.com/" target="_blank">
<img width="100%" src="https://github.com/raw/ultralytics/assets/main/im/banner-yolo-vision-2023.png"></a>
<a href="http://www.ultralytics.com/blog/ultralytics-yolov8-turns-one-a-year-of-breakthroughs-and-innovations" target="_blank">
<img width="100%" src="https://github.com/raw/ultralytics/assets/main/yolov8/banner-yolov8.png"></a>
<!--
<a align="center" href="https://ultralytics.com/yolov5" target="_blank">
<img width="100%" src="https://github.com/raw/ultralytics/assets/main/yolov5/v70/splash.png"></a>
Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div align="center">
<p>
<a href="https://yolovision.ultralytics.com/" target="_blank">
<img width="100%" src="https://github.com/raw/ultralytics/assets/main/im/banner-yolo-vision-2023.png"></a>
<a href="http://www.ultralytics.com/blog/ultralytics-yolov8-turns-one-a-year-of-breakthroughs-and-innovations" target="_blank">
<img width="100%" src="https://github.com/raw/ultralytics/assets/main/yolov8/banner-yolov8.png"></a>
<!--
<a align="center" href="https://ultralytics.com/yolov5" target="_blank">
<img width="100%" src="https://github.com/raw/ultralytics/assets/main/yolov5/v70/splash.png"></a>
Expand Down
33 changes: 12 additions & 21 deletions export.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,28 +226,19 @@ def export_openvino(file, metadata, half, int8, data, prefix=colorstr("OpenVINO:
from openvino.tools import mo # noqa

LOGGER.info(f"\n{prefix} starting export with openvino {ov.__version__}...")
f = str(file).replace(file.suffix, f"_openvino_model{os.sep}")
f = str(file).replace(file.suffix, f"_{'int8_' if int8 else ''}openvino_model{os.sep}")
f_onnx = file.with_suffix(".onnx")
f_ov = str(Path(f) / file.with_suffix(".xml").name)

ov_model = mo.convert_model(f_onnx, model_name=file.stem, framework="onnx", compress_to_fp16=half) # export

if int8:
check_requirements("nncf>=2.4.0") # requires at least version 2.4.0 to use the post-training quantization
check_requirements("nncf>=2.5.0") # requires at least version 2.5.0 to use the post-training quantization
import nncf
import numpy as np
from openvino.runtime import Core

from utils.dataloaders import create_dataloader

core = Core()
onnx_model = core.read_model(f_onnx) # export

def prepare_input_tensor(image: np.ndarray):
input_tensor = image.astype(np.float32) # uint8 to fp16/32
input_tensor /= 255.0 # 0 - 255 to 0.0 - 1.0

if input_tensor.ndim == 3:
input_tensor = np.expand_dims(input_tensor, 0)
return input_tensor

def gen_dataloader(yaml_path, task="train", imgsz=640, workers=4):
data_yaml = check_yaml(yaml_path)
data = check_dataset(data_yaml)
Expand All @@ -268,15 +259,15 @@ def transform_fn(data_item):
Returns:
input_tensor: Input data for quantization
"""
img = data_item[0].numpy()
input_tensor = prepare_input_tensor(img)
return input_tensor
assert data_item[0].dtype == torch.uint8, "input image must be uint8 for the quantization preprocessing"

img = data_item[0].numpy().astype(np.float32) # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
return np.expand_dims(img, 0) if img.ndim == 3 else img

ds = gen_dataloader(data)
quantization_dataset = nncf.Dataset(ds, transform_fn)
ov_model = nncf.quantize(onnx_model, quantization_dataset, preset=nncf.QuantizationPreset.MIXED)
else:
ov_model = mo.convert_model(f_onnx, model_name=file.stem, framework="onnx", compress_to_fp16=half) # export
ov_model = nncf.quantize(ov_model, quantization_dataset, preset=nncf.QuantizationPreset.MIXED)

ov.serialize(ov_model, f_ov) # save
yaml_save(Path(f) / file.with_suffix(".yaml").name, metadata) # add metadata.yaml
Expand Down Expand Up @@ -555,7 +546,7 @@ def export_tfjs(file, int8, prefix=colorstr("TensorFlow.js:")):
"--quantize_uint8" if int8 else "",
"--output_node_names=Identity,Identity_1,Identity_2,Identity_3",
str(f_pb),
str(f),
f,
]
subprocess.run([arg for arg in args if arg], check=True)

Expand Down
16 changes: 11 additions & 5 deletions models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,11 +859,17 @@ def pandas(self):
def tolist(self):
# return a list of Detections objects, i.e. 'for result in results.tolist():'
r = range(self.n) # iterable
x = [Detections([self.ims[i]], [self.pred[i]], [self.files[i]], self.times, self.names, self.s) for i in r]
# for d in x:
# for k in ['ims', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
# setattr(d, k, getattr(d, k)[0]) # pop out of list
return x
return [
Detections(
[self.ims[i]],
[self.pred[i]],
[self.files[i]],
self.times,
self.names,
self.s,
)
for i in r
]

def print(self):
LOGGER.info(self.__str__())
Expand Down
32 changes: 11 additions & 21 deletions train.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,10 +682,7 @@ def main(opt, callbacks=Callbacks()):
)

# Delete the items in meta dictionary whose first value is False
del_ = []
for item in meta.keys():
if meta[item][0] is False:
del_.append(item)
del_ = [item for item, value_ in meta.items() if value_[0] is False]
hyp_GA = hyp.copy() # Make a copy of hyp dictionary
for item in del_:
del meta[item] # Remove the item from meta dictionary
Expand All @@ -696,9 +693,7 @@ def main(opt, callbacks=Callbacks()):
upper_limit = np.array([meta[k][2] for k in hyp_GA.keys()])

# Create gene_ranges list to hold the range of values for each gene in the population
gene_ranges = []
for i in range(len(upper_limit)):
gene_ranges.append((lower_limit[i], upper_limit[i]))
gene_ranges = [(lower_limit[i], upper_limit[i]) for i in range(len(upper_limit))]

# Initialize the population with initial_values or random values
initial_values = []
Expand All @@ -723,25 +718,20 @@ def main(opt, callbacks=Callbacks()):

# Generate random values within the search space for the rest of the population
if initial_values is None:
population = [generate_individual(gene_ranges, len(hyp_GA)) for i in range(pop_size)]
else:
if pop_size > 1:
population = [
generate_individual(gene_ranges, len(hyp_GA)) for i in range(pop_size - len(initial_values))
]
for initial_value in initial_values:
population = [initial_value] + population
population = [generate_individual(gene_ranges, len(hyp_GA)) for _ in range(pop_size)]
elif pop_size > 1:
population = [generate_individual(gene_ranges, len(hyp_GA)) for _ in range(pop_size - len(initial_values))]
for initial_value in initial_values:
population = [initial_value] + population

# Run the genetic algorithm for a fixed number of generations
list_keys = list(hyp_GA.keys())
for generation in range(opt.evolve):
if generation >= 1:
save_dict = {}
for i in range(len(population)):
little_dict = {}
for j in range(len(population[i])):
little_dict[list_keys[j]] = float(population[i][j])
save_dict["gen" + str(generation) + "number" + str(i)] = little_dict
little_dict = {list_keys[j]: float(population[i][j]) for j in range(len(population[i]))}
save_dict[f"gen{str(generation)}number{str(i)}"] = little_dict

with open(save_dir / "evolve_population.yaml", "w") as outfile:
yaml.dump(save_dict, outfile, default_flow_style=False)
Expand Down Expand Up @@ -771,7 +761,7 @@ def main(opt, callbacks=Callbacks()):

# Select the fittest individuals for reproduction using adaptive tournament selection
selected_indices = []
for i in range(pop_size - elite_size):
for _ in range(pop_size - elite_size):
# Adaptive tournament size
tournament_size = max(
max(2, tournament_size_min),
Expand All @@ -788,7 +778,7 @@ def main(opt, callbacks=Callbacks()):
selected_indices.extend(elite_indices)
# Create the next generation through crossover and mutation
next_generation = []
for i in range(pop_size):
for _ in range(pop_size):
parent1_index = selected_indices[random.randint(0, pop_size - 1)]
parent2_index = selected_indices[random.randint(0, pop_size - 1)]
# Adaptive crossover rate
Expand Down
4 changes: 1 addition & 3 deletions utils/downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ def is_url(url, check=True):
def gsutil_getsize(url=""):
# gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du
output = subprocess.check_output(["gsutil", "du", url], shell=True, encoding="utf-8")
if output:
return int(output.split()[0])
return 0
return int(output.split()[0]) if output else 0


def url_getsize(url="https://ultralytics.com/images/bus.jpg"):
Expand Down
7 changes: 2 additions & 5 deletions utils/flask_rest_api/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Flask REST API

[REST](https://en.wikipedia.org/wiki/Representational_state_transfer) [API](https://en.wikipedia.org/wiki/API)s are
commonly used to expose Machine Learning (ML) models to other services. This folder contains an example REST API
created using Flask to expose the YOLOv5s model from [PyTorch Hub](https://pytorch.org/hub/ultralytics_yolov5/).
[REST](https://en.wikipedia.org/wiki/Representational_state_transfer) [API](https://en.wikipedia.org/wiki/API)s are commonly used to expose Machine Learning (ML) models to other services. This folder contains an example REST API created using Flask to expose the YOLOv5s model from [PyTorch Hub](https://pytorch.org/hub/ultralytics_yolov5/).

## Requirements

Expand Down Expand Up @@ -69,5 +67,4 @@ The model inference results are returned as a JSON response:
]
```

An example python script to perform inference using [requests](https://docs.python-requests.org/en/master/) is given
in `example_request.py`
An example python script to perform inference using [requests](https://docs.python-requests.org/en/master/) is given in `example_request.py`
2 changes: 1 addition & 1 deletion utils/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class Profile(contextlib.ContextDecorator):
def __init__(self, t=0.0, device: torch.device = None):
self.t = t
self.device = device
self.cuda = True if (device and str(device)[:4] == "cuda") else False
self.cuda = bool(device and str(device).startswith("cuda"))

def __enter__(self):
self.start = self.time()
Expand Down
25 changes: 11 additions & 14 deletions utils/loggers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ def _json_default(value):
value = value.item()
except ValueError: # "only one element tensors can be converted to Python scalars"
pass
if isinstance(value, float):
return value
return str(value)
return value if isinstance(value, float) else str(value)


class Loggers:
Expand Down Expand Up @@ -250,12 +248,12 @@ def on_fit_epoch_end(self, vals, epoch, best_fitness, fi):
f.write(s + ("%20.5g," * n % tuple([epoch] + vals)).rstrip(",") + "\n")
if self.ndjson_console or self.ndjson_file:
json_data = json.dumps(dict(epoch=epoch, **x), default=_json_default)
if self.ndjson_console:
print(json_data)
if self.ndjson_file:
file = self.save_dir / "results.ndjson"
with open(file, "a") as f:
print(json_data, file=f)
if self.ndjson_console:
print(json_data)
if self.ndjson_file:
file = self.save_dir / "results.ndjson"
with open(file, "a") as f:
print(json_data, file=f)

if self.tb:
for k, v in x.items():
Expand Down Expand Up @@ -370,10 +368,7 @@ def __init__(self, opt, console_logger, include=("tb", "wandb", "clearml")):
if clearml and "clearml" in self.include:
try:
# Hyp is not available in classification mode
if "hyp" not in opt:
hyp = {}
else:
hyp = opt.hyp
hyp = {} if "hyp" not in opt else opt.hyp
self.clearml = ClearmlLogger(opt, hyp)
except Exception:
self.clearml = None
Expand Down Expand Up @@ -427,7 +422,9 @@ def log_graph(self, model, imgsz=(640, 640)):
if self.tb:
log_tensorboard_graph(self.tb, model, imgsz)

def log_model(self, model_path, epoch=0, metadata={}):
def log_model(self, model_path, epoch=0, metadata=None):
if metadata is None:
metadata = {}
# Log model to all loggers
if self.wandb:
art = wandb.Artifact(name=f"run_{wandb.run.id}_model", type="model", metadata=metadata)
Expand Down
23 changes: 10 additions & 13 deletions utils/loggers/clearml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ Either sign up for free to the [ClearML Hosted Service](https://cutt.ly/yolov5-t

1. Install the `clearml` python package:

```bash
pip install clearml
```
```bash
pip install clearml
```

1. Connect the ClearML SDK to the server by [creating credentials](https://app.clear.ml/settings/workspace-configuration) (go right top to Settings -> Workspace -> Create new credentials), then execute the command below and follow the instructions:
2. Connect the ClearML SDK to the server by [creating credentials](https://app.clear.ml/settings/workspace-configuration) (go right top to Settings -> Workspace -> Create new credentials), then execute the command below and follow the instructions:

```bash
clearml-init
```
```bash
clearml-init
```

That's it! You're done 😎

Expand All @@ -58,8 +58,7 @@ pip install clearml>=1.2.0

This will enable integration with the YOLOv5 training script. Every training run from now on, will be captured and stored by the ClearML experiment manager.

If you want to change the `project_name` or `task_name`, use the `--project` and `--name` arguments of the `train.py` script, by default the project will be called `YOLOv5` and the task `Training`.
PLEASE NOTE: ClearML uses `/` as a delimiter for subprojects, so be careful when using `/` in your project name!
If you want to change the `project_name` or `task_name`, use the `--project` and `--name` arguments of the `train.py` script, by default the project will be called `YOLOv5` and the task `Training`. PLEASE NOTE: ClearML uses `/` as a delimiter for subprojects, so be careful when using `/` in your project name!

```bash
python train.py --img 640 --batch 16 --epochs 3 --data coco128.yaml --weights yolov5s.pt --cache
Expand All @@ -86,8 +85,7 @@ This will capture:
- Validation images per epoch
- ...

That's a lot right? 🤯
Now, we can visualize all of this information in the ClearML UI to get an overview of our training progress. Add custom columns to the table view (such as e.g. mAP_0.5) so you can easily sort on the best performing model. Or select multiple experiments and directly compare them!
That's a lot right? 🤯 Now, we can visualize all of this information in the ClearML UI to get an overview of our training progress. Add custom columns to the table view (such as e.g. mAP_0.5) so you can easily sort on the best performing model. Or select multiple experiments and directly compare them!
There even more we can do with all of this information, like hyperparameter optimization and remote execution, so keep reading if you want to see how that works!
Expand Down Expand Up @@ -181,8 +179,7 @@ python utils/loggers/clearml/hpo.py
## 🤯 Remote Execution (advanced)
Running HPO locally is really handy, but what if we want to run our experiments on a remote machine instead? Maybe you have access to a very powerful GPU machine on-site, or you have some budget to use cloud GPUs.
This is where the ClearML Agent comes into play. Check out what the agent can do here:
Running HPO locally is really handy, but what if we want to run our experiments on a remote machine instead? Maybe you have access to a very powerful GPU machine on-site, or you have some budget to use cloud GPUs. This is where the ClearML Agent comes into play. Check out what the agent can do here:
- [YouTube video](https://youtu.be/MX3BrXnaULs)
- [Documentation](https://clear.ml/docs/latest/docs/clearml_agent)
Expand Down
Loading

0 comments on commit 4049ad0

Please sign in to comment.