Source code for npdl.layers.convolution

# -*- coding: utf-8 -*-

import numpy as np

from .base import Layer
from .. import activations
from ..initializations import _zero
from ..initializations import get as get_init


[docs]class Convolution(Layer): """Convolution operator for filtering windows of two-dimensional inputs. When using this layer as the first layer in a model, provide the keyword argument `input_shape` (tuple of integers, does not include the sample axis), e.g. `input_shape=(3, 128, 128)` for 128x128 RGB pictures. """ def __init__(self, nb_filter, filter_size, input_shape=None, stride=1, init='glorot_uniform', activation='relu'): self.nb_filter = nb_filter self.filter_size = filter_size self.input_shape = input_shape self.stride = stride self.W, self.dW = None, None self.b, self.db = None, None self.out_shape = None self.last_output = None self.last_input = None self.init = get_init(init) self.activation = activations.get(activation)
[docs] def connect_to(self, prev_layer=None): if prev_layer is None: assert self.input_shape is not None input_shape = self.input_shape else: input_shape = prev_layer.out_shape # input_shape: (batch size, num input feature maps, image height, image width) assert len(input_shape) == 4 nb_batch, pre_nb_filter, pre_height, pre_width = input_shape filter_height, filter_width = self.filter_size height = (pre_height - filter_height) // self.stride + 1 width = (pre_width - filter_width) // self.stride + 1 # output shape self.out_shape = (nb_batch, self.nb_filter, height, width) # filters self.W = self.init((self.nb_filter, pre_nb_filter, filter_height, filter_width)) self.b = _zero((self.nb_filter,))
[docs] def forward(self, input, *args, **kwargs): self.last_input = input # shape nb_batch, input_depth, old_img_h, old_img_w = input.shape filter_h, filter_w = self.filter_size new_img_h, new_img_w = self.out_shape[2:] # init outputs = _zero((nb_batch, self.nb_filter, new_img_h, new_img_w)) # convolution operation for x in np.arange(nb_batch): for y in np.arange(self.nb_filter): for h in np.arange(new_img_h): for w in np.arange(new_img_w): h_shift, w_shift = h * self.stride, w * self.stride # patch: (input_depth, filter_h, filter_w) patch = input[x, :, h_shift: h_shift + filter_h, w_shift: w_shift + filter_w] outputs[x, y, h, w] = np.sum(patch * self.W[y]) + self.b[y] # nonlinear activation # self.last_output: (nb_batch, output_depth, image height, image width) self.last_output = self.activation.forward(outputs) return self.last_output
[docs] def backward(self, pre_grad, *args, **kwargs): # shape assert pre_grad.shape == self.last_output.shape nb_batch, input_depth, old_img_h, old_img_w = self.last_input.shape new_img_h, new_img_w = self.out_shape[2:] filter_h, filter_w = self.filter_size old_img_h, old_img_w = self.last_input.shape[-2:] # gradients self.dW = _zero(self.W.shape) self.db = _zero(self.b.shape) delta = pre_grad * self.activation.derivative() # dW for r in np.arange(self.nb_filter): for t in np.arange(input_depth): for h in np.arange(filter_h): for w in np.arange(filter_w): input_window = self.last_input[:, t, h:old_img_h - filter_h + h + 1:self.stride, w:old_img_w - filter_w + w + 1:self.stride] delta_window = delta[:, r] self.dW[r, t, h, w] = np.sum(input_window * delta_window) / nb_batch # db for r in np.arange(self.nb_filter): self.db[r] = np.sum(delta[:, r]) / nb_batch # dX if not self.first_layer: layer_grads = _zero(self.last_input.shape) for b in np.arange(nb_batch): for r in np.arange(self.nb_filter): for t in np.arange(input_depth): for h in np.arange(new_img_h): for w in np.arange(new_img_w): h_shift, w_shift = h * self.stride, w * self.stride layer_grads[b, t, h_shift:h_shift + filter_h, w_shift:w_shift + filter_w] += \ self.W[r, t] * delta[b, r, h, w] return layer_grads
@property def params(self): return self.W, self.b @property def grads(self): return self.dW, self.db