• docker配置深度学习环境


    docker配置深度学习环境

    版权声明:本文为博主原创文章,转载注明出处即可。 https://blog.csdn.net/bskfnvjtlyzmv867/article/details/81017226

    阅读本篇文章可以帮你解决的问题是:提供一套解决方案,能够在支持Docker的任何版本Ubuntu系统下,搭建出完美运行各种深度学习框架各种版本各种环境依赖(NAIVID显卡)深度学习工程的开发环境。不仅如此,还要像在本机一样方便的修改代码运行计算。

    搭建深度学习计算平台,一般需要我们在本机上安装一些必要的环境,安装系统、显卡驱动、cuda、cudnn等。而随着Docker的流行,往往能够帮我们轻松的进行环境搭建、复制与隔离,所以官方也利用容器技术与深度学习相结合,因此也出现了以下方案。

    容器方案比传统方案带来更多的随意性,装系统前不需要考虑Ubuntu哪一个版本符合不符合我们的代码运行要求,我们只需要安装一个自己喜欢的(18.04完全可以),再在官网下载一下显卡驱动,或者软件源附加驱动更新一下就行了,剩下都不需要我们继续考虑。这些也非常的轻松,因为Nvidia对Ubuntu的支持越来越友好,我们只需要下载deb包,一行命令即可安装成功。

    系统 显卡驱动 Cuda Cudnn
    传统方案 一种版本 必需 一种版本 必需
    容器方案 各种版本 必需 非必需 非必需

    安装显卡驱动可以参照:https://blog.csdn.net/bskfnvjtlyzmv867/article/details/80102000

    正式进入正文之前,确保你已经安装好趁手的系统和显卡驱动。

    I. 安装Docker

    关于Docker教程,详见:Docker——入门实战

    安装指定版本Docker CE

    这里的版本由第二部分的Nvidia Docker依赖决定,笔者在写此文时需要的版本是18.03.1,如果在安装Nvidia Docker时依赖的Docker CE版本已经变更,可以卸载重新安装需要的版本。

    sudo apt install curl
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial edge" | sudo tee /etc/apt/sources.list.d/docker.list
    sudo apt-get update && sudo apt-get install -y docker-ce=18.03.1~ce-0~ubuntu1234

    执行这个命令后,脚本就会自动的将一切准备工作做好,并且把Docker CE 的Edge版本安装在系统中。

    启动Docker CE

    sudo systemctl enable docker
    sudo systemctl start docker12

    建立docker 用户组

    默认情况下,docker 命令会使用Unix socket 与Docker 引擎通讯。而只有root 用户和docker 组的用户才可以访问Docker 引擎的Unix socket。出于安全考虑,一般Ubuntu系统上不会直接使用root 用户。因此,更好地做法是将需要使用docker 的用户加入docker用户组。

    # 建立docker组
    sudo groupadd docker
    # 将当前用户加入docker组
    sudo usermod -aG docker $USER1234

    注销当前用户,重新登录Ubuntu,输入docker info,此时可以直接出现信息。

    img

    配置国内镜像加速

    在/etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)

    {
        "registry-mirrors": [
            "https://registry.docker-cn.com"
        ]
    }12345

    重新启动服务

    sudo systemctl daemon-reload
    sudo systemctl restart docker12

    II. 安装Nvidia Docker2

    Nvidia Docker2项目的主页:https://github.com/NVIDIA/nvidia-docker

    # If you have nvidia-docker 1.0 installed: we need to remove it and all existing GPU containers
    ocker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
    sudo apt-get purge -y nvidia-docker
    
    # Add the package repositories
    curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
    curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu16.04/amd64/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
    
    # Install nvidia-docker2 and reload the Docker daemon configuration
    sudo apt-get update && sudo apt-get install -y nvidia-docker2
    sudo pkill -SIGHUP dockerd
    
    # Test nvidia-smi with the latest official CUDA image
    docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi1234567891011121314

    img

    III. 搭建环境

    拉取镜像

    Nvidia官网在DockerHub中提供了关于深度学习的各个版本环境,点我…Ubuntu14.04-18.04,Cuda6.5-9.2,Cudnn4-7,基本含盖了我们所需要的各种版本的深度学习环境,我们直接拉取镜像,在已有的镜像基础上配置我们的深度学习环境。

    img

    下载镜像,这里以ubuntu16.04、cuda8.0、cudnn5.1的版本为例,我们找到满足版本要求的TAG为8.0-cudnn5-devel-ubuntu16.04。

    # 拉取镜像
    docker pull nvidia/cuda:8.0-cudnn5-devel-ubuntu16.04
    # 查看镜像
    docker images -a1234

    创建启动容器

    利用下载好的镜像,创建一个交互式的容器。容器需要使用nvidia显卡,需要设置额外的参数。-p 8022:22

    docker run -it --name 自定义容器名 -v /home/你的用户名/mnist/:/home/你的用户名/mnist/ --runtime=nvidia -e NVIDIA_VISIBLE_DEVICE=0,1 nvidia/cuda:8.0-cudnn5-devel-ubuntu16.041

    NVIDIA_VISIBLE_DEVICE参数指定对容器分配几块GPU资源;-v参数用于挂载本地目录,冒号前为宿主机目录,冒号后为容器目录,两个可以设置为一样比较方便代码书写。配置目录挂载是为了方便本文下一部分测试Mnist服务。容器启动完毕,此时,可以像正常本机配置的深度学习环境一样,测试各个软件的版本。

    nvidia-smi
    nvcc -V
    # 查看cudnn版本
    cd /usr/lib/x86_64-linux-gnu/
    ll |grep cudnn12345

    img

    img

    IV. 构建环境

    安装环境

    在上一部分我们搭建了深度学习计算的必要环境,包括CUDA和CUDNN。然而大多数深度学习环境都是需要执行Python编写的深度学习代码的,甚至需要一些常用的深度学习框架,如TensorFlow、PyTorch等。在上一部分我们拉取的Nvidia官方提供的镜像中并没有包含Python运行环境以及任何的深度学习框架,需要我们自己安装。

    附上安装环境的所有命令:

    apt-get update
    # 安装Python2.7环境 3.+版本自行添加
    apt-get install -y --no-install-recommends build-essential curl libfreetype6-dev libpng12-dev libzmq3-dev pkg-config python python-dev python-pip python-qt4 python-tk git vim
    apt-get clean
    ## 安装深度学习框架 自行添加
    pip --no-cache-dir install setuptools
    pip --no-cache-dir install tensorflow-gpu==1.2 opencv-python Pillow scikit-image matplotlib1234567

    构建镜像

    安装好环境后,其实已经可以开始运行我们的深度学习代码了。如果你想立刻测试自己的Docker深度学习环境搭建成功与否,可以直接开始下一部分的Mnist数据集测试。

    如果此时,项目组另一位小伙伴也想跑深度学习,恰好需要和你一样的环境依赖,我们完全可以“拷贝”一份配好的环境给他,他可以直接上手去使用。Docker的方便之处也体现在这,我们可以将自己定制的容器构建成镜像,可以上传到Docker Hub给别人下载,也可以生成压缩包拷贝给别人。

    利用commit命令,生成一个名为homography1.0的新镜像。

    # docker commit -a "作者信息" -m "提交信息" 之前启动的容器名 自定义镜像名
    docker commit -a "wangguoping" -m "deep homography environment" tensorflow1.2 homography1.012

    至于将镜像提交Hub和拷贝就不是本文重点,也就不介绍了。

    另外,这里生成镜像还有一个好处,就是第六部分结合PyCharm使用。PyCharm里面配Docker选择的是镜像(IMAGE),而不是容器(Container),它会根据我们选择的镜像自己帮我们启动一个容器,用来运行PyCharm里面的代码。我一开始没有搞清楚这个概念,也走了不少弯路。

    V. 测试Mnist

    进入上一部分挂载的目录:

    cd /home/test/mnist # test是我的用户名
    vim mnist.py # 创建mnist的tensorflow代码12

    代码内容可以参考:Tensorflow——nn、cnn、rnn玩mnist

    # coding=utf-8
    

    import tensorflow as tf
    from tensorflow.examples.tutorials.mnist import input_data

    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
    train_img = mnist.train.images
    train_lab = mnist.train.labels
    test_img = mnist.test.images
    test_lab = mnist.test.labels

    dim_input = 784
    dim_output = 10

    x_data = tf.placeholder(tf.float32, [None, dim_input])
    y_real = tf.placeholder(tf.float32, [None, dim_output])

    stddev = 0.1
    weights = {"w_conv1": tf.Variable(tf.random_normal([3, 3, 1, 64], stddev=stddev)),
    "w_conv2": tf.Variable(tf.random_normal([3, 3, 64, 128], stddev=stddev)),
    "w_fc1": tf.Variable(tf.random_normal([7 * 7 * 128, 1024], stddev=stddev)),
    "w_fc2": tf.Variable(tf.random_normal([1024, dim_output], stddev=stddev))}

    biases = {"b_conv1": tf.Variable(tf.zeros([64])),
    "b_conv2": tf.Variable(tf.zeros([128])),
    "b_fc1": tf.Variable(tf.zeros([1024])),
    "b_fc2": tf.Variable(tf.zeros([dim_output]))}

    def forward_prop(_input, _w, _b, keep_prob):

    _input_r = tf.reshape(_input, shape=[<span class="hljs-number">-1</span>, <span class="hljs-number">28</span>, <span class="hljs-number">28</span>, <span class="hljs-number">1</span>])
    
    _conv1 = tf.nn.conv2d(_input_r, _w[<span class="hljs-string">"w_conv1"</span>], strides=[<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>], padding=<span class="hljs-string">"SAME"</span>)
    _conv1 = tf.nn.relu(tf.nn.bias_add(_conv1, _b[<span class="hljs-string">"b_conv1"</span>]))
    
    _pool1 = tf.nn.max_pool(_conv1, ksize=[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>], strides=[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>], padding=<span class="hljs-string">"SAME"</span>)
    <span class="hljs-comment"># dropout</span>
    _pool_dr1 = tf.nn.dropout(_pool1, keep_prob=keep_prob)
    
    _conv2 = tf.nn.conv2d(_pool_dr1, _w[<span class="hljs-string">"w_conv2"</span>], strides=[<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>], padding=<span class="hljs-string">"SAME"</span>)
    _conv2 = tf.nn.relu(tf.nn.bias_add(_conv2, _b[<span class="hljs-string">"b_conv2"</span>]))
    _pool2 = tf.nn.max_pool(_conv2, ksize=[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>], strides=[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>], padding=<span class="hljs-string">"SAME"</span>)
    _pool_dr2 = tf.nn.dropout(_pool2, keep_prob=keep_prob)
    
    
    flatten = tf.reshape(_pool_dr2, shape=[<span class="hljs-number">-1</span>, _w[<span class="hljs-string">"w_fc1"</span>].get_shape().as_list()[<span class="hljs-number">0</span>]])
    
    _fc1 = tf.nn.relu(tf.add(tf.matmul(flatten, _w[<span class="hljs-string">"w_fc1"</span>]), _b[<span class="hljs-string">"b_fc1"</span>]))
    
    _fc_dr1 = tf.nn.dropout(_fc1, keep_prob=keep_prob)
    
    _out = tf.nn.relu(tf.add(tf.matmul(_fc_dr1, _w[<span class="hljs-string">"w_fc2"</span>]), _b[<span class="hljs-string">"b_fc2"</span>]))
    
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"input_r"</span>: _input_r, <span class="hljs-string">"conv1"</span>: _conv1, <span class="hljs-string">"pool1"</span>: _pool1, <span class="hljs-string">"pool_dr1"</span>: _pool_dr1, <span class="hljs-string">"conv2"</span>: _conv2,
            <span class="hljs-string">"pool2"</span>: _pool2, <span class="hljs-string">"pool_dr2"</span>: _pool_dr2, <span class="hljs-string">"flatten"</span>: flatten, <span class="hljs-string">"fc1"</span>: _fc1, <span class="hljs-string">"fc_dr1"</span>: _fc_dr1, <span class="hljs-string">"out"</span>: _out}
    

    keep_prob = tf.placeholder(tf.float32)

    y_pred = forward_prop(x_data, weights, biases, keep_prob)["out"]
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=y_pred, labels=y_real))
    op = tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(loss)

    correct = tf.equal(tf.arg_max(y_pred, 1), tf.arg_max(y_real, 1))
    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

    training_epoch = 100
    batch_size = 128
    display_step = 2

    init = tf.global_variables_initializer()

    total_batch = mnist.train.num_examples // batch_size
    print("have %d batchs,each batch size is:%d" % (total_batch, batch_size))

    saver = tf.train.Saver(max_to_keep=2)
    is_training = True

    with tf.Session() as sess:
    sess.run(init)

    <span class="hljs-keyword">if</span> is_training:
        <span class="hljs-keyword">for</span> epoch <span class="hljs-keyword">in</span> range(training_epoch):
            avg_loss = <span class="hljs-number">0</span>
    
            <span class="hljs-keyword">for</span> i_batch <span class="hljs-keyword">in</span> range(total_batch):
                batch_xs, batch_ys = mnist.train.next_batch(batch_size)
                feed_dict = {x_data: batch_xs, y_real: batch_ys, keep_prob: <span class="hljs-number">0.5</span>}
                sess.run(op, feed_dict=feed_dict)
                avg_loss += sess.run(loss, feed_dict=feed_dict)
    
            avg_loss = avg_loss / total_batch
    
            <span class="hljs-keyword">if</span> epoch % display_step == <span class="hljs-number">0</span>:
                print(<span class="hljs-string">"Epoch:%3d/%3d, loss:%.6f"</span> % (epoch, training_epoch, avg_loss))
    
                feed_dict = {x_data: batch_xs, y_real: batch_ys, keep_prob: <span class="hljs-number">0.5</span>}
                train_accuracy = sess.run(accuracy, feed_dict=feed_dict)
                print(<span class="hljs-string">"train accuracy:%.6f"</span> % train_accuracy)
    
                saver.save(sess, <span class="hljs-string">"MNIST_model/model.ckpt-"</span> + str(epoch))
    <span class="hljs-keyword">else</span>:
        saver.restore(sess, tf.train.latest_checkpoint(checkpoint_dir=<span class="hljs-string">"MNIST_model/"</span>))
        feed_dict = {x_data: mnist.test.images, y_real: mnist.test.labels, keep_prob: <span class="hljs-number">1.0</span>}
        test_accuracy = sess.run(accuracy, feed_dict=feed_dict)
        print(<span class="hljs-string">"test accuracy:%.6f"</span> % test_accuracy)
    
    print(<span class="hljs-string">"end!"</span>)</code></pre>
    

    下载Mnist数据集,保存在/home/test/mnist/MNIST_data目录下。这里需要我们修改目录权限,Docker共享目录默认只读。注意,这里切换到宿主机的终端下进行操作,可能你会问为什么不直接容器内下载,因为以后我们要跑的数据集不可能只是mnist大小,难道你要在docker里下载几十个G的数据集吗。

    sudo chmod -R a+rw /home/test/mnist
    mkdir -p /home/test/mnist/MNIST_data
    wget http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
    wget http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
    wget http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
    wget http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz123456

    切换至Docker的终端下,运行mnist.py脚本文件,即可发现已经可以跑起来了。

    python mnist.py1

    img

    此时会在/home/test/mnist下产生MNSIT_model文件夹,里面保存着训练生成的模型。

    img

    VI. PyCharm+Docker

    Mnist已经测试成功,基本的Docker+Deep Learning方案演示已经完成。然而,我一直不喜欢命令行修改代码,运行脚本,查看结果,我十分推崇PyCharm去调试Python。比如,当Mnist训练完毕,我需要修改第80行的is_training = False来测试我训练出的模型,没有PyCharm,我需要通过vim修改mnist.py,然后再输入python来运行。可能你会觉得也不是很麻烦,如果需要修改模型,更换网络,甚至重构代码呢?

    所以能用IDE尽量还是让PyCharm来开发我们代码,我们只需要编码,点击运行,剩下的其他操作我都不太愿意去干,一行命令都懒得敲,毕竟懒嘛!

    PyCharm在2018的Profession版本之后都是提供Docker的功能的,可以利用容器中的Python解释器为我们的代码提供运行条件。利用PyCharm,你可以像在使用本机的深度学习环境一样,无需考虑因容器带来的过多的繁琐操作。官方关于Docker的使用文档参见:http://www.jetbrains.com/help/pycharm/run-debug-configuration-docker.html

    在Settings的Build下有一个Docker选项,右侧添加,PyCharm默认会给我们设置好选择Unix Socket的方式与Docker守护进程进行通信。出现Connection successful即可,点击OK。

    img

    添加成功后,PyCharm下方会出现docker的窗口,可以可视化的查看镜像与容器的相关信息。其中的homography1.0:latsest是我们上一步构建的镜像。

    img

    下面新建一个Python的解释器,类似于本地的Python创建虚拟环境。

    img

    按照图示新建,点击OK,即可发现导入容器的Python解释器已经拥有了全部的第三方Python库以及深度学习框架。

    img

    等待PyCharm导入容器的解释器成功,理论上我们便可以开始点击运行按钮跑起我们的代码了。但实际还有两个问题需要解决。

    首先,PyCharm会根据我们的镜像来启动一个容器运行我们的代码,然而PyCharm并不知道我们是要运行深度学习程序,需要利用Nvidia Docker使用GPU资源。我们之前是通过配置–runtime=nvidia参数来启动一个可以使用GPU的容器,那我们只要指定PyCharm启动一个容器的时候也带上这个参数就好。

    另一个问题就是,我们现在跑的程序是读取宿主机上某个目录下的几十个G的数据集,好像Docker也不知道数据在哪里,毕竟我们没有挂载。同样,模型它也不会帮我们保存,一旦程序运行结束,PyCharm启动的容器销毁,所有的结果都没了,程序白跑了,所以我们也要指定-v参数告诉PyCharm挂载什么目录。

    解决这两个问题,在PyCharm的Run/Debug Configuration中,可以配置。

    img

    点击Docker Container settings右边的按钮,添加上面所说的两个参数即可。

    img
    坑的是,你发现没法加入–runtime参数。然而,还是找到了解决方案。把default-runtime”: “nvidia”添加到/etc/docker/daemon.json文件中。这个文件也是我们配置国内镜像加速的文件。

    {
        "registry-mirrors": [
            "https://registry.docker-cn.com"
        ],
        "default-runtime": "nvidia",
        "runtimes": {
            "nvidia": {
                "path": "/usr/bin/nvidia-container-runtime",
                "runtimeArgs": []
            }
        }
    }123456789101112

    修改完毕,需要重启docker服务。

    sudo systemctl daemon-reload
    sudo systemctl restart docker12

    好了,大功告成,点击运行,跑起来~~

    img

    VII. 结语

    Docker还是有很多技巧的,短暂几天也只学了个皮毛,用于深度学习也十分不错。官方也有很多构建好的深度学习环境镜像,包含了主流的深度学习框架,可以再Docker Hub自行搜索。实验室电脑有时候还是很奇葩的,需要耐心解决,积极的去利用一些新的技术解决难题应该是更应该考虑的事情。

  • 相关阅读:
    现实世界的Windows Azure:采访Definition 6首席技术官Paul Hernacki
    CloudConnect回顾,以及数据的未来
    现已可用:集成了Web Deploy的Windows Azure SDK 1.4更新版
    使用Azure SDK 1.4.1中的Web Deploy
    现实世界的SQL Azure:采访Zitec公司CEO,Alexandru Lapusan
    Microsoft和Toyota宣布战略合作伙伴关系,联手构建基于Windows Azure的下一代远程通信系统
    Delphi 2007 的重构功能
    关于类的入门的例子(6): 类引用示例
    获取所有汉字与 Unicode 的对照表
    关于类的入门例子(4): property
  • 原文地址:https://www.cnblogs.com/ruiyang-/p/10152267.html
Copyright © 2020-2023  润新知