easy-sm 0.1.3__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 easy-sm might be problematic. Click here for more details.
- easy_sm/__init__.py +0 -0
- easy_sm/__main__.py +27 -0
- easy_sm/commands/__init__.py +0 -0
- easy_sm/commands/build.py +81 -0
- easy_sm/commands/cloud.py +356 -0
- easy_sm/commands/initialize.py +169 -0
- easy_sm/commands/local.py +151 -0
- easy_sm/commands/push.py +86 -0
- easy_sm/config/__init__.py +0 -0
- easy_sm/config/config.py +52 -0
- easy_sm/sagemaker/__init__.py +0 -0
- easy_sm/sagemaker/sagemaker.py +294 -0
- easy_sm/template/__init__.py +0 -0
- easy_sm/template/easy_sm_base/Dockerfile +27 -0
- easy_sm/template/easy_sm_base/__init__.py +0 -0
- easy_sm/template/easy_sm_base/build.sh +20 -0
- easy_sm/template/easy_sm_base/executor.sh +11 -0
- easy_sm/template/easy_sm_base/local_test/deploy_local.sh +7 -0
- easy_sm/template/easy_sm_base/local_test/process_local.sh +10 -0
- easy_sm/template/easy_sm_base/local_test/test_dir/input/data/training/.gitkeep +0 -0
- easy_sm/template/easy_sm_base/local_test/test_dir/model/.gitkeep +0 -0
- easy_sm/template/easy_sm_base/local_test/test_dir/output/.gitkeep +0 -0
- easy_sm/template/easy_sm_base/local_test/train_local.sh +7 -0
- easy_sm/template/easy_sm_base/prediction/__init__.py +0 -0
- easy_sm/template/easy_sm_base/prediction/serve +57 -0
- easy_sm/template/easy_sm_base/processing/__init__.py +0 -0
- easy_sm/template/easy_sm_base/push.sh +74 -0
- easy_sm/template/easy_sm_base/training/__init__.py +0 -0
- easy_sm/template/easy_sm_base/training/train +83 -0
- easy_sm/template/easy_sm_base/training/training.py +10 -0
- easy_sm-0.1.3.dist-info/LICENSE +21 -0
- easy_sm-0.1.3.dist-info/METADATA +17 -0
- easy_sm-0.1.3.dist-info/RECORD +36 -0
- easy_sm-0.1.3.dist-info/WHEEL +5 -0
- easy_sm-0.1.3.dist-info/entry_points.txt +2 -0
- easy_sm-0.1.3.dist-info/top_level.txt +1 -0
easy_sm/__init__.py
ADDED
|
File without changes
|
easy_sm/__main__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from easy_sm.commands.local import local
|
|
3
|
+
from easy_sm.commands.initialize import init
|
|
4
|
+
from easy_sm.commands.build import build
|
|
5
|
+
from easy_sm.commands.push import push
|
|
6
|
+
from easy_sm.commands.cloud import cloud
|
|
7
|
+
|
|
8
|
+
@click.group()
|
|
9
|
+
@click.option(u"-t", u"--docker-tag", default=u"latest", help=u"Specify tag for Docker image")
|
|
10
|
+
@click.pass_context
|
|
11
|
+
def cli(ctx, docker_tag):
|
|
12
|
+
"""
|
|
13
|
+
easy_sm enables training and deploying machine learning models on AWS SageMaker in a few minutes!
|
|
14
|
+
"""
|
|
15
|
+
ctx.obj = {'docker_tag': docker_tag}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_commands(cli):
|
|
19
|
+
cli.add_command(init)
|
|
20
|
+
cli.add_command(build)
|
|
21
|
+
cli.add_command(local)
|
|
22
|
+
cli.add_command(push)
|
|
23
|
+
cli.add_command(cloud)
|
|
24
|
+
# cli.add_command(configure)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
add_commands(cli)
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import click
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from easy_sm.config.config import ConfigManager
|
|
6
|
+
|
|
7
|
+
def _config(app_name):
|
|
8
|
+
config_file_path = os.path.join(f'{app_name}.json')
|
|
9
|
+
if not os.path.isfile(config_file_path):
|
|
10
|
+
raise ValueError("This is not a easy_sm directory: {}".format(os.getcwd()))
|
|
11
|
+
else:
|
|
12
|
+
return ConfigManager(config_file_path).get_config()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _build(source_dir, requirements_dir, image_name, docker_tag, python_version):
|
|
16
|
+
"""
|
|
17
|
+
Builds a Docker image that contains code under the given source root directory.
|
|
18
|
+
|
|
19
|
+
Assumes that Docker is installed and running locally.
|
|
20
|
+
|
|
21
|
+
:param source_dir: [str], source root directory
|
|
22
|
+
:param requirements_dir: [str], path to requirements.txt
|
|
23
|
+
:param image_name: [str], The name of the Docker image
|
|
24
|
+
:param docker_tag: [str], the Docker tag for the image
|
|
25
|
+
"""
|
|
26
|
+
easy_sm_module_path = os.path.relpath(os.path.join(source_dir, 'easy_sm_base/'))
|
|
27
|
+
|
|
28
|
+
build_script_path = os.path.join(easy_sm_module_path, 'build.sh')
|
|
29
|
+
dockerfile_path = os.path.join(easy_sm_module_path, 'Dockerfile')
|
|
30
|
+
|
|
31
|
+
train_file_path = os.path.join(easy_sm_module_path, 'training', 'train')
|
|
32
|
+
serve_file_path = os.path.join(easy_sm_module_path, 'prediction', 'serve')
|
|
33
|
+
executor_file_path = os.path.join(easy_sm_module_path, 'executor.sh')
|
|
34
|
+
|
|
35
|
+
if not os.path.isfile(build_script_path) or not os.path.isfile(train_file_path) or not \
|
|
36
|
+
os.path.isfile(serve_file_path):
|
|
37
|
+
raise ValueError("This is not a easy_sm directory: {}".format(source_dir))
|
|
38
|
+
|
|
39
|
+
os.chmod(train_file_path, 0o777)
|
|
40
|
+
os.chmod(serve_file_path, 0o777)
|
|
41
|
+
os.chmod(executor_file_path, 0o777)
|
|
42
|
+
|
|
43
|
+
target_dir_name = os.path.basename(os.path.normpath(source_dir))
|
|
44
|
+
output = subprocess.check_output(
|
|
45
|
+
[
|
|
46
|
+
"{}".format(build_script_path),
|
|
47
|
+
"{}".format(os.path.relpath(source_dir)),
|
|
48
|
+
"{}".format(os.path.relpath(target_dir_name)),
|
|
49
|
+
"{}".format(dockerfile_path),
|
|
50
|
+
"{}".format(os.path.relpath(requirements_dir)),
|
|
51
|
+
docker_tag,
|
|
52
|
+
image_name,
|
|
53
|
+
python_version
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
print(output)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@click.command()
|
|
60
|
+
@click.option(
|
|
61
|
+
u"-a",
|
|
62
|
+
u"--app-name",
|
|
63
|
+
required=True,
|
|
64
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
65
|
+
)
|
|
66
|
+
@click.pass_obj
|
|
67
|
+
def build(obj, app_name):
|
|
68
|
+
"""
|
|
69
|
+
Command to build SageMaker app
|
|
70
|
+
"""
|
|
71
|
+
print("Started building SageMaker Docker image. It will take some minutes...\n")
|
|
72
|
+
|
|
73
|
+
config = _config(app_name)
|
|
74
|
+
_build(
|
|
75
|
+
source_dir=config.easy_sm_module_dir,
|
|
76
|
+
requirements_dir=config.requirements_dir,
|
|
77
|
+
docker_tag=obj['docker_tag'],
|
|
78
|
+
image_name=config.image_name,
|
|
79
|
+
python_version=config.python_version)
|
|
80
|
+
|
|
81
|
+
print("Docker image built successfully!")
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import click
|
|
4
|
+
from easy_sm.config.config import ConfigManager
|
|
5
|
+
from easy_sm.sagemaker import sagemaker
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _config(app_name):
|
|
9
|
+
config_file_path = os.path.join(f'{app_name}.json')
|
|
10
|
+
if not os.path.isfile(config_file_path):
|
|
11
|
+
raise ValueError("This is not a easy_sm directory: {}".format(os.getcwd()))
|
|
12
|
+
else:
|
|
13
|
+
return ConfigManager(config_file_path).get_config()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def cloud():
|
|
18
|
+
"""
|
|
19
|
+
Commands for AWS operations: upload data, train and deploy
|
|
20
|
+
"""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.command(name='upload-data')
|
|
25
|
+
@click.option(u"-i", u"--input-dir", required=True, help="Path to data input directory")
|
|
26
|
+
@click.option(
|
|
27
|
+
u"-t", u"--target-dir",
|
|
28
|
+
required=True,
|
|
29
|
+
help="s3 location to upload data",
|
|
30
|
+
type=click.Path()
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
u"-r",
|
|
34
|
+
u"--iam-role-arn",
|
|
35
|
+
required=True,
|
|
36
|
+
help="The AWS role to use for the upload command"
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
u"-a",
|
|
40
|
+
u"--app-name",
|
|
41
|
+
required=True,
|
|
42
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
43
|
+
)
|
|
44
|
+
def upload_data(input_dir, target_dir, iam_role_arn, app_name):
|
|
45
|
+
"""
|
|
46
|
+
Command to upload data to S3
|
|
47
|
+
"""
|
|
48
|
+
print("Started uploading data to S3...\n")
|
|
49
|
+
config = _config(app_name)
|
|
50
|
+
sage_maker_client = sagemaker.SageMakerClient(config.aws_profile, config.aws_region, iam_role_arn)
|
|
51
|
+
target_path = sage_maker_client.upload_data(input_dir, target_dir)
|
|
52
|
+
print("Data uploaded to {} successfully".format(target_path))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@click.command(name='train')
|
|
56
|
+
@click.option(
|
|
57
|
+
u"-i", u"--input-s3-dir",
|
|
58
|
+
required=True,
|
|
59
|
+
help="s3 location to input data",
|
|
60
|
+
type=click.Path()
|
|
61
|
+
)
|
|
62
|
+
@click.option(
|
|
63
|
+
u"-o", u"--output-s3-dir",
|
|
64
|
+
required=True,
|
|
65
|
+
help="s3 location to save output (models, etc)",
|
|
66
|
+
type=click.Path()
|
|
67
|
+
)
|
|
68
|
+
@click.option(u"-e", u"--ec2-type", required=True, help="ec2 instance type")
|
|
69
|
+
@click.option(
|
|
70
|
+
u"-r",
|
|
71
|
+
u"--iam-role-arn",
|
|
72
|
+
required=True,
|
|
73
|
+
help="The AWS role to use for the train command"
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
u"-n",
|
|
77
|
+
u"--base-job-name",
|
|
78
|
+
required=True,
|
|
79
|
+
help="Prefix for the SageMaker training job."
|
|
80
|
+
)
|
|
81
|
+
@click.option(
|
|
82
|
+
u"-a",
|
|
83
|
+
u"--app-name",
|
|
84
|
+
required=True,
|
|
85
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
86
|
+
)
|
|
87
|
+
@click.pass_obj
|
|
88
|
+
def train(
|
|
89
|
+
obj,
|
|
90
|
+
input_s3_dir,
|
|
91
|
+
output_s3_dir,
|
|
92
|
+
ec2_type,
|
|
93
|
+
iam_role_arn,
|
|
94
|
+
base_job_name,
|
|
95
|
+
app_name
|
|
96
|
+
):
|
|
97
|
+
"""
|
|
98
|
+
Command to train ML model(s) on SageMaker
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
print("Started training on SageMaker...\n")
|
|
102
|
+
config = _config(app_name)
|
|
103
|
+
sage_maker_client = sagemaker.SageMakerClient(config.aws_profile, config.aws_region, iam_role_arn)
|
|
104
|
+
|
|
105
|
+
image_name = config.image_name+':'+obj['docker_tag']
|
|
106
|
+
|
|
107
|
+
s3_model_location = sage_maker_client.train(
|
|
108
|
+
image_name=image_name,
|
|
109
|
+
input_s3_data_location=input_s3_dir,
|
|
110
|
+
train_instance_type=ec2_type,
|
|
111
|
+
output_path=output_s3_dir,
|
|
112
|
+
base_job_name=base_job_name
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
print("Training on SageMaker succeeded")
|
|
116
|
+
print("Model S3 location: {}".format(s3_model_location))
|
|
117
|
+
return s3_model_location # To pipe into other commands
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@click.command(name='deploy-serverless')
|
|
121
|
+
@click.option(
|
|
122
|
+
u"-m", u"--s3-model-location",
|
|
123
|
+
required=True,
|
|
124
|
+
help="s3 location to model tar.gz",
|
|
125
|
+
type=click.Path()
|
|
126
|
+
)
|
|
127
|
+
@click.option(u"-s",
|
|
128
|
+
u"--memory-size-in-mb",
|
|
129
|
+
required=True,
|
|
130
|
+
type=click.INT,
|
|
131
|
+
help="memory size in MB for serverless endpoint")
|
|
132
|
+
@click.option(
|
|
133
|
+
u"-r",
|
|
134
|
+
u"--iam-role-arn",
|
|
135
|
+
required=True,
|
|
136
|
+
help="The AWS role to use for the deploy command"
|
|
137
|
+
)
|
|
138
|
+
@click.option(
|
|
139
|
+
u"-n",
|
|
140
|
+
u"--endpoint-name",
|
|
141
|
+
required=True,
|
|
142
|
+
default=None,
|
|
143
|
+
help="Name for the SageMaker endpoint"
|
|
144
|
+
)
|
|
145
|
+
@click.option(
|
|
146
|
+
u"-a",
|
|
147
|
+
u"--app-name",
|
|
148
|
+
required=True,
|
|
149
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
150
|
+
)
|
|
151
|
+
@click.pass_obj
|
|
152
|
+
def deploy_serverless(
|
|
153
|
+
obj,
|
|
154
|
+
s3_model_location,
|
|
155
|
+
memory_size_in_mb,
|
|
156
|
+
iam_role_arn,
|
|
157
|
+
endpoint_name,
|
|
158
|
+
app_name
|
|
159
|
+
):
|
|
160
|
+
"""
|
|
161
|
+
Command to deploy ML model(s) on SageMaker
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
print("Started deployment on SageMaker ...\n")
|
|
165
|
+
config = _config(app_name)
|
|
166
|
+
image_name = config.image_name+':'+obj['docker_tag']
|
|
167
|
+
|
|
168
|
+
sage_maker_client = sagemaker.SageMakerClient(config.aws_profile, config.aws_region, iam_role_arn)
|
|
169
|
+
endpoint_name = sage_maker_client.deploy_serverless(
|
|
170
|
+
image_name=image_name,
|
|
171
|
+
s3_model_location=s3_model_location,
|
|
172
|
+
memory_size_in_mb=memory_size_in_mb,
|
|
173
|
+
endpoint_name=endpoint_name
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
print("Endpoint name: {}".format(endpoint_name))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@click.command(name='batch-transform')
|
|
180
|
+
@click.option(
|
|
181
|
+
u"-m", u"--s3-model-location",
|
|
182
|
+
required=True,
|
|
183
|
+
help="s3 location to model tar.gz",
|
|
184
|
+
type=click.Path()
|
|
185
|
+
)
|
|
186
|
+
@click.option(
|
|
187
|
+
u"-i", u"--s3-input-location",
|
|
188
|
+
required=True,
|
|
189
|
+
help="s3 input data location",
|
|
190
|
+
type=click.Path()
|
|
191
|
+
)
|
|
192
|
+
@click.option(
|
|
193
|
+
u"-o", u"--s3-output-location",
|
|
194
|
+
required=True,
|
|
195
|
+
help="s3 location to save predictions",
|
|
196
|
+
type=click.Path()
|
|
197
|
+
)
|
|
198
|
+
@click.option(u"--num-instances", required=True, type=int, help="Number of ec2 instances")
|
|
199
|
+
@click.option(u"--ec2-type", required=True, help="ec2 instance type")
|
|
200
|
+
@click.option(
|
|
201
|
+
u"-r",
|
|
202
|
+
u"--iam-role-arn",
|
|
203
|
+
required=True,
|
|
204
|
+
help="The AWS role to use for batch transform command"
|
|
205
|
+
)
|
|
206
|
+
@click.option(
|
|
207
|
+
u"-w",
|
|
208
|
+
u"--wait",
|
|
209
|
+
default=False,
|
|
210
|
+
is_flag=True,
|
|
211
|
+
help="Wait until Batch Transform is finished. "
|
|
212
|
+
"Default: don't wait"
|
|
213
|
+
)
|
|
214
|
+
@click.option(
|
|
215
|
+
u"-n",
|
|
216
|
+
u"--job-name",
|
|
217
|
+
required=False,
|
|
218
|
+
default=None,
|
|
219
|
+
help="Name for the SageMaker batch transform job."
|
|
220
|
+
)
|
|
221
|
+
@click.option(
|
|
222
|
+
u"-a",
|
|
223
|
+
u"--app-name",
|
|
224
|
+
required=True,
|
|
225
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
226
|
+
)
|
|
227
|
+
@click.pass_obj
|
|
228
|
+
def batch_transform(
|
|
229
|
+
obj,
|
|
230
|
+
s3_model_location,
|
|
231
|
+
s3_input_location,
|
|
232
|
+
s3_output_location,
|
|
233
|
+
num_instances,
|
|
234
|
+
ec2_type,
|
|
235
|
+
iam_role_arn,
|
|
236
|
+
wait,
|
|
237
|
+
job_name,
|
|
238
|
+
app_name
|
|
239
|
+
):
|
|
240
|
+
"""
|
|
241
|
+
Command to execute a batch transform job given a trained ML model on SageMaker
|
|
242
|
+
"""
|
|
243
|
+
print("Started configuration of batch transform on SageMaker ...\n")
|
|
244
|
+
|
|
245
|
+
config = _config(app_name)
|
|
246
|
+
image_name = config.image_name+':'+obj['docker_tag']
|
|
247
|
+
|
|
248
|
+
sage_maker_client = sagemaker.SageMakerClient(config.aws_profile, config.aws_region, iam_role_arn)
|
|
249
|
+
status = sage_maker_client.batch_transform(
|
|
250
|
+
image_name=image_name,
|
|
251
|
+
s3_model_location=s3_model_location,
|
|
252
|
+
s3_input_location=s3_input_location,
|
|
253
|
+
s3_output_location=s3_output_location,
|
|
254
|
+
transform_instance_count=num_instances,
|
|
255
|
+
transform_instance_type=ec2_type,
|
|
256
|
+
wait=wait,
|
|
257
|
+
job_name=job_name
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if wait:
|
|
261
|
+
print("Batch transform on SageMaker finished with status: {}".format(status))
|
|
262
|
+
if status == "Failed":
|
|
263
|
+
sys.exit(1)
|
|
264
|
+
else:
|
|
265
|
+
print("Started batch transform on SageMaker successfully")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@click.command(name='delete-endpoint')
|
|
269
|
+
@click.option(
|
|
270
|
+
u"-n",
|
|
271
|
+
u"--endpoint-name",
|
|
272
|
+
required=True,
|
|
273
|
+
default=None,
|
|
274
|
+
help="Name of the SageMaker endpoint"
|
|
275
|
+
)
|
|
276
|
+
@click.option(
|
|
277
|
+
u"-r",
|
|
278
|
+
u"--iam-role-arn",
|
|
279
|
+
required=True,
|
|
280
|
+
help="The AWS role to use for delete command"
|
|
281
|
+
)
|
|
282
|
+
@click.option(
|
|
283
|
+
u"-a",
|
|
284
|
+
u"--app-name",
|
|
285
|
+
required=True,
|
|
286
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
287
|
+
)
|
|
288
|
+
def delete_endpoint(endpoint_name, iam_role_arn, app_name):
|
|
289
|
+
config = _config(app_name)
|
|
290
|
+
sage_maker_client = sagemaker.SageMakerClient(config.aws_profile, config.aws_region, iam_role_arn)
|
|
291
|
+
sage_maker_client.shutdown_endpoint(endpoint_name)
|
|
292
|
+
print(f"Endpoint {endpoint_name} has been deleted")
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@click.command(name='process')
|
|
296
|
+
@click.option(u"-e", u"--ec2-type", required=True, help="ec2 instance type")
|
|
297
|
+
@click.option(
|
|
298
|
+
u"-r",
|
|
299
|
+
u"--iam-role-arn",
|
|
300
|
+
required=True,
|
|
301
|
+
help="The AWS role to use"
|
|
302
|
+
)
|
|
303
|
+
@click.option(
|
|
304
|
+
u"-n",
|
|
305
|
+
u"--base-job-name",
|
|
306
|
+
required=True,
|
|
307
|
+
help="Prefix for the SageMaker processing job."
|
|
308
|
+
"If not specified, default job name is generated, based on the docker image name and current timestamp."
|
|
309
|
+
)
|
|
310
|
+
@click.option(
|
|
311
|
+
u"-f",
|
|
312
|
+
u"--file",
|
|
313
|
+
required=True,
|
|
314
|
+
help="The name (not path) of python file to run as processing job"
|
|
315
|
+
)
|
|
316
|
+
@click.option(
|
|
317
|
+
u"-a",
|
|
318
|
+
u"--app-name",
|
|
319
|
+
required=True,
|
|
320
|
+
help="The app name whose json file will be referenced for setting up command"
|
|
321
|
+
)
|
|
322
|
+
@click.pass_obj
|
|
323
|
+
def process(
|
|
324
|
+
obj,
|
|
325
|
+
ec2_type,
|
|
326
|
+
iam_role_arn,
|
|
327
|
+
base_job_name,
|
|
328
|
+
file,
|
|
329
|
+
app_name
|
|
330
|
+
):
|
|
331
|
+
"""
|
|
332
|
+
Command to run python file as processing job on Sagemaker
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
print("Started processing job on SageMaker...\n")
|
|
336
|
+
config = _config(app_name)
|
|
337
|
+
sage_maker_client = sagemaker.SageMakerClient(config.aws_profile, config.aws_region, iam_role_arn)
|
|
338
|
+
|
|
339
|
+
image_name = config.image_name+':'+obj['docker_tag']
|
|
340
|
+
|
|
341
|
+
s3_model_location = sage_maker_client.process(
|
|
342
|
+
image_name=image_name,
|
|
343
|
+
processing_instance_type=ec2_type,
|
|
344
|
+
file=file,
|
|
345
|
+
base_job_name=base_job_name
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
print("Processing job on SageMaker succeeded")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
cloud.add_command(upload_data)
|
|
352
|
+
cloud.add_command(train)
|
|
353
|
+
cloud.add_command(deploy_serverless)
|
|
354
|
+
cloud.add_command(batch_transform)
|
|
355
|
+
cloud.add_command(delete_endpoint)
|
|
356
|
+
cloud.add_command(process)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import boto3
|
|
5
|
+
import click
|
|
6
|
+
from click import BadParameter
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from easy_sm.config.config import ConfigManager
|
|
10
|
+
from distutils.dir_util import copy_tree
|
|
11
|
+
|
|
12
|
+
_FILE_DIR_PATH = os.path.dirname(os.path.realpath(__file__))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _template_creation(app_name, aws_profile, aws_region, python_version, output_dir, requirements_dir):
|
|
16
|
+
easy_sm_module_name = 'easy_sm_base'
|
|
17
|
+
|
|
18
|
+
easy_sm_exists = os.path.exists(os.path.join(output_dir, easy_sm_module_name))
|
|
19
|
+
if easy_sm_exists:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
"There is a easy_sm directory/module already. "
|
|
22
|
+
"Please, rename it in order to use easy_sm."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
Path(output_dir).mkdir(exist_ok=True)
|
|
26
|
+
Path(os.path.join(output_dir, '__init__.py')).touch()
|
|
27
|
+
|
|
28
|
+
# Set 'easy_sm module' directory up
|
|
29
|
+
copy_tree(os.path.join(_FILE_DIR_PATH, '../template'), output_dir)
|
|
30
|
+
|
|
31
|
+
# Set configuration file up
|
|
32
|
+
config_manager = ConfigManager(os.path.join(f"{app_name}.json"))
|
|
33
|
+
config = config_manager.get_config()
|
|
34
|
+
|
|
35
|
+
config.image_name = app_name
|
|
36
|
+
config.aws_region = aws_region
|
|
37
|
+
config.aws_profile = aws_profile
|
|
38
|
+
config.easy_sm_module_dir = output_dir
|
|
39
|
+
config.python_version = python_version
|
|
40
|
+
config.requirements_dir = requirements_dir
|
|
41
|
+
config_manager.set_config(config)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_local_aws_profiles():
|
|
45
|
+
return boto3.Session().available_profiles
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def ask_for_app_name():
|
|
49
|
+
return click.prompt(
|
|
50
|
+
text="Type in a name for your SageMaker app (Only alphanumeric characters and - are allowed))",
|
|
51
|
+
type=str
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def ask_if_existing_project_exists():
|
|
56
|
+
return click.confirm(text="Are you starting a new project?")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def ask_for_root_dir():
|
|
60
|
+
return click.prompt(text="Type in the directory where your code lives. Example: src", type=str).strip('/')
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def ask_for_python_version():
|
|
64
|
+
print("Select Python interpreter:")
|
|
65
|
+
print('{}'.format('\n'.join(['1 - Python39', '2 - Python310', '3 - Python311'])))
|
|
66
|
+
|
|
67
|
+
def _validate_python_option(input_value):
|
|
68
|
+
if int(input_value) not in {1, 2, 3}:
|
|
69
|
+
raise BadParameter(
|
|
70
|
+
message="invalid choice: {}. (choose from 1, 2, 3)".format(str(input_value))
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return int(input_value)
|
|
74
|
+
|
|
75
|
+
chosen_python_index = click.prompt(
|
|
76
|
+
text="Choose from 1, 2, 3",
|
|
77
|
+
default=1,
|
|
78
|
+
value_proc=lambda x: _validate_python_option(x)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
_index_to_version = {
|
|
82
|
+
1: '3.9',
|
|
83
|
+
2: '3.10',
|
|
84
|
+
3: '3.11'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return _index_to_version[chosen_python_index]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ask_for_aws_details():
|
|
91
|
+
available_profiles = _get_local_aws_profiles()
|
|
92
|
+
|
|
93
|
+
if len(available_profiles) == 0:
|
|
94
|
+
print("aws cli is not configured!")
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
valid_positions = list(range(1, len(available_profiles) + 1))
|
|
98
|
+
print("Select AWS profile:")
|
|
99
|
+
print('{}'.format(
|
|
100
|
+
'\n'.join(
|
|
101
|
+
[
|
|
102
|
+
'{} - {}'.format(pos, profile)
|
|
103
|
+
for pos, profile in zip(valid_positions, available_profiles)
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _validate_profile_position(input_pos):
|
|
110
|
+
if int(input_pos) not in valid_positions:
|
|
111
|
+
raise BadParameter(
|
|
112
|
+
message="invalid choice: {}. (choose from {})".format(
|
|
113
|
+
input_pos,
|
|
114
|
+
', '.join([str(pos) for pos in valid_positions])
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
return int(input_pos)
|
|
118
|
+
|
|
119
|
+
chosen_profile_index = click.prompt(
|
|
120
|
+
text="Choose from {}".format(', '.join([str(pos) for pos in valid_positions])),
|
|
121
|
+
default=1,
|
|
122
|
+
value_proc=lambda x: _validate_profile_position(x)
|
|
123
|
+
)
|
|
124
|
+
chosen_profile_index -= 1
|
|
125
|
+
|
|
126
|
+
chosen_profile = available_profiles[chosen_profile_index]
|
|
127
|
+
|
|
128
|
+
chosen_region = click.prompt(
|
|
129
|
+
text="Type in your preferred AWS region name",
|
|
130
|
+
default='eu-west-1',
|
|
131
|
+
type=str
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return chosen_profile, chosen_region
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def ask_for_requirements_dir():
|
|
138
|
+
return click.prompt(text="Type in the path to requirements.txt. Example: requirements.txt", type=str).strip('/')
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@click.command()
|
|
142
|
+
def init():
|
|
143
|
+
"""
|
|
144
|
+
Command to initialize SageMaker template
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
easy_sm_app_name = ask_for_app_name()
|
|
148
|
+
|
|
149
|
+
is_new_project = ask_if_existing_project_exists()
|
|
150
|
+
|
|
151
|
+
root_dir = None
|
|
152
|
+
if not is_new_project:
|
|
153
|
+
root_dir = ask_for_root_dir()
|
|
154
|
+
|
|
155
|
+
python_version = ask_for_python_version()
|
|
156
|
+
|
|
157
|
+
aws_profile, aws_region = ask_for_aws_details()
|
|
158
|
+
|
|
159
|
+
requirements_dir = ask_for_requirements_dir()
|
|
160
|
+
_template_creation(
|
|
161
|
+
app_name=easy_sm_app_name,
|
|
162
|
+
aws_profile=aws_profile,
|
|
163
|
+
aws_region=aws_region,
|
|
164
|
+
python_version=python_version,
|
|
165
|
+
output_dir=root_dir if root_dir else easy_sm_app_name,
|
|
166
|
+
requirements_dir=requirements_dir
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
print("\neasy_sm module is created! ヽ(´▽`)/")
|