摘要:Theano是主流的深度學習Python庫之一,亦支持GPU,然而Theano入門較難,Domino的這篇博文介紹了如何使用GPU和Theano加速深度學習,教程從多層感知器到卷積神經網絡,由淺入深,是不錯的入門資料。
【編者按】GPU因其浮點計算和矩陣運算能力有助於加速深度學習是業界的共識,Theano是主流的深度學習Python庫之一,亦支持GPU,然而Theano入門較難,Domino的這篇博文介紹了如何使用GPU和Theano加速深度學習,使用更簡單的基於Theano的 Nolearn庫。教程由多層感知器及卷積神經網絡,由淺入深,是不錯的入門資料。
基於Python的深度學習
實現神經網絡算法的Python庫中,最受歡迎的當屬Theano。然而,Theano並不是嚴格意義上的神經網絡庫,而是一個Python庫,它可以實現各種各樣的數學抽象。正因為如此,Theano有著陡峭的學習曲線,所以我將介紹基於Theano構建的有更平緩的學習曲線的兩個神經網絡庫。
第一個庫是 Lasagne。該庫提供了一個很好的抽象,它允許你構建神經網絡的每一層,然後堆疊在彼此的頂部來構建一個完整的模型。儘管這比Theano顯得更好,但是構建每一層,然後附加在彼此頂部會顯得有些冗長乏味,所以我們將使用 Nolearn庫,它在Lasagne庫上提供了一個類似 Scikit-Learn風格的API,能夠輕鬆地構建多層神經網絡。
延伸閱讀: 從Theano到Lasagne:基於Python的深度學習的框架和庫
由於這些庫默認使用的不是Domino硬體,所以你需要創建一個requirements.txt文件,該文件內容如下:
配置Theano
-e git://github.com/Theano/Theano.git#egg=Theano-e git://github.com/lasagne/lasagne.git#egg=lasagnenolearn==0.5.0現在,在我們導入Lasagne庫和Nolearn庫之前,首先我們需要配置Theano,使其可以使用GPU硬體。要做到這一點,我們需要在我們的工程目錄中新建一個.theanorc文件,該文件內容如下:
[global] device = gpu floatX = float32 [nvcc] fastmath = True這個.theanorc文件必須放置在主目錄中。在你的本地計算機上,這個操作可以手工完成,但我們不能直接訪問Domino機器的主目錄,所以我們需要使用下面的代碼將文件移到它的主目錄中:
import os import shutil destfile = "/home/ubuntu/.theanorc" open(destfile, 'a').close() shutil.copyfile(".theanorc", destfile)上面的代碼會在主目錄創建了一個空的.theanorc文件,然後複製我們項目目錄下的.theanorc文件內容到該文件中。
將硬體切換到GPU後,我們可以來做一下測試,使用Theano文檔中提供的測試代碼來看看Theano是否能夠檢測到GPU。
from theano import function, config, shared, sandbox import theano.tensor as T import numpy import timevlen = 10 * 30 * 768 # 10 x #cores x # threads per core iters = 1000rng = numpy.random.RandomState(22) x = shared(numpy.asarray(rng.rand(vlen), config.floatX)) f = function([], T.exp(x)) print f.maker.fgraph.toposort() t0 = time.time() for i in xrange(iters): r = f()t1 = time.time() print 'Looping %d times took' % iters, t1 - t0, 'seconds' print 'Result is', r if numpy.any([isinstance(x.op, T.Elemwise) for x in f.maker.fgraph.toposort()]): print 'Used the cpu'else: print 'Used the gpu'如果Theano檢測到GPU,上面的函數運行時間應該需要0.7秒,並且輸出「Used the gpu」。否則,整個過程將需要2.6秒的運行時間,同時輸出「Used the cpu」'。如果輸出的是後一個,那麼你肯定是忘記將硬體切換到GPU了。
數據集
對於這個項目,我們將使用CIFAR-10圖像數據集,它來自10個不同的類別,包含了60000個32x32大小的彩色圖像。
幸運的是,這些數據屬於 pickled格式,所以我們可以使用輔助函數來加載數據,將每個文件加載到NumPy數組中並返回訓練集(Xtr),訓練集標籤(Ytr),測試集(Xte)以及測試集標籤(Yte)。下列代碼歸功於 Stanford's CS231n課程的工作人員。
import cPickle as pickle import numpy as np import osdef load_CIFAR_file(filename): '''Load a single file of CIFAR''' with open(filename, 'rb') as f: datadict= pickle.load(f) X = datadict['data'] Y = datadict['labels'] X = X.reshape(10000, 3, 32, 32).transpose(0,2,3,1).astype('float32') Y = np.array(Y).astype('int32') return X, Ydef load_CIFAR10(directory): '''Load all of CIFAR''' xs = [] ys = [] for k in range(1,6): f = os.path.join(directory, "data_batch_%d" % k) X, Y = load_CIFAR_file(f) xs.append(X) ys.append(Y) Xtr = np.concatenate(xs) Ytr = np.concatenate(ys) Xte, Yte = load_CIFAR_file(os.path.join(directory, 'test_batch')) return Xtr, Ytr, Xte, Yte多層感知器
多層感知器是一種最簡單的神經網絡模型。該模型包括一個輸入層數據,一個施加一些數學變換的隱藏層,以及一個輸出層用來產生一個標籤(不管是分類還是回歸,都一樣)。
圖片來源:http://dms.irb.hr/tutorial/tut_nnets_short.php
在我們使用訓練數據之前,我們需要把它的灰度化,把它變成一個二維矩陣。此外,我們將每個值除以255然後減去0.5。當我們對圖像進行灰度化時,我們將每一個(R,G,B)元組轉換成0到255之間的浮點值)。通過除以255,可以標準化灰度值映射到[0,1]之間。接下來,我們將所有的值減去0.5,映射到區間[ -0.5,0.5 ]上。現在,每個圖像都由一個1024維的數組表示,每一個值都在- 0.5到0.5之間。在訓練分類網絡時,標準化你的輸入值在[-1,1]之間是個很常見的做法。
X_train_flat = np.dot(X_train[...,:3], [0.299, 0.587, 0.114]).reshape(X_train.shape[0],-1).astype(np.float32) X_train_flat = (X_train_flat/255.0)-0.5 X_test_flat = np.dot(X_test[...,:3], [0.299, 0.587, 0.114]).reshape(X_test.shape[0],-1).astype(np.float32) X_test_flat = (X_test_flat/255.0)-.5使用nolearn的API,我們可以很容易地創建一個輸入層,隱藏層和輸出層的多層感知器。hidden_num_units = 100表示我們的隱藏層有100個神經元,output_num_units = 10則表示我們的輸出層有10個神經元,並與標籤一一對應。輸出前,網絡使用 softmax函數來確定最可能的標籤。迭代50次並且設置verbose=1來訓練模型,最後會輸出每次迭代的結果及其需要的運行時間。
net1 = NeuralNet( layers = [ ('input', layers.InputLayer), ('hidden', layers.DenseLayer), ('output', layers.DenseLayer), ], #layers parameters: input_shape = (None, 1024), hidden_num_units = 100, output_nonlinearity = softmax, output_num_units = 10, #optimization parameters: update = nesterov_momentum, update_learning_rate = 0.01, update_momentum = 0.9, regression = False, max_epochs = 50, verbose = 1, )從側面來說,這個接口使得它很容易建立深層網絡。如果我們想要添加第二個隱藏層,我們所需要做的就是把它添加到圖層參數中,然後在新增的一層中指定多少個神經元。
net1 = NeuralNet( layers = [ ('input', layers.InputLayer), ('hidden1', layers.DenseLayer), ('hidden2', layers.DenseLayer), #Added Layer Here ('output', layers.DenseLayer), ], #layers parameters: input_shape = (None, 1024), hidden1_num_units = 100, hidden2_num_units = 100, #Added Layer Params Here現在,正如我前面提到的關於Nolearn類似Scikit-Learn風格的API,我們可以用fit函數來擬合神經網絡。
net1.fit(X_train_flat, y_train)當網絡使用GPU訓練時,我們可以看到每次迭代時間通常需要0.5秒。
另一方面,當Domino的硬體參數設置為XX-Large(32 core, 60 GB RAM),每次迭代時間通常需要1.3秒。
通過GPU訓練的神經網絡,我們可以看到在訓練網絡上大約提速了3倍。正如預期的那樣,使用GPU訓練好的神經網絡和使用CPU訓練好的神經網絡產生了類似的結果。兩者產生了相似的測試精度(約為41%)以及相似的訓練損失。
通過下面代碼,我們可以在測試數據上測試網絡:
y_pred1 = net1.predict(X_test_flat) print "The accuracy of this network is: %0.2f" % (y_pred1 == y_test).mean()最後,我們在測試數據上得到的精度為41%。
卷積網絡
卷積神經網絡是一種更為複雜的神經網絡結構,它的一個層中的神經元和上一層的一個子集神經元相連。結果,卷積往往會池化每個子集的輸出。
圖片來源: http://colah.github.io/posts/2014-07-Conv-Nets-Modular/
卷積神經網絡在企業和 Kaggle 競賽中很受歡迎,因為它能靈活地學習不同的問題並且易擴展。
同樣,在我們建立卷積神經網絡之前,我們首先必須對數據進行灰度化和變換。這次我們會保持圖像32x32的大小不變。此外,我已經修改了矩陣的行順序,所以每個圖像現在被表示為(color,x,y)格式。跟之前一樣,我將特徵的每個值除以255,再減去0.5,最後將數值映射到區間(-1,1)。
X_train_2d = np.dot(X_train[...,:3], [0.299, 0.587, 0.114]).reshape(-1,1,32,32).astype(np.float32) X_train_2d = (X_train_2d/255.0)-0.5 X_test_2d = np.dot(X_test[...,:3], [0.299, 0.587, 0.114]).reshape(-1,1,32,32).astype(np.float32) X_train_2d = (X_train_2d/255.0)-0.5現在我們可以構造卷積神經網絡了。該網絡由輸入層,3個卷積層,3個2x2池化層,200個神經元隱藏層以及最後的輸出層構成。
net2 = NeuralNet( layers = [ ('input', layers.InputLayer), ('conv1', layers.Conv2DLayer), ('pool1', layers.MaxPool2DLayer), ('conv2', layers.Conv2DLayer), ('pool2', layers.MaxPool2DLayer), ('conv3', layers.Conv2DLayer), ('pool3', layers.MaxPool2DLayer), ("hidden4", layers.DenseLayer), ("output", layers.DenseLayer), ], #layer parameters: input_shape = (None, 1, 32, 32), conv1_num_filters = 16, conv1_filter_size = (3, 3), pool1_pool_size = (2,2), conv2_num_filters = 32, conv2_filter_size = (2, 2) , pool2_pool_size = (2,2), conv3_num_filters = 64, conv3_filter_size = (2, 2), pool3_pool_size = (2,2), hidden4_num_units = 200, output_nonlinearity = softmax, output_num_units = 10, #optimization parameters: update = nesterov_momentum, update_learning_rate = 0.015, update_momentum = 0.9, regression = False, max_epochs = 5, verbose = 1, )接著,我們再次使用fit函數來擬合模型。
net2.fit(X_train_2d, y_train)與多層感知器相比,卷積神經網絡的訓練時間會更長。使用GPU來訓練,大多數的迭代需要12.8s來完成,然而,卷積神經網絡驗證損失約為63%,超過了驗證損失為40%的多層感知器。也就是說,通過卷積層和池化層的結合,我們可以提高20%的精度。
在只有Domino的XX-大型硬體層的CPU上,每個訓練周期大概需要177秒完成,接近於3分鐘。也就是說,用GPU訓練,訓練時間提升了大約15倍。
和前面一樣,我們可以看到在CUP上訓練的卷積神經網絡與GPU上訓練的卷積神經網絡有著類似的結果,相似的驗證精度與訓練損失。
此外,當我們在測試數據上測試卷積神經網絡時,我們得到了61%的精度。
y_pred2 = net2.predict(X_test_2d) print "The accuracy of this network is: %0.2f" % (y_pred2 == y_test).mean()建立卷積神經網絡的所有代碼都可以在ConvolutionNN.py這個 文件中找到。
最後,正如你所看到的,使用GPU訓練的深度神經網絡會加快運行加速,在這個項目中它提升的速度在3倍到15倍之間。無論是在工業界還是學術界,我們經常會使用多個GPU,因為這會大大減少深層網絡訓練的運行時間,通常能從幾周下降至幾天。
原文:Faster deep learning with GPUs and Theano(譯者/劉帝偉 審校/劉翔宇、朱正貴 責編/周建丁)
關於譯者:劉帝偉,中南大學軟體學院在讀研究生,關注機器學習、數據挖掘及生物信息領域。
本文為CSDN原創,點擊「閱讀原文」可查看原文並參與討論。
如果您喜歡這篇文章,請點擊右上角「…」將本文分享給你的朋友