leap-model-parser 0.1.216__py3-none-any.whl → 0.1.232__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.
@@ -47,6 +47,7 @@ class Node:
47
47
  outputs: Dict[str, ConnectionOutput] = field(default_factory=dict)
48
48
  pruning_plan_id: Optional[str] = None
49
49
  wrapper: Optional[WrapperData] = None
50
+ shape: Optional[List[str]] = None
50
51
 
51
52
  def __key(self):
52
53
  return (self.id, self.name)
@@ -5,7 +5,6 @@ from typing import Set, Dict, Any, List, Type, Optional, Tuple
5
5
 
6
6
  import tensorflow as tf # type: ignore
7
7
  from keras.engine.keras_tensor import KerasTensor # type: ignore
8
- from keras.engine.node import Node # type: ignore
9
8
  from keras.engine.node import Node as keras_node # type: ignore
10
9
  from keras.layers import Layer # type: ignore
11
10
  from keras.layers.convolutional.base_conv import Conv # type: ignore
@@ -206,7 +205,7 @@ class KerasJsonModelImport:
206
205
  'serialized_call_kwargs': serialized_call_kwargs})
207
206
 
208
207
  def generate_regular_node(self, layer: Dict[str, Any], layer_metadata: Dict[str, Any], node_key: str,
209
- layer_name_to_inbound_nodes: Dict[str, List[Node]]):
208
+ layer_name_to_inbound_nodes: Dict[str, List[keras_node]]):
210
209
  data = layer['config']
211
210
  if layer['class_name'] in ('TFOpLambda', 'SlicingOpLambda') or layer['class_name'] in self.custom_layers:
212
211
  call_args = layer_name_to_inbound_nodes[layer['config']
@@ -221,11 +220,17 @@ class KerasJsonModelImport:
221
220
 
222
221
  self.layer_data_adjustments(data, layer_metadata)
223
222
  node = Node(id=str(self.id), name=layer_metadata.get(
224
- "class_name", layer["class_name"]), data=data, position=[0, 0])
223
+ "class_name", layer["class_name"]), data=data, position=[0, 0],
224
+ shape=self._convert_layer_shape_to_string(layer['output_shape']))
225
225
  if 'wrapper' in layer:
226
226
  node.wrapper = layer['wrapper']
227
227
  self.nodes_cache[node_key] = node
228
228
 
229
+ @staticmethod
230
+ def _convert_layer_shape_to_string(s: List[Optional[int]]) -> List[str]:
231
+ return [str(dim) if dim is not None else 'dynamic_shape' for dim in s]
232
+
233
+
229
234
  @classmethod
230
235
  def handle_wrapper_layer(cls, layer):
231
236
  wrapped_layer = layer['config']['layer']
@@ -332,7 +337,8 @@ class KerasJsonModelImport:
332
337
  self.connected_inputs.append(input_info)
333
338
  input_copy = {
334
339
  'type': 'Input',
335
- 'output_name': input_name
340
+ 'output_name': input_name,
341
+ 'batch_input_shape_origin': shape
336
342
  }
337
343
  inputNode = Node(
338
344
  id=str(self.id), name='Input', data=input_copy, position=[0, 0])
@@ -31,6 +31,8 @@ class LeapGraphEditor:
31
31
  f"Prediction node with name {prediction_node.name} not found in model graph"
32
32
  prediction_node_id = prediction_mapping_node.id
33
33
  self.model_graph[prediction_node_id].data['prediction_type'] = prediction_labels_name
34
+ elif 'Input' in node_connection.node.type.value:
35
+ self._find_or_add_input_node(node_connection.node)
34
36
  elif node_connection.node.type == NodeMappingType.Visualizer:
35
37
  new_visualizer_node_id = self._add_visualizer_node(
36
38
  node_connection.node.name, node_connection.node.sub_type,
@@ -47,6 +49,7 @@ class LeapGraphEditor:
47
49
  self._add_connection_to_node(new_metric_node_id, input_name, input_node_id)
48
50
  elif node_connection.node.type in (NodeMappingType.Loss, NodeMappingType.CustomLoss):
49
51
  new_loss_node_id = self._add_loss_node(node_connection.node.name,
52
+ node_connection.node.user_unique_name,
50
53
  node_connection.node.type == NodeMappingType.CustomLoss,
51
54
  node_connection.node.arg_names)
52
55
  for input_name, node in node_connection.node_inputs.items():
@@ -124,6 +127,22 @@ class LeapGraphEditor:
124
127
  self.model_graph[new_node_id] = ground_truth_node
125
128
  return new_node_id
126
129
 
130
+ def _add_input_encoder_not_connected_to_the_model_node(self, input_name: str) -> str:
131
+ new_node_id = self._generate_new_node_id()
132
+ ground_truth_node = Node(
133
+ new_node_id,
134
+ 'Input',
135
+ position=[0, 0],
136
+ data={'name': input_name, 'output_name': input_name,
137
+ 'type': 'Input', "selected": input_name},
138
+ inputs={},
139
+ outputs={
140
+ f'{new_node_id}-{input_name}': ConnectionOutput([])
141
+ }
142
+ )
143
+ self.model_graph[new_node_id] = ground_truth_node
144
+ return new_node_id
145
+
127
146
  def _add_visualizer_node(self, visualizer_name: str, visualizer_type: str,
128
147
  user_unique_name: str, arg_names: List[str]) -> str:
129
148
  new_node_id = self._generate_new_node_id()
@@ -157,7 +176,7 @@ class LeapGraphEditor:
157
176
  self.model_graph[new_node_id] = metric_node
158
177
  return new_node_id
159
178
 
160
- def _add_loss_node(self, loss_name: str, is_custom_loss: bool, arg_names: Optional[List[str]]=None) -> str:
179
+ def _add_loss_node(self, loss_name: str, user_unique_name:str, is_custom_loss: bool, arg_names: Optional[List[str]]=None) -> str:
161
180
  new_node_id = self._generate_new_node_id()
162
181
 
163
182
  loss_type = 'CustomLoss' if is_custom_loss else 'Loss'
@@ -167,7 +186,7 @@ class LeapGraphEditor:
167
186
  new_node_id,
168
187
  loss_node_name,
169
188
  position=[0, 0],
170
- data={'type': loss_type, 'selected': loss_name, 'name': loss_name},
189
+ data={'type': loss_type, 'selected': loss_name, 'name': loss_name, 'user_unique_name': user_unique_name},
171
190
  inputs={},
172
191
  outputs={
173
192
  f'{new_node_id}-loss': ConnectionOutput([])
@@ -237,6 +256,8 @@ class LeapGraphEditor:
237
256
  if input_node_id is None:
238
257
  if input_node.type == NodeMappingType.GroundTruth:
239
258
  input_node_id = self._add_ground_truth_node(input_node.name)
259
+ elif input_node.type == NodeMappingType.Input:
260
+ input_node_id = self._add_input_encoder_not_connected_to_the_model_node(input_node.name)
240
261
  else:
241
262
  raise Exception(f'Couldnt find input node name {input_node.name}')
242
263
  elif 'Input' in input_node.type.value:
@@ -1,13 +1,13 @@
1
+ # mypy: ignore-errors
2
+
1
3
  import glob
2
4
  import json
3
5
  import ntpath
4
- import pickle
5
6
  import tarfile
6
7
  import tempfile
7
8
  from importlib.util import find_spec
8
9
  from pathlib import Path
9
10
 
10
-
11
11
  import tensorflow as tf # type: ignore
12
12
  from code_loader.contract.mapping import NodeConnection, NodeMapping # type: ignore
13
13
  from keras import Model # type: ignore
@@ -34,6 +34,7 @@ if spec is not None:
34
34
 
35
35
  onnx_imported = True
36
36
 
37
+
37
38
  class ModelParser:
38
39
  def __init__(self, should_transform_inputs_and_outputs=False,
39
40
  custom_layers=None,
@@ -54,8 +55,14 @@ class ModelParser:
54
55
  ImportModelTypeEnum.PB_TF2.value: self.convert_pb_model,
55
56
  }
56
57
 
58
+ @staticmethod
59
+ def _add_output_node_shape_to_model_schema(model_schema: Dict, keras_model: Model):
60
+ for i, layer in enumerate(keras_model.layers):
61
+ model_schema['config']['layers'][i]['output_shape'] = list(layer.output_shape)
62
+
57
63
  def get_keras_model_and_model_graph(
58
- self, model_path: Path, model_type: ImportModelTypeEnum) -> Tuple[Dict[str, Node], List[InputInfo], Optional[Model], Optional[str]]:
64
+ self, model_path: Path, model_type: ImportModelTypeEnum) -> Tuple[
65
+ Dict[str, Node], List[InputInfo], Optional[Model], Optional[str]]:
59
66
  model_to_keras_converter: Optional[Callable[[str], Tuple[Dict[str, Node], Model, Optional[str]]]] = \
60
67
  self._model_types_converter.get(model_type.value)
61
68
  if model_to_keras_converter is None:
@@ -64,6 +71,9 @@ class ModelParser:
64
71
 
65
72
  file_path = str(model_path)
66
73
  model_schema, keras_model_with_weights, error_info = model_to_keras_converter(file_path)
74
+
75
+ self._add_output_node_shape_to_model_schema(model_schema, keras_model_with_weights)
76
+
67
77
  model_generator = KerasJsonModelImport(self.custom_layers)
68
78
 
69
79
  keras_model = keras_model_with_weights
@@ -76,13 +86,19 @@ class ModelParser:
76
86
 
77
87
  graph, connected_inputs = model_generator.generate_graph(
78
88
  model_schema, layer_name_to_inbound_nodes)
79
-
89
+ # make sure input order is kept with original model
90
+ input_list = []
91
+ for inp in keras_model.inputs:
92
+ name = inp.name
93
+ name = name.replace(".", "_")
94
+ for inp_graph in connected_inputs:
95
+ if inp_graph.name == name:
96
+ input_list.append(inp_graph)
80
97
  if self.mapping_connections is not None:
81
98
  leap_graph_editor = LeapGraphEditor(graph, keras_model_with_weights)
82
99
  leap_graph_editor.add_connections_to_graph(self.mapping_connections)
83
100
 
84
- return graph, connected_inputs, keras_model_with_weights, error_info
85
-
101
+ return graph, input_list, keras_model_with_weights, error_info
86
102
 
87
103
  def _get_k_model_from_pb_path(self, file_path: str):
88
104
  tar_file = tarfile.open(file_path)
@@ -98,7 +114,8 @@ class ModelParser:
98
114
  k_model = self._load_keras_model_with_custom_layers(pb_folder_path)
99
115
  return k_model
100
116
 
101
- def generate_model_graph(self, model_path: Path, model_type: ImportModelTypeEnum) -> Tuple[Dict[str, Node], List[InputInfo]]:
117
+ def generate_model_graph(self, model_path: Path, model_type: ImportModelTypeEnum) -> Tuple[
118
+ Dict[str, Node], List[InputInfo]]:
102
119
  model_graph, connected_inputs, _, error_info = self.get_keras_model_and_model_graph(
103
120
  model_path, model_type)
104
121
  return model_graph, connected_inputs
@@ -121,25 +138,58 @@ class ModelParser:
121
138
  input_all = [_input.name for _input in onnx_model.graph.input]
122
139
  input_initializer = [
123
140
  node.name for node in onnx_model.graph.initializer]
124
- input_names = list(set(input_all) - set(input_initializer))
141
+ input_names = [name for name in input_all if name not in input_initializer]
125
142
  converted_response: ConvertedResponse = onnx_to_keras(onnx_model, input_names=input_names,
126
- name_policy='attach_weights_name', allow_partial_compilation=False)
143
+ name_policy='attach_weights_name',
144
+ allow_partial_compilation=False)
127
145
  return self.convert_to_keras_model(converted_response.converted_model, converted_response.error_info)
128
146
 
129
147
  def _load_keras_model_with_custom_layers(self, file_path: str):
130
148
  custom_objects = {}
131
149
  if self.custom_layers is not None:
132
150
  custom_objects = self.custom_layers
133
- return load_model(file_path, custom_objects=custom_objects, compile=False)
151
+
152
+ try:
153
+ return load_model(file_path, custom_objects=custom_objects, compile=False)
154
+ except OSError as e:
155
+ if 'signature' in str(e):
156
+ raise Exception('Unable to open model file. The model might be corrupted or not a valid.')
157
+ else:
158
+ raise e
159
+
134
160
 
135
161
  def convert_h5_model(self, file_path: str) -> Tuple[Dict[str, Node], Model, Optional[str]]:
136
162
  imported_model = self._load_keras_model_with_custom_layers(file_path)
137
163
  return self.convert_to_keras_model(imported_model)
138
164
 
139
- def convert_to_keras_model(self, k_model, error_info: Optional[str] = None) -> Tuple[Dict[str, Node], Model, Optional[str]]:
165
+ def convert_to_keras_model(self, k_model, error_info: Optional[str] = None) -> Tuple[
166
+ Dict[str, Node], Model, Optional[str]]:
140
167
  converted_k_model = convert_channels_first_to_last(
141
168
  k_model, self._should_transform_inputs_and_outputs, self.custom_layers)
142
169
 
170
+ from keras.saving.legacy.saved_model import json_utils # type: ignore
171
+ import numpy as np
172
+
173
+ _orig_get_json_type = json_utils.get_json_type
174
+
175
+
176
+ def _patched_get_json_type(obj): # type: ignore
177
+ # Handle numpy dtype explicitly
178
+ if isinstance(obj, np.dtype):
179
+ return obj.name # e.g. "int64"
180
+ # Make sure common numpy scalars/containers are handled robustly
181
+ if isinstance(obj, np.integer):
182
+ return int(obj)
183
+ if isinstance(obj, np.floating):
184
+ return float(obj)
185
+ if isinstance(obj, np.bool_):
186
+ return bool(obj)
187
+ if isinstance(obj, np.ndarray):
188
+ return obj.tolist()
189
+ return _orig_get_json_type(obj)
190
+
191
+ json_utils.get_json_type = _patched_get_json_type
192
+
143
193
  model_schema = json.loads(converted_k_model.to_json())
144
194
 
145
195
  return model_schema, converted_k_model, error_info
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: leap-model-parser
3
- Version: 0.1.216
3
+ Version: 0.1.232
4
4
  Summary:
5
- Home-page: https://github.com/tensorleap/leap-model-parser
6
5
  License: MIT
6
+ License-File: LICENSE
7
7
  Author: idan
8
8
  Author-email: idan.yogev@tensorleap.ai
9
9
  Requires-Python: >=3.8,<3.11
@@ -12,15 +12,16 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.8
13
13
  Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
- Requires-Dist: code-loader (>=1.0.115)
16
- Requires-Dist: keras-data-format-converter (==0.1.22)
15
+ Requires-Dist: code-loader (>=1.0.127)
16
+ Requires-Dist: keras-data-format-converter (==0.1.24)
17
17
  Requires-Dist: leap-model-rebuilder (==0.1.7)
18
18
  Requires-Dist: numpy (>=1.22.3,<2.0.0)
19
19
  Requires-Dist: onnx (==1.13.0)
20
- Requires-Dist: onnx2kerastl (==0.0.175)
20
+ Requires-Dist: onnx2kerastl (==0.0.180)
21
21
  Requires-Dist: tensorflow (==2.12.0) ; platform_machine == "x86_64"
22
22
  Requires-Dist: tensorflow-io-gcs-filesystem (==0.34.0)
23
23
  Requires-Dist: tensorflow-macos (==2.12.0) ; platform_machine == "arm64"
24
+ Project-URL: Homepage, https://github.com/tensorleap/leap-model-parser
24
25
  Project-URL: Repository, https://github.com/tensorleap/leap-model-parser
25
26
  Description-Content-Type: text/markdown
26
27
 
@@ -1,13 +1,12 @@
1
- LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
2
1
  leap_model_parser/__init__.py,sha256=OAU7rFHAVVWUM-cDtQ4Ohum567KN8M-YTkHZp5KiYbo,132
3
2
  leap_model_parser/contract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- leap_model_parser/contract/graph.py,sha256=c5rSlJJQ3JzLhyeniglzrP5GH0fp8UZDQKZw1fcrNe8,1132
3
+ leap_model_parser/contract/graph.py,sha256=lbs_IUnlkvoVjiARBMJee-aNbqeNXNPsUeOEwQliKgU,1170
5
4
  leap_model_parser/contract/importmodelresponse.py,sha256=GlvnKS8xrebU2Sj0dxqtEhAOOo3DlOtT7AMJ2BlzH9E,145
6
5
  leap_model_parser/contract/nodedata.py,sha256=1_ML0nzp3QUZ0_9mGSLhfO4_hqjYMwi0DWLwymUnWEs,43326
7
6
  leap_model_parser/contract/ui_components.json,sha256=0lsxwOLElW1E-imCcdh3zKPWgzFuQ_bApG6aHvYfTvo,410591
8
- leap_model_parser/keras_json_model_import.py,sha256=x7HOH6iaASfzJgwMRHgF5SQS-iFOF5j9yCG0mDC9HEA,16794
9
- leap_model_parser/leap_graph_editor.py,sha256=fGbGo_28nG_KU7vFB3-d_zkXVvYWwtqOk_1ZkBeg6So,13289
10
- leap_model_parser/model_parser.py,sha256=Je37-FjrNgi8w17rBIt_b71xm-BoiopBAvgW30qfb7o,6614
7
+ leap_model_parser/keras_json_model_import.py,sha256=h9-eb41W67goutRV5c3VkStxvntgb6EmPFdPeshhtsY,17058
8
+ leap_model_parser/leap_graph_editor.py,sha256=s6iVO06xM6LJ8ZjSxkr1srYxsHhGl2KnQjQoc9g4_iE,14329
9
+ leap_model_parser/model_parser.py,sha256=672YTDpQP38439juRr22UbrLG8YQng8HVmMmLVOy1mI,8510
11
10
  leap_model_parser/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
11
  leap_model_parser/utils/layerpedia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
12
  leap_model_parser/utils/layerpedia/layerpedia.py,sha256=1syubfXBTB630TVkgcQ-Ge7Qe9Zbr6EtZRreuqCJnQ8,9292
@@ -18,8 +17,8 @@ leap_model_parser/utils/uicomponents/generatenodedata.py,sha256=LRaPlO5jJ9pUtkvL
18
17
  leap_model_parser/utils/uicomponents/tensorflowinscpection.py,sha256=ym613z9iQKPDBpr0RYD35bTABdm1L-Ez86G47BYT7qw,6775
19
18
  leap_model_parser/utils/uicomponents/ui_components.json,sha256=0lsxwOLElW1E-imCcdh3zKPWgzFuQ_bApG6aHvYfTvo,410591
20
19
  leap_model_parser/utils/uicomponents/ui_components_config.yaml,sha256=cRH8T-c3TAL0nfefRvt9pFsjbTWNEg38NRyHR7RpJsk,19534
21
- leap_model_parser-0.1.216.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
22
- leap_model_parser-0.1.216.dist-info/METADATA,sha256=2wxebfyNwh5M_-qk4uyURhnXyOsiYngrwhxC3CN16Ps,1104
23
- leap_model_parser-0.1.216.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
24
- leap_model_parser-0.1.216.dist-info/entry_points.txt,sha256=ZvV6EuQt1uAqwapNg5Lo2qjJM9ZG5g2wfzZoLh_Ztyk,77
25
- leap_model_parser-0.1.216.dist-info/RECORD,,
20
+ leap_model_parser-0.1.232.dist-info/METADATA,sha256=3jnqLtGeBomuwwUqg-9mV4mVHvl-h3fOTuaoKTN5EOE,1138
21
+ leap_model_parser-0.1.232.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
22
+ leap_model_parser-0.1.232.dist-info/entry_points.txt,sha256=ZvV6EuQt1uAqwapNg5Lo2qjJM9ZG5g2wfzZoLh_Ztyk,77
23
+ leap_model_parser-0.1.232.dist-info/licenses/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
24
+ leap_model_parser-0.1.232.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2021 TensorLeap
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.