easydetect 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {easydetect-0.2.1 → easydetect-0.2.2}/PKG-INFO +1 -1
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/__init__.py +1 -1
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/exporter.py +19 -1
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/model.py +4 -2
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/predictor.py +75 -1
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/trainer.py +25 -2
- {easydetect-0.2.1 → easydetect-0.2.2}/pyproject.toml +1 -1
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_onnxruntime.py +27 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_train_export.py +27 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/.gitignore +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/LICENSE +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/NOTICE +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/README.md +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/cli.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/data/__init__.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/data/augment.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/data/dataset.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/data/labels.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/downloads.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/errors.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/metrics.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/__init__.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/decoder.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/denoising.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/dfine_net.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/encoder.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/hgnetv2.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/nn/ops.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/plotting.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/results.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/sources.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/tracker.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/utils/__init__.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/utils/loss.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/utils/ops.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/easydetect/validator.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/__init__.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/conftest.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_augment.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_cli.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_coco2yolo.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_dataset.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_downloads.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_lab_api.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_model.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_nn.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_predictor.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_results.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_sources.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tests/test_tracker.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/bench.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/build_mirror.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/coco2yolo.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/compare_yolo.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/convert_dfine.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/hub_README.md +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/make_demo.py +0 -0
- {easydetect-0.2.1 → easydetect-0.2.2}/tools/make_toyset.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: easydetect
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Easy real-time object detection you can ship: D-FINE with PyTorch training, OpenVINO inference on CPU/GPU/NPU, and a browser app to label and train. Apache-2.0 throughout.
|
|
5
5
|
Project-URL: Homepage, https://github.com/themakerrobot/easydetect
|
|
6
6
|
Project-URL: Source, https://github.com/themakerrobot/easydetect
|
|
@@ -24,7 +24,7 @@ needs numpy/opencv/openvino/pyyaml; training adds
|
|
|
24
24
|
|
|
25
25
|
from __future__ import annotations
|
|
26
26
|
|
|
27
|
-
__version__ = "0.2.
|
|
27
|
+
__version__ = "0.2.2"
|
|
28
28
|
|
|
29
29
|
from .errors import DownloadError, EasyDetectError, ModelNotFoundError
|
|
30
30
|
from .metrics import BoxMetrics, DetMetrics
|
|
@@ -62,12 +62,30 @@ def export_onnx(net, names, imgsz=640, out_dir=".", fname="easydetect", half=Fal
|
|
|
62
62
|
opset_version=17,
|
|
63
63
|
dynamo=False,
|
|
64
64
|
)
|
|
65
|
-
_write_labels(out_dir, fname, names)
|
|
65
|
+
table = _write_labels(out_dir, fname, names)
|
|
66
|
+
_embed_names(onnx_path, table)
|
|
66
67
|
if verbose:
|
|
67
68
|
print(f"[easydetect] exported: {onnx_path}")
|
|
68
69
|
return onnx_path
|
|
69
70
|
|
|
70
71
|
|
|
72
|
+
def _embed_names(onnx_path: Path, table: dict[int, str]) -> None:
|
|
73
|
+
"""Keep the class names inside the .onnx too, so the file works on its own —
|
|
74
|
+
one download from a Hugging Face page, no labels.txt to fetch beside it."""
|
|
75
|
+
import onnx
|
|
76
|
+
|
|
77
|
+
from .predictor import ONNX_NAMES_KEY
|
|
78
|
+
|
|
79
|
+
model = onnx.load(str(onnx_path))
|
|
80
|
+
kept = [p for p in model.metadata_props if p.key != ONNX_NAMES_KEY]
|
|
81
|
+
del model.metadata_props[:]
|
|
82
|
+
model.metadata_props.extend(kept)
|
|
83
|
+
entry = model.metadata_props.add()
|
|
84
|
+
entry.key = ONNX_NAMES_KEY
|
|
85
|
+
entry.value = json.dumps({str(k): v for k, v in table.items()}, ensure_ascii=False)
|
|
86
|
+
onnx.save(model, str(onnx_path))
|
|
87
|
+
|
|
88
|
+
|
|
71
89
|
def export_openvino(net, names, imgsz=640, out_dir=".", fname="easydetect", half=False,
|
|
72
90
|
verbose=True):
|
|
73
91
|
"""Write ``<out_dir>/<fname>.xml`` (+ .bin, + labels). Returns the .xml path."""
|
|
@@ -349,7 +349,7 @@ class Detector:
|
|
|
349
349
|
name: str = "train",
|
|
350
350
|
resume: bool = False,
|
|
351
351
|
patience: int = 50,
|
|
352
|
-
lr0: float =
|
|
352
|
+
lr0: float | None = None,
|
|
353
353
|
seed: int = 0,
|
|
354
354
|
val: bool = True,
|
|
355
355
|
amp: bool = True,
|
|
@@ -362,7 +362,9 @@ class Detector:
|
|
|
362
362
|
"""Train on a data.yaml dataset. Returns the path of ``best.pt``.
|
|
363
363
|
|
|
364
364
|
``freeze="backbone"`` trains about twice as fast on CPU and helps when
|
|
365
|
-
the dataset is small. ``
|
|
365
|
+
the dataset is small. ``lr0=None`` sets the learning rate from the
|
|
366
|
+
batch size, ``1e-4 × √(batch / 4)``, so a bigger batch is not a run
|
|
367
|
+
that learned less; a number sets it outright. ``augment=True`` trains on zoomed-out, cropped
|
|
366
368
|
and colour-jittered pictures (D-FINE's recipe) and on plain ones for
|
|
367
369
|
the last tenth of the epochs; ``False`` keeps only the flip.
|
|
368
370
|
``on_epoch_end`` is called with a dict of the epoch's numbers — the
|
|
@@ -37,13 +37,87 @@ def _looks_like_logits(scores: np.ndarray) -> bool:
|
|
|
37
37
|
return bool(scores.size) and (scores.min() < 0.0 or scores.max() > 1.0)
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
#: The ONNX metadata key easydetect keeps the class names under.
|
|
41
|
+
ONNX_NAMES_KEY = "easydetect.names"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _varint(f) -> int | None:
|
|
45
|
+
shift = value = 0
|
|
46
|
+
while True:
|
|
47
|
+
byte = f.read(1)
|
|
48
|
+
if not byte:
|
|
49
|
+
return None
|
|
50
|
+
value |= (byte[0] & 0x7F) << shift
|
|
51
|
+
if byte[0] < 0x80:
|
|
52
|
+
return value
|
|
53
|
+
shift += 7
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def onnx_metadata(path: Path) -> dict[str, str]:
|
|
57
|
+
"""The ``metadata_props`` of an ONNX file, read without the ``onnx`` package.
|
|
58
|
+
|
|
59
|
+
ModelProto keeps them as field 14, each a (key = 1, value = 2) pair. Only
|
|
60
|
+
the top level of the file is walked; the graph and its weights are skipped
|
|
61
|
+
over with a seek, so a large model costs no more than a small one.
|
|
62
|
+
"""
|
|
63
|
+
props: dict[str, str] = {}
|
|
64
|
+
with open(path, "rb") as f:
|
|
65
|
+
while (tag := _varint(f)) is not None:
|
|
66
|
+
field, wire = tag >> 3, tag & 7
|
|
67
|
+
if wire == 0:
|
|
68
|
+
_varint(f)
|
|
69
|
+
elif wire == 1:
|
|
70
|
+
f.seek(8, 1)
|
|
71
|
+
elif wire == 5:
|
|
72
|
+
f.seek(4, 1)
|
|
73
|
+
elif wire == 2:
|
|
74
|
+
size = _varint(f) or 0
|
|
75
|
+
if field != 14:
|
|
76
|
+
f.seek(size, 1)
|
|
77
|
+
continue
|
|
78
|
+
entry, key, value, i = f.read(size), "", "", 0
|
|
79
|
+
while i < len(entry):
|
|
80
|
+
sub_tag, n = entry[i], 0
|
|
81
|
+
i += 1
|
|
82
|
+
shift = 0
|
|
83
|
+
while True: # the length, a varint
|
|
84
|
+
n |= (entry[i] & 0x7F) << shift
|
|
85
|
+
i += 1
|
|
86
|
+
if entry[i - 1] < 0x80:
|
|
87
|
+
break
|
|
88
|
+
shift += 7
|
|
89
|
+
text = entry[i:i + n].decode("utf-8", "replace")
|
|
90
|
+
i += n
|
|
91
|
+
if sub_tag >> 3 == 1:
|
|
92
|
+
key = text
|
|
93
|
+
elif sub_tag >> 3 == 2:
|
|
94
|
+
value = text
|
|
95
|
+
props[key] = value
|
|
96
|
+
else: # not an ONNX file after all
|
|
97
|
+
break
|
|
98
|
+
return props
|
|
99
|
+
|
|
100
|
+
|
|
40
101
|
def read_names(model_path: Path) -> dict[int, str]:
|
|
41
|
-
"""Class names
|
|
102
|
+
"""Class names: ``<stem>.names.json`` beside the model, then the names an
|
|
103
|
+
``.onnx`` carries inside it, then ``labels.txt`` in its folder.
|
|
104
|
+
|
|
105
|
+
The names inside the file come before a ``labels.txt`` because they belong
|
|
106
|
+
to that file: a lone ``best.onnx`` downloaded into a folder that holds
|
|
107
|
+
another model's ``labels.txt`` still names its own classes.
|
|
108
|
+
"""
|
|
42
109
|
model_path = Path(model_path)
|
|
43
110
|
sidecar = model_path.with_suffix(".names.json")
|
|
44
111
|
if sidecar.exists():
|
|
45
112
|
table = json.loads(sidecar.read_text(encoding="utf-8"))
|
|
46
113
|
return {int(k): str(v) for k, v in table.items()}
|
|
114
|
+
if model_path.suffix.lower() == ".onnx" and model_path.is_file():
|
|
115
|
+
try:
|
|
116
|
+
inside = onnx_metadata(model_path).get(ONNX_NAMES_KEY)
|
|
117
|
+
if inside:
|
|
118
|
+
return {int(k): str(v) for k, v in json.loads(inside).items()}
|
|
119
|
+
except (OSError, ValueError, IndexError):
|
|
120
|
+
pass
|
|
47
121
|
labels = model_path.parent / "labels.txt"
|
|
48
122
|
if labels.exists():
|
|
49
123
|
lines = [ln.strip() for ln in labels.read_text(encoding="utf-8").splitlines()]
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Apache-2.0
|
|
2
2
|
"""Training loop (PyTorch). Kept deliberately small and readable.
|
|
3
3
|
|
|
4
|
-
AdamW with a lower LR on the backbone,
|
|
4
|
+
AdamW with a lower LR on the backbone, the LR set from the batch size unless
|
|
5
|
+
given, linear warmup into a cosine decay, AMP
|
|
5
6
|
on CUDA, ``last.pt``/``best.pt`` after every epoch, early stop on ``patience``,
|
|
6
7
|
and ``resume=True`` to pick a killed run back up where it stopped.
|
|
7
8
|
"""
|
|
@@ -31,6 +32,24 @@ def seed_everything(seed: int) -> None:
|
|
|
31
32
|
torch.cuda.manual_seed_all(seed)
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
#: The learning rate a batch of 4 fine-tunes well at: D-FINE from its COCO
|
|
36
|
+
#: weights reached 0.765 mAP50-95 on a dataset where RT-DETR's own run gave 0.762.
|
|
37
|
+
REF_BATCH, REF_LR = 4, 1e-4
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def auto_lr(batch: int) -> float:
|
|
41
|
+
"""The learning rate for a batch size: ``1e-4 × √(batch / 4)``.
|
|
42
|
+
|
|
43
|
+
A bigger batch means fewer optimizer steps an epoch, and Adam moves each
|
|
44
|
+
weight by about the learning rate per step whatever the batch, so at one
|
|
45
|
+
fixed rate a batch of 32 ends the same epochs having learned an eighth as
|
|
46
|
+
much. The square root is the usual rule for Adam (four times the batch,
|
|
47
|
+
twice the rate); at 32 it gives 2.8e-4, beside the 2–2.5e-4 D-FINE's own
|
|
48
|
+
configs train with at their batch of 32.
|
|
49
|
+
"""
|
|
50
|
+
return REF_LR * math.sqrt(max(int(batch), 1) / REF_BATCH)
|
|
51
|
+
|
|
52
|
+
|
|
34
53
|
#: The backbone learns slower than the rest — D-FINE's per-size ratios.
|
|
35
54
|
BACKBONE_LR_MULT = {"n": 0.5, "s": 0.5, "m": 0.1, "l": 0.05, "x": 0.01}
|
|
36
55
|
|
|
@@ -153,7 +172,7 @@ class Trainer:
|
|
|
153
172
|
epochs=100,
|
|
154
173
|
imgsz=640,
|
|
155
174
|
batch=8,
|
|
156
|
-
lr=
|
|
175
|
+
lr=None,
|
|
157
176
|
lr_backbone_mult=None,
|
|
158
177
|
weight_decay=1e-4,
|
|
159
178
|
warmup_epochs=1,
|
|
@@ -174,6 +193,8 @@ class Trainer:
|
|
|
174
193
|
seed_everything(seed)
|
|
175
194
|
self.augment = augment
|
|
176
195
|
self.seed, self.freeze, self.origin = seed, freeze, origin
|
|
196
|
+
self.lr_auto = lr is None
|
|
197
|
+
lr = auto_lr(batch) if lr is None else float(lr)
|
|
177
198
|
self.lr, self.lr_backbone_mult, self.weight_decay = lr, lr_backbone_mult, weight_decay
|
|
178
199
|
self.net = net
|
|
179
200
|
self.frozen = freeze_modules(net, freeze)
|
|
@@ -288,6 +309,8 @@ class Trainer:
|
|
|
288
309
|
"optimizer": {
|
|
289
310
|
"name": "AdamW",
|
|
290
311
|
"lr": self.lr,
|
|
312
|
+
"lr_from": (f"batch {self.batch}: {REF_LR:g} × √({self.batch}/{REF_BATCH})"
|
|
313
|
+
if self.lr_auto else "set"),
|
|
291
314
|
"lr_backbone": self.lr * self.lr_backbone_mult,
|
|
292
315
|
"weight_decay": self.weight_decay,
|
|
293
316
|
"schedule": "linear warmup, then cosine decay to 1%",
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "easydetect"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Easy real-time object detection you can ship: D-FINE with PyTorch training, OpenVINO inference on CPU/GPU/NPU, and a browser app to label and train. Apache-2.0 throughout."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -104,3 +104,30 @@ def test_a_named_model_downloads_the_onnx_for_onnxruntime(monkeypatch, tmp_path,
|
|
|
104
104
|
monkeypatch.setattr(downloads, "_asset", fake_asset)
|
|
105
105
|
assert downloads.download_onnx("D-FINE-S") == tmp_path / "dfine-s.onnx"
|
|
106
106
|
assert fetched == ["dfine-s.onnx", "labels.txt"]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_an_onnx_carries_its_class_names_so_it_works_alone(tiny_ir, tmp_path):
|
|
110
|
+
"""One file from a Hugging Face page: no labels.txt beside it, or someone else's."""
|
|
111
|
+
import shutil
|
|
112
|
+
|
|
113
|
+
lone = tmp_path / "downloads" / "best.onnx"
|
|
114
|
+
lone.parent.mkdir()
|
|
115
|
+
shutil.copy(tiny_ir.with_suffix(".onnx"), lone)
|
|
116
|
+
(lone.parent / "labels.txt").write_text("person\ncar\nbus\n") # another model's
|
|
117
|
+
for backend in ("openvino", "onnxruntime"):
|
|
118
|
+
model = Detector(str(lone), backend=backend, verbose=False)
|
|
119
|
+
assert model(draw(), conf=0.0)[0].names == {0: "can", 1: "bottle"}, backend
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_the_names_are_read_without_the_onnx_package(tiny_ir, tmp_path):
|
|
123
|
+
onnx = pytest.importorskip("onnx")
|
|
124
|
+
from easydetect.predictor import ONNX_NAMES_KEY, onnx_metadata, read_names
|
|
125
|
+
|
|
126
|
+
path = tiny_ir.with_suffix(".onnx")
|
|
127
|
+
expected = {p.key: p.value for p in onnx.load(str(path)).metadata_props}
|
|
128
|
+
assert onnx_metadata(path) == expected and ONNX_NAMES_KEY in expected
|
|
129
|
+
|
|
130
|
+
# not an ONNX file at all: no names, and no crash
|
|
131
|
+
junk = tmp_path / "junk.onnx"
|
|
132
|
+
junk.write_bytes(b"\xff\xff\xff\xff\x0f not a model")
|
|
133
|
+
assert read_names(junk) == {}
|
|
@@ -217,6 +217,9 @@ def test_training_reports_every_epoch_to_a_callback_and_a_csv(dataset, tmp_path,
|
|
|
217
217
|
assert setup["variant"] == "n" and setup["start"] == {"kind": "scratch"}
|
|
218
218
|
assert setup["freeze"] == "backbone" and 0 < setup["trainable_params"] < setup["params"]
|
|
219
219
|
assert setup["optimizer"]["name"] == "AdamW" and setup["amp"] is False
|
|
220
|
+
# no lr0: the rate came from the batch of 2, and the record says so
|
|
221
|
+
assert setup["optimizer"]["lr"] == pytest.approx(1e-4 * (2 / 4) ** 0.5)
|
|
222
|
+
assert setup["optimizer"]["lr_from"].startswith("batch 2:")
|
|
220
223
|
train = setup["data"]["train"]
|
|
221
224
|
assert train["images"] > 0 and sum(train["per_class"]) == train["boxes"] > 0
|
|
222
225
|
assert setup["versions"]["torch"] and setup["device"] == "cpu" and setup["gpu"] is None
|
|
@@ -306,3 +309,27 @@ def test_pretrained_false_never_reaches_for_the_mirror(dataset, tmp_path, monkey
|
|
|
306
309
|
data=str(dataset), project=str(tmp_path), epochs=1, imgsz=64, batch=2, workers=0,
|
|
307
310
|
device="cpu", val=False, amp=False,
|
|
308
311
|
).exists()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_the_learning_rate_follows_the_batch_unless_it_is_given():
|
|
315
|
+
from easydetect.trainer import auto_lr
|
|
316
|
+
|
|
317
|
+
assert auto_lr(4) == pytest.approx(1e-4)
|
|
318
|
+
assert auto_lr(16) == pytest.approx(2e-4)
|
|
319
|
+
assert auto_lr(32) == pytest.approx(2.83e-4, rel=1e-3)
|
|
320
|
+
assert auto_lr(0) == auto_lr(1)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@needs_torch
|
|
324
|
+
def test_a_learning_rate_that_is_given_is_used_as_it_is(dataset, tmp_path, monkeypatch):
|
|
325
|
+
import json
|
|
326
|
+
|
|
327
|
+
monkeypatch.setattr("easydetect.nn.hgnetv2.HGNetv2.load_imagenet", lambda self, name, url: None)
|
|
328
|
+
model = Detector("dfine-n", verbose=False, pretrained=False)
|
|
329
|
+
best = model.train(data=str(dataset), project=str(tmp_path), epochs=1, imgsz=64, batch=2,
|
|
330
|
+
workers=0, device="cpu", val=False, amp=False, lr0=3e-4,
|
|
331
|
+
warmup_epochs=0, weight_decay=0.0, lr_backbone_mult=0.25)
|
|
332
|
+
opt = json.loads((best.parent.parent / "run.json").read_text())["optimizer"]
|
|
333
|
+
assert opt["lr"] == pytest.approx(3e-4) and opt["lr_from"] == "set"
|
|
334
|
+
assert opt["lr_backbone"] == pytest.approx(0.75e-4)
|
|
335
|
+
assert opt["warmup_epochs"] == 0 and opt["weight_decay"] == 0.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|