Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 122 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,128 @@
# Use an official Python runtime as a parent image
FROM python:3.6-slim
# Based off of python:3.6-slim, except that we are using ubuntu instead of debian.
FROM ubuntu:16.04


# ensure local python is preferred over distribution python
ENV PATH /usr/local/bin:$PATH

# http://bugs.python.org/issue19846
# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK.
ENV LANG C.UTF-8

# runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
libexpat1 \
libffi6 \
libgdbm3 \
libreadline6 \
libsqlite3-0 \
libssl1.0.0 \
&& rm -rf /var/lib/apt/lists/*

ENV GPG_KEY 0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D
ENV PYTHON_VERSION 3.6.4

RUN set -ex \
&& buildDeps=" \
dpkg-dev \
gcc \
libbz2-dev \
libc6-dev \
libexpat1-dev \
libffi-dev \
libgdbm-dev \
liblzma-dev \
libncursesw5-dev \
libreadline-dev \
libsqlite3-dev \
libssl-dev \
make \
tcl-dev \
tk-dev \
wget \
xz-utils \
zlib1g-dev \
# as of Stretch, "gpg" is no longer included by default
$(command -v gpg > /dev/null || echo 'gnupg dirmngr') \
" \
&& apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* \
\
&& wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \
&& wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \
&& export GNUPGHOME="$(mktemp -d)" \
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY" \
&& gpg --batch --verify python.tar.xz.asc python.tar.xz \
&& rm -rf "$GNUPGHOME" python.tar.xz.asc \
&& mkdir -p /usr/src/python \
&& tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \
&& rm python.tar.xz \
\
&& cd /usr/src/python \
&& gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \
&& ./configure \
--build="$gnuArch" \
--enable-loadable-sqlite-extensions \
--enable-shared \
--with-system-expat \
--with-system-ffi \
--without-ensurepip \
&& make -j "$(nproc)" \
&& make install \
&& ldconfig \
\
&& apt-get purge -y --auto-remove $buildDeps \
\
&& find /usr/local -depth \
\( \
\( -type d -a \( -name test -o -name tests \) \) \
-o \
\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \
\) -exec rm -rf '{}' + \
&& rm -rf /usr/src/python

# make some useful symlinks that are expected to exist
RUN cd /usr/local/bin \
&& ln -s idle3 idle \
&& ln -s pydoc3 pydoc \
&& ln -s python3 python \
&& ln -s python3-config python-config

# if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value '<VERSION>'"
ENV PYTHON_PIP_VERSION 9.0.3

RUN set -ex; \
\
apt-get update; \
apt-get install -y --no-install-recommends wget; \
rm -rf /var/lib/apt/lists/*; \
\
wget -O get-pip.py 'https://bootstrap.pypa.io/get-pip.py'; \
\
apt-get purge -y --auto-remove wget; \
\
python get-pip.py \
--disable-pip-version-check \
--no-cache-dir \
"pip==$PYTHON_PIP_VERSION" \
; \
pip --version; \
\
find /usr/local -depth \
\( \
\( -type d -a \( -name test -o -name tests \) \) \
-o \
\( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \
\) -exec rm -rf '{}' +; \
rm -f get-pip.py


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

# xvfb is used to do CPU based rendering of Unity
RUN apt-get install -y xvfb


ADD python/requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ to the wider research and game developer communities.
* Built-in support for Imitation Learning
* Flexible Agent control with On Demand Decision Making
* Visualizing network outputs within the environment
* Simplified set-up with Docker (Experimental)
* Simplified set-up with Docker

## Documentation and References

Expand Down
4 changes: 2 additions & 2 deletions docs/Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

To install and use ML-Agents, you need install Unity, clone this repository
and install Python with additional dependencies. Each of the subsections
below overviews each step, in addition to an experimental Docker set-up.
below overviews each step, in addition to a Docker set-up.

## Install **Unity 2017.1** or Later

Expand Down Expand Up @@ -49,7 +49,7 @@ and run from the command line:

pip3 install .

## Docker-based Installation (Experimental)
## Docker-based Installation

If you'd like to use Docker for ML-Agents, please follow
[this guide](Using-Docker.md).
Expand Down
3 changes: 1 addition & 2 deletions docs/ML-Agents-Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,7 @@ the broadcasting feature
* **Docker Set-up (Experimental)** - To facilitate setting up ML-Agents
without installing Python or TensorFlow directly, we provide a
[guide](Using-Docker.md) on how
to create and run a Docker container. Due to limitations on rendering visual
observations, this feature is marked experimental.
to create and run a Docker container.

* **Cloud Training on AWS** - To facilitate using ML-Agents on
Amazon Web Services (AWS) machines, we provide a
Expand Down
2 changes: 1 addition & 1 deletion docs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* [Background: TensorFlow](Background-TensorFlow.md)
* [Installation & Set-up](Installation.md)
* [Background: Jupyter Notebooks](Background-Jupyter.md)
* [Docker Set-up (Experimental)](Using-Docker.md)
* [Docker Set-up](Using-Docker.md)
* [Getting Started with the 3D Balance Ball Environment](Getting-Started-with-Balance-Ball.md)
* [Example Environments](Learning-Environment-Examples.md)

Expand Down
16 changes: 8 additions & 8 deletions docs/Using-Docker.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Using Docker For ML-Agents (Experimental)
# Using Docker For ML-Agents

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 does not use a GPU and uses [`Xvfb`](https://en.wikipedia.org/wiki/Xvfb) to do visual rendering. `Xvfb` is a utility that enables `ML-Agents` (or any other application) to do rendering virtually i.e. it does not assume that the machine running `ML-Agents` has a GPU or a display attached to it. This means that rich environments which involve agents using camera-based visual observations might be slower.

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.

## Requirements
- Unity _Linux Build Support_ Component
Expand All @@ -26,13 +27,12 @@ Since Docker typically runs a container sharing a (linux) kernel with the host m
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:
- Set the _Target Platform_ to `Linux`
- Set the _Architecture_ to `x86_64`
- `Uncheck` the _Development Build_ option
- `Check` the _Headless Mode_ option. (_This is required because the Unity binary will run in a container that does not have graphics drivers installed_.)

![Build Settings For Docker](images/docker_build_settings.png)
- If the environment does not contain visual observations, you can select the `headless` option here.

Then click `Build`, pick an environment name (e.g. `3DBall`) and set the output directory to `unity-volume`. After building, ensure that the file `<environment-name>.x86_64` and subdirectory `<environment-name>_Data/` are created under `unity-volume`.

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

### Build the Docker Container

First, make sure the Docker engine is running on your machine. Then build the Docker container by calling the following command at the top-level of the repository:
Expand Down Expand Up @@ -66,7 +66,7 @@ For the `3DBall` environment, for example this would be:
```
docker run --name 3DBallContainer.first.trial \
--mount type=bind,source="$(pwd)"/unity-volume,target=/unity-volume \
balance.ball.v0.1:latest 3Dball \
balance.ball.v0.1:latest 3DBall \
--docker-target-name=unity-volume \
--train \
--run-id=3dball_first_trial
Expand All @@ -77,7 +77,7 @@ For more detail on Docker mounts, check out [these](https://docs.docker.com/stor

### Stopping Container and Saving State

If you are satisfied with the training progress, you can stop the Docker container while saving state using the following command:
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:

```
docker kill --signal=SIGINT <container-name>
Expand Down
Binary file modified docs/images/docker_build_settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 34 additions & 6 deletions python/unityagents/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
class UnityEnvironment(object):
def __init__(self, file_name, worker_id=0,
base_port=5005, curriculum=None,
seed=0):
seed=0, docker_training=False):
"""
Starts a new unity environment and establishes a connection with the environment.
Notice: Currently communication between Unity and Python takes place over an open socket without authentication.
Expand All @@ -32,6 +32,7 @@ def __init__(self, file_name, worker_id=0,
:string file_name: Name of Unity environment binary.
:int base_port: Baseline port number to connect to Unity environment over. worker_id increments over this.
:int worker_id: Number to add to communication port (5005) [0]. Used for asynchronous agent scenarios.
:param docker_training: Informs this class whether the process is being run within a container.
"""

atexit.register(self.close)
Expand Down Expand Up @@ -95,11 +96,38 @@ def __init__(self, file_name, worker_id=0,
else:
logger.debug("This is the launch string {}".format(launch_string))
# Launch Unity environment
proc1 = subprocess.Popen(
[launch_string,
'--port', str(self.port),
'--seed', str(seed)])

if docker_training == False:
proc1 = subprocess.Popen(
[launch_string,
'--port', str(self.port),
'--seed', str(seed)])
else:
"""
Comments for future maintenance:
xvfb-run is a wrapper around Xvfb, a virtual xserver where all
rendering is done to virtual memory. It automatically creates a
new virtual server automatically picking a server number `auto-servernum`.
The server is passed the arguments using `server-args`, we are telling
Xvfb to create Screen number 0 with width 640, height 480 and depth 24 bits.
Note that 640 X 480 are the default width and height. The main reason for
us to add this is because we'd like to change the depth from the default
of 8 bits to 24.
Unfortunately, this means that we will need to pass the arguments through
a shell which is why we set `shell=True`. Now, this adds its own
complications. E.g SIGINT can bounce off the shell and not get propagated
to the child processes. This is why we add `exec`, so that the shell gets
launched, the arguments are passed to `xvfb-run`. `exec` replaces the shell
we created with `xvfb`.
"""
docker_ls = ("exec xvfb-run --auto-servernum"
" --server-args='-screen 0 640x480x24'"
" {0} --port {1} --seed {2}").format(launch_string,
str(self.port),
str(seed))
proc1 = subprocess.Popen(docker_ls,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True)
self._socket.settimeout(30)
try:
try:
Expand Down
1 change: 0 additions & 1 deletion python/unitytrainers/ppo/trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ def add_experiences(self, curr_all_info: AllBrainInfo, next_all_info: AllBrainIn
:param next_all_info: Dictionary of all current brains and corresponding BrainInfo.
:param take_action_outputs: The outputs of the take action method.
"""

curr_info = curr_all_info[self.brain_name]
next_info = next_all_info[self.brain_name]

Expand Down
5 changes: 4 additions & 1 deletion python/unitytrainers/trainer_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ def __init__(self, env_path, run_id, save_freq, curriculum_file, fast_simulation
.replace('.x86', '')) # Strip out executable extensions if passed
# Recognize and use docker volume if one is passed as an argument
if docker_target_name == '':
self.docker_training = False
self.model_path = './models/{run_id}'.format(run_id=run_id)
self.curriculum_file = curriculum_file
self.summaries_dir = './summaries'
else:
self.docker_training = True
self.model_path = '/{docker_target_name}/models/{run_id}'.format(
docker_target_name=docker_target_name,
run_id=run_id)
Expand Down Expand Up @@ -74,7 +76,8 @@ def __init__(self, env_path, run_id, save_freq, curriculum_file, fast_simulation
np.random.seed(self.seed)
tf.set_random_seed(self.seed)
self.env = UnityEnvironment(file_name=env_path, worker_id=self.worker_id,
curriculum=self.curriculum_file, seed=self.seed)
curriculum=self.curriculum_file, seed=self.seed,
docker_training=self.docker_training)
self.env_name = os.path.basename(os.path.normpath(env_path)) # Extract out name of environment

def _get_progress(self):
Expand Down