Skip to content

Commit dccb030

Browse files
committed
[containers] Enables container support for scenes that use visual observations
1 parent f7b6918 commit dccb030

File tree

6 files changed

+148
-14
lines changed

6 files changed

+148
-14
lines changed

Dockerfile

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,130 @@
1-
# Use an official Python runtime as a parent image
2-
FROM python:3.6-slim
1+
# Based off of python:3.6-slim, except that we are using ubuntu instead of debian.
2+
FROM ubuntu:16.04
3+
4+
5+
# ensure local python is preferred over distribution python
6+
ENV PATH /usr/local/bin:$PATH
7+
8+
# http://bugs.python.org/issue19846
9+
# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK.
10+
ENV LANG C.UTF-8
11+
12+
# runtime dependencies
13+
RUN apt-get update && apt-get install -y --no-install-recommends \
14+
ca-certificates \
15+
libexpat1 \
16+
libffi6 \
17+
libgdbm3 \
18+
libreadline6 \
19+
libsqlite3-0 \
20+
libssl1.0.0 \
21+
&& rm -rf /var/lib/apt/lists/*
22+
23+
ENV GPG_KEY 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
24+
ENV PYTHON_VERSION 3.6.4
25+
26+
RUN set -ex \
27+
&& buildDeps=" \
28+
dpkg-dev \
29+
gcc \
30+
libbz2-dev \
31+
libc6-dev \
32+
libexpat1-dev \
33+
libffi-dev \
34+
libgdbm-dev \
35+
liblzma-dev \
36+
libncursesw5-dev \
37+
libreadline-dev \
38+
libsqlite3-dev \
39+
libssl-dev \
40+
make \
41+
tcl-dev \
42+
tk-dev \
43+
wget \
44+
xz-utils \
45+
zlib1g-dev \
46+
# as of Stretch, "gpg" is no longer included by default
47+
$(command -v gpg > /dev/null || echo 'gnupg dirmngr') \
48+
" \
49+
&& apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* \
50+
\
51+
&& wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \
52+
&& wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \
53+
&& export GNUPGHOME="$(mktemp -d)" \
54+
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY" \
55+
&& gpg --batch --verify python.tar.xz.asc python.tar.xz \
56+
&& rm -rf "$GNUPGHOME" python.tar.xz.asc \
57+
&& mkdir -p /usr/src/python \
58+
&& tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \
59+
&& rm python.tar.xz \
60+
\
61+
&& cd /usr/src/python \
62+
&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
63+
&& ./configure \
64+
--build="$gnuArch" \
65+
--enable-loadable-sqlite-extensions \
66+
--enable-shared \
67+
--with-system-expat \
68+
--with-system-ffi \
69+
--without-ensurepip \
70+
&& make -j "$(nproc)" \
71+
&& make install \
72+
&& ldconfig \
73+
\
74+
&& apt-get purge -y --auto-remove $buildDeps \
75+
\
76+
&& find /usr/local -depth \
77+
\( \
78+
\( -type d -a \( -name test -o -name tests \) \) \
79+
-o \
80+
\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \
81+
\) -exec rm -rf '{}' + \
82+
&& rm -rf /usr/src/python
83+
84+
# make some useful symlinks that are expected to exist
85+
RUN cd /usr/local/bin \
86+
&& ln -s idle3 idle \
87+
&& ln -s pydoc3 pydoc \
88+
&& ln -s python3 python \
89+
&& ln -s python3-config python-config
90+
91+
# if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value '<VERSION>'"
92+
ENV PYTHON_PIP_VERSION 9.0.3
93+
94+
RUN set -ex; \
95+
\
96+
apt-get update; \
97+
apt-get install -y --no-install-recommends wget; \
98+
rm -rf /var/lib/apt/lists/*; \
99+
\
100+
wget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \
101+
\
102+
apt-get purge -y --auto-remove wget; \
103+
\
104+
python get-pip.py \
105+
--disable-pip-version-check \
106+
--no-cache-dir \
107+
"pip==$PYTHON_PIP_VERSION" \
108+
; \
109+
pip --version; \
110+
\
111+
find /usr/local -depth \
112+
\( \
113+
\( -type d -a \( -name test -o -name tests \) \) \
114+
-o \
115+
\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \
116+
\) -exec rm -rf '{}' +; \
117+
rm -f get-pip.py
118+
119+
CMD ["python3"]
120+
3121

4122
RUN apt-get update && apt-get -y upgrade
5123

124+
# xvfb is used to do CPU based rendering of Unity
125+
RUN apt-get install -y xvfb
126+
127+
6128
ADD python/requirements.txt .
7129
RUN pip install --trusted-host pypi.python.org -r requirements.txt
8130

docs/Using-Docker.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Using Docker For ML-Agents (Experimental)
22

3-
We currently offer an experimental solution for Windows and Mac users who would like to do training or inference using Docker. This option may be appealing to those who would like to avoid installing Python and TensorFlow themselves. The current setup forces both TensorFlow and Unity to _only_ rely on the CPU for computations. Consequently, our Docker support is limited to environments whose agents **do not** use camera-based visual observations. For example, the [GridWorld](Learning-Environment-Examples.md#gridworld) environment is **not** supported.
3+
We currently offer a solution for Windows and Mac users who would like to do training or inference using Docker. This option may be appealing to those who would like to avoid installing Python and TensorFlow themselves. The current setup forces both TensorFlow and Unity to _only_ rely on the CPU for computations. Consequently, our Docker simulation [uses the CPU](https://en.wikipedia.org/wiki/Xvfb) to do visual rendering. This means that environments which involve agents using camera-based visual observations might be slower.
4+
45

56
## Requirements
67
- Unity _Linux Build Support_ Component
@@ -26,8 +27,6 @@ Since Docker typically runs a container sharing a (linux) kernel with the host m
2627
Unity environment **has** to be built for the **linux platform**. When building a Unity environment, please select the following options from the the Build Settings window:
2728
- Set the _Target Platform_ to `Linux`
2829
- Set the _Architecture_ to `x86_64`
29-
- `Uncheck` the _Development Build_ option
30-
- `Check` the _Headless Mode_ option. (_This is required because the Unity binary will run in a container that does not have graphics drivers installed_.)
3130

3231
![Build Settings For Docker](images/docker_build_settings.png)
3332

@@ -77,7 +76,7 @@ For more detail on Docker mounts, check out [these](https://docs.docker.com/stor
7776

7877
### Stopping Container and Saving State
7978

80-
If you are satisfied with the training progress, you can stop the Docker container while saving state using the following command:
79+
If you are satisfied with the training progress, you can stop the Docker container while saving state by either using `Ctrl+C` or `⌘+C` (Mac) or by using the following command:
8180

8281
```
8382
docker kill --signal=SIGINT <container-name>
91.5 KB
Loading

python/unityagents/environment.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class UnityEnvironment(object):
2424
def __init__(self, file_name, worker_id=0,
2525
base_port=5005, curriculum=None,
26-
seed=0):
26+
seed=0, docker_training=False):
2727
"""
2828
Starts a new unity environment and establishes a connection with the environment.
2929
Notice: Currently communication between Unity and Python takes place over an open socket without authentication.
@@ -32,6 +32,7 @@ def __init__(self, file_name, worker_id=0,
3232
:string file_name: Name of Unity environment binary.
3333
:int base_port: Baseline port number to connect to Unity environment over. worker_id increments over this.
3434
:int worker_id: Number to add to communication port (5005) [0]. Used for asynchronous agent scenarios.
35+
:param docker_training: Informs this class whether the process is being run within a container.
3536
"""
3637

3738
atexit.register(self.close)
@@ -95,11 +96,21 @@ def __init__(self, file_name, worker_id=0,
9596
else:
9697
logger.debug("This is the launch string {}".format(launch_string))
9798
# Launch Unity environment
98-
proc1 = subprocess.Popen(
99-
[launch_string,
100-
'--port', str(self.port),
101-
'--seed', str(seed)])
102-
99+
if docker_training == False:
100+
proc1 = subprocess.Popen(
101+
[launch_string,
102+
'--port', str(self.port),
103+
'--seed', str(seed)])
104+
else:
105+
docker_ls = ("exec xvfb-run --auto-servernum"
106+
" --server-args='-screen 0 640x480x24'"
107+
" {0} --port {1} --seed {2}").format(launch_string,
108+
str(self.port),
109+
str(seed))
110+
proc1 = subprocess.Popen(docker_ls,
111+
stdout=subprocess.PIPE,
112+
stderr=subprocess.PIPE,
113+
shell=True)
103114
self._socket.settimeout(30)
104115
try:
105116
try:

python/unitytrainers/ppo/trainer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ def add_experiences(self, curr_all_info: AllBrainInfo, next_all_info: AllBrainIn
214214
:param next_all_info: Dictionary of all current brains and corresponding BrainInfo.
215215
:param take_action_outputs: The outputs of the take action method.
216216
"""
217-
218217
curr_info = curr_all_info[self.brain_name]
219218
next_info = next_all_info[self.brain_name]
220219

python/unitytrainers/trainer_controller.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ def __init__(self, env_path, run_id, save_freq, curriculum_file, fast_simulation
4242
.replace('.x86', '')) # Strip out executable extensions if passed
4343
# Recognize and use docker volume if one is passed as an argument
4444
if docker_target_name == '':
45+
self.docker_training = False
4546
self.model_path = './models/{run_id}'.format(run_id=run_id)
4647
self.curriculum_file = curriculum_file
4748
self.summaries_dir = './summaries'
4849
else:
50+
self.docker_training = True
4951
self.model_path = '/{docker_target_name}/models/{run_id}'.format(
5052
docker_target_name=docker_target_name,
5153
run_id=run_id)
@@ -74,7 +76,8 @@ def __init__(self, env_path, run_id, save_freq, curriculum_file, fast_simulation
7476
np.random.seed(self.seed)
7577
tf.set_random_seed(self.seed)
7678
self.env = UnityEnvironment(file_name=env_path, worker_id=self.worker_id,
77-
curriculum=self.curriculum_file, seed=self.seed)
79+
curriculum=self.curriculum_file, seed=self.seed,
80+
docker_training=self.docker_training)
7881
self.env_name = os.path.basename(os.path.normpath(env_path)) # Extract out name of environment
7982

8083
def _get_progress(self):

0 commit comments

Comments
 (0)