Theano - 更多的例子

Logistic函数

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)一直存在,但是我们使用它。

复制函数(copying functions)

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

使用随机数(Using Random Numbers)

简洁的例子

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

种子流(Seeding Streams)

随机变量可以单独也可以共同产生,你可以通过对.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)

函数之间共享流(Sharing Streams Between Functions)

像共享变量一样,随机变量使用的随机数生成器在不同函数之间是相同的。所以我们的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 Graphs之间复制随机状态

在很多应用场景中,使用者可能想把一个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]))

相关推荐