本文為數(shù)盟原創(chuàng)譯文,轉(zhuǎn)載時(shí)請注明出處為“數(shù)盟社區(qū)”。
Theano是一個(gè)Python庫,可以在CPU或GPU上運(yùn)行快速數(shù)值計(jì)算。
這是Python深度學(xué)習(xí)中的一個(gè)關(guān)鍵基礎(chǔ)庫,你可以直接用它來創(chuàng)建深度學(xué)習(xí)模型或包裝庫,大大簡化了程序。
在這篇文章中,你會發(fā)現(xiàn)Theano Python庫。
Theano是什么?
Theano是在BSD許可證下發(fā)布的一個(gè)開源項(xiàng)目,是由LISA集團(tuán)(現(xiàn)MILA)在加拿大魁北克的蒙特利爾大學(xué)(Yoshua Bengio主場)開發(fā)。它是用一個(gè)希臘數(shù)學(xué)家的名字命名的。
Python的核心Theano是一個(gè)數(shù)學(xué)表達(dá)式的編譯器。它知道如何獲取你的結(jié)構(gòu),并使之成為一個(gè)使用numpy、高效本地庫的非常高效的代碼,如BLAS和本地代碼(C++),在CPU或GPU上盡可能快地運(yùn)行。
它巧妙的采用一系列代碼優(yōu)化從硬件中攫取盡可能多的性能。如果你對代碼中的數(shù)學(xué)優(yōu)化的基本事實(shí)感興趣,看看這個(gè)有趣的名單。
Theano表達(dá)式的實(shí)際語法是象征性的,可以推送給初學(xué)者用于一般軟件開發(fā)。具體來說,表達(dá)式是在抽象的意義上定義,編譯和后期是用來進(jìn)行計(jì)算。
它是為深度學(xué)習(xí)中處理大型神經(jīng)網(wǎng)絡(luò)算法所需的計(jì)算而專門設(shè)計(jì)的。它是這類庫的首創(chuàng)之一(發(fā)展始于2007年),被認(rèn)為是深度學(xué)習(xí)研究和開發(fā)的行業(yè)標(biāo)準(zhǔn)。
如何安裝Theano
Theano提供了主要的操作系統(tǒng)詳細(xì)的安裝說明:Windows、OS X和Linux。為你的平臺閱讀Theano安裝指南。
Theano需要一個(gè)Python2或Python3包含SciPy的工作環(huán)境。這種方法使安裝更加容易,比如用Anaconda在你的機(jī)器上快速建立Python和SciPy,以及實(shí)用Docker圖像。
隨著運(yùn)作的Python和SciPy環(huán)境,安裝Theano就變得相對簡單。使用PIP來自PyPI,例如:
1 | pip install Theano |
撰寫Theano的最后一個(gè)正式發(fā)布的版本為0.8,發(fā)布時(shí)間是2016年3月21日。
新版本將要宣布,你將要通過更新得到一些錯(cuò)誤修復(fù)和效率的提高。您可以使用PIP升級Theano方法如下:
1 | sudo pip install –upgrade –no-deps theano |
您可能需要使用Theano的前沿版本來直接找出Github。
這可能需要一些包裝庫,用來改變API的前沿。您可以按如下方法從找到的Github上直接安裝Theano:
1 | pip install –upgrade –no-deps git+git://github.com/Theano/Theano.git |
你現(xiàn)在已經(jīng)準(zhǔn)備好在你的CPU上運(yùn)行Theano了,其十分適合小模型的開發(fā)。
大型模型可能在CPU上運(yùn)行緩慢。如果你有一個(gè)Nvidia的GPU,你可能想看看使用您的GPU配置Theano。閱讀對于Linux或Mac OS X的使用GPU指南建立Theano并使用GPU,使用GPU指南測試其是否正常工作。
簡單的Theano例子
在這一節(jié)中我們展示了一個(gè)簡單的Python腳本,讓你對Theano稍加了解。
它是從Theano一覽導(dǎo)向中摘取出來的。在這個(gè)例子中,我們定義了兩個(gè)符號浮點(diǎn)變量a和b。
我們定義一個(gè)使用這些變量的表達(dá)式(C=A + B)。
然后,我們編譯這個(gè)象征性的表達(dá)式為使用Theano功能,我們可以在以后使用。
最后,我們通過填入一些真正的值和采用高效的編譯代碼Theano執(zhí)行計(jì)算,來使用我們編寫的表達(dá)式。
import theanofrom theano import tensor# declare two symbolic floating-point scalarsa=tensor.dscalar()b=tensor.dscalar()# create a simple expressionc=a + b# convert the expression into a callable object that takes (a,b)# values as input and computes a value for cf=theano.function([a,b], c)# bind 1.5 to ‘a(chǎn)’, 2.5 to ‘b’, and evaluate ‘c’assert 4.0==f(1.5, 2.5) |
運(yùn)行的示例不提供任何輸出。結(jié)論1.5+2.5=4.0是真實(shí)的。
這是一個(gè)有用的例子,因?yàn)樗鼮槟峁┝艘粋€(gè)象征性的表達(dá)式是怎么定義,編譯和使用的過程。你可以看到它是如何擴(kuò)展到深度學(xué)習(xí)所需要的大向量和矩陣運(yùn)算中去。
Theano的擴(kuò)展和包裝
如果你是深度學(xué)習(xí)的新人,你不必直接使用Theano。
事實(shí)上,我們強(qiáng)烈鼓勵使用許多流行的Python項(xiàng)目,它會使深度學(xué)習(xí)中的Theano使用起來更加簡便。
這些項(xiàng)目提供Python中的數(shù)據(jù)結(jié)構(gòu)和行為,專門為快速、可信的深度學(xué)習(xí)模型創(chuàng)建而設(shè)計(jì),確保Theano在幕后執(zhí)行快速、高效地創(chuàng)建模型。
Theano語法的數(shù)量由庫變化而顯現(xiàn)。
﹒例如,Lasagne庫為創(chuàng)建深度學(xué)習(xí)模型提供便利類數(shù)據(jù),但仍期望你知道并利用Theano語法。知道或愿意學(xué)一點(diǎn)Theano對于初學(xué)者是有利的。
﹒另一個(gè)例子是Keras,它完全隱藏了Theano并提供了一個(gè)非常簡單的API去創(chuàng)造深度學(xué)習(xí)模型。它把Theano隱藏的如此之好,以至于它實(shí)際上可以作為叫做TensorFlow的另一種流行的基礎(chǔ)框架的包裝運(yùn)行。
我強(qiáng)烈建議直接嘗試一些與Theano相關(guān)的內(nèi)容,然后選擇一個(gè)包裝庫學(xué)習(xí)和實(shí)踐深度學(xué)習(xí)。
對于建立在Theano上的庫的完整列表,請參閱維基上Theano相關(guān)的項(xiàng)目指南。
更多 Theano 資源
Looking for some more resources on Theano? Take a look at some of the following.
Theano Official Homepage
Theano GitHub Repository
Theano: A CPU and GPU Math Compiler in Python(2010) (PDF)
List of Libraries Built on Theano
List of Theano configuration options
Theano 和 深度學(xué)習(xí) 教程
Theano Tutorial
Deep Learning with Theano Tutorial
Theano幫助教程
Theano Users Google Group
總結(jié)
在這篇文章中,你發(fā)現(xiàn)了進(jìn)行有效數(shù)值計(jì)算的Theano Python庫。
你了解到這是用于深度學(xué)習(xí)研究和發(fā)展的基礎(chǔ)庫,它可以直接用于創(chuàng)建深度學(xué)習(xí)模型或通過便利庫建立在它之上,如Lasagne和Keras。
原文鏈接:http://machinelearningmastery.com/introduction-python-deep-learning-library-theano/
導(dǎo)讀:NumPy是數(shù)據(jù)計(jì)算的基礎(chǔ),更是深度學(xué)習(xí)框架的基石。但如果直接使用NumPy計(jì)算大數(shù)據(jù),其性能已成為一個(gè)瓶頸。
隨著數(shù)據(jù)爆炸式增長,尤其是圖像數(shù)據(jù)、音頻數(shù)據(jù)等數(shù)據(jù)的快速增長,迫切需要突破NumPy性能上的瓶頸。需求就是強(qiáng)大動力!通過大家的不懈努力,在很多方面取得可喜進(jìn)展,如硬件有GPU,軟件有Theano、Keras、TensorFlow,算法有卷積神經(jīng)網(wǎng)絡(luò)、循環(huán)神經(jīng)網(wǎng)絡(luò)等。
作者:吳茂貴,王冬,李濤,楊本法
如需轉(zhuǎn)載請聯(lián)系華章科技
Theano是Python的一個(gè)庫,為開源項(xiàng)目,在2008年,由Yoshua Bengio領(lǐng)導(dǎo)的加拿大蒙特利爾理工學(xué)院LISA實(shí)驗(yàn)室開發(fā)。對于解決大量數(shù)據(jù)的問題,使用Theano可能獲得與手工用C實(shí)現(xiàn)差不多的性能。另外通過利用GPU,它能獲得比CPU上快很多數(shù)量級的性能。
至于Theano是如何實(shí)現(xiàn)性能方面的跨越,如何用“符號計(jì)算圖”來運(yùn)算等內(nèi)容,本文都將有所涉獵,但限于篇幅無法深入分析,只做一些基礎(chǔ)性的介紹。涵蓋的主要內(nèi)容:
Theano開發(fā)者在2010年公布的測試報(bào)告中指出:在CPU上執(zhí)行程序時(shí),Theano程序性能是NumPy的1.8倍,而在GPU上是NumPy的11倍。這還是2010年的測試結(jié)果,近些年無論是Theano還是GPU,性能都有顯著提高。
這里我們把Theano作為基礎(chǔ)來講,除了性能方面的跨越外,它還是“符合計(jì)算圖”的開創(chuàng)者,當(dāng)前很多優(yōu)秀的開源工具,如TensorFlow、Keras等,都派生于或借鑒了Theano的底層設(shè)計(jì)。所以了解Theano的使用,將有助于我們更好地學(xué)習(xí)TensorFlow、Keras等其他開源工具。
這里主要介紹Linux+Anaconda+theano環(huán)境的安裝說明,在CentOS或Ubuntu環(huán)境下,建議使用Python的Anaconda發(fā)行版,后續(xù)版本升級或添加新模塊可用Conda工具。當(dāng)然也可用pip進(jìn)行安裝。但最好使用工具來安裝,這樣可以避免很多程序依賴的麻煩,而且日后的軟件升級維護(hù)也很方便。
Theano支持CPU、GPU,如果使用GPU還需要安裝其驅(qū)動程序如CUDA等,限于篇幅,這里只介紹CPU的,有關(guān)GPU的安裝,大家可參考:
http://www.deeplearning.net/software/theano/install.html
以下為主要安裝步驟:
1. 安裝anaconda
從anaconda官網(wǎng)下載Linux環(huán)境最新的軟件包,Python版本建議選擇3系列的,2系列后續(xù)將不再維護(hù)。
anaconda官網(wǎng):
https://www.anaconda.com/download/
下載文件為一個(gè)sh程序包,如Anaconda3-4.3.1-Linux-x86_64.sh,然后在下載目錄下運(yùn)行如下命令:
bash Anaconda3-4.3.1-Linux-x86_64.sh
安裝過程中按enter或y即可,安裝完成后,程序提示是否把a(bǔ)naconda的binary加入到.bashrc配置文件中,加入后運(yùn)行python、ipython時(shí)將自動使用新安裝的Python環(huán)境。
安裝完成后,你可用conda list命令查看已安裝的庫:
conda list
安裝成功的話,應(yīng)該能看到numpy、scipy、matplotlib、conda等庫。
2. 安裝theano
利用conda 來安裝或更新程序:
conda install theano
3. 測試
先啟動Python,然后導(dǎo)入theano模塊,如果不報(bào)錯(cuò),說明安裝成功。
$ Python Python 3.6.0 |Anaconda custom (64-bit)| (default, Dec 23 2016, 12:22:00) [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import theano >>>
存儲數(shù)據(jù)需要用到各種變量,那Theano是如何使用變量的呢?Theano用符號變量TensorVariable來表示變量,又稱為張量(Tensor)。
張量是Theano的核心元素(也是TensorFlow的核心元素),是Theano表達(dá)式和運(yùn)算操作的基本單位。張量可以是標(biāo)量(scalar)、向量(vector)、矩陣(matrix)等的統(tǒng)稱。
具體來說,標(biāo)量就是我們通常看到的0階的張量,如12,a等,而向量和矩陣分別為1階張量和2階的張量。
如果通過這些概念,你還不很清楚,沒有關(guān)系,可以結(jié)合以下實(shí)例來直觀感受一下。
首先定義三個(gè)標(biāo)量:一個(gè)代表輸入x、一個(gè)代表權(quán)重w、一個(gè)代表偏移量b,然后計(jì)算這些標(biāo)量運(yùn)算結(jié)果z=x*w+b,Theano代碼實(shí)現(xiàn)如下:
#導(dǎo)入需要的庫或模塊 import theano from theano import tensor as T #初始化張量 x=T.scalar(name='input',dtype='float32') w=T.scalar(name='weight',dtype='float32') b=T.scalar(name='bias',dtype='float32') z=w*x+b #編譯程序 net_input=theano.function(inputs=[w,x,b],outputs=z) #執(zhí)行程序 print('net_input: %2f'% net_input(2.0,3.0,0.5))
打印結(jié)果:
net_input: 6.500000
通過以上實(shí)例我們不難看出,Theano本身是一個(gè)通用的符號計(jì)算框架,與非符號架構(gòu)的框架不同,它先使用tensor variable初始化變量,然后將復(fù)雜的符號表達(dá)式編譯成函數(shù)模型,最后運(yùn)行時(shí)傳入實(shí)際數(shù)據(jù)進(jìn)行計(jì)算。
整個(gè)過程涉及三個(gè)步驟:定義符號變量,編譯代碼,執(zhí)行代碼。這節(jié)主要介紹第一步如何定義符號變量,其他步驟將在后續(xù)小節(jié)介紹。
如何定義符號變量?或定義符號變量有哪些方式?在Theano中定義符號變量的方式有三種:使用內(nèi)置的變量類型、自定義變量類型、轉(zhuǎn)換其他的變量類型。具體如下:
1. 使用內(nèi)置的變量類型創(chuàng)建
目前Theano支持7種內(nèi)置的變量類型,分別是標(biāo)量(scalar)、向量(vector)、行(row)、列(col)、矩陣(matrix)、tensor3、tensor4等。其中標(biāo)量是0階張量,向量為1階張量,矩陣為2階張量等,以下為創(chuàng)建內(nèi)置變量的實(shí)例:
import theano from theano import tensor as T x=T.scalar(name='input',dtype='float32') data=T.vector(name='data',dtype='float64')
其中,name指定變量名字,dtype指變量的數(shù)據(jù)類型。
2. 自定義變量類型
內(nèi)置的變量類型只能處理4維及以下的變量,如果需要處理更高維的數(shù)據(jù)時(shí),可以使用Theano的自定義變量類型,具體通過TensorType方法來實(shí)現(xiàn):
import theano from theano import tensor as T mytype=T.TensorType('float64',broadcastable=(),name=None,sparse_grad=False)
其中broadcastable是True或False的布爾類型元組,元組的大小等于變量的維度,如果為True,表示變量在對應(yīng)維度上的數(shù)據(jù)可以進(jìn)行廣播,否則數(shù)據(jù)不能廣播。
廣播機(jī)制(broadcast)是一種重要機(jī)制,有了這種機(jī)制,就可以方便地對不同維的張量進(jìn)行運(yùn)算,否則,就要手工把低維數(shù)據(jù)變成高維,利用廣播機(jī)制系統(tǒng)自動復(fù)制等方法把低維數(shù)據(jù)補(bǔ)齊(MumPy也有這種機(jī)制)。以下我們通過圖2-1所示的一個(gè)實(shí)例來說明廣播機(jī)制原理。
▲圖2-1 廣播機(jī)制
圖2-1中矩陣與向量相加的具體代碼如下:
import theano import numpy as np import theano.tensor as T r=T.row() r.broadcastable # (True, False) mtr=T.matrix() mtr.broadcastable # (False, False) f_row=theano.function([r, mtr], [r + mtr]) R=np.arange(1,3).reshape(1,2) print(R) #array([[1, 2]]) M=np.arange(1,7).reshape(3, 2) print(M) #array([[1, 2], # [3, 4], # [5, 6]]) f_row(R, M) #[array([[ 2., 4.], # [ 4., 6.], # [ 6., 8.]])]
3. 將Python類型變量或者NumPy類型變量轉(zhuǎn)化為Theano共享變量
共享變量是Theano實(shí)現(xiàn)變量更新的重要機(jī)制,后面我們會詳細(xì)講解。要創(chuàng)建一個(gè)共享變量,只要把一個(gè)Python對象或NumPy對象傳遞給shared函數(shù)即可,如下所示:
import theano import numpy as np import theano.tensor as T data=np.array([[1,2],[3,4]]) shared_data=theano.shared(data) type(shared_data)
符號變量定義后,需要說明這些變量間的運(yùn)算關(guān)系,那如何描述變量間的運(yùn)算關(guān)系呢?Theano實(shí)際采用符號計(jì)算圖模型來實(shí)現(xiàn)。首先創(chuàng)建表達(dá)式所需的變量,然后通過操作符(op)把這些變量結(jié)合在一起,如前文圖2-1所示。
Theano處理符號表達(dá)式時(shí)是通過把符號表達(dá)式轉(zhuǎn)換為一個(gè)計(jì)算圖(graph)來處理(TensorFlow也使用了這種方法,等到我們介紹TensorFlow時(shí),大家可對比一下),符號計(jì)算圖的節(jié)點(diǎn)有:variable、type、apply和op。
Theano是將符號表達(dá)式的計(jì)算表示成計(jì)算圖。這些計(jì)算圖是由Apply 和 Variable將節(jié)點(diǎn)連接而組成,它們分別與函數(shù)的應(yīng)用和數(shù)據(jù)相連接。操作由op 實(shí)例表示,而數(shù)據(jù)類型由type 實(shí)例表示。
下面這段代碼和圖2-2說明了這些代碼所構(gòu)建的結(jié)構(gòu)。借助這個(gè)圖或許有助于你進(jìn)一步理解如何將這些內(nèi)容擬合在一起:
import theano import numpy as np import theano.tensor as T x=T.dmatrix('x') y=T.dmatrix('y') z=x + y
▲圖2-2 符號計(jì)算圖
圖2-2中箭頭表示指向Python對象的引用。中間大的長方形是一個(gè) Apply 節(jié)點(diǎn),3個(gè)圓角矩形(如X)是 Variable 節(jié)點(diǎn),帶+號的圓圈是ops,3個(gè)圓角小長方形(如matrix)是Types。
在創(chuàng)建 Variables 之后,應(yīng)用 Apply ops得到更多的變量,這些變量僅僅是一個(gè)占位符,在function中作為輸入。變量指向 Apply 節(jié)點(diǎn)的過程是用來表示函數(shù)通過owner 域來生成它們 。這些Apply節(jié)點(diǎn)是通過它們的inputs和outputs域來得到它們的輸入和輸出變量。
x和y的owner域的指向都是None,這是因?yàn)樗鼈儾皇橇硪粋€(gè)計(jì)算的結(jié)果。如果它們中的一個(gè)變量是另一個(gè)計(jì)算的結(jié)果,那么owner域?qū)赶蛄硪粋€(gè)藍(lán)色盒。
上節(jié)我們介紹了如何把一個(gè)符號表達(dá)式轉(zhuǎn)化為符號計(jì)算圖,這節(jié)我們介紹函數(shù)的功能,函數(shù)是Theano的一個(gè)核心設(shè)計(jì)模塊,它提供一個(gè)接口,把函數(shù)計(jì)算圖編譯為可調(diào)用的函數(shù)對象。前面介紹了如何定義自變量x(不需要賦值),這節(jié)介紹如何編寫函數(shù)方程。
1. 函數(shù)定義的格式
先來看一下函數(shù)格式示例:
theano.function(inputs, outputs, mode=None, updates=None, givens=None, no_default_updates=False, accept_inplace=False, name=None,rebuild_strict=True, allow_input_downcast=None, profile=None, on_unused_input='raise')
這里參數(shù)看起來很多,但一般只用到三個(gè):inputs表示自變量;outputs表示函數(shù)的因變量(也就是函數(shù)的返回值);還有一個(gè)比較常用的是updates參數(shù),它一般用于神經(jīng)網(wǎng)絡(luò)共享變量參數(shù)更新,通常以字典或元組列表的形式指定。
此外,givens是一個(gè)字典或元組列表,記為[(var1,var2)],表示在每一次函數(shù)調(diào)用時(shí),在符號計(jì)算圖中,把符號變量var1節(jié)點(diǎn)替換為var2節(jié)點(diǎn),該參數(shù)常用來指定訓(xùn)練數(shù)據(jù)集的batch大小。
下面我們看一個(gè)有多個(gè)自變量,同時(shí)又有多個(gè)因變量的函數(shù)定義例子:
import theano x, y=theano.tensor.fscalars('x', 'y') z1=x + y z2=x*y #定義x、y為自變量,z1、z2為函數(shù)返回值(因變量) f=theano.function([x,y],[z1,z2]) #返回當(dāng)x=2,y=3的時(shí)候,函數(shù)f的因變量z1,z2的值 print(f(2,3))
打印結(jié)果:
[array(5.0, dtype=float32), array(6.0, dtype=float32)]
在執(zhí)行theano.function()時(shí),Theano進(jìn)行了編譯優(yōu)化,得到一個(gè)end-to-end的函數(shù),傳入數(shù)據(jù)調(diào)用f(2,3)時(shí),執(zhí)行的是優(yōu)化后保存在圖結(jié)構(gòu)中的模型,而不是我們寫的那行z=x+y,盡管二者結(jié)果一樣。
這樣的好處是Theano可以對函數(shù)f進(jìn)行優(yōu)化,提升速度;壞處是不方便開發(fā)和調(diào)試,由于實(shí)際執(zhí)行的代碼不是我們寫的代碼,所以無法設(shè)置斷點(diǎn)進(jìn)行調(diào)試,也無法直接觀察執(zhí)行時(shí)中間變量的值。
2. 自動求導(dǎo)
有了符號計(jì)算,自動計(jì)算導(dǎo)數(shù)就很容易了。tensor.grad()唯一需要做的就是從outputs逆向遍歷到輸入節(jié)點(diǎn)。對于每個(gè)op,它都定義了怎么根據(jù)輸入計(jì)算出偏導(dǎo)數(shù)。使用鏈?zhǔn)椒▌t就可以計(jì)算出梯度了。利用Theano求導(dǎo)時(shí)非常方便,可以直接利用函數(shù)theano.grad(),比如求s函數(shù)的導(dǎo)數(shù):
以下代碼實(shí)現(xiàn)當(dāng)x=3的時(shí)候,求s函數(shù)的導(dǎo)數(shù):
import theano x=theano.tensor.fscalar('x')#定義一個(gè)float類型的變量x y=1 / (1 + theano.tensor.exp(-x))#定義變量y dx=theano.grad(y,x)#偏導(dǎo)數(shù)函數(shù) f=theano.function([x],dx)#定義函數(shù)f,輸入為x,輸出為s函數(shù)的偏導(dǎo)數(shù) print(f(3))#計(jì)算當(dāng)x=3的時(shí)候,函數(shù)y的偏導(dǎo)數(shù)
打印結(jié)果:
0.045176658779382706
3. 更新共享變量參數(shù)
在深度學(xué)習(xí)中通常需要迭代多次,每次迭代都需要更新參數(shù)。Theano如何更新參數(shù)呢?
在theano.function函數(shù)中,有一個(gè)非常重要的參數(shù)updates。updates是一個(gè)包含兩個(gè)元素的列表或tuple,一般示例為updates=[old_w,new_w],當(dāng)函數(shù)被調(diào)用的時(shí)候,會用new_w替換old_w,具體看下面這個(gè)例子。
import theano w=theano.shared(1)#定義一個(gè)共享變量w,其初始值為1 x=theano.tensor.iscalar('x') f=theano.function([x], w, updates=[[w, w+x]])#定義函數(shù)自變量為x,因變量為w,當(dāng)函數(shù)執(zhí)行完畢后,更新參數(shù)w=w+x print(f(3))#函數(shù)輸出為w print(w.get_value())#這個(gè)時(shí)候可以看到w=w+x為4
打印結(jié)果:
1、4
在求梯度下降的時(shí)候,經(jīng)常用到updates這個(gè)參數(shù)。比如updates=[w,w-α*(dT/dw)],其中dT/dw就是梯度下降時(shí),代價(jià)函數(shù)對參數(shù)w的偏導(dǎo)數(shù),α是學(xué)習(xí)速率。為便于大家更全面地了解Theano函數(shù)的使用方法,下面我們通過一個(gè)邏輯回歸的完整實(shí)例來說明:
import numpy as np import theano import theano.tensor as T rng=np.random # 我們?yōu)榱藴y試,自己生成10個(gè)樣本,每個(gè)樣本是3維的向量,然后用于訓(xùn)練 N=10 feats=3 D=(rng.randn(N, feats).astype(np.float32), rng.randint(size=N, low=0, high=2).astype(np.float32)) # 聲明自變量x、以及每個(gè)樣本對應(yīng)的標(biāo)簽y(訓(xùn)練標(biāo)簽) x=T.matrix("x") y=T.vector("y") #隨機(jī)初始化參數(shù)w、b=0,為共享變量 w=theano.shared(rng.randn(feats), name="w") b=theano.shared(0., name="b") #構(gòu)造代價(jià)函數(shù) p_1=1 / (1 + T.exp(-T.dot(x, w) - b)) # s激活函數(shù) xent=-y * T.log(p_1) - (1-y) * T.log(1-p_1) # 交叉商代價(jià)函數(shù) cost=xent.mean() + 0.01 * (w ** 2).sum()# 代價(jià)函數(shù)的平均值+L2正則項(xiàng)以防過擬合,其中權(quán)重衰減系數(shù)為0.01 gw, gb=T.grad(cost, [w, b]) #對總代價(jià)函數(shù)求參數(shù)的偏導(dǎo)數(shù) prediction=p_1 > 0.5 # 大于0.5預(yù)測值為1,否則為0. train=theano.function(inputs=[x,y],outputs=[prediction, xent],updates=((w, w - 0.1 * gw), (b, b - 0.1 * gb)))#訓(xùn)練所需函數(shù) predict=theano.function(inputs=[x], outputs=prediction)#測試階段函數(shù) #訓(xùn)練 training_steps=1000 for i in range(training_steps): pred, err=train(D[0], D[1]) print (err.mean())#查看代價(jià)函數(shù)下降變化過程
編寫函數(shù)需要經(jīng)常用到條件語句或循環(huán)語句,這節(jié)我們就簡單介紹Theano如何實(shí)現(xiàn)條件判斷或邏輯循環(huán)。
1. 條件判斷
Theano是一種符號語言,條件判斷不能直接使用Python的if語句。在Theano可以用ifelse和switch來表示判定語句。這兩個(gè)判定語句有何區(qū)別呢?
switch對每個(gè)輸出變量進(jìn)行操作,ifelse只對一個(gè)滿足條件的變量操作。比如對語句:
switch(cond, ift, iff)
如果滿足條件,則switch既執(zhí)行ift也執(zhí)行iff。而對語句:
if cond then ift else iff
ifelse只執(zhí)行ift或者只執(zhí)行iff。
下面通過一個(gè)示例進(jìn)一步說明:
from theano import tensor as T from theano.ifelse import ifelse import theano,time,numpy a,b=T.scalars('a','b') x,y=T.matrices('x','y') z_switch=T.switch(T.lt(a,b),T.mean(x),T.mean(y))#lt:a < b? z_lazy=ifelse(T.lt(a,b),T.mean(x),T.mean(y)) #optimizer:optimizer的類型結(jié)構(gòu)(可以簡化計(jì)算,增加計(jì)算的穩(wěn)定性) #linker:決定使用哪種方式進(jìn)行編譯(C/Python) f_switch=theano.function([a, b, x, y], z_switch,mode=theano.Mode(linker='vm')) f_lazyifelse=theano.function([a, b, x, y], z_lazy,mode=theano.Mode(linker='vm')) val1=0. val2=1. big_mat1=numpy.ones((1000, 100)) big_mat2=numpy.ones((1000, 100)) n_times=10 tic=time.clock() for i in range(n_times): f_switch(val1, val2, big_mat1, big_mat2) print('time spent evaluating both values %f sec' % (time.clock() - tic)) tic=time.clock() for i in range(n_times): f_lazyifelse(val1, val2, big_mat1, big_mat2) print('time spent evaluating one value %f sec' % (time.clock() - tic))
打印結(jié)果:
time spent evaluating both values 0.005268 sec time spent evaluating one value 0.007501 sec
2. 循環(huán)語句
scan是Theano中構(gòu)建循環(huán)Graph的方法,scan是個(gè)靈活復(fù)雜的函數(shù),任何用循環(huán)、遞歸或者跟序列有關(guān)的計(jì)算,都可以用scan完成。其格式如下:
theano.scan(fn, sequences=None, outputs_info=None, non_sequences=None, n_steps=None, truncate_gradient=-1, go_backwards=False, mode=None, name=None, profile=False, allow_gc=None, strict=False)
參數(shù)說明:
下面通過一個(gè)例子解釋scan函數(shù)的具體使用方法。
代碼實(shí)現(xiàn)思路是:先定義函數(shù)one_step,即scan里的fn,其任務(wù)就是計(jì)算多項(xiàng)式的一項(xiàng),scan函數(shù)返回的result里會保存多項(xiàng)式每一項(xiàng)的值,然后我們對result求和,就得到了多項(xiàng)式的值。
import theano import theano.tensor as T import numpy as np # 定義單步的函數(shù),實(shí)現(xiàn)a*x^n # 輸入?yún)?shù)的順序要與下面scan的輸入?yún)?shù)對應(yīng) def one_step(coef, power, x): return coef * x ** power coefs=T.ivector() # 每步變化的值,系數(shù)組成的向量 powers=T.ivector() # 每步變化的值,指數(shù)組成的向量 x=T.iscalar() # 每步不變的值,自變量 # seq,out_info,non_seq與one_step函數(shù)的參數(shù)順序一一對應(yīng) # 返回的result是每一項(xiàng)的符號表達(dá)式組成的list result, updates=theano.scan(fn=one_step, sequences=[coefs, powers], outputs_info=None, non_sequences=x) # 每一項(xiàng)的值與輸入的函數(shù)關(guān)系 f_poly=theano.function([x, coefs, powers], result, allow_input_downcast=True) coef_val=np.array([2,3,4,6,5]) power_val=np.array([0,1,2,3,4]) x_val=10 print("多項(xiàng)式各項(xiàng)的值: ",f_poly(x_val, coef_val, power_val)) #scan返回的result是每一項(xiàng)的值,并沒有求和,如果我們只想要多項(xiàng)式的值,可以把f_poly寫成這樣: # 多項(xiàng)式每一項(xiàng)的和與輸入的函數(shù)關(guān)系 f_poly=theano.function([x, coefs, powers], result.sum(), allow_input_downcast=True) print("多項(xiàng)式和的值:",f_poly(x_val, coef_val, power_val))
打印結(jié)果:
多項(xiàng)式各項(xiàng)的值: [ 2 30 400 6000 50000] 多項(xiàng)式和的值: 56432
共享變量(shared variable)是實(shí)現(xiàn)機(jī)器學(xué)習(xí)算法參數(shù)更新的重要機(jī)制。shared函數(shù)會返回共享變量。這種變量的值在多個(gè)函數(shù)可直接共享。可以用符號變量的地方都可以用共享變量。
但不同的是,共享變量有一個(gè)內(nèi)部狀態(tài)的值,這個(gè)值可以被多個(gè)函數(shù)共享。它可以存儲在顯存中,利用GPU提高性能。我們可以使用get_value和set_value方法來讀取或者修改共享變量的值,使用共享變量實(shí)現(xiàn)累加操作。
import theano import theano.tensor as T from theano import shared import numpy as np #定義一個(gè)共享變量,并初始化為0 state=shared(0) inc=T.iscalar('inc') accumulator=theano.function([inc], state, updates=[(state, state+inc)]) # 打印state的初始值 print(state.get_value()) accumulator(1) # 進(jìn)行一次函數(shù)調(diào)用 # 函數(shù)返回后,state的值發(fā)生了變化 print(state.get_value())
這里state是一個(gè)共享變量,初始化為0,每次調(diào)用accumulator(),state都會加上inc。共享變量可以像普通張量一樣用于符號表達(dá)式,另外,它還有自己的值,可以直接用.get_value()和.set_value()方法來訪問和修改。
上述代碼引入了函數(shù)中的updates參數(shù)。updates參數(shù)是一個(gè)list,其中每個(gè)元素是一個(gè)元組(tuple),這個(gè)tuple的第一個(gè)元素是一個(gè)共享變量,第二個(gè)元素是一個(gè)新的表達(dá)式。updatas中的共享變量會在函數(shù)返回后更新自己的值。
updates的作用在于執(zhí)行效率,updates多數(shù)時(shí)候可以用原地(in-place)算法快速實(shí)現(xiàn),在GPU上,Theano可以更好地控制何時(shí)何地給共享變量分配空間,帶來性能提升。最常見的神經(jīng)網(wǎng)絡(luò)權(quán)值更新,一般會用update實(shí)現(xiàn)。
Theano基于NumPy,但性能方面又高于NumPy。因Theano采用了張量(Tensor)這個(gè)核心元素,在計(jì)算方面采用符號計(jì)算模型,而且采用共享變量、自動求導(dǎo)、利用GPU等適合于大數(shù)據(jù)、深度學(xué)習(xí)的方法,其他很多開發(fā)項(xiàng)目也深受這些技術(shù)和框架影響。
關(guān)于作者:吳茂貴,BI和大數(shù)據(jù)專家,就職于中國外匯交易中心,在BI、數(shù)據(jù)挖掘與分析、數(shù)據(jù)倉庫、機(jī)器學(xué)習(xí)等領(lǐng)域有超過20年的工作經(jīng)驗(yàn),在Spark機(jī)器學(xué)習(xí)、TensorFlow深度學(xué)習(xí)領(lǐng)域大量的實(shí)踐經(jīng)驗(yàn)。王冬,任職于博世(中國)投資有限公司,負(fù)責(zé)Bosch企業(yè)BI及工業(yè)4.0相關(guān)大數(shù)據(jù)和數(shù)據(jù)挖掘項(xiàng)目。對機(jī)器學(xué)習(xí)、人工智能有多年實(shí)踐經(jīng)驗(yàn)。李濤,參與過多個(gè)人工智能項(xiàng)目,如研究開發(fā)服務(wù)機(jī)器人、無人售后店等項(xiàng)目。熟悉python、caffe、TensorFlow等,對深度學(xué)習(xí)、尤其對計(jì)算機(jī)視覺方面有較深理解。楊本法,高級算法工程師,在機(jī)器學(xué)習(xí)、文本挖掘、可視化等領(lǐng)域有多年實(shí)踐經(jīng)驗(yàn)。熟悉Hadoop、Spark生態(tài)圈的相關(guān)技術(shù),對Python有豐富的實(shí)戰(zhàn)經(jīng)驗(yàn)。
本文摘編自《Python深度學(xué)習(xí):基于TensorFlow》,經(jīng)出版方授權(quán)發(fā)布。
延伸閱讀《Python深度學(xué)習(xí):基于TensorFlow》
推薦語:從Python和數(shù)學(xué),到機(jī)器學(xué)習(xí)和TensorFlow,再到深度學(xué)習(xí)的應(yīng)用和擴(kuò)展,為深度學(xué)習(xí)提供全棧解決方案。