learning-loop-node 0.9.3__py3-none-any.whl → 0.10.1__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.

Potentially problematic release.


This version of learning-loop-node might be problematic. Click here for more details.

Files changed (54) hide show
  1. learning_loop_node/__init__.py +2 -3
  2. learning_loop_node/annotation/annotator_logic.py +2 -2
  3. learning_loop_node/annotation/annotator_node.py +16 -15
  4. learning_loop_node/data_classes/__init__.py +17 -10
  5. learning_loop_node/data_classes/detections.py +7 -2
  6. learning_loop_node/data_classes/general.py +4 -5
  7. learning_loop_node/data_classes/training.py +49 -21
  8. learning_loop_node/data_exchanger.py +85 -139
  9. learning_loop_node/detector/__init__.py +0 -1
  10. learning_loop_node/detector/detector_node.py +10 -13
  11. learning_loop_node/detector/inbox_filter/cam_observation_history.py +4 -7
  12. learning_loop_node/detector/outbox.py +0 -1
  13. learning_loop_node/detector/rest/about.py +1 -0
  14. learning_loop_node/detector/tests/conftest.py +0 -1
  15. learning_loop_node/detector/tests/test_client_communication.py +5 -3
  16. learning_loop_node/detector/tests/test_outbox.py +2 -0
  17. learning_loop_node/detector/tests/testing_detector.py +1 -8
  18. learning_loop_node/globals.py +2 -2
  19. learning_loop_node/helpers/gdrive_downloader.py +1 -1
  20. learning_loop_node/helpers/misc.py +124 -17
  21. learning_loop_node/loop_communication.py +57 -25
  22. learning_loop_node/node.py +62 -135
  23. learning_loop_node/tests/test_downloader.py +8 -7
  24. learning_loop_node/tests/test_executor.py +14 -11
  25. learning_loop_node/tests/test_helper.py +3 -5
  26. learning_loop_node/trainer/downloader.py +1 -1
  27. learning_loop_node/trainer/executor.py +87 -83
  28. learning_loop_node/trainer/io_helpers.py +68 -9
  29. learning_loop_node/trainer/rest/backdoor_controls.py +10 -5
  30. learning_loop_node/trainer/rest/controls.py +3 -1
  31. learning_loop_node/trainer/tests/conftest.py +19 -28
  32. learning_loop_node/trainer/tests/states/test_state_cleanup.py +5 -3
  33. learning_loop_node/trainer/tests/states/test_state_detecting.py +23 -20
  34. learning_loop_node/trainer/tests/states/test_state_download_train_model.py +18 -12
  35. learning_loop_node/trainer/tests/states/test_state_prepare.py +13 -12
  36. learning_loop_node/trainer/tests/states/test_state_sync_confusion_matrix.py +21 -18
  37. learning_loop_node/trainer/tests/states/test_state_train.py +27 -28
  38. learning_loop_node/trainer/tests/states/test_state_upload_detections.py +34 -32
  39. learning_loop_node/trainer/tests/states/test_state_upload_model.py +22 -20
  40. learning_loop_node/trainer/tests/test_errors.py +20 -12
  41. learning_loop_node/trainer/tests/test_trainer_states.py +4 -5
  42. learning_loop_node/trainer/tests/testing_trainer_logic.py +25 -30
  43. learning_loop_node/trainer/trainer_logic.py +80 -590
  44. learning_loop_node/trainer/trainer_logic_generic.py +495 -0
  45. learning_loop_node/trainer/trainer_node.py +27 -106
  46. {learning_loop_node-0.9.3.dist-info → learning_loop_node-0.10.1.dist-info}/METADATA +1 -1
  47. learning_loop_node-0.10.1.dist-info/RECORD +85 -0
  48. learning_loop_node/converter/converter_logic.py +0 -68
  49. learning_loop_node/converter/converter_node.py +0 -125
  50. learning_loop_node/converter/tests/test_converter.py +0 -55
  51. learning_loop_node/trainer/training_syncronizer.py +0 -52
  52. learning_loop_node-0.9.3.dist-info/RECORD +0 -88
  53. /learning_loop_node/{converter/__init__.py → py.typed} +0 -0
  54. {learning_loop_node-0.9.3.dist-info → learning_loop_node-0.10.1.dist-info}/WHEEL +0 -0
@@ -1,74 +1,59 @@
1
1
  import asyncio
2
- import time
3
2
  from dataclasses import asdict
4
- from typing import Dict, Optional, Union
3
+ from typing import Dict, Optional
5
4
 
6
- from dacite import from_dict
7
5
  from fastapi.encoders import jsonable_encoder
8
6
  from socketio import AsyncClient
9
7
 
10
- from ..data_classes import Context, NodeState, TrainingState, TrainingStatus
11
- from ..data_classes.socket_response import SocketResponse
8
+ from ..data_classes import TrainingStatus
12
9
  from ..node import Node
13
10
  from .io_helpers import LastTrainingIO
14
11
  from .rest import backdoor_controls, controls
15
- from .trainer_logic import TrainerLogic
12
+ from .trainer_logic_generic import TrainerLogicGeneric
16
13
 
17
14
 
18
15
  class TrainerNode(Node):
19
16
 
20
- def __init__(self, name: str, trainer_logic: TrainerLogic, uuid: Optional[str] = None, use_backdoor_controls: bool = False):
21
- super().__init__(name, uuid)
22
- trainer_logic._node = self # pylint: disable=protected-access
17
+ def __init__(self, name: str, trainer_logic: TrainerLogicGeneric, uuid: Optional[str] = None, use_backdoor_controls: bool = False):
18
+ super().__init__(name, uuid, 'trainer')
19
+ trainer_logic._node = self
23
20
  self.trainer_logic = trainer_logic
24
21
  self.last_training_io = LastTrainingIO(self.uuid)
22
+ self.trainer_logic._last_training_io = self.last_training_io
23
+
25
24
  self.include_router(controls.router, tags=["controls"])
26
25
  if use_backdoor_controls:
27
26
  self.include_router(backdoor_controls.router, tags=["controls"])
28
27
 
29
- # --------------------------------------------------- STATUS ---------------------------------------------------
30
-
31
- @property
32
- def progress(self) -> Union[float, None]:
33
- return self.trainer_logic.general_progress if (self.trainer_logic is not None and
34
- hasattr(self.trainer_logic, 'general_progress')) else None
35
-
36
- @property
37
- def training_uptime(self) -> Union[float, None]:
38
- return time.time() - self.trainer_logic.start_time if self.trainer_logic.start_time else None
39
-
40
- # ----------------------------------- LIVECYCLE: ABSTRACT NODE METHODS --------------------------
28
+ # ----------------------------------- NODE LIVECYCLE METHODS --------------------------
41
29
 
42
30
  async def on_startup(self):
43
31
  pass
44
32
 
45
33
  async def on_shutdown(self):
46
34
  self.log.info('shutdown detected, stopping training')
47
- await self.trainer_logic.shutdown()
35
+ await self.trainer_logic.on_shutdown()
48
36
 
49
37
  async def on_repeat(self):
50
38
  try:
51
- if await self.continue_run_if_incomplete():
39
+ if await self.trainer_logic.try_continue_run_if_incomplete():
52
40
  return # NOTE: we prevent sending idle status after starting a continuation
53
41
  await self.send_status()
54
42
  except Exception as e:
55
43
  if isinstance(e, asyncio.TimeoutError):
56
44
  self.log.warning('timeout when sending status to learning loop, reconnecting sio_client')
57
- await self.sio_client.disconnect()
58
- # NOTE: reconnect happens in node._on_repeat
45
+ await self.sio_client.disconnect() # NOTE: reconnect happens in node._on_repeat
59
46
  else:
60
47
  self.log.exception(f'could not send status state: {e}')
61
48
 
62
- # ---------------------------------------------- NODE ABSTRACT METHODS ---------------------------------------------------
49
+ # ---------------------------------------------- NODE METHODS ---------------------------------------------------
63
50
 
64
51
  def register_sio_events(self, sio_client: AsyncClient):
65
52
 
66
53
  @sio_client.event
67
54
  async def begin_training(organization: str, project: str, details: Dict):
68
- assert self._sio_client is not None
69
55
  self.log.info('received begin_training from server')
70
- self.trainer_logic.init_new_training(Context(organization=organization, project=project), details)
71
- asyncio.get_event_loop().create_task(self.trainer_logic.run())
56
+ await self.trainer_logic.begin_training(organization, project, details)
72
57
  return True
73
58
 
74
59
  @sio_client.event
@@ -81,93 +66,29 @@ class TrainerNode(Node):
81
66
  return True
82
67
 
83
68
  async def send_status(self):
84
- if self._sio_client is None or not self._sio_client.connected:
69
+ if not self.sio_client.connected:
85
70
  self.log.warning('cannot send status - not connected to the Learning Loop')
86
71
  return
87
72
 
88
- if not self.trainer_logic.is_initialized:
89
- state_for_learning_loop = str(NodeState.Idle.value)
90
- else:
91
- assert self.trainer_logic.training.training_state is not None
92
- state_for_learning_loop = TrainerNode.state_for_learning_loop(
93
- self.trainer_logic.training.training_state)
94
-
95
73
  status = TrainingStatus(id=self.uuid,
96
74
  name=self.name,
97
- state=state_for_learning_loop,
75
+ state=self.trainer_logic.state,
98
76
  errors={},
99
- uptime=self.training_uptime,
100
- progress=self.progress)
77
+ uptime=self.trainer_logic.training_uptime,
78
+ progress=self.trainer_logic.general_progress)
101
79
 
102
80
  status.pretrained_models = self.trainer_logic.provided_pretrained_models
103
81
  status.architecture = self.trainer_logic.model_architecture
104
82
 
105
- if self.trainer_logic.is_initialized and self.trainer_logic.training.data:
106
- status.train_image_count = self.trainer_logic.training.data.train_image_count()
107
- status.test_image_count = self.trainer_logic.training.data.test_image_count()
108
- status.skipped_image_count = self.trainer_logic.training.data.skipped_image_count
109
- status.hyperparameters = self.trainer_logic.hyperparameters
83
+ if data := self.trainer_logic.training_data:
84
+ status.train_image_count = data.train_image_count()
85
+ status.test_image_count = data.test_image_count()
86
+ status.skipped_image_count = data.skipped_image_count
87
+ status.hyperparameters = self.trainer_logic.hyperparameters_for_state_sync
110
88
  status.errors = self.trainer_logic.errors.errors
111
- status.context = self.trainer_logic.training.context
89
+ status.context = self.trainer_logic.training_context
112
90
 
113
91
  self.log.info(f'sending status: {status.short_str()}')
114
- result = await self._sio_client.call('update_trainer', jsonable_encoder(asdict(status)), timeout=30)
115
- assert isinstance(result, Dict)
116
- response = from_dict(data_class=SocketResponse, data=result)
117
-
118
- if not response.success:
119
- self.log.error(f'Error when sending status update: Response from loop was:\n {asdict(response)}')
120
-
121
- async def continue_run_if_incomplete(self) -> bool:
122
- if not self.trainer_logic.is_initialized and self.last_training_io.exists():
123
- self.log.info('found incomplete training, continuing now.')
124
- self.trainer_logic.init_from_last_training()
125
- asyncio.get_event_loop().create_task(self.trainer_logic.run())
126
- return True
127
- return False
128
-
129
- async def get_state(self):
130
- if self.trainer_logic._executor is not None and self.trainer_logic._executor.is_process_running(): # pylint: disable=protected-access
131
- return NodeState.Running
132
- return NodeState.Idle
133
-
134
- def get_node_type(self):
135
- return 'trainer'
136
-
137
- # --------------------------------------------------- HELPER ---------------------------------------------------
138
-
139
- @staticmethod
140
- def state_for_learning_loop(trainer_state: Union[TrainingState, str]) -> str:
141
- if trainer_state == TrainingState.Initialized:
142
- return 'Training is initialized'
143
- if trainer_state == TrainingState.DataDownloading:
144
- return 'Downloading data'
145
- if trainer_state == TrainingState.DataDownloaded:
146
- return 'Data downloaded'
147
- if trainer_state == TrainingState.TrainModelDownloading:
148
- return 'Downloading model'
149
- if trainer_state == TrainingState.TrainModelDownloaded:
150
- return 'Model downloaded'
151
- if trainer_state == TrainingState.TrainingRunning:
152
- return NodeState.Running
153
- if trainer_state == TrainingState.TrainingFinished:
154
- return 'Training finished'
155
- if trainer_state == TrainingState.Detecting:
156
- return NodeState.Detecting
157
- if trainer_state == TrainingState.ConfusionMatrixSyncing:
158
- return 'Syncing confusion matrix'
159
- if trainer_state == TrainingState.ConfusionMatrixSynced:
160
- return 'Confusion matrix synced'
161
- if trainer_state == TrainingState.TrainModelUploading:
162
- return 'Uploading trained model'
163
- if trainer_state == TrainingState.TrainModelUploaded:
164
- return 'Trained model uploaded'
165
- if trainer_state == TrainingState.Detecting:
166
- return 'calculating detections'
167
- if trainer_state == TrainingState.Detected:
168
- return 'Detections calculated'
169
- if trainer_state == TrainingState.DetectionUploading:
170
- return 'Uploading detections'
171
- if trainer_state == TrainingState.ReadyForCleanup:
172
- return 'Cleaning training'
173
- return 'unknown state'
92
+ result = await self.sio_client.call('update_trainer', jsonable_encoder(asdict(status)), timeout=30)
93
+ if isinstance(result, Dict) and not result['success']:
94
+ self.log.error(f'Error when sending status update: Response from loop was:\n {result}')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: learning-loop-node
3
- Version: 0.9.3
3
+ Version: 0.10.1
4
4
  Summary: Python Library for Nodes which connect to the Zauberzeug Learning Loop
5
5
  Home-page: https://github.com/zauberzeug/learning_loop_node
6
6
  License: MIT
@@ -0,0 +1,85 @@
1
+ learning_loop_node/__init__.py,sha256=onN5s8-x_xBsCM6NLmJO0Ym1sJHeCFaGw8qb0oQZmz8,364
2
+ learning_loop_node/annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ learning_loop_node/annotation/annotator_logic.py,sha256=BTaopkJZkIf1CI5lfsVKsxbxoUIbDJrevavuQUT5e_c,1000
4
+ learning_loop_node/annotation/annotator_node.py,sha256=wk11CQtM3A0Dr7efCn_Mw2X7ql5xn2sgEJzrIeSBC6Q,4043
5
+ learning_loop_node/annotation/tests/test_annotator_node.py,sha256=yCox-ohCXOpCbcTit7PwtE4iLHud8qiZ6UOqqycwPxg,2065
6
+ learning_loop_node/conftest.py,sha256=Q_8Dbl3RwI8X_Y5Zvzstr6NJEqY3KldZXVx618OLdb4,3492
7
+ learning_loop_node/data_classes/__init__.py,sha256=wCX88lDgbb8V-gtVCVe9i-NvvZuMe5FX7eD_UJgYYXw,1305
8
+ learning_loop_node/data_classes/annotations.py,sha256=iInU0Nuy_oYT_sj4k_n-W0UShCBI2cHQYrt8imymbtM,1211
9
+ learning_loop_node/data_classes/detections.py,sha256=1BcU5PNzIbryWcj2xJ6ysLBTBwGOdv9SxSJiUG8WEmw,4349
10
+ learning_loop_node/data_classes/general.py,sha256=44GJrJvGfPwDUmRsS7If9uSlE6KPP50LGUX91VzesLw,4664
11
+ learning_loop_node/data_classes/socket_response.py,sha256=tIdt-oYf6ULoJIDYQCecNM9OtWR6_wJ9tL0Ksu83Vko,655
12
+ learning_loop_node/data_classes/training.py,sha256=hnMHZMk-WNRERyo7U97qL09v1tIdhnzPfTH-JgifLwU,6164
13
+ learning_loop_node/data_exchanger.py,sha256=hxF0zANA35f5EV8tkQ4yjelrKuvafMaKUya0CCjVrK0,8221
14
+ learning_loop_node/detector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ learning_loop_node/detector/detector_logic.py,sha256=se0jRFbV7BfTvCuCI3gcUllSYIZ5dxTkvdISe6pPTRg,1660
16
+ learning_loop_node/detector/detector_node.py,sha256=Qmj87e5-mmS5SnT_VlfZ1I6vhS6XWzt60H9w82LiZbk,16649
17
+ learning_loop_node/detector/inbox_filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ learning_loop_node/detector/inbox_filter/cam_observation_history.py,sha256=TD346I9ymtIP0_CJXCIKMRuiXbfVVanXNu_iHAwDd7Q,3318
19
+ learning_loop_node/detector/inbox_filter/relevance_filter.py,sha256=s2FuwZ-tD_5obkSutstjc8pE_hLGbrv9WjrEO9t8rJ8,1011
20
+ learning_loop_node/detector/inbox_filter/tests/test_observation.py,sha256=ORN08yjprqmgmtU25RsVysniyrWX-qGvqFN8ZkkYxow,1385
21
+ learning_loop_node/detector/inbox_filter/tests/test_relevance_group.py,sha256=RUgsk1CnKSOCRZBzNjE7AZTqk06-yelgUqvHFRLH7_I,7865
22
+ learning_loop_node/detector/inbox_filter/tests/test_unexpected_observations_count.py,sha256=y_dFUV21h6uZc90Q43s0u4oivJfuCNWlk5iXAWiXGgc,1804
23
+ learning_loop_node/detector/outbox.py,sha256=kxOzIhffTbrBCvZqGQsrwDS68zWJe00mN12O_qNwwVI,4176
24
+ learning_loop_node/detector/rest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ learning_loop_node/detector/rest/about.py,sha256=-PNqlQI_tzRvoSI_UR9rX8-5GeiENNpRDQ4Ylw3wYVs,607
26
+ learning_loop_node/detector/rest/backdoor_controls.py,sha256=38axRG66Z3_Q6bYKa7Hw-ldChEAu-dJcBM_Sl_17Ozo,1725
27
+ learning_loop_node/detector/rest/detect.py,sha256=8Rl1swANKgHc42P1z75t_PErQxpCKKPdAsKqDIZgdNU,1873
28
+ learning_loop_node/detector/rest/operation_mode.py,sha256=eIo6_56qyZECftf4AEN8wJMABIojC0TRazvWeg0Uj_s,1664
29
+ learning_loop_node/detector/rest/upload.py,sha256=MifNhban7GeaCjwa39lDhTQWyRuVyvyGFGscoszplH0,435
30
+ learning_loop_node/detector/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ learning_loop_node/detector/tests/conftest.py,sha256=v08N5_jvQyeqQkOzkTaanCwwVd75Rf-tc1M2-fgiv54,3254
32
+ learning_loop_node/detector/tests/test.jpg,sha256=msA-vHPmvPiro_D102Qmn1fn4vNfooqYYEXPxZUmYpk,161390
33
+ learning_loop_node/detector/tests/test_client_communication.py,sha256=QjoES3qMqcsfZgNHI825_bfvjdxxX8NNncCRKIeV9Wo,4603
34
+ learning_loop_node/detector/tests/test_outbox.py,sha256=bXvbxBVSF2O0M3V9gSDEqjMQ1g12_hwckq7rziG3i1c,2051
35
+ learning_loop_node/detector/tests/test_relevance_filter.py,sha256=FzeOU6k17VIQvAHR8fjHbcPeAE7D7C-2Yxol0lDrMEM,1981
36
+ learning_loop_node/detector/tests/testing_detector.py,sha256=2DSwIYJDOG4ixOGU8OxjsZQgaOdVU7_d3ASKsSkf8qc,564
37
+ learning_loop_node/examples/novelty_score_updater.py,sha256=1DRgM9lxjFV-q2JvGDDsNLz_ic_rhEZ9wc6ZdjcxwPE,2038
38
+ learning_loop_node/globals.py,sha256=tgw_8RYOipPV9aYlyUhYtXfUxvJKRvfUk6u-qVAtZmY,174
39
+ learning_loop_node/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ learning_loop_node/helpers/environment_reader.py,sha256=1GNTXRBcv6QKolbJ50TpsbWoVyw1oO0kvKrt9yjJ0nA,1543
41
+ learning_loop_node/helpers/gdrive_downloader.py,sha256=zeYJciTAJVRpu_eFjwgYLCpIa6hU1d71anqEBb564Rk,1145
42
+ learning_loop_node/helpers/log_conf.py,sha256=3yd-jaMOeD5cRIgA5w_BH2L5odf8c4-ZjD89Bdqwe44,824
43
+ learning_loop_node/helpers/misc.py,sha256=j4is8Rv0ttnCqF-R-wP3xwEi67OI6IBJav5Woo5lyDk,7701
44
+ learning_loop_node/loop_communication.py,sha256=w_CF1Ynl6GdpL1cdZ6wbb-X0-5gmGlPLjSfDQDRpwLE,6157
45
+ learning_loop_node/node.py,sha256=jpTP3gnGxFDF7tq9H7MVfVIIHm8e7bYA5euttHNJAEg,7252
46
+ learning_loop_node/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ learning_loop_node/pytest.ini,sha256=8QdjmawLy1zAzXrJ88or1kpFDhJw0W5UOnDfGGs_igU,262
48
+ learning_loop_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ learning_loop_node/tests/conftest.py,sha256=vFj8jjwSBzt6IAIxEo7y0rN0I94SPkK2pZsMvrvET5Q,920
50
+ learning_loop_node/tests/test_data/file_1.txt,sha256=Lis06nfvbFPVCBZyEgQlfI_Nle2YDq1GQBlYvEfFtxw,19
51
+ learning_loop_node/tests/test_data/file_2.txt,sha256=Xp8EETGhZBdVAgb4URowSSpOytwwwJdV0Renkdur7R8,19
52
+ learning_loop_node/tests/test_data/model.json,sha256=_xNDucGOWila8gWnu8yFfrqmQ45Xq-_39eLKzjRtvpE,516
53
+ learning_loop_node/tests/test_data_classes.py,sha256=m8LEk1quGErxuPzNdW_ExqQjkwE4u7ribwnTdyeiHR8,788
54
+ learning_loop_node/tests/test_downloader.py,sha256=RkXNVQJkfPddGzxfJgSF7kc-V6Qp1OOvYYuhQ9g_ls4,2806
55
+ learning_loop_node/tests/test_executor.py,sha256=-QLylzigUEoUzeQssPR3ECFkD2tWH-fsA_lEGXy993c,2119
56
+ learning_loop_node/tests/test_helper.py,sha256=AjOrTu3dHIlJLYI0mxcNx8MCmFF6IjLhHtPzuai4O9E,2334
57
+ learning_loop_node/tests/test_learning_loop_node.py,sha256=4qWi1ovBzebUAbvw8ecSa-TBGKYuJvlKe2AMnMZ-Qs8,701
58
+ learning_loop_node/trainer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ learning_loop_node/trainer/downloader.py,sha256=qzx7zzObcFEvRVQFe8gi8KJNIapASi1_XssbspXD1Rw,1469
60
+ learning_loop_node/trainer/executor.py,sha256=j-1pkc_5b1Y0Bh8KiQthAK7SoOfJelr5Qi7L0p1sTxE,3933
61
+ learning_loop_node/trainer/io_helpers.py,sha256=fyjImT1NY10gLV0KM4Ns7sRrtKFswVJU_pV9-c3IVEY,7152
62
+ learning_loop_node/trainer/rest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
+ learning_loop_node/trainer/rest/backdoor_controls.py,sha256=YQcG0KwxzKDNYeMtHrSwr26q__N7ty0o6Kar6CLWAd0,5869
64
+ learning_loop_node/trainer/rest/controls.py,sha256=XF37i2edeMHKdSXyJc4ZqaTZ38u6d3u3Sb3C-Mwyfko,934
65
+ learning_loop_node/trainer/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ learning_loop_node/trainer/tests/conftest.py,sha256=UIVmnZZVe1DigOxeraiLh89cyw1QNQFvNJWmPUY1uFI,2455
67
+ learning_loop_node/trainer/tests/state_helper.py,sha256=igoGqTBqcqqFcDng2i7ctC67bYR1hLPDl4G_mNRG6r8,934
68
+ learning_loop_node/trainer/tests/states/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
+ learning_loop_node/trainer/tests/states/test_state_cleanup.py,sha256=tiL31hSjg1Bl2obzg2ufAVpil5qW0YkpDCpSHPqXQrk,1312
70
+ learning_loop_node/trainer/tests/states/test_state_detecting.py,sha256=NU065RhpUwhExldGp-79YD_P9bG4O9DNcLjBfW0qmik,3811
71
+ learning_loop_node/trainer/tests/states/test_state_download_train_model.py,sha256=rNRXIyqyHzHz4fXY1Lsf7WKg8FFVFYfFPIevMCBBcCY,2940
72
+ learning_loop_node/trainer/tests/states/test_state_prepare.py,sha256=fx9_bgPTaR5ANVB8n_hW8dXcaJIh_iKEnInmhzamZ9E,2432
73
+ learning_loop_node/trainer/tests/states/test_state_sync_confusion_matrix.py,sha256=zfNbHB3GFSJXXoEkW-8PYtmX62md3feWp4oisyzs8A4,4773
74
+ learning_loop_node/trainer/tests/states/test_state_train.py,sha256=j1vedjH2EwLTgHhon6eR9ttp-Sw9ozR9-9QgAKlFO-M,3248
75
+ learning_loop_node/trainer/tests/states/test_state_upload_detections.py,sha256=38MumBw_N6Pt5cXI8x-xZjczmWUc7BIqyw4TSfim9h4,7751
76
+ learning_loop_node/trainer/tests/states/test_state_upload_model.py,sha256=gAXJJcoxj2EVdFJcPAeqxGAjeL1a4ofLtytnM4joi-Q,3808
77
+ learning_loop_node/trainer/tests/test_errors.py,sha256=8H-kjs9kEBoHWcQVJIZvW5zcwCs1VQI5Tf5I0VSbCUA,2245
78
+ learning_loop_node/trainer/tests/test_trainer_states.py,sha256=OBrClH6srAM2hqqel2xTtfHCeTKYZlG_S4KO2G2GrS4,1147
79
+ learning_loop_node/trainer/tests/testing_trainer_logic.py,sha256=7sQ6okiOhM4IhvRRo4XvLPjxnBrqFu9SPbuDX2LLwRs,3925
80
+ learning_loop_node/trainer/trainer_logic.py,sha256=PJxiO1chPdvpq8UTtzv_nVam9CouCswX9b1FnRwT2Tw,8411
81
+ learning_loop_node/trainer/trainer_logic_generic.py,sha256=KFDuxgzrGITHQaJoGvhjHxWzhbb4Q7HBxSpks4CeGBg,24801
82
+ learning_loop_node/trainer/trainer_node.py,sha256=bcyOMeLXrLuLgsPqS8lwEOSZ6vCjGLgT0pLXgaylI1Q,4155
83
+ learning_loop_node-0.10.1.dist-info/METADATA,sha256=j9ZrDeZGnT8IuYUAmt53KSvVtjEhsn3kxBAlPkbX2cE,9158
84
+ learning_loop_node-0.10.1.dist-info/WHEEL,sha256=WGfLGfLX43Ei_YORXSnT54hxFygu34kMpcQdmgmEwCQ,88
85
+ learning_loop_node-0.10.1.dist-info/RECORD,,
@@ -1,68 +0,0 @@
1
- import json
2
- import os
3
- import shutil
4
- from abc import abstractmethod
5
- from typing import List, Optional
6
-
7
- from ..data_classes import ModelInformation
8
- from ..node import Node
9
-
10
-
11
- class ConverterLogic():
12
-
13
- def __init__(
14
- self, source_format: str, target_format: str):
15
- self.source_format = source_format
16
- self.target_format = target_format
17
- self._node: Optional[Node] = None
18
- self.model_folder: Optional[str] = None
19
-
20
- def init(self, node: Node) -> None:
21
- self._node = node
22
-
23
- @property
24
- def node(self) -> Node:
25
- if self._node is None:
26
- raise Exception('ConverterLogic not initialized')
27
- return self._node
28
-
29
- async def convert(self, model_information: ModelInformation) -> None:
30
- project_folder = Node.create_project_folder(model_information.context)
31
-
32
- self.model_folder = ConverterLogic.create_model_folder(project_folder, model_information.id)
33
- await self.node.data_exchanger.download_model(self.model_folder,
34
- model_information.context,
35
- model_information.id,
36
- self.source_format)
37
-
38
- with open(f'{self.model_folder}/model.json', 'r') as f:
39
- content = json.load(f)
40
- if 'resolution' in content:
41
- model_information.resolution = content['resolution']
42
-
43
- await self._convert(model_information)
44
-
45
- async def upload_model(self, context, model_id: str) -> None:
46
- files = self.get_converted_files(model_id)
47
- await self.node.data_exchanger.upload_model(context, files, model_id, self.target_format)
48
-
49
- @abstractmethod
50
- async def _convert(self, model_information: ModelInformation) -> None:
51
- """Converts the model in self.model_folder to the target format."""
52
-
53
- @abstractmethod
54
- def get_converted_files(self, model_id) -> List[str]:
55
- """Returns a list of files that should be uploaded to the server."""
56
-
57
- @staticmethod
58
- def create_convert_folder(project_folder: str) -> str:
59
- image_folder = f'{project_folder}/images'
60
- os.makedirs(image_folder, exist_ok=True)
61
- return image_folder
62
-
63
- @staticmethod
64
- def create_model_folder(project_folder: str, model_id: str) -> str:
65
- model_folder = f'{project_folder}/{model_id}'
66
- shutil.rmtree(model_folder, ignore_errors=True) # cleanup
67
- os.makedirs(model_folder, exist_ok=True)
68
- return model_folder
@@ -1,125 +0,0 @@
1
- import logging
2
- from dataclasses import asdict
3
- from http import HTTPStatus
4
- from typing import List, Optional
5
-
6
- from dacite import from_dict
7
- from fastapi.encoders import jsonable_encoder
8
- from fastapi_utils.tasks import repeat_every
9
- from socketio import AsyncClient
10
-
11
- from ..data_classes import Category, ModelInformation, NodeState
12
- from ..node import Node
13
- from .converter_logic import ConverterLogic
14
-
15
-
16
- class ConverterNode(Node):
17
- converter: ConverterLogic
18
- skip_check_state: bool = False
19
- bad_model_ids: List[str] = []
20
-
21
- def __init__(self, name: str, converter: ConverterLogic, uuid: Optional[str] = None):
22
- super().__init__(name, uuid)
23
- self.converter = converter
24
- converter.init(self)
25
-
26
- @self.on_event("startup")
27
- @repeat_every(seconds=60, raise_exceptions=True, wait_first=False)
28
- async def check_state():
29
- if not self.skip_check_state:
30
- try:
31
- await self.check_state()
32
- except Exception:
33
- logging.error('could not check state. Is loop reachable?')
34
-
35
- async def convert_model(self, model_information: ModelInformation):
36
- if model_information.id in self.bad_model_ids:
37
- logging.info(
38
- f'skipping bad model model {model_information.id} for {model_information.context.organization}/{model_information.context.project}.')
39
- return
40
- try:
41
- logging.info(
42
- f'converting model {jsonable_encoder(asdict(model_information))}')
43
- await self.converter.convert(model_information)
44
- logging.info('uploading model ')
45
- await self.converter.upload_model(model_information.context, model_information.id)
46
- except Exception as e:
47
- self.bad_model_ids.append(model_information.id)
48
- logging.error(
49
- f'could not convert model {model_information.id} for {model_information.context.organization}/{model_information.context.project}. Details: {str(e)}.')
50
-
51
- async def check_state(self):
52
- logging.info(f'checking state: {self.status.state}')
53
-
54
- if self.status.state == NodeState.Running:
55
- return
56
- self.status.state = NodeState.Running
57
- try:
58
- await self.convert_models()
59
- except Exception as exc:
60
- logging.error(str(exc))
61
-
62
- self.status.state = NodeState.Idle
63
-
64
- async def convert_models(self) -> None:
65
- try:
66
- response = await self.loop_communicator.get('/projects')
67
- assert response.status_code == 200, f'Assert statuscode 200, but was {response.status_code}.'
68
- content = response.json()
69
- projects = content['projects']
70
-
71
- for project in projects:
72
- organization_id = project['organization_id']
73
- project_id = project['project_id']
74
-
75
- response = await self.loop_communicator.get(f'{project["resource"]}')
76
- if response.status_code != HTTPStatus.OK:
77
- logging.error(f'got bad response for {response.url}: {str(response.status_code)}')
78
- continue
79
-
80
- project_categories = [from_dict(data_class=Category, data=c) for c in response.json()['categories']]
81
-
82
- path = f'{project["resource"]}/models'
83
- models_response = await self.loop_communicator.get(path)
84
- assert models_response.status_code == 200
85
- content = models_response.json()
86
- models = content['models']
87
-
88
- for model in models:
89
- if (model['version']
90
- and self.converter.source_format in model['formats']
91
- and self.converter.target_format not in model['formats']
92
- ):
93
- # if self.converter.source_format in model['formats'] and project_id == 'drawingbot' and model['version'] == "6.0":
94
- model_information = ModelInformation(
95
- host=self.loop_communicator.base_url,
96
- organization=organization_id,
97
- project=project_id,
98
- id=model['id'],
99
- categories=project_categories,
100
- version=model['version'],
101
- )
102
- await self.convert_model(model_information)
103
- except Exception:
104
- logging.exception('could not convert models')
105
-
106
- async def send_status(self):
107
- pass
108
-
109
- async def on_startup(self):
110
- pass
111
-
112
- async def on_shutdown(self):
113
- pass
114
-
115
- async def on_repeat(self):
116
- pass
117
-
118
- def register_sio_events(self, sio_client: AsyncClient):
119
- pass
120
-
121
- async def get_state(self):
122
- return NodeState.Idle # NOTE unused for this node type
123
-
124
- def get_node_type(self):
125
- return 'converter'
@@ -1,55 +0,0 @@
1
- import logging
2
- from typing import List
3
-
4
- import pytest
5
-
6
- from learning_loop_node.converter.converter_logic import ConverterLogic
7
- from learning_loop_node.converter.converter_node import ConverterNode
8
- from learning_loop_node.data_classes import ModelInformation
9
- from learning_loop_node.loop_communication import LoopCommunicator
10
- from learning_loop_node.tests import test_helper
11
-
12
-
13
- class TestConverter(ConverterLogic):
14
- __test__ = False # hint for pytest
15
-
16
- def __init__(self, source_format: str, target_format: str, models: List[ModelInformation]):
17
- super().__init__(source_format, target_format)
18
- self.models = models
19
-
20
- async def _convert(self, model_information: ModelInformation) -> None:
21
- self.models.append(model_information)
22
-
23
- def get_converted_files(self, model_id) -> List[str]:
24
- return [] # test: test_meta_information fails because model cannot be uploaded
25
-
26
-
27
- @pytest.mark.asyncio
28
- @pytest.fixture()
29
- async def setup_converter_test_project(glc: LoopCommunicator):
30
- await glc.delete("/zauberzeug/projects/pytest_conv?keep_images=true")
31
- project_configuration = {
32
- 'project_name': 'pytest_conv', 'box_categories': 1, 'point_categories': 1, 'inbox': 0, 'annotate': 0, 'review': 0,
33
- 'complete': 0, 'image_style': 'plain', 'thumbs': False, 'trainings': 1}
34
- r = await glc.post("/zauberzeug/projects/generator", json=project_configuration)
35
- assert r.status_code == 200
36
- yield
37
- await glc.delete("/zauberzeug/projects/pytest?keep_images=true")
38
-
39
-
40
- # pylint: disable=redefined-outer-name, unused-argument
41
- @pytest.mark.asyncio
42
- async def test_meta_information(setup_converter_test_project):
43
- model_id = await test_helper.get_latest_model_id(project='pytest_conv')
44
-
45
- converter = TestConverter(source_format='mocked', target_format='test', models=[])
46
- node = ConverterNode(name='test', converter=converter)
47
- await node.convert_models()
48
-
49
- pytest_project_model = [m for m in converter.models if m.id == model_id][0]
50
-
51
- categories = pytest_project_model.categories
52
- assert len(categories) == 2
53
- category_types = [category.type for category in categories]
54
- assert 'box' in category_types
55
- assert 'point' in category_types
@@ -1,52 +0,0 @@
1
-
2
- import asyncio
3
- import logging
4
- from dataclasses import asdict
5
- from typing import TYPE_CHECKING, Any
6
-
7
- import socketio
8
- from dacite import from_dict
9
- from fastapi.encoders import jsonable_encoder
10
-
11
- from ..data_classes import TrainingOut
12
- from ..data_classes.socket_response import SocketResponse
13
-
14
- if TYPE_CHECKING:
15
- from .trainer_logic import TrainerLogic
16
-
17
-
18
- async def try_sync_model(trainer: 'TrainerLogic', trainer_node_uuid: str, sio_client: socketio.AsyncClient):
19
- try:
20
- model = trainer.get_new_model()
21
- except Exception as exc:
22
- logging.exception('error while getting new model')
23
- raise Exception(f'Could not get new model: {str(exc)}') from exc
24
- logging.debug(f'new model {model}')
25
-
26
- if model:
27
- response = await sync_model(trainer, trainer_node_uuid, sio_client, model)
28
-
29
- if not response.success:
30
- error_msg = f'Error for update_training: Response from loop was : {asdict(response)}'
31
- logging.error(error_msg)
32
- raise Exception(error_msg)
33
-
34
-
35
- async def sync_model(trainer, trainer_node_uuid, sio_client, model):
36
- current_training = trainer.training
37
- new_training = TrainingOut(
38
- trainer_id=trainer_node_uuid,
39
- confusion_matrix=model.confusion_matrix,
40
- train_image_count=current_training.data.train_image_count(),
41
- test_image_count=current_training.data.test_image_count(),
42
- hyperparameters=trainer.hyperparameters)
43
-
44
- await asyncio.sleep(0.1) # NOTE needed for tests.
45
-
46
- result = await sio_client.call('update_training', (current_training.context.organization, current_training.context.project, jsonable_encoder(new_training)))
47
- response = from_dict(data_class=SocketResponse, data=result)
48
-
49
- if response.success:
50
- logging.info(f'successfully updated training {asdict(new_training)}')
51
- trainer.on_model_published(model)
52
- return response