def scan:
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
`是一个函数,这个函数描述了scan中的一个
步骤。fn描述了迭代的输出结果的公式。传给
的变量顺序如下所示
* 第一个sequence
* 第一个sequence
* ...
* 最后一个sequence
* 第一个output
* 第二个output
* ...
* 最后一个output
* 所有其他参数 (non_sequence给出)
sequences的顺序和输入给scan的列表‘sequences’一样
outputs的顺序“outputs_info”的顺序一样。
对每个sequence 或者output来说,time slices的顺序和taps里给出的顺序一样
如下例所示
.. code-block:: python
scan(fn, sequences = [ dict(input= Sequence1, taps = [-3,2,-1])
, Sequence2
, dict(input = Sequence3, taps = 3) ]
, outputs_info = [ dict(initial = Output1, taps = [-3,-5])
, dict(initial = Output2, taps = None)
, Output3 ]
, non_sequences = [ Argument1, Argument2])
``fn`` 中参数的顺序由上到下所示:
#. ``Sequence1[t-3]``
#. ``Sequence1[t+2]``
#. ``Sequence1[t-1]``
#. ``Sequence2[t]``
#. ``Sequence3[t+3]``
#. ``Output1[t-3]``
#. ``Output1[t-5]``
#. ``Output3[t-1]``
#. ``Argument1``
#. ``Argument2``
``non_sequences``列表也可以包括shared参数,
但是scan通常直接忽略他们。
为了方便理解,我们通常显示的把shraed变量传给 scan
``scan`` 也可以计算出其他的``non sequences`` (非 shared)
即使他们没有被传给scan (但被`fn`使用). 如下所示:
.. code-block:: python
#是2还是W?
import theano.tensor as TT
W = TT.matrix()
W_2 = W**2
def f(x):
return TT.dot(x,W_2)
这个函数做了两件事,首先它给出了
一个ouputs list,其顺序和output_info一致,
区别在在于对每一个output initial state,只有一
个output 变量(即使没有是使用tap value),第二,
`应该返回一个update 字典(定义来怎样在每一
u次迭代后更新shared变量)。shared 变量可以定义为
一个元组构成的列表。对这两个列表的顺序并没有
过多的限制。’fn‘可以返回``(outputs_list, update_dictionary)``
或者仅仅是其中的一个
sequences
``sequences`` 是一个theano变量或者字典,它描述了
进行迭代的对象。如果一个序列是以字典的
形式给出的,那么会有以下可选的参数信息
* ``input`` (*mandatory*) -- 代表了输入序列
* ``taps`` -- 代表本次输入使用来第k个输入
默,例如[-2,-1]代表使用了x[t-2]和x[t-1],默认值为0,
outputs_info
``outputs_info`` 代表了每次outputs的初始状态
当初始状态为字典时,有以下可选信息
keys:
* ``initial`` -- 代表了给定output的初始状态
如果output是循环计算,并且不需要初始
状态的话,可以忽略。假设fn只用了上一次
输出,那么初始状态应该和output的形式
相同,并且不应该对output进行向下的类型
转换。如果使用了多个time taps,初始
状态应该包括所以的可能的taps.例如,
如果使用-5,-2,-1作为past taps,在step0,fn
应该使用``output[-5]``,``output[-2]`` 和 ``output[-1]``
作为初始状态
* ``taps`` -- 传给fn的output的tap
``scan`` 满足下列规则:
*如果output不是一个字典,那么‘scan’默认你总是使用最后一步输出
(即 tap=-1)
* 如果你将output放在一个字典里面,提供了初始状态但不提供任何的taps
那么默认使用tap=-1
* 如果你将output方在一个字典里面,但不提供初始状态,
默认不使用taps
* 如果使用一个空的字典或者None,默认不使用taps
如果``outputs_info`` 是空列表或者None,默认所有的ouputs不使用taps.
如果只对output的子集提供某些信息,那么将发生错误(因为没有规定如何
将提供的信息传递给scan
non_sequences
``non_sequences`` 描述了非序列的输入(参数),它的值传给fn后面的参数,且每次迭代的A都是不变的。
n_steps
``n_steps`` 迭代次数
返回
-------
元组
outputs, updates)形式的元组或者一个列表
列表中是scan的输出(和outputs_info)顺序一致
``updates`` 是一个字典的子集,规定了所有shared变量更新的规则
在编译的的时候,这个字典将被传递给theano.function
程序示例
import theano
import theano.tensor as T
k = T.iscalar("k")
A = T.vector("A")
# Symbolic description of the result
result, updates = theano.scan(fn=lambda prior_result, A: prior_result * A,
outputs_info=T.ones_like(A),
non_sequences=A,
n_steps=k)
# We only care about A**k, but scan has provided us with A**1 through A**k.
# Discard the values that we don't care about. Scan is smart enough to
# notice this and not waste memory saving them.
final_result = result[-1]
# compiled function that returns A**k
power = theano.function(inputs=[A,k], outputs=final_result, updates=updates)
print(power(range(10),2))
print(power(range(10),4))
结果
[ 0. 1. 4. 9. 16. 25. 36. 49. 64. 81.]
[ 0.00000000e+00 1.00000000e+00 1.60000000e+01 8.10000000e+01
2.56000000e+02 6.25000000e+02 1.29600000e+03 2.40100000e+03
4.09600000e+03 6.56100000e+03]
上面程序中 fn是一个匿名函数 给定prior_result 和A 返,回 prior_result * A.参数的顺序是固定的,我们可以看到此时由于output不是一个字典,所以我们默认使用上一步的输出(taps=-1),然后才是所有的non_sequences,即prior_result=outputs[-1],A=non_sequences.
然后我们初始化output为A和一样的大小,和dtype,所有元素的值为1。
第1步,初始化output=[1,1,1,1,1,1,1,1,1,1]
[1,1,1,1,1,1,1,1,1,1]*[0,1,2,3,4,5,6,7,8,9]=[0,1,2,3,4,5,6,7,8,9]
第2步[0,1,2,3,4,5,6,7,8,9]*[0,1,2,3,4,5,6,7,8,9]= 【0. 1. 4. 9. 16. 25. 36. 49. 64. 81.]
import numpy
coefficients = theano.tensor.vector("coefficients")
x = T.scalar("x")
max_coefficients_supported = 10000
# Generate the components of the polynomial
components, updates = theano.scan(fn=lambda coefficient, power, free_variable: coefficient * (free_variable ** power),
outputs_info=None,
sequences=[coefficients, theano.tensor.arange(max_coefficients_supported)],
non_sequences=x)
# Sum them up
polynomial = components.sum()
# Compile a function
calculate_polynomial = theano.function(inputs=[coefficients, x], outputs=polynomial)
# Test
test_coefficients = numpy.asarray([1, 0, 2], dtype=numpy.float32)
test_value = 3
print(calculate_polynomial(test_coefficients, test_value))
print(1.0 * (3 ** 0) + 0.0 * (3 ** 1) + 2.0 * (3 ** 2))
19.0
19.0
fn 为lambda函数 coefficient * (free_variable ** power)
根据 scan中参数的默认顺序 test_coefficients=[1, 0, 2] 和 是第一个sequences ,arange(max_coefficients_supported)是第二个sequences,test——value=3 是non_sequences
import numpy as np
import theano
import theano.tensor as T
up_to = T.iscalar("up_to")
# define a named function, rather than using lambda
def accumulate_by_adding(arange_val, sum_to_date):
return sum_to_date + arange_val
seq = T.arange(up_to)
# An unauthorized implicit downcast from the dtype of 'seq', to that of
# 'T.as_tensor_variable(0)' which is of dtype 'int8' by default would occur
# if this instruction were to be used instead of the next one:
# outputs_info = T.as_tensor_variable(0)
outputs_info = T.as_tensor_variable(np.asarray(0, seq.dtype))
scan_result, scan_updates = theano.scan(fn=accumulate_by_adding,
outputs_info=outputs_info,
sequences=seq)
triangular_sequence = theano.function(inputs=[up_to], outputs=scan_result)
# test
some_num = 15
print(triangular_sequence(some_num))
print([n * (n + 1) // 2 for n in range(some_num)])
[ 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105]
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105]
初始化output为T.as_tensor_variable(np.asarray(0, seq.dtype)),即[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
sequences为[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
默认taps=-1,则传给fn的第一个参数为outputs[-1],第二个参数为sequences