Theano 0.6 文檔[3] - 更多例子

本文爲Lancelod_Liu編譯, 轉載請保留此行.

This Article is translated and edited by Lancelod_Liu, keep this line if you share it.

原文: .

More Examples

更系統地熟悉Theano的基本對象和操作是非常有幫助的. 參考:Basic Tensor Functionality.

隨着教程展開, 你也應該逐漸地熟悉其他相關庫和相關的文檔.

Logistic Function(對數函數)

下面是一個更直接的例子, 儘管比兩數相加要複雜一些. 假設你想要計算對數曲線:

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

../_images/logistic.png

對數函數圖像

如果想要計算雙精度浮點矩陣中每個值(elementwise)的對數值.

可以這樣操作:

>>> x = T.dmatrix('x')
>>> s = 1 / (1 + T.exp(-x))
>>> logistic = function([x], s)
>>> logistic([[0, 1], [-1, -2]])
array([[ 0.5       ,  0.73105858],
       [ 0.26894142,  0.11920292]])

對數能夠按元素進行演算(elementwise)的原因是所有的操作--除, 加, 對數本身就是elementwise的操作.

下面這個例子是另一種公式表達:

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

可以看到結果是一致的:

>>> s2 = (1 + T.tanh(x / 2)) / 2
>>> logistic2 = function([x], s2)
>>> logistic2([[0, 1], [-1, -2]])
array([[ 0.5       ,  0.73105858],
       [ 0.26894142,  0.11920292]])

Computing More than one Thing at the Same Time(事半功倍)

Theano支持多輸出的函數. 舉例來說, 我們可以按元素同時計算兩個矩陣a和b差, 差絕對值 和平方差:

>>> a, b = T.dmatrices('a', 'b')
>>> diff = a - b
>>> abs_diff = abs(diff)
>>> diff_squared = diff**2
>>> f = function([a, b], [diff, abs_diff, diff_squared])

注意

dmatrices 生成個數等於輸入字符串個數的矩陣. 這是一種分配符號變量的快捷方式.

當我們使用函數時, 它返回三個變量.(the printing was reformatted for readability):

>>> f([[1, 1], [1, 1]], [[0, 1], [2, 3]])
[array([[ 1.,  0.],
        [-1., -2.]]),
 array([[ 1.,  0.],
        [ 1.,  2.]]),
 array([[ 1.,  0.],
        [ 1.,  4.]])]

Setting a Default Value for an Argument(初始狀態)

如果你要定義一個方程來計算兩個數的和, 當第二個數沒有給定時, 你希望第二個數默認值爲1. 可以如下操作:

>>> from theano import Param
>>> x, y = T.dscalars('x', 'y')
>>> z = x + y
>>> f = function([x, Param(y, default=1)], z)
>>> f(33)
array(34.0)
>>> f(33, 2)
array(35.0)

使用Param 類來指定一些你函數參數的特性. 此處我們通過構造一個Param對象, 並將其default域設置爲1來給y指定默認值1 .

帶默認值的輸入必須位於無默認值的輸入後面(和Python的函數要求一樣). 可以有多個輸入都有默認值, 這些參數可以指定位置或者名字來設置, 和Python標準相同:

>>> x, y, w = T.dscalars('x', 'y', 'w')
>>> z = (x + y) * w
>>> f = function([x, Param(y, default=1), Param(w, default=2, name='w_by_name')], z)
>>> f(33)
array(68.0)
>>> f(33, 2)
array(70.0)
>>> f(33, 0, 1)
array(33.0)
>>> f(33, w_by_name=1)
array(34.0)
>>> f(33, w_by_name=1, y=0)
array(33.0)

注意

Param 並不通過局部變量y和w的名字來設置值. 符號變量對象有name的屬性(attribute)(上例中使用dscalars設置的), 這些是我們創建的函數的關鍵字參數的名字.  這是Param(y,default=1)的工作原理. 在Param(w,default=2,name='w_by_name')中, 我們覆寫了符號變量的name屬性, 可以在這個函數中使用它.

更多細節: 參考這裏 Function.

Using Shared Variables(共享變量)

使用一個內部狀態來構建一個函數是可能的. 舉個栗子, 如果我們想要構造一個累加器: 一開始, 狀態是0, 隨後每次調用函數, 狀態都累加參數.

首先定義累加器函數accumulator. 它把參數加到它的內部狀態中, 隨後返回舊狀態.

>>> from theano import shared
>>> state = shared(0)
>>> inc = T.iscalar('inc')
>>> accumulator = function([inc], state, updates=[(state, state+inc)])

上述代碼引入了一些新概念. shared 函數構造了所謂的shared variables. 它們既是符號也不是符號變量, 即它們的值可以再多個函數中共享(是不是有點像C語言的全局靜態變量). 共享變量可以用在符號表達式中, 就和dmatrices(...) 返回的對象相同. 但它們也有一個內部值定義了在所有使用它的函數中的值.這就是shared 變量因爲它的值在許多函數中共享. 可以通過.get_value().set_value()獲取和修改變量的值. 之後會繼續討論.

上述代碼中的另一件新寶貝就是updates 參數.function.updates 接收一個成對的列表(shared-variable, new expression). 它可以是一個字典, 其中的keys 是 shared-variables 而 values 是new expressions(新值). 無論哪種, 它都表示"只要函數被調用, 它就會按照表達式的值代替每一個共享變量的值.". 所以, 我們的累加器每次使用輸入與舊值的和來替換state‘的值.

下面是一些例子:

>>> state.get_value()
array(0)
>>> accumulator(1)
array(0)
>>> state.get_value()
array(1)
>>> accumulator(300)
array(1)
>>> state.get_value()
array(301)

重新設置值也是可以的.  只需要使用.set_value():

>>> state.set_value(-1)
>>> accumulator(3)
array(-1)
>>> state.get_value()
array(2)

和我們上面提到的一樣, 你可以定義不止一個函數來使用同一個共享變量. 這些函數都可以更新這個變量的值.

>>> decrementor = function([inc], state, updates=[(state, state-inc)])
>>> decrementor(2)
array(2)
>>> state.get_value()
array(0)

你可能會疑惑爲什麼需要updates這個機制. 其實你的確可以通過返回新的表達式來實現類似的功能. 然而updates機制卻可以使得代碼非常的"優雅"和高效. 更新共享變量有時可以通過即時算法快速實現(如低階矩陣更新). 同時, Theano對於共享變量的定位和存儲有更多的控制, 使得GPU的性能得到大幅優化.

你可能想要使用一個共享變量來表示一些公式, 同時又不需要它的值. 在這種情況下, 你可以使用givens參數來代替圖中一個指定的節點實現指定的函數.

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

givens 參數可以用來替換任何一個符號變量(並非只有共享變量). 你可以替換常數/表達式. 但請小心, 經由givens替換的表達式不能存在相互依賴的關係. 替換的順序是未定義的, 所以替換必須可以以任何順序進行.

實際應用中, 你可以把givens看做一個允許你替換表達式中任何部分的機制, 並且用於替換的可以是一個用於計算同樣形狀/類型tensor(張量)的表達式.

(In practice, a good way of thinking about the givens is as a mechanism that allows you to replace any part of your formula with a different expression that evaluates to a tensor of same shape and dtype.)

Using Random Numbers(隨機的魅力)

因爲Theano使用符號來表達每件事, 隨後編譯這個表達式來得到函數, 使用僞隨機數並不和在NumPy中一樣簡單, 儘管也並不複雜.

考慮在Theano的計算中引入隨機性就是在你的圖中放置隨機變量. Theano 會對每一個隨機變量分配一個NumPyRandomStream(一個隨機數生成器), 在需要的時候從中取數. 我們稱這種隨機數序列爲一個隨機流(random stream). 隨機流基於它們的核心共享變量, 所以共享變量的使用方法在這裏也通用. Theanos的隨機對象通過RandomStreams 實現和調用, 更底層的則是在 RandomStreamsBase.

Brief Example(簡單例子)

基本代碼:

from theano.tensor.shared_randomstreams import RandomStreams
from theano import function
srng = RandomStreams(seed=234)
rv_u = srng.uniform((2,2))
rv_n = srng.normal((2,2))
f = function([], rv_u)
g = function([], rv_n, no_default_updates=True)    #Not updating rv_n.rng
nearly_zeros = function([], rv_u + rv_u - 2 * rv_u)

這裏的‘rv_u’ 代表一個2x2的基於平均分佈的隨機流矩陣. 同樣的 ‘rv_n’ 代表一個2x2的基於正態分佈的隨機流矩陣. 分佈的實現在RandomStreams 更底層的則在raw_random.

現在來使用這些對象吧. 如果我們調用f(), 我們就可以獲得平均分佈的數(4個). 隨機數生成器的內部狀態會自動更新, 所以每次調用的隨機數都不一樣.

>>> f_val0 = f()
>>> f_val1 = f()  #different numbers from f_val0

當我們加入額外的參數 no_default_updates=True (比如g), 隨機數生成器的狀態就不再變化了. 所以每次調用的隨機數都是一樣的.

>>> g_val0 = g()  # different numbers from f_val0 and f_val1
>>> g_val1 = g()  # same numbers as g_val0!

需要注意的是, 一個隨機變量在任何單個函數執行過程(single function execution)中最多抽取(draw)一次. 所以nearly_zeros function 可以保證每次都返回 0 的近似值(除了舍入誤差), 儘管rv_u隨機變量看似調用了3次.

>>> nearly_zeros = function([], rv_u + rv_u - 2 * rv_u)

Seeding Streams(種子流)

隨機變量可以獨立或整體設立種子.

你可以通過設置.rng屬性來設置一個隨機變量的種子, 只需要使用:.rng.set_value().

>>> rng_val = rv_u.rng.get_value(borrow=True)   # 獲取rv_u的rng值
>>> rng_val.seed(89234)                         # 設置種子
>>> rv_u.rng.set_value(rng_val, borrow=True)    # 重新設置rng的值

可以使用對象的seed方法同時設置所有使用RandomStreams分配的隨機變量. 這個種子會被用來設置一個臨時隨機數生成器, 使用它來生成每個隨機變量的種子.

>>> srng.seed(902340)  # seeds rv_u and rv_n with different seeds each

Sharing Streams Between Functions(函數間共享流)

和一般的共享變量一樣, 用於隨機變量的隨機數生成器在函數間通用. 所以我們的nearly_zeros 函數會更新上面的 f 使用的生成器的狀態.

舉個栗子:

>>> state_after_v0 = rv_u.rng.get_value().get_state()
>>> nearly_zeros()       # this affects rv_u's generator
>>> v1 = f()
>>> rng = rv_u.rng.get_value(borrow=True)
>>> rng.set_state(state_after_v0)
>>> rv_u.rng.set_value(rng, borrow=True)
>>> v2 = f()             # v2 != v1
>>> v3 = f()             # v3 == v1

Copying Random State Between Theano Graphs(隨機狀態複製)

在一些例子中, 用戶可能希望把所有和theano圖(比如g1)相關的隨機數生成器的狀態傳遞到一個第二個圖(如g2). 如果你想要基於一個先前模型的參數來初始化一個新模型的狀態, 你就會碰到這個問題. 對於theano.tensor.shared_randomstreams.RandomStreams 以及theano.sandbox.rng_mrg.MRG_RandomStreams, 可以通過複製state_updates 參數的成員來解決這個問題.

每次從一個隨機流對象中抽取一個隨機變量時, 一個tuple就被加到state_updates 列表中. 第一個元素是一個共享變量, 代表着和指定變量相關的隨機數生成器的狀態, 第二個元素代表着和隨機數生成過程相關的theano圖(比如 RandomFunction{uniform}.0).

下面是一個隨機狀態如何從一個theano函數傳遞到另一個的例子:

import theano
import numpy
import theano.tensor as T
from theano.sandbox.rng_mrg import MRG_RandomStreams
from theano.tensor.shared_randomstreams import RandomStreams

class Graph():
    def __init__(self, seed=123):
        self.rng = RandomStreams(seed)
        self.y = self.rng.uniform(size=(1,))

g1 = Graph(seed=123)
f1 = theano.function([], g1.y)

g2 = Graph(seed=987)
f2 = theano.function([], g2.y)

print 'By default, the two functions are out of sync.'
print 'f1() returns ', f1()
print 'f2() returns ', f2()

def copy_random_state(g1, g2):
    if isinstance(g1.rng, MRG_RandomStreams):
        g2.rng.rstate = g1.rng.rstate
    for (su1, su2) in zip(g1.rng.state_updates, g2.rng.state_updates):
        su2[0].set_value(su1[0].get_value())

print 'We now copy the state of the theano random number generators.'
copy_random_state(g1, g2)
print 'f1() returns ', f1()
print 'f2() returns ', f2()

This gives the following output:

# By default, the two functions are out of sync.
f1() returns  [ 0.72803009]
f2() returns  [ 0.55056769]
# We now copy the state of the theano random number generators.
f1() returns  [ 0.59044123]
f2() returns  [ 0.59044123]

Other Random Distributions(其他分佈)

其他的分佈實現: other distributions implemented.

Other Implementations(其他實現)

這裏有2個不同的實現, 分別基於 CURAND andMRG31k3p

A Real Example: Logistic Regression(對數迴歸)

前述的所有要素在這個實例中都應用到了.

import numpy
import theano
import theano.tensor as T
rng = numpy.random

N = 400
feats = 784
D = (rng.randn(N, feats), rng.randint(size=N, low=0, high=2))
training_steps = 10000

# Declare Theano symbolic variables
x = T.matrix("x")
y = T.vector("y")
w = theano.shared(rng.randn(feats), name="w")
b = theano.shared(0., name="b")
print "Initial model:"
print w.get_value(), b.get_value()

# Construct Theano expression graph
p_1 = 1 / (1 + T.exp(-T.dot(x, w) - b))   # Probability that target = 1
prediction = p_1 > 0.5                    # The prediction thresholded
xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1) # Cross-entropy loss function
cost = xent.mean() + 0.01 * (w ** 2).sum()# The cost to minimize
gw, gb = T.grad(cost, [w, b])             # Compute the gradient of the cost
                                          # (we shall return to this in a
                                          # following section of this tutorial)

# Compile
train = theano.function(
          inputs=[x,y],
          outputs=[prediction, xent],
          updates=((w, w - 0.1 * gw), (b, b - 0.1 * gb)))
predict = theano.function(inputs=[x], outputs=prediction)

# Train
for i in range(training_steps):
    pred, err = train(D[0], D[1])

print "Final model:"
print w.get_value(), b.get_value()
print "target values for D:", D[1]
print "prediction on D:", predict(D[0])
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章