我想寫一系列深度學習的簡單實戰教程,用mxnet做實現平台的實例代碼簡單講解深度學習常用的一些技術方向和實戰樣例。這一系列的主要內容偏向於講解實際的例子,從樣例和代碼里中學習解決實際問題。我會默認讀者有一定神經網絡和深度學習的基礎知識,讀者在這里不會看到大段推導和理論闡述。基礎理論知識十分重要,如果讀者對理論知識有興趣,可以參看已有的深度學習教程補充和鞏固理論基礎,這里http://deeplearning.net/reading-list/tutorials/有一些不錯的理論教程,相關的理論知識在此不贅述。
MXnet: 輕量化分布式可移植深度學習計算平台
MXnet是一群聰明勇敢勤勞的年輕計算機科學家實現的開源深度學習計算平台,它是DMLC分布式機器學習通用工具包http://dmlc.ml/ 的重要部分(如果你知道xgboosthttps://github.com/dmlc/xgboost 這個並行GBT的實現,應該對DMLC也不會陌生)。MXnet的優點是,輕量化、可移植性高、也可輕松分布式並行,並且高效利用顯存,更可以靈活的運行在移動設備上。它的代碼和使用方法也簡潔明了,適合學習實戰。這么有意思的深度學習工具平台,大家快去點這個github連接給它加個星加個fork吧,傳送門:https://github.com/dmlc/mxnet
安裝MXnet
MXnet支持Linux,Windows和Mac平台。本文使用的主要平台是ubuntu 14.04 LTS。提醒注意,這一些系列教程使用CUDA平台做GPU運算,而在本文寫作的時候CUDA暫時還不支持最新的ubuntu 15.10版本的環境和編譯器(主要是gcc 5.2的編譯器),所以強烈建議堅守14.04 LTS版本或者是最多到15.04版。
安裝環境可以是帶nVidia顯卡的實體機器或者是帶GPU的雲服務器。如果選擇實體機,請不要通過虛擬機安裝,比如原生Windows下面跑個虛擬的Linux,因為多數虛擬機軟件不支持直接調用本機顯卡。如果選擇雲服務器,請一定選擇GPU instance比如AWS的g2.2xlarge
或g2.8xlarge
,或者是terminal.com
的GPU instance。注意:terminal.com
號稱運行時可以改虛擬機的類型,但是純CPU的虛擬機在運行時不能無縫切換到GPU,建議一開始就選擇GPU instance。
以下安裝步驟參考於官方文檔:http://mxnt.ml/en/latest/build.html#building-on-linux,本文根據CUDA的安裝和實際操作略有修改。
基本依賴的安裝
MXnet的另一個優點就是它只需要很少的第三方包,它基本只需要gcc的編譯器,BLAS以及可選安裝OpenCV。這里如果還沒有安裝git可以順道安裝一下。
sudo apt-get update
sudo apt-get install -y build-essential git libblas-dev libopencv-dev
下載mxnet
git clone --recursive https://github.com/dmlc/mxnet
這里提醒注意一定不要忘記--recursive
參數,因為mxnet依賴於DMLC通用工具包http://dmlc.ml/,--recursive
參數可以自動加載mshadow
等依賴。這里暫時不要着急編譯,我們還要裝一下CUDA。
安裝CUDA
這里提到的CUDA安裝方法也適用於除MXnet之外的其他深度學習軟件包。我們通過nVidia官方鏈接下載安裝CUDA驅動和工具包,請前往https://developer.nvidia.com/cuda-downloads 選擇對應的安裝方式。國內讀者建議網絡安裝方式deb(network)
,這樣ubuntu會選擇就近的國內的源安裝,速度可能比較快。
如果用ubuntu 14.04,不用去官網,直接運行以下這些命令也可以調用官網下載(安裝包較大需要耐心等待):
wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1404/x86_64/cuda-repo-ubuntu1404_7.5-18_amd64.deb
sudo dpkg -i cuda-repo-ubuntu1404_7.5-18_amd64.deb
sudo apt-get update
sudo apt-get install cuda
如果一切安裝成功,可以用nvidia-smi
命令查看你的顯卡使用情況,一般空閑的顯卡狀態是這個樣子的:
顯卡型號取決於個人經濟能力,不過mxnet的顯存利用率高,一般一個4G的顯卡就足夠處理多數別的工具包要很多顯存的問題。
可選安裝:Mxnet也支持cuDNN
,它是nVidia推出的深度學習加速工具包,能高效實現一些卷積等深度學習常用操作,在內存使用和計算速度上面能有所提高。大家可以到這里https://developer.nvidia.com/cudnn 申請開發者項目,如果批准通過可以下載安裝cuDNN工具包,具體請參照nVidia官方教程。
編譯支持GPU的MXnet
MXnet需要打開一個編譯和鏈接選項來支持CUDA。在前一步git clone
得到的mxnet/
目錄里找到mxnet/make/
子目錄,把該目錄下的config.mk
復制到mxnet/
目錄,用文本編輯器打開,找到並修改以下三行:
USE_CUDA = 1
USE_CUDA_PATH = /usr/local/cuda
其中第二行是CUDA的安裝目錄。如果選擇默認安裝方式,它會在/usr/local/cuda
或者是類似/usr/local/cuda-7.5
這樣的原始安裝目錄,如果是自定義目錄的安裝,請自行修改本條。
如果用戶選擇安裝atlas或者openblas等其他BLAS的實現,需要額外的修改。如果ubuntu的atlas
實現(sudo apt-get install libatlas-base-dev
或者sudo apt-get install libopenblas-dev
),需要修改為:
USE_BLAS = atlas 或者 openblas
修改之后,在mxnet/
目錄下編譯(-j4
是可選參數表示用4線程編譯):
make -j4
注意:如果沒有CUDA支持的顯卡(比如Intel的Iris顯卡或者AMD的R系列顯卡)或者沒有顯卡,安裝和編譯GPU版本的mxnet會出錯。解決方法是,把USE_CUDA = 1
改回USE_CUDA = 0
,並確保USE_OPENMP = 1
,mxnet會自動編譯CPU版本並使用OpenMP進行多核CPU計算。根據問題的不同,GPU版本對比CPU版一般會有20-30倍左右的加速。
安裝Python支持
MXnet支持Python調用。簡單來說就這么安裝:
cd python; python setup.py install
建議使用python 2.7版本,需要預先安裝setuptools
和numpy
(sudo apt-get install python-numpy
)。如果你的系統安裝Numpy有些困難,可以考慮安裝Anaconda或者Miniconda之類的python發行版:
wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh
bash Miniconda-latest-Linux-x86_64.sh
(確認回答若干安裝問題后)
conda install numpy
運行MNIST手寫數字識別
2015年11月19日更新:這里的樣例基於舊版mxnet/example
的目錄結構,新版的MNIST代碼在mxnet/example/image-classification/
下,可以通過--gpu (gpu_id)
開啟GPU計算選項,請自行更新並參見新版說明:https://github.com/dmlc/mxnet/tree/master/example/image-classification 。
當MXnet一切安裝好之后,可以試試看一下最簡單的例子,MNIST手寫數字識別。MNIST數據集包含6萬個手寫數字的訓練數據集以及1萬個測試數據集,每個圖片是28x28的灰度圖。在mxnet/example/mnist
里可以找到MXnet自帶MNIST的識別樣例,我們可以先運行一下試試:
cd mxnet/example/mnist
python mlp.py
mlp.py
會自動下載MNIST數據集,在第一次運行的時候耐心等待一下。
注意:mlp.py
默認使用CPU,訓練過程可以跑起來但是很慢。我們已經安裝了GPU,只需要修改一行代碼,把FeedForward
調用的CPU部分改成GPU即可讓MXnet運行在GPU上:
model = mx.model.FeedForward(
ctx = mx.cpu(), symbol = mlp, num_epoch = 20,
learning_rate = 0.1, momentum = 0.9, wd = 0.00001)
變成:
model = mx.model.FeedForward(
ctx = mx.gpu(), symbol = mlp, num_epoch = 20,
learning_rate = 0.1, momentum = 0.9, wd = 0.00001)
再運行一下,是不是快多了呢?MXnet的優點就是接口簡潔。運行的時候,nvidia-smi
命令查看顯卡狀態差不多是這個樣子的:
可以看到python
進程在使用GPU,因為這是個比較小的問題同時MXnet的顯存優化較好,GPU使用率30%到40%之間,顯存占用67MB。
可能出現的問題
運行GPU例子的時候可能會遇到這樣的問題:
ImportError: libcudart.so.7.0: cannot open shared object file: No such file
這是因為沒有把CUDA的動態鏈接庫加入PATH里,解決方法是,可以在./bashrc
里面加入:
export LD_LIBRARY_PATH=/usr/local/cuda-7.5/targets/x86_64-linux/lib/:$LD_LIBRARY_PATH
或者是在編譯MXnet的時候,在config.mk
里的
ADD_LDFLAGS = -I/usr/local/cuda-7.5/targets/x86_64-linux/lib/
ADD_CFLAGS =-I/usr/local/cuda-7.5/targets/x86_64-linux/lib/
MNIST代碼簡單講解:設計一個最簡單的多層神經網絡
mlp.py
實現的是一個多層感知器網絡(multilayer perceptron (MLP) )或者叫多層神經網絡。在MXnet里,實現一個MLP首先需要定義一下這個MLP的結構,比如在代碼里一個三層網絡的MLP就是這樣的:
data = mx.symbol.Variable('data')
fc1 = mx.symbol.FullyConnected(data = data, name='fc1', num_hidden=128)
act1 = mx.symbol.Activation(data = fc1, name='relu1', act_type="relu")
fc2 = mx.symbol.FullyConnected(data = act1, name = 'fc2', num_hidden = 64)
act2 = mx.symbol.Activation(data = fc2, name='relu2', act_type="relu")
fc3 = mx.symbol.FullyConnected(data = act2, name='fc3', num_hidden=10)
mlp = mx.symbol.Softmax(data = fc3, name = 'mlp')
簡單解釋一下這幾行代碼:MNIST的數據集每組數據是28x28的灰度圖像,差不多如下圖:
每組數據就可以表示成一個長度為28x28=784的一維數組,數組的每個元素是這個像素的灰度值。MLP的每一層需要定義這一層節點的樣式,比如fc1
就是接受輸入的第一層,它定義為一個全鏈接層mx.symbol.FullyConnected
,通過data
接受輸入,這一層包含了128個節點(num_hidden
)。每一層也需要定義激活函數Activation
,比如第一層到第二層之間的激活函數就是relu
(代表rectified linear unit或者叫Rectifier
)ReLu是深度神經網絡里最常見的一個激活函數,主要因為計算函數相對容易和梯度下降不會發散,並且由於MNIST的問題比較稀疏更適合ReLU。限於這里篇幅主要是為了介紹實現一個網絡,關於ReLU的相關背景知識請參考wikipedia )和其他相關教程。第二層網絡fc2
和第一層相似,它接受fc1
的數據作為輸入,輸出給第三層。第三層網絡fc3
和前兩層類似,不一樣的是它是個結果輸出層,產生的是輸入圖片對應於0-9總共10個數字里每個數字的概率,所以它的num_hidden=10
。
設計好了網絡結構之后,MXnet需要聲明輸入feature的格式,因為每個圖片都是28x28大小,按照每個像素的灰度值展開成一列向量就是784維,我們可以告訴mxnet數據的輸入尺寸是784,mnist_iterator
是一個python generator一次提供100組數據給我們剛剛設計的MLP,參見同目錄的data.py
:
train, val = mnist_iterator(batch_size=100, input_shape = (784,))
接下來就讓MXnet建立並運行這個一個模型,就是這樣簡單,如果你會scikit-learn
會感到很親切,對不對(記得剛剛修改的指定GPU運行的那一行么?):
model = mx.model.FeedForward(
ctx = mx.gpu(), symbol = mlp, num_epoch = 20,
learning_rate = 0.1, momentum = 0.9, wd = 0.00001)
model.fit(X=train, eval_data=val)
到這里,大家就基本會實現一個多層感知器MLP,恭喜你們這是掌握深度學習的第一步。MXnet的方式比Caffe等其他工具要寫個配置文件簡單的多了。工業界和學術界的多數深度學習的實際問題都是圍繞着設計多層感知器展開,在結構設計激活函數設計等方面有很多有意思的問題。
有讀者會問,MLP是不是非要像MNIST手寫數字識別這么設計。不是的,這個三層網絡只是一個最簡單的MLP的例子,這里每一層並不一定需要這樣。設計一個更好更高效的多層神經網絡和藝術一樣沒有止境。比如在MNIST同一個目錄下的lenet.py
就是用Yann Lecun設計的卷積網絡實現數字識別,每層網絡需要做的是Convolution
Activation
和Pooling
(如果想知道這三個具體是什么,請參看他的深度學習教程,以后的文章里面可能也會提到。
當做課后作業,讀者可以自己試試調一下mlp.py
里不同的節點數和激活函數看看對數字識別率有什么提升,也可以增加num_epoch
調整learning_rate
等參數,在轉發、評論或留言寫下你們的設計方法和識別准確度(並沒有獎勵,嗯)。Kaggle針對MNIST數據集有一個教學比賽,讀者可以用MXnet訓練一個自己的MNIST模型,把結果提交上去比一比,記得說你是用MXnet做的喲,傳送門:https://www.kaggle.com/c/digit-recognizer
后記
這篇文章是這一系列的第一篇,我本意是想寫個MXnet的GPU安裝方法,后來想想加個例子講解一下各種模型順便當做另外一種深度學習入門教程吧。后續的一些文章會挑選mxnet自帶的例子,介紹一些常見的有意思的深度學習模型,比如RNN,LSTM,以及它們在MXnet里的實現,比如寫個自動作詞機模仿汪峰老師作詞之類的。MXnet這么有意思的深度學習工具平台,大家快去這個github連接給它加個星加個fork吧,傳送門:https://github.com/dmlc/mxnet