Files
2026-07-13 13:30:25 +08:00

94 lines
3.1 KiB
Python
Executable File

import numpy as np
from prml.nn.tensor.tensor import Tensor
from prml.nn.function import Function
from prml.nn.image.util import img2patch, patch2img
class MaxPooling2d(Function):
def __init__(self, pool_size, stride, pad):
"""
construct 2 dimensional max-pooling function
Parameters
----------
pool_size : int or tuple of ints
pooling size
stride : int or tuple of ints
stride of kernel application
pad : int or tuple of ints
padding image
"""
self.pool_size = self._check_tuple(pool_size, "pool_size")
self.stride = self._check_tuple(stride, "stride")
self.pad = self._check_tuple(pad, "pad")
self.pad = (0,) + self.pad + (0,)
def _check_tuple(self, tup, name):
if isinstance(tup, int):
tup = (tup,) * 2
if not isinstance(tup, tuple):
raise TypeError(
"Unsupported type for {}: {}".format(name, type(tup))
)
if len(tup) != 2:
raise ValueError(
"the length of {} must be 2, not {}".format(name, len(tup))
)
if not all([isinstance(n, int) for n in tup]):
raise TypeError(
"Unsuported type for {}".format(name)
)
if not all([n >= 0 for n in tup]):
raise ValueError(
"{} must be non-negative values".format(name)
)
return tup
def forward(self, x):
x = self._convert2tensor(x)
self._equal_ndim(x, 4)
self.x = x
img = np.pad(x.value, [(p,) for p in self.pad], "constant")
patch = img2patch(img, self.pool_size, self.stride)
n_batch, xlen_out, ylen_out, _, _, in_channels = patch.shape
patch = patch.reshape(n_batch, xlen_out, ylen_out, -1, in_channels)
self.shape = img.shape
self.index = patch.argmax(axis=3)
return Tensor(patch.max(axis=3), function=self)
def backward(self, delta):
delta_patch = np.zeros(delta.shape + (np.prod(self.pool_size),))
index = np.where(delta == delta) + (self.index.ravel(),)
delta_patch[index] = delta.ravel()
delta_patch = np.reshape(delta_patch, delta.shape + self.pool_size)
delta_patch = delta_patch.transpose(0, 1, 2, 4, 5, 3)
dx = patch2img(delta_patch, self.stride, self.shape)
slices = [slice(p, len_ - p) for p, len_ in zip(self.pad, self.shape)]
dx = dx[slices]
self.x.backward(dx)
def max_pooling2d(x, pool_size, stride=1, pad=0):
"""
spatial max pooling
Parameters
----------
x : (n_batch, xlen, ylen, in_channel) Tensor
input tensor
pool_size : int or tuple of ints (kx, ky)
pooling size
stride : int or tuple of ints (sx, sy)
stride of pooling application
pad : int or tuple of ints (px, py)
padding input
Returns
-------
output : (n_batch, xlen', ylen', out_channel) Tensor
max pooled image
len' = (len + p - k) // s + 1
"""
return MaxPooling2d(pool_size, stride, pad).forward(x)