最近使用linux环境,发现这个系统给的root权限太高了,常常配环境和依赖库的时候不小心把别的变量改了,或者是装错了依赖库版本,导致系统无法运行;或者一个算法和另一个算法之间的依赖库版本不兼容,导致要换依赖库的版本非常痛苦。
但在linux中,有一个非常好用的技术–docker,它和虚拟机不同,运行代价小,而且可以达到虚拟机快速切换系统环境的功能,可以说是小而精。使用docker部署环境与依赖库,已经成为了一种新的技术风向。在tensorflow contribution guideline中,官方指定nvidia-docker为编译和修改代码时的环境配置工具(nvidia-docker 在windows中不可用,详见这里)。
Docker简介
以下内容的docker部分参考自Docker — 从入门到实践。如果您之前没有接触过docker,强烈建议结合其中的基本概念、安装docker、使用镜像、操作容器、访问仓库的相关章节,来理解下面的内容。
nvdia-docker
如果您需要使用cuda和cudnn相关的库,则应使用nvdia-docker。请在安装docker以后,按照Qucikstart中的步骤安装nvdia-docker插件。
虚拟机和Docker
虚拟机技术(virtual machine)是模拟一个完整的计算机系统。它将一整个系统建立在虚拟化的硬件(infrastructure)之上,而虚拟机软件(hypervisor),是架起了连接虚拟系统和显示硬件的桥梁。
由于需要模拟一整套虚拟系统与虚拟硬件,虚拟机技术会占用大量的硬件资源。这时人们创造了另一种虚拟形式–docker容器。
docker容器(container)是建立在宿主机硬件和系统上的技术,所以不同容器间的系统资源和核心库是共享的,这意味着,你使用下列两行代码得到的结果,竟然是一样的:
sudo docker run --rm ubuntu:14.04 uname -r
sudo docker run --rm ubuntu:18.04 uname -r
结果是:
4.15.0-46-generic
4.15.0-46-generic
这两段的代码做的事情差不多,分别在ubuntu14.04和ubuntu18.04中运行uname命令查看linux核心版本号,你得到的版本号应该是你宿主机系统(Host OS)的版本。而以下两段代码,得到的结果是不一样的
sudo docker run --rm ubuntu:14.04 cat /etc/issue
sudo docker run --rm ubuntu:18.04 cat /etc/issue
结果分别是
Ubuntu 14.04.5 LTS \n \l
Ubuntu 18.04.1 LTS \n \l
因为/etc/issue文件不属于系统文件,而是容器中的文件。
虚拟机 | docker容器 | |
---|---|---|
优点 | 可以调用所有虚拟系统资源与核心库 | 极少运行资源 类似git的分层部署方式,避免冗余 |
缺点 | 运行资源占用大 | 非linux系统支持 |
Docker在windows/mac中
docker在windows和mac中,首先会运行一个docker desktop客户端,这个客户端相当于一个小型的linux虚拟机,然后在该虚拟机中启动docker客户端程序,所以docker在这两个系统中的支持和运行速度都比在Linux中(实现方式为启动守护程序)要慢一个些。
所以如果您想要体验原生的docker,推荐使用linux系统。
Dockerfile的编写方式
在《Docker — 从入门到实践》中,推荐使用dockerfile的方式构建镜像,笔者也强烈推荐这个方式。之前我曾使用docker commit方式构建的镜像,大小约为dockerfile构建的5倍左右,冗余非常严重,而且由于docker镜像的层层构建方式,每次commit会导致镜像越来越大,除非确保做好每次的清理工作。
本次我们使用dockerfile来构建镜像,会用到两个命令:RUN和ENV。
- RUN:docker中的RUN、CMD、ENTRYPOINT都可以干类似的事情,即运行shell命令。具体区别可以参考这。一般来说,我们会使用RUN命令构建多层镜像。
- ENV:它可以设定新镜像中的系统环境变量,以免运行时找不到依赖库。
以下是构建SNIPER运行环境的dockerfile:
# 下面这段进行编译环境布置
FROM nvidia/cuda:9.2-cudnn7-devel-ubuntu16.04 as env
USER root
RUN apt-get update \
&& apt-get -y install wget locales git bzip2 curl \
&& rm -rf /var/lib/apt/lists/*
RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen \
&& locale-gen en_US.utf8 \
&& /usr/sbin/update-locale LANG=en_US.UTF-8
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8%
ENV LD_LIBRARY_PATH /usr/local/cuda/lib64
RUN apt-get update \
&& apt-get -y install \
libatlas-base-dev \
libopencv-dev \
libopenblas-dev \
&& rm -rf /var/lib/apt/lists/*
# 下面这段进行运行库布置
FROM sniper/env as prod
RUN apt-get update \
&& apt-get -y install python-pip
RUN pip install opencv-python==3.2.0.8 \
Cython \
matplotlib==2.2.4 \
numpy \
scipy \
pyyaml \
EasyDict \
protobuf \
argparse \
scikit-image \
tqdm \
futures\
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf ~/.cache/pip/*
ENV LD_LIBRARY_PATH /usr/lib/x86_64-linux-gnu:/usr/local/cuda/lib64
ENV PYTHONPATH /root/SNIPER/SNIPER-mxnet/python:/root/SNIPER/lib
文件已上传github,地址在此。
构建镜像
下载dockerfile到本地
使用以下命令构建镜像:
sudo nvidia-docker build -t sniper .
由于dockerfile是分步写的,所以您也可以使用以下命令构建:
sudo nvidia-docker build --target env -t sniper/env .
sudo nvidia-docker build --target prod -t sniper/prod .
构建完的镜像大小为4.01GB。
第二种方式中,你在env编好之后就可以开始SNIPER-mxnet的编译工作,prod只是增加以下SNIPER的运行库。无论您使用哪种方式, 不要忘记复制代码最后的"."。
SNIPER简介
SNIPER是一种目标检测算法,在coco2017和voc 2007数据集上,都实现了state-of-the-art的检出精度。这里是论文和代码。
您可以按照这里的第一步clone代码
在按照流程构建镜像之后,您可以使用以下代码开启一个容器(默认进入bash)
sudo nvidia-docker run -it -rm -v /root/SNIPER:/home/xxx/python/SNIPER/ sniper/prod
把这里的/home/xxx
替换为您自己的SNIPER路径,然后开始编译SNIPER-mxnet代码
cd /root/SNIPER/SNIPER-mxnet/
make USE_CUDA_PATH=/usr/local/cuda
cd ..
bash scripts/compile.sh
使用make编译完成后,您会得到libmxnet.so:
运行scripts/compile.sh后,在nms/bbox/chips中,能看到下面的.so文件
运行SNIPER/demo.py
编译好之后,先运行scipt/download_sniper_detector.sh下载预训练模型,再运行demo.py,发现以下输出结果,大功告成!
关于如何在pycharm中无缝使用nvidia-docker环境请参考这里。
最后,祝您身体健康,再见!