aimodelshare 0.1.55__py3-none-any.whl → 0.1.60__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 aimodelshare might be problematic. Click here for more details.
- aimodelshare/__init__.py +94 -14
- aimodelshare/aimsonnx.py +263 -82
- aimodelshare/api.py +13 -12
- aimodelshare/auth.py +163 -0
- aimodelshare/aws.py +4 -4
- aimodelshare/base_image.py +1 -1
- aimodelshare/containerisation.py +1 -1
- aimodelshare/data_sharing/download_data.py +133 -83
- aimodelshare/generatemodelapi.py +7 -6
- aimodelshare/main/authorization.txt +275 -275
- aimodelshare/main/eval_lambda.txt +81 -13
- aimodelshare/model.py +493 -197
- aimodelshare/modeluser.py +89 -1
- aimodelshare/moral_compass/README.md +408 -0
- aimodelshare/moral_compass/__init__.py +58 -0
- aimodelshare/moral_compass/_version.py +3 -0
- aimodelshare/moral_compass/api_client.py +601 -0
- aimodelshare/moral_compass/challenge.py +365 -0
- aimodelshare/moral_compass/config.py +187 -0
- aimodelshare/playground.py +26 -14
- aimodelshare/preprocessormodules.py +60 -6
- aimodelshare/pyspark/authorization.txt +258 -258
- aimodelshare/pyspark/eval_lambda.txt +1 -1
- aimodelshare/reproducibility.py +20 -5
- aimodelshare/utils/__init__.py +78 -0
- aimodelshare/utils/optional_deps.py +38 -0
- aimodelshare-0.1.60.dist-info/METADATA +258 -0
- {aimodelshare-0.1.55.dist-info → aimodelshare-0.1.60.dist-info}/RECORD +31 -25
- aimodelshare-0.1.60.dist-info/licenses/LICENSE +5 -0
- {aimodelshare-0.1.55.dist-info → aimodelshare-0.1.60.dist-info}/top_level.txt +0 -1
- aimodelshare-0.1.55.dist-info/METADATA +0 -63
- aimodelshare-0.1.55.dist-info/licenses/LICENSE +0 -2
- tests/__init__.py +0 -0
- tests/test_aimsonnx.py +0 -135
- tests/test_playground.py +0 -721
- {aimodelshare-0.1.55.dist-info → aimodelshare-0.1.60.dist-info}/WHEEL +0 -0
aimodelshare/__init__.py
CHANGED
|
@@ -1,18 +1,98 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
from .data_sharing.download_data import download_data, import_quickstart_data
|
|
4
|
-
from .reproducibility import export_reproducibility_env, import_reproducibility_env
|
|
1
|
+
"""
|
|
2
|
+
Top-level aimodelshare package initializer.
|
|
5
3
|
|
|
4
|
+
Refactored to:
|
|
5
|
+
- Avoid eager importing of heavy optional dependencies (networkx, torch, tensorflow, etc.)
|
|
6
|
+
- Allow lightweight submodules (e.g. aimodelshare.moral_compass) to work in minimal environments.
|
|
7
|
+
- Preserve backward compatibility where possible without forcing heavy installs.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from importlib import import_module
|
|
11
|
+
import warnings
|
|
6
12
|
|
|
7
13
|
__all__ = [
|
|
8
|
-
#
|
|
9
|
-
ModelPlayground,
|
|
10
|
-
Competition,
|
|
11
|
-
Data,
|
|
12
|
-
Experiment,
|
|
13
|
-
# Preprocessor
|
|
14
|
-
upload_preprocessor,
|
|
15
|
-
import_preprocessor,
|
|
16
|
-
export_preprocessor,
|
|
17
|
-
download_data
|
|
14
|
+
# Heavy/high-level objects (added only if imported successfully)
|
|
15
|
+
"ModelPlayground",
|
|
16
|
+
"Competition",
|
|
17
|
+
"Data",
|
|
18
|
+
"Experiment",
|
|
19
|
+
# Preprocessor helpers (optional)
|
|
20
|
+
"upload_preprocessor",
|
|
21
|
+
"import_preprocessor",
|
|
22
|
+
"export_preprocessor",
|
|
23
|
+
"download_data",
|
|
24
|
+
# Moral Compass client (lightweight)
|
|
25
|
+
"MoralcompassApiClient",
|
|
26
|
+
"MoralcompassTableMeta",
|
|
27
|
+
"MoralcompassUserStats",
|
|
28
|
+
"ApiClientError",
|
|
29
|
+
"NotFoundError",
|
|
30
|
+
"ServerError",
|
|
31
|
+
"get_api_base_url",
|
|
18
32
|
]
|
|
33
|
+
|
|
34
|
+
def _safe_import(module_name, symbols, remove_if_missing=True):
|
|
35
|
+
"""
|
|
36
|
+
Attempt to import given symbols from module_name.
|
|
37
|
+
On failure, emit a warning and optionally remove them from __all__.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
mod = import_module(module_name)
|
|
41
|
+
for sym in symbols:
|
|
42
|
+
try:
|
|
43
|
+
globals()[sym] = getattr(mod, sym)
|
|
44
|
+
except AttributeError:
|
|
45
|
+
warnings.warn(
|
|
46
|
+
f"aimodelshare: symbol '{sym}' not found in module '{module_name}'."
|
|
47
|
+
)
|
|
48
|
+
if remove_if_missing and sym in __all__:
|
|
49
|
+
__all__.remove(sym)
|
|
50
|
+
except Exception as exc:
|
|
51
|
+
warnings.warn(
|
|
52
|
+
f"aimodelshare: optional module '{module_name}' not loaded ({exc}). "
|
|
53
|
+
"Lightweight submodules remain usable."
|
|
54
|
+
)
|
|
55
|
+
if remove_if_missing:
|
|
56
|
+
for sym in symbols:
|
|
57
|
+
if sym in __all__ and sym not in globals():
|
|
58
|
+
__all__.remove(sym)
|
|
59
|
+
|
|
60
|
+
# Attempt optional imports (silently skip if deps missing)
|
|
61
|
+
_safe_import(
|
|
62
|
+
"aimodelshare.playground",
|
|
63
|
+
["ModelPlayground", "Competition", "Experiment", "Data"],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
_safe_import(
|
|
67
|
+
"aimodelshare.preprocessormodules",
|
|
68
|
+
["export_preprocessor", "upload_preprocessor", "import_preprocessor"],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
_safe_import(
|
|
72
|
+
"aimodelshare.data_sharing.download_data",
|
|
73
|
+
["download_data", "import_quickstart_data"], # import_quickstart_data not in __all__
|
|
74
|
+
remove_if_missing=True,
|
|
75
|
+
)
|
|
76
|
+
# If import_quickstart_data is missing, we don't expose it; if present we leave it accessible.
|
|
77
|
+
if "import_quickstart_data" not in globals():
|
|
78
|
+
# Ensure it's not accidentally in __all__
|
|
79
|
+
if "import_quickstart_data" in __all__:
|
|
80
|
+
__all__.remove("import_quickstart_data")
|
|
81
|
+
|
|
82
|
+
# Moral Compass submodule (expected always present in new branch)
|
|
83
|
+
_safe_import(
|
|
84
|
+
"aimodelshare.moral_compass",
|
|
85
|
+
[
|
|
86
|
+
"MoralcompassApiClient",
|
|
87
|
+
"MoralcompassTableMeta",
|
|
88
|
+
"MoralcompassUserStats",
|
|
89
|
+
"ApiClientError",
|
|
90
|
+
"NotFoundError",
|
|
91
|
+
"ServerError",
|
|
92
|
+
"get_api_base_url",
|
|
93
|
+
],
|
|
94
|
+
remove_if_missing=False, # If this fails, keep names so import errors surface clearly later
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Ensure __all__ only contains names actually available (except intentional exposure for debug)
|
|
98
|
+
__all__ = [name for name in __all__ if name in globals()]
|
aimodelshare/aimsonnx.py
CHANGED
|
@@ -2,28 +2,31 @@
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
|
+
# Import optional dependency checker
|
|
6
|
+
from aimodelshare.utils.optional_deps import check_optional
|
|
7
|
+
|
|
5
8
|
# ml frameworks
|
|
6
9
|
try:
|
|
7
10
|
import sklearn
|
|
8
11
|
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
|
|
9
12
|
except:
|
|
10
|
-
|
|
13
|
+
check_optional("sklearn", "Scikit-learn")
|
|
11
14
|
|
|
12
15
|
try:
|
|
13
16
|
import torch
|
|
14
17
|
except:
|
|
15
|
-
|
|
18
|
+
check_optional("torch", "PyTorch")
|
|
16
19
|
|
|
17
20
|
try:
|
|
18
21
|
import xgboost
|
|
19
22
|
except:
|
|
20
|
-
|
|
23
|
+
check_optional("xgboost", "XGBoost")
|
|
21
24
|
|
|
22
25
|
try:
|
|
23
26
|
import tensorflow as tf
|
|
24
27
|
import keras
|
|
25
28
|
except:
|
|
26
|
-
|
|
29
|
+
check_optional("tensorflow", "TensorFlow/Keras")
|
|
27
30
|
|
|
28
31
|
try:
|
|
29
32
|
import pyspark
|
|
@@ -32,14 +35,17 @@ try:
|
|
|
32
35
|
from pyspark.ml.tuning import CrossValidatorModel, TrainValidationSplitModel
|
|
33
36
|
from onnxmltools import convert_sparkml
|
|
34
37
|
except:
|
|
35
|
-
|
|
38
|
+
check_optional("pyspark", "PySpark")
|
|
36
39
|
|
|
37
40
|
|
|
38
41
|
# onnx modules
|
|
39
42
|
import onnx
|
|
40
43
|
import skl2onnx
|
|
41
44
|
from skl2onnx import convert_sklearn
|
|
42
|
-
import
|
|
45
|
+
# tf2onnx import is lazy-loaded to avoid requiring TensorFlow for non-TF workflows
|
|
46
|
+
_TF2ONNX_AVAILABLE = None
|
|
47
|
+
_tf2onnx_module = None
|
|
48
|
+
_tensorflow_module = None
|
|
43
49
|
try:
|
|
44
50
|
from torch.onnx import export
|
|
45
51
|
except:
|
|
@@ -71,18 +77,59 @@ import wget
|
|
|
71
77
|
from copy import copy
|
|
72
78
|
import psutil
|
|
73
79
|
from pympler import asizeof
|
|
74
|
-
from IPython.
|
|
80
|
+
from IPython.display import display, HTML, SVG
|
|
75
81
|
import absl.logging
|
|
76
82
|
import networkx as nx
|
|
77
83
|
import warnings
|
|
78
84
|
from pathlib import Path
|
|
79
85
|
import time
|
|
80
86
|
import signal
|
|
81
|
-
|
|
87
|
+
|
|
88
|
+
# scikeras imports keras which requires TensorFlow - lazy load it
|
|
89
|
+
try:
|
|
90
|
+
from scikeras.wrappers import KerasClassifier, KerasRegressor
|
|
91
|
+
_SCIKERAS_AVAILABLE = True
|
|
92
|
+
except ImportError:
|
|
93
|
+
_SCIKERAS_AVAILABLE = False
|
|
94
|
+
KerasClassifier = None
|
|
95
|
+
KerasRegressor = None
|
|
82
96
|
|
|
83
97
|
|
|
84
98
|
absl.logging.set_verbosity(absl.logging.ERROR)
|
|
85
99
|
|
|
100
|
+
def _check_tf2onnx_available():
|
|
101
|
+
"""Check if tf2onnx and TensorFlow are available, and load them if needed.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
tuple: (tf2onnx_module, tensorflow_module) on success
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
RuntimeError: If TensorFlow or tf2onnx are not installed
|
|
108
|
+
"""
|
|
109
|
+
global _TF2ONNX_AVAILABLE, _tf2onnx_module, _tensorflow_module
|
|
110
|
+
|
|
111
|
+
if _TF2ONNX_AVAILABLE is None:
|
|
112
|
+
try:
|
|
113
|
+
import tf2onnx as tf2onnx_temp
|
|
114
|
+
import tensorflow as tf_temp
|
|
115
|
+
_tf2onnx_module = tf2onnx_temp
|
|
116
|
+
_tensorflow_module = tf_temp
|
|
117
|
+
_TF2ONNX_AVAILABLE = True
|
|
118
|
+
except ImportError as e:
|
|
119
|
+
_TF2ONNX_AVAILABLE = False
|
|
120
|
+
raise RuntimeError(
|
|
121
|
+
"TensorFlow and tf2onnx are required for Keras model conversion to ONNX. "
|
|
122
|
+
"Please install them with: pip install tensorflow tf2onnx"
|
|
123
|
+
) from e
|
|
124
|
+
|
|
125
|
+
if not _TF2ONNX_AVAILABLE:
|
|
126
|
+
raise RuntimeError(
|
|
127
|
+
"TensorFlow and tf2onnx are required for Keras model conversion to ONNX. "
|
|
128
|
+
"Please install them with: pip install tensorflow tf2onnx"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return _tf2onnx_module, _tensorflow_module
|
|
132
|
+
|
|
86
133
|
def _extract_onnx_metadata(onnx_model, framework):
|
|
87
134
|
'''Extracts model metadata from ONNX file.'''
|
|
88
135
|
|
|
@@ -265,28 +312,8 @@ def _sklearn_to_onnx(model, initial_types=None, transfer_learning=None,
|
|
|
265
312
|
|
|
266
313
|
onx = convert_sklearn(model, initial_types=initial_types,target_opset={'': 15, 'ai.onnx.ml': 2})
|
|
267
314
|
|
|
268
|
-
##
|
|
269
|
-
|
|
270
|
-
import onnx
|
|
271
|
-
import numpy as np
|
|
272
|
-
|
|
273
|
-
indexlocationlist=[]
|
|
274
|
-
for i in VERSION_TABLE:
|
|
275
|
-
indexlocationlist.append(str(i).find(str(onnx.__version__)))
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
arr = np.array(indexlocationlist)
|
|
279
|
-
|
|
280
|
-
def condition(x): return x > -1
|
|
281
|
-
|
|
282
|
-
bool_arr = condition(arr)
|
|
283
|
-
|
|
284
|
-
output = np.where(bool_arr)[0]
|
|
285
|
-
|
|
286
|
-
ir_version=VERSION_TABLE[output[0]][1]
|
|
287
|
-
|
|
288
|
-
#add to model object before saving
|
|
289
|
-
onx.ir_version = ir_version
|
|
315
|
+
## set model ir_version to ensure sklearn opsets work properly
|
|
316
|
+
onx.ir_version = 8
|
|
290
317
|
|
|
291
318
|
# generate metadata dict
|
|
292
319
|
metadata = {}
|
|
@@ -555,8 +582,9 @@ def _keras_to_onnx(model, transfer_learning=None,
|
|
|
555
582
|
deep_learning=None, task_type=None, epochs=None):
|
|
556
583
|
'''Converts a Keras model to ONNX and extracts metadata.'''
|
|
557
584
|
|
|
558
|
-
|
|
559
|
-
|
|
585
|
+
# Check and load tf2onnx and TensorFlow lazily (only when needed)
|
|
586
|
+
tf2onnx, tf = _check_tf2onnx_available()
|
|
587
|
+
|
|
560
588
|
import numpy as np
|
|
561
589
|
import onnx
|
|
562
590
|
import pickle
|
|
@@ -934,7 +962,7 @@ def model_to_onnx(model, framework=None, model_input=None, initial_types=None,
|
|
|
934
962
|
from pyspark.ml.tuning import CrossValidatorModel, TrainValidationSplitModel
|
|
935
963
|
from onnxmltools import convert_sparkml
|
|
936
964
|
except:
|
|
937
|
-
|
|
965
|
+
check_optional("pyspark", "PySpark")
|
|
938
966
|
onnx = _pyspark_to_onnx(model, initial_types=initial_types,
|
|
939
967
|
transfer_learning=transfer_learning,
|
|
940
968
|
deep_learning=deep_learning,
|
|
@@ -989,23 +1017,39 @@ def model_to_onnx_timed(model_filepath, force_onnx=False, timeout=60, model_inpu
|
|
|
989
1017
|
|
|
990
1018
|
except:
|
|
991
1019
|
print("Timeout: Model to ONNX conversion is taking longer than expected. This can be the case for big models.")
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1020
|
+
|
|
1021
|
+
# Detect CI/testing environment for non-interactive fallback
|
|
1022
|
+
is_non_interactive = (
|
|
1023
|
+
os.environ.get("PYTEST_CURRENT_TEST") is not None or
|
|
1024
|
+
os.environ.get("AIMS_NON_INTERACTIVE") == "1"
|
|
1025
|
+
)
|
|
1026
|
+
|
|
1027
|
+
if is_non_interactive:
|
|
1028
|
+
# Auto-fallback to predictions-only in CI/testing environment
|
|
1029
|
+
print("Non-interactive environment detected. Falling back to predictions-only submission.")
|
|
1030
|
+
model_filepath = None
|
|
1031
|
+
else:
|
|
1032
|
+
# Interactive prompt for manual runs
|
|
1033
|
+
response = ''
|
|
1034
|
+
while response not in {"1", "2"}:
|
|
1035
|
+
response = input("Do you want to keep trying (1) or submit predictions only (2)? ")
|
|
1036
|
+
|
|
1037
|
+
if response == "1":
|
|
1038
|
+
try:
|
|
1039
|
+
import torch
|
|
1040
|
+
if isinstance(model_filepath, torch.nn.Module):
|
|
1041
|
+
onnx_model = model_to_onnx(model_filepath, model_input=model_input)
|
|
1042
|
+
else:
|
|
1043
|
+
onnx_model = model_to_onnx(model_filepath)
|
|
1044
|
+
except Exception as e:
|
|
1045
|
+
# Final fallback - if torch-specific handling failed, try generic conversion
|
|
1046
|
+
# This handles cases where torch module detection fails but conversion might still work
|
|
1047
|
+
warnings.warn(f"PyTorch-specific ONNX conversion failed ({e}), attempting generic conversion")
|
|
1000
1048
|
onnx_model = model_to_onnx(model_filepath, model_input=model_input)
|
|
1001
|
-
|
|
1002
|
-
onnx_model = model_to_onnx(model_filepath)
|
|
1003
|
-
except:
|
|
1004
|
-
onnx_model = model_to_onnx(model_filepath)
|
|
1005
|
-
model_filepath = onnx_model
|
|
1049
|
+
model_filepath = onnx_model
|
|
1006
1050
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1051
|
+
elif response == "2":
|
|
1052
|
+
model_filepath = None
|
|
1009
1053
|
|
|
1010
1054
|
finally:
|
|
1011
1055
|
print()
|
|
@@ -1024,6 +1068,12 @@ def _get_metadata(onnx_model):
|
|
|
1024
1068
|
#assert(isinstance(onnx_model, onnx.onnx_ml_pb2.ModelProto)), \
|
|
1025
1069
|
#"Please pass a onnx model object."
|
|
1026
1070
|
|
|
1071
|
+
# Handle None input gracefully - always return a dict
|
|
1072
|
+
if onnx_model is None:
|
|
1073
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1074
|
+
print("[DEBUG] _get_metadata: onnx_model is None, returning empty dict")
|
|
1075
|
+
return {}
|
|
1076
|
+
|
|
1027
1077
|
try:
|
|
1028
1078
|
onnx_meta = onnx_model.metadata_props
|
|
1029
1079
|
|
|
@@ -1034,36 +1084,121 @@ def _get_metadata(onnx_model):
|
|
|
1034
1084
|
|
|
1035
1085
|
onnx_meta_dict = ast.literal_eval(onnx_meta_dict['model_metadata'])
|
|
1036
1086
|
|
|
1087
|
+
# Handle case where metadata is stored as a list instead of dict
|
|
1088
|
+
if isinstance(onnx_meta_dict, list):
|
|
1089
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1090
|
+
print(f"[DEBUG] _get_metadata: metadata is a list of length {len(onnx_meta_dict)}")
|
|
1091
|
+
if len(onnx_meta_dict) > 0 and isinstance(onnx_meta_dict[0], dict):
|
|
1092
|
+
onnx_meta_dict = onnx_meta_dict[0]
|
|
1093
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1094
|
+
print("[DEBUG] _get_metadata: Extracted first dict from list")
|
|
1095
|
+
else:
|
|
1096
|
+
# Return empty dict if list doesn't contain valid dicts
|
|
1097
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1098
|
+
print("[DEBUG] _get_metadata: List does not contain valid dicts, returning empty dict")
|
|
1099
|
+
return {}
|
|
1100
|
+
|
|
1101
|
+
# Ensure we have a dict at this point
|
|
1102
|
+
if not isinstance(onnx_meta_dict, dict):
|
|
1103
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1104
|
+
print(f"[DEBUG] _get_metadata: Unexpected metadata type {type(onnx_meta_dict)}, returning empty dict")
|
|
1105
|
+
return {}
|
|
1106
|
+
|
|
1037
1107
|
#if onnx_meta_dict['model_config'] != None and \
|
|
1038
1108
|
#onnx_meta_dict['ml_framework'] != 'pytorch':
|
|
1039
1109
|
# onnx_meta_dict['model_config'] = ast.literal_eval(onnx_meta_dict['model_config'])
|
|
1040
1110
|
|
|
1041
|
-
if
|
|
1042
|
-
|
|
1111
|
+
# Attempt to parse nested fields only if they are string representations of dicts
|
|
1112
|
+
if 'model_architecture' in onnx_meta_dict and onnx_meta_dict['model_architecture'] != None:
|
|
1113
|
+
try:
|
|
1114
|
+
if isinstance(onnx_meta_dict['model_architecture'], str):
|
|
1115
|
+
onnx_meta_dict['model_architecture'] = ast.literal_eval(onnx_meta_dict['model_architecture'])
|
|
1116
|
+
except (ValueError, SyntaxError):
|
|
1117
|
+
# Keep as-is if parsing fails
|
|
1118
|
+
pass
|
|
1119
|
+
|
|
1120
|
+
if 'model_config' in onnx_meta_dict and onnx_meta_dict['model_config'] != None:
|
|
1121
|
+
try:
|
|
1122
|
+
if isinstance(onnx_meta_dict['model_config'], str):
|
|
1123
|
+
onnx_meta_dict['model_config'] = ast.literal_eval(onnx_meta_dict['model_config'])
|
|
1124
|
+
except (ValueError, SyntaxError):
|
|
1125
|
+
# Keep as-is if parsing fails
|
|
1126
|
+
pass
|
|
1043
1127
|
|
|
1044
|
-
if onnx_meta_dict['metadata_onnx'] != None:
|
|
1045
|
-
|
|
1128
|
+
if 'metadata_onnx' in onnx_meta_dict and onnx_meta_dict['metadata_onnx'] != None:
|
|
1129
|
+
try:
|
|
1130
|
+
if isinstance(onnx_meta_dict['metadata_onnx'], str):
|
|
1131
|
+
onnx_meta_dict['metadata_onnx'] = ast.literal_eval(onnx_meta_dict['metadata_onnx'])
|
|
1132
|
+
except (ValueError, SyntaxError):
|
|
1133
|
+
# Keep as-is if parsing fails
|
|
1134
|
+
pass
|
|
1046
1135
|
|
|
1047
1136
|
# onnx_meta_dict['model_image'] = onnx_to_image(onnx_model)
|
|
1048
1137
|
|
|
1049
1138
|
except Exception as e:
|
|
1050
1139
|
|
|
1051
|
-
|
|
1140
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1141
|
+
print(f"[DEBUG] _get_metadata: Exception during metadata extraction: {e}")
|
|
1052
1142
|
|
|
1053
|
-
|
|
1143
|
+
try:
|
|
1144
|
+
onnx_meta_dict = ast.literal_eval(onnx_meta_dict)
|
|
1145
|
+
# Handle list case in exception path as well
|
|
1146
|
+
if isinstance(onnx_meta_dict, list) and len(onnx_meta_dict) > 0 and isinstance(onnx_meta_dict[0], dict):
|
|
1147
|
+
onnx_meta_dict = onnx_meta_dict[0]
|
|
1148
|
+
elif not isinstance(onnx_meta_dict, dict):
|
|
1149
|
+
onnx_meta_dict = {}
|
|
1150
|
+
except:
|
|
1151
|
+
onnx_meta_dict = {}
|
|
1152
|
+
|
|
1153
|
+
# Final safety check: ensure we always return a dict
|
|
1154
|
+
if not isinstance(onnx_meta_dict, dict):
|
|
1155
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1156
|
+
print(f"[DEBUG] _get_metadata: Final check failed, returning empty dict instead of {type(onnx_meta_dict)}")
|
|
1157
|
+
return {}
|
|
1054
1158
|
|
|
1055
1159
|
return onnx_meta_dict
|
|
1056
1160
|
|
|
1057
1161
|
|
|
1058
1162
|
|
|
1059
1163
|
def _get_leaderboard_data(onnx_model, eval_metrics=None):
|
|
1164
|
+
'''Extract leaderboard data from ONNX model or return defaults.
|
|
1060
1165
|
|
|
1166
|
+
This function performs single-pass normalization and safely handles:
|
|
1167
|
+
- None onnx_model (returns defaults)
|
|
1168
|
+
- Invalid metadata structures
|
|
1169
|
+
- Missing keys in metadata
|
|
1170
|
+
'''
|
|
1171
|
+
|
|
1172
|
+
# Start with eval_metrics if provided, otherwise empty dict
|
|
1061
1173
|
if eval_metrics is not None:
|
|
1062
|
-
metadata = eval_metrics
|
|
1174
|
+
metadata = dict(eval_metrics) if isinstance(eval_metrics, dict) else {}
|
|
1063
1175
|
else:
|
|
1064
|
-
metadata =
|
|
1176
|
+
metadata = {}
|
|
1177
|
+
|
|
1178
|
+
# Handle None onnx_model gracefully
|
|
1179
|
+
if onnx_model is None:
|
|
1180
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1181
|
+
print("[DEBUG] _get_leaderboard_data: onnx_model is None, using default metadata")
|
|
1182
|
+
# Return metadata with safe defaults injected
|
|
1183
|
+
metadata['ml_framework'] = metadata.get('ml_framework', None)
|
|
1184
|
+
metadata['transfer_learning'] = metadata.get('transfer_learning', None)
|
|
1185
|
+
metadata['deep_learning'] = metadata.get('deep_learning', None)
|
|
1186
|
+
metadata['model_type'] = metadata.get('model_type', None)
|
|
1187
|
+
metadata['depth'] = metadata.get('depth', 0)
|
|
1188
|
+
metadata['num_params'] = metadata.get('num_params', 0)
|
|
1189
|
+
return metadata
|
|
1065
1190
|
|
|
1191
|
+
# Get metadata from ONNX - _get_metadata now always returns a dict
|
|
1066
1192
|
metadata_raw = _get_metadata(onnx_model)
|
|
1193
|
+
|
|
1194
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1195
|
+
print(f"[DEBUG] _get_leaderboard_data: metadata_raw type={type(metadata_raw)}, keys={list(metadata_raw.keys()) if isinstance(metadata_raw, dict) else 'N/A'}")
|
|
1196
|
+
|
|
1197
|
+
# Single-pass normalization: ensure metadata_raw is a dict
|
|
1198
|
+
if not isinstance(metadata_raw, dict):
|
|
1199
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1200
|
+
print(f"[DEBUG] _get_leaderboard_data: metadata_raw is not a dict (type={type(metadata_raw)}), using empty dict")
|
|
1201
|
+
metadata_raw = {}
|
|
1067
1202
|
|
|
1068
1203
|
# get list of current layer types
|
|
1069
1204
|
layer_list_keras, activation_list_keras = _get_layer_names()
|
|
@@ -1072,46 +1207,55 @@ def _get_leaderboard_data(onnx_model, eval_metrics=None):
|
|
|
1072
1207
|
layer_list = list(set(layer_list_keras + layer_list_pytorch))
|
|
1073
1208
|
activation_list = list(set(activation_list_keras + activation_list_pytorch))
|
|
1074
1209
|
|
|
1075
|
-
# get general model info
|
|
1076
|
-
metadata['ml_framework'] = metadata_raw
|
|
1077
|
-
metadata['transfer_learning'] = metadata_raw
|
|
1078
|
-
metadata['deep_learning'] = metadata_raw
|
|
1079
|
-
metadata['model_type'] = metadata_raw
|
|
1210
|
+
# get general model info - use .get() for safety
|
|
1211
|
+
metadata['ml_framework'] = metadata_raw.get('ml_framework')
|
|
1212
|
+
metadata['transfer_learning'] = metadata_raw.get('transfer_learning')
|
|
1213
|
+
metadata['deep_learning'] = metadata_raw.get('deep_learning')
|
|
1214
|
+
metadata['model_type'] = metadata_raw.get('model_type')
|
|
1080
1215
|
|
|
1081
1216
|
|
|
1082
1217
|
# get neural network metrics
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1218
|
+
# Add isinstance check for model_architecture to prevent TypeError
|
|
1219
|
+
if (metadata_raw.get('ml_framework') in ['keras', 'pytorch'] or
|
|
1220
|
+
metadata_raw.get('model_type') in ['MLPClassifier', 'MLPRegressor']) and \
|
|
1221
|
+
isinstance(metadata_raw.get('model_architecture'), dict):
|
|
1222
|
+
|
|
1223
|
+
metadata['depth'] = metadata_raw['model_architecture'].get('layers_number', 0)
|
|
1224
|
+
metadata['num_params'] = sum(metadata_raw['model_architecture'].get('layers_n_params', []))
|
|
1086
1225
|
|
|
1087
1226
|
for i in layer_list:
|
|
1088
|
-
|
|
1089
|
-
|
|
1227
|
+
layers_summary = metadata_raw['model_architecture'].get('layers_summary', {})
|
|
1228
|
+
if i in layers_summary:
|
|
1229
|
+
metadata[i.lower()+'_layers'] = layers_summary[i]
|
|
1090
1230
|
elif i.lower()+'_layers' not in metadata.keys():
|
|
1091
1231
|
metadata[i.lower()+'_layers'] = 0
|
|
1092
1232
|
|
|
1093
1233
|
for i in activation_list:
|
|
1094
|
-
|
|
1234
|
+
activations_summary = metadata_raw['model_architecture'].get('activations_summary', {})
|
|
1235
|
+
if i in activations_summary:
|
|
1095
1236
|
if i.lower()+'_act' in metadata:
|
|
1096
|
-
metadata[i.lower()+'_act'] +=
|
|
1237
|
+
metadata[i.lower()+'_act'] += activations_summary[i]
|
|
1097
1238
|
else:
|
|
1098
|
-
metadata[i.lower()+'_act'] =
|
|
1239
|
+
metadata[i.lower()+'_act'] = activations_summary[i]
|
|
1099
1240
|
else:
|
|
1100
1241
|
if i.lower()+'_act' not in metadata:
|
|
1101
1242
|
metadata[i.lower()+'_act'] = 0
|
|
1102
1243
|
|
|
1103
|
-
metadata['loss'] = metadata_raw['model_architecture']
|
|
1104
|
-
metadata['optimizer'] = metadata_raw['model_architecture']
|
|
1105
|
-
metadata['model_config'] = metadata_raw
|
|
1106
|
-
metadata['epochs'] = metadata_raw
|
|
1107
|
-
metadata['memory_size'] = metadata_raw
|
|
1244
|
+
metadata['loss'] = metadata_raw['model_architecture'].get('loss')
|
|
1245
|
+
metadata['optimizer'] = metadata_raw['model_architecture'].get('optimizer')
|
|
1246
|
+
metadata['model_config'] = metadata_raw.get('model_config')
|
|
1247
|
+
metadata['epochs'] = metadata_raw.get('epochs')
|
|
1248
|
+
metadata['memory_size'] = metadata_raw.get('memory_size')
|
|
1108
1249
|
|
|
1109
1250
|
# get sklearn & pyspark model metrics
|
|
1110
|
-
elif metadata_raw
|
|
1251
|
+
elif metadata_raw.get('ml_framework') in ['sklearn', 'xgboost', 'pyspark']:
|
|
1111
1252
|
metadata['depth'] = 0
|
|
1112
1253
|
|
|
1113
1254
|
try:
|
|
1114
|
-
|
|
1255
|
+
if isinstance(metadata_raw.get('model_architecture'), dict):
|
|
1256
|
+
metadata['num_params'] = sum(metadata_raw['model_architecture'].get('layers_n_params', []))
|
|
1257
|
+
else:
|
|
1258
|
+
metadata['num_params'] = 0
|
|
1115
1259
|
except:
|
|
1116
1260
|
metadata['num_params'] = 0
|
|
1117
1261
|
|
|
@@ -1124,15 +1268,29 @@ def _get_leaderboard_data(onnx_model, eval_metrics=None):
|
|
|
1124
1268
|
metadata['loss'] = None
|
|
1125
1269
|
|
|
1126
1270
|
try:
|
|
1127
|
-
|
|
1271
|
+
if isinstance(metadata_raw.get('model_architecture'), dict):
|
|
1272
|
+
metadata['optimizer'] = metadata_raw['model_architecture'].get('optimizer')
|
|
1273
|
+
else:
|
|
1274
|
+
metadata['optimizer'] = None
|
|
1128
1275
|
except:
|
|
1129
1276
|
metadata['optimizer'] = None
|
|
1130
1277
|
|
|
1131
1278
|
try:
|
|
1132
|
-
metadata['model_config'] = metadata_raw
|
|
1279
|
+
metadata['model_config'] = metadata_raw.get('model_config')
|
|
1133
1280
|
except:
|
|
1134
1281
|
metadata['model_config'] = None
|
|
1135
1282
|
|
|
1283
|
+
# Default handling for unknown frameworks
|
|
1284
|
+
else:
|
|
1285
|
+
if os.environ.get("AIMODELSHARE_DEBUG_METADATA"):
|
|
1286
|
+
print(f"[DEBUG] _get_leaderboard_data: Unknown framework '{metadata_raw.get('ml_framework')}', using defaults")
|
|
1287
|
+
metadata.setdefault('depth', 0)
|
|
1288
|
+
metadata.setdefault('num_params', 0)
|
|
1289
|
+
for i in layer_list:
|
|
1290
|
+
metadata.setdefault(i.lower()+'_layers', 0)
|
|
1291
|
+
for i in activation_list:
|
|
1292
|
+
metadata.setdefault(i.lower()+'_act', 0)
|
|
1293
|
+
|
|
1136
1294
|
return metadata
|
|
1137
1295
|
|
|
1138
1296
|
|
|
@@ -1553,7 +1711,8 @@ def _get_sklearn_modules():
|
|
|
1553
1711
|
|
|
1554
1712
|
sklearn_modules = ['ensemble', 'gaussian_process', 'isotonic',
|
|
1555
1713
|
'linear_model', 'mixture', 'multiclass', 'naive_bayes',
|
|
1556
|
-
'neighbors', 'neural_network', 'svm', 'tree'
|
|
1714
|
+
'neighbors', 'neural_network', 'svm', 'tree',
|
|
1715
|
+
'discriminant_analysis', 'calibration']
|
|
1557
1716
|
|
|
1558
1717
|
models_modules_dict = {}
|
|
1559
1718
|
|
|
@@ -1569,9 +1728,31 @@ def _get_sklearn_modules():
|
|
|
1569
1728
|
|
|
1570
1729
|
def model_from_string(model_type):
|
|
1571
1730
|
models_modules_dict = _get_sklearn_modules()
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1731
|
+
try:
|
|
1732
|
+
module = models_modules_dict[model_type]
|
|
1733
|
+
model_class = getattr(importlib.import_module(module), model_type)
|
|
1734
|
+
return model_class
|
|
1735
|
+
except KeyError:
|
|
1736
|
+
# Return a placeholder class if estimator not found
|
|
1737
|
+
import warnings
|
|
1738
|
+
warnings.warn(f"Model type '{model_type}' not found in sklearn modules. Returning placeholder class.")
|
|
1739
|
+
|
|
1740
|
+
# Create a minimal placeholder class that can be instantiated
|
|
1741
|
+
class PlaceholderModel:
|
|
1742
|
+
def __init__(self, **kwargs):
|
|
1743
|
+
self._model_type = model_type
|
|
1744
|
+
self._params = kwargs
|
|
1745
|
+
|
|
1746
|
+
def get_params(self, deep=True):
|
|
1747
|
+
return self._params
|
|
1748
|
+
|
|
1749
|
+
def __str__(self):
|
|
1750
|
+
return f"PlaceholderModel({self._model_type})"
|
|
1751
|
+
|
|
1752
|
+
def __repr__(self):
|
|
1753
|
+
return f"PlaceholderModel({self._model_type})"
|
|
1754
|
+
|
|
1755
|
+
return PlaceholderModel
|
|
1575
1756
|
|
|
1576
1757
|
def _get_pyspark_modules():
|
|
1577
1758
|
try:
|