| import math |
| import torch |
| from .Module import Module |
| |
| class VolumetricFullConvolution(Module): |
| |
| def __init__(self, nInputPlane, nOutputPlane, |
| kT, kW, kH, # kernel size |
| dT=1, dW=1, dH=1, # stride |
| padT=0, padW=0, padH=0, # padding |
| adjT=0, adjW=0, adjH=0): # extra output adjustment |
| super(VolumetricFullConvolution, self).__init__() |
| |
| self.nInputPlane = nInputPlane |
| self.nOutputPlane = nOutputPlane |
| self.kW = kW |
| self.kH = kH |
| self.kT = kT |
| self.dW = dW |
| self.dH = dH |
| self.dT = dT |
| self.padW = padW |
| self.padH = padH |
| self.padT = padT |
| self.adjW = adjW |
| self.adjH = adjH |
| self.adjT = adjT |
| |
| if self.adjW > self.dW - 1 or self.adjH > self.dH - 1 or self.adjT > self.dT - 1: |
| raise RuntimeError('adjW, adjH and adjT must be smaller than self.dW - 1, ' |
| ' self.dH - 1 and self.dT - 1 respectively') |
| |
| self.weight = torch.Tensor(nInputPlane, nOutputPlane, kT, kH, kW) |
| self.gradWeight = torch.Tensor(nInputPlane, nOutputPlane, kT, kH, kW) |
| self.bias = torch.Tensor(self.nOutputPlane) |
| self.gradBias = torch.Tensor(self.nOutputPlane) |
| |
| self.ones = torch.Tensor() |
| self.finput = torch.Tensor() |
| self.fgradInput = torch.Tensor() |
| |
| self.reset() |
| |
| |
| def reset(self, stdv=None): |
| if stdv is not None: |
| stdv = stdv * math.sqrt(3) |
| else: |
| nInputPlane = self.nInputPlane |
| kT = self.kT |
| kH = self.kH |
| kW = self.kW |
| stdv = 1. / math.sqrt(kW*kH*kT*nInputPlane) |
| |
| self.weight.uniform_(-stdv, stdv) |
| self.bias.uniform_(-stdv, stdv) |
| |
| def _makeContiguous(self, input, gradOutput=None): |
| if not input.isContiguous(): |
| self._input = self._input or input.new() |
| self._input.resizeAs_(input).copy_(input) |
| input = self._input |
| |
| if gradOutput is not None: |
| if not gradOutput.isContiguous(): |
| self._gradOutput = self._gradOutput or gradOutput.new() |
| self._gradOutput.resizeAs_(gradOutput).copy_(gradOutput) |
| gradOutput = self._gradOutput |
| return input, gradOutput |
| |
| return input |
| |
| def _calculateAdj(targetSize, ker, pad, stride): |
| return (targetSize + 2 * pad - ker) % stride |
| |
| def updateOutput(self, input): |
| inputTensor = input |
| adjT, adjW, adjH = self.adjT, self.adjW, self.adjH |
| |
| # The input can be a table where the second element indicates the target |
| # output size, in which case the adj factors are computed automatically |
| if isinstance(input, list): |
| inputTensor = input[0] |
| targetTensor = input[1] |
| tDims = targetTensor.dim() |
| tT = targetTensor.size(tDims-3) |
| tH = targetTensor.size(tDims-2) |
| tW = targetTensor.size(tDims-1) |
| adjT = self._calculateAdj(tT, self.kT, self.padT, self.dT) |
| adjW = self._calculateAdj(tW, self.kW, self.padW, self.dW) |
| adjH = self._calculateAdj(tH, self.kH, self.padH, self.dH) |
| |
| inputTensor = self._makeContiguous(inputTensor) |
| self._backend.VolumetricFullConvolution_updateOutput( |
| self._backend.library_state, |
| inputTensor, |
| self.output, |
| self.weight, |
| self.bias, |
| self.finput, |
| self.fgradInput, |
| self.dT, self.dW, self.dH, |
| self.padT, self.padW, self.padH, |
| adjT, adjW, adjH |
| ) |
| |
| return self.output |
| |
| def updateGradInput(self, input, gradOutput): |
| inputTensor = input |
| adjT, adjW, adjH = self.adjT, self.adjW, self.adjH |
| |
| # The input can be a table where the second element indicates the target |
| # output size, in which case the adj factors are computed automatically |
| if isinstance(input, list): |
| inputTensor = input[0] |
| targetTensor = input[1] |
| tDims = targetTensor.dim() |
| tT = targetTensor.size(tDims-3) |
| tH = targetTensor.size(tDims-2) |
| tW = targetTensor.size(tDims-1) |
| adjT = self._calculateAdj(tT, self.kT, self.padT, self.dT) |
| adjW = self._calculateAdj(tW, self.kW, self.padW, self.dW) |
| adjH = self._calculateAdj(tH, self.kH, self.padH, self.dH) |
| # Momentarily extract the gradInput tensor |
| if isinstance(self.gradInput, list): |
| self.gradInput = self.gradInput[0] |
| |
| inputTensor, gradOutput = self._makeContiguous(inputTensor, gradOutput) |
| self._backend.VolumetricFullConvolution_updateGradInput( |
| self._backend.library_state, |
| inputTensor, |
| gradOutput, |
| self.gradInput, |
| self.weight, |
| self.finput, |
| self.fgradInput, |
| self.dT, self.dW, self.dH, |
| self.padT, self.padW, self.padH, |
| adjT, adjW, adjH |
| ) |
| |
| if isinstance(input, list): |
| # Create a zero tensor to be expanded and used as gradInput[1]. |
| self.zeroScalar = self.zeroScalar or input[1].new(1).zero_() |
| self.ones.resize_(input[1].dim()).fill_(1) |
| zeroTensor = self.zeroScalar.view(self.ones.tolist()).expandAs(input[1]) |
| self.gradInput = [self.gradInput, zeroTensor] |
| |
| return self.gradInput |
| |
| def accGradParameters(self, input, gradOutput, scale=1): |
| inputTensor = input |
| adjT, adjW, adjH = self.adjT, self.adjW, self.adjH |
| |
| # The input can be a table where the second element indicates the target |
| # output size, in which case the adj factors are computed automatically |
| if isinstance(input, list): |
| inputTensor = input[0] |
| targetTensor = input[1] |
| tDims = targetTensor.dim() |
| tT = targetTensor.size(tDims-3) |
| tH = targetTensor.size(tDims-2) |
| tW = targetTensor.size(tDims-1) |
| adjT = self._calculateAdj(tT, self.kT, self.padT, self.dT) |
| adjW = self._calculateAdj(tW, self.kW, self.padW, self.dW) |
| adjH = self._calculateAdj(tH, self.kH, self.padH, self.dH) |
| |
| inputTensor, gradOutput = self._makeContiguous(inputTensor, gradOutput) |
| self._backend.VolumetricFullConvolution_accGradParameters( |
| self._backend.library_state, |
| inputTensor, |
| gradOutput, |
| self.gradWeight, |
| self.gradBias, |
| self.finput, |
| self.fgradInput, |
| self.dT, self.dW, self.dH, |
| self.padT, self.padW, self.padH, |
| adjT, adjW, adjH, |
| scale |
| ) |
| |
| def type(self, type, tensorCache=None): |
| self.finput = torch.Tensor() |
| self.fgradInput = torch.Tensor() |
| return super(VolumetricFullConvolution, self).type(type, tensorCache) |
| |
| def __repr__(self): |
| s = super(VolumetricFullConvolution, self).__repr__() |
| s += '({} -> {}, {}x{}x{}'.format(self.nInputPlane, self.nOutputPlane, self.kT, self.kW, self.kH) |
| if self.dT != 1 or self.dW != 1 or self.dH != 1 or \ |
| self.padT != 0 or self.padW != 0 or self.padH != 0 or \ |
| self.adjT != 0 or self.adjW != 0 or self.adjH != 0: |
| s += ', {}, {}, {}'.format(self.dT, self.dW, self.dH) |
| |
| if self.padT != 0 or self.padW != 0 or self.padH != 0 or \ |
| self.adjT != 0 or self.adjW != 0 or self.adjH != 0: |
| s += ', {}, {}, {}'.format(self.padT, self.padW, self.padH) |
| |
| if self.adjT != 0 or self.adjW != 0 or self.adjH != 0: |
| s += ', {}, {}, {}'.format(self.adjT, self.adjW, self.adjH) |
| |
| s += ')' |
| return s |
| |