原文地址:http://deeplearning.net/software/theano/tutorial/examples.html
在这里,自己系统地熟悉Theano的功能对象和操作是个更明智的选择,详情请看:Basic Tensor Functionality(内容实在太多了,我只翻译了里面的一小部分)
在这里有一个比把两个数相加更详细,更直观的例子。如果我们想计算一个logistic函数,如下表示:
一个logistic函数的图,x在x轴,s(x)在y轴
你想在一个doubles矩阵上元素依次(elementwise)计算,意味着你想把这个函数应用到矩阵的每个独立的元素上。
好,你所做的就是:
import theano import theano.tensor as T x = T.dmatrix() s = 1/(1 + T.exp(-x)) logistic = theano.function([x], s) logistic([[0,1], [-1,-2]])logistic依次执行的原因是因为所有它的运算-除法,加法,指数运算 - 是它们自己的元素依次运算。
另外一个例子:
我们可以通过相同的值来校验这个备用形式。
s2 = (1 + T.tanh(x / 2)) / 2 logistic2 = theano.function([x], s2) logistic2([[0, 1], [-1, -2]])Theano支持多输出的函数。例如,我们可以同时计算两个矩阵a和b元素依次的差,绝对值的差,平方差:
a, b = T.dmatrices('a', 'b') diff = a - b abs_diff = abs(diff) diff_squared = diff**2 f = theano.function([a, b], [diff, abs_diff, diff_squared])注意:dmatrices可以同时生成多个被你命名的输出。
当你使用函数 f 的时候,它会返回三个变量。
f([[1,1], [1,1]], [[0,1], [2,3]])当你想定义一个函数把两个数相加,除了你提供一个数外,另一个输入假设是1,你可以这样做:
from theano import In from theano import function x, y = T.dscalar('x', 'y') z = x + y f = function([x, In(y, value=1)], z) f(33) #array(34.0) f(33, 2) #array(35.0)这里使用了一个In类允许你为函数的参数指定更细的细节。这里我们用过In实例的vale域设定为1来定义y默认是1。
默认值的输入必须跟在没有默认值的输入后面(像python的函数)。这里可以设置不同输入的默认值。这些参数可以在位置上被设置或者通过name:
x, y, w = T.dscalar('x', 'y', 'w') z = (x + y) * w f = function([x, In(y, value=1), In(w, value=2, name='w_by_name)], z) f(33) #arrya(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)注意:In不知道传入的局部变量y和w参数的名字。符号变量类型有一个name的属性(在例子中通过dscalars设置)和这是在构建的方式中的关键参数。只是运行In(y, value=1)的机制。在In(w, value=2, name='w_by_name')中,我们重写符号变量的name。
用一个内在状态新建一个函数也是有可能的。例如,我们想实现一个累加器,初始状态为0,而每次调用函数,累加器的状态增加函数的参数。
from theano import shared state = shared(0) inc = T.iscalar('inc') accumulator = function([inc], state, updates=[(state, state+inc)])这段代码介绍了一些新概念,shared函数构建了共享变量。这是可以被不同函数共享的混合符号或者无符号变量。共享变量可以向其他返回dmatrices(...)的对象一样使用,但是他们也有一个被所有函数通过符号变量使用的被定义的内部值。被叫做共享变量的原因是以为他们被许多函数共享。这个值可以通过.get_value()和.set_value()方法来访问和修改。
另一个新的东西就是function的updates参数一定要被一个列表里面有元组对,类似(shared-variable, new expression)的形式提供。它也可以是一个字典,键是共享变量,键值是新表达式。这两种方法之一,意味着“不管什么时候函数被调用,每一个共享变量的.value都会被相应的新表达式代替”。综上,我们的累加器用总和和增量来代替了state的值。
让我们试一试:
state.get_value() #0 accumulator(1) #0 state.get_value() #1 accumulator(300) #1 state.get_value() #301我们也可以重新设置state。使用.set_value()方法:
state.set_value(-1) accumulator(3) #-1 state.get_value() #2我们可能想知道为什么updates机制会存在。我们可以完全可以通过返回一个新的表达式实现一个类似的结果,并且想平常在Numpy里实现的一样。updates机制在语法上是方便的,但是主要是为了效率。使用原地(in-place)算法(例如,低秩矩阵更新)更新共享变量有时候会更快。Theano更多地控制共享变量哪里和怎样被指定,一个重要的因素是为了在GPU上更好地运行。
你可能使用共享变量来表示一些公式,但是你不想使用它的值。这种情况你可以使用function的givens参数来用一个特定函数来代替图内的特定节点。
fn_of_state = state * 2 + inc # foo的类型一定要和我们要代替的共享变量匹配 foo = T.scalar(dtype=state.dtype) skip_shared = function([inc, foo], fn_of_state, givens=[(state, foo)]) skip_shared(1, 3) #我们给state定义3, 不是state.value #array(7) state.get_value() #0givens参数可以被用来代替任何一个符号变量,不仅仅是共享变量。你可以代替参数和表达式。但是要注意,不允许表达式被一个givens相互依赖的没有定义顺序的代替(substitution)导入,所以代替一定要以一定循序运行。
一般地,一个好的方法去理解givens机制就是允许你用不同的表达式去代替你公式里的任何部分来评估一个相同形状类型的张量。
注意:Theano共享变量的广播模式默认是每个维度False。共享变量的大小可以被改变,所以我们不可以使用它的形状来找到它的广播机制。如果你想要不同的模式,这里有一个参数:theano.shared(..., broadcastable=(True, False))
Theno的函数是可以被复制的,这对新建一个用不同共享变量(shared variable)或更新(updates)的类似函数是很有用的。这个通过function对象的copy()的方法来实现。优化图的初始函数是被复制的,所以汇编只需要被执行一次。
让我们从累加器的定义开始:
import theano import theano,tensor as T state = theano.shared(0) inc = T.iscalar('inc') accumulator = theano.function([inc], state, updates=[(state, state+inc)])我们及恶意使用它来增加state的状态:
accumulator(10) #0 state.get_value() #10我们可以使用copy()来新建一个类似的累加器,但是有它自己的内在状态使用swap参数,这是一个要交换共享变量的字典:
new_state = theano.shared(0) new_accumulator = accumulator.copy(swap={state:new_state}) new_accumulator(100) #0 new_state.get_value() #100第一个函数的state是不变的
state.get_value(0) #10我们现在新建一个使用delete_updates参数的删除updates复制,默认设置为false:
null_accumulator = accumulator.copy(delete_updates=True)相应的,共享变量不再被更新
null_accumlator(9000) #10 state.get_values() #10因为在Theano,我们首先使用符号变大所有东西,然后编译这些表达式来获得函数,在Numpy里使用伪随机数不是很直截了当,即使不是很复杂。
想在Theano的计算里使用随机数的方法就是把随机变量放在你的图中。Theano会分配一个Numpy RandomStream对象(一个随机数生成器)给每一个这样的变量,并且必要时从这里获得。我们称这些随机数结果的排序叫做random stream。Random streams在他们核心的共享变量,所以对共享变量的观察也在这里。Theano的随机对象在RandomStreams,RandomStreamBase里被定义和实现。
这里有一个简明的例子,代码如下:
from theano.tensor.shared_randomstreams import RandomStreams from theano import function srng = RandomStreams(seed=234) rv_u = srng.uniform((2, 2)) rv_v = srng.nromal((2, 2)) f = function([], rv_u) g = function([], rv_n, no_default_updates=True) #不更新rv_n.rng nearly_zeros = function([], rv_u + rv_u - 2 * rv_u)这里,’rv_u’代表一个2*2均匀分布的随机流矩阵。类似的,’rv_n’代表一个2*2的正态分布的随即流矩阵。分布在RandomStreams内被定义和实现,再低一级在raw_random。他们只工作在CPU,可以看看它在GPU的实现
f_val0 = f() f_val1 = f() #不同于f_val0的数当我们使用function的no_default_updates=True时,随机数生成器的状态不会因为调用返回函数的不同而不同。所以下面的数十相同的。
g_val0 = g() #不同于f_val0和f_val1的数 g_val1 = g() #和g_val0相同一个重要的注意点就是,随机变量在任何单个函数内只被执行一次,所以nearly_zero保证返回一个大约为0的数即使rv_u在函数里出现三次。
nearly_zeros = function([], rv_u + rv_u - 2 * rv_u)随机变量可以被单独或者相关地seed。
你可以使用看一个seeding随机变量或者使用.rng.set_value()来设置.rng属性。
rng_val = rv_u.get_value(borrow=True) rng_val.seed(89234) rv_u.rng.set_value(rng_val, borrow=True)你也可以seed所有的随机变量通过对象的seed方法分配给RandomStreams对象。这个方法会被用来生成一个临时的随机数生成器,会改变每个随机变量的生成seeds。
srng.seed(902340) # rv_u和rv_n各自会不同对于共享变量,用于随机变量的随机数生成器可以不同函数共用。所以nearly_zeros函数会更新被上面f函数使用的生成器状态。
例如:
state_after_v0 = rv_u.rng.get_value().get_state() nearly_zero() #这会影响rv_u生成器 #[[0,0],[0,0]] 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()在一些情况下,一个用户可能想要转换给定theano图的所有随机数生成器的状态(例如在下面,g1被f1编译)到第一个图(例如,f2的g2)。对于theano.tensor.shared_randomstreams.RandomStreams和theano.sandbox.rng_mrg.MRG_RandomStreams,就可以通过赋值元素的state_updates的参数来实现。
一个展示random_state怎样从一个theano函数到另一个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) #默认地,两个函数不是同步 f1() # [0.72803009] f2() # [0.55056769] 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(su[0].get_values()) copy_random_state(g1, g2) f1() f2()其他分布
这里有另外两个实现基于MRG31k3p和DURAND。随机流只会工作在CPU,而MRG31k3p工作在CPU和GPU,CURAND只工作在GPU
为了方便使用MRG,你可以只改变import: from theano.sandbox.rng_mrg import MRG_RandomStreams as RandStreams