blob: cd0e9c41ee11b5df678228a3ba412bc3957fc0c7 [file] [log] [blame]
import math
import torch
from .Module import Module
from .utils import clear
class SpatialFullConvolution(Module):
def __init__(self, nInputPlane, nOutputPlane, kW, kH, dW=1, dH=1, padW=0, padH=None, adjW=0, adjH=0):
super(SpatialFullConvolution, self).__init__()
self.nInputPlane = nInputPlane
self.nOutputPlane = nOutputPlane
self.kW = kW
self.kH = kH
self.dW = dW
self.dH = dH
self.padW = padW
self.padH = padH if padH is not None else padW
self.adjW = adjW
self.adjH = adjH
if self.adjW > self.dW - 1 or self.adjH > self.dH - 1:
raise ValueError('adjW and adjH must be smaller than self.dW - 1 and self.dH - 1 respectively')
self.weight = torch.Tensor(nInputPlane, nOutputPlane, kH, kW)
self.gradWeight = torch.Tensor(nInputPlane, nOutputPlane, kH, kW)
self.bias = torch.Tensor(self.nOutputPlane)
self.gradBias = torch.Tensor(self.nOutputPlane)
self.ones = torch.Tensor()
self.finput = None
self.fgradInput = None
self.zeroScalar = None
self._gradOutput = None
self.reset()
def noBias(self):
self.bias = None
self.gradBias = None
return self
def reset(self, stdv=None):
if stdv is not None:
stdv = stdv * math.sqrt(3)
else:
nInputPlane = self.nInputPlane
kH = self.kH
kW = self.kW
stdv = 1 / math.sqrt(kW * kH * nInputPlane)
self.weight.uniform_(-stdv, stdv)
if self.bias is not None:
self.bias.uniform_(-stdv, stdv)
def _makeContiguous(self, input, gradOutput=None):
if not input.is_contiguous():
if self._input is None:
self._input = input.new()
self._input.resize_as_(input).copy_(input)
input = self._input
if gradOutput is not None:
if not gradOutput.is_contiguous():
if self._gradOutput is None:
self._gradOutput = gradOutput.new()
self._gradOutput.resize_as_(gradOutput).copy_(gradOutput)
gradOutput = self._gradOutput
return input, gradOutput
return input
def _calculateAdj(self, targetSize, ker, pad, stride):
return (targetSize + 2 * pad - ker) % stride
def updateOutput(self, input):
inputTensor = input
adjW, adjH = 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()
tH = targetTensor.size(tDims - 2)
tW = targetTensor.size(tDims - 1)
adjW = self._calculateAdj(tW, self.kW, self.padW, self.dW)
adjH = self._calculateAdj(tH, self.kH, self.padH, self.dH)
if self.finput is None:
self.finput = input[0].new()
if self.fgradInput is None:
self.fgradInput = input[0].new()
else:
if self.finput is None:
self.finput = input.new()
if self.fgradInput is None:
self.fgradInput = input.new()
inputTensor = self._makeContiguous(inputTensor)
self._backend.SpatialFullConvolution_updateOutput(
self._backend.library_state,
inputTensor,
self.output,
self.weight,
self.bias,
self.finput,
self.fgradInput,
self.kW, self.kH,
self.dW, self.dH,
self.padW, self.padH,
adjW, adjH
)
return self.output
def updateGradInput(self, input, gradOutput):
if self.gradInput is None:
return
inputTensor = input
adjW, adjH = 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()
tH = targetTensor.size(tDims - 2)
tW = targetTensor.size(tDims - 1)
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.SpatialFullConvolution_updateGradInput(
self._backend.library_state,
inputTensor,
gradOutput,
self.gradInput,
self.weight,
self.finput,
self.kW, self.kH,
self.dW, self.dH,
self.padW, self.padH,
adjW, adjH
)
if isinstance(input, list):
# Create a zero tensor to be expanded and used as gradInput[1].
if self.zeroScalar is None:
self.zeroScalar = input[1].new(1).zero_()
self.ones.resize_(input[1].dim()).fill_(1)
zeroTensor = self.zeroScalar.view_as(self.ones).expand_as(input[1])
self.gradInput = [self.gradInput, zeroTensor]
return self.gradInput
def accGradParameters(self, input, gradOutput, scale=1):
inputTensor = input
adjW, adjH = 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(inputTensor, list):
inputTensor = input[0]
targetTensor = input[1]
tDims = targetTensor.dim()
tH = targetTensor.size(tDims - 2)
tW = targetTensor.size(tDims - 1)
adjW = calculateAdj(tW, self.kW, self.padW, self.dW)
adjH = calculateAdj(tH, self.kH, self.padH, self.dH)
inputTensor, gradOutput = self._makeContiguous(inputTensor, gradOutput)
self._backend.SpatialFullConvolution_accGradParameters(
self._backend.library_state,
inputTensor,
gradOutput,
self.gradWeight,
self.gradBias,
self.finput,
self.fgradInput,
self.kW, self.kH,
self.dW, self.dH,
self.padW, self.padH,
adjW, adjH,
scale
)
def type(self, type=None, tensorCache=None):
if self.finput is not None:
self.finput = torch.Tensor()
if self.fgradInput is not None:
self.fgradInput = torch.Tensor()
return super(SpatialFullConvolution, self).type(type, tensorCache)
def __repr__(self):
s = super(SpatialFullConvolution, self).__repr__()
s += '({} -> {}, {}x{}'.format(self.nInputPlane, self.nOutputPlane, self.kW, self.kH)
if self.dW != 1 or self.dH != 1 or self.padW != 0 or self.padH != 0:
s += ', {}, {}'.format(self.dW, self.dH)
if (self.padW or self.padH) and (self.padW != 0 or self.padH != 0):
s += ', {}, {}'.format(self.padW, self.padH)
if (self.adjW or self.adjH) and (self.adjW != 0 or self.adjH != 0):
s += ', {}, {}'.format(self.adjW, self.adjH)
s += ')'
if self.bias is None:
s += ' without bias'
return s
def clearState(self):
clear(self, 'finput', 'fgradInput', '_input', '_gradOutput')
return super(SpatialFullConvolution, self).clearState()