docker配置深度學習環境

版權聲明:本文爲博主原創文章,轉載註明出處便可。 http://www.javashuo.com/article/p-vsqghqsm-mw.htmlhtml

閱讀本篇文章能夠幫你解決的問題是:提供一套解決方案,可以在支持Docker的任何版本Ubuntu系統下,搭建出完美運行各類深度學習框架各類版本各類環境依賴(NAIVID顯卡)深度學習工程的開發環境。不只如此,還要像在本機同樣方便的修改代碼運行計算。python

搭建深度學習計算平臺,通常須要咱們在本機上安裝一些必要的環境,安裝系統、顯卡驅動、cuda、cudnn等。而隨着Docker的流行,每每可以幫咱們輕鬆的進行環境搭建、複製與隔離,因此官方也利用容器技術與深度學習相結合,所以也出現瞭如下方案。linux

容器方案比傳統方案帶來更多的隨意性,裝系統前不須要考慮Ubuntu哪個版本符合不符合咱們的代碼運行要求,咱們只須要安裝一個本身喜歡的(18.04徹底能夠),再在官網下載一下顯卡驅動,或者軟件源附加驅動更新一下就好了,剩下都不須要咱們繼續考慮。這些也很是的輕鬆,由於Nvidia對Ubuntu的支持愈來愈友好,咱們只須要下載deb包,一行命令便可安裝成功。git

系統 顯卡驅動 Cuda Cudnn
傳統方案 一種版本 必需 一種版本 必需
容器方案 各類版本 必需 非必需 非必需

安裝顯卡驅動能夠參照:https://blog.csdn.net/bskfnvjtlyzmv867/article/details/80102000github

正式進入正文以前,確保你已經安裝好趁手的系統和顯卡驅動。docker

I. 安裝Docker

關於Docker教程,詳見:Docker——入門實戰shell

安裝指定版本Docker CE

這裏的版本由第二部分的Nvidia Docker依賴決定,筆者在寫此文時須要的版本是18.03.1,若是在安裝Nvidia Docker時依賴的Docker CE版本已經變動,能夠卸載從新安裝須要的版本。json

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版本安裝在系統中。ubuntu

啓動Docker CE

sudo systemctl enable docker
sudo systemctl start docker12

創建docker 用戶組

默認狀況下,docker 命令會使用Unix socket 與Docker 引擎通信。而只有root 用戶和docker 組的用戶才能夠訪問Docker 引擎的Unix socket。出於安全考慮,通常Ubuntu系統上不會直接使用root 用戶。所以,更好地作法是將須要使用docker 的用戶加入docker用戶組。vim

# 創建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.0四、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顯卡,須要設置額外的參數。

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=[-1, 28, 28, 1])

    _conv1 = tf.nn.conv2d(_input_r, _w["w_conv1"], strides=[1, 1, 1, 1], padding="SAME")
    _conv1 = tf.nn.relu(tf.nn.bias_add(_conv1, _b["b_conv1"]))

    _pool1 = tf.nn.max_pool(_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    # dropout
    _pool_dr1 = tf.nn.dropout(_pool1, keep_prob=keep_prob)

    _conv2 = tf.nn.conv2d(_pool_dr1, _w["w_conv2"], strides=[1, 1, 1, 1], padding="SAME")
    _conv2 = tf.nn.relu(tf.nn.bias_add(_conv2, _b["b_conv2"]))
    _pool2 = tf.nn.max_pool(_conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    _pool_dr2 = tf.nn.dropout(_pool2, keep_prob=keep_prob)


    flatten = tf.reshape(_pool_dr2, shape=[-1, _w["w_fc1"].get_shape().as_list()[0]])

    _fc1 = tf.nn.relu(tf.add(tf.matmul(flatten, _w["w_fc1"]), _b["b_fc1"]))

    _fc_dr1 = tf.nn.dropout(_fc1, keep_prob=keep_prob)

    _out = tf.nn.relu(tf.add(tf.matmul(_fc_dr1, _w["w_fc2"]), _b["b_fc2"]))

    return {"input_r": _input_r, "conv1": _conv1, "pool1": _pool1, "pool_dr1": _pool_dr1, "conv2": _conv2,
            "pool2": _pool2, "pool_dr2": _pool_dr2, "flatten": flatten, "fc1": _fc1, "fc_dr1": _fc_dr1, "out": _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)

    if is_training:
        for epoch in range(training_epoch):
            avg_loss = 0

            for i_batch in 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: 0.5}
                sess.run(op, feed_dict=feed_dict)
                avg_loss += sess.run(loss, feed_dict=feed_dict)

            avg_loss = avg_loss / total_batch

            if epoch % display_step == 0:
                print("Epoch:%3d/%3d, loss:%.6f" % (epoch, training_epoch, avg_loss))

                feed_dict = {x_data: batch_xs, y_real: batch_ys, keep_prob: 0.5}
                train_accuracy = sess.run(accuracy, feed_dict=feed_dict)
                print("train accuracy:%.6f" % train_accuracy)

                saver.save(sess, "MNIST_model/model.ckpt-" + str(epoch))
    else:
        saver.restore(sess, tf.train.latest_checkpoint(checkpoint_dir="MNIST_model/"))
        feed_dict = {x_data: mnist.test.images, y_real: mnist.test.labels, keep_prob: 1.0}
        test_accuracy = sess.run(accuracy, feed_dict=feed_dict)
        print("test accuracy:%.6f" % test_accuracy)

    print("end!")

下載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自行搜索。實驗室電腦有時候仍是很奇葩的,須要耐心解決,積極的去利用一些新的技術解決難題應該是更應該考慮的事情。

相關文章
相關標籤/搜索