【问题标题】:Scanning over different dimensions of tensors in theano在 theano 中扫描不同维度的张量
【发布时间】:2015-09-28 10:21:34
【问题描述】:

我正在使用theano 迈出第一步,但我不知道如何解决这个问题,这实际上很容易。

我有一个3 * 4 * 2 张量,如下所示:

[1 1] | [2 2] | [3 3]
[1 1] | [2 2] | [3 3]
[0 0] | [2 2] | [3 3]
[9 9] | [0 0] | [3 3]

所以我有N=3 序列,每个序列的长度为L=4,其元素是维度为d=2 的向量。实际上,序列可以有不同的长度,但我可以考虑用[0 0] 向量填充它们,如上所示。

我想要做的是,首先扫描张量的第一个轴并将列表中的所有向量相加直到第一个 [0 0] 向量 - 这就是为什么我在第一个张量切片的末尾添加了 [9 9],以检查总和退出条件 [1]。我应该以[[2 2], [6 6], [12 12]] 结束。我尝试了很多方法来解决这个问题,在我看来只是一个嵌套循环问题......但总是遇到一些奇怪的错误[2]。

谢谢,
朱利奥

--
[1]:实际问题是训练用于 NLP 目的的循环神经网络,N 是批次的维度,L 是批次中句子的最大长度,d 是表示的维度每个单词的。我省略了这个问题,以便我可以专注于最简单的编码方面。
[2] 我省略了我失败的历史,也许我可以稍后再补充。

【问题讨论】:

    标签: python loops iteration theano


    【解决方案1】:

    如果您的序列总是零填充,那么您可以沿感兴趣的轴求和,因为填充区域不会改变总和。但是,如果填充区域可能包含非零值,则有两种方法。

    1. 使用扫描。这很慢,应尽可能避免。事实上,这是可以避免的,因为,
    2. 创建一个二进制掩码并乘以填充区域。

    这里有一些代码说明了这三种方法。对于允许非零填充区域的两种方法(v2v3),计算需要一个额外的输入:一个向量,给出批次中序列的长度。

    import numpy
    import theano
    import theano.tensor as tt
    
    
    def v1():
        # NOTE: [9, 9] element changed to [0, 0] 
        # since zero padding must be used for
        # this method
        x_data = [[[1, 1], [1, 1], [0, 0], [0, 0]],
                  [[2, 2], [2, 2], [2, 2], [0, 0]],
                  [[3, 3], [3, 3], [3, 3], [3, 3]]]
        x = tt.tensor3()
        x.tag.test_value = x_data
        y = x.sum(axis=1)
        f = theano.function([x], outputs=y)
        print f(x_data)
    
    
    def v2_step(i_t, s_tm1, x, l):
        in_sequence = tt.lt(i_t, l).dimshuffle(0, 'x')
        s_t = s_tm1 + tt.switch(in_sequence, x[i_t], 0)
        return s_t
    
    
    def v2():
        x_data = [[[1, 1], [1, 1], [0, 0], [9, 9]],
                  [[2, 2], [2, 2], [2, 2], [0, 0]],
                  [[3, 3], [3, 3], [3, 3], [3, 3]]]
        l_data = [2, 3, 4]
        x = tt.tensor3()
        x.tag.test_value = x_data
        l = tt.lvector()
        l.tag.test_value = l_data
        # Must dimshuffle first because scan can only iterate over first (0'th) axis.
        x_hat = x.dimshuffle(1, 0, 2)
        y, _ = theano.scan(v2_step, sequences=[tt.arange(x_hat.shape[0])],
                           outputs_info=[tt.zeros_like(x_hat[0])],
                           non_sequences=[x_hat, l], strict=True)
        f = theano.function([x, l], outputs=y[-1])
        print f(x_data, l_data)
    
    
    def v3():
        x_data = [[[1, 1], [1, 1], [0, 0], [9, 9]],
                  [[2, 2], [2, 2], [2, 2], [0, 0]],
                  [[3, 3], [3, 3], [3, 3], [3, 3]]]
        l_data = [2, 3, 4]
        x = tt.tensor3()
        x.tag.test_value = x_data
        l = tt.lvector()
        l.tag.test_value = l_data
        indexes = tt.arange(x.shape[1]).dimshuffle('x', 0)
        mask = tt.lt(indexes, l.dimshuffle(0, 'x')).dimshuffle(0, 1, 'x')
        y = (mask * x).sum(axis=1)
        f = theano.function([x, l], outputs=y)
        print f(x_data, l_data)
    
    
    def main():
        theano.config.compute_test_value = 'raise'
        v1()
        v2()
        v3()
    
    
    main()
    

    一般来说,如果您的步进函数依赖于上一步的输出,那么您需要使用scan

    如果原则上每个步骤/迭代都可以同时执行(即它们根本不相互依赖),那么通常有一种更有效的方法可以在不使用扫描的情况下执行此操作

    【讨论】:

    • 你好丹尼尔。 v2 是我更喜欢的解决方案,因为在实际场景中我需要训练循环神经网络,而不仅仅是调用 tt.sum(),所以我认为我必须使用 scan——或者,至少,无法弄清楚任何另一种方式。基于掩码的解决方案非常棒:我正试图找出一种方法来适应我的情况。欢迎提出任何建议。
    • 一般来说,如果您的步进函数依赖于上一步的输出,那么您需要使用scan。如果原则上每个步骤/迭代都可以同时执行(即它们根本不相互依赖),那么通常有一种更有效的方法可以在不使用scan 的情况下执行此操作,但可能很难解决它。
    • 我明白了。所以就我而言,我完全必须使用扫描。我认为我们可以通过这种方式(为下一位读者)找出答案,以便我可以将其标记为正确。非常感谢您的支持。
    猜你喜欢
    • 2020-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-22
    • 2018-10-29
    • 2022-01-22
    • 2014-10-06
    相关资源
    最近更新 更多