Skip to content

Commit 5fa71a9

Browse files
committed
feat: added AML pipeline script
1 parent cfd3e2e commit 5fa71a9

File tree

1 file changed

+198
-0
lines changed

1 file changed

+198
-0
lines changed

aml_service/04-AmlPipelines.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
"""
2+
Copyright (C) Microsoft Corporation. All rights reserved.​
3+
4+
Microsoft Corporation (“Microsoft”) grants you a nonexclusive, perpetual,
5+
royalty-free right to use, copy, and modify the software code provided by us
6+
("Software Code"). You may not sublicense the Software Code or any use of it
7+
(except to your affiliates and to vendors to perform work on your behalf)
8+
through distribution, network access, service agreement, lease, rental, or
9+
otherwise. This license does not purport to express any claim of ownership over
10+
data you may have shared with Microsoft in the creation of the Software Code.
11+
Unless applicable law gives you more rights, Microsoft reserves all other
12+
rights not expressly granted herein, whether by implication, estoppel or
13+
otherwise. ​
14+
15+
THE SOFTWARE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
16+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18+
MICROSOFT OR ITS LICENSORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22+
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23+
ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE CODE, EVEN IF ADVISED OF THE
24+
POSSIBILITY OF SUCH DAMAGE.
25+
"""
26+
#%%
27+
import os, json, requests, datetime
28+
import argparse
29+
from azureml.core import Workspace, Experiment, Datastore
30+
from azureml.core.runconfig import RunConfiguration, CondaDependencies
31+
from azureml.data.data_reference import DataReference
32+
from azureml.pipeline.core import Pipeline, PipelineData, StepSequence
33+
from azureml.pipeline.steps import PythonScriptStep
34+
from azureml.pipeline.core import PublishedPipeline
35+
from azureml.pipeline.core.graph import PipelineParameter
36+
from azureml.core.compute import ComputeTarget
37+
38+
from azureml.widgets import RunDetails
39+
from azureml.core.authentication import AzureCliAuthentication
40+
41+
print("Pipeline SDK-specific imports completed")
42+
43+
cli_auth = AzureCliAuthentication()
44+
45+
46+
parser = argparse.ArgumentParser("Pipeline")
47+
parser.add_argument(
48+
"--pipeline_action",
49+
type=str,
50+
choices=["unpublish-test", "publish"],
51+
help="Determines if pipeline needs to run on small data set \
52+
or pipeline needs to be republished",
53+
)
54+
55+
args = parser.parse_args()
56+
57+
58+
# Get workspace
59+
ws = Workspace.from_config(path="aml_config/config.json", auth=cli_auth)
60+
def_blob_store = Datastore(ws, "workspaceblobstore")
61+
62+
# Get AML Compute name and Experiment Name
63+
with open("aml_config/security_config.json") as f:
64+
config = json.load(f)
65+
66+
experiment_name = config["experiment_name"]
67+
aml_cluster_name = config["aml_cluster_name"]
68+
aml_pipeline_name = "training-pipeline"
69+
70+
source_directory = "code"
71+
72+
# Run Config
73+
# Declare packages dependencies required in the pipeline (these can also be expressed as a YML file)
74+
# cd = CondaDependencies.create(pip_packages=["azureml-defaults", 'tensorflow==1.8.0'])
75+
cd = CondaDependencies("aml_config/conda_dependencies.yml")
76+
77+
run_config = RunConfiguration(conda_dependencies=cd)
78+
79+
aml_compute = ws.compute_targets[aml_cluster_name]
80+
81+
jsonconfigs = PipelineData("jsonconfigs", datastore=def_blob_store)
82+
83+
# Suffix for all the config files
84+
config_suffix = datetime.datetime.now().strftime("%Y%m%d%H")
85+
print("PipelineData object created")
86+
87+
# Create python script step to run the training/scoring main script
88+
train = PythonScriptStep(
89+
name="Train New Model",
90+
script_name="training/train.py",
91+
compute_target=aml_compute,
92+
source_directory=source_directory,
93+
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
94+
runconfig=run_config,
95+
# inputs=[jsonconfigs],
96+
outputs=[jsonconfigs],
97+
allow_reuse=False,
98+
)
99+
print("Step Train created")
100+
101+
evaluate = PythonScriptStep(
102+
name="Evaluate New Model with Prod Model",
103+
script_name="evaluate/evaluate_model.py",
104+
compute_target=aml_compute,
105+
source_directory=source_directory,
106+
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
107+
runconfig=run_config,
108+
inputs=[jsonconfigs],
109+
# outputs=[jsonconfigs],
110+
allow_reuse=False,
111+
)
112+
print("Step Evaluate created")
113+
114+
register_model = PythonScriptStep(
115+
name="Register New Trained Model",
116+
script_name="register/register_model.py",
117+
compute_target=aml_compute,
118+
source_directory=source_directory,
119+
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
120+
runconfig=run_config,
121+
inputs=[jsonconfigs],
122+
# outputs=[jsonconfigs],
123+
allow_reuse=False,
124+
)
125+
print("Step register model created")
126+
127+
package_model = PythonScriptStep(
128+
name="Package Model as Scoring Image",
129+
script_name="scoring/create_scoring_image.py",
130+
compute_target=aml_compute,
131+
source_directory=source_directory,
132+
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
133+
runconfig=run_config,
134+
inputs=[jsonconfigs],
135+
# outputs=[jsonconfigs],
136+
allow_reuse=False,
137+
)
138+
print("Packed the model into a Scoring Image")
139+
140+
# Create Steps dependency such that they run in sequence
141+
evaluate.run_after(train)
142+
register_model.run_after(evaluate)
143+
create_scoring_image.run_after(register_model)
144+
145+
steps = [create_scoring_image]
146+
147+
148+
# Build Pipeline
149+
pipeline1 = Pipeline(workspace=ws, steps=steps)
150+
print("Pipeline is built")
151+
152+
# Validate Pipeline
153+
pipeline1.validate()
154+
print("Pipeline validation complete")
155+
156+
# Submit unpublished pipeline with small data set for test
157+
if args.pipeline_action == "unpublish-test":
158+
pipeline_run1 = Experiment(ws, experiment_name).submit(
159+
pipeline1, regenerate_outputs=True
160+
)
161+
print("Pipeline is submitted for execution")
162+
pipeline_run1.wait_for_completion(show_output=True)
163+
164+
165+
# RunDetails(pipeline_run1).show()
166+
167+
## Publish Pipeline
168+
169+
# Define pipeline parameters
170+
# run_env = PipelineParameter(
171+
# name="dev_flag",
172+
# default_value=True)
173+
174+
# dbname = PipelineParameter(
175+
# name="dbname",
176+
# default_value='opex')
177+
178+
179+
# Publish Pipeline
180+
181+
published_pipeline1 = pipeline1.publish(
182+
name=aml_pipeline_name, description="Model training/retraining pipeline"
183+
)
184+
185+
186+
# # Run a piblished pipeline
187+
# cli_auth = AzureCliAuthentication()
188+
# aad_token = cli_auth.get_authentication_header()
189+
# rest_endpoint1 = published_pipeline1.endpoint
190+
# print(rest_endpoint1)
191+
192+
# response = requests.post(published_pipeline1.endpoint,
193+
# headers=aad_token,
194+
# json={"ExperimentName": "devops-ai",
195+
# "ParameterAssignments": {"dev_flag": True}})
196+
197+
# run_id = response.json()["Id"]
198+
# print(run_id)

0 commit comments

Comments
 (0)