编写函数需要经常用到条件语句或循环语句,这节我们就简单介绍Theano如何实现条件判断或逻辑循环。
Theano是一种符号语言,条件判断不能直接使用Python的if语句。在Theano可以用ifelse和switch来表示判定语句。这两个判定语句有何区别呢?
switch对每个输出变量进行操作,ifelse只对一个满足条件的变量操作。比如对语句:
switch(cond, ift, iff)
如果满足条件,则switch既执行ift也执行iff。而对语句:
if cond then ift else iff
ifelse只执行ift或者只执行iff。
下面通过一个示例进一步说明:
from theano import tensor as T from theano.ifelse import ifelse import theano,time,numpy a,b=T.scalars('a','b') x,y=T.matrices('x','y') z_switch=T.switch(T.lt(a,b),T.mean(x),T.mean(y))#lt:a < b? z_lazy=ifelse(T.lt(a,b),T.mean(x),T.mean(y)) #optimizer:optimizer的类型结构(可以简化计算,增加计算的稳定性) #linker:决定使用哪种方式进行编译(C/Python) f_switch = theano.function([a, b, x, y], z_switch,mode=theano.Mode(linker='vm')) f_lazyifelse = theano.function([a, b, x, y], z_lazy,mode=theano.Mode(linker='vm')) val1 = 0. val2 = 1. big_mat1 = numpy.ones((1000, 100)) big_mat2 = numpy.ones((1000, 100)) n_times = 10 tic = time.clock() for i in range(n_times): f_switch(val1, val2, big_mat1, big_mat2) print('time spent evaluating both values %f sec' % (time.clock() - tic)) tic = time.clock() for i in range(n_times): f_lazyifelse(val1, val2, big_mat1, big_mat2) print('time spent evaluating one value %f sec' % (time.clock() - tic))
打印结果:
time spent evaluating both values 0.005268 sec time spent evaluating one value 0.007501 sec
scan是Theano中构建循环Graph的方法,scan是个灵活复杂的函数,任何用循环、递归或者跟序列有关的计算,都可以用scan完成。其格式如下:
theano.scan(fn, sequences=None, outputs_info=None, non_sequences=None, n_steps=None, truncate_gradient=-1, go_backwards=False, mode=None, name=None, profile=False, allow_gc=None, strict=False)
参数说明:
·fn:一个lambda或者def函数,描述了scan中的一个步骤。除了outputs_info,fn可以返回sequences变量的更新updates。fn的输入变量的顺序为sequences中的变量、outputs_info的变量、non_sequences中的变量。如果使用了taps,则按照taps给fn喂变量。taps的详细介绍会在后面的例子中给出。
·sequences:scan进行迭代的变量,scan会在T.arange()生成的list上遍历,例如下面的polynomial例子。
·outputs_info:初始化fn的输出变量,和输出的shape一致。如果初始化值设为None,表示这个变量不需要初始值。
·non_sequences:fn函数用到的其他变量,迭代过程中不可改变(unchange)。
·n_steps:fn的迭代次数。
下面通过一个例子解释scan函数的具体使用方法。
代码实现思路是:先定义函数one_step,即scan里的fn,其任务就是计算多项式的一项,scan函数返回的result里会保存多项式每一项的值,然后我们对result求和,就得到了多项式的值。
import theano import theano.tensor as T import numpy as np # 定义单步的函数,实现a*x^n # 输入参数的顺序要与下面scan的输入参数对应 def one_step(coef, power, x): return coef * x ** power coefs = T.ivector() # 每步变化的值,系数组成的向量 powers = T.ivector() # 每步变化的值,指数组成的向量 x = T.iscalar() # 每步不变的值,自变量 # seq,out_info,non_seq与one_step函数的参数顺序一一对应 # 返回的result是每一项的符号表达式组成的list result, updates = theano.scan(fn = one_step, sequences = [coefs, powers], outputs_info = None, non_sequences = x) # 每一项的值与输入的函数关系 f_poly = theano.function([x, coefs, powers], result, allow_input_downcast=True) coef_val = np.array([2,3,4,6,5]) power_val = np.array([0,1,2,3,4]) x_val = 10 print("多项式各项的值: ",f_poly(x_val, coef_val, power_val)) #scan返回的result是每一项的值,并没有求和,如果我们只想要多项式的值,可以把f_poly写成这样: # 多项式每一项的和与输入的函数关系 f_poly = theano.function([x, coefs, powers], result.sum(), allow_input_downcast=True) print("多项式和的值:",f_poly(x_val, coef_val, power_val))
打印结果:
多项式各项的值: [ 2 30 400 6000 50000] 多项式和的值: 56432