原文地址:http://deeplearning.net/software/theano/tutorial/examples.html
在这里,自己系统地熟悉Theano的功能对象和操作是个更明智的选择,详情请看:Basic Tensor Functionality(内容实在太多了,我只翻译了里面的一小部分)
Logistic Function 逻辑斯特函数
在这里有一个比把两个数相加更详细,更直观的例子。如果我们想计算一个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]])
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 = theano.function([a, b], [diff, abs_diff, diff_squared])
注意:dmatrices可以同时生成多个被你命名的输出。
当你使用函数 f 的时候,它会返回三个变量。
f([[1,1], [1,1]], [[0,1], [2,3]])
Setting a Default Value for an Argument 设置参数默认值
当你想定义一个函数把两个数相加,除了你提供一个数外,另一个输入假设是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。
Using Shared Variables 使用共享变量
用一个内在状态新建一个函数也是有可能的。例如,我们想实现一个累加器,初始状态为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()
#0
givens
参数可以被用来代替任何一个符号变量,不仅仅是共享变量。你可以代替参数和表达式。但是要注意,不允许表达式被一个givens
相互依赖的没有定义顺序的代替(substitution)导入,所以代替一定要以一定循序运行。
一般地,一个好的方法去理解givens
机制就是允许你用不同的表达式去代替你公式里的任何部分来评估一个相同形状类型的张量。
注意:Theano共享变量的广播模式默认是每个维度False。共享变量的大小可以被改变,所以我们不可以使用它的形状来找到它的广播机制。如果你想要不同的模式,这里有一个参数:theano.shared(..., broadcastable=(True, False))
Copying functions 复制函数
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
Using Random Numbers 使用随机数
因为在Theano,我们首先使用符号变大所有东西,然后编译这些表达式来获得函数,在Numpy里使用伪随机数不是很直截了当,即使不是很复杂。
想在Theano的计算里使用随机数的方法就是把随机变量放在你的图中。Theano会分配一个Numpy RandomStream对象(一个随机数生成器)给每一个这样的变量,并且必要时从这里获得。我们称这些随机数结果的排序叫做random stream。Random streams在他们核心的共享变量,所以对共享变量的观察也在这里。Theano的随机对象在RandomStreams,RandomStreamBase里被定义和实现。
Brief Example 简明的例子
这里有一个简明的例子,代码如下:
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)
Seeding Streams 播撒流
随机变量可以被单独或者相关地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各自会不同
Sharing Streams Between Functions 函数间的共享流
对于共享变量,用于随机变量的随机数生成器可以不同函数共用。所以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()
Copying Random State Between Theano Graphs 复制随机状态在Theano的图间
在一些情况下,一个用户可能想要转换给定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()
Other Random Distributions 其他分布
Other Implementations 其他实现
这里有另外两个实现基于MRG31k3p
和DURAND
。随机流只会工作在CPU,而MRG31k3p工作在CPU和GPU,CURAND只工作在GPU
为了方便使用MRG,你可以只改变import: from theano.sandbox.rng_mrg import MRG_RandomStreams as RandStreams
A Real Example : Logistic Regression 一个真实例子:逻辑斯特回归
import numpy
import theano
import theano.tensor as T
rng = numpy.random
N = 400
feats = 784
# 生成一个数据集:D = (input_values, target_class)
D = (rng.rand(N, feats), rng.randint(size=N, low=0, high=2))
training_steps = 10000
#声明Theano符号变量
x = T.dmatrix("x")
y = T.dmatrix("y")
#权重和偏置项在训练迭代器被共享
#
#随机初始化权重向量w
w = theano.shared(rng.random(feats), name="w")
#随机初始化偏置项b
b = theano.shared(0., name="b")
print("Initial model:")
print(w.get_value())
print(b.get_value())
#构建Theano表达图
p_1 = 1 / (1 + T.exp(-T.dot(x, w)- b) #目标为1的概率
prediction = p_1 > 0.5 #预测阈值
xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1) #交叉熵损失函数
cost = xent.mean() + 0.01 * (w ** 2).sum() #损失最小化
gw, gb = T.grad(cost, [w, b]) #计算损失函数的梯度
#编译
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)
#训练
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]))