Skip to content
Snippets Groups Projects
transposed.py 2.71 KiB
Newer Older
  • Learn to ignore specific revisions
  • johannes bilk's avatar
    johannes bilk committed
    import numpy as np
    from numpy.typing import ArrayLike
    from .layer import LearningLayer
    from .convolution import getWindows, assignParameter
    from .weights import Weights
    
    
    class TransposedConv(LearningLayer):
        """
        Transposed Convolution Layer for a Convolutional Neural Network (CNN).
        This layer can increase the spatial dimensions (height, width) of the input.
        It is also known as a deconvolution layer, although it does not exactly reverse the convolution operation.
        """
        __slots__ = ['inChannels', 'outChannels', 'kernelSize', 'stride', 'padding', 'windows', 'input']
    
        def __init__(self, inChannels: int, outChannels: int, kernelSize: tuple = (3,3), padding: tuple = (0,0), stride: tuple = (1,1), weights: ArrayLike = None, bias: ArrayLike = None) -> None:
            super().__init__()
            self.inChannels = inChannels
            self.outChannels = outChannels
    
            self.kernelSize = assignParameter(kernelSize)
            self.paddingTransposed = (kernelSize[0] - padding[0] - 1, kernelSize[1] - padding[1] - 1)
            self.padding = assignParameter(padding)
            self.strideTransposed = (stride[0] - 1, stride[1] - 1)
            self.stride = assignParameter(stride)
    
            self.weights = Weights((self.outChannels, self.inChannels, self.kernelSize[0], self.kernelSize[1])) if weights is None else weights
            self.bias = Weights(self.outChannels, init='zeros') if bias is None else bias
    
        def forward(self, input: np.ndarray) -> np.ndarray:
            """
            The forward pass of transposed convolution
            """
            self.input = input
            batchSize, channels, height, width = input.shape
            outHeight = self.stride[0] * (height - 1) + self.kernelSize[0] - 2 * self.padding[0]
            outWidth = self.stride[1] * (width - 1) + self.kernelSize[1] - 2 * self.padding[1]
            self.outputShape = (batchSize, channels, outHeight, outWidth)
            self.windows = getWindows(self.input, self.kernelSize, self.outputShape, padding=self.paddingTransposed, stride=1, dilate=self.strideTransposed)
            out = np.einsum('bihwkl,oikl->bohw', self.windows, self.weights.values)
    
            # add bias to kernels
            out += self.bias.values[None, :, None, None]
            return out
    
        def backward(self, gradient: np.ndarray) -> np.ndarray:
            """
            The backward pass of transposed convolution
            """
            gradientWindows = getWindows(gradient, self.kernelSize, self.input.shape, padding=0, stride=self.stride)
            rotatedKernel = np.rot90(self.weights.values, 2, axes=(2, 3))
    
            self.weights.deltas = np.einsum('bihwkl,bohw->oikl', self.windows, gradient)
            self.bias.deltas = np.sum(gradient, axis=(0, 2, 3))
    
            return np.einsum('bohwkl,oikl->bihw', gradientWindows, rotatedKernel)