【编者的话】这篇文章介绍了 Docker 与深度学习结合的例子。Docker 的优势是解决了依赖的问题,方便分发个人工作成果;缺点是不直接支持 GPU,需要开发者自己安装 nvidia-docker。
Docker 提供了一种将 Linux Kernel 中需要的内容静态链接到你的应用中的方法。Docker 容器可以使用宿主机的 GPUs,因此我们可以把 TensorFlow 或者机器学习代码的任何依赖都链接到
容器中,这样其他小伙伴就可以使用你的工作成果了。
你可以发布一个可再现的机器学习项目,它几乎不需要用户设置,不需要用户花 6 小时去下载依赖或者报错:
- python train.py
- **加粗文字**ERROR: libobscure.so cannot open shared object
相反,你可以这样做:
- dockrun tensorflow/tensorflow:0.12.1-gpu python train.py
- TRAINING SUCCESSFUL
这种方法可以直接运行你的
脚本,所有的依赖包括 GPU 支持都帮你准备好了。
- train.py
Docker 容器是短暂的,不需要持久化任何数据,你可以把 Docker 容器想象成 1G 大的
,它把你所有的依赖都编译进去了。
- tensorflow.exe
开源软件因为有难以重现、复杂的依赖网络:不同版本的编译器、丢失头文件、错误的库路径等,所有这些导致在软件的安装配置阶段浪费你大量的时间。
使用 Docker 时,理论上你只要要让 Docker 正确工作,然后你所有的代码就可以运行了。幸运的是,Docker 已经融资 1.8 亿美元,并将一部分投入到 docker 开发中,转换成可以工作的软件。
我打算在 Linux 上用 Docker,Mac 上的用法应该一样,除了不支持 GPU。
针对机器学习的使用场景,你最好把你的代码发布到 GitHub repo 上。你的依赖通常表示成一系列 Linux 命令行,这些命令行能复制粘帖到终端中运行。( 译者注: 即依赖放到 Dockerfile 中)。
Docker 用一个命令替换第二部分( 译者注: 第一部分是把代码放到 GitHub repo 上,第二部分是在 Docker 镜像中执行命令行,配置你的依赖),该命令将拉取运行代码所需的正确 docker 镜像。你会把所有的依赖集成到 3G 大(压缩后)的镜像中,用户可以直接下载使用你的镜像。
我们看看 Torch 的实现方式:
- git clone https://github.com/phillipi/pix2pix.git
- cd pix2pix
- bash datasets/download_dataset.sh facades
...
env \
DATA_ROOT=datasets/facades \
name=facades \
niter=200 \
save_latest_freq=400 \
which_direction=BtoA \
display=0 \
gpu=0 \
cudnn=0 \
th train.lua
尽管训练脚本的依赖很少(做到这样很伟大了),但是脚本使用的工具却有很多依赖,而且这些依赖文档不全面,组合在一个镜像中非常复杂繁琐。
如果你不小心弄乱了依赖,可能会遇到下面的错误:
- luajit: symbol lookup error:
- /root/torch/install/lib/lua/5.1/libTHNN.so: undefined symbol: TH_CONVERT_ACCREAL_TO_REAL
加粗文字
在 Linux server 上直接安装 docker 和 nvidia-docker,Docker 容器可以访问 GPU,没有明显的性能损失。
如果你在 Mac 上你需要安装 ,在 Mac 上使用 Docker 我还是有很多经验的。现在你还不能在 GPU 上运行任何东西,Mac 几乎不再支持 CUDA。你可以在 CPU 模式下测试,它工作良好,只是有点慢。
我这里有一个在 Ubuntu 16.04 LTS 上安装 Docker 的脚本,适用于云服务提供商:
- curl -fsSL https://affinelayer.com/docker/setup-docker.py | sudo python3
装好 Docker 后,运行
代码:
- pix2pix
- sudo docker run --rm --volume /:/host --workdir /host$PWD affinelayer/pix2pix <command>
下面是完整的脚本,多行显示方便阅读:
- git clone https://github.com/phillipi/pix2pix.git
- cd pix2pix
- bash datasets/download_dataset.sh facades
- sudo docker run \
- --rm \
- --volume /:/host \
- --workdir /host$PWD \
- affinelayer/pix2pix \
- env \
- DATA_ROOT=datasets/facades \
- name=facades \
- niter=200 \
- save_latest_freq=400 \
- which_direction=BtoA \
- display=0 \
- gpu=0 \
- cudnn=0 \
- th train.lua
它会下载我构建的镜像(镜像包含 Torch,支持
),大小在 3G。
- nvidia-docker
运行后会打印 debug 信息,到这里已经很棒了。但运行在 GPU 上更重要,因为 pix2pix 的架构设计在 GPU 上可以获得足够的训练速度。
在 GPU 上运行只需要把上面命令中的
镜像替换成
- docker
。
- nvidia-docker
不包含在标准 Docker 中,所以你需要额外的工作。下面的脚本可以在 Ubuntu 16.04 LTS 上配置 nvidia-docker:
- nvidia-docker
- curl -fsSL https://affinelayer.com/docker/setup-nvidia-docker.py | sudo python3
大概花费 5 分钟,而且我已经在 AWS 和 Azure 上测试过了。两者都是 NVIDIA K80 卡,额定值为 2.9 FP32 TFLOPS。
nvidia-docker 配置好运行:
- sudo nvidia-docker run --rm nvidia/cuda nvidia-smi
假设上面的命令正常运行,重新运行 pix2pix 的脚本:
- sudo nvidia-docker run \
- --rm \
- --volume /:/host \
- --workdir /host$PWD \
- affinelayer/pix2pix \
- env \
- DATA_ROOT=datasets/facades \
- name=facades \
- niter=200 \
- save_latest_freq=400 \
- which_direction=BtoA \
- display=0 \
- th train.lua
它使用相同的 docker 镜像,但是支持 GPU。
使用 Python 和 TensorFlow 时,有许多有用的命令行选项:
- --env PYTHONUNBUFFERED=x
这会让 python 立即打印输出,而不是先缓存起来。
- --env CUDA_CACHE_PATH=/host/tmp/cuda-cache
这使得你每次启动 Tensorflow 时都没有 1 分钟的延迟,它必须从头重新编译 CUDA 内核。
这两个选项集成到 Docker 命令行中后:
- sudo nvidia-docker run \
- --rm \
- --volume /:/host \
- --workdir /host$PWD \
- --env PYTHONUNBUFFERED=x \
- --env CUDA_CACHE_PATH=/host/tmp/cuda-cache \
- <image> \
- <command>
这个命令很长,你可以定义命令别名:
- alias dockrun="sudo nvidia-docker run --rm --volume /:/host --workdir /host\$PWD --env PYTHONUNBUFFERED=x --env CUDA_CACHE_PATH=/host/tmp/cuda-cache"
定义别名后运行 :
- git clone https://github.com/affinelayer/pix2pix-tensorflow.git
- cd pix2pix-tensorflow
- python tools/download-dataset.py facades
- dockrun affinelayer/pix2pix-tensorflow python pix2pix.py \
- --mode train \
- --output_dir facades_train \
- --max_epochs 200 \
- --input_dir facades/train \
- --which_direction BtoA
除了 Tensorflow 0.12.1(当时当前发布的版本)之外没有别的依赖关系。但是即使如此,第一个 GitHub issue 是一个用户使用错误版本的 Tensorflow 导致的。
- pix2pix-tensorflow
幸运地是,集成到你自己的项目中非常简单。
你先新建空目录,新建文件 Dockerfile。然后构建镜像:
- mkdir docker-build
- cd docker-build
- curl -O https://affinelayer.com/docker/Dockerfile
- sudo docker build --rm --no-cache --tag pix2pix .
构建结束后你就可以查看镜像了:
- sudo docker images pix2pix
- REPOSITORY TAG IMAGE ID CREATED SIZE
- pix2pix latest bf5bd6bb35f8 3 seconds ago 11.38 GB
通过 docker push 把你的镜像推送到 Docker Hub 上:
- sudo docker tag pix2pix <accountname>/pix2pix
- sudo docker push <accountname>/pix2pix
Docker 新用户可以先使用我的镜像。docker 提供了不依赖 Docker Hub 分发镜像的机制,但是他用起来不是很方便:
sudo docker save pix2pix | gzip > pix2pix.image.gz
gunzip --stdout pix2pix.image.gz | sudo docker load
虽然 Docker 镜像容易复制,但是从 Dockerfile 到镜像的转换不一定是可复制的。你可以使用下面的命令检查镜像的构建历史记录:
- sudo docker history --no-trunc pix2pix
它不会显示被添加到镜像中的所有文件。比如,如果你的 Dockerfile 包含
或者
- git clone
,很可能在两个不同的日子里构建产生不同的镜像。
- apt-get update
另外,如果 docker 构建时指定了 cpu 版本,那么镜像在其它机器上很可能不工作。
只要我们分发的是 Docker 镜像,那么它就是可再现的。如果你想通过 Dockerfile 再现镜像,如果你不非常小心编写构建 Dockerfile 的话,很可能失败。( 译者注: 镜像构建好后不会变,可再现,但是从 Dockerfile 构建,很可能因为 cpu 版本、git clone 仓库更新而不可再现镜像)
目前还不清楚这些优势是否值得付出努力,但是如果你的 Dockerfile 是从头开始构建的,并且使用
选项,大多数情形镜像是可重现的。
- --network none
如果镜像再现很容易,这项技术将会很酷。现在 Docker 已经取得实质性进展,让依赖再现成为可能,这是伟大的进步。
这篇文章介绍了 Docker 与深度学习结合的例子。Docker 的优势是解决了依赖的问题,方便分发个人工作成果;缺点是不直接支持 GPU,需要开发者自己安装 nvidia-docker。
文章最后作者论述了 Docker 镜像的可再现问题,总结如下:
来源: http://www.tuicool.com/articles/363ENjz