【问题标题】:Custom Spatial Convolution In TorchTorch 中的自定义空间卷积
【发布时间】:2015-11-26 02:51:57
【问题描述】:

我需要在 Torch 中执行自定义空间卷积。与其简单地将每个输入像素乘以该像素的权重,然后将它们与滤波器的偏差相加以形成每个输出像素,不如在将它们加在一起之前对输入像素执行更复杂的数学函数。

我知道如何做到这一点,但我不知道这样做的好方法。我想出的最好方法是获取完整的输入张量,创建一堆次级张量,它们是原始的“视图”而不分配额外的内存,将它们放入复制层(输出过滤器计数是复制count),并将其输入到一个 ParallelTable 层中,该层包含一堆常规层,它们的参数在过滤器之间共享。

问题是,尽管这在内存方面很好,开销非常可管理,但我们正在谈论 inputwidth^inputheight^inputdepth^outputdepth 迷你网络,在这里。也许有一些方法可以创建大规模的“长而高”的网络,可以同时处理整个复制的输入集,但是如何创建部分连接(如卷积)而不是全连接的层?

我本来希望只使用继承来创建常规 SpatialConvolution “类” 的特殊副本并对其进行修改,但我什至无法尝试,因为它是在外部 C 库中实现的。我不能只在常规 SpatialConvolution 层之前使用常规层,因为我需要对每个过滤器使用不同的权重和偏差进行数学运算(在相同过滤器的应用程序之间共享到不同的输入坐标)。

【问题讨论】:

    标签: lua neural-network convolution torch


    【解决方案1】:

    好问题。你让我认真思考。 您的方法有一个缺陷:它不允许利用矢量化计算,因为每个迷你网络都是独立工作的。

    我的想法如下:

    假设网络的inputoutput 是二维张量。我们可以(高效,无需内存复制)生成一个辅助 4D 张量 rf_input (kernel_size x kernel_size x output_h x output_w) 这样rf_input[:, :, k, l] 是一个大小为kernel_size x kernel_size 的二维张量,其中包含一个接受域,output[k, l] 将从中获取。然后我们遍历内核rf_input[i, j, :, :] 内的位置,获取所有感受野内(i, j) 位置的像素,并使用矢量化一次计算它们对每个output[k, l] 的贡献。

    示例:

    例如,让我们的“卷积”函数是和的切线的乘积:

    然后它的偏导数 w.r.t.其感受野中(s,t) 位置处的input 像素是

    衍生 w.r.t. weight 也一样。

    当然,最后,我们必须总结来自不同output[k,l] 点的梯度。例如,每个input[m, n] 最多对kernel_size^2 输出做出贡献,作为其感受野的一部分,每个weight[i, j] 对所有output_h x output_w 输出做出贡献。

    简单的实现可能如下所示:

    require 'nn'
    local CustomConv, parent = torch.class('nn.CustomConv', 'nn.Module')
    
    -- This module takes and produces a 2D map. 
    -- To work with multiple input/output feature maps and batches, 
    -- you have to iterate over them or further vectorize computations inside the loops.
    
    function CustomConv:__init(ker_size)
        parent.__init(self)
    
        self.ker_size = ker_size
        self.weight = torch.rand(self.ker_size, self.ker_size):add(-0.5)
        self.gradWeight = torch.Tensor(self.weight:size()):zero()
    end
    
    function CustomConv:_get_recfield_input(input)
        local rf_input = {}
        for i = 1, self.ker_size do
            rf_input[i] = {}
            for j = 1, self.ker_size do
                rf_input[i][j] = input[{{i, i - self.ker_size - 1}, {j, j - self.ker_size - 1}}]
            end
        end
        return rf_input
    end
    
    function CustomConv:updateOutput(_)
        local output = torch.Tensor(self.rf_input[1][1]:size())
        --  Kernel-specific: our kernel is multiplicative, so we start with ones
        output:fill(1)                                              
        --
        for i = 1, self.ker_size do
            for j = 1, self.ker_size do
                local ker_pt = self.rf_input[i][j]:clone()
                local w = self.weight[i][j]
                -- Kernel-specific
                output:cmul(ker_pt:add(w):tan())
                --
            end
        end
        return output
    end
    
    function CustomConv:updateGradInput_and_accGradParameters(_, gradOutput)
        local gradInput = torch.Tensor(self.input:size()):zero()
        for i = 1, self.ker_size do
            for j = 1, self.ker_size do
                local ker_pt = self.rf_input[i][j]:clone()
                local w = self.weight[i][j]
                -- Kernel-specific
                local subGradInput = torch.cmul(gradOutput, torch.cdiv(self.output, ker_pt:add(w):tan():cmul(ker_pt:add(w):cos():pow(2))))
                local subGradWeight = subGradInput
                --
                gradInput[{{i, i - self.ker_size - 1}, {j, j - self.ker_size - 1}}]:add(subGradInput)
                self.gradWeight[{i, j}] = self.gradWeight[{i, j}] + torch.sum(subGradWeight)
            end
        end
        return gradInput
    end
    
    function CustomConv:forward(input)
        self.input = input
        self.rf_input = self:_get_recfield_input(input)
        self.output = self:updateOutput(_)
        return self.output
    end
    
    function CustomConv:backward(input, gradOutput)
        gradInput = self:updateGradInput_and_accGradParameters(_, gradOutput)
        return gradInput
    end
    

    如果你稍微修改一下这段代码:

    updateOutput:                                             
        output:fill(0)
        [...]
        output:add(ker_pt:mul(w))
    
    updateGradInput_and_accGradParameters:
        local subGradInput = torch.mul(gradOutput, w)
        local subGradWeight = torch.cmul(gradOutput, ker_pt)
    

    那么它将完全像nn.SpatialConvolutionMM 那样工作,bias 为零(我已经测试过了)。

    【讨论】:

      猜你喜欢
      • 2016-12-13
      • 2018-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-15
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      相关资源
      最近更新 更多