從AWS搭一個GPU運算環境來玩tensorflow

這一陣子開始跑一些deep learning的範例之後,開始覺得小筆電不夠力了
而現在一些deep learning的環境很多內建支援用GPU來加速,由於家理空間不夠,不想再用台大桌機來插顯卡
後來雖說也有想到最近有外接顯示卡的產品可以給筆電用,但是我只是跑幾個試驗讓自己玩的
一下子在去買張顯卡好像操之過急,後來看到Amazon的AWS有提供GPU運算的環境,加上之前沒玩過AWS,趁這個機會就來順道玩玩看

這篇文章紀錄整個過程跟遇到的問題,畢竟我不是AWS的專家,就先申請一台來玩玩
流程如下

  • 申請一個AWS GPU環境的Spot Instance
  • 登入環境安裝GPU driver跟機器學習環境及tensorflow
  • 跑CNN測試

申請一個AWS GPU環境的Spot Instance

首先呢,要去申請一個AWS的帳號,申請網址如下
https://aws.amazon.com
申請過程會需要信用卡資料,請先準備好,申請帳號過程我就省略了
這邊直接進入啟動Instance的步驟

帳號申請好之用一樣從 https://aws.amazon.com 去進入管理頁面,點選[Service] -> [EC2]

之後會進入Instance的管理選單選擇[Spot Requests] -> [Request Spot Instances]

這裡閒聊一下為什麼選Spot Requests而不是直接選Instances
首先是價錢問題,Spot Requests產生的instance用途不是為了長期使用,而是短期開一個環境在那邊而已
種種因素Spot Requests的使用上會比直接起一個Instances還要便宜些
另一方面直接從Instances啟動GPU環境時不見得會成功,我第一次從Instances想要啟動一個GPU環境時
被告知必須申請調高該環境上限,送個申請加施工要花24~48小時,我只是想跑個測試還要等一堆時間
基於以上理由,後來就選擇了Spot Requests的方式產生

進入選單頁一步一步往下填
第一頁是需求的伺服器類形,比較重要的東西有下面幾項

  • Target capacity: 要啟幾個環境,這邊先選一個
  • AMI: OS類形,我習慣CentOS,就選了Amazon Linux AMI
  • Instance type(s): 環境類形,也是最重要的選項
    現在AWS有提供的GPU運算環境比較便宜的有兩個,其他p2.xlarge什麼都先不要考慮
    g2.2xlarge跟g2.8xlarge,裡面內建了Nvidia的GPU及CUDA環境
    我這邊是測試用,所以選擇g2.2xlarge就好
    參考:https://aws.amazon.com/cn/ec2/instance-types/

  • Network: 請確定有選項基本上用預設的就好,沒有的話用[Create new VPC]做一個

  • Availability Zone: 選擇一個Zone,這邊先適當的選了ap-northeast-1c

其他先用預設值,其他的沒問題後就選[Next]進到下一頁
第二頁主要是網路跟儲存空間的設定


  • EBS volumes: 預設只有8G請適當的調大這邊設定20G,因為之後要裝GPU driver跟機器學習環境等等有的沒有,一開始我沒有調裝個兩三樣東西就空間不足了
  • Key pair name: 用來產生登入用的ssh私鑰,如果沒有的話點選[Create new key pair]產生下載之後好好保存
  • Security groups: 選擇default值

上面完成後點選[Review]確認一下OK的話就可以點選[Launch]了

大概過個幾個分鐘就可以從[Instances]選單裡面看到g2.2xlarge,點選Connect可以看看要怎麼存取剛剛產生那台伺服器其IP跟HOST的資訊
登入方式就是準備個可以ssh client搭配剛剛產生instance時在[Key pair name]階段下載的ssh金鑰

卡住的點: 一開始的時候我無法用ssh登入,會出現

ssh: connect to host xxx.xxx.xxx.xx port 22: Operation timed out

這樣一個錯誤,後來查了一下資料必須設定[Security groups]才行
剛才Instances那頁往下拉選擇Security groups的default rule
看看[inbound]的選單裡面有沒有[SSH]那欄,沒有的話點選[Edit]之後適當的加入



租賃費用

環境起來了,總是要在意一下會花多少錢
可以參考:https://aws.amazon.com/cn/ec2/spot/pricing/
先不考慮流量計價的話AWS租用是以小時計費,然後每個"Zone"的計費又不太一樣
我因為人在東京,當初就很自然的選擇東京的zone
東京的zone的g2.2xlarge的Linux計費是每小時說是$0.3017美金
我什麼都不做在那邊給他放個兩天就花了10美金上下
嗯...只是測試租用就先算了,好像有些Zone不會記入租賃費用,下次有機會再另外申請來試試
想要看目前花費多少的話,可以從右上角的自己帳號點選[My billing Dashboard]去看

登入環境安裝GPU driver跟機器學習環境及tensorflow

環境準備好了,登上去測試看看,先準備好有ssh client的環境,我這邊是mac直接就有了
再來就是將剛剛下載的ssh金鑰安置好

local$ mv ${YOUR_SSH_KEY} ~/.ssh/myaws.pem
local$ chmod 600 ~/.ssh/myaws.pem

設定一下ssh config

local$ vim ~/.ssh/config

增加以下內容

Host aws
        HostName        ${填入你的AWS instance ip/host位置}
        User            ec2-user
        IdentityFile    ~/.ssh/myaws.pem

之後就可以直接登入

local$ ssh aws

Last login: Mon Jan  2 05:31:52 2017 from p2600006-ipngn22001marunouchi.tokyo.ocn.ne.jp

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2016.09-release-notes/
14 package(s) needed for security, out of 22 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-xxxxxxxx ~]$

登入之後先切換成root來進行作業
接著確定有沒有GPU跟OS型號

$ sudo su -   #切換成root

root$ lspci | grep -i nvidia  #檢查GPU

00:03.0 VGA compatible controller: NVIDIA Corporation GK104GL [GRID K520] (rev a1)

root$ uname -m && cat /etc/*release  #檢查OS

Amazon Linux AMI release 2016.09

確認沒問題後開始安裝GPU的Driver,這裡要裝兩個東西

  • NVIDIA CUDA Toolkit
  • cuDNN library

安裝過程大致可以參考:http://www.pyimagesearch.com/2016/07/04/how-to-install-cuda-toolkit-and-cudnn-for-deep-learning/

第一個先確定自己即將裝的tensorflow-gpu吃的是哪個版本的library
我現在的環境會去參照8.0的driver

去NVidia的網站去找下載路徑
https://developer.nvidia.com/cuda-toolkit

安裝NVIDIA CUDA Toolkit

CUDA Toolkit的安裝會簡單點,只要有目標版號就可以直接抓了

root$ yum install -y gcc
root$ yum install -y kernel-devel-$(uname -r) kernel-headers-$(uname -r)
root$ wget https://developer.nvidia.com/compute/cuda/8.0/prod/local_installers/cuda_8.0.44_linux-run
root$ chmod 755 cuda_8.0.44_linux-run
root$ ./cuda_8.0.44_linux-run -extract=/root
root$ ./NVIDIA-Linux-x86_64-367.48.run -s
root$ ./cuda-linux64-rel-8.0.44-21122537.run -noprompt

設置環境變數

root$ vim ~/.bashrc
#增加下面三行

export CUDA_ROOT=/usr/local/cuda-8.0
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64
export PATH=$PATH:$CUDA_ROOT/bin

root$ source ~/.bashrc

測試一下安裝

root$ nvidia-smi -q | head
==============NVSMI LOG==============

Timestamp                           : Mon Jan  2 09:39:51 2017
Driver Version                      : 367.48

Attached GPUs                       : 1
GPU 0000:00:03.0
    Product Name                    : GRID K520
    Product Brand                   : Grid
安裝cuDNN library

cuDNN的安裝就比較麻煩了
首先一樣先去NVidia的官網
https://developer.nvidia.com/cudnn

但是cuDNN library的頁面會要求註冊一個帳號,這邊就適當的申請開通帳號之後下載到本機
帳號註冊成功後登入上面的網址可以進入一個下載頁
配合CUDA Toolkit的版本,選擇[cuDNN vx.x Library for Linux]下載
我現在可以選的是cuDNN v5.1 Library for Linux

下載之後上傳到aws的環境

local$ scp cudnn-8.0-linux-x64-v5.1.tgz  aws:/tmp/
local$ ssh aws
$ sudo su -
root$ cp /tmp/cudnn-8.0-linux-x64-v5.1.tgz .
root$ tar -zxvf cudnn-8.0-linux-x64-v5.1.tgz
root$ cp cuda/lib64/* /usr/local/cuda-8.0/lib64/
root$ cp cuda/include/* /usr/local/cuda-8.0/include/

上面完成後Driver的設置就完成了
之後開始安裝tensorflow相關的環境

首先Python機器學習相關的環境我直接用Anaconda python2.7的版本去安裝
參考:https://www.continuum.io/downloads

root$ wget https://repo.continuum.io/archive/Anaconda2-4.2.0-Linux-x86_64.sh
root$ sh Anaconda2-4.2.0-Linux-x86_64.sh
root$ source /root/.bashrc

裝好之後numpy,scipy等常用套件就可以直接使用了
接下來安裝tensorflow跟tensorflow-gpu

root$ pip install tensorflow
root$ pip install tensorflow-gpu

我現在環境裝起來兩個都是0.12版

裝好之後開啓python測試一下

import tensorflow as tf
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcudnn.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:128] successfully opened CUDA library libcurand.so locally

sess=tf.Session()

I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties:
name: GRID K520
major: 3 minor: 0 memoryClockRate (GHz) 0.797
pciBusID 0000:00:03.0
Total memory: 3.94GiB
Free memory: 3.91GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating Tensor

如果import的時候全部的CUDA library都能成功載進來就沒問題了
如果要說有錯,我遇過以下兩種

  • libcudart.so找不到

    import tensorflow as tf
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/root/anaconda2/lib/python2.7/site-packages/tensorflow/__init__.py", line 24, in <module>
    from tensorflow.python import *
    File "/root/anaconda2/lib/python2.7/site-packages/tensorflow/python/__init__.py", line 60, in <module>
    raise ImportError(msg)
    ImportError: Traceback (most recent call last):
    File "/root/anaconda2/lib/python2.7/site-packages/tensorflow/python/__init__.py", line 49, in <module>
    from tensorflow.python import pywrap_tensorflow
    File "/root/anaconda2/lib/python2.7/site-packages/tensorflow/python/pywrap_tensorflow.py", line 28, in <module>
    _pywrap_tensorflow = swig_import_helper()
    File "/root/anaconda2/lib/python2.7/site-packages/tensorflow/python/pywrap_tensorflow.py", line 24, in swig_import_helper
    _mod = imp.load_module('_pywrap_tensorflow', fp, pathname, description)
    ImportError: libcudart.so.8.0: cannot open shared object file: No such file or directory
    

    代表CUDA Toolkit沒裝好,請回頭參考CUDA Toolkit的安裝方法

  • libcudnn.so找不到

    ...
    I tensorflow/stream_executor/dso_loader.cc:119] Couldn't open CUDA library libcudnn.so. LD_LIBRARY_PATH: /usr/local/cuda-8.0/lib64
    I tensorflow/stream_executor/cuda/cuda_dnn.cc:3459] Unable to load cuDNN DSO
    

    代表cuDNN library沒裝好,請回頭參考cuDNN library的安裝方法

跑CNN測試

上面環境都裝好之後就可以跑個CNN深度學習測測看
tensorflow gpu進階的操作可以參考:
https://www.tensorflow.org/how_tos/using_gpu/

測試的程式碼如下,MNIST的手寫辨識
一個只訓練1000次的CNN網路

cnn.py
from __future__ import print_function
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# number 1 to 10 data

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

def compute_accuracy(v_xs, v_ys):
    global prediction
    y_pre = sess.run(prediction, feed_dict={xs: v_xs, keep_prob: 1})
    correct_prediction = tf.equal(tf.argmax(y_pre,1), tf.argmax(v_ys,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs: v_xs, ys: v_ys, keep_prob: 1})
    return result

def weight_variable(shape):
    inital = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(inital)

def bias_variable(shape):
    inital = tf.constant(0.1, shape=shape)
    return tf.Variable(inital)

def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                        strides=[1, 2, 2, 1], padding='SAME')


# define placeholder for inputs to network

xs = tf.placeholder(tf.float32, [None, 784]) # 28x28

ys = tf.placeholder(tf.float32, [None, 10])
keep_prob = tf.placeholder(tf.float32)
x_image = tf.reshape(xs, [-1, 28, 28, 1])
## conv1 layer ##

W_conv1 = weight_variable([5, 5, 1, 32]) #patch 5x5, in channel size 1, out size 32

## pool1 layer ##

b_conv1 = bias_variable([32])
#Combine

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) #output size 28x28x32

h_pool1 = max_pool_2x2(h_conv1) #output size 14x14x32

## conv2 layer ##

W_conv2 = weight_variable([5, 5, 32, 64]) #patch 5x5, in channel size 32, out size 64

## pool2 layer ##

b_conv2 = bias_variable([64])
#Combine

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) #output size 14x14x64

h_pool2 = max_pool_2x2(h_conv2) #output size 7x7x64

## fc1 layer ##

W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) #[n_samples, 7,7,64]  => [n_samples, 7*7*64]

h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
## fc2 layer ##

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
# the error between prediction and real data

cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction),
                                              reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(1001):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob:0.5})
    if i % 50 == 0:
        print(compute_accuracy(
            mnist.test.images, mnist.test.labels))

跑來測測看

root$ time python cnn.py
...
real  0m43.571s
user  0m32.352s
sys 0m9.068s

只要43秒就搞定了,同樣的程式用我的小筆電去跑要跑15分鐘,也算有不錯的進展了
第一次玩AWS,還有很多不清楚的地方,這個就留到以後研究了

2017/01/04

別人做的效能測試:http://qiita.com/shouta-dev/items/5aa4a746b31b9be4838d

comments powered by Disqus