Source code for webdnn.graph.operators.convolution2d

from typing import Tuple, Optional

from webdnn.graph.axis import Axis
from webdnn.graph.operator import Operator
from webdnn.graph.operators.attributes.tensorwise import Tensorwise
from webdnn.graph.operators.util import IntOrTuple, to_tuple
from webdnn.graph.order import OrderNHWC, OrderNCHW, Order
from webdnn.graph.placeholder import Placeholder
from webdnn.graph.variable import Variable
from webdnn.util.assertion import assert_sequence_type


[docs]class Convolution2D(Operator): """Convolution2D(name, ksize, stride, padding, dilation_rate=1) Spatial convolution operator. Args: name (str): Operator name. ksize (int or tuple of int): Kernel size. stride (int or tuple of int): Stride size. padding (int or tuple of int): Padding size. dilation_rate (int or tuple of int): Dilation rate. 1 means ordinary convolution. Input pixels are shifted by (dilation_rate - 1) pixels. Signature .. code:: y, = op(x, w) - **x** - Input variables. It must has 4 axes, :obj:`~webdnn.Axis.N`, :obj:`~webdnn.Axis.C`, :obj:`~webdnn.Axis.H`, and :obj:`~webdnn.Axis.W`. - **w** - Kernel variable. It must has :obj:`~webdnn.Axis.N`, :obj:`~webdnn.Axis.C`, :obj:`~webdnn.Axis.H`, :obj:`~webdnn.Axis.W`. Its size of :obj:`~webdnn.Axis.H` and :obj:`~webdnn.Axis.W` must be same as kernel size. Its size of :obj:`~webdnn.Axis.C` must be same as :code:`x`. - **y** - Output variable. Its order is same as :code:`x`. """ def __init__(self, name: Optional[str], ksize: IntOrTuple, stride: IntOrTuple, padding: IntOrTuple, dilation_rate: Optional[IntOrTuple] = 1): super().__init__(name) self.parameters["ksize"] = assert_sequence_type(to_tuple(ksize), (int, Placeholder), message=f""" [Convolution2D] Parameter "ksize" must be integer or tuple of integer""") self.parameters["stride"] = assert_sequence_type(to_tuple(stride), (int, Placeholder), message=f""" [Convolution2D] Parameter "stride" must be integer or tuple of integer""") self.parameters["padding"] = assert_sequence_type(to_tuple(padding), (int, Placeholder), message=f""" [Convolution2D] Parameter "padding" must be integer or tuple of integer""") self.parameters["dilation_rate"] = assert_sequence_type(to_tuple(dilation_rate), (int, Placeholder), message=f""" [Convolution2D] Parameter "dilation_rate" must be integer or tuple of integer""") self.attributes.add(Tensorwise(Axis.N)) def __call__(self, x: Variable, w: Variable) -> Tuple[Variable]: assert x.order.check_same_axes(OrderNCHW), f""" [Convolution2D] Input variable of Convolution2D must have N, C, H, and W axes: (x.order.axes) = {x.order.axes}""" assert w.order.check_same_axes(Order([Axis.N, Axis.KH, Axis.KW, Axis.C])), f""" [Convolution2D] Kernel variable of Convolution2D must have N, C, KH, and KW axes: (w.order.axes) = {w.order.axes}""" if Placeholder.check_resolved(w.shape_dict[Axis.KH]) and Placeholder.check_resolved(w.shape_dict[Axis.KW]): assert (w.shape_dict[Axis.KH], w.shape_dict[Axis.KW]) == self.ksize, f""" [Convolution2D] Kernel variable of Convolution2D must be same spatial size as ksize parameter: (w.shape_dict[Axis.KH]) = {w.shape_dict[Axis.KH]} (w.shape_dict[Axis.KW]) = {w.shape_dict[Axis.KW]} (self.ksize) = {self.ksize}""" if Placeholder.check_resolved(w.shape_dict[Axis.C]) and Placeholder.check_resolved(x.shape_dict[Axis.C]): assert w.shape_dict[Axis.C] == x.shape_dict[Axis.C], f""" [Convolution2D] Input and Kernel variables of Convolution2D must be same channel size: (x.shape_dict[Axis.C]) = {x.shape_dict[Axis.C]} (w.shape_dict[Axis.C]) = {w.shape_dict[Axis.C]}""" N = x.shape_dict[Axis.N] H2 = (x.shape_dict[Axis.H] + 2 * self.PH - self.WH) // self.SH + 1 W2 = (x.shape_dict[Axis.W] + 2 * self.PW - self.WW) // self.SW + 1 C2 = w.shape_dict[Axis.N] y = Variable([N, H2, W2, C2], OrderNHWC) y.change_order(x.order) # output same order as input to preserve following reshape semantics self.append_input("x", x) self.append_input("w", w) self.append_output("y", y) return y, @property def ksize(self) -> Tuple[int, int]: return self.parameters["ksize"] @property def stride(self) -> Tuple[int, int]: return self.parameters["stride"] @property def padding(self) -> Tuple[int, int]: return self.parameters["padding"] @property def dilation_rate(self) -> Tuple[int, int]: return self.parameters["dilation_rate"] @property def KH(self) -> int: return self.ksize[0] @property def KW(self) -> int: return self.ksize[1] @property def SH(self) -> int: return self.stride[0] @property def SW(self) -> int: return self.stride[1] @property def PH(self) -> int: return self.padding[0] @property def PW(self) -> int: return self.padding[1] @property def DH(self) -> int: return self.dilation_rate[0] @property def DW(self) -> int: return self.dilation_rate[1] @property def WH(self) -> int: return self.DH * (self.KH - 1) + 1 @property def WW(self) -> int: return self.DW * (self.KW - 1) + 1