[转] 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)])
# 注意,这里是先输出state,再调用updates

上述代码引入了一些新概念. 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)])# 把foo的值暂时传递到state中,计算函数,state的值不改变
>>> 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 使用的生成器的状态.

举个栗子:

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

 

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函数传递到另一个的例子:

 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()

 

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(对数回归)

前述的所有要素在这个实例中都应用到了.

 1 # 实例:Logistic回归分类器
 2 
 3 import numpy
 4 import theano
 5 import theano.tensor as T
 6 
 7 rng = numpy.random
 8 
 9 N = 400         # 样本个数
10 feats = 784     # 样本维数(特征数)
11 D = (rng.randn(N, feats), rng.randint(size=N, low=0, high=2))   # D[0]为随机784维数据,D[1]为随机label[0,1]
12 training_steps = 10000
13 # 定义Theano符号变量
14 x = T.matrix("x")
15 y = T.vector("y")
16 # w,b为分界直线的参数,由于需要使用梯度下降法迭代,在定义标号时设置为share
17 w = theano.shared(rng.randn(feats), name="w")   # w为标准正态分布的随机数
18 b = theano.shared(0., name="b")
19 print "Initial model:"
20 # print w.get_value(), b.get_value()
21 
22 # 构造theano的表达式结构图
23 p_1 = 1 / (1 + T.exp(-T.dot(x, w) - b))         # 概率估计函数
24 prediction = p_1 > 0.5                          # 预测阈值
25 xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1)   # logistic的代价函数(交叉熵)
26 cost = xent.mean() + 0.01 * (w ** 2).sum()      # softmax回归的代价函数 见UFLDL教程
27 # 第一项是规则化项,第二项是权重衰减项(避免w过大)
28 gw, gb = T.grad(cost, [w, b])                   # Theano自带梯度计算函数 
29                                                 # (we shall return to this in a
30                                                 # following section of this tutorial)
31 
32 # 注意:theano代码结构的特点
33 # Compile 将上面各公式的结构图联接在一起
34 train = theano.function(
35           inputs=[x,y],
36           outputs=[prediction, xent],
37           updates=((w, w - 0.1 * gw), (b, b - 0.1 * gb)))   # 梯度下降法,α=0.1   
38 predict = theano.function(inputs=[x], outputs=prediction)
39 
40 # Train
41 for i in range(training_steps):
42     pred, err = train(D[0], D[1])
43 
44 print "Final model:"
45 # print w.get_value(), b.get_value()
46 print "target values for D:", D[1]
47 print "prediction on D:", predict(D[0])
48 err = numpy.sum(D[1]-predict(D[0]))/N
49 print err

 

 

posted on 2014-11-29 20:53  hanahimi  阅读(241)  评论(0)    收藏  举报

导航