Skip to content

Commit 092d65b

Browse files
committed
More WIP BYO stuff
1 parent afc07cd commit 092d65b

File tree

3 files changed

+165
-26
lines changed

3 files changed

+165
-26
lines changed

scikit_bring_your_own/container/decision_trees/train

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,20 @@ def train():
6565
# save the model
6666
with open(os.path.join(model_path, 'decision-tree-model.pkl'), 'w') as out:
6767
pickle.dump(clf, out)
68-
69-
# Write out the success file
70-
with open(os.path.join(output_path, 'success'), 'w') as s:
71-
s.write('Done')
7268
print('Training complete.')
7369
except Exception as e:
74-
# Write out an error file
75-
# Either a failure file or non-zero exit code will indicate failure. Here we do
76-
# both.
70+
# Write out an error file. This will be returned as the failureReason in the
71+
# DescribeTrainingJob result.
7772
trc = traceback.format_exc()
7873
with open(os.path.join(output_path, 'failure'), 'w') as s:
7974
s.write('Exception during training: ' + str(e) + '\n' + trc)
75+
# Printing this causes the exception to be in the training job logs, as well.
8076
print('Exception during training: ' + str(e) + '\n' + trc, file=sys.stderr)
77+
# A non-zero exit code causes the training job to be marked as Failed.
8178
sys.exit(255)
8279

8380
if __name__ == '__main__':
8481
train()
82+
83+
# A zero exit code causes the job to be marked a Succeeded.
8584
sys.exit(0)

scikit_bring_your_own/scikit_bring_your_own.ipynb

Lines changed: 159 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,27 @@
1010
"\n",
1111
"By packaging an algorithm in a container, you can bring almost any code to the Amazon SageMaker environment, regardless of programming language, environment, framework, or dependencies. \n",
1212
"\n",
13-
"_TODO: Insert TOC here_\n",
13+
"_TODO: make sure TOC is up-to-date_\n",
14+
"\n",
15+
"1. [Building your own algorithm container](#Building-your-own-algorithm-container)\n",
16+
" 1. [When should I build my own algorithm container?](#When-should-I-build-my-own-algorithm-container?)\n",
17+
" 1. [Permissions](#Permissions)\n",
18+
" 1. [The example](#The-example)\n",
19+
" 1. [The presentation](#The-presentation)\n",
20+
"1. [Part 1: Packaging and Uploading your Algorithm for use with Amazon SageMaker](#Part-1:-Packaging-and-Uploading-your-Algorithm-for-use-with-Amazon-SageMaker)\n",
21+
" 1. [An overview of Docker](#An-overview-of-Docker)\n",
22+
" 1. [How Amazon SageMaker runs your Docker container](#How-Amazon-SageMaker-runs-your-Docker-container)\n",
23+
" 1. [Running your container during training](#Running-your-container-during-training)\n",
24+
" 1. [The input](#The-input)\n",
25+
" 1. [The output](#The-output)\n",
26+
" 1. [Running your container during hosting](#Running-your-container-during-hosting)\n",
27+
" 1. [The parts of the sample container](#The-parts-of-the-sample-container)\n",
28+
" 1. [The Dockerfile](#The-Dockerfile)\n",
29+
" 1. [Testing your algorithm on your local machine or on an Amazon SageMaker notebook instance](#Testing-your-algorithm-on-your-local-machine-or-on-an-Amazon-SageMaker-notebook-instance)\n",
30+
"1. [Part 2: Training and Hosting your Algorithm in Amazon SageMaker](#Part-2:-Training-and-Hosting-your-Algorithm-in-Amazon-SageMaker)\n",
31+
" 1. [Upload the data for training](#Upload-the-data-for-training)\n",
32+
" \n",
33+
"_or_ I'm impatient, just [let me see the code](#The-Dockerfile)!\n",
1434
"\n",
1535
"## When should I build my own algorithm container?\n",
1636
"\n",
@@ -20,18 +40,23 @@
2040
"\n",
2141
"If there isn't direct SDK support for your environment, don't worry. You'll see in this walk-through that building your own container is quite straightforward.\n",
2242
"\n",
23-
"## The example\n",
43+
"## Permissions\n",
44+
"\n",
45+
"Running this notebook requires permissions in addition to the normal `SageMakerFullAccess` permissions. This is because we'll creating new repositories in Amazon ECR. The easiest way to add these permissions is simply to add the managed policy `AmazonEC2ContainerRegistryPowerUser` to the role that you used to start your notebook instance. There's no need to start your notebook instance when you do this, the new permissions will be available immediately.\n",
2446
"\n",
25-
"TODO: links\n",
47+
"## The example\n",
2648
"\n",
27-
"Here, we'll show how to package a simple Python example which showcases the decision tree algorithm from the widely used scikit-learn machine learning package. The example is purposefully fairly trivial since the point is to show the surrounding structure that you'll want to add to your own code so you can train and host it in Amazon SageMaker.\n",
49+
"Here, we'll show how to package a simple Python example which showcases the [decision tree][] algorithm from the widely used [scikit-learn][] machine learning package. The example is purposefully fairly trivial since the point is to show the surrounding structure that you'll want to add to your own code so you can train and host it in Amazon SageMaker.\n",
2850
"\n",
2951
"The ideas shown here will work in any language or environment. You'll need to choose the right tools for your environment to serve HTTP requests for inference, but good HTTP environments are available in every language these days.\n",
3052
"\n",
3153
"In this example, we use a single image to support training and hosting. This is easy because it means that we only need to manage one image and we can set it up to do everything. Sometimes you'll want separate images for training and hosting because they have different requirements. Just separate the parts discussed below into separate Dockerfiles and build two images. Choosing whether to have a single image or two images is really a matter of which is more convenient for you to develop and manage.\n",
3254
"\n",
3355
"If you're only using Amazon SageMaker for training or hosting, but not both, there is no need to build the unused functionality into your container.\n",
3456
"\n",
57+
"[scikit-learn]: http://scikit-learn.org/stable/\n",
58+
"[decision tree]: http://scikit-learn.org/stable/modules/tree.html\n",
59+
"\n",
3560
"## The presentation\n",
3661
"\n",
3762
"This presentation is divided into two parts: _building_ the container and _using_ the container."
@@ -41,13 +66,10 @@
4166
"cell_type": "markdown",
4267
"metadata": {},
4368
"source": [
44-
"# Part 1: Packaging and Uploading your Algorithm for use with Amazon SageMaker \n",
69+
"# Part 1: Packaging and Uploading your Algorithm for use with Amazon SageMaker\n",
4570
"\n",
4671
"### An overview of Docker\n",
4772
"\n",
48-
"TODO: links to docker, docker run, and Dockerfile reference. and ECS\n",
49-
"TODO: check virtualenv spelling\n",
50-
"\n",
5173
"If you're familiar with Docker already, you can skip ahead to the next section.\n",
5274
"\n",
5375
"For many data scientists, Docker containers are a new concept, but they are not difficult, as you'll see here. \n",
@@ -60,12 +82,21 @@
6082
"\n",
6183
"Docker uses a simple file called a `Dockerfile` to specify how the image is assembled. We'll see an example of that below. You can build your Docker images based on Docker images built by yourself or others, which can simplify things quite a bit.\n",
6284
"\n",
63-
"Docker has become very popular in the programming and devops communities for its flexibility and well-defined specification of the code to be run. It is the underpinning of many services built in the past few years, such as Amazon ECS.\n",
85+
"Docker has become very popular in the programming and devops communities for its flexibility and well-defined specification of the code to be run. It is the underpinning of many services built in the past few years, such as [Amazon ECS].\n",
6486
"\n",
6587
"Amazon SageMaker uses Docker to allow users to train and deploy arbitrary algorithms.\n",
6688
"\n",
6789
"In Amazon SageMaker, Docker containers are invoked in a certain way for training and a slightly different way for hosting. The following sections outline how to build containers for the SageMaker environment.\n",
6890
"\n",
91+
"Some helpful links:\n",
92+
"\n",
93+
"* [Docker home page](http://www.docker.com)\n",
94+
"* [Getting started with Docker](https://docs.docker.com/get-started/)\n",
95+
"* [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)\n",
96+
"* [`docker run` reference](https://docs.docker.com/engine/reference/run/)\n",
97+
"\n",
98+
"[Amazon ECS]: https://aws.amazon.com/ecs/\n",
99+
"\n",
69100
"### How Amazon SageMaker runs your Docker container\n",
70101
"\n",
71102
"Because you can run the same image in training or hosting, Amazon SageMaker runs your container with the argument `train` or `serve`. How your container processes this argument depends on the container:\n",
@@ -76,15 +107,50 @@
76107
"\n",
77108
"#### Running your container during training\n",
78109
"\n",
79-
"The container is run with the argument \"train\"\n",
110+
"When Amazon SageMaker runs training, your `train` script is run just like a regular Python program. A number of files are laid out for your use, under the `/opt/ml` directory:\n",
80111
"\n",
81-
"The container gets some special files:\n",
112+
" /opt/ml\n",
113+
" ├── input\n",
114+
" │   ├── config\n",
115+
" │   │   ├── hyperparameters.json\n",
116+
" │   │   └── resourceConfig.json\n",
117+
" │   └── data\n",
118+
" │   └── <channel_name>\n",
119+
" │   └── <input data>\n",
120+
" ├── model\n",
121+
" │   └── <model files>\n",
122+
" └── output\n",
123+
" └── failure\n",
82124
"\n",
83-
"TODO: Insert overview of file system here\n",
125+
"##### The input\n",
126+
"\n",
127+
"* `/opt/ml/input/config` contains information to control how your program runs. `hyperparameters.json` is a JSON-formatted dictionary of hyperparameter names to values. These values will always be strings, so you may need to convert them. `resourceConfig.json` is a JSON-formatted file that describes the network layout used for distributed training. Since scikit-learn doesn't support distributed training, we'll ignore it here.\n",
128+
"* `/opt/ml/input/data/<channel_name>/` (for File mode) contains the input data for that channel. The channels are created based on the call to CreateTrainingJob but it's generally important that channels match what the algorithm expects. The files for each channel will be copied from S3 to this directory, preserving the tree structure indicated by the S3 key structure. \n",
129+
"* `/opt/ml/input/data/<channel_name>_<epoch_number>` (for Pipe mode) is the pipe for a given epoch. Epochs start at zero and go up by one each time you read them. There is no limit to the number of epochs that you can run, but you must close each pipe before reading the next epoch.\n",
130+
"\n",
131+
"##### The output\n",
132+
"\n",
133+
"* `/opt/ml/model/` is the directory where you write the model that your algorithm generates. Your model can be in any format that you want. It can be a single file or a whole directory tree. SageMaker will package any files in this directory into a compressed tar archive file. This file will be available at the S3 location returned in the `DescribeTrainingJob` result.\n",
134+
"* `/opt/ml/output` is a directory where the algorithm can write a file `failure` that describes why the job failed. The contents of this file will be returned in the `FailureReason` field of the `DescribeTrainingJob` result. For jobs that succeed, there is no reason to write this file as it will be ignored.\n",
84135
"\n",
85136
"#### Running your container during hosting\n",
86137
"\n",
87-
"The container is run with the argument \"serve\". \n",
138+
"Hosting has a very different model that training because hosting is reponding to inference requests that come in via HTTP. In this example, we use our recommended Python serving stack to provide robust and scalable serving of inference requests:\n",
139+
"\n",
140+
"![Request serving stack](stack.png)\n",
141+
"\n",
142+
"This stack is implemented in the sample code here and you can mostly just leave it alone. \n",
143+
"\n",
144+
"Amazon SageMaker uses two URLs in the container:\n",
145+
"\n",
146+
"* `/ping` will receive `GET` requests from the infrastructure. Your program returns 200 if the container is up and accepting requests.\n",
147+
"* `/invocations` is the endpoint that receives client inference `POST` requests. The format of the request and the response is up to the algorithm. If the client supplied `ContentType` and `Accept` headers, these will be passed in as well. \n",
148+
"\n",
149+
"The container will have the model files in the same place they were written during training:\n",
150+
"\n",
151+
" /opt/ml\n",
152+
" └── model\n",
153+
"    └── <model files>\n",
88154
"\n"
89155
]
90156
},
@@ -128,20 +194,96 @@
128194
"\n"
129195
]
130196
},
197+
{
198+
"cell_type": "markdown",
199+
"metadata": {},
200+
"source": [
201+
"### The Dockerfile\n",
202+
"\n",
203+
"The Dockerfile describes the image that we want to build. You can think of it as describing the complete operating system installation of the system that you want to run. A Docker container running is quite a bit lighter than a full operating system, however, because it takes advantage of Linux on the host machine for the basic operations. \n",
204+
"\n",
205+
"For the Python science stack, we will start from a standard Ubuntu installation and run the normal tools to install the things needed by scikit-learn. Finally, we add the code that implements our specific algorithm to the container and set up the right environment to run under.\n",
206+
"\n",
207+
"Along the way, we clean up extra space. This makes the container smaller and faster to start.\n",
208+
"\n",
209+
"Let's look at the Dockerfile for the example:"
210+
]
211+
},
131212
{
132213
"cell_type": "code",
133-
"execution_count": null,
214+
"execution_count": 23,
134215
"metadata": {},
135-
"outputs": [],
216+
"outputs": [
217+
{
218+
"name": "stdout",
219+
"output_type": "stream",
220+
"text": [
221+
"# Build an image that can do training and inference in SageMaker\r\n",
222+
"# This is a Python 2 image that uses the nginx, gunicorn, flask stack\r\n",
223+
"# for serving inferences in a stable way.\r\n",
224+
"\r\n",
225+
"FROM ubuntu:16.04\r\n",
226+
"\r\n",
227+
"MAINTAINER Amazon AI <[email protected]>\r\n",
228+
"\r\n",
229+
"\r\n",
230+
"RUN apt-get -y update && apt-get install -y --no-install-recommends \\\r\n",
231+
" wget \\\r\n",
232+
" python \\\r\n",
233+
" nginx \\\r\n",
234+
" ca-certificates \\\r\n",
235+
" && rm -rf /var/lib/apt/lists/*\r\n",
236+
"\r\n",
237+
"# Here we get all python packages.\r\n",
238+
"# There's substantial overlap between scipy and numpy that we eliminate by\r\n",
239+
"# linking them together. Likewise, pip leaves the install caches populated which uses\r\n",
240+
"# a significant amount of space. These optimizations save a fair amount of space in the\r\n",
241+
"# image, which reduces start up time.\r\n",
242+
"RUN wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py && \\\r\n",
243+
" pip install numpy scipy scikit-learn pandas flask gevent gunicorn && \\\r\n",
244+
" (cd /usr/local/lib/python2.7/dist-packages/scipy/.libs; rm *; ln ../../numpy/.libs/* .) && \\\r\n",
245+
" rm -rf /root/.cache\r\n",
246+
"\r\n",
247+
"# Set some environment variables. PYTHONUNBUFFERED keeps Python from buffering our standard\r\n",
248+
"# output stream, which means that logs can be delivered to the user quickly. PYTHONDONTWRITEBYTECODE\r\n",
249+
"# keeps Python from writing the .pyc files which are unnecessary in this case. We also update\r\n",
250+
"# PATH so that the train and serve programs are found when the container is invoked.\r\n",
251+
"\r\n",
252+
"ENV PYTHONUNBUFFERED=TRUE\r\n",
253+
"ENV PYTHONDONTWRITEBYTECODE=TRUE\r\n",
254+
"ENV PATH=\"/opt/program:${PATH}\"\r\n",
255+
"\r\n",
256+
"# Make nginx log to stdout/err so that the log messages will be picked up by the\r\n",
257+
"# Docker logger\r\n",
258+
"RUN ln -s /dev/stdout /tmp/nginx.access.log && ln -s /dev/stderr /tmp/nginx.error.log\r\n",
259+
"\r\n",
260+
"# Set up the program in the image\r\n",
261+
"COPY decision_trees /opt/program\r\n",
262+
"WORKDIR /opt/program\r\n",
263+
"\r\n"
264+
]
265+
}
266+
],
136267
"source": [
137268
"!cat container/Dockerfile"
138269
]
139270
},
140271
{
141272
"cell_type": "code",
142-
"execution_count": null,
273+
"execution_count": 24,
143274
"metadata": {},
144-
"outputs": [],
275+
"outputs": [
276+
{
277+
"name": "stderr",
278+
"output_type": "stream",
279+
"text": [
280+
"sh: 25: docker: not found\n",
281+
"sh: 29: docker: not found\n",
282+
"sh: 30: docker: not found\n",
283+
"sh: 32: docker: not found\n"
284+
]
285+
}
286+
],
145287
"source": [
146288
"%%sh\n",
147289
"\n",
@@ -150,8 +292,6 @@
150292
"\n",
151293
"cd container\n",
152294
"\n",
153-
"#set -e # stop if anything fails\n",
154-
"\n",
155295
"account=$(aws sts get-caller-identity --query Account --output text)\n",
156296
"\n",
157297
"# Get the region defined in the current configuration (default to us-west-2 if none defined)\n",

scikit_bring_your_own/stack.png

13.3 KB
Loading

0 commit comments

Comments
 (0)