Skip to content

Commit d059b63

Browse files
authored
Merge pull request #28 from microsoft/model-artifact
Added Model artifact
2 parents 01b3c4d + 5685eb4 commit d059b63

13 files changed

+114
-55
lines changed

aml_config/security_config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"remote_vm_ip" : "<>",
99
"experiment_name" : "devops-ai-demo",
1010
"aml_cluster_name" : "aml-compute",
11+
"model_name" : "sklearn_regression_model.pkl",
1112
"vnet_resourcegroup_name" : "<>",
1213
"vnet_name" : "<>",
1314
"subnet_name" : "<>"

aml_service/04-AmlPipelines.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
experiment_name = config["experiment_name"]
6868
aml_cluster_name = config["aml_cluster_name"]
6969
aml_pipeline_name = "training-pipeline"
70+
#model_name = config["model_name"]
71+
model_name = PipelineParameter(name="model_name", default_value="sklearn_regression_model.pkl")
7072

7173
source_directory = "code"
7274

@@ -76,8 +78,9 @@
7678
cd = CondaDependencies("aml_config/conda_dependencies.yml")
7779

7880
run_config = RunConfiguration(conda_dependencies=cd)
79-
8081
aml_compute = ws.compute_targets[aml_cluster_name]
82+
run_config.environment.docker.enabled = True
83+
run_config.environment.spark.precache_packages = False
8184

8285
jsonconfigs = PipelineData("jsonconfigs", datastore=def_blob_store)
8386

@@ -91,7 +94,11 @@
9194
script_name="training/train.py",
9295
compute_target=aml_compute,
9396
source_directory=source_directory,
94-
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
97+
arguments=[
98+
"--config_suffix", config_suffix,
99+
"--json_config", jsonconfigs,
100+
"--model_name", model_name,
101+
],
95102
runconfig=run_config,
96103
# inputs=[jsonconfigs],
97104
outputs=[jsonconfigs],
@@ -104,7 +111,10 @@
104111
script_name="evaluate/evaluate_model.py",
105112
compute_target=aml_compute,
106113
source_directory=source_directory,
107-
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
114+
arguments=[
115+
"--config_suffix", config_suffix,
116+
"--json_config", jsonconfigs,
117+
],
108118
runconfig=run_config,
109119
inputs=[jsonconfigs],
110120
# outputs=[jsonconfigs],
@@ -117,33 +127,38 @@
117127
script_name="register/register_model.py",
118128
compute_target=aml_compute,
119129
source_directory=source_directory,
120-
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
130+
arguments=[
131+
"--config_suffix", config_suffix,
132+
"--json_config", jsonconfigs,
133+
"--model_name", model_name,
134+
],
121135
runconfig=run_config,
122136
inputs=[jsonconfigs],
123137
# outputs=[jsonconfigs],
124138
allow_reuse=False,
125139
)
126140
print("Step register model created")
127141

128-
package_model = PythonScriptStep(
129-
name="Package Model as Scoring Image",
130-
script_name="scoring/create_scoring_image.py",
131-
compute_target=aml_compute,
132-
source_directory=source_directory,
133-
arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
134-
runconfig=run_config,
135-
inputs=[jsonconfigs],
136-
# outputs=[jsonconfigs],
137-
allow_reuse=False,
138-
)
139-
print("Packed the model into a Scoring Image")
142+
# Package model step is moved to Azure DevOps Release Pipeline
143+
# package_model = PythonScriptStep(
144+
# name="Package Model as Scoring Image",
145+
# script_name="scoring/create_scoring_image.py",
146+
# compute_target=aml_compute,
147+
# source_directory=source_directory,
148+
# arguments=["--config_suffix", config_suffix, "--json_config", jsonconfigs],
149+
# runconfig=run_config,
150+
# inputs=[jsonconfigs],
151+
# # outputs=[jsonconfigs],
152+
# allow_reuse=False,
153+
# )
154+
# print("Packed the model into a Scoring Image")
140155

141156
# Create Steps dependency such that they run in sequence
142157
evaluate.run_after(train)
143158
register_model.run_after(evaluate)
144-
package_model.run_after(register_model)
159+
#package_model.run_after(register_model)
145160

146-
steps = [package_model]
161+
steps = [register_model]
147162

148163

149164
# Build Pipeline

aml_service/05-TriggerAmlPipeline.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
try:
3232
with open("aml_config/pipeline_config.json") as f:
3333
config = json.load(f)
34+
with open("aml_config/security_config.json") as f:
35+
security_config = json.load(f)
3436
except:
3537
print("No pipeline config found")
3638
sys.exit(0)
@@ -40,10 +42,14 @@
4042
aad_token = cli_auth.get_authentication_header()
4143
rest_endpoint1 = config["rest_endpoint"]
4244
experiment_name = config["experiment_name"]
45+
model_name = security_config["model_name"]
46+
4347
print(rest_endpoint1)
4448

4549
response = requests.post(
46-
rest_endpoint1, headers=aad_token, json={"ExperimentName": experiment_name}
50+
rest_endpoint1, headers=aad_token,
51+
json={"ExperimentName": experiment_name,
52+
"ParameterAssignments": {"model_name":model_name}}
4753
)
4854

4955
run_id = response.json()["Id"]

aml_service/30-CreateScoringImage.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,32 +36,44 @@
3636

3737
# Get the latest model details
3838

39+
# try:
40+
# with open("aml_config/model.json") as f:
41+
# config = json.load(f)
42+
# except:
43+
# print("No new model to register thus no need to create new scoring image")
44+
# # raise Exception('No new model to register as production model perform better')
45+
# sys.exit(0)
46+
47+
# model_name = config["model_name"]
48+
# model_version = config["model_version"]
49+
50+
51+
# model_list = Model.list(workspace=ws)
52+
# model, = (m for m in model_list if m.version == model_version and m.name == model_name)
53+
# print(
54+
# "Model picked: {} \nModel Description: {} \nModel Version: {}".format(
55+
# model.name, model.description, model.version
56+
# )
57+
# )
58+
3959
try:
40-
with open("aml_config/model.json") as f:
41-
config = json.load(f)
60+
with open("aml_config/security_config.json") as f:
61+
security_config = json.load(f)
4262
except:
43-
print("No new model to register thus no need to create new scoring image")
44-
# raise Exception('No new model to register as production model perform better')
63+
print("No Security Config found")
4564
sys.exit(0)
4665

47-
model_name = config["model_name"]
48-
model_version = config["model_version"]
49-
50-
51-
model_list = Model.list(workspace=ws)
52-
model, = (m for m in model_list if m.version == model_version and m.name == model_name)
53-
print(
54-
"Model picked: {} \nModel Description: {} \nModel Version: {}".format(
55-
model.name, model.description, model.version
56-
)
57-
)
66+
# Run a published pipeline
67+
#model_name = "sklearn_regression_model.pkl"
68+
model_name = security_config["model_name"]
69+
model = Model(ws, name=model_name)
5870

5971
os.chdir("./code/scoring")
6072
image_name = "diabetes-model-score"
6173

6274
image_config = ContainerImage.image_configuration(
6375
execution_script="score.py",
64-
runtime="python-slim",
76+
runtime="python",
6577
conda_file="conda_dependencies.yml",
6678
description="Image with ridge regression model",
6779
tags={"area": "diabetes", "type": "regression"},

code/register/register_model.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@
5050
type=str,
5151
help="Directory to write all the intermediate json configs",
5252
)
53+
parser.add_argument(
54+
"--model_name",
55+
type=str,
56+
help="Name of the Model",
57+
default="sklearn_regression_model.pkl",
58+
)
59+
5360
args = parser.parse_args()
5461

5562
print("Argument 1: %s" % args.config_suffix)
@@ -61,6 +68,7 @@
6168

6269
evaluate_run_id_json = "run_id_{}.json".format(args.config_suffix)
6370
evaluate_output_path = os.path.join(args.json_config, evaluate_run_id_json)
71+
model_name = args.model_name
6472

6573
# Get the latest evaluation result
6674
try:
@@ -85,7 +93,7 @@
8593
os.makedirs(model_local_dir, exist_ok=True)
8694

8795
# Download Model to Project root directory
88-
model_name = "sklearn_regression_model.pkl"
96+
# model_name = "sklearn_regression_model.pkl"
8997
run.download_file(
9098
name="./outputs/" + model_name, output_file_path="./model/" + model_name
9199
)

code/training/train.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,20 @@
4848
type=str,
4949
help="Directory to write all the intermediate json configs",
5050
)
51+
parser.add_argument(
52+
"--model_name",
53+
type=str,
54+
help="Name of the Model",
55+
default="sklearn_regression_model.pkl",
56+
)
57+
5158
args = parser.parse_args()
5259

5360
print("Argument 1: %s" % args.config_suffix)
5461
print("Argument 2: %s" % args.json_config)
5562

63+
model_name = args.model_name
64+
5665
if not (args.json_config is None):
5766
os.makedirs(args.json_config, exist_ok=True)
5867
print("%s created" % args.json_config)
@@ -80,7 +89,7 @@
8089

8190

8291
# Save model as part of the run history
83-
model_name = "sklearn_regression_model.pkl"
92+
8493
# model_name = "."
8594

8695
with open(model_name, "wb") as file:

docs/getting_started.md

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ On the next screen, click on **Save** and then click **Ok** to save the empty re
134134
![release create ](./images/release-create.png)
135135
- On the next screen click on **Create** button, this creates a manual release for you.
136136

137-
**Note**: This release pipeline will call the published AML pipeline. The AML pipeline will train the model and package it into image. It will take around 10 mins to complete. The next steps need this pipeline to complete successfully.
137+
**Note**: This release pipeline will call the published AML pipeline. The AML pipeline will train the model and package it into image. It will take around 10 mins to complete. The next steps need this pipeline to complete successfully. At this point, you can go to the Azure Portal AML WOrkspace resource created inside resource group "DevOps_AzureML_Demo" and click on the **Pipeline** tab to see the running pipeline.
138138

139139
### 7. Set up release (Deployment) pipeline
140140

@@ -174,26 +174,34 @@ Let's set up the release deployment pipeline now.
174174
- Click on **Add**, and then **Save** the pipeline
175175
![release retraining artifact](./images/release-retrainingartifact.png)
176176

177-
Here are the steps to add ACR as an artifact
177+
**Here are the steps to add [Azure ML Model as an artifact](https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.vss-services-azureml)**
178178

179-
![release retraining agent](./images/release-deployment-service-conn.png)
180-
181-
179+
180+
- Install the Azure Machine Learning extension for your DevOps organization from [here](https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.vss-services-azureml). You need to have admin rights to install it.
181+
182+
- Create Service Connection
183+
1. Go to your DevOps project and click on Project settings on bottom left corner
184+
2. Under Project Settings -> Pipelines, click on Service connections, click on "New service connection" and select Azure Resource Manager
185+
![release retraining agent](./images/service-connection.png)
186+
187+
3. Provide following info and click Ok once done:
188+
![release retraining agent](./images/service-connection-add.png)
189+
190+
182191
- Click on pipeline tab to go back to pipeline view and click **Add an artifact**. This will open a pop up window
183-
- For Source type, click on **more artifact types** dropdown and select **Azure Container Registry**
184-
- For **service connection**, select an existing service connection to Azure, if you don't see anything in the dropdown, click on **Manage** and [create new **Azure Resource Manager**](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops#create-a-service-connection) service connection for your subscription.
192+
- For Source type, click on **more artifact types** dropdown and select **AzureML Model Artifact**
193+
- For **Service Endpoint**, select an existing endpoint **MLOpsPython**, if you don't see anything in the dropdown, click on **Manage** and [create new **Azure Resource Manager**](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops#create-a-service-connection) service connection for your subscription.
194+
![release retraining agent](./images/model-artifact.png)
185195
**Note:** You must have sufficient privileges to create a service connection, if not contact your subscription adminstrator.
186-
- For Resource Group, select **DevOps_AzureML_Demo**, this is the default resource group name that we are using and if the previous pipelines executed properly you will see this resource group in the drop down.
187-
- Under Azure container registry dropdown, select the container registry, there should be only one container registry entry.
188-
- For repository, select **diabetes-model-score** repository.
189-
- For Default version, keep it to **latest**
196+
- For Model Names, select **sklearn_regression_model.pkl**, this is the name of the newly trained model and if the previous pipelines executed properly you will see this model name in the drop down.
197+
- For Default version, keep it to **Latest version**
190198
- For Source alias, keep the default generated name.
191199
- Click Add
192-
- Click on lighting sign to enable the **Continous Deployment Trigger**, click Save.
193-
![release retraining artifact](./images/release-deploymentcitrigger.png)
200+
- Click on lighting sign to enable the **Continous Deployment Trigger**, click **Save**.
201+
![release retraining artifact](./images/model-artifact-cd-trigger.png)
194202

195203

196-
1. We now have QA environment continously deployed each time there is a new image available in container registry. You can select pre-deployment conditions for prod environment, normally you don't want it to be auto deployed, so select manual only trigger here.
204+
1. We now have QA environment continously deployed each time there is a new ml model registered in AML Model Management. You can select pre-deployment conditions for prod environment, normally you don't want it to be auto deployed, so select manual only trigger here.
197205

198206
![release retraining artifact](./images/release-deploymentprodtrigger.png)
199207

@@ -202,6 +210,6 @@ Let's set up the release deployment pipeline now.
202210

203211
Congratulations, you now have three pipelines set up end to end.
204212
- Build pipeline: triggered on code change to master branch on GitHub.
205-
- Release Trigger pipeline: triggered on build pipeline execution and produces a new model image if better than previous one.
206-
- Release Deployment pipeline: QA environment is auto triggered when there is a new image.
213+
- Release Trigger pipeline: triggered on build pipeline execution and registers a new ML model to AML Model Management if better than previous one.
214+
- Release Deployment pipeline: QA environment is auto triggered when there is a new model.
207215
Prod is manual only and user decides when to release to this environment.
-226 KB
Loading
181 KB
Loading

docs/images/model-artifact.png

195 KB
Loading

0 commit comments

Comments
 (0)