【編者按】GPU因其浮點計算和矩陣運算能力有助于加速深度學(xué)習(xí)是業(yè)界的共識,Theano是主流的深度學(xué)習(xí)Python庫之一,亦支持GPU,然而Theano入門較難,Domino的這篇博文介紹了如何使用GPU和Theano加速深度學(xué)習(xí),使用更簡單的基于Theano的 Nolearn庫。教程由多層感知器及卷積神經(jīng)網(wǎng)絡(luò),由淺入深,是不錯的入門資料。
實現(xiàn)神經(jīng)網(wǎng)絡(luò)算法的Python庫中,最受歡迎的當(dāng)屬Theano。然而,Theano并不是嚴(yán)格意義上的神經(jīng)網(wǎng)絡(luò)庫,而是一個Python庫,它可以實現(xiàn)各種各樣的數(shù)學(xué)抽象。正因為如此,Theano有著陡峭的學(xué)習(xí)曲線,所以我將介紹基于Theano構(gòu)建的有更平緩的學(xué)習(xí)曲線的兩個神經(jīng)網(wǎng)絡(luò)庫。
第一個庫是 Lasagne。該庫提供了一個很好的抽象,它允許你構(gòu)建神經(jīng)網(wǎng)絡(luò)的每一層,然后堆疊在彼此的頂部來構(gòu)建一個完整的模型。盡管這比Theano顯得更好,但是構(gòu)建每一層,然后附加在彼此頂部會顯得有些冗長乏味,所以我們將使用 Nolearn庫,它在Lasagne庫上提供了一個類似 Scikit-Learn風(fēng)格的API,能夠輕松地構(gòu)建多層神經(jīng)網(wǎng)絡(luò)。
延伸閱讀: 從Theano到Lasagne:基于Python的深度學(xué)習(xí)的框架和庫
由于這些庫默認(rèn)使用的不是Domino硬件,所以你需要創(chuàng)建一個requirements.txt文件,該文件內(nèi)容如下:
-e git://github.com/Theano/Theano.git#egg=Theano-e git://github.com/lasagne/lasagne.git#egg=lasagnenolearn==0.5.0
現(xiàn)在,在我們導(dǎo)入Lasagne庫和Nolearn庫之前,首先我們需要配置Theano,使其可以使用GPU硬件。要做到這一點,我們需要在我們的工程目錄中新建一個.theanorc文件,該文件內(nèi)容如下:
[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)
上面的代碼會在主目錄創(chuàng)建了一個空的.theanorc文件,然后復(fù)制我們項目目錄下的.theanorc文件內(nèi)容到該文件中。
將硬件切換到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,上面的函數(shù)運行時間應(yīng)該需要0.7秒,并且輸出“Used the gpu”。否則,整個過程將需要2.6秒的運行時間,同時輸出“Used the cpu”'。如果輸出的是后一個,那么你肯定是忘記將硬件切換到GPU了。
對于這個項目,我們將使用CIFAR-10圖像數(shù)據(jù)集,它來自10個不同的類別,包含了60000個32x32大小的彩色圖像。
幸運的是,這些數(shù)據(jù)屬于 pickled格式,所以我們可以使用輔助函數(shù)來加載數(shù)據(jù),將每個文件加載到NumPy數(shù)組中并返回訓(xùn)練集(Xtr),訓(xùn)練集標(biāo)簽(Ytr),測試集(Xte)以及測試集標(biāo)簽(Yte)。下列代碼歸功于 課程的工作人員。
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
多層感知器是一種最簡單的神經(jīng)網(wǎng)絡(luò)模型。該模型包括一個輸入層數(shù)據(jù),一個施加一些數(shù)學(xué)變換的隱藏層,以及一個輸出層用來產(chǎn)生一個標(biāo)簽(不管是分類還是回歸,都一樣)。
圖片來源:http://dms.irb.hr/tutorial/tut_nnets_short.php
在我們使用訓(xùn)練數(shù)據(jù)之前,我們需要把它的灰度化,把它變成一個二維矩陣。此外,我們將每個值除以255然后減去0.5。當(dāng)我們對圖像進(jìn)行灰度化時,我們將每一個(R,G,B)元組轉(zhuǎn)換成0到255之間的浮點值)。通過除以255,可以標(biāo)準(zhǔn)化灰度值映射到[0,1]之間。接下來,我們將所有的值減去0.5,映射到區(qū)間[ -0.5,0.5 ]上?,F(xiàn)在,每個圖像都由一個1024維的數(shù)組表示,每一個值都在- 0.5到0.5之間。在訓(xùn)練分類網(wǎng)絡(luò)時,標(biāo)準(zhǔn)化你的輸入值在[-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,我們可以很容易地創(chuàng)建一個輸入層,隱藏層和輸出層的多層感知器。hidden_num_units = 100表示我們的隱藏層有100個神經(jīng)元,output_num_units = 10則表示我們的輸出層有10個神經(jīng)元,并與標(biāo)簽一一對應(yīng)。輸出前,網(wǎng)絡(luò)使用 softmax函數(shù)來確定最可能的標(biāo)簽。迭代50次并且設(shè)置verbose=1來訓(xùn)練模型,最后會輸出每次迭代的結(jié)果及其需要的運行時間。
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, )
從側(cè)面來說,這個接口使得它很容易建立深層網(wǎng)絡(luò)。如果我們想要添加第二個隱藏層,我們所需要做的就是把它添加到圖層參數(shù)中,然后在新增的一層中指定多少個神經(jīng)元。
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
現(xiàn)在,正如我前面提到的關(guān)于Nolearn類似Scikit-Learn風(fēng)格的API,我們可以用fit函數(shù)來擬合神經(jīng)網(wǎng)絡(luò)。
net1.fit(X_train_flat, y_train)
當(dāng)網(wǎng)絡(luò)使用GPU訓(xùn)練時,我們可以看到每次迭代時間通常需要0.5秒。
另一方面,當(dāng)Domino的硬件參數(shù)設(shè)置為XX-Large(32 core, 60 GB RAM),每次迭代時間通常需要1.3秒。
通過GPU訓(xùn)練的神經(jīng)網(wǎng)絡(luò),我們可以看到在訓(xùn)練網(wǎng)絡(luò)上大約提速了3倍。正如預(yù)期的那樣,使用GPU訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)和使用CPU訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)產(chǎn)生了類似的結(jié)果。兩者產(chǎn)生了相似的測試精度(約為41%)以及相似的訓(xùn)練損失。
通過下面代碼,我們可以在測試數(shù)據(jù)上測試網(wǎng)絡(luò):
y_pred1 = net1.predict(X_test_flat) print "The accuracy of this network is: %0.2f" % (y_pred1 == y_test).mean()
最后,我們在測試數(shù)據(jù)上得到的精度為41%。
卷積神經(jīng)網(wǎng)絡(luò)是一種更為復(fù)雜的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),它的一個層中的神經(jīng)元和上一層的一個子集神經(jīng)元相連。結(jié)果,卷積往往會池化每個子集的輸出。
圖片來源: http://colah.github.io/posts/2014-07-Conv-Nets-Modular/
卷積神經(jīng)網(wǎng)絡(luò)在企業(yè)和 Kaggle 競賽中很受歡迎,因為它能靈活地學(xué)習(xí)不同的問題并且易擴展。
同樣,在我們建立卷積神經(jīng)網(wǎng)絡(luò)之前,我們首先必須對數(shù)據(jù)進(jìn)行灰度化和變換。這次我們會保持圖像32x32的大小不變。此外,我已經(jīng)修改了矩陣的行順序,所以每個圖像現(xiàn)在被表示為(color,x,y)格式。跟之前一樣,我將特征的每個值除以255,再減去0.5,最后將數(shù)值映射到區(qū)間(-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現(xiàn)在我們可以構(gòu)造卷積神經(jīng)網(wǎng)絡(luò)了。該網(wǎng)絡(luò)由輸入層,3個卷積層,3個2x2池化層,200個神經(jīng)元隱藏層以及最后的輸出層構(gòu)成。
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函數(shù)來擬合模型。
net2.fit(X_train_2d, y_train)與多層感知器相比,卷積神經(jīng)網(wǎng)絡(luò)的訓(xùn)練時間會更長。使用GPU來訓(xùn)練,大多數(shù)的迭代需要12.8s來完成,然而,卷積神經(jīng)網(wǎng)絡(luò)驗證損失約為63%,超過了驗證損失為40%的多層感知器。也就是說,通過卷積層和池化層的結(jié)合,我們可以提高20%的精度。
在只有Domino的XX-大型硬件層的CPU上,每個訓(xùn)練周期大概需要177秒完成,接近于3分鐘。也就是說,用GPU訓(xùn)練,訓(xùn)練時間提升了大約15倍。
和前面一樣,我們可以看到在CUP上訓(xùn)練的卷積神經(jīng)網(wǎng)絡(luò)與GPU上訓(xùn)練的卷積神經(jīng)網(wǎng)絡(luò)有著類似的結(jié)果,相似的驗證精度與訓(xùn)練損失。
此外,當(dāng)我們在測試數(shù)據(jù)上測試卷積神經(jīng)網(wǎng)絡(luò)時,我們得到了61%的精度。
y_pred2 = net2.predict(X_test_2d) print "The accuracy of this network is: %0.2f" % (y_pred2 == y_test).mean()
建立卷積神經(jīng)網(wǎng)絡(luò)的所有代碼都可以在ConvolutionNN.py這個 文件中找到。
最后,正如你所看到的,使用GPU訓(xùn)練的深度神經(jīng)網(wǎng)絡(luò)會加快運行加速,在這個項目中它提升的速度在3倍到15倍之間。無論是在工業(yè)界還是學(xué)術(shù)界,我們經(jīng)常會使用多個GPU,因為這會大大減少深層網(wǎng)絡(luò)訓(xùn)練的運行時間,通常能從幾周下降至幾天。
原文鏈接: Faster deep learning with GPUs and Theano(譯者/劉帝偉 審校/劉翔宇、朱正貴 責(zé)編/周建丁)
關(guān)于譯者: 劉帝偉,中南大學(xué)軟件學(xué)院在讀研究生,關(guān)注機器學(xué)習(xí)、數(shù)據(jù)挖掘及生物信息領(lǐng)域。
聯(lián)系客服