Theano新手在这里。我正在做一些实验,以生成可变长度的序列。我开始考虑最简单的事情:模拟范围()。这是我写的简单代码:
from theano import scan
from theano import function
from theano import tensor as T
X = T.iscalar('X')
STEP = T.iscalar('STEP')
MAX_LENGTH = 1024 # or any othe very large value
def fstep(i, x, t):
n = i * t
return n, until(n >= x)
t_fwd_range, _ = scan(
fn=fstep,
sequences=T.arange(MAX_LENGTH),
non_sequences=[X, STEP]
)
getRange = function(
inputs=[X, Param(STEP, 1, 'step')],
outputs=t_fwd_range
)
getRange(x, step)
print list(f)
assert list(f[:-1]) == list(range(0, x, step))
因此,我必须使用MAX_LENGTH
作为范围的长度,以用作fstep
theano scan
的输入。所以,我的主要问题是:有没有办法在没有输入序列的情况下使用scan
?而且,正如我认为答案是 no ,接下来的问题是:这是正确的(最有效的,ecc)方式来做我正在做的事情吗?
答案 0 :(得分:1)
无需提供输入序列进行扫描。您可以通过扫描的n_steps
参数指定迭代次数。或者,您也可以通过theano.scan_module.until
指定扫描应提前停止的条件。
因此,Python的range
函数可以使用Theano的scan
进行模拟,而无需通过确定构造所请求序列所需的迭代次数来指定输入序列。
这是基于Theano的scan
的范围函数的实现。唯一复杂的部分是确定需要多少步骤。
import numpy
import theano
import theano.tensor as tt
import theano.ifelse
def scan_range_step(x_tm1, step):
return x_tm1 + step
def compile_theano_range():
tt.arange
symbolic_start = tt.lscalar()
symbolic_stop = tt.lscalar()
symbolic_step = tt.lscalar()
n_steps = tt.cast(
tt.ceil(tt.abs_(symbolic_stop - symbolic_start) / tt.cast(tt.abs_(symbolic_step), theano.config.floatX)),
'int64') - 1
outputs, _ = theano.scan(scan_range_step, outputs_info=[symbolic_start], n_steps=n_steps,
non_sequences=[symbolic_step], strict=True)
outputs = theano.ifelse.ifelse(tt.eq(n_steps, 0), tt.stack(symbolic_start), outputs)
f = theano.function([symbolic_start, symbolic_stop, symbolic_step],
outputs=tt.concatenate([[symbolic_start], outputs]))
def theano_range(start, stop=None, step=1):
assert isinstance(start, int)
assert isinstance(step, int)
if step == 0:
raise ValueError()
if stop is None:
stop = start
start = 0
else:
assert isinstance(stop, int)
if start == stop:
return []
if stop < start and step > 0:
return []
if stop > start and step < 0:
return []
return f(start, stop, step)
return theano_range
def main():
theano_range = compile_theano_range()
python_range = range
for start in [-10, -5, -1, 0, 1, 5, 10]:
for stop in [-10, -5, -1, 0, 1, 5, 10]:
for step in [-3, -2, -1, 1, 2, 3]:
a = theano_range(start, stop, step)
b = python_range(start, stop, step)
assert numpy.all(numpy.equal(a, b)), (start, stop, step, a, b)
main()
显然,由于Theano已经提供了Python的range
函数的符号版本,即theano.tensor.arange
,因此这对于实际来说是一件愚蠢的事情。内置实现也比我们的scan
版本更有效,因为它不使用scan
,而是使用自定义操作。
根据经验法则:必须通过range
或n_steps
参数设置最大迭代步数。您可以将其设置为一个非常大的数字,然后如果满足停止条件,则使用theano.scan_module.until
在较早阶段停止迭代。