Docker与深度学习

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