在Theano中定义函数的正确方式是什么?

6

背景:

通常我会使用类似于“x = fmatrix()”这样的输入来定义一个theano函数,然而,在修改基于theano的深度学习库keras以使其能够与CTC成本一起使用时,我发现了一个非常奇怪的问题:如果成本函数的一个输入被声明为

x = tensor.zeros(shape=[M,N], dtype='float32')

替换为
x = fmatrix()

培训过程会收敛得更快。

一个简化的问题:

上面的所有代码都很大。因此,我尝试将问题简化为:编写一个计算Levenshtein编辑距离的函数,如下所示:

import theano
from theano import tensor
from theano.ifelse import ifelse
def editdist(s, t):
    def update(x, previous_row, target):
        current_row = previous_row + 1
        current_row = tensor.set_subtensor(current_row[1:], tensor.minimum(current_row[1:], tensor.add(previous_row[:-1], tensor.neq(target,x))))
        current_row = tensor.set_subtensor(current_row[1:], tensor.minimum(current_row[1:], current_row[0:-1] + 1))
        return current_row
    source, target = ifelse(tensor.lt(s.shape[0], t.shape[0]), (t, s), (s, t))
    previous_row = tensor.arange(target.size + 1, dtype=theano.config.floatX)
    result, updates = theano.scan(fn = update, sequences=source, outputs_info=previous_row, non_sequences=target, name='editdist')
    return result[-1,-1]

接下来我定义了两个函数f1和f2,代码如下:

x1 = tensor.fvector()
x2 = tensor.fvector()
r1 = editdist(x1,x2)
f1 = theano.function([x1,x2], r1)
x3 = tensor.zeros(3, dtype='float32')
x4 = tensor.zeros(3, dtype='float32')
r2 = editdist(x3,x4)
f2 = theano.function([x3,x4], r2)

当使用f1和f2进行计算时,结果是不同的:

>>f1([1,2,3],[1,3,3])
   array(1.0)

>>f2([1,2,3],[1,3,3])
   array(3.0)

f1得到了正确的结果,但f2没有。

我的问题是:定义theano函数的正确方法是什么?而且,f2出了什么问题?

更新:

我使用的是版本0.8.0.dev0的theano。我刚刚尝试了theano 0.7.0,f1和f2都给出了正确的结果。也许这是theano的一个bug?

第一次更新 - 2016年1月27日:

根据@lamblin在这个问题上的解释(https://github.com/Theano/Theano/issues/3925#issuecomment-175088918),这实际上是theano的一个bug,并已在最新版本(2016年1月26日)中修复。为方便起见,在此引用lamblin的解释:

第一种方式是最自然的,但理论上两种方式应该是等效的。 x3和x4被创建为“alloc”操作的输出,其输入应该是常量3,而不是像x1和x2这样的自由输入,但这并不重要,因为您将[x3,x4]作为输入传递给theano.function,它应该在那里剪切计算图。

我猜测scan会过早地进行优化,认为x3或x4保证始终为常量0,并进行一些简化,但当为它们提供值时,这些简化被证明是不正确的。那将是扫描中的一个实际bug。”

第二次更新 - 2016年1月27日:

不幸的是,这个bug还没有完全修复。在背景部分中我提到,如果成本函数的一个输入声明为tensor.zeros(),收敛过程将会更快,我已经找到了原因:当输入声明为tensor.zeros()时,成本函数给出了不正确的结果,尽管神奇地帮助了收敛。我在这里制作了一个简化的问题重现演示(https://github.com/daweileng/TheanoDebug),运行ctc_bench.py,您就可以看到结果。


我复制了你的代码,但在我的机器上f1([1,2,3],[1,3,3])和f2([1,2,3],[1,3,3])给出了相同的结果(0.1),顺便说一下,我正在使用theano 0.7。 - dontloo
@dontloo:您的意思是result=1.0吗?我正在使用最新的0.8.0.dev0版本。所以您认为这与theano有关?我会尝试一下。 - Jedi
是的,1.0版本。抱歉打错了。我看到你的更新了,恭喜XD。 - dontloo
很棒,感谢您在这里详细说明。这是否意味着可以将theano图中的任何变量声明为函数的输入,并且函数会适当地切断图形? - eickenberg
1个回答

2

theano.tensor.zeros(...) 只能取0值,不能取其他任何值。

当然,除非你添加节点到图中并使用 theano.tensor.set_subtensor 修改部分零张量。

输入张量 theano.tensor.fmatrix 可以接受任何您输入的值。


我其实有点惊讶,你可以将“zeros”张量声明为函数的输入。它应该会抛出错误... - eickenberg
实际上在Keras中有很多这样的声明,所以我认为这应该是定义一个Theano函数的正确方式。 - Jedi
嗯,如果它能够正常运行并加快速度,那就很有意思。事实上,它是在一个软件包中完成的,并不意味着这是一种良好的做法。但也许这是一种合理的技巧。Theano文档中有关于这个的内容吗? - eickenberg
@nouiz,如果你路过这里,能否评论一下这个问题? - eickenberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接