aimodelshare 0.3.7__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.
Files changed (171) hide show
  1. aimodelshare/README.md +26 -0
  2. aimodelshare/__init__.py +100 -0
  3. aimodelshare/aimsonnx.py +2381 -0
  4. aimodelshare/api.py +836 -0
  5. aimodelshare/auth.py +163 -0
  6. aimodelshare/aws.py +511 -0
  7. aimodelshare/aws_client.py +173 -0
  8. aimodelshare/base_image.py +154 -0
  9. aimodelshare/bucketpolicy.py +106 -0
  10. aimodelshare/color_mappings/color_mapping_keras.csv +121 -0
  11. aimodelshare/color_mappings/color_mapping_pytorch.csv +117 -0
  12. aimodelshare/containerisation.py +244 -0
  13. aimodelshare/containerization.py +712 -0
  14. aimodelshare/containerization_templates/Dockerfile.txt +8 -0
  15. aimodelshare/containerization_templates/Dockerfile_PySpark.txt +23 -0
  16. aimodelshare/containerization_templates/buildspec.txt +14 -0
  17. aimodelshare/containerization_templates/lambda_function.txt +40 -0
  18. aimodelshare/custom_approach/__init__.py +1 -0
  19. aimodelshare/custom_approach/lambda_function.py +17 -0
  20. aimodelshare/custom_eval_metrics.py +103 -0
  21. aimodelshare/data_sharing/__init__.py +0 -0
  22. aimodelshare/data_sharing/data_sharing_templates/Dockerfile.txt +3 -0
  23. aimodelshare/data_sharing/data_sharing_templates/__init__.py +1 -0
  24. aimodelshare/data_sharing/data_sharing_templates/buildspec.txt +15 -0
  25. aimodelshare/data_sharing/data_sharing_templates/codebuild_policies.txt +129 -0
  26. aimodelshare/data_sharing/data_sharing_templates/codebuild_trust_relationship.txt +12 -0
  27. aimodelshare/data_sharing/download_data.py +620 -0
  28. aimodelshare/data_sharing/share_data.py +373 -0
  29. aimodelshare/data_sharing/utils.py +8 -0
  30. aimodelshare/deploy_custom_lambda.py +246 -0
  31. aimodelshare/documentation/Makefile +20 -0
  32. aimodelshare/documentation/karma_sphinx_theme/__init__.py +28 -0
  33. aimodelshare/documentation/karma_sphinx_theme/_version.py +2 -0
  34. aimodelshare/documentation/karma_sphinx_theme/breadcrumbs.html +70 -0
  35. aimodelshare/documentation/karma_sphinx_theme/layout.html +172 -0
  36. aimodelshare/documentation/karma_sphinx_theme/search.html +50 -0
  37. aimodelshare/documentation/karma_sphinx_theme/searchbox.html +14 -0
  38. aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css +2 -0
  39. aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css.map +1 -0
  40. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css +2751 -0
  41. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css.map +1 -0
  42. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css +2 -0
  43. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css.map +1 -0
  44. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.eot +0 -0
  45. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.svg +32 -0
  46. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.ttf +0 -0
  47. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff +0 -0
  48. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff2 +0 -0
  49. aimodelshare/documentation/karma_sphinx_theme/static/js/theme.js +68 -0
  50. aimodelshare/documentation/karma_sphinx_theme/theme.conf +9 -0
  51. aimodelshare/documentation/make.bat +35 -0
  52. aimodelshare/documentation/requirements.txt +2 -0
  53. aimodelshare/documentation/source/about.rst +18 -0
  54. aimodelshare/documentation/source/advanced_features.rst +137 -0
  55. aimodelshare/documentation/source/competition.rst +218 -0
  56. aimodelshare/documentation/source/conf.py +58 -0
  57. aimodelshare/documentation/source/create_credentials.rst +86 -0
  58. aimodelshare/documentation/source/example_notebooks.rst +132 -0
  59. aimodelshare/documentation/source/functions.rst +151 -0
  60. aimodelshare/documentation/source/gettingstarted.rst +390 -0
  61. aimodelshare/documentation/source/images/creds1.png +0 -0
  62. aimodelshare/documentation/source/images/creds2.png +0 -0
  63. aimodelshare/documentation/source/images/creds3.png +0 -0
  64. aimodelshare/documentation/source/images/creds4.png +0 -0
  65. aimodelshare/documentation/source/images/creds5.png +0 -0
  66. aimodelshare/documentation/source/images/creds_file_example.png +0 -0
  67. aimodelshare/documentation/source/images/predict_tab.png +0 -0
  68. aimodelshare/documentation/source/index.rst +110 -0
  69. aimodelshare/documentation/source/modelplayground.rst +132 -0
  70. aimodelshare/exceptions.py +11 -0
  71. aimodelshare/generatemodelapi.py +1270 -0
  72. aimodelshare/iam/codebuild_policy.txt +129 -0
  73. aimodelshare/iam/codebuild_trust_relationship.txt +12 -0
  74. aimodelshare/iam/lambda_policy.txt +15 -0
  75. aimodelshare/iam/lambda_trust_relationship.txt +12 -0
  76. aimodelshare/json_templates/__init__.py +1 -0
  77. aimodelshare/json_templates/api_json.txt +155 -0
  78. aimodelshare/json_templates/auth/policy.txt +1 -0
  79. aimodelshare/json_templates/auth/role.txt +1 -0
  80. aimodelshare/json_templates/eval/policy.txt +1 -0
  81. aimodelshare/json_templates/eval/role.txt +1 -0
  82. aimodelshare/json_templates/function/policy.txt +1 -0
  83. aimodelshare/json_templates/function/role.txt +1 -0
  84. aimodelshare/json_templates/integration_response.txt +5 -0
  85. aimodelshare/json_templates/lambda_policy_1.txt +15 -0
  86. aimodelshare/json_templates/lambda_policy_2.txt +8 -0
  87. aimodelshare/json_templates/lambda_role_1.txt +12 -0
  88. aimodelshare/json_templates/lambda_role_2.txt +16 -0
  89. aimodelshare/leaderboard.py +174 -0
  90. aimodelshare/main/1.txt +132 -0
  91. aimodelshare/main/1B.txt +112 -0
  92. aimodelshare/main/2.txt +153 -0
  93. aimodelshare/main/3.txt +134 -0
  94. aimodelshare/main/4.txt +128 -0
  95. aimodelshare/main/5.txt +109 -0
  96. aimodelshare/main/6.txt +105 -0
  97. aimodelshare/main/7.txt +144 -0
  98. aimodelshare/main/8.txt +142 -0
  99. aimodelshare/main/__init__.py +1 -0
  100. aimodelshare/main/authorization.txt +275 -0
  101. aimodelshare/main/eval_classification.txt +79 -0
  102. aimodelshare/main/eval_lambda.txt +1709 -0
  103. aimodelshare/main/eval_regression.txt +80 -0
  104. aimodelshare/main/lambda_function.txt +8 -0
  105. aimodelshare/main/nst.txt +149 -0
  106. aimodelshare/model.py +1543 -0
  107. aimodelshare/modeluser.py +215 -0
  108. aimodelshare/moral_compass/README.md +408 -0
  109. aimodelshare/moral_compass/__init__.py +65 -0
  110. aimodelshare/moral_compass/_version.py +3 -0
  111. aimodelshare/moral_compass/api_client.py +601 -0
  112. aimodelshare/moral_compass/apps/__init__.py +69 -0
  113. aimodelshare/moral_compass/apps/ai_consequences.py +540 -0
  114. aimodelshare/moral_compass/apps/bias_detective.py +714 -0
  115. aimodelshare/moral_compass/apps/ethical_revelation.py +898 -0
  116. aimodelshare/moral_compass/apps/fairness_fixer.py +889 -0
  117. aimodelshare/moral_compass/apps/judge.py +888 -0
  118. aimodelshare/moral_compass/apps/justice_equity_upgrade.py +853 -0
  119. aimodelshare/moral_compass/apps/mc_integration_helpers.py +820 -0
  120. aimodelshare/moral_compass/apps/model_building_game.py +1104 -0
  121. aimodelshare/moral_compass/apps/model_building_game_beginner.py +687 -0
  122. aimodelshare/moral_compass/apps/moral_compass_challenge.py +858 -0
  123. aimodelshare/moral_compass/apps/session_auth.py +254 -0
  124. aimodelshare/moral_compass/apps/shared_activity_styles.css +349 -0
  125. aimodelshare/moral_compass/apps/tutorial.py +481 -0
  126. aimodelshare/moral_compass/apps/what_is_ai.py +853 -0
  127. aimodelshare/moral_compass/challenge.py +365 -0
  128. aimodelshare/moral_compass/config.py +187 -0
  129. aimodelshare/placeholders/model.onnx +0 -0
  130. aimodelshare/placeholders/preprocessor.zip +0 -0
  131. aimodelshare/playground.py +1968 -0
  132. aimodelshare/postprocessormodules.py +157 -0
  133. aimodelshare/preprocessormodules.py +373 -0
  134. aimodelshare/pyspark/1.txt +195 -0
  135. aimodelshare/pyspark/1B.txt +181 -0
  136. aimodelshare/pyspark/2.txt +220 -0
  137. aimodelshare/pyspark/3.txt +204 -0
  138. aimodelshare/pyspark/4.txt +187 -0
  139. aimodelshare/pyspark/5.txt +178 -0
  140. aimodelshare/pyspark/6.txt +174 -0
  141. aimodelshare/pyspark/7.txt +211 -0
  142. aimodelshare/pyspark/8.txt +206 -0
  143. aimodelshare/pyspark/__init__.py +1 -0
  144. aimodelshare/pyspark/authorization.txt +258 -0
  145. aimodelshare/pyspark/eval_classification.txt +79 -0
  146. aimodelshare/pyspark/eval_lambda.txt +1441 -0
  147. aimodelshare/pyspark/eval_regression.txt +80 -0
  148. aimodelshare/pyspark/lambda_function.txt +8 -0
  149. aimodelshare/pyspark/nst.txt +213 -0
  150. aimodelshare/python/my_preprocessor.py +58 -0
  151. aimodelshare/readme.md +26 -0
  152. aimodelshare/reproducibility.py +181 -0
  153. aimodelshare/sam/Dockerfile.txt +8 -0
  154. aimodelshare/sam/Dockerfile_PySpark.txt +24 -0
  155. aimodelshare/sam/__init__.py +1 -0
  156. aimodelshare/sam/buildspec.txt +11 -0
  157. aimodelshare/sam/codebuild_policies.txt +129 -0
  158. aimodelshare/sam/codebuild_trust_relationship.txt +12 -0
  159. aimodelshare/sam/codepipeline_policies.txt +173 -0
  160. aimodelshare/sam/codepipeline_trust_relationship.txt +12 -0
  161. aimodelshare/sam/spark-class.txt +2 -0
  162. aimodelshare/sam/template.txt +54 -0
  163. aimodelshare/tools.py +103 -0
  164. aimodelshare/utils/__init__.py +78 -0
  165. aimodelshare/utils/optional_deps.py +38 -0
  166. aimodelshare/utils.py +57 -0
  167. aimodelshare-0.3.7.dist-info/METADATA +298 -0
  168. aimodelshare-0.3.7.dist-info/RECORD +171 -0
  169. aimodelshare-0.3.7.dist-info/WHEEL +5 -0
  170. aimodelshare-0.3.7.dist-info/licenses/LICENSE +5 -0
  171. aimodelshare-0.3.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1270 @@
1
+ import email
2
+ import boto3
3
+ import botocore
4
+ import os
5
+ import jwt
6
+ from numpy.core.fromnumeric import var
7
+ import requests
8
+ import uuid
9
+ import json
10
+ import math
11
+ import time
12
+ import datetime
13
+ import onnx
14
+ import tempfile
15
+ import types
16
+ import sys
17
+ import base64
18
+ import mimetypes
19
+ import numpy as np
20
+ import pandas as pd
21
+ from aimodelshare.tools import extract_varnames_fromtrainingdata, _get_extension_from_filepath
22
+ from aimodelshare.aws import get_s3_iam_client, run_function_on_lambda, get_token, get_aws_token, get_aws_client
23
+ from aimodelshare.bucketpolicy import _custom_upload_policy
24
+ from aimodelshare.exceptions import AuthorizationError, AWSAccessError, AWSUploadError
25
+ from aimodelshare.api import get_api_json
26
+ from aimodelshare.modeluser import decode_token_unverified
27
+ from aimodelshare.preprocessormodules import upload_preprocessor
28
+ from aimodelshare.model import _get_predictionmodel_key, _extract_model_metadata
29
+ from aimodelshare.data_sharing.share_data import share_data_codebuild
30
+ from aimodelshare.aimsonnx import _get_metadata
31
+ from aimodelshare.utils import HiddenPrints
32
+
33
+ def take_user_info_and_generate_api(model_filepath, model_type, categorical,labels, preprocessor_filepath,
34
+ custom_libraries, requirements, exampledata_json_filepath, repo_name,
35
+ image_tag, reproducibility_env_filepath, memory, timeout, pyspark_support=False):
36
+ """
37
+ Generates an api using model parameters and user credentials, from the user
38
+
39
+ -----------
40
+ Parameters
41
+
42
+ model_filepath : string ends with '.onnx'
43
+ value - Absolute path to model file [REQUIRED] to be set by the user
44
+ .onnx is the only accepted model file extension
45
+ "example_model.onnx" filename for file in directory.
46
+ "/User/xyz/model/example_model.onnx" absolute path to model file from local directory
47
+ preprocessor_filepath: string
48
+ value - absolute path to preprocessor file
49
+ [REQUIRED] to be set by the user
50
+ "./preprocessor.zip"
51
+ searches for an exported zip preprocessor file in the current directory
52
+ file is generated using export_preprocessor function from the AI Modelshare library
53
+ model_type : string
54
+ values - [ 'text' , 'image' , 'tabular' , 'timeseries' ]
55
+ Type of model data
56
+ categorical: string
57
+ "TRUE" if model is Classification, categorical type
58
+ "FALSE" if model is continuous, Regression type
59
+ labels: list
60
+ value - labels for training data
61
+ can be extracted from columns of y train or can be provided by the user
62
+ custom_libraries: string
63
+ "TRUE" if user wants to load custom Python libraries to their prediction runtime
64
+ "FALSE" if user wishes to use AI Model Share base libraries including latest versions of most common ML libs.
65
+
66
+
67
+ -----------
68
+ Returns
69
+ finalresult : list
70
+ length 5
71
+ [ api url, api status code,
72
+ start time for api generation,
73
+ model unique key,
74
+ s3 bucket name
75
+ ]
76
+
77
+ """
78
+ import tempfile
79
+ from zipfile import ZipFile
80
+ import os
81
+ import random
82
+
83
+ # create temporary folder
84
+ temp_dir = tempfile.gettempdir()
85
+
86
+ api_json= get_api_json()
87
+ user_client = boto3.client('apigateway', aws_access_key_id=str(
88
+ os.environ.get("AWS_ACCESS_KEY_ID_AIMS")), aws_secret_access_key=str(os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS")), region_name=str(os.environ.get("AWS_REGION_AIMS")))
89
+
90
+ response2 = user_client.import_rest_api(
91
+ failOnWarnings=True,
92
+ parameters={
93
+ 'endpointConfigurationTypes': 'REGIONAL'
94
+ },
95
+ body=api_json
96
+ )
97
+
98
+ api_id = response2['id']
99
+ now = datetime.datetime.now()
100
+ s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"), os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"), os.environ.get("AWS_REGION_AIMS"))
101
+
102
+ def create_bucket(s3_client, bucket_name, region):
103
+ try:
104
+ response=s3_client.head_bucket(Bucket=bucket_name)
105
+ except:
106
+ if(region=="us-east-1"):
107
+ response = s3_client.create_bucket(
108
+ ACL="private",
109
+ Bucket=bucket_name
110
+ )
111
+ else:
112
+ location={'LocationConstraint': region}
113
+ response=s3_client.create_bucket(
114
+ ACL="private",
115
+ Bucket=bucket_name,
116
+ CreateBucketConfiguration=location
117
+ )
118
+ return response
119
+
120
+ create_bucket(s3['client'], os.environ.get("BUCKET_NAME"), region)
121
+
122
+ # model upload
123
+ if isinstance(model_filepath, onnx.ModelProto):
124
+ model = model_filepath
125
+ temp_prep=tempfile.mkdtemp()
126
+ Filepath = temp_prep+"/model.onnx"
127
+ with open(Filepath, "wb") as f:
128
+ f.write(model.SerializeToString())
129
+ else:
130
+ Filepath = model_filepath
131
+ model = onnx.load(model_filepath)
132
+ metadata = _extract_model_metadata(model)
133
+ input_shape = metadata["input_shape"]
134
+ #tab_imports ='./tabular_imports.pkl'
135
+ #img_imports ='./image_imports.pkl'
136
+ file_extension = _get_extension_from_filepath(Filepath)
137
+ unique_model_id = str(api_id)
138
+ file_key, versionfile_key = _get_predictionmodel_key(
139
+ unique_model_id, file_extension)
140
+ try:
141
+ s3["client"].upload_file(exampledata_json_filepath, os.environ.get("BUCKET_NAME"), unique_model_id + "/exampledata.json")
142
+ except:
143
+ pass
144
+ try:
145
+ s3["client"].upload_file(Filepath, os.environ.get("BUCKET_NAME"), file_key)
146
+ s3["client"].upload_file(Filepath, os.environ.get("BUCKET_NAME"), versionfile_key)
147
+
148
+ # preprocessor upload
149
+ #s3["client"].upload_file(tab_imports, os.environ.get("BUCKET_NAME"), 'tabular_imports.pkl')
150
+ #s3["client"].upload_file(img_imports, os.environ.get("BUCKET_NAME"), 'image_imports.pkl')
151
+ # preprocessor upload
152
+
153
+ # ADD model/Preprocessor VERSION
154
+ if isinstance(preprocessor_filepath, types.FunctionType):
155
+ from aimodelshare.preprocessormodules import export_preprocessor
156
+ temp_prep=tempfile.mkdtemp()
157
+ with HiddenPrints():
158
+ export_preprocessor(preprocessor_filepath,temp_prep)
159
+ preprocessor_filepath = temp_prep+"/preprocessor.zip"
160
+ response = upload_preprocessor(
161
+ preprocessor_filepath, s3, os.environ.get("BUCKET_NAME"), unique_model_id, 1)
162
+ preprocessor_file_extension = _get_extension_from_filepath(
163
+ preprocessor_filepath)
164
+ # write runtime JSON
165
+ json_path = os.path.join(temp_dir, "runtime_data.json")
166
+ if(preprocessor_file_extension == '.py'):
167
+ runtime_preprocessor_type = "module"
168
+ elif(preprocessor_file_extension == '.pkl'):
169
+ runtime_preprocessor_type = "pickle object"
170
+ else:
171
+ runtime_preprocessor_type = "others"
172
+ runtime_data = {}
173
+ runtime_data["runtime_model"] = {"name": "runtime_model.onnx"}
174
+ runtime_data["runtime_preprocessor"] = runtime_preprocessor_type
175
+
176
+ #runtime_data = {"runtime_model": {"name": "runtime_model.onnx"},"runtime_preprocessor": runtime_preprocessor_type }
177
+ json_string = json.dumps(runtime_data, sort_keys=False)
178
+ with open(json_path, 'w') as outfile:
179
+ outfile.write(json_string)
180
+ s3["client"].upload_file(
181
+ json_path, os.environ.get("BUCKET_NAME"), unique_model_id + "/runtime_data.json"
182
+ )
183
+ os.remove(json_path)
184
+
185
+ # upload model metadata
186
+ upload_model_metadata(model, s3, os.environ.get("BUCKET_NAME"), unique_model_id)
187
+
188
+ # upload reproducibility env
189
+ if reproducibility_env_filepath:
190
+ upload_reproducibility_env(reproducibility_env_filepath, s3, os.environ.get("BUCKET_NAME"), unique_model_id)
191
+ except Exception as err:
192
+ raise AWSUploadError(
193
+ "There was a problem with model/preprocessor upload. "+str(err))
194
+
195
+ #Delete Legacy exampledata json:
196
+ try:
197
+ os.remove(exampledata_json_filepath)
198
+ except:
199
+ pass
200
+
201
+ #headers = {'content-type': 'application/json'}
202
+
203
+ ### Progress Update #2/6 {{{
204
+ sys.stdout.write('\r')
205
+ sys.stdout.write("[======== ] Progress: 30% - Building lambda functions and updating permissions...")
206
+ sys.stdout.flush()
207
+ # }}}
208
+
209
+ from aimodelshare.api import create_prediction_api
210
+ apiurl = create_prediction_api(model_filepath, unique_model_id,
211
+ model_type, categorical, labels,api_id,
212
+ custom_libraries, requirements, repo_name,
213
+ image_tag, memory, timeout, pyspark_support=pyspark_support)
214
+
215
+ finalresult = [apiurl["body"], apiurl["statusCode"],
216
+ now, unique_model_id, os.environ.get("BUCKET_NAME"), input_shape]
217
+ return finalresult
218
+
219
+ def upload_reproducibility_env(reproducibility_env_file, s3, bucket, model_id):
220
+ # Check the reproducibility_env {{{
221
+ with open(reproducibility_env_file) as json_file:
222
+ reproducibility_env = json.load(json_file)
223
+ if "global_seed_code" not in reproducibility_env \
224
+ or "local_seed_code" not in reproducibility_env \
225
+ or "gpu_cpu_parallelism_ops" not in reproducibility_env \
226
+ or "session_runtime_info" not in reproducibility_env:
227
+ raise Exception("reproducibility environment is not complete")
228
+
229
+ # Upload the json {{{
230
+ try:
231
+ s3["client"].upload_file(
232
+ reproducibility_env_file, bucket, model_id + "/runtime_reproducibility.json"
233
+ )
234
+ except Exception as err:
235
+ raise err
236
+ # }}}
237
+
238
+ def upload_model_metadata(model, s3, bucket, model_id):
239
+ meta_dict = _get_metadata(model)
240
+ model_metadata = {
241
+ "model_config": meta_dict["model_config"],
242
+ "ml_framework": meta_dict["ml_framework"],
243
+ "model_type": meta_dict["model_type"]
244
+ }
245
+
246
+ temp = tempfile.mkdtemp()
247
+ model_metadata_path = temp + "/" + 'model_metadata.json'
248
+ with open(model_metadata_path, 'w') as outfile:
249
+ json.dump(model_metadata, outfile)
250
+
251
+ # Upload the json {{{
252
+ try:
253
+ s3["client"].upload_file(
254
+ model_metadata_path, bucket, model_id + "/runtime_metadata.json"
255
+ )
256
+ except Exception as err:
257
+ raise err
258
+
259
+ def send_model_data_to_dyndb_and_return_api(api_info, private, categorical, preprocessor_filepath,
260
+ aishare_modelname, aishare_modeldescription, aishare_modelevaluation, model_type,
261
+ aishare_tags, aishare_apicalls, exampledata_json_filepath,
262
+ variablename_and_type_data="default", email_list=[]):
263
+ """
264
+ Updates dynamodb with model data taken as input from user along with already generated api info
265
+ -----------
266
+ Parameters
267
+ api_info : list
268
+ length 5
269
+ api and s3 bucket information
270
+ returned from take_user_info_and_generate_api function
271
+ private : string, default="FALSE"
272
+ TRUE if model and its corresponding data is not public
273
+ FALSE if model and its corresponding data is public
274
+ categorical: string, default="TRUE"
275
+ TRUE if model is of Classification type with categorical variables
276
+ FALSE if model is Regression type with continuous variables
277
+ preprocessor_filepath: string
278
+ value - Absolute path to preprocessor file [REQUIRED] to be set by the user
279
+ "./preprocessor.zip"
280
+ searches for an exported zip preprocessor file in the current directory
281
+ variablename_and_type_data : list, default='default'
282
+ value- extracted from example_data
283
+ [variable types,variable columns]
284
+ 'default' when training data info is not available to extract columns
285
+ email_list: list of strings
286
+ values - list including all emails of users who have access the playground.
287
+ list should contain same emails used to sign up for modelshare.ai account.
288
+ [OPTIONAL] to be set by the playground owner
289
+ -----------
290
+ Results
291
+ print (api_info) : statements with the generated live prediction API information for the user
292
+
293
+ """
294
+
295
+ # unpack user credentials
296
+ unique_model_id = api_info[3]
297
+ bucket_name = api_info[4]
298
+ input_shape = api_info[5]
299
+ if variablename_and_type_data == "default":
300
+ variablename_and_type_data = ["", ""]
301
+
302
+ # needs to use double backslashes and have full filepath
303
+
304
+ if isinstance(preprocessor_filepath, types.FunctionType):
305
+ from aimodelshare.preprocessormodules import export_preprocessor
306
+ temp_prep=tempfile.mkdtemp()
307
+ with HiddenPrints():
308
+ export_preprocessor(preprocessor_filepath,temp_prep)
309
+ preprocessor_filepath = temp_prep+"/preprocessor.zip"
310
+ preprocessor_file_extension = _get_extension_from_filepath(
311
+ preprocessor_filepath)
312
+ if exampledata_json_filepath!="":
313
+ exampledata_addtodatabase={"exampledata":"TRUE"}
314
+ else:
315
+ exampledata_addtodatabase={"exampledata":"FALSE"}
316
+ bodydata = {
317
+ "id": int(math.log(1/((time.time()*1000000)))*100000000000000),
318
+ "unique_model_id": unique_model_id,
319
+ "apideveloper": os.environ.get("username"), # change this to first and last name
320
+ "apimodeldescription": aishare_modeldescription,
321
+ "apimodelevaluation": aishare_modelevaluation,
322
+ "apimodeltype": model_type,
323
+ # getting rid of extra quotes that screw up dynamodb string search on apiurls
324
+ "apiurl": api_info[0].strip('\"'),
325
+ "bucket_name": bucket_name,
326
+ "version": 1,
327
+ "modelname": aishare_modelname,
328
+ "tags": aishare_tags,
329
+ "Private": private,
330
+ "Categorical": categorical,
331
+ "delete": "FALSE",
332
+ "input_feature_dtypes": variablename_and_type_data[0],
333
+ "input_feature_names": variablename_and_type_data[1],
334
+ "preprocessor": preprocessor_filepath,
335
+ "preprocessor_fileextension": preprocessor_file_extension,
336
+ "input_shape": input_shape,
337
+ "email_list": email_list,
338
+ "useremails": ','.join(email_list),
339
+ }
340
+ bodydata.update(exampledata_addtodatabase)
341
+ # Get the response
342
+ headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
343
+ 'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
344
+ # modeltoapi lambda function invoked through below url to return new prediction api in response
345
+ response = requests.post("https://bhrdesksak.execute-api.us-east-1.amazonaws.com/dev/modeldata",
346
+ json=bodydata, headers=headers_with_authentication)
347
+ response_string = response.text
348
+ response_string = response_string[1:-1]
349
+ import json
350
+ response_stringfinal = json.loads(response_string).get("id","no id")
351
+ # Build output {{{
352
+ final_message = ("\nYou can now use your Model Playground.\n\n"
353
+ "Follow this link to explore your Model Playground's functionality\n"
354
+ "You can make predictions with the Dashboard and access example code from the Programmatic tab.\n")
355
+ web_dashboard_url = ("https://www.modelshare.ai/detail/"+ response_stringfinal)
356
+
357
+ start = api_info[2]
358
+ end = datetime.datetime.now()
359
+ difference = (end - start).total_seconds()
360
+ finalresult2 = "Success! Your Model Playground was created in " + \
361
+ str(int(difference)) + " seconds. \n" + " Playground Url: " + api_info[0]
362
+
363
+
364
+ # }}}
365
+
366
+ ### Progress Update #6/6 {{{
367
+ sys.stdout.write('\r')
368
+ sys.stdout.write("[=====================================] Progress: 100% - Complete! ")
369
+ sys.stdout.flush()
370
+ # }}}
371
+
372
+ return print("\n\n" + finalresult2 + "\n" + final_message + web_dashboard_url)
373
+
374
+
375
+ def model_to_api(model_filepath, model_type, private, categorical, y_train, preprocessor_filepath,
376
+ custom_libraries="FALSE", example_data=None, image="",
377
+ base_image_api_endpoint="https://vupwujn586.execute-api.us-east-1.amazonaws.com/dev/copybasetouseracct",
378
+ update=False, reproducibility_env_filepath=None, memory=None, timeout=None, email_list=[],pyspark_support=False,
379
+ input_dict=None, print_output=True, playground_id=False):
380
+ """
381
+ Launches a live prediction REST API for deploying ML models using model parameters and user credentials, provided by the user
382
+ Inputs : 8
383
+ Output : model launched to an API
384
+ detaled API info printed out
385
+
386
+ -----------
387
+ Parameters
388
+
389
+ model_filepath : string ends with '.onnx'
390
+ value - Absolute path to model file
391
+ [REQUIRED] to be set by the user
392
+ .onnx is the only accepted model file extension
393
+ "example_model.onnx" filename for file in directory.
394
+ "/User/xyz/model/example_model.onnx" absolute path to model file from local directory
395
+ preprocessor_filepath: string
396
+ value - absolute path to preprocessor file
397
+ [REQUIRED] to be set by the user
398
+ "./preprocessor.zip"
399
+ searches for an exported zip preprocessor file in the current directory
400
+ file is generated using export_preprocessor function from the AI Modelshare library
401
+ model_type : string
402
+ values - [ 'Text' , 'Image' , 'Tabular' , 'Timeseries' ]
403
+ type of model data
404
+ categorical: bool, default=True
405
+ True [DEFAULT] if model is of Classification type with categorical target variables
406
+ False if model is of Regression type with continuous target variables
407
+ y_train : training labels of size of dataset
408
+ value - y values for model
409
+ [REQUIRED] for classification type models
410
+ expects a one hot encoded y train data format
411
+ private : bool, default = False
412
+ True if model and its corresponding data is not public
413
+ False [DEFAULT] if model and its corresponding data is public
414
+ custom_libraries: string
415
+ "TRUE" if user wants to load custom Python libraries to their prediction runtime
416
+ "FALSE" if user wishes to use AI Model Share base libraries including latest versions of most common ML libs.
417
+ example_data: pandas DataFrame (for tabular & text data) OR filepath as string (image, audio, video data)
418
+ tabular data - pandas DataFrame in same structure expected by preprocessor function
419
+ other data types - absolute path to folder containing example data
420
+ (first five files with relevent file extensions will be accepted)
421
+ [REQUIRED] for tabular data
422
+ reproducibility_env_filepath: string
423
+ value - absolute path to environment environment json file
424
+ [OPTIONAL] to be set by the user
425
+ "./reproducibility.json"
426
+ file is generated using export_reproducibility_env function from the AI Modelshare library
427
+ email_list: list of strings
428
+ values - list including all emails of users who have access the playground.
429
+ list should contain same emails used to sign up for modelshare.ai account.
430
+ [OPTIONAL] to be set by the playground owner
431
+ -----------
432
+ Returns
433
+ print_api_info : prints statements with generated live prediction API details
434
+ also prints steps to update the model submissions by the user/team
435
+
436
+
437
+ """
438
+ # Used 2 python functions :
439
+ # 1. take_user_info_and_generate_api : to upload model/preprocessor and generate an api for model submitted by user
440
+ # 2. send_model_data_to_dyndb_and_return_api : to add new record to database with user data, model and api related information
441
+
442
+
443
+ # Get user inputs, pass to other functions {{{
444
+ user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
445
+ aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
446
+ region_name=os.environ.get("AWS_REGION_AIMS"))
447
+
448
+ if all([isinstance(email_list, list)]):
449
+ idtoken = get_aws_token()
450
+ decoded = decode_token_unverified(idtoken)
451
+
452
+ email=None
453
+ email = decoded['email']
454
+ # Owner has to be the first on the list
455
+ email_list.insert(0, email)
456
+ if any([private==False,private==None]):
457
+ email_list=["publicaimsplayground"]
458
+ else:
459
+ pass
460
+ else:
461
+ return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized playground users formatted as strings.")
462
+
463
+ if(image!=""):
464
+ repo_name, image_tag = image.split(':')
465
+ elif model_type=="tabular":
466
+ repo_name, image_tag = "aimodelshare_base_image", "tabular"
467
+ elif model_type=="text":
468
+ repo_name, image_tag = "aimodelshare_base_image", "texttest"
469
+ elif model_type=="image":
470
+ repo_name, image_tag = "aimodelshare_base_image", "v3"
471
+ elif model_type=="video":
472
+ repo_name, image_tag = "aimodelshare_base_image", "v3"
473
+ else:
474
+ repo_name, image_tag = "aimodelshare_base_image", "v3"
475
+
476
+ # Pyspark mode
477
+ if pyspark_support:
478
+ repo_name, image_tag = "aimodelshare_base_image", "pyspark"
479
+
480
+ from aimodelshare.containerization import clone_base_image
481
+ response = clone_base_image(user_session, repo_name, image_tag, "517169013426", base_image_api_endpoint, update)
482
+ if(response["Status"]==0):
483
+ print(response["Success"])
484
+ return
485
+
486
+ if input_dict == None:
487
+ print("We need some information about your model before we can build your REST API and interactive Model Playground.")
488
+ print(" ")
489
+
490
+ requirements = ""
491
+ if(any([custom_libraries=='TRUE',custom_libraries=='true'])):
492
+ requirements = input("Enter all required Python libraries you need at prediction runtime (separated with commas):")
493
+ #_confirm_libraries_exist(requirements)
494
+
495
+ aishare_modelname = input("Model Name (for AI Model Share Website):")
496
+ aishare_modeldescription = input("Model Description (Explain what your model does and \n why end-users would find your model useful):")
497
+
498
+ aishare_tags = input(
499
+ "Model Key Words (Search categories that describe your model, separated with commas):")
500
+ print(" ")
501
+ # }}}
502
+ else:
503
+ requirements = input_dict.get("requirements","")
504
+ aishare_modelname = input_dict.get("model_name","")
505
+ aishare_modeldescription = input_dict.get("model_description","")
506
+ aishare_tags = input_dict.get("tags","")
507
+
508
+ aishare_modelevaluation = "unverified" # verified metrics added to playground once 1. a model is submitted to a competition leaderboard and 2. playground owner updates runtime
509
+ #...model with update_runtime_model()
510
+ aishare_apicalls = 0
511
+
512
+ # Force user to provide example data for tabular models {{{
513
+ if any([model_type.lower() == "tabular", model_type.lower() == "timeseries"]):
514
+ if not isinstance(example_data, pd.DataFrame):
515
+ return print("Error: Example data is required for tabular models. \n Please provide a pandas DataFrame with a sample of your X data (in the format expected by your preprocessor) and try again.")
516
+ else:
517
+ pass
518
+ #}}}
519
+
520
+ print("Creating your prediction API. (This process may take several minutes.)\n")
521
+ variablename_and_type_data = None
522
+ private = str(private).upper()
523
+ categorical = str(categorical).upper()
524
+ if model_type == "tabular" or "keras_tabular" or 'Tabular':
525
+ variablename_and_type_data = extract_varnames_fromtrainingdata(
526
+ example_data)
527
+ if categorical == "TRUE":
528
+ try:
529
+ labels = y_train.columns.tolist()
530
+ except:
531
+ #labels = list(set(y_train.to_frame()['tags'].tolist()))
532
+ labels = list(set(y_train))
533
+ else:
534
+ labels = "no data"
535
+ temp_dir = tempfile.gettempdir()
536
+ # Create Example Data JSON
537
+ exampledata_json_filepath = ""
538
+ if example_data is not None:
539
+ _create_exampledata_json(model_type, example_data)
540
+ exampledata_json_filepath = temp_dir+ "/exampledata.json"
541
+
542
+ ### Progress Update #1/6 {{{
543
+ sys.stdout.write("[=== ] Progress: 5% - Accessing Amazon Web Services, uploading resources...")
544
+ sys.stdout.flush()
545
+ # }}}
546
+
547
+ api_info = take_user_info_and_generate_api(
548
+ model_filepath, model_type, categorical, labels,
549
+ preprocessor_filepath, custom_libraries, requirements,
550
+ exampledata_json_filepath, repo_name, image_tag,
551
+ reproducibility_env_filepath, memory, timeout, pyspark_support=pyspark_support)
552
+
553
+ if input_dict and playground_id:
554
+ aishare_modelname = aishare_modelname+" "+str(api_info[3])
555
+
556
+ ### Progress Update #5/6 {{{
557
+ sys.stdout.write('\r')
558
+ sys.stdout.write("[================================= ] Progress: 90% - Finishing web dashboard... ")
559
+ sys.stdout.flush()
560
+ # }}}
561
+
562
+ print_api_info = send_model_data_to_dyndb_and_return_api(
563
+ api_info, private, categorical,preprocessor_filepath, aishare_modelname,
564
+ aishare_modeldescription, aishare_modelevaluation, model_type,
565
+ aishare_tags, aishare_apicalls, exampledata_json_filepath,
566
+ variablename_and_type_data, email_list)
567
+
568
+ return api_info[0]
569
+
570
+ def create_competition(apiurl, data_directory, y_test, eval_metric_filepath=None, email_list=[], public=False, public_private_split=0.5,
571
+ input_dict=None, print_output=True):
572
+ """
573
+ Creates a model competition for a deployed prediction REST API
574
+ Inputs : 4
575
+ Output : Create ML model competition and allow authorized users to submit models to resulting leaderboard/competition
576
+
577
+ ---------
578
+ Parameters
579
+
580
+ apiurl: string
581
+ URL of deployed prediction API
582
+
583
+ y_test : list of y values for test data used to generate metrics from predicted values from X test data submitted via the submit_model() function
584
+ [REQUIRED] to generate eval metrics in competition leaderboard
585
+
586
+ data_directory : folder storing training data and test data (excluding Y test data)
587
+ email_list: [REQUIRED] list of comma separated emails for users who are allowed to submit models to competition. Emails should be strings in a list.
588
+
589
+ ---------
590
+ Returns
591
+ finalmessage : Information such as how to submit models to competition
592
+
593
+ """
594
+ if all([isinstance(email_list, list)]):
595
+ if any([len(email_list)>0, public=="True",public=="TRUE",public==True]):
596
+ idtoken=get_aws_token()
597
+ decoded = decode_token_unverified(idtoken)
598
+
599
+ email=decoded['email']
600
+ email_list.append(email)
601
+ else:
602
+ return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized competition participants formatted as strings.")
603
+
604
+ # create temporary folder
605
+ temp_dir = tempfile.gettempdir()
606
+
607
+ s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"), os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"), os.environ.get("AWS_REGION_AIMS"))
608
+
609
+ # Get bucket and model_id subfolder for user based on apiurl {{{
610
+ response, error = run_function_on_lambda(
611
+ apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
612
+ )
613
+ if error is not None:
614
+ raise error
615
+
616
+ _, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
617
+ # }}}
618
+
619
+ # upload y_test data:
620
+ ytest_path = os.path.join(temp_dir, "ytest.pkl")
621
+ import pickle
622
+ #ytest data to load to s3
623
+
624
+ if y_test is not None:
625
+ if type(y_test) is not list:
626
+ y_test=y_test.tolist()
627
+ else:
628
+ pass
629
+
630
+ if all(isinstance(x, (np.float64)) for x in y_test):
631
+ y_test = [float(i) for i in y_test]
632
+ else:
633
+ pass
634
+
635
+
636
+ pickle.dump(y_test,open(ytest_path,"wb"))
637
+ s3["client"].upload_file(ytest_path, os.environ.get("BUCKET_NAME"), model_id + "/competition/ytest.pkl")
638
+
639
+
640
+ if eval_metric_filepath is not None:
641
+
642
+ if isinstance(eval_metric_filepath, list):
643
+
644
+ for i in eval_metric_filepath:
645
+
646
+ eval_metric_name = i.split('/')[-1]
647
+
648
+ s3["client"].upload_file(i, os.environ.get("BUCKET_NAME"), model_id + '/competition/metrics_' + eval_metric_name)
649
+
650
+ else:
651
+
652
+ eval_metric_name = eval_metric_filepath.split('/')[-1]
653
+
654
+ print(eval_metric_name)
655
+
656
+ s3["client"].upload_file(eval_metric_filepath, os.environ.get("BUCKET_NAME"), model_id + '/competition/metrics_' + eval_metric_name)
657
+
658
+
659
+ # get api_id from apiurl, generate txt file name
660
+ api_url_trim = apiurl.split('https://')[1]
661
+ api_id = api_url_trim.split(".")[0]
662
+
663
+ if input_dict == None:
664
+ print("\n--INPUT COMPETITION DETAILS--\n")
665
+ aishare_competitionname = input("Enter competition name:")
666
+ aishare_competitiondescription = input("Enter competition description:")
667
+
668
+ print("\n--INPUT DATA DETAILS--\n")
669
+ print("Note: (optional) Save an optional LICENSE.txt file in your competition data directory to make users aware of any restrictions on data sharing/usage.\n")
670
+
671
+ aishare_datadescription = input(
672
+ "Enter data description (i.e.- filenames denoting training and test data, file types, and any subfolders where files are stored):")
673
+
674
+ aishare_datalicense = input(
675
+ "Enter optional data license descriptive name (e.g.- 'MIT, Apache 2.0, CC0, Other, etc.'):")
676
+
677
+ else:
678
+ aishare_competitionname = input_dict["competition_name"]
679
+ aishare_competitiondescription = input_dict["competition_description"]
680
+ aishare_datadescription = input_dict["data_description"]
681
+ aishare_datalicense = input_dict["data_license"]
682
+
683
+
684
+ user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
685
+ aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
686
+ region_name=os.environ.get("AWS_REGION_AIMS"))
687
+
688
+ account_number = user_session.client(
689
+ 'sts').get_caller_identity().get('Account')
690
+
691
+ datauri=share_data_codebuild(account_number,os.environ.get("AWS_REGION_AIMS"),data_directory)
692
+
693
+ #create and upload json file with list of authorized users who can submit to this competition.
694
+ _create_competitionuserauth_json(apiurl, email_list,public,datauri['ecr_uri'], submission_type="competition")
695
+ _create_public_private_split_json(apiurl, public_private_split, "competition")
696
+
697
+ bodydata = {"unique_model_id": model_id,
698
+ "bucket_name": api_bucket,
699
+ "apideveloper": os.environ.get("username"), # change this to first and last name
700
+ "competitionname":aishare_competitionname,
701
+ "competitiondescription": aishare_competitiondescription,
702
+ # getting rid of extra quotes that screw up dynamodb string search on apiurls
703
+ "apiurl": apiurl,
704
+ "version": 0,
705
+ "Private": "FALSE",
706
+ "delete": "FALSE",
707
+ 'datadescription':aishare_datadescription,
708
+ 'dataecruri':datauri['ecr_uri'],
709
+ 'datalicense': aishare_datalicense}
710
+
711
+ # Get the response
712
+ headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
713
+ 'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
714
+ # modeltoapi lambda function invoked through below url to return new prediction api in response
715
+ requests.post("https://o35jwfakca.execute-api.us-east-1.amazonaws.com/dev/modeldata",
716
+ json=bodydata, headers=headers_with_authentication)
717
+
718
+
719
+ final_message = ("\n Success! Model competition created. \n\n"
720
+ "You may now update your prediction API runtime model and verify evaluation metrics with the update_runtime_model() function.\n\n"
721
+ "To upload new models and/or preprocessors to this API, team members should use \n"
722
+ "the following credentials:\n\napiurl='" + apiurl+"'"+"\nfrom aimodelshare.aws import set_credentials\nset_credentials(apiurl=apiurl)\n\n"
723
+ "They can then submit models to your competition by using the following code: \n\ncompetition= ai.Competition(apiurl)\n"
724
+ "download_data('"+datauri['ecr_uri']+"') \n"
725
+ "# Use this data to preprocess data and train model. Write and save preprocessor fxn, save model to onnx file, generate predicted y values\n using X test data, then submit a model below.\n\n"
726
+ "competition.submit_model(model_filepath, preprocessor_filepath, prediction_submission_list)")
727
+
728
+ if print_output:
729
+ return print(final_message)
730
+ else:
731
+ return
732
+
733
+
734
+
735
+ def create_experiment(apiurl, data_directory, y_test, eval_metric_filepath=None, email_list=[], public=False, public_private_split=0,
736
+ input_dict=None, print_output=True):
737
+ """
738
+ Creates a model experiment for a deployed prediction REST API
739
+ Inputs : 4
740
+ Output : Create ML model experiment and allow authorized users to submit models to resulting leaderboard/competition
741
+
742
+ ---------
743
+ Parameters
744
+
745
+ apiurl: string
746
+ URL of deployed prediction API
747
+
748
+ y_test : list of y values for test data used to generate metrics from predicted values from X test data submitted via the submit_model() function
749
+ [REQUIRED] to generate eval metrics in competition leaderboard
750
+
751
+ data_directory : folder storing training data and test data (excluding Y test data)
752
+ email_list: [REQUIRED] list of comma separated emails for users who are allowed to submit models to experiment. Emails should be strings in a list.
753
+
754
+ ---------
755
+ Returns
756
+ finalmessage : Information such as how to submit models to competition
757
+
758
+ """
759
+ if all([isinstance(email_list, list)]):
760
+ if any([len(email_list)>0, public=="True",public=="TRUE",public==True]):
761
+ idtoken=get_aws_token()
762
+ decoded = decode_token_unverified(idtoken)
763
+
764
+ email=decoded['email']
765
+ email_list.append(email)
766
+ else:
767
+ return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized competition participants formatted as strings.")
768
+
769
+ # create temporary folder
770
+ temp_dir = tempfile.gettempdir()
771
+
772
+ s3, iam, region = get_s3_iam_client(os.environ.get("AWS_ACCESS_KEY_ID_AIMS"), os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"), os.environ.get("AWS_REGION_AIMS"))
773
+
774
+ # Get bucket and model_id subfolder for user based on apiurl {{{
775
+ response, error = run_function_on_lambda(
776
+ apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
777
+ )
778
+ if error is not None:
779
+ raise error
780
+
781
+ _, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
782
+ # }}}
783
+
784
+ # upload y_test data:
785
+ ytest_path = os.path.join(temp_dir, "ytest.pkl")
786
+ import pickle
787
+ #ytest data to load to s3
788
+
789
+ if y_test is not None:
790
+ if type(y_test) is not list:
791
+ y_test=y_test.tolist()
792
+ else:
793
+ pass
794
+
795
+ if all(isinstance(x, (np.float64)) for x in y_test):
796
+ y_test = [float(i) for i in y_test]
797
+ else:
798
+ pass
799
+
800
+
801
+ pickle.dump(y_test,open(ytest_path,"wb"))
802
+ s3["client"].upload_file(ytest_path, os.environ.get("BUCKET_NAME"), model_id + "/experiment/ytest.pkl")
803
+
804
+
805
+ if eval_metric_filepath is not None:
806
+
807
+ if isinstance(eval_metric_filepath, list):
808
+
809
+ for i in eval_metric_filepath:
810
+
811
+ eval_metric_name = i.split('/')[-1]
812
+
813
+ s3["client"].upload_file(i, os.environ.get("BUCKET_NAME"), model_id + '/experiment/metrics_' + eval_metric_name)
814
+
815
+ else:
816
+
817
+ eval_metric_name = eval_metric_filepath.split('/')[-1]
818
+
819
+ print(eval_metric_name)
820
+
821
+ s3["client"].upload_file(eval_metric_filepath, os.environ.get("BUCKET_NAME"), model_id + '/experiment/metrics_' + eval_metric_name)
822
+
823
+
824
+ # get api_id from apiurl, generate txt file name
825
+ api_url_trim = apiurl.split('https://')[1]
826
+ api_id = api_url_trim.split(".")[0]
827
+
828
+ if input_dict == None:
829
+ print("\n--INPUT EXPERIMENT DETAILS--\n")
830
+ aishare_competitionname = input("Enter experiment name:")
831
+ aishare_competitiondescription = input("Enter experiment description:")
832
+
833
+ print("\n--INPUT DATA DETAILS--\n")
834
+ print("Note: (optional) Save an optional LICENSE.txt file in your experiment data directory to make users aware of any restrictions on data sharing/usage.\n")
835
+
836
+ aishare_datadescription = input(
837
+ "Enter data description (i.e.- filenames denoting training and test data, file types, and any subfolders where files are stored):")
838
+
839
+ aishare_datalicense = input(
840
+ "Enter optional data license descriptive name (e.g.- 'MIT, Apache 2.0, CC0, Other, etc.'):")
841
+
842
+ else:
843
+ aishare_competitionname = input_dict["experiment_name"]
844
+ aishare_competitiondescription = input_dict["experiment_description"]
845
+ aishare_datadescription = input_dict["data_description"]
846
+ aishare_datalicense = input_dict["data_license"]
847
+
848
+
849
+ user_session = boto3.session.Session(aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID_AIMS"),
850
+ aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY_AIMS"),
851
+ region_name=os.environ.get("AWS_REGION_AIMS"))
852
+
853
+ account_number = user_session.client(
854
+ 'sts').get_caller_identity().get('Account')
855
+
856
+ datauri=share_data_codebuild(account_number,os.environ.get("AWS_REGION_AIMS"),data_directory)
857
+
858
+ #create and upload json file with list of authorized users who can submit to this competition.
859
+ _create_competitionuserauth_json(apiurl, email_list,public,datauri['ecr_uri'], submission_type="experiment")
860
+ _create_public_private_split_json(apiurl, public_private_split, "experiment")
861
+
862
+ bodydata = {"unique_model_id": model_id,
863
+ "bucket_name": api_bucket,
864
+ "apideveloper": os.environ.get("username"), # change this to first and last name
865
+ "experiment":"TRUE",
866
+ "competitionname":aishare_competitionname,
867
+ "competitiondescription": aishare_competitiondescription,
868
+ # getting rid of extra quotes that screw up dynamodb string search on apiurls
869
+ "apiurl": apiurl,
870
+ "version": 0,
871
+ "Private": "FALSE",
872
+ "delete": "FALSE",
873
+ 'datadescription':aishare_datadescription,
874
+ 'dataecruri':datauri['ecr_uri'],
875
+ 'datalicense': aishare_datalicense}
876
+
877
+ # Get the response
878
+ headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
879
+ 'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
880
+ # modeltoapi lambda function invoked through below url to return new prediction api in response
881
+ requests.post("https://o35jwfakca.execute-api.us-east-1.amazonaws.com/dev/modeldata",
882
+ json=bodydata, headers=headers_with_authentication)
883
+
884
+
885
+ final_message = ("\n Success! Model experiment created. \n\n"
886
+ "You may now update your prediction API runtime model and verify evaluation metrics with the update_runtime_model() function.\n\n"
887
+ "To upload new models and/or preprocessors to this API, team members should use \n"
888
+ "the following credentials:\n\napiurl='" + apiurl+"'"+"\nfrom aimodelshare.aws import set_credentials\nset_credentials(apiurl=apiurl)\n\n"
889
+ "They can then submit models to your experiment by using the following code: \n\nexperiment= ai.Experiment(apiurl)\n"
890
+ "download_data('"+datauri['ecr_uri']+"') \n"
891
+ "# Use this data to preprocess data and train model. Write and save preprocessor fxn, save model to onnx file, generate predicted y values\n using X test data, then submit a model below.\n\n"
892
+ "experiment.submit_model(model_filepath, preprocessor_filepath, prediction_submission_list)")
893
+
894
+ if print_output:
895
+ return print(final_message)
896
+ else:
897
+ return
898
+
899
+ def _create_public_private_split_json(apiurl, split=0.5, submission_type='competition'):
900
+ import json
901
+ if all(["AWS_ACCESS_KEY_ID_AIMS" in os.environ,
902
+ "AWS_SECRET_ACCESS_KEY_AIMS" in os.environ,
903
+ "AWS_REGION_AIMS" in os.environ,
904
+ "username" in os.environ,
905
+ "password" in os.environ]):
906
+ pass
907
+ else:
908
+ return print("'Set public-private split' unsuccessful. Please provide credentials with set_credentials().")
909
+
910
+ # Create user session
911
+ aws_client=get_aws_client(aws_key=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
912
+ aws_secret=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
913
+ aws_region=os.environ.get('AWS_REGION_AIMS'))
914
+
915
+ user_sess = boto3.session.Session(aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
916
+ aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
917
+ region_name=os.environ.get('AWS_REGION_AIMS'))
918
+
919
+ s3 = user_sess.resource('s3')
920
+
921
+ # Get bucket and model_id for user based on apiurl {{{
922
+ response, error = run_function_on_lambda(
923
+ apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
924
+ )
925
+ if error is not None:
926
+ raise error
927
+
928
+ _, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
929
+ # }}}
930
+
931
+ import json
932
+ import tempfile
933
+ tempdir = tempfile.TemporaryDirectory()
934
+ with open(tempdir.name+'/public_private_split.json', 'w', encoding='utf-8') as f:
935
+ json.dump({"public_private_split": str(split)}, f, ensure_ascii=False, indent=4)
936
+
937
+ aws_client['client'].upload_file(
938
+ tempdir.name+"/public_private_split.json", api_bucket, model_id +"/"+submission_type+"/public_private_split.json"
939
+ )
940
+
941
+ return
942
+
943
+ def _create_competitionuserauth_json(apiurl, email_list=[],public=False, datauri=None, submission_type="competition"):
944
+ import json
945
+ if all(["AWS_ACCESS_KEY_ID_AIMS" in os.environ,
946
+ "AWS_SECRET_ACCESS_KEY_AIMS" in os.environ,
947
+ "AWS_REGION_AIMS" in os.environ,
948
+ "username" in os.environ,
949
+ "password" in os.environ]):
950
+ pass
951
+ else:
952
+ return print("'Update Runtime Model' unsuccessful. Please provide credentials with set_credentials().")
953
+
954
+ # Create user session
955
+ aws_client=get_aws_client(aws_key=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
956
+ aws_secret=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
957
+ aws_region=os.environ.get('AWS_REGION_AIMS'))
958
+
959
+ user_sess = boto3.session.Session(aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
960
+ aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
961
+ region_name=os.environ.get('AWS_REGION_AIMS'))
962
+
963
+ s3 = user_sess.resource('s3')
964
+
965
+ # Get bucket and model_id for user based on apiurl {{{
966
+ response, error = run_function_on_lambda(
967
+ apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
968
+ )
969
+ if error is not None:
970
+ raise error
971
+
972
+ _, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
973
+ # }}}
974
+
975
+
976
+ import json
977
+ import tempfile
978
+ tempdir = tempfile.TemporaryDirectory()
979
+ with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
980
+ json.dump({"emaillist": email_list, "public":str(public).upper(),"datauri":str(datauri)}, f, ensure_ascii=False, indent=4)
981
+
982
+ aws_client['client'].upload_file(
983
+ tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
984
+ )
985
+
986
+ return
987
+
988
+ def update_playground_access_list(apiurl, email_list=[], update_type="Add"):
989
+ """
990
+ Updates list of authenticated participants who can submit new models to a competition.
991
+ ---------------
992
+ Parameters:
993
+ apiurl: string
994
+ URL of deployed prediction API
995
+
996
+ email_list: [REQUIRED] list of comma separated emails for users who are allowed to access model playground. Emails should be strings in a list.
997
+ update_type:[REQUIRED] options, string: 'Add', 'Remove', 'Replace'. Add appends user emails to original list, Remove deletes users from list,
998
+ and 'Replace' overwrites the original list with the new list provided.
999
+ -----------------
1000
+ Returns
1001
+ response: "Success" upon successful request
1002
+ """
1003
+ if update_type not in ['Add', 'Remove', 'Replace']:
1004
+ return "Error: update_type must be in the form of 'Add', 'Remove', or 'Replace'"
1005
+
1006
+ bodydata = {
1007
+ "modifyaccess": "TRUE",
1008
+ "apideveloper": os.environ.get("username"), # change this to first and last name
1009
+ "apiurl": apiurl,
1010
+ "operation": update_type,
1011
+ "email_list": email_list,
1012
+ }
1013
+
1014
+ # Get the response
1015
+ headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
1016
+ 'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
1017
+ # modeltoapi lambda function invoked through below url to return new prediction api in response
1018
+ response = requests.post("https://bhrdesksak.execute-api.us-east-1.amazonaws.com/dev/modeldata",
1019
+ json=bodydata, headers=headers_with_authentication)
1020
+ response_string = response.text
1021
+ response_string = response_string[1:-1]
1022
+
1023
+ # Build output {{{
1024
+ finalresult = "Success! Your Model Playground email list has been updated. Playground Url: " + apiurl
1025
+ return finalresult
1026
+
1027
+ def get_playground_access_list(apiurl):
1028
+ bodydata = {
1029
+ "getaccess": "TRUE",
1030
+ "apideveloper": os.environ.get("username"), # change this to first and last name
1031
+ "apiurl": apiurl,
1032
+ }
1033
+
1034
+ # Get the response
1035
+ headers_with_authentication = {'Content-Type': 'application/json', 'authorizationToken': os.environ.get("JWT_AUTHORIZATION_TOKEN"), 'Access-Control-Allow-Headers':
1036
+ 'Content-Type,X-Amz-Date,authorizationToken,Access-Control-Allow-Origin,X-Api-Key,X-Amz-Security-Token,Authorization', 'Access-Control-Allow-Origin': '*'}
1037
+ # modeltoapi lambda function invoked through below url to return new prediction api in response
1038
+ response = requests.post("https://bhrdesksak.execute-api.us-east-1.amazonaws.com/dev/modeldata",
1039
+ json=bodydata, headers=headers_with_authentication)
1040
+ response_string = response.text
1041
+ response_string = response_string[1:-1]
1042
+
1043
+ return response_string.split(',')
1044
+
1045
+ def update_access_list(apiurl, email_list=[],update_type="Add", submission_type="competition"):
1046
+ """
1047
+ Updates list of authenticated participants who can submit new models to a competition.
1048
+ ---------------
1049
+ Parameters:
1050
+ apiurl: string
1051
+ URL of deployed prediction API
1052
+
1053
+ email_list: [REQUIRED] list of comma separated emails for users who are allowed to submit models to competition. Emails should be strings in a list.
1054
+ update_type:[REQUIRED] options, string: 'Add', 'Remove', 'Replace_list','Get. Add appends user emails to original list, Remove deletes users from list,
1055
+ 'Replace_list' overwrites the original list with the new list provided, and Get returns the current list.
1056
+ -----------------
1057
+ Returns
1058
+ response: "Success" upon successful request
1059
+ """
1060
+ import json
1061
+ import os
1062
+ if all(["AWS_ACCESS_KEY_ID_AIMS" in os.environ,
1063
+ "AWS_SECRET_ACCESS_KEY_AIMS" in os.environ,
1064
+ "AWS_REGION_AIMS" in os.environ,
1065
+ "username" in os.environ,
1066
+ "password" in os.environ]):
1067
+ pass
1068
+ else:
1069
+ return print("'Update unsuccessful. Please provide credentials with set_credentials().")
1070
+
1071
+ if all([isinstance(email_list, list)]):
1072
+ if all([len(email_list)>0]):
1073
+ pass
1074
+ else:
1075
+ return print("email_list argument empty or incorrectly formatted. Please provide a list of emails for authorized competition participants formatted as strings.")
1076
+
1077
+ # Create user session
1078
+ aws_client=get_aws_client(aws_key=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
1079
+ aws_secret=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
1080
+ aws_region=os.environ.get('AWS_REGION_AIMS'))
1081
+
1082
+ user_sess = boto3.session.Session(aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID_AIMS'),
1083
+ aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY_AIMS'),
1084
+ region_name=os.environ.get('AWS_REGION_AIMS'))
1085
+
1086
+ s3 = user_sess.resource('s3')
1087
+
1088
+ # Get bucket and model_id for user based on apiurl {{{
1089
+ response, error = run_function_on_lambda(
1090
+ apiurl, **{"delete": "FALSE", "versionupdateget": "TRUE"}
1091
+ )
1092
+ if error is not None:
1093
+ raise error
1094
+
1095
+ _, api_bucket, model_id = json.loads(response.content.decode("utf-8"))
1096
+ # }}}
1097
+
1098
+ email_list=json.loads(json.dumps(email_list))
1099
+
1100
+
1101
+ if update_type=="Replace_list":
1102
+ import json
1103
+ import tempfile
1104
+ tempdir = tempfile.TemporaryDirectory()
1105
+ content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
1106
+ file_content = content_object.get()['Body'].read().decode('utf-8')
1107
+ json_content = json.loads(file_content)
1108
+ json_content['emaillist']=email_list
1109
+ with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
1110
+ json.dump(json_content, f, ensure_ascii=False, indent=4)
1111
+
1112
+ aws_client['client'].upload_file(
1113
+ tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
1114
+ )
1115
+ elif update_type=="Add":
1116
+ import json
1117
+ import tempfile
1118
+ tempdir = tempfile.TemporaryDirectory()
1119
+ content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
1120
+ file_content = content_object.get()['Body'].read().decode('utf-8')
1121
+ json_content = json.loads(file_content)
1122
+
1123
+ email_list_old=json_content["emaillist"]
1124
+ email_list_new=email_list_old+email_list
1125
+ print(email_list_new)
1126
+
1127
+ json_content["emaillist"]=email_list_new
1128
+ with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
1129
+ json.dump(json_content, f, ensure_ascii=False, indent=4)
1130
+
1131
+ aws_client['client'].upload_file(
1132
+ tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
1133
+ )
1134
+
1135
+ return "Success: Your competition participant access list is now updated."
1136
+ elif update_type=="Remove":
1137
+ import json
1138
+ import tempfile
1139
+ tempdir = tempfile.TemporaryDirectory()
1140
+
1141
+ aws_client['resource']
1142
+ content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
1143
+ file_content = content_object.get()['Body'].read().decode('utf-8')
1144
+ json_content = json.loads(file_content)
1145
+
1146
+ email_list_old=json_content["emaillist"]
1147
+ email_list_new=list(set(list(email_list_old)) - set(email_list))
1148
+ print(email_list_new)
1149
+
1150
+ json_content["emaillist"]=email_list_new
1151
+ with open(tempdir.name+'/competitionuserdata.json', 'w', encoding='utf-8') as f:
1152
+ json.dump(json_content, f, ensure_ascii=False, indent=4)
1153
+
1154
+ aws_client['client'].upload_file(
1155
+ tempdir.name+"/competitionuserdata.json", api_bucket, model_id +"/"+submission_type+"/competitionuserdata.json"
1156
+ )
1157
+ return "Success: Your competition participant access list is now updated."
1158
+ elif update_type=="Get":
1159
+ import json
1160
+ import tempfile
1161
+ tempdir = tempfile.TemporaryDirectory()
1162
+
1163
+ aws_client['resource']
1164
+ content_object = aws_client['resource'].Object(bucket_name=api_bucket, key=model_id +"/"+submission_type+"/competitionuserdata.json")
1165
+ file_content = content_object.get()['Body'].read().decode('utf-8')
1166
+ json_content = json.loads(file_content)
1167
+ return json_content['emaillist']
1168
+ else:
1169
+ return "Error: Check inputs and resubmit."
1170
+
1171
+
1172
+ def _confirm_libraries_exist(requirements):
1173
+ requirements = requirements.split(",")
1174
+ for i in range(len(requirements)):
1175
+ requirements[i] = requirements[i].strip(" ")
1176
+ exists = requests.get("https://pypi.org/project/" + requirements[i])
1177
+ if exists.status_code == 404:
1178
+ try_again_message = ("The entered library '" + requirements[i] + "' was not found. "
1179
+ "Please confirm and re-submit library name: ")
1180
+ requirements[i] = input(try_again_message)
1181
+ exists = requests.get("https://pypi.org/project/" + requirements[i])
1182
+
1183
+ if exists.status_code == 404:
1184
+ error_message = ("ModuleNotFoundError: No module named '" + requirements[i] + "' found in the Python Package Index (PyPI). \n"
1185
+ "Please double-check library name and try again.")
1186
+ return print(error_message)
1187
+
1188
+ return
1189
+
1190
+
1191
+
1192
+ def _create_exampledata_json(model_type, exampledata_folder_filepath):
1193
+ import tempfile
1194
+ # create temporary folder
1195
+ temp_dir = tempfile.gettempdir()
1196
+
1197
+ image_extensions = ['.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif', '.avif',
1198
+ '.svg', '.webp', '.tif', '.bmp', '.jpe', '.jif', '.jfif',
1199
+ '.jfi', 'psd', '.raw', '.arw', '.cr2', '.nrw', '.k25', '.eps']
1200
+ video_extensions = ['.avchd', '.avi', '.flv', '.mov', '.mkv', '.mp4', '.wmv']
1201
+ audio_extensions = ['.m4a', '.flac', '.mp3', '.mp4', '.wav', '.wma', '.aac']
1202
+
1203
+ import pandas as pd
1204
+
1205
+ if any([model_type.lower() == "tabular", model_type.lower() == "timeseries", model_type.lower() == "text"]):
1206
+ #confirm data type is data frame, try to convert if not [necessary for front end]
1207
+ if isinstance(exampledata_folder_filepath, pd.DataFrame):
1208
+ pass
1209
+ else:
1210
+ exampledata_folder_filepath = pd.DataFrame(exampledata_folder_filepath)
1211
+
1212
+ tabularjson = exampledata_folder_filepath.to_json(orient='split', index=False)
1213
+
1214
+
1215
+ with open(temp_dir+'/exampledata.json', 'w', encoding='utf-8') as f:
1216
+ json.dump({"exampledata": tabularjson, "totalfiles":1}, f, ensure_ascii=False, indent=4)
1217
+
1218
+ return
1219
+
1220
+ else:
1221
+
1222
+ if isinstance(exampledata_folder_filepath, pd.DataFrame):
1223
+
1224
+ tabularjson = exampledata_folder_filepath.to_json(orient='split', index=False)
1225
+ with open(temp_dir+'/exampledata.json', 'w', encoding='utf-8') as f:
1226
+ json.dump({"exampledata": tabularjson, "totalfiles":1}, f, ensure_ascii=False, indent=4)
1227
+ else:
1228
+ #Check file types & make list to convert
1229
+ data = ""
1230
+ file_list = os.listdir(exampledata_folder_filepath)
1231
+ files_to_convert = []
1232
+ for i in range(len(file_list)):
1233
+ file_list[i] = exampledata_folder_filepath + "/" + file_list[i]
1234
+ root, ext = os.path.splitext(file_list[i])
1235
+
1236
+ if not ext:
1237
+ ext = mimetypes.guess_extension(file_list[i])
1238
+
1239
+ if model_type.lower() == "image" and ext in image_extensions:
1240
+ files_to_convert.append(file_list[i])
1241
+
1242
+ if model_type.lower() == "video" and ext in video_extensions:
1243
+ files_to_convert.append(file_list[i])
1244
+
1245
+ if model_type.lower() == "audio" and ext in audio_extensions:
1246
+ files_to_convert.append(file_list[i])
1247
+
1248
+ i += 1
1249
+ if len(files_to_convert) == 5:
1250
+ break
1251
+
1252
+ #base64 encode confirmed file list
1253
+ for i in range(len(files_to_convert)):
1254
+ with open(files_to_convert[i], "rb") as current_file:
1255
+ encoded_string = base64.b64encode(current_file.read())
1256
+ data = data + encoded_string.decode('utf-8') + ", "
1257
+ i += 1
1258
+
1259
+ #build json
1260
+ with open(temp_dir+'/exampledata.json', 'w', encoding='utf-8') as f:
1261
+ json.dump({"exampledata": data[:-2], "totalfiles": len(files_to_convert)}, f, ensure_ascii=False, indent=4)
1262
+
1263
+ return
1264
+
1265
+ __all__ = [
1266
+ take_user_info_and_generate_api,
1267
+ send_model_data_to_dyndb_and_return_api,
1268
+ model_to_api,
1269
+ create_competition,update_access_list
1270
+ ]