dtlpy 1.114.13__py3-none-any.whl → 1.114.15__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.
- dtlpy/__init__.py +4 -1
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__pycache__/{__init__.cpython-38.pyc → __init__.cpython-310.pyc} +0 -0
- dtlpy/entities/__init__.py +1 -1
- dtlpy/entities/driver.py +3 -2
- dtlpy/entities/filters.py +165 -161
- dtlpy/entities/model.py +14 -24
- dtlpy/entities/paged_entities.py +14 -5
- dtlpy/entities/pipeline.py +31 -0
- dtlpy/entities/task.py +4 -0
- dtlpy/ml/base_model_adapter.py +208 -67
- dtlpy/new_instance.py +1 -1
- dtlpy/repositories/downloader.py +18 -10
- dtlpy/repositories/dpks.py +1 -1
- dtlpy/repositories/drivers.py +293 -81
- dtlpy/repositories/models.py +137 -26
- dtlpy/repositories/pipeline_executions.py +14 -11
- dtlpy/repositories/pipelines.py +179 -181
- dtlpy/repositories/tasks.py +711 -359
- {dtlpy-1.114.13.dist-info → dtlpy-1.114.15.dist-info}/METADATA +14 -3
- {dtlpy-1.114.13.dist-info → dtlpy-1.114.15.dist-info}/RECORD +28 -28
- {dtlpy-1.114.13.dist-info → dtlpy-1.114.15.dist-info}/WHEEL +1 -1
- {dtlpy-1.114.13.data → dtlpy-1.114.15.data}/scripts/dlp +0 -0
- {dtlpy-1.114.13.data → dtlpy-1.114.15.data}/scripts/dlp.bat +0 -0
- {dtlpy-1.114.13.data → dtlpy-1.114.15.data}/scripts/dlp.py +0 -0
- {dtlpy-1.114.13.dist-info → dtlpy-1.114.15.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.114.13.dist-info → dtlpy-1.114.15.dist-info/licenses}/LICENSE +0 -0
- {dtlpy-1.114.13.dist-info → dtlpy-1.114.15.dist-info}/top_level.txt +0 -0
dtlpy/entities/model.py
CHANGED
|
@@ -449,42 +449,26 @@ class Model(entities.BaseEntity):
|
|
|
449
449
|
# methods #
|
|
450
450
|
###########
|
|
451
451
|
|
|
452
|
-
def add_subset(
|
|
452
|
+
def add_subset(
|
|
453
|
+
self, subset_name: str, subset_filter=None, subset_annotation_filter=None
|
|
454
|
+
):
|
|
453
455
|
"""
|
|
454
456
|
Adds a subset for the model, specifying a subset of the model's dataset that could be used for training or
|
|
455
|
-
validation.
|
|
457
|
+
validation. Optionally also adds an annotations subset.
|
|
456
458
|
|
|
457
459
|
:param str subset_name: the name of the subset
|
|
458
|
-
:param
|
|
459
|
-
|
|
460
|
-
**Example**
|
|
461
|
-
|
|
462
|
-
.. code-block:: python
|
|
463
|
-
|
|
464
|
-
model.add_subset(subset_name='train', subset_filter=dtlpy.Filters(field='dir', values='/train'))
|
|
465
|
-
model.metadata['system']['subsets']
|
|
466
|
-
{'train': <dtlpy.entities.filters.Filters object at 0x1501dfe20>}
|
|
460
|
+
:param subset_filter: filtering for items subset. Can be `entities.Filters`, `dict`, or `None`
|
|
461
|
+
:param subset_annotation_filter: optional filtering for annotations subset. Can be `entities.Filters`, `dict`, or `None`
|
|
467
462
|
|
|
468
463
|
"""
|
|
469
|
-
self.models.add_subset(self, subset_name, subset_filter)
|
|
464
|
+
self.models.add_subset(self, subset_name, subset_filter, subset_annotation_filter)
|
|
470
465
|
|
|
471
466
|
def delete_subset(self, subset_name: str):
|
|
472
467
|
"""
|
|
473
|
-
Removes a subset from the model's metadata.
|
|
468
|
+
Removes a subset from the model's metadata (both subsets and annotationsSubsets).
|
|
474
469
|
|
|
475
470
|
:param str subset_name: the name of the subset
|
|
476
471
|
|
|
477
|
-
**Example**
|
|
478
|
-
|
|
479
|
-
.. code-block:: python
|
|
480
|
-
|
|
481
|
-
model.add_subset(subset_name='train', subset_filter=dtlpy.Filters(field='dir', values='/train'))
|
|
482
|
-
model.metadata['system']['subsets']
|
|
483
|
-
{'train': <dtlpy.entities.filters.Filters object at 0x1501dfe20>}
|
|
484
|
-
models.delete_subset(subset_name='train')
|
|
485
|
-
metadata['system']['subsets']
|
|
486
|
-
{}
|
|
487
|
-
|
|
488
472
|
"""
|
|
489
473
|
self.models.delete_subset(self, subset_name)
|
|
490
474
|
|
|
@@ -529,6 +513,8 @@ class Model(entities.BaseEntity):
|
|
|
529
513
|
tags: list = None,
|
|
530
514
|
train_filter: entities.Filters = None,
|
|
531
515
|
validation_filter: entities.Filters = None,
|
|
516
|
+
annotations_train_filter: entities.Filters = None,
|
|
517
|
+
annotations_validation_filter: entities.Filters = None,
|
|
532
518
|
wait=True
|
|
533
519
|
):
|
|
534
520
|
"""
|
|
@@ -545,6 +531,8 @@ class Model(entities.BaseEntity):
|
|
|
545
531
|
:param list tags: `list` of `str` - label of the model
|
|
546
532
|
:param dtlpy.entities.filters.Filters train_filter: Filters entity or a dictionary to define the items' scope in the specified dataset_id for the model train
|
|
547
533
|
:param dtlpy.entities.filters.Filters validation_filter: Filters entity or a dictionary to define the items' scope in the specified dataset_id for the model validation
|
|
534
|
+
:param dtlpy.entities.filters.Filters annotations_train_filter: Filters entity or a dictionary to define the annotations' scope in the specified dataset_id for the model train
|
|
535
|
+
:param dtlpy.entities.filters.Filters annotations_validation_filter: Filters entity or a dictionary to define the annotations' scope in the specified dataset_id for the model validation
|
|
548
536
|
:param bool wait: `bool` wait for the model to be ready before returning
|
|
549
537
|
|
|
550
538
|
:return: dl.Model which is a clone version of the existing model
|
|
@@ -561,6 +549,8 @@ class Model(entities.BaseEntity):
|
|
|
561
549
|
tags=tags,
|
|
562
550
|
train_filter=train_filter,
|
|
563
551
|
validation_filter=validation_filter,
|
|
552
|
+
annotations_train_filter=annotations_train_filter,
|
|
553
|
+
annotations_validation_filter=annotations_validation_filter,
|
|
564
554
|
wait=wait
|
|
565
555
|
)
|
|
566
556
|
|
dtlpy/entities/paged_entities.py
CHANGED
|
@@ -148,7 +148,7 @@ class PagedEntities:
|
|
|
148
148
|
self.get_page()
|
|
149
149
|
else:
|
|
150
150
|
# For offset pagination, increment the offset
|
|
151
|
-
self.
|
|
151
|
+
self._move_page_offset(1)
|
|
152
152
|
self.get_page()
|
|
153
153
|
|
|
154
154
|
if not self.items:
|
|
@@ -166,7 +166,17 @@ class PagedEntities:
|
|
|
166
166
|
yield self.items
|
|
167
167
|
if self.page_offset == 0:
|
|
168
168
|
break
|
|
169
|
-
self.
|
|
169
|
+
self._move_page_offset(-1)
|
|
170
|
+
|
|
171
|
+
def _move_page_offset(self, offset: int) -> None:
|
|
172
|
+
"""
|
|
173
|
+
Move the page offset by a given step.
|
|
174
|
+
:param offset: offset to move
|
|
175
|
+
"""
|
|
176
|
+
self.page_offset += offset
|
|
177
|
+
if self.filters.custom_filter is not None:
|
|
178
|
+
if 'page' in self.filters.custom_filter and self.filters.custom_filter['page'] != self.page_offset:
|
|
179
|
+
self.filters.custom_filter['page'] = self.page_offset
|
|
170
180
|
|
|
171
181
|
def return_page(self, page_offset: Optional[int] = None, page_size: Optional[int] = None) -> List[Any]:
|
|
172
182
|
"""
|
|
@@ -212,7 +222,6 @@ class PagedEntities:
|
|
|
212
222
|
operator=operator_value,
|
|
213
223
|
method=FiltersOperations.AND,
|
|
214
224
|
)
|
|
215
|
-
|
|
216
225
|
# Fetch data
|
|
217
226
|
if self._list_function is None:
|
|
218
227
|
result = self.items_repository._list(filters=req)
|
|
@@ -246,7 +255,7 @@ class PagedEntities:
|
|
|
246
255
|
self.get_page()
|
|
247
256
|
else:
|
|
248
257
|
# For offset pagination, increment the offset
|
|
249
|
-
self.
|
|
258
|
+
self._move_page_offset(1)
|
|
250
259
|
self.get_page()
|
|
251
260
|
|
|
252
261
|
def prev_page(self) -> None:
|
|
@@ -256,7 +265,7 @@ class PagedEntities:
|
|
|
256
265
|
"""
|
|
257
266
|
if self.use_id_based_paging:
|
|
258
267
|
raise NotImplementedError("prev_page is not supported for keyset pagination.")
|
|
259
|
-
self.
|
|
268
|
+
self._move_page_offset(-1)
|
|
260
269
|
self.get_page()
|
|
261
270
|
|
|
262
271
|
def go_to_page(self, page: int = 0) -> None:
|
dtlpy/entities/pipeline.py
CHANGED
|
@@ -504,6 +504,20 @@ class Pipeline(entities.BaseEntity):
|
|
|
504
504
|
)
|
|
505
505
|
return execution
|
|
506
506
|
|
|
507
|
+
def test(self, execution_input=None):
|
|
508
|
+
"""
|
|
509
|
+
Execute a pipeline in test mode and return the pipeline execution as an object.
|
|
510
|
+
|
|
511
|
+
:param execution_input: list of the dl.FunctionIO or dict of pipeline input - example {'item': 'item_id'}
|
|
512
|
+
:return: entities.PipelineExecution object
|
|
513
|
+
"""
|
|
514
|
+
execution = self.pipelines.test(
|
|
515
|
+
pipeline=self,
|
|
516
|
+
pipeline_id=self.id,
|
|
517
|
+
execution_input=execution_input,
|
|
518
|
+
)
|
|
519
|
+
return execution
|
|
520
|
+
|
|
507
521
|
def execute_batch(
|
|
508
522
|
self,
|
|
509
523
|
filters,
|
|
@@ -591,3 +605,20 @@ class Pipeline(entities.BaseEntity):
|
|
|
591
605
|
for variable in self.variables:
|
|
592
606
|
if variable.name in keys:
|
|
593
607
|
variable.value = kwargs[variable.name]
|
|
608
|
+
|
|
609
|
+
def validate(self):
|
|
610
|
+
"""
|
|
611
|
+
Validate the pipeline configuration.
|
|
612
|
+
|
|
613
|
+
**prerequisites**: You must be an *owner* or *developer* to use this method.
|
|
614
|
+
|
|
615
|
+
:return: Validation result
|
|
616
|
+
:rtype: dict
|
|
617
|
+
|
|
618
|
+
**Example**:
|
|
619
|
+
|
|
620
|
+
.. code-block:: python
|
|
621
|
+
|
|
622
|
+
validation_result = pipeline.validate()
|
|
623
|
+
"""
|
|
624
|
+
return self.pipelines.validate(pipeline_json=self.to_json())
|
dtlpy/entities/task.py
CHANGED
|
@@ -9,6 +9,10 @@ from .. import repositories, entities, exceptions
|
|
|
9
9
|
logger = logging.getLogger(name='dtlpy')
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class AllocationMethod(str, Enum):
|
|
13
|
+
DISTRIBUTION = 'distribution'
|
|
14
|
+
PULLING = 'pulling'
|
|
15
|
+
|
|
12
16
|
class ConsensusTaskType(str, Enum):
|
|
13
17
|
CONSENSUS = 'consensus'
|
|
14
18
|
QUALIFICATION = 'qualification'
|
dtlpy/ml/base_model_adapter.py
CHANGED
|
@@ -15,6 +15,7 @@ from functools import partial
|
|
|
15
15
|
import numpy as np
|
|
16
16
|
from concurrent.futures import ThreadPoolExecutor
|
|
17
17
|
import attr
|
|
18
|
+
from collections.abc import MutableMapping
|
|
18
19
|
from .. import entities, utilities, repositories, exceptions
|
|
19
20
|
from ..services import service_defaults
|
|
20
21
|
from ..services.api_client import ApiClient
|
|
@@ -22,8 +23,89 @@ from ..services.api_client import ApiClient
|
|
|
22
23
|
logger = logging.getLogger('ModelAdapter')
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
class ModelConfigurations(MutableMapping):
|
|
27
|
+
"""
|
|
28
|
+
Manages model configuration using composition with a backing dict.
|
|
29
|
+
|
|
30
|
+
Uses MutableMapping to implement dict-like behavior without inheritance.
|
|
31
|
+
This avoids duplication: if we inherited from dict, we'd have two dicts
|
|
32
|
+
(one from inheritance, one from model_entity.configuration), leading to
|
|
33
|
+
data inconsistency and maintenance issues.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, base_model_adapter):
|
|
37
|
+
# Store reference to base_model_adapter dictionary
|
|
38
|
+
self._backing_dict = {}
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
base_model_adapter is not None
|
|
42
|
+
and base_model_adapter.model_entity is not None
|
|
43
|
+
and base_model_adapter.model_entity.configuration is not None
|
|
44
|
+
):
|
|
45
|
+
self._backing_dict = base_model_adapter.model_entity.configuration
|
|
46
|
+
if 'include_background' not in self._backing_dict:
|
|
47
|
+
self._backing_dict['include_background'] = False
|
|
48
|
+
self._base_model_adapter = base_model_adapter
|
|
49
|
+
# Don't call _update_model_entity during initialization to avoid premature updates
|
|
50
|
+
|
|
51
|
+
def _update_model_entity(self):
|
|
52
|
+
if self._base_model_adapter is not None and self._base_model_adapter.model_entity is not None:
|
|
53
|
+
self._base_model_adapter.model_entity.update()
|
|
54
|
+
|
|
55
|
+
def __ior__(self, other):
|
|
56
|
+
self.update(other)
|
|
57
|
+
return self
|
|
58
|
+
|
|
59
|
+
# Required MutableMapping abstract methods
|
|
60
|
+
def __getitem__(self, key):
|
|
61
|
+
return self._backing_dict[key]
|
|
62
|
+
|
|
63
|
+
def __setitem__(self, key, value):
|
|
64
|
+
# Note: This method only updates the backing dict, not object attributes.
|
|
65
|
+
# If you need to also update object attributes, be careful to avoid
|
|
66
|
+
# infinite recursion by not calling __setattr__ from here.
|
|
67
|
+
update = False
|
|
68
|
+
if key not in self._backing_dict or self._backing_dict.get(key) != value:
|
|
69
|
+
update = True
|
|
70
|
+
self._backing_dict[key] = value
|
|
71
|
+
if update:
|
|
72
|
+
self._update_model_entity()
|
|
73
|
+
|
|
74
|
+
def __delitem__(self, key):
|
|
75
|
+
del self._backing_dict[key]
|
|
76
|
+
|
|
77
|
+
def __iter__(self):
|
|
78
|
+
return iter(self._backing_dict)
|
|
79
|
+
|
|
80
|
+
def __len__(self):
|
|
81
|
+
return len(self._backing_dict)
|
|
82
|
+
|
|
83
|
+
def get(self, key, default=None):
|
|
84
|
+
if key not in self._backing_dict:
|
|
85
|
+
self.__setitem__(key, default)
|
|
86
|
+
return self._backing_dict.get(key)
|
|
87
|
+
|
|
88
|
+
def update(self, *args, **kwargs):
|
|
89
|
+
# Check if there will be any modifications
|
|
90
|
+
update_dict = dict(*args, **kwargs)
|
|
91
|
+
has_changes = False
|
|
92
|
+
for key, value in update_dict.items():
|
|
93
|
+
if key not in self._backing_dict or self._backing_dict[key] != value:
|
|
94
|
+
has_changes = True
|
|
95
|
+
break
|
|
96
|
+
self._backing_dict.update(*args, **kwargs)
|
|
97
|
+
|
|
98
|
+
if has_changes:
|
|
99
|
+
self._update_model_entity()
|
|
100
|
+
|
|
101
|
+
def setdefault(self, key, default=None):
|
|
102
|
+
if key not in self._backing_dict:
|
|
103
|
+
self._backing_dict[key] = default
|
|
104
|
+
return self._backing_dict[key]
|
|
105
|
+
|
|
106
|
+
|
|
25
107
|
@dataclasses.dataclass
|
|
26
|
-
class AdapterDefaults(
|
|
108
|
+
class AdapterDefaults(ModelConfigurations):
|
|
27
109
|
# for predict items, dataset, evaluate
|
|
28
110
|
upload_annotations: bool = dataclasses.field(default=True)
|
|
29
111
|
clean_annotations: bool = dataclasses.field(default=True)
|
|
@@ -34,20 +116,32 @@ class AdapterDefaults(dict):
|
|
|
34
116
|
data_path: str = dataclasses.field(default=None)
|
|
35
117
|
output_path: str = dataclasses.field(default=None)
|
|
36
118
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
119
|
+
def __init__(self, base_model_adapter=None):
|
|
120
|
+
super().__init__(base_model_adapter)
|
|
121
|
+
for f in dataclasses.fields(AdapterDefaults):
|
|
122
|
+
# if the field exists in model_entity.configuration, use it
|
|
123
|
+
# else set it from the attribute default value
|
|
124
|
+
if super().get(f.name) is not None:
|
|
125
|
+
super().__setattr__(f.name, super().get(f.name))
|
|
126
|
+
else:
|
|
127
|
+
super().__setitem__(f.name, f.default)
|
|
128
|
+
|
|
129
|
+
def __setattr__(self, key, value):
|
|
130
|
+
# Dataclass-like fields behave as attributes, so map to dict
|
|
131
|
+
super().__setattr__(key, value)
|
|
132
|
+
if not key.startswith("_"):
|
|
133
|
+
super().__setitem__(key, value)
|
|
40
134
|
|
|
41
|
-
def update(self, **kwargs):
|
|
135
|
+
def update(self, *args, **kwargs):
|
|
42
136
|
for f in dataclasses.fields(AdapterDefaults):
|
|
43
137
|
if f.name in kwargs:
|
|
44
138
|
setattr(self, f.name, kwargs[f.name])
|
|
45
|
-
super().update(**kwargs)
|
|
139
|
+
super().update(*args, **kwargs)
|
|
46
140
|
|
|
47
141
|
def resolve(self, key, *args):
|
|
48
|
-
|
|
49
142
|
for arg in args:
|
|
50
143
|
if arg is not None:
|
|
144
|
+
super().__setitem__(key, arg)
|
|
51
145
|
return arg
|
|
52
146
|
return self.get(key, None)
|
|
53
147
|
|
|
@@ -56,12 +150,13 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
56
150
|
_client_api = attr.ib(type=ApiClient, repr=False)
|
|
57
151
|
|
|
58
152
|
def __init__(self, model_entity: entities.Model = None):
|
|
59
|
-
self.adapter_defaults = AdapterDefaults()
|
|
60
153
|
self.logger = logger
|
|
61
154
|
# entities
|
|
62
155
|
self._model_entity = None
|
|
63
156
|
self._package = None
|
|
64
157
|
self._base_configuration = dict()
|
|
158
|
+
self._configuration = None
|
|
159
|
+
self.adapter_defaults = None
|
|
65
160
|
self.package_name = None
|
|
66
161
|
self.model = None
|
|
67
162
|
self.bucket_path = None
|
|
@@ -81,7 +176,7 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
81
176
|
def configuration(self) -> dict:
|
|
82
177
|
# load from model
|
|
83
178
|
if self._model_entity is not None:
|
|
84
|
-
configuration = self.
|
|
179
|
+
configuration = self._configuration
|
|
85
180
|
# else - load the default from the package
|
|
86
181
|
elif self._package is not None:
|
|
87
182
|
configuration = self.package.metadata.get('system', {}).get('ml', {}).get('defaultConfiguration', {})
|
|
@@ -90,10 +185,13 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
90
185
|
return configuration
|
|
91
186
|
|
|
92
187
|
@configuration.setter
|
|
93
|
-
def configuration(self,
|
|
94
|
-
assert isinstance(
|
|
188
|
+
def configuration(self, configuration: dict):
|
|
189
|
+
assert isinstance(configuration, dict)
|
|
95
190
|
if self._model_entity is not None:
|
|
96
|
-
|
|
191
|
+
# Update configuration with received dict
|
|
192
|
+
self._model_entity.configuration = configuration
|
|
193
|
+
self.adapter_defaults = AdapterDefaults(self)
|
|
194
|
+
self._configuration = self.adapter_defaults
|
|
97
195
|
|
|
98
196
|
############
|
|
99
197
|
# Entities #
|
|
@@ -115,6 +213,8 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
115
213
|
'Replacing Model from {!r} to {!r}'.format(self._model_entity.name, model_entity.name))
|
|
116
214
|
self._model_entity = model_entity
|
|
117
215
|
self.package = model_entity.package
|
|
216
|
+
self.adapter_defaults = AdapterDefaults(self)
|
|
217
|
+
self._configuration = self.adapter_defaults
|
|
118
218
|
|
|
119
219
|
@property
|
|
120
220
|
def package(self):
|
|
@@ -252,15 +352,39 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
252
352
|
|
|
253
353
|
return processed
|
|
254
354
|
|
|
255
|
-
def
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
355
|
+
def __include_model_annotations(self, annotation_filters):
|
|
356
|
+
include_model_annotations = self.model_entity.configuration.get("include_model_annotations", False)
|
|
357
|
+
if include_model_annotations is False:
|
|
358
|
+
if annotation_filters.custom_filter is None:
|
|
359
|
+
annotation_filters.add(
|
|
360
|
+
field="metadata.system.model.name", values=False, operator=entities.FiltersOperations.EXISTS
|
|
361
|
+
)
|
|
362
|
+
else:
|
|
363
|
+
annotation_filters.custom_filter['filter']['$and'].append({'metadata.system.model.name': {'$exists': False}})
|
|
364
|
+
return annotation_filters
|
|
365
|
+
|
|
366
|
+
def __download_background_images(self, filters, data_subset_base_path, annotation_options):
|
|
367
|
+
background_list = list()
|
|
368
|
+
if self.configuration.get('include_background', False) is True:
|
|
369
|
+
filters.custom_filter["filter"]["$and"].append({"annotated": False})
|
|
370
|
+
background_list = self.model_entity.dataset.items.download(
|
|
371
|
+
filters=filters,
|
|
372
|
+
local_path=data_subset_base_path,
|
|
373
|
+
annotation_options=annotation_options,
|
|
374
|
+
)
|
|
375
|
+
return background_list
|
|
376
|
+
|
|
377
|
+
def prepare_data(
|
|
378
|
+
self,
|
|
379
|
+
dataset: entities.Dataset,
|
|
380
|
+
# paths
|
|
381
|
+
root_path=None,
|
|
382
|
+
data_path=None,
|
|
383
|
+
output_path=None,
|
|
384
|
+
#
|
|
385
|
+
overwrite=False,
|
|
386
|
+
**kwargs,
|
|
387
|
+
):
|
|
264
388
|
"""
|
|
265
389
|
Prepares dataset locally before training or evaluation.
|
|
266
390
|
download the specific subset selected to data_path and preforms `self.convert` to the data_path dir
|
|
@@ -277,7 +401,6 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
277
401
|
root_path = self.adapter_defaults.resolve("root_path", root_path)
|
|
278
402
|
data_path = self.adapter_defaults.resolve("data_path", data_path)
|
|
279
403
|
output_path = self.adapter_defaults.resolve("output_path", output_path)
|
|
280
|
-
|
|
281
404
|
if root_path is None:
|
|
282
405
|
now = datetime.datetime.now()
|
|
283
406
|
root_path = os.path.join(dataloop_path,
|
|
@@ -300,54 +423,72 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
300
423
|
annotation_options = entities.ViewAnnotationOptions.INSTANCE
|
|
301
424
|
|
|
302
425
|
# Download the subset items
|
|
303
|
-
subsets = self.model_entity.metadata.get("system",
|
|
426
|
+
subsets = self.model_entity.metadata.get("system", {}).get("subsets", None)
|
|
427
|
+
annotations_subsets = self.model_entity.metadata.get("system", {}).get("annotationsSubsets", {})
|
|
304
428
|
if subsets is None:
|
|
305
429
|
raise ValueError("Model (id: {}) must have subsets in metadata.system.subsets".format(self.model_entity.id))
|
|
306
430
|
for subset, filters_dict in subsets.items():
|
|
307
|
-
filters = entities.Filters(custom_filter=filters_dict)
|
|
308
431
|
data_subset_base_path = os.path.join(data_path, subset)
|
|
309
432
|
if os.path.isdir(data_subset_base_path) and not overwrite:
|
|
310
433
|
# existing and dont overwrite
|
|
311
434
|
self.logger.debug("Subset {!r} already exists (and overwrite=False). Skipping.".format(subset))
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
435
|
+
continue
|
|
436
|
+
|
|
437
|
+
filters = entities.Filters(custom_filter=filters_dict)
|
|
438
|
+
self.logger.debug("Downloading subset {!r} of {}".format(subset, self.model_entity.dataset.name))
|
|
439
|
+
|
|
440
|
+
annotation_filters = None
|
|
441
|
+
if subset in annotations_subsets:
|
|
442
|
+
annotation_filters = entities.Filters(
|
|
443
|
+
use_defaults=False,
|
|
444
|
+
resource=entities.FiltersResource.ANNOTATION,
|
|
445
|
+
custom_filter=annotations_subsets[subset]
|
|
446
|
+
)
|
|
447
|
+
# if user provided annotation_filters, skip the default filters
|
|
448
|
+
elif self.model_entity.output_type is not None and self.model_entity.output_type != "embedding":
|
|
449
|
+
annotation_filters = entities.Filters(resource=entities.FiltersResource.ANNOTATION, use_defaults=False)
|
|
450
|
+
if self.model_entity.output_type in [
|
|
451
|
+
entities.AnnotationType.SEGMENTATION,
|
|
452
|
+
entities.AnnotationType.POLYGON,
|
|
453
|
+
]:
|
|
454
|
+
model_output_types = [entities.AnnotationType.SEGMENTATION, entities.AnnotationType.POLYGON]
|
|
455
|
+
else:
|
|
456
|
+
model_output_types = [self.model_entity.output_type]
|
|
457
|
+
|
|
458
|
+
annotation_filters.add(
|
|
459
|
+
field=entities.FiltersKnownFields.TYPE,
|
|
460
|
+
values=model_output_types,
|
|
461
|
+
operator=entities.FiltersOperations.IN,
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
annotation_filters = self.__include_model_annotations(annotation_filters)
|
|
465
|
+
annotations_subsets[subset] = annotation_filters.prepare()
|
|
466
|
+
|
|
467
|
+
ret_list = dataset.items.download(
|
|
468
|
+
filters=filters,
|
|
469
|
+
local_path=data_subset_base_path,
|
|
470
|
+
annotation_options=annotation_options,
|
|
471
|
+
annotation_filters=annotation_filters,
|
|
472
|
+
)
|
|
473
|
+
filters = entities.Filters(custom_filter=subsets[subset])
|
|
474
|
+
background_ret_list = self.__download_background_images(
|
|
475
|
+
filters=filters, data_subset_base_path=data_subset_base_path, annotation_options=annotation_options
|
|
476
|
+
)
|
|
477
|
+
ret_list = list(ret_list)
|
|
478
|
+
background_ret_list = list(background_ret_list)
|
|
479
|
+
self.logger.debug(f"Subset '{subset}': ret_list length: {len(ret_list)}, background_ret_list length: {len(background_ret_list)}")
|
|
480
|
+
# Combine ret_list and background_ret_list generators into a single generator
|
|
481
|
+
ret_list = ret_list + background_ret_list
|
|
482
|
+
if isinstance(ret_list, list) and len(ret_list) == 0:
|
|
483
|
+
if annotation_filters is not None:
|
|
484
|
+
annotation_filters_str = annotation_filters.prepare()
|
|
485
|
+
else:
|
|
486
|
+
annotation_filters_str = None
|
|
487
|
+
raise ValueError(
|
|
488
|
+
f"No items downloaded for subset {subset}! Cannot train model with empty subset.\n"
|
|
489
|
+
f"Subset {subset} filters: {filters.prepare()}\n"
|
|
490
|
+
f"Annotation filters: {annotation_filters_str}"
|
|
491
|
+
)
|
|
351
492
|
|
|
352
493
|
self.convert_from_dtlpy(data_path=data_path, **kwargs)
|
|
353
494
|
return root_path, data_path, output_path
|
|
@@ -365,10 +506,10 @@ class BaseModelAdapter(utilities.BaseServiceRunner):
|
|
|
365
506
|
self.model_entity = model_entity
|
|
366
507
|
if local_path is None:
|
|
367
508
|
local_path = os.path.join(service_defaults.DATALOOP_PATH, "models", self.model_entity.name)
|
|
368
|
-
# Load configuration
|
|
369
|
-
self.
|
|
370
|
-
#
|
|
371
|
-
self.
|
|
509
|
+
# Load configuration and adapter defaults
|
|
510
|
+
self.adapter_defaults = AdapterDefaults(self)
|
|
511
|
+
# Point _configuration to the same object since AdapterDefaults inherits from ModelConfigurations
|
|
512
|
+
self._configuration = self.adapter_defaults
|
|
372
513
|
# Download
|
|
373
514
|
self.model_entity.artifacts.download(
|
|
374
515
|
local_path=local_path,
|
dtlpy/new_instance.py
CHANGED
|
@@ -10,7 +10,7 @@ class Dtlpy:
|
|
|
10
10
|
# main entities
|
|
11
11
|
Project, Dataset, ExpirationOptions, ExportVersion, Trigger, Item, Execution, AnnotationCollection, Annotation,
|
|
12
12
|
Recipe, IndexDriver, AttributesTypes, AttributesRange, Dpk, App, AppModule, AppScope,
|
|
13
|
-
Ontology, Label, Task, TaskPriority, ConsensusTaskType, Assignment, Service, Package, Codebase, Model,
|
|
13
|
+
Ontology, Label, Task, TaskPriority, ConsensusTaskType, AllocationMethod, Assignment, Service, Package, Codebase, Model,
|
|
14
14
|
PackageModule, PackageFunction,
|
|
15
15
|
# annotations
|
|
16
16
|
Box, Cube, Cube3d, Point, Note, Message, Segmentation, Ellipse, Classification, Subtitle, Polyline, Pose, Gis, GisType,
|
dtlpy/repositories/downloader.py
CHANGED
|
@@ -136,16 +136,24 @@ class Downloader:
|
|
|
136
136
|
if file_types is not None:
|
|
137
137
|
filters.add(field='metadata.system.mimetype', values=file_types, operator=entities.FiltersOperations.IN)
|
|
138
138
|
if annotation_filters is not None:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
139
|
+
if len(annotation_filters.and_filter_list) > 0 or len(annotation_filters.or_filter_list) > 0:
|
|
140
|
+
for annotation_filter_and in annotation_filters.and_filter_list:
|
|
141
|
+
filters.add_join(field=annotation_filter_and.field,
|
|
142
|
+
values=annotation_filter_and.values,
|
|
143
|
+
operator=annotation_filter_and.operator,
|
|
144
|
+
method=entities.FiltersMethod.AND)
|
|
145
|
+
for annotation_filter_or in annotation_filters.or_filter_list:
|
|
146
|
+
filters.add_join(field=annotation_filter_or.field,
|
|
147
|
+
values=annotation_filter_or.values,
|
|
148
|
+
operator=annotation_filter_or.operator,
|
|
149
|
+
method=entities.FiltersMethod.OR)
|
|
150
|
+
elif annotation_filters.custom_filter is not None:
|
|
151
|
+
annotation_query_dict = annotation_filters.prepare()
|
|
152
|
+
items_query_dict = filters.prepare()
|
|
153
|
+
items_query_dict["join"] = annotation_query_dict
|
|
154
|
+
filters.reset()
|
|
155
|
+
filters.custom_filter = items_query_dict
|
|
156
|
+
|
|
149
157
|
else:
|
|
150
158
|
annotation_filters = entities.Filters(resource=entities.FiltersResource.ANNOTATION)
|
|
151
159
|
filters._user_query = 'false'
|
dtlpy/repositories/dpks.py
CHANGED
|
@@ -254,7 +254,7 @@ class Dpks:
|
|
|
254
254
|
local_path = os.path.dirname(manifest_filepath)
|
|
255
255
|
if dpk.codebase is None:
|
|
256
256
|
dpk.codebase = self.project.codebases.pack(directory=local_path,
|
|
257
|
-
name=dpk.
|
|
257
|
+
name=dpk.name,
|
|
258
258
|
extension='dpk',
|
|
259
259
|
ignore_directories=['artifacts'],
|
|
260
260
|
ignore_max_file_size=ignore_max_file_size)
|