Source code for helios.nn.layers.pool
import numpy as np
import numpy.typing as npt
import torch
import torch.nn.functional as F
from torch import nn
[docs]
class AdaptiveAvgPool2d(nn.Module):
r"""
Applies a 2D adaptive average pooling over an input signal composed of several input planes.
The output is of size :math:`H \times W`, for any input size. The number of output
features is equal to the number of input planes.
.. note::
This class is a re-implementation of ``torch.nn.AdaptiveAvgPool2d`` that can be
exported to ONNX and serves as a drop-in replacement for torch's version.
Args:
output_size: the target output size of the image of the form :math:`H \times W`.
Can be a tuple ``(H, W)`` or a single ``H`` for a square image :math:`H \times
H`. ``H`` and ``W`` can be either a ``int``, or ``None`` which means the size
will be the same as that of the input.
""" # noqa: E501
def __init__(self, output_size: int | tuple[int | None, int | None]):
"""Create the pool."""
super().__init__()
self._validate_output_size(output_size)
if isinstance(output_size, int):
output_size = (output_size, output_size)
self._output_size = output_size
def _validate_output_size(
self, output_size: int | tuple[int | None, int | None]
) -> None:
if not isinstance(output_size, int | tuple):
raise RuntimeError("error: output_size must be an int or a tuple")
if isinstance(output_size, tuple):
if len(output_size) != 2:
raise RuntimeError(
f"error: output_size must have size 2 but received {len(output_size)}"
)
if any(not isinstance(o, int | None) for o in output_size):
raise RuntimeError("error: expected a tuple of int or None")
def _get_output_size(self, x: torch.Tensor) -> npt.NDArray:
x_size = np.array(x.shape[-2:])
if self._output_size is None:
return x_size
# At this point, the output size should just be a tuple
assert isinstance(self._output_size, tuple)
x_size[0] = (
self._output_size[0] if self._output_size[0] is not None else x_size[0]
)
x_size[1] = (
self._output_size[1] if self._output_size[1] is not None else x_size[1]
)
return x_size
[docs]
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Evaluate the adaptive average pool on the input.
Args:
x: the input tensor.
Returns:
The pooled tensor.
"""
output_size = self._get_output_size(x)
stride_size = np.floor(np.array(x.shape[-2:]) / output_size).astype(np.int32)
kernel_size = np.array(x.shape[-2:]) - (output_size - 1) * stride_size # type: ignore[operator]
x = F.avg_pool2d(x, kernel_size=list(kernel_size), stride=list(stride_size))
return x