mrking00 2019-06-21
import theano import theano.tensor as T x = T.dmatrix('x') s = 1 / (1 + T.exp(-x)) logistic = theano.function([x], s) logistic([[0, 1], [-1, -2]]) # s(x) = 1/(1+exp(-x)) = (1+tanh(x/2))/2 s2 = (1 + T.tanh(x / 2)) / 2 logistic2 = theano.function([x], s2) logistic2([[0, 1], [-1, -2]])
Theano支持多种输出的函数。例如,我们可以同时计算两个矩阵a,b相应元素之间的差、绝对差、平方差。当我们调用函数f是,返回三个变量:
import theano import theano.tensor as T 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]) f([[1, 1], [1, 1]], [[0, 1], [2, 3]])
假设我们要定义一个实现两个数字加法的函数。如果你仅仅提供一个数字,另一个数字假设(默认)为1,就可以这么做:
from theano import In, function import theano.tensor as T x, y = T.dscalars('x', 'y') z = x + y f = function([x, In(y, value=1)], z) f(33) f(33, 2)
含有默认值的输入必须位于不含默认值的输入之后(和python的函数类似)。允许多个输入含有默认值,这些参数可以通过位置设定,也可以通过名字进行设定。
x, y, w = T.dscalars('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) f(33, 2) f(33, 0, 1) f(33, w_by_name=1) f(33, w_by_name=1, y=0)
In 不知道通过参数传递的局部变量x,y的名字。符号变量对象拥有名字(name)属性(在上本例中通过dscalars进行设置),这也是我们构建函数function关键字参数的名字。通过In(y, value=1)这一机制实现。在In(w, value=2, name='w_by_name')中,我们重写了符号变量的名字属性。所有当我们通过f(x=33, y=0, w=1)的形式调用函数时,就会出错。w应该改为w_by_name.
我们也可以构建一个含有内状态(internal state)的函数。例如,假设我们要构造一个累加函数(accumulator):初始状态设置为0。接着,每次调用函数,状态就会通过函数的参数自动增加。
# 首先,我们定义一个累加函数。它将自己的内状态加上它的参数,然后返回旧状态的值。 import theano import theano.tensor as T from theano import shared state = shared(0) inc = T.iscalar('inc') accumulator = function([inc], state, updates=[(state, state+inc)]) # state的值可以通过.get_value()和.set_value()惊行获取和修改 state.get_value() accumulator(1) state.get_value() accumulator(300) state.get_value() state.set_value(-1) accumulator(3) state.get_value() # 我们可以构造多个函数,使用相同共享变量,这些函数都可以更新状态的值 decrementor = function([inc], state, updates=[(state, state-inc)]) decrementor(2) state.get_value() # 可能你会使用一个共享变量表达多个公式,但是你并不想使用共享变量的值。 # 这种情况下,你可以使用function中的givens参数。 fn_of_state = state * 2 + inc foo = T.scalar(dtype=state.dtype) # foo的类型必须和将要通过givens取代的共享变量的类型保持一致 skip_shared = function([inc, foo], fn_of_state, givens=[(state, foo)]) skip_shared(1, 3) # 我们正在使用3作为state,并非state.value state.get_value() # 旧的状态(state)一直存在,但是我们使用它。
Theano中的函数可以被复制,被用于构造相似的函数(拥有不同的共享变量和更新),这可以通过function中的copy()实现。让我们从以上定义的累加函数(accumulator)开始:
import theano import theano.tensor as T state = theano.shared(0) inc = T.iscalar('inc') accumulator = function([inc], state, updates=[(state, state+inc)]) # 我们可以像平常一样增加它的状态(state) accumulator(10) state.get_value() # 我们可以用copy()创建一个相似的累加器(accumulator),但是可以通过swap参数拥有自己的内状态, # swap参数是将要交换的共享参数字典 new_state = theano.shared(0) new_accumulator = accumulator.copy(swap={state:new_state}) new_accumulator(100) new_state.get_value() state.get_value() # 现在我们创建一个复制,但是使用delete_updates参数移除更新,此时,默认为False # 此时,共享状态将不会再更新。 null_accumulator = accumulator.copy(delete_updates=True) null_accumulator(9000) state.get_value()
from theano.tensor.shared_randomstreams import RandomStreams from theano import function srng = RandomStreams(seed=324) rv_u = srng.uniform((2,2)) rv_n = srng.normal((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随机数矩阵 # 现在我们来调用这些对象。如果调用f(),我们将会得到随机均匀分布数。 # 随机数产生器的内状态将会自动更新,所以我们每次调用f()时将会得到不同的随机数 f_val0 = f() f_val1 = f() # 当我们添加额外的参数no_default_updates=True(在函数g中)后,随机数产生器的状态将不会受调用函数的影响。 # 例如:多次调用g()将会返回相同的随机数,g_val0和g_val1相同。 g_val0 = g() g_val1 = g() # 一个重要的观点是:一个随机变量在一次调用函数期中最多只能构建一次。 # 所以nearly_zeros函数保证了输出近似为0,尽管rv_u随机变量在输出表达式中出现了3次。 nearly_zeros()
随机变量可以单独也可以共同产生,你可以通过对.rng属性进行seeding或者使用.rng.set_value()对.rng进行赋值产生一个随机变量。
rng_val = rv_u.rng.get_value(borrow=True) # 获取rv_u的rng(随机数生成器) rng_val.seed(89234) # 对generator(生成器)进行seeds(播种) rv_u.rng.set_value(rng_val, borrow=True) # 对rng进行赋值 # 你可以seed由RandomStreams对象分配的所有随机变量。 srng.seed(902340)
像共享变量一样,随机变量使用的随机数生成器在不同函数之间是相同的。所以我们的nearly_zeros函数将会更新f函数使用的生成器的状态。例如:
state_after_v0 = rv_u.rng.get_value().get_state() nearly_zeros() # 这将会影响rv_u的生成器 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
在很多应用场景中,使用者可能想把一个theano graph(图:g1,内置函数:f1)中的所有随机数生成器的状态传递给第二个theano graph(图:g2,内置函数:f2)。
例如:如果你试图从之前储存模型的参数中,初始化一个模型的状态,将会出现上述需要。theano.tensor.shared_randomstreams.RandomStreams和theano.sandbox.rng_mrg.MRG_RandomStreams这些在state_updates参数的复制元素可以实现。
每一次从RandomStreams对象中生成一个随机变量,将会有一个元组添加到state_update列表中。 第一个元素是共享变量:它表示和特定变量相关的随机数生成器的状态。第二个元素表示和随机数生成过程相对应的theano graph。
下面的例子展示了:随机状态(random states)如何从一个theano function 传递给另一个theano function中的。
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,f2不同步 f1() 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()) # 现在我们赋值theano随机数生成器的状态 copy_random_state(g1, g2) f1() f2()
import numpy import theano import theano.tensor as T rng = numpy.random N = 400 # training sample size feats = 784 # number of input variables # generate a data set: D = (input_values, target_class) D = (rng.rand(N, feats), rng.randint(size=N, low=0, high=2)) training_steps = 10000 # Declare Theano symbolic variables x = T.dmatrix('x') y = T.dvector('y') # initialize the weight vector w randomly # # this and the following bias variable b # are shared so they keep their values # between training iterations (updates) w = theano.shared(rng.randn(feats), name='w') # initialize the bias term b = theano.shared(0., name='b') print('Initial model:') print(w.get_value()) print(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 # 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()) print(b.get_value()) print('target values for D:') print(D[1]) print('prediction on D:') print(predict(D[0]))