Source code for numpynet.layers.pooling

import numpy as np
from .base import Layer


[docs] class MaxPooling2D(Layer): """ 2D Max Pooling layer (channels-last: N, H, W, C). """ def __init__(self, pool_size=2, stride=None, name=None): super().__init__(name=name) self.pool_size = (pool_size, pool_size) if isinstance(pool_size, int) else tuple(pool_size) self.stride = stride if stride is not None else self.pool_size
[docs] def forward(self, x, training=False): n, h, w, c = x.shape ph, pw = self.pool_size sh, sw = (self.stride, self.stride) if isinstance(self.stride, int) else self.stride out_h = (h - ph) // sh + 1 out_w = (w - pw) // sw + 1 self._input = x self._input_shape = x.shape out = np.zeros((n, out_h, out_w, c)) self._max_mask = np.zeros_like(x, dtype=bool) for i in range(out_h): for j in range(out_w): patch = x[:, i*sh:i*sh+ph, j*sw:j*sw+pw, :] max_val = np.max(patch, axis=(1, 2), keepdims=True) out[:, i, j, :] = max_val[:, 0, 0, :] mask = (patch == max_val) self._max_mask[:, i*sh:i*sh+ph, j*sw:j*sw+pw, :] |= mask return out
[docs] def backward(self, grad): n, h, w, c = self._input_shape ph, pw = self.pool_size sh, sw = (self.stride, self.stride) if isinstance(self.stride, int) else self.stride out_h, out_w = grad.shape[1], grad.shape[2] dx = np.zeros(self._input_shape) for i in range(out_h): for j in range(out_w): patch_mask = self._max_mask[:, i*sh:i*sh+ph, j*sw:j*sw+pw, :] g = grad[:, i, j, :][:, np.newaxis, np.newaxis, :] dx[:, i*sh:i*sh+ph, j*sw:j*sw+pw, :] += patch_mask * g return dx
[docs] class AveragePooling2D(Layer): """ 2D Average Pooling layer (channels-last: N, H, W, C). """ def __init__(self, pool_size=2, stride=None, name=None): super().__init__(name=name) self.pool_size = (pool_size, pool_size) if isinstance(pool_size, int) else tuple(pool_size) self.stride = stride if stride is not None else self.pool_size
[docs] def forward(self, x, training=False): n, h, w, c = x.shape ph, pw = self.pool_size sh, sw = (self.stride, self.stride) if isinstance(self.stride, int) else self.stride out_h = (h - ph) // sh + 1 out_w = (w - pw) // sw + 1 self._input_shape = x.shape out = np.zeros((n, out_h, out_w, c)) for i in range(out_h): for j in range(out_w): out[:, i, j, :] = np.mean(x[:, i*sh:i*sh+ph, j*sw:j*sw+pw, :], axis=(1, 2)) return out
[docs] def backward(self, grad): ph, pw = self.pool_size sh, sw = (self.stride, self.stride) if isinstance(self.stride, int) else self.stride out_h, out_w = grad.shape[1], grad.shape[2] dx = np.zeros(self._input_shape) pool_area = ph * pw for i in range(out_h): for j in range(out_w): g = grad[:, i, j, :][:, np.newaxis, np.newaxis, :] dx[:, i*sh:i*sh+ph, j*sw:j*sw+pw, :] += g / pool_area return dx
[docs] class GlobalAveragePooling2D(Layer): """ Global Average Pooling — reduces (N, H, W, C) to (N, C). """
[docs] def forward(self, x, training=False): self._input_shape = x.shape return np.mean(x, axis=(1, 2))
[docs] def backward(self, grad): n, h, w, c = self._input_shape return np.broadcast_to( grad[:, np.newaxis, np.newaxis, :] / (h * w), self._input_shape ).copy()
[docs] class GlobalMaxPooling2D(Layer): """ Global Max Pooling — reduces (N, H, W, C) to (N, C). """
[docs] def forward(self, x, training=False): self._input_shape = x.shape self._input = x out = np.max(x, axis=(1, 2)) self._max_mask = (x == out[:, np.newaxis, np.newaxis, :]) return out
[docs] def backward(self, grad): return self._max_mask * grad[:, np.newaxis, np.newaxis, :]