Theano作为深度学习领域一个经典的python库,可以先定义数学表达式再进行运算,开创了符号计算的先河。最近在科研过程中用到了其中的scan()函数,虽然以前也学过这个框架,但是tensorflow永久了,感觉就把一些知识点忘记了,于是想到了写笔记的重要性,故在这里记录下来,以备随时回头翻看。
在学习任何一种编程语言的时候,我们都会学习它的变量定义、数据类型、程序结构控制(顺序结构,选择结构,循环结构等),而theano作为一种符号计算框架,是用于解决循环结构的,只不过与一般的编程语言中的循环结构不同的是,它针对的是符号变量(symbol variable)。scan()函数中的参数有很多,包括sequences, outputs_info, non_sequences, n_steps等,刚开始学习时我们可能会想,为什么会有这么多参数呢?我所能想到的就是,循环结构本身其实包含了不同的情况,有的只需要一步并行计算即可,有的则需要在反复迭代中进行变量自身的更新。另外,theano中的变量包括一般的tensor,shared variable等,需要不同的考虑。以下对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’:'fn’是一个lambda或者def函数,描述了一步scan操作的运算式,运算式的输入参数按照sequences, outputs_info, non_sequences的顺序,运算式的输出作为theano.scan的返回值。
‘sequences’:sequences是一个theano variables或者dictionaries的列表。字典对象的结构为{‘variable’:taps},其中taps是一个整数列表。'sequences’列表中的所有Theano variable会被自动封装成一个字典,此时taps被设置成[0]。比如sequences = [ dict(input= Sequence1, taps = [-3,2,-1]), Sequence2, dict(input = Sequence3, taps = 3) ], 映射到scan输入参数为Sequence1[t-3],Sequence1[t+2],Sequence1[t-1],Sequence2[t],Sequence3[t+3]。还有一点就是,如果序列的长度不一致,scan会裁剪成它们中最短的,这个性质方便我们传递一个很长的arange,比如sequences=[coefficients, theano.tensor.arange(max_coefficients_supported)]
‘outputs_info’:outputs_info
是一个theano variables或者dictionaries的列表,它描述了输出的初始状态,显然应该和输出有相同的shape,而且,每进行一步scan操作,outputs_info中的数值会被上一次迭代的输出值更新掉。当然,如果当前循环结构不需要recursive,而仅仅是一个map操作的话,这个参数便可以省略;
‘non_sequences’:non_sequences
是一个‘常量’参数列表,这里所谓的‘常量’是相对于‘outputs_info’中的参数更新而言的,代表了一步scan操作中不会被更新的变量。计算图中的有些变量在这里也可以不显式的指明,但显式指明变量参数会得到一个简化的计算图,加速编译器对图的优化和执行。 常见的应用是,把shared variables作为non_sequences参数中的值.
‘n_steps’:‘n_steps’参数是一个int或者theano scalar,代表了scan操作的迭代次数。如果存在输入序列,其中的元素个数小于n_steps,scan函数会报错。如果n_steps参数未指定,scan会根据他的输入参数自动计算出迭代步数;
‘truncate_gradient’:‘truncate_gradient’参数代表了使用BPTT(back propagation through time)算法时,“梯度截断”后的步数。“梯度截断”的目的是在可接受的误差范围内,降低梯度的计算复杂度。常见的应用场景是RNN(recurrent neural network)。
‘strict’:'strict’是一个shared variable校验标志,用于检验是否fn函数用到的所有shared variabes都在non_sequences中,若不满足则会Raise an error。
其他参数:用的比较少,这里就略了。
返回参数:
形如(outputs, updates)格式的元组类型。’outputs‘是一个theano变量,或者多个theano变量构成的list。并且,每一个theano变量包含了所有迭代步骤的输出结果。’updates‘是形如(var, expression)的字典结构,指明了scan中用到的所有shared variables的更新规则 。
使用共享变量的例子-Gibbs采样
import theano
from theano import tensor as T
W = theano.shared(W_values) # we assume that ``W_values`` contains the
# initial values of your weight matrix
bvis = theano.shared(bvis_values)
bhid = theano.shared(bhid_values)
trng = T.shared_randomstreams.RandomStreams(1234)
def OneStep(vsample) :
hmean = T.nnet.sigmoid(theano.dot(vsample, W) + bhid)
hsample = trng.binomial(size=hmean.shape, n=1, p=hmean)
vmean = T.nnet.sigmoid(theano.dot(hsample, W.T) + bvis)
return trng.binomial(size=vsample.shape, n=1, p=vmean,
dtype=theano.config.floatX)
sample = theano.tensor.vector()
values, updates = theano.scan(OneStep, outputs_info=sample, n_steps=10)
gibbs10 = theano.function([sample], values[-1], updates=updates)
在这段样例中,updates字典建立了shared variable到k steps后的更新值的映射,在这段代码中也就是random streams的更新情况。如果不传递updates参数到theano.function中,每次调用gibbs10函数时,会得到相同的10个随机数sets,导致返回值values[-1]是相同的。
注:更多的关于theano.scan的使用样例,请参见官网教程http://deeplearning.net/software/theano/library/scan.html