版權聲明:本文爲博主原創文章,轉載註明出處便可。 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
關於Docker教程,詳見:Docker——入門實戰shell
這裏的版本由第二部分的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
sudo systemctl enable docker sudo systemctl start docker12
默認狀況下,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,此時能夠直接出現信息。
在/etc/docker/daemon.json 中寫入以下內容(若是文件不存在請新建該文件)
{ "registry-mirrors": [ "https://registry.docker-cn.com" ] }12345
sudo systemctl daemon-reload sudo systemctl restart docker12
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
Nvidia官網在DockerHub中提供了關於深度學習的各個版本環境,點我…有Ubuntu14.04-18.04,Cuda6.5-9.2,Cudnn4-7,基本含蓋了咱們所須要的各類版本的深度學習環境,咱們直接拉取鏡像,在已有的鏡像基礎上配置咱們的深度學習環境。
下載鏡像,這裏以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
在上一部分咱們搭建了深度學習計算的必要環境,包括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裏面的代碼。我一開始沒有搞清楚這個概念,也走了很多彎路。
進入上一部分掛載的目錄:
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
此時會在/home/test/mnist下產生MNSIT_model文件夾,裏面保存着訓練生成的模型。
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。
添加成功後,PyCharm下方會出現docker的窗口,能夠可視化的查看鏡像與容器的相關信息。其中的homography1.0:latsest是咱們上一步構建的鏡像。
下面新建一個Python的解釋器,相似於本地的Python建立虛擬環境。
按照圖示新建,點擊OK,便可發現導入容器的Python解釋器已經擁有了所有的第三方Python庫以及深度學習框架。
等待PyCharm導入容器的解釋器成功,理論上咱們即可以開始點擊運行按鈕跑起咱們的代碼了。但實際還有兩個問題須要解決。
首先,PyCharm會根據咱們的鏡像來啓動一個容器運行咱們的代碼,然而PyCharm並不知道咱們是要運行深度學習程序,須要利用Nvidia Docker使用GPU資源。咱們以前是經過配置–runtime=nvidia參數來啓動一個可使用GPU的容器,那咱們只要指定PyCharm啓動一個容器的時候也帶上這個參數就好。
另外一個問題就是,咱們如今跑的程序是讀取宿主機上某個目錄下的幾十個G的數據集,好像Docker也不知道數據在哪裏,畢竟咱們沒有掛載。一樣,模型它也不會幫咱們保存,一旦程序運行結束,PyCharm啓動的容器銷燬,全部的結果都沒了,程序白跑了,因此咱們也要指定-v參數告訴PyCharm掛載什麼目錄。
解決這兩個問題,在PyCharm的Run/Debug Configuration中,能夠配置。
點擊Docker Container settings右邊的按鈕,添加上面所說的兩個參數便可。
坑的是,你發現無法加入–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
好了,大功告成,點擊運行,跑起來~~
Docker仍是有不少技巧的,短暫幾天也只學了個皮毛,用於深度學習也十分不錯。官方也有不少構建好的深度學習環境鏡像,包含了主流的深度學習框架,能夠再Docker Hub自行搜索。實驗室電腦有時候仍是很奇葩的,須要耐心解決,積極的去利用一些新的技術解決難題應該是更應該考慮的事情。