Theano2.1.3-基礎知識之更多的例子


來自:http://deeplearning.net/software/theano/tutorial/examples.html

More Examples

    現在,是時候開始系統的熟悉theano的基礎對象和操作了,可以通過瀏覽庫的部分來詳細的了解 Basic Tensor Functionality. 隨着這個教程的深入,你可以逐漸的讓自己熟悉庫的其他相關的部分和文檔入口頁面的其他相關的主題了。 

一、Logistic 函數

     這是一個簡單的例子,雖然會比兩個數值相加要難一些。假設你想要計算一個邏輯曲線,首先得到一個如下的式子:

s(x) = \frac{1}{1 + e^{-x}}

                                                                               ../_images/logistic.png

上圖是一個邏輯函數,x軸表示x值,y軸表示s(x)值。

你需要在doubles矩陣上逐元素( elementwise )的計算這個函數,也就是說你是想要在矩陣的每個獨立的元素上都使用該函數。代碼如下:

[python]  view plain copy
 
  1. >>> x = T.dmatrix('x')  
  2. >>> s = 1 / (1 + T.exp(-x))  
  3. >>> logistic = function([x], s)  
  4. >>> logistic([[0, 1], [-1, -2]])  
  5. array([[ 0.5       ,  0.73105858],  
  6.        [ 0.26894142,  0.11920292]])  

 

    需要逐元素計算是因為它的操作:除法、加法、指數和減法,都是逐元素的操作。在該情況下也是:

s(x) = \frac{1}{1 + e^{-x}} = \frac{1 + \tanh(x/2)}{2}

我們可以驗證從這個可代替的式子上得到的結果是一樣的:

[python]  view plain copy
 
  1. >>> s2 = (1 + T.tanh(x / 2)) / 2  
  2. >>> logistic2 = function([x], s2)  
  3. >>> logistic2([[0, 1], [-1, -2]])  
  4. array([[ 0.5       ,  0.73105858],  
  5.        [ 0.26894142,  0.11920292]])  

二、在同一時間對多個操作進行計算

    Theano支持函數有着多於一個的輸出。例如,我們可以在同一時刻計算兩個矩陣a和b 之間的逐元素(elementwise )的差,絕對值的差,平方值的差:

[python]  view plain copy
 
  1. >>> a, b = T.dmatrices('a', 'b')  
  2. >>> diff = a - b  
  3. >>> abs_diff = abs(diff)  
  4. >>> diff_squared = diff**2  
  5. >>> f = function([a, b], [diff, abs_diff, diff_squared])  

note:dmatrices 生成提供的名字一樣數量的輸出。這是一個用來分配符號變量的快捷方式,在本教程中會經常用到。

    當我們使用函數f 時,它返回三個變量(輸出的時候會為了更好的可讀性而被重新格式):

[python]  view plain copy
 
  1. >>> f([[1, 1], [1, 1]], [[0, 1], [2, 3]])  
  2. [array([[ 1.,  0.],  
  3.         [-1., -2.]]),  
  4.  array([[ 1.,  0.],  
  5.         [ 1.,  2.]]),  
  6.  array([[ 1.,  0.],  
  7.         [ 1.,  4.]])]  

 

三、對參數設置默認值

    假設你想要定義一個相加兩個數的函數,如果你定義完之后,在調用的時候,只提供了一個參數,那么另一個輸入可以假設默認為1,可以如下所示:

[python]  view plain copy
 
  1. >>> from theano import Param  
  2. >>> x, y = T.dscalars('x', 'y')  
  3. >>> z = x + y  
  4. >>> f = function([x, Param(y, default=1)], z)  
  5. >>> f(33)  
  6. array(34.0)  
  7. >>> f(33, 2)  
  8. array(35.0)  

    使用的 Param 參數允許你指定你函數的參數有着更詳細的值。這里我們通過創建一個Param實例來將y設置其默認值為1。

    有着默認值的輸入必須在沒有默認值的輸入的后面(和python的函數一樣的順序)可以對多個輸入進行設置莫仍只。這些蠶食可以通過位置或者名稱來進行設定,就像標准的python中一樣:

[python]  view plain copy
 
  1. >>> x, y, w = T.dscalars('x', 'y', 'w')  
  2. >>> z = (x + y) * w  
  3. >>> f = function([x, Param(y, default=1), Param(w, default=2, name='w_by_name')], z)  
  4. >>> f(33)  
  5. array(68.0)  
  6. >>> f(33, 2)  
  7. array(70.0)  
  8. >>> f(33, 0, 1)  
  9. array(33.0)  
  10. >>> f(33, w_by_name=1)  
  11. array(34.0)  
  12. >>> f(33, w_by_name=1, y=0)  
  13. array(33.0)  


note:Param 不知道作為參數傳入的局部變量y 和w 的名稱。這些符號變量對象都有name屬性(和上面例子一樣通過dscalars來設置),這些是我們構建的函數中的關鍵參數的名稱。這就是 Param(y, default=1)中的工作機制。在Param(w, default=2, name='w_by_name')的情況下 ,我們用在這個函數中使用過的名字來覆蓋符號變量的名字屬性。
    你可以看看庫中的 Function 來更詳細的了解。

四、使用共享變量

      同樣的也可以讓函數有一個內部狀態。例如,我們想要在開始就設置一個累加器,先初始化為0。那么,在每次的函數調用, 該狀態就會被函數的參數遞增的。首先定義一個accumulator 函數。然后將參數增加到內部狀態上,然后返回增加之前的狀態值。

>>> from theano import shared
>>> state = shared(0)
>>> inc = T.iscalar('inc')
>>> accumulator = function([inc], state, updates=[(state, state+inc)])
    該代碼引入了一些新的概念。 shared 函數構建所謂的 shared variables。這些都是混合符號和非符號變量,他們的值可以在多個函數中共享,就像是由dmatrices(...)返回的對象一樣,不過他們同樣有着一個內部值,這個值是通過這個在所有函數中使用的符號變量定義的。被稱作共享變量是因為它的值在許多函數之間共享的。該值可以被 .get_value() 和 .set_value() 方法所訪問和修改。

    該代碼中另一個新事物就是functionupdates的參數 updates 必須被以(shared-variable, new expression)這種對形式的列表所賦值。它同樣可以是一個字典,其中的鍵是共享變量而值是新表達式。。不管怎么說,它表示“不論什么時候運行,它會將.value的每個共享變量替換成對應的表達式的結果” 。也就是說,我們的累加器會用狀態state的和以及遞增數來替換狀態state的值。 

[python]  view plain copy
 
  1. >>> state.get_value()  
  2. array(0)  
  3. >>> accumulator(1)  
  4. array(0)  
  5. >>> state.get_value()  
  6. array(1)  
  7. >>> accumulator(300)  
  8. array(1)  
  9. >>> state.get_value()  
  10. array(301)  

可以使用 .set_value() 方法來重置狀態state:

 

[python]  view plain copy
 
  1. >>> state.set_value(-1)  
  2. >>> accumulator(3)  
  3. array(-1)  
  4. >>> state.get_value()  
  5. array(2)  

    正如上面說的,你可以定義超過一個函數來使用相同的共享變量。這些函數都能夠更新這個值。

 

[python]  view plain copy
 
  1. >>> decrementor = function([inc], state, updates=[(state, state-inc)])  
  2. >>> decrementor(2)  
  3. array(2)  
  4. >>> state.get_value()  
  5. array(0)  

    你也許會驚訝為什么這個更新機制會存在。你總可以通過返回一個新的表達式來得到一個相似的結果,然后在NumPy里面使用它們。該更新機制是一個語法上的方便,不過在這里主要是因為效率問題。對共享變量的更新有時候可以使用in-place算法更快的完成(了例如: low-rank矩陣更新).。同樣的,theano有着更多有關在哪和如何來分配共享權重的函數,這些都是在需要使用在 GPU上很重要的組成部分.

  有時候你想要使用一個共享變量來表示一些公式,卻不想要使用它們的值。在這種情況下,你可以使用function函數的givens的參數,這個用來代替這種情況下graph中的特定的節點。

[python]  view plain copy
 
  1. >>> fn_of_state = state * 2 + inc  
  2. >>> # The type of foo must match the shared variable we are replacing  
  3. >>> # with the ``givens``  
  4. >>> foo = T.scalar(dtype=state.dtype)  
  5. >>> skip_shared = function([inc, foo], fn_of_state,  
  6.                            givens=[(state, foo)])  
  7. >>> skip_shared(1, 3)  # we're using 3 for the state, not state.value  
  8. array(7)  
  9. >>> state.get_value()  # old state still there, but we didn't use it  
  10. array(0)  

     givens 參數可以用來代替任何符號變量,不只是共享變量。你還可以用來代替常量、表達式。不過要注意,不要讓由givens替換的表達式之間有着相互依賴關系,替換的順序是沒法保證的,所以替換之后是有可能以任意順序來執行的。

    在實際中,有關使用givens的一個好的方法就是替換公式的任何部分的時候使用的是不同的表達式,只不過該表達式有着相同shape和dtype的張量而已。

note:Theano 共享變量的廣播模式默認情況下對於每個維度來說都是False。共享變量的size可以隨着時間變化,所以我們沒法使用shape來找到可廣播的模式。如果你想要一個不同的模式,只要將它像參數一樣傳遞 theano.shared(..., broadcastable=(True, False))。

五、使用隨機數

      因為在theano中,你首先會將任何事情進行符號化,然后編譯這個表達式來得到函數,然后使用偽隨機數不是和Numpy中一樣簡單的,當然也不會太復雜。

    將隨機放入theano的計算中就是將隨機變量放到你的graph中。theano將會對每個這樣的變量分配一個 NumPy RandomStream 對象 (一個隨機生成器) ,然后必要的時候提取出來。我們稱這類隨機數序列為a random stream. 隨機流的核心也是共享變量,所以對共享變量的觀察在這里也是一樣的。theano的隨機對象的定義和實現在 RandomStreams 更低的版本,也就是其父類RandomStreamsBase.

5.1 一個簡單的例子

    這里是一個簡短的例子,構建代碼為:

[python]  view plain copy
 
  1. from theano.tensor.shared_randomstreams import RandomStreams  
  2. from theano import function  
  3. srng = RandomStreams(seed=234)  
  4. rv_u = srng.uniform((2,2))  
  5. rv_n = srng.normal((2,2))  
  6. f = function([], rv_u)  
  7. g = function([], rv_n, no_default_updates=True)    #Not updating rv_n.rng  
  8. nearly_zeros = function([], rv_u + rv_u - 2 * rv_u)  

    這里 ‘rv_u’ 表示一個從均勻分布中提取的2*2的矩陣的隨機流。同樣的, ‘rv_n’ 表示一個從正太分布中提取的2*2矩陣的一個隨機流。該分布實現和定義都在 RandomStreams 中,其父類為 raw_random. 他們只在CPU上工作。 Other Implementations 上面有GPU版本的。

    現在讓我們來使用這些對象。如果我們調用 f(),我們就得到了均勻隨機數。 隨機數生成器的內部狀態是自動進行更新的,所以我們在每個時間上得到的是不同的隨機數:

[python]  view plain copy
 
  1. >>> f_val0 = f()  
  2. >>> f_val1 = f()  #different numbers from f_val0  


    當我們增加額外的參數 no_default_updates=True 到 function (as in g),那么隨機數生成器狀態不會受到返回函數調用的影響。所以,例如,調用g 函數多次,而返回的是同樣的數值:

 

[python]  view plain copy
 
  1. >>> g_val0 = g()  # different numbers from f_val0 and f_val1  
  2. >>> g_val1 = g()  # same numbers as g_val0!  

    一個重要的備注是隨機變量在任何單一函數執行中最多被提取一次。所以,即使 rv_u 隨機變量在輸出表達式中出現了三次,nearly_zero函數可以保證返回的值可以逼近0 (除了舍入導致的錯誤):

[python]  view plain copy
 
  1. >>> nearly_zeros = function([], rv_u + rv_u - 2 * rv_u)  

 

5.2 Seeding Streams

 

    隨機變量可以被獨立或集體的被傳入種子。你可以只對隨機變量通過seeding或者使用.rng.set_value()的.rng 屬性來指定傳入種子:

[python]  view plain copy
 
  1. >>> rng_val = rv_u.rng.get_value(borrow=True)   # Get the rng for rv_u  
  2. >>> rng_val.seed(89234)                         # seeds the generator  
  3. >>> rv_u.rng.set_value(rng_val, borrow=True)    # Assign back seeded rng  

   你同樣可以對所有的隨機變量通過RandomStreams對象的seed方法來分配種子。該種子將會被用來傳遞給一個臨時隨機數生成器,然后對每個隨機變量生成種子。

[python]  view plain copy
 
  1. >>> srng.seed(902340)  # seeds rv_u and rv_n with different seeds each  

5.3 在函數之間共享流

    和通常的共享變量一樣,用於隨機變量的隨機數生成器都是函數中常見的。所以我們的nearly_zeros 函數將會使用上面介紹的函數 f 來更新生成器的狀態。例如:

[python]  view plain copy
 
  1. >>> state_after_v0 = rv_u.rng.get_value().get_state()  
  2. >>> nearly_zeros()       # this affects rv_u's generator  
  3. >>> v1 = f()  
  4. >>> rng = rv_u.rng.get_value(borrow=True)  
  5. >>> rng.set_state(state_after_v0)  
  6. >>> rv_u.rng.set_value(rng, borrow=True)  
  7. >>> v2 = f()             # v2 != v1  
  8. >>> v3 = f()             # v3 == v1  

5.4 在theano graphs之間復制隨機狀態

    在一些使用的情況中,用戶有可能想要將所有隨機數生成器的“state”從一個給定的theano graph (例如, g1,和下面編譯的函數f1一起的) 到另一個 graph (例如 g2,和函數f2一起的).。如果你想要從之前模型的pickled版本的參數中初始化模型的state,那么這個問題就會出現。 theano.tensor.shared_randomstreams.RandomStreams 和 theano.sandbox.rng_mrg.MRG_RandomStreams 可以通過復制state_updates參數的元素來獲得

    每次從一個RandomStreams對象中得到一個隨機變量,一個元組就會被添入到 state_updates 列表中。第一個元素就是一個共享變量,用來表示與這個具體的變量相關聯的隨機數生成器的狀態,第二個元素用來表示對應於隨機數生成過程(即RandomFunction{uniform}.0)的theano graph。

    一個關於如何“random states”可以從一個theano function遷移到另一個的例子如下:

[python]  view plain copy
 
  1. import theano  
  2. import numpy  
  3. import theano.tensor as T  
  4. from theano.sandbox.rng_mrg import MRG_RandomStreams  
  5. from theano.tensor.shared_randomstreams import RandomStreams  
  6.   
  7. class Graph():  
  8.     def __init__(self, seed=123):  
  9.         self.rng = RandomStreams(seed)  
  10.         self.y = self.rng.uniform(size=(1,))  
  11.   
  12. g1 = Graph(seed=123)  
  13. f1 = theano.function([], g1.y)  
  14.   
  15. g2 = Graph(seed=987)  
  16. f2 = theano.function([], g2.y)  
  17.   
  18. print 'By default, the two functions are out of sync.'  
  19. print 'f1() returns ', f1()  
  20. print 'f2() returns ', f2()  
  21.   
  22. def copy_random_state(g1, g2):  
  23.     if isinstance(g1.rng, MRG_RandomStreams):  
  24.         g2.rng.rstate = g1.rng.rstate  
  25.     for (su1, su2) in zip(g1.rng.state_updates, g2.rng.state_updates):  
  26.         su2[0].set_value(su1[0].get_value())  
  27.   
  28. print 'We now copy the state of the theano random number generators.'  
  29. copy_random_state(g1, g2)  
  30. print 'f1() returns ', f1()  
  31. print 'f2() returns ', f2()  

    會有如下的輸出:

 

[python]  view plain copy
 
  1. # By default, the two functions are out of sync.  
  2. f1() returns  [ 0.72803009]  
  3. f2() returns  [ 0.55056769]  
  4. # We now copy the state of the theano random number generators.  
  5. f1() returns  [ 0.59044123]  
  6. f2() returns  [ 0.59044123]  

 

5.5 其他隨機分布

      見 other distributions implemented.

5.6 其他實現

     這里有2個基於 CURAND 和 MRG31k3p.的兩個實現。該RandomStream 只工作在CPU上,而 MRG31k3p 既能在CPU 上也能在GPU上。CURAND 只工作在 GPU上。

5.7 一個真實的例子:邏輯回歸

      前面的內容會在下面這個更實際的例子中見到,而且該例子會被多次使用。

[python]  view plain copy
 
  1. import numpy  
  2. import theano  
  3. import theano.tensor as T  
  4. rng = numpy.random  
  5.   
  6. N = 400  
  7. feats = 784  
  8. D = (rng.randn(N, feats), rng.randint(size=N, low=0, high=2))  
  9. training_steps = 10000  
  10.   
  11. # Declare Theano symbolic variables聲明theano符號變量  
  12. x = T.matrix("x")  
  13. y = T.vector("y")  
  14. w = theano.shared(rng.randn(feats), name="w")  
  15. b = theano.shared(0., name="b")  
  16. print "Initial model:"  
  17. print w.get_value(), b.get_value()  
  18.   
  19. # Construct Theano expression graph構建theano表達式的圖  
  20. p_1 = 1 / (1 + T.exp(-T.dot(x, w) - b))   # Probability that target = 1  
  21. prediction = p_1 > 0.5                    # The prediction thresholded  
  22. xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1) # Cross-entropy loss function  
  23. cost = xent.mean() + 0.01 * (w ** 2).sum()# The cost to minimize  
  24. gw, gb = T.grad(cost, [w, b])             # Compute the gradient of the cost  
  25.                                           # (we shall return to this in a  
  26.                                           # following section of this tutorial)  
  27.   
  28. # Compile編譯  
  29. train = theano.function(  
  30.           inputs=[x,y],  
  31.           outputs=[prediction, xent],  
  32.           updates=((w, w - 0.1 * gw), (b, b - 0.1 * gb)))  
  33. predict = theano.function(inputs=[x], outputs=prediction)  
  34.   
  35. # Train訓練  
  36. for i in range(training_steps):  
  37.     pred, err = train(D[0], D[1])  
  38.   
  39. print "Final model:"  
  40. print w.get_value(), b.get_value()  
  41. print "target values for D:", D[1]  
  42. print "prediction on D:", predict(D[0])  


參考資料:

 

[1]官網:http://deeplearning.net/software/theano/tutorial/examples.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM