zoukankan      html  css  js  c++  java
  • 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自行搜索。实验室电脑有时候还是很奇葩的,需要耐心解决,积极的去利用一些新的技术解决难题应该是更应该考虑的事情。

  • 相关阅读:
    matlab矩阵中如何去掉重复的行;如何找到相同的行,并找到其位置
    Coursera 机器学习 第9章(下) Recommender Systems 学习笔记
    机器学习基石笔记1——在何时可以使用机器学习(1)
    Coursera 机器学习 第9章(上) Anomaly Detection 学习笔记
    matlab安装过程的被要求的配置程序
    jdk环境变量配置
    Coursera 机器学习 第8章(下) Dimensionality Reduction 学习笔记
    Coursera 机器学习 第8章(上) Unsupervised Learning 学习笔记
    Coursera 机器学习 第7章 Support Vector Machines 学习笔记
    linux服务器---squid限制
  • 原文地址:https://www.cnblogs.com/ruiyang-/p/10152267.html
Copyright © 2011-2022 走看看