dtlpy 1.113.10__py3-none-any.whl → 1.114.13__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 +488 -488
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/__pycache__/__init__.cpython-38.pyc +0 -0
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +311 -311
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +296 -296
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +442 -442
- dtlpy/entities/dataset.py +1285 -1285
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +222 -222
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +641 -641
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +953 -953
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +694 -691
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +803 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +290 -267
- dtlpy/entities/pipeline.py +593 -593
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +499 -499
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +958 -958
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +491 -491
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +945 -940
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -348
- dtlpy/repositories/commands.py +158 -158
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +434 -406
- dtlpy/repositories/datasets.py +1291 -1291
- dtlpy/repositories/downloader.py +895 -895
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +266 -266
- dtlpy/repositories/executions.py +817 -817
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +238 -238
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +909 -915
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +877 -867
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +448 -448
- dtlpy/repositories/pipelines.py +642 -642
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +399 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1124 -1124
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +651 -651
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1782 -1782
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +264 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp +1 -1
- dtlpy-1.114.13.data/scripts/dlp.bat +2 -0
- {dtlpy-1.113.10.data → dtlpy-1.114.13.data}/scripts/dlp.py +128 -128
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/LICENSE +200 -200
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/METADATA +172 -172
- dtlpy-1.114.13.dist-info/RECORD +240 -0
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/WHEEL +1 -1
- tests/features/environment.py +551 -550
- dtlpy-1.113.10.data/scripts/dlp.bat +0 -2
- dtlpy-1.113.10.dist-info/RECORD +0 -244
- tests/assets/__init__.py +0 -0
- tests/assets/models_flow/__init__.py +0 -0
- tests/assets/models_flow/failedmain.py +0 -52
- tests/assets/models_flow/main.py +0 -62
- tests/assets/models_flow/main_model.py +0 -54
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.113.10.dist-info → dtlpy-1.114.13.dist-info}/top_level.txt +0 -0
dtlpy/repositories/models.py
CHANGED
|
@@ -1,867 +1,877 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from typing import List
|
|
3
|
-
import logging
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from ..
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
:param
|
|
78
|
-
:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
'
|
|
147
|
-
'
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
:
|
|
171
|
-
:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
:param
|
|
225
|
-
:param
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
model_entity.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
model.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
:param
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
model_entity.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
model_entity
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
model.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
:param str
|
|
295
|
-
:param
|
|
296
|
-
:param
|
|
297
|
-
:param str
|
|
298
|
-
:param
|
|
299
|
-
:param
|
|
300
|
-
:param
|
|
301
|
-
:param
|
|
302
|
-
:param
|
|
303
|
-
:param
|
|
304
|
-
:param str
|
|
305
|
-
:param str
|
|
306
|
-
:param str
|
|
307
|
-
:param str
|
|
308
|
-
:param
|
|
309
|
-
:param dtlpy.entities.filters.Filters
|
|
310
|
-
:param dtlpy.entities.
|
|
311
|
-
:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
labels
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
payload
|
|
362
|
-
|
|
363
|
-
'
|
|
364
|
-
'
|
|
365
|
-
'
|
|
366
|
-
'
|
|
367
|
-
'
|
|
368
|
-
'
|
|
369
|
-
'
|
|
370
|
-
'
|
|
371
|
-
'
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
:param
|
|
451
|
-
:param str
|
|
452
|
-
:param
|
|
453
|
-
:param
|
|
454
|
-
:param str
|
|
455
|
-
:param str
|
|
456
|
-
:param
|
|
457
|
-
:param
|
|
458
|
-
:param
|
|
459
|
-
:param
|
|
460
|
-
:param dtlpy.entities.filters.Filters
|
|
461
|
-
:param
|
|
462
|
-
:
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
"
|
|
467
|
-
"
|
|
468
|
-
"
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
_ = from_json['configuration'].pop('
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
:param
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
:param
|
|
555
|
-
:param str
|
|
556
|
-
:
|
|
557
|
-
:
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
return
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
return
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
""
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
""
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
if not
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
""
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
if not
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
""
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
if
|
|
749
|
-
raise
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
return
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
# return
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1
|
+
import time
|
|
2
|
+
from typing import List
|
|
3
|
+
import logging
|
|
4
|
+
from urllib.parse import urlencode
|
|
5
|
+
|
|
6
|
+
from .. import entities, repositories, exceptions, miscellaneous
|
|
7
|
+
from ..services.api_client import ApiClient
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(name='dtlpy')
|
|
10
|
+
|
|
11
|
+
MIN_INTERVAL = 1
|
|
12
|
+
BACKOFF_FACTOR = 1.2
|
|
13
|
+
MAX_INTERVAL = 12
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Models:
|
|
17
|
+
"""
|
|
18
|
+
Models Repository
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self,
|
|
22
|
+
client_api: ApiClient,
|
|
23
|
+
package: entities.Package = None,
|
|
24
|
+
project: entities.Project = None,
|
|
25
|
+
project_id: str = None):
|
|
26
|
+
self._client_api = client_api
|
|
27
|
+
self._project = project
|
|
28
|
+
self._package = package
|
|
29
|
+
self._project_id = project_id
|
|
30
|
+
|
|
31
|
+
if self._project is not None:
|
|
32
|
+
self._project_id = self._project.id
|
|
33
|
+
|
|
34
|
+
############
|
|
35
|
+
# entities #
|
|
36
|
+
############
|
|
37
|
+
@property
|
|
38
|
+
def project(self) -> entities.Project:
|
|
39
|
+
if self._project is None:
|
|
40
|
+
if self._project_id is not None:
|
|
41
|
+
projects = repositories.Projects(client_api=self._client_api)
|
|
42
|
+
self._project = projects.get(project_id=self._project_id)
|
|
43
|
+
if self._project is None:
|
|
44
|
+
if self._package is not None:
|
|
45
|
+
if self._package._project is not None:
|
|
46
|
+
self._project = self._package._project
|
|
47
|
+
if self._project is None:
|
|
48
|
+
raise exceptions.PlatformException(
|
|
49
|
+
error='2001',
|
|
50
|
+
message='Missing "project". need to set a Project entity or use project.models repository')
|
|
51
|
+
assert isinstance(self._project, entities.Project)
|
|
52
|
+
return self._project
|
|
53
|
+
|
|
54
|
+
@project.setter
|
|
55
|
+
def project(self, project: entities.Project):
|
|
56
|
+
if not isinstance(project, entities.Project):
|
|
57
|
+
raise ValueError('Must input a valid Project entity')
|
|
58
|
+
self._project = project
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def package(self) -> entities.Package:
|
|
62
|
+
if self._package is None:
|
|
63
|
+
raise exceptions.PlatformException(
|
|
64
|
+
error='2001',
|
|
65
|
+
message='Cannot perform action WITHOUT Package entity in {} repository.'.format(
|
|
66
|
+
self.__class__.__name__) +
|
|
67
|
+
' Please use package.models or set a model')
|
|
68
|
+
assert isinstance(self._package, entities.Package)
|
|
69
|
+
return self._package
|
|
70
|
+
|
|
71
|
+
###########
|
|
72
|
+
# methods #
|
|
73
|
+
###########
|
|
74
|
+
def get(self, model_name=None, model_id=None) -> entities.Model:
|
|
75
|
+
"""
|
|
76
|
+
Get model object
|
|
77
|
+
:param model_name:
|
|
78
|
+
:param model_id:
|
|
79
|
+
:return: dl.Model object
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
if model_id is not None:
|
|
83
|
+
success, response = self._client_api.gen_request(req_type="get",
|
|
84
|
+
path="/ml/models/{}".format(model_id))
|
|
85
|
+
if not success:
|
|
86
|
+
raise exceptions.PlatformException(response)
|
|
87
|
+
model = entities.Model.from_json(client_api=self._client_api,
|
|
88
|
+
_json=response.json(),
|
|
89
|
+
project=self._project,
|
|
90
|
+
package=self._package)
|
|
91
|
+
# verify input model name is same as the given id
|
|
92
|
+
if model_name is not None and model.name != model_name:
|
|
93
|
+
logger.warning(
|
|
94
|
+
"Mismatch found in models.get: model_name is different then model.name:"
|
|
95
|
+
" {!r} != {!r}".format(
|
|
96
|
+
model_name,
|
|
97
|
+
model.name))
|
|
98
|
+
elif model_name is not None:
|
|
99
|
+
|
|
100
|
+
filters = entities.Filters(
|
|
101
|
+
resource=entities.FiltersResource.MODEL,
|
|
102
|
+
field='name',
|
|
103
|
+
values=model_name
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
project_id = None
|
|
107
|
+
|
|
108
|
+
if self._project is not None:
|
|
109
|
+
project_id = self._project.id
|
|
110
|
+
elif self._project_id is not None:
|
|
111
|
+
project_id = self._project_id
|
|
112
|
+
|
|
113
|
+
if project_id is not None:
|
|
114
|
+
filters.add(field='projectId', values=project_id)
|
|
115
|
+
|
|
116
|
+
if self._package is not None:
|
|
117
|
+
filters.add(field='packageId', values=self._package.id)
|
|
118
|
+
|
|
119
|
+
models = self.list(filters=filters)
|
|
120
|
+
|
|
121
|
+
if models.items_count == 0:
|
|
122
|
+
raise exceptions.PlatformException(
|
|
123
|
+
error='404',
|
|
124
|
+
message='Model not found. Name: {}'.format(model_name))
|
|
125
|
+
elif models.items_count > 1:
|
|
126
|
+
raise exceptions.PlatformException(
|
|
127
|
+
error='400',
|
|
128
|
+
message='More than one Model found by the name of: {}. Try "get" by id or "list()".'.format(
|
|
129
|
+
model_name))
|
|
130
|
+
model = models.items[0]
|
|
131
|
+
else:
|
|
132
|
+
raise exceptions.PlatformException(
|
|
133
|
+
error='400',
|
|
134
|
+
message='No checked-out Model was found, must checkout or provide an identifier in inputs')
|
|
135
|
+
|
|
136
|
+
return model
|
|
137
|
+
|
|
138
|
+
def _build_entities_from_response(self, response_items) -> miscellaneous.List[entities.Model]:
|
|
139
|
+
jobs = [None for _ in range(len(response_items))]
|
|
140
|
+
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
141
|
+
|
|
142
|
+
# return triggers list
|
|
143
|
+
for i_service, service in enumerate(response_items):
|
|
144
|
+
jobs[i_service] = pool.submit(entities.Model._protected_from_json,
|
|
145
|
+
**{'client_api': self._client_api,
|
|
146
|
+
'_json': service,
|
|
147
|
+
'package': self._package,
|
|
148
|
+
'project': self._project})
|
|
149
|
+
|
|
150
|
+
# get all results
|
|
151
|
+
results = [j.result() for j in jobs]
|
|
152
|
+
# log errors
|
|
153
|
+
_ = [logger.warning(r[1]) for r in results if r[0] is False]
|
|
154
|
+
# return good jobs
|
|
155
|
+
return miscellaneous.List([r[1] for r in results if r[0] is True])
|
|
156
|
+
|
|
157
|
+
def _list(self, filters: entities.Filters):
|
|
158
|
+
# request
|
|
159
|
+
success, response = self._client_api.gen_request(req_type='POST',
|
|
160
|
+
path='/ml/models/query',
|
|
161
|
+
json_req=filters.prepare())
|
|
162
|
+
if not success:
|
|
163
|
+
raise exceptions.PlatformException(response)
|
|
164
|
+
return response.json()
|
|
165
|
+
|
|
166
|
+
def list(self, filters: entities.Filters = None) -> entities.PagedEntities:
|
|
167
|
+
"""
|
|
168
|
+
List project model
|
|
169
|
+
|
|
170
|
+
:param dtlpy.entities.filters.Filters filters: Filters entity or a dictionary containing filters parameters
|
|
171
|
+
:return: Paged entity
|
|
172
|
+
:rtype: dtlpy.entities.paged_entities.PagedEntities
|
|
173
|
+
"""
|
|
174
|
+
# default filters
|
|
175
|
+
if filters is None:
|
|
176
|
+
filters = entities.Filters(resource=entities.FiltersResource.MODEL)
|
|
177
|
+
if self._project is not None:
|
|
178
|
+
filters.add(field='projectId', values=self._project.id)
|
|
179
|
+
if self._package is not None:
|
|
180
|
+
filters.add(field='packageId', values=self._package.id)
|
|
181
|
+
|
|
182
|
+
# assert type filters
|
|
183
|
+
if not isinstance(filters, entities.Filters):
|
|
184
|
+
raise exceptions.PlatformException(error='400',
|
|
185
|
+
message='Unknown filters type: {!r}'.format(type(filters)))
|
|
186
|
+
|
|
187
|
+
if filters.resource != entities.FiltersResource.MODEL:
|
|
188
|
+
raise exceptions.PlatformException(
|
|
189
|
+
error='400',
|
|
190
|
+
message='Filters resource must to be FiltersResource.MODEL. Got: {!r}'.format(filters.resource))
|
|
191
|
+
|
|
192
|
+
paged = entities.PagedEntities(items_repository=self,
|
|
193
|
+
filters=filters,
|
|
194
|
+
page_offset=filters.page,
|
|
195
|
+
page_size=filters.page_size,
|
|
196
|
+
client_api=self._client_api)
|
|
197
|
+
paged.get_page()
|
|
198
|
+
return paged
|
|
199
|
+
|
|
200
|
+
def _set_model_filter(self,
|
|
201
|
+
metadata: dict,
|
|
202
|
+
train_filter: entities.Filters = None,
|
|
203
|
+
validation_filter: entities.Filters = None):
|
|
204
|
+
if metadata is None:
|
|
205
|
+
metadata = {}
|
|
206
|
+
if 'system' not in metadata:
|
|
207
|
+
metadata['system'] = {}
|
|
208
|
+
if 'subsets' not in metadata['system']:
|
|
209
|
+
metadata['system']['subsets'] = {}
|
|
210
|
+
if train_filter is not None:
|
|
211
|
+
metadata['system']['subsets']['train'] = train_filter.prepare() if isinstance(train_filter,
|
|
212
|
+
entities.Filters) else train_filter
|
|
213
|
+
if validation_filter is not None:
|
|
214
|
+
metadata['system']['subsets']['validation'] = validation_filter.prepare() if isinstance(validation_filter,
|
|
215
|
+
entities.Filters) else validation_filter
|
|
216
|
+
return metadata
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def add_subset(model: entities.Model, subset_name: str, subset_filter: entities.Filters):
|
|
220
|
+
"""
|
|
221
|
+
Adds a subset for a model, specifying a subset of the model's dataset that could be used for training or
|
|
222
|
+
validation.
|
|
223
|
+
|
|
224
|
+
:param dtlpy.entities.Model model: the model to which the subset should be added
|
|
225
|
+
:param str subset_name: the name of the subset
|
|
226
|
+
:param dtlpy.entities.Filters subset_filter: the filtering operation that this subset performs in the dataset.
|
|
227
|
+
|
|
228
|
+
**Example**
|
|
229
|
+
|
|
230
|
+
.. code-block:: python
|
|
231
|
+
|
|
232
|
+
project.models.add_subset(model=model_entity, subset_name='train', subset_filter=dtlpy.Filters(field='dir', values='/train'))
|
|
233
|
+
model_entity.metadata['system']['subsets']
|
|
234
|
+
{'train': <dtlpy.entities.filters.Filters object at 0x1501dfe20>}
|
|
235
|
+
|
|
236
|
+
"""
|
|
237
|
+
if 'system' not in model.metadata:
|
|
238
|
+
model.metadata['system'] = dict()
|
|
239
|
+
if 'subsets' not in model.metadata['system']:
|
|
240
|
+
model.metadata['system']['subsets'] = dict()
|
|
241
|
+
model.metadata['system']['subsets'][subset_name] = subset_filter.prepare()
|
|
242
|
+
model.update(system_metadata=True)
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def delete_subset(model: entities.Model, subset_name: str):
|
|
246
|
+
"""
|
|
247
|
+
Removes a subset from a model's metadata.
|
|
248
|
+
|
|
249
|
+
:param dtlpy.entities.Model model: the model to which the subset should be added
|
|
250
|
+
:param str subset_name: the name of the subset
|
|
251
|
+
|
|
252
|
+
**Example**
|
|
253
|
+
|
|
254
|
+
.. code-block:: python
|
|
255
|
+
|
|
256
|
+
project.models.add_subset(model=model_entity, subset_name='train', subset_filter=dtlpy.Filters(field='dir', values='/train'))
|
|
257
|
+
model_entity.metadata['system']['subsets']
|
|
258
|
+
{'train': <dtlpy.entities.filters.Filters object at 0x1501dfe20>}
|
|
259
|
+
project.models.delete_subset(model=model_entity, subset_name='train')
|
|
260
|
+
model_entity.metadata['system']['subsets']
|
|
261
|
+
{}
|
|
262
|
+
|
|
263
|
+
"""
|
|
264
|
+
if model.metadata.get("system", dict()).get("subsets", dict()).get(subset_name) is None:
|
|
265
|
+
logger.error(f"Model system metadata incomplete, could not delete subset {subset_name}.")
|
|
266
|
+
else:
|
|
267
|
+
_ = model.metadata['system']['subsets'].pop(subset_name)
|
|
268
|
+
model.update(system_metadata=True)
|
|
269
|
+
|
|
270
|
+
def create(
|
|
271
|
+
self,
|
|
272
|
+
model_name: str,
|
|
273
|
+
dataset_id: str = None,
|
|
274
|
+
labels: list = None,
|
|
275
|
+
ontology_id: str = None,
|
|
276
|
+
description: str = None,
|
|
277
|
+
model_artifacts: List[entities.Artifact] = None,
|
|
278
|
+
project_id=None,
|
|
279
|
+
tags: List[str] = None,
|
|
280
|
+
package: entities.Package = None,
|
|
281
|
+
configuration: dict = None,
|
|
282
|
+
status: str = None,
|
|
283
|
+
scope: entities.EntityScopeLevel = entities.EntityScopeLevel.PROJECT,
|
|
284
|
+
version: str = '1.0.0',
|
|
285
|
+
input_type=None,
|
|
286
|
+
output_type=None,
|
|
287
|
+
train_filter: entities.Filters = None,
|
|
288
|
+
validation_filter: entities.Filters = None,
|
|
289
|
+
app: entities.App = None
|
|
290
|
+
) -> entities.Model:
|
|
291
|
+
"""
|
|
292
|
+
Create a Model entity
|
|
293
|
+
|
|
294
|
+
:param str model_name: name of the model
|
|
295
|
+
:param str dataset_id: dataset id
|
|
296
|
+
:param list labels: list of labels from ontology (must mach ontology id) can be a subset
|
|
297
|
+
:param str ontology_id: ontology to connect to the model
|
|
298
|
+
:param str description: description
|
|
299
|
+
:param model_artifacts: optional list of dl.Artifact. Can be ItemArtifact, LocaArtifact or LinkArtifact
|
|
300
|
+
:param str project_id: project that owns the model
|
|
301
|
+
:param list tags: list of string tags
|
|
302
|
+
:param package: optional - Package object
|
|
303
|
+
:param dict configuration: optional - model configuration - dict
|
|
304
|
+
:param str status: `str` of the optional values of
|
|
305
|
+
:param str scope: the scope level of the model dl.EntityScopeLevel
|
|
306
|
+
:param str version: version of the model
|
|
307
|
+
:param str input_type: the file type the model expect as input (image, video, txt, etc)
|
|
308
|
+
:param str output_type: dl.AnnotationType - the type of annotations the model produces (class, box segment, text, etc)
|
|
309
|
+
: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
|
|
310
|
+
: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
|
|
311
|
+
:param dtlpy.entities.App app: App entity to connect the model to
|
|
312
|
+
:return: Model Entity
|
|
313
|
+
|
|
314
|
+
**Example**:
|
|
315
|
+
|
|
316
|
+
.. code-block:: python
|
|
317
|
+
|
|
318
|
+
project.models.create(model_name='model_name', dataset_id='dataset_id', labels=['label1', 'label2'], train_filter={filter: {$and: [{dir: "/10K short videos"}]},page: 0,pageSize: 1000,resource: "items"}})
|
|
319
|
+
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
if ontology_id is not None:
|
|
323
|
+
# take labels from ontology
|
|
324
|
+
ontologies = repositories.Ontologies(client_api=self._client_api)
|
|
325
|
+
labels = [label.tag for label in ontologies.get(ontology_id=ontology_id).labels]
|
|
326
|
+
|
|
327
|
+
if labels is None:
|
|
328
|
+
# dont have to have labels. can use an empty list
|
|
329
|
+
labels = list()
|
|
330
|
+
|
|
331
|
+
if input_type is None:
|
|
332
|
+
input_type = 'image'
|
|
333
|
+
|
|
334
|
+
if output_type is None:
|
|
335
|
+
output_type = entities.AnnotationType.CLASSIFICATION
|
|
336
|
+
|
|
337
|
+
if package is None and self._package is None:
|
|
338
|
+
raise exceptions.PlatformException('Must provide a Package or create from package.models')
|
|
339
|
+
elif package is None:
|
|
340
|
+
package = self._package
|
|
341
|
+
|
|
342
|
+
# TODO need to remove the entire project id user interface - need to take it from dataset id (in BE)
|
|
343
|
+
if project_id is None:
|
|
344
|
+
if self._project is None:
|
|
345
|
+
raise exceptions.PlatformException('Please provide project_id')
|
|
346
|
+
project_id = self._project.id
|
|
347
|
+
else:
|
|
348
|
+
if project_id != self._project_id:
|
|
349
|
+
if (isinstance(package, entities.Package) and not package.is_global) or \
|
|
350
|
+
(isinstance(package, entities.Dpk) and not package.scope != 'public'):
|
|
351
|
+
logger.warning(
|
|
352
|
+
"Note! you are specified project_id {!r} which is different from repository context: {!r}".format(
|
|
353
|
+
project_id, self._project_id))
|
|
354
|
+
|
|
355
|
+
if model_artifacts is None:
|
|
356
|
+
model_artifacts = []
|
|
357
|
+
|
|
358
|
+
if not isinstance(model_artifacts, list):
|
|
359
|
+
raise ValueError('`model_artifacts` must be a list of dl.Artifact entities')
|
|
360
|
+
|
|
361
|
+
# create payload for request
|
|
362
|
+
payload = {
|
|
363
|
+
'packageId': package.id,
|
|
364
|
+
'name': model_name,
|
|
365
|
+
'projectId': project_id,
|
|
366
|
+
'datasetId': dataset_id,
|
|
367
|
+
'labels': labels,
|
|
368
|
+
'artifacts': [artifact.to_json(as_artifact=True) for artifact in model_artifacts],
|
|
369
|
+
'scope': scope,
|
|
370
|
+
'version': version,
|
|
371
|
+
'inputType': input_type,
|
|
372
|
+
'outputType': output_type,
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if app is not None:
|
|
376
|
+
if not isinstance(package, entities.Dpk):
|
|
377
|
+
raise ValueError('package must be a Dpk entity')
|
|
378
|
+
if app.dpk_name != package.name or app.dpk_version != package.version:
|
|
379
|
+
raise ValueError('App and package must be the same')
|
|
380
|
+
component_name = None
|
|
381
|
+
compute_config = None
|
|
382
|
+
for model in package.components.models:
|
|
383
|
+
if model['name'] == model_name:
|
|
384
|
+
component_name = model['name']
|
|
385
|
+
compute_config = model.get('computeConfigs', None)
|
|
386
|
+
break
|
|
387
|
+
if component_name is None:
|
|
388
|
+
raise ValueError('Model name not found in package')
|
|
389
|
+
payload['app'] = {
|
|
390
|
+
"id": app.id,
|
|
391
|
+
"componentName": component_name,
|
|
392
|
+
"dpkName": package.name,
|
|
393
|
+
"dpkVersion": package.version
|
|
394
|
+
}
|
|
395
|
+
if compute_config is not None:
|
|
396
|
+
payload['app']['computeConfig'] = compute_config
|
|
397
|
+
|
|
398
|
+
if configuration is not None:
|
|
399
|
+
payload['configuration'] = configuration
|
|
400
|
+
|
|
401
|
+
if tags is not None:
|
|
402
|
+
payload['tags'] = tags
|
|
403
|
+
|
|
404
|
+
if description is not None:
|
|
405
|
+
payload['description'] = description
|
|
406
|
+
|
|
407
|
+
if status is not None:
|
|
408
|
+
payload['status'] = status
|
|
409
|
+
|
|
410
|
+
if train_filter or validation_filter:
|
|
411
|
+
metadata = self._set_model_filter(metadata={},
|
|
412
|
+
train_filter=train_filter,
|
|
413
|
+
validation_filter=validation_filter)
|
|
414
|
+
payload['metadata'] = metadata
|
|
415
|
+
|
|
416
|
+
# request
|
|
417
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
418
|
+
path='/ml/models',
|
|
419
|
+
json_req=payload)
|
|
420
|
+
|
|
421
|
+
# exception handling
|
|
422
|
+
if not success:
|
|
423
|
+
raise exceptions.PlatformException(response)
|
|
424
|
+
|
|
425
|
+
model = entities.Model.from_json(_json=response.json(),
|
|
426
|
+
client_api=self._client_api,
|
|
427
|
+
project=self._project,
|
|
428
|
+
package=package)
|
|
429
|
+
|
|
430
|
+
return model
|
|
431
|
+
|
|
432
|
+
def clone(self,
|
|
433
|
+
from_model: entities.Model,
|
|
434
|
+
model_name: str,
|
|
435
|
+
dataset: entities.Dataset = None,
|
|
436
|
+
configuration: dict = None,
|
|
437
|
+
status=None,
|
|
438
|
+
scope=None,
|
|
439
|
+
project_id: str = None,
|
|
440
|
+
labels: list = None,
|
|
441
|
+
description: str = None,
|
|
442
|
+
tags: list = None,
|
|
443
|
+
train_filter: entities.Filters = None,
|
|
444
|
+
validation_filter: entities.Filters = None,
|
|
445
|
+
wait=True,
|
|
446
|
+
) -> entities.Model:
|
|
447
|
+
"""
|
|
448
|
+
Clones and creates a new model out of existing one
|
|
449
|
+
|
|
450
|
+
:param from_model: existing model to clone from
|
|
451
|
+
:param str model_name: `str` new model name
|
|
452
|
+
:param str dataset: dataset object for the cloned model
|
|
453
|
+
:param dict configuration: `dict` (optional) if passed replaces the current configuration
|
|
454
|
+
:param str status: `str` (optional) set the new status
|
|
455
|
+
:param str scope: `str` (optional) set the new scope. default is "project"
|
|
456
|
+
:param str project_id: `str` specify the project id to create the new model on (if other than the source model)
|
|
457
|
+
:param list labels: `list` of `str` - label of the model
|
|
458
|
+
:param str description: `str` description of the new model
|
|
459
|
+
:param list tags: `list` of `str` - label of the model
|
|
460
|
+
: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
|
|
461
|
+
: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
|
|
462
|
+
:param bool wait: `bool` wait for model to be ready
|
|
463
|
+
:return: dl.Model which is a clone version of the existing model
|
|
464
|
+
"""
|
|
465
|
+
from_json = {"name": model_name,
|
|
466
|
+
"packageId": from_model.package_id,
|
|
467
|
+
"configuration": from_model.configuration,
|
|
468
|
+
"outputType": from_model.output_type,
|
|
469
|
+
"inputType": from_model.input_type}
|
|
470
|
+
if project_id is None:
|
|
471
|
+
if dataset is not None:
|
|
472
|
+
# take dataset project
|
|
473
|
+
project_id = dataset.project.id
|
|
474
|
+
else:
|
|
475
|
+
# take model's project
|
|
476
|
+
project_id = self.project.id
|
|
477
|
+
from_json['projectId'] = project_id
|
|
478
|
+
if dataset is not None:
|
|
479
|
+
if labels is None:
|
|
480
|
+
labels = list(dataset.labels_flat_dict.keys())
|
|
481
|
+
from_json['datasetId'] = dataset.id
|
|
482
|
+
if labels is not None:
|
|
483
|
+
from_json['labels'] = labels
|
|
484
|
+
# if there are new labels - pop the mapping from the original
|
|
485
|
+
_ = from_json['configuration'].pop('id_to_label_map', None)
|
|
486
|
+
_ = from_json['configuration'].pop('label_to_id_map', None)
|
|
487
|
+
if configuration is not None:
|
|
488
|
+
from_json['configuration'].update(configuration)
|
|
489
|
+
if description is not None:
|
|
490
|
+
from_json['description'] = description
|
|
491
|
+
if tags is not None:
|
|
492
|
+
from_json['tags'] = tags
|
|
493
|
+
if scope is not None:
|
|
494
|
+
from_json['scope'] = scope
|
|
495
|
+
if status is not None:
|
|
496
|
+
from_json['status'] = status
|
|
497
|
+
|
|
498
|
+
metadata = self._set_model_filter(metadata={},
|
|
499
|
+
train_filter=train_filter if train_filter is not None else from_model.metadata.get(
|
|
500
|
+
'system', {}).get('subsets', {}).get('train', None),
|
|
501
|
+
validation_filter=validation_filter if validation_filter is not None else from_model.metadata.get(
|
|
502
|
+
'system', {}).get('subsets', {}).get('validation', None))
|
|
503
|
+
if metadata:
|
|
504
|
+
from_json['metadata'] = metadata
|
|
505
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
506
|
+
path='/ml/models/{}/clone'.format(from_model.id),
|
|
507
|
+
json_req=from_json)
|
|
508
|
+
if not success:
|
|
509
|
+
raise exceptions.PlatformException(response)
|
|
510
|
+
new_model = entities.Model.from_json(_json=response.json(),
|
|
511
|
+
client_api=self._client_api,
|
|
512
|
+
project=self._project,
|
|
513
|
+
package=from_model._package)
|
|
514
|
+
if wait:
|
|
515
|
+
new_model = self.wait_for_model_ready(model=new_model)
|
|
516
|
+
return new_model
|
|
517
|
+
|
|
518
|
+
def wait_for_model_ready(self, model: entities.Model):
|
|
519
|
+
"""
|
|
520
|
+
Wait for model to be ready
|
|
521
|
+
|
|
522
|
+
:param model: Model entity
|
|
523
|
+
"""
|
|
524
|
+
sleep_time = MIN_INTERVAL
|
|
525
|
+
while model.status == entities.ModelStatus.CLONING:
|
|
526
|
+
model = self.get(model_id=model.id)
|
|
527
|
+
time.sleep(sleep_time)
|
|
528
|
+
sleep_time = min(sleep_time * BACKOFF_FACTOR, MAX_INTERVAL)
|
|
529
|
+
time.sleep(sleep_time)
|
|
530
|
+
return model
|
|
531
|
+
|
|
532
|
+
@property
|
|
533
|
+
def platform_url(self):
|
|
534
|
+
return self._client_api._get_resource_url("projects/{}/models".format(self.project.id))
|
|
535
|
+
|
|
536
|
+
def open_in_web(self, model=None, model_id=None):
|
|
537
|
+
"""
|
|
538
|
+
Open the model in web platform
|
|
539
|
+
|
|
540
|
+
:param model: model entity
|
|
541
|
+
:param str model_id: model id
|
|
542
|
+
"""
|
|
543
|
+
if model is not None:
|
|
544
|
+
model.open_in_web()
|
|
545
|
+
elif model_id is not None:
|
|
546
|
+
self._client_api._open_in_web(url=self.platform_url + '/' + str(model_id) + '/main')
|
|
547
|
+
else:
|
|
548
|
+
self._client_api._open_in_web(url=self.platform_url)
|
|
549
|
+
|
|
550
|
+
def delete(self, model: entities.Model = None, model_name=None, model_id=None):
|
|
551
|
+
"""
|
|
552
|
+
Delete Model object
|
|
553
|
+
|
|
554
|
+
:param model: Model entity to delete
|
|
555
|
+
:param str model_name: delete by model name
|
|
556
|
+
:param str model_id: delete by model id
|
|
557
|
+
:return: True
|
|
558
|
+
:rtype: bool
|
|
559
|
+
"""
|
|
560
|
+
# get id and name
|
|
561
|
+
if model_id is None:
|
|
562
|
+
if model is not None:
|
|
563
|
+
model_id = model.id
|
|
564
|
+
elif model_name is not None:
|
|
565
|
+
model = self.get(model_name=model_name)
|
|
566
|
+
model_id = model.id
|
|
567
|
+
else:
|
|
568
|
+
raise exceptions.PlatformException(error='400',
|
|
569
|
+
message='Must input at least one parameter to models.delete')
|
|
570
|
+
|
|
571
|
+
# request
|
|
572
|
+
success, response = self._client_api.gen_request(
|
|
573
|
+
req_type="delete",
|
|
574
|
+
path="/ml/models/{}".format(model_id)
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# exception handling
|
|
578
|
+
if not success:
|
|
579
|
+
raise exceptions.PlatformException(response)
|
|
580
|
+
|
|
581
|
+
# return results
|
|
582
|
+
return True
|
|
583
|
+
|
|
584
|
+
def update(self,
|
|
585
|
+
model: entities.Model,
|
|
586
|
+
system_metadata: bool = False,
|
|
587
|
+
reload_services: bool = True
|
|
588
|
+
) -> entities.Model:
|
|
589
|
+
"""
|
|
590
|
+
Update Model changes to platform
|
|
591
|
+
|
|
592
|
+
:param model: Model entity
|
|
593
|
+
:param bool system_metadata: True, if you want to change metadata system
|
|
594
|
+
:param bool reload_services: True, if you want to update services
|
|
595
|
+
:return: Model entity
|
|
596
|
+
"""
|
|
597
|
+
# payload
|
|
598
|
+
payload = model.to_json()
|
|
599
|
+
|
|
600
|
+
# url
|
|
601
|
+
url_path = '/ml/models/{}'.format(model.id)
|
|
602
|
+
query_params = {}
|
|
603
|
+
if system_metadata:
|
|
604
|
+
query_params['system'] = 'true'
|
|
605
|
+
if reload_services is not None:
|
|
606
|
+
query_params['reloadServices'] = 'true' if reload_services else 'false'
|
|
607
|
+
|
|
608
|
+
if query_params:
|
|
609
|
+
url_path += '?' + urlencode(query_params)
|
|
610
|
+
|
|
611
|
+
# request
|
|
612
|
+
success, response = self._client_api.gen_request(req_type='patch',
|
|
613
|
+
path=url_path,
|
|
614
|
+
json_req=payload)
|
|
615
|
+
|
|
616
|
+
# exception handling
|
|
617
|
+
if not success:
|
|
618
|
+
raise exceptions.PlatformException(response)
|
|
619
|
+
|
|
620
|
+
# return entity
|
|
621
|
+
return entities.Model.from_json(_json=response.json(),
|
|
622
|
+
client_api=self._client_api,
|
|
623
|
+
project=self._project,
|
|
624
|
+
package=model._package)
|
|
625
|
+
|
|
626
|
+
def train(self, model_id: str, service_config=None):
|
|
627
|
+
"""
|
|
628
|
+
Train the model in the cloud. This will create a service and will run the adapter's train function as an execution
|
|
629
|
+
|
|
630
|
+
:param model_id: id of the model to train
|
|
631
|
+
:param dict service_config : Service object as dict. Contains the spec of the default service to create.
|
|
632
|
+
:return:
|
|
633
|
+
"""
|
|
634
|
+
payload = dict()
|
|
635
|
+
if service_config is not None:
|
|
636
|
+
payload['serviceConfig'] = service_config
|
|
637
|
+
success, response = self._client_api.gen_request(req_type="post",
|
|
638
|
+
path=f"/ml/models/{model_id}/train",
|
|
639
|
+
json_req=payload)
|
|
640
|
+
if not success:
|
|
641
|
+
raise exceptions.PlatformException(response)
|
|
642
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
643
|
+
client_api=self._client_api,
|
|
644
|
+
project=self._project)
|
|
645
|
+
|
|
646
|
+
def evaluate(self, model_id: str, dataset_id: str, filters: entities.Filters = None, service_config=None):
|
|
647
|
+
"""
|
|
648
|
+
Evaluate Model, provide data to evaluate the model on You can also provide specific config for the deployed service
|
|
649
|
+
|
|
650
|
+
:param str model_id: Model id to predict
|
|
651
|
+
:param dict service_config : Service object as dict. Contains the spec of the default service to create.
|
|
652
|
+
:param str dataset_id: ID of the dataset to evaluate
|
|
653
|
+
:param entities.Filters filters: dl.Filter entity to run the predictions on
|
|
654
|
+
:return:
|
|
655
|
+
"""
|
|
656
|
+
|
|
657
|
+
payload = {'input': {'datasetId': dataset_id}}
|
|
658
|
+
if service_config is not None:
|
|
659
|
+
payload['config'] = {'serviceConfig': service_config}
|
|
660
|
+
if filters is None:
|
|
661
|
+
filters = entities.Filters()
|
|
662
|
+
if filters is not None:
|
|
663
|
+
payload['input']['datasetQuery'] = filters.prepare()
|
|
664
|
+
success, response = self._client_api.gen_request(req_type="post",
|
|
665
|
+
path=f"/ml/models/{model_id}/evaluate",
|
|
666
|
+
json_req=payload)
|
|
667
|
+
if not success:
|
|
668
|
+
raise exceptions.PlatformException(response)
|
|
669
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
670
|
+
client_api=self._client_api,
|
|
671
|
+
project=self._project)
|
|
672
|
+
|
|
673
|
+
def predict(self, model, item_ids, dataset_id=None):
|
|
674
|
+
"""
|
|
675
|
+
Run model prediction with items
|
|
676
|
+
|
|
677
|
+
:param model: dl.Model entity to run the prediction.
|
|
678
|
+
:param item_ids: a list of item id to run the prediction.
|
|
679
|
+
:param dataset_id: a dataset id to run the prediction.
|
|
680
|
+
:return:
|
|
681
|
+
"""
|
|
682
|
+
if len(model.metadata['system'].get('deploy', {}).get('services', [])) == 0:
|
|
683
|
+
# no services for model
|
|
684
|
+
raise ValueError("Model doesnt have any associated services. Need to deploy before predicting")
|
|
685
|
+
if item_ids is None and dataset_id is None:
|
|
686
|
+
raise ValueError("Need to provide either item_ids or dataset_id")
|
|
687
|
+
payload_input = {}
|
|
688
|
+
if item_ids is not None:
|
|
689
|
+
payload_input['itemIds'] = item_ids
|
|
690
|
+
if dataset_id is not None:
|
|
691
|
+
payload_input['datasetId'] = dataset_id
|
|
692
|
+
payload = {'input': payload_input,
|
|
693
|
+
'config': {'serviceId': model.metadata['system']['deploy']['services'][0]}}
|
|
694
|
+
|
|
695
|
+
success, response = self._client_api.gen_request(req_type="post",
|
|
696
|
+
path=f"/ml/models/{model.id}/predict",
|
|
697
|
+
json_req=payload)
|
|
698
|
+
if not success:
|
|
699
|
+
raise exceptions.PlatformException(response)
|
|
700
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
701
|
+
client_api=self._client_api,
|
|
702
|
+
project=self._project)
|
|
703
|
+
|
|
704
|
+
def embed(self, model, item_ids=None, dataset_id=None):
|
|
705
|
+
"""
|
|
706
|
+
Run model embed with items
|
|
707
|
+
|
|
708
|
+
:param model: dl.Model entity to run the prediction.
|
|
709
|
+
:param item_ids: a list of item id to run the embed.
|
|
710
|
+
:param dataset_id: a dataset id to run the embed.
|
|
711
|
+
:return: Execution
|
|
712
|
+
:rtype: dtlpy.entities.execution.Execution
|
|
713
|
+
"""
|
|
714
|
+
if len(model.metadata['system'].get('deploy', {}).get('services', [])) == 0:
|
|
715
|
+
# no services for model
|
|
716
|
+
raise ValueError("Model doesnt have any associated services. Need to deploy before predicting")
|
|
717
|
+
if item_ids is None and dataset_id is None:
|
|
718
|
+
raise ValueError("Need to provide either item_ids or dataset_id")
|
|
719
|
+
payload_input = {}
|
|
720
|
+
if item_ids is not None:
|
|
721
|
+
payload_input['itemIds'] = item_ids
|
|
722
|
+
if dataset_id is not None:
|
|
723
|
+
payload_input['datasetId'] = dataset_id
|
|
724
|
+
payload = {'input': payload_input,
|
|
725
|
+
'config': {'serviceId': model.metadata['system']['deploy']['services'][0]}}
|
|
726
|
+
|
|
727
|
+
success, response = self._client_api.gen_request(req_type="post",
|
|
728
|
+
path=f"/ml/models/{model.id}/embed",
|
|
729
|
+
json_req=payload)
|
|
730
|
+
if not success:
|
|
731
|
+
raise exceptions.PlatformException(response)
|
|
732
|
+
return entities.Execution.from_json(_json=response.json(),
|
|
733
|
+
client_api=self._client_api,
|
|
734
|
+
project=self._project)
|
|
735
|
+
|
|
736
|
+
def embed_datasets(self, model, dataset_ids, attach_trigger=False):
|
|
737
|
+
"""
|
|
738
|
+
Run model embed with datasets
|
|
739
|
+
|
|
740
|
+
:param model: dl.Model entity to run the prediction.
|
|
741
|
+
:param dataset_ids: a list of dataset id to run the embed.
|
|
742
|
+
:param attach_trigger: bool, if True will activate the trigger
|
|
743
|
+
:return:
|
|
744
|
+
"""
|
|
745
|
+
if len(model.metadata['system'].get('deploy', {}).get('services', [])) == 0:
|
|
746
|
+
# no services for model
|
|
747
|
+
raise ValueError("Model doesnt have any associated services. Need to deploy before predicting")
|
|
748
|
+
if dataset_ids is None:
|
|
749
|
+
raise ValueError("Need to provide either dataset_id")
|
|
750
|
+
payload = {'datasetIds': dataset_ids,
|
|
751
|
+
'config': {'serviceId': model.metadata['system']['deploy']['services'][0]},
|
|
752
|
+
'attachTrigger': attach_trigger
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
success, response = self._client_api.gen_request(req_type="post",
|
|
756
|
+
path=f"/ml/models/{model.id}/embed/datasets",
|
|
757
|
+
json_req=payload)
|
|
758
|
+
if not success:
|
|
759
|
+
raise exceptions.PlatformException(response)
|
|
760
|
+
command = entities.Command.from_json(_json=response.json(),
|
|
761
|
+
client_api=self._client_api)
|
|
762
|
+
command = command.wait()
|
|
763
|
+
return command
|
|
764
|
+
|
|
765
|
+
def deploy(self, model_id: str, service_config=None) -> entities.Service:
|
|
766
|
+
"""
|
|
767
|
+
Deploy a trained model. This will create a service that will execute predictions
|
|
768
|
+
|
|
769
|
+
:param model_id: id of the model to deploy
|
|
770
|
+
:param dict service_config : Service object as dict. Contains the spec of the default service to create.
|
|
771
|
+
:return: dl.Service: the deployed service
|
|
772
|
+
"""
|
|
773
|
+
payload = dict()
|
|
774
|
+
if service_config is not None:
|
|
775
|
+
payload['serviceConfig'] = service_config if not service_config.get("serviceConfig") else service_config.get("serviceConfig")
|
|
776
|
+
success, response = self._client_api.gen_request(req_type="post",
|
|
777
|
+
path=f"/ml/models/{model_id}/deploy",
|
|
778
|
+
json_req=payload)
|
|
779
|
+
if not success:
|
|
780
|
+
raise exceptions.PlatformException(response)
|
|
781
|
+
|
|
782
|
+
return entities.Service.from_json(_json=response.json(),
|
|
783
|
+
client_api=self._client_api,
|
|
784
|
+
project=self._project,
|
|
785
|
+
package=self._package)
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
class Metrics:
|
|
789
|
+
def __init__(self, client_api, model=None, model_id=None):
|
|
790
|
+
self._client_api = client_api
|
|
791
|
+
self._model_id = model_id
|
|
792
|
+
self._model = model
|
|
793
|
+
|
|
794
|
+
@property
|
|
795
|
+
def model(self):
|
|
796
|
+
return self._model
|
|
797
|
+
|
|
798
|
+
def create(self, samples, dataset_id) -> bool:
|
|
799
|
+
"""
|
|
800
|
+
Add Samples for model analytics and metrics
|
|
801
|
+
|
|
802
|
+
:param samples: list of dl.PlotSample - must contain: model_id, figure, legend, x, y
|
|
803
|
+
:param model_id: model id to save samples on
|
|
804
|
+
:param dataset_id:
|
|
805
|
+
:return: bool: True if success
|
|
806
|
+
"""
|
|
807
|
+
if not isinstance(samples, list):
|
|
808
|
+
samples = [samples]
|
|
809
|
+
|
|
810
|
+
payload = list()
|
|
811
|
+
for sample in samples:
|
|
812
|
+
_json = sample.to_json()
|
|
813
|
+
_json['modelId'] = self.model.id
|
|
814
|
+
_json['datasetId'] = dataset_id
|
|
815
|
+
payload.append(_json)
|
|
816
|
+
# request
|
|
817
|
+
success, response = self._client_api.gen_request(req_type='post',
|
|
818
|
+
path='/ml/metrics/publish',
|
|
819
|
+
json_req=payload)
|
|
820
|
+
|
|
821
|
+
# exception handling
|
|
822
|
+
if not success:
|
|
823
|
+
raise exceptions.PlatformException(response)
|
|
824
|
+
|
|
825
|
+
# return entity
|
|
826
|
+
return True
|
|
827
|
+
|
|
828
|
+
def _list(self, filters: entities.Filters):
|
|
829
|
+
# request
|
|
830
|
+
success, response = self._client_api.gen_request(req_type='POST',
|
|
831
|
+
path='/ml/metrics/query',
|
|
832
|
+
json_req=filters.prepare())
|
|
833
|
+
if not success:
|
|
834
|
+
raise exceptions.PlatformException(response)
|
|
835
|
+
return response.json()
|
|
836
|
+
|
|
837
|
+
def _build_entities_from_response(self, response_items) -> miscellaneous.List[entities.Model]:
|
|
838
|
+
jobs = [None for _ in range(len(response_items))]
|
|
839
|
+
pool = self._client_api.thread_pools(pool_name='entity.create')
|
|
840
|
+
|
|
841
|
+
# return triggers list
|
|
842
|
+
for i_service, sample in enumerate(response_items):
|
|
843
|
+
jobs[i_service] = pool.submit(entities.PlotSample,
|
|
844
|
+
**{'x': sample.get('data', dict()).get('x', None),
|
|
845
|
+
'y': sample.get('data', dict()).get('y', None),
|
|
846
|
+
'legend': sample.get('legend', ''),
|
|
847
|
+
'figure': sample.get('figure', '')})
|
|
848
|
+
|
|
849
|
+
# get all results
|
|
850
|
+
results = [j.result() for j in jobs]
|
|
851
|
+
# return good jobs
|
|
852
|
+
return miscellaneous.List(results)
|
|
853
|
+
|
|
854
|
+
def list(self, filters=None) -> entities.PagedEntities:
|
|
855
|
+
"""
|
|
856
|
+
List Samples for model analytics and metrics
|
|
857
|
+
|
|
858
|
+
:param filters: dl.Filter query entity
|
|
859
|
+
"""
|
|
860
|
+
if filters is None:
|
|
861
|
+
filters = entities.Filters(resource=entities.FiltersResource.METRICS)
|
|
862
|
+
if not isinstance(filters, entities.Filters):
|
|
863
|
+
raise exceptions.PlatformException(error='400',
|
|
864
|
+
message='Unknown filters type: {!r}'.format(type(filters)))
|
|
865
|
+
if filters.resource != entities.FiltersResource.METRICS:
|
|
866
|
+
raise exceptions.PlatformException(
|
|
867
|
+
error='400',
|
|
868
|
+
message='Filters resource must to be FiltersResource.METRICS. Got: {!r}'.format(filters.resource))
|
|
869
|
+
if self._model is not None:
|
|
870
|
+
filters.add(field='modelId', values=self._model.id)
|
|
871
|
+
paged = entities.PagedEntities(items_repository=self,
|
|
872
|
+
filters=filters,
|
|
873
|
+
page_offset=filters.page,
|
|
874
|
+
page_size=filters.page_size,
|
|
875
|
+
client_api=self._client_api)
|
|
876
|
+
paged.get_page()
|
|
877
|
+
return paged
|