def max_pool_handler(converter: TensorFlowConverter, tf_op: "tf.Operation"): x = converter.get_variable(tf_op.inputs[0]) data_format = tf_op.get_attr("data_format") check_data_format(x, data_format) ksize = tuple(tf_op.get_attr("ksize")) # type: Tuple[int,...] assert ksize[x.order.axes_dict[Axis.N]] == 1 assert ksize[x.order.axes_dict[Axis.C]] == 1 ksize = (ksize[x.order.axes_dict[Axis.H]], ksize[x.order.axes_dict[Axis.W]]) stride = tuple(tf_op.get_attr("strides")) # type: Tuple[int,...] assert stride[x.order.axes_dict[Axis.N]] == 1 assert stride[x.order.axes_dict[Axis.C]] == 1 stride = (stride[x.order.axes_dict[Axis.H]], stride[x.order.axes_dict[Axis.W]]) x, padding = convolution_handler_preprocess( x, ksize=ksize, padding=tf_op.get_attr("padding"), dilation_rate=(1, 1), data_format=data_format) y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=padding, cover_all=False)(x) converter.set_variable(tf_op.outputs[0], y)
def test_irregular_size(): vx = np.random.rand(2, 4, 6, 8) vy = np.empty((2, 4, 5, 8)) KH, KW = (3, 4) SH, SW = (1, 2) PH, PW = (1, 3) for n, h2, w2, c in itertools.product(range(vy.shape[0]), range(vy.shape[1]), range(vy.shape[2]), range(vy.shape[3])): v = -float("inf") for (kh, kw) in itertools.product(range(KH), range(KW)): h1 = (h2 * SH - PH) + kh w1 = (w2 * SW - PW) + kw v = max(v, 0 if (h1 < 0 or h1 >= 4 or w1 < 0 or w1 >= 6) else vx[n, h1, w1, c]) vy[n, h2, w2, c] = v x = Variable(vx.shape, order=OrderNHWC) y, = MaxPooling2D(None, ksize=(KH, KW), stride=(SH, SW), padding=(PH, PW))(x) generate_kernel_test_case( description=f"Max Pooling with irregular window size", backend=["webgpu", "webgl", "webassembly", "fallback"], graph=Graph([x], [y]), inputs={x: vx}, expected={y: vy} )
def max_pool_handler(converter: TensorFlowConverter, tf_op: "tf.Operation"): # padding: https://www.tensorflow.org/api_guides/python/nn#Notes_on_SAME_Convolution_Padding x = converter.get_variable(tf_op.inputs[0]) # NHWC assert tf_op.get_attr("data_format") == b"NHWC" unify_order(x.order, OrderNHWC) ksize_nhwc = tf_op.get_attr("ksize") # type: List[int] assert ksize_nhwc[0] == 1 assert ksize_nhwc[3] == 1 ksize = (ksize_nhwc[1], ksize_nhwc[2]) stride_nhwc = tf_op.get_attr("strides") # type: List[int] assert stride_nhwc[0] == 1 assert stride_nhwc[3] == 1 stride_hw = stride_nhwc[1:3] padding_name = tf_op.get_attr("padding") # type: str if padding_name == b"SAME": padding = (padding_same(x.shape_dict[Axis.H], ksize[0], stride_hw[0]), padding_same(x.shape_dict[Axis.W], ksize[1], stride_hw[1])) elif padding_name == b"VALID": padding = (0, 0) else: raise NotImplementedError( f"[TensorFlowConverter] MaxPool: padding '{padding_name}' is not supported yet." ) y, = MaxPooling2D(None, ksize=ksize, stride=stride_hw, padding=padding)(x) converter.set_variable(tf_op.outputs[0], y)
def _convert_max_pooling1d(converter: KerasConverter, k_op: "keras.layers.MaxPooling1D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) # FIXME: More effective implementation y, = Reshape(None, in_order=x.order, out_order=OrderNHWC, out_shape=[x.shape[0], x.shape[1], 1, x.shape[2]])(x) if k_op.padding == "valid": padding = (0, 0) elif k_op.padding == "same": padding = (k_op.pool_size[0] // 2, 0) else: raise NotImplementedError(f"Unknown padding: {k_op.padding}") y, = MaxPooling2D(None, ksize=(k_op.pool_size[0], 1), stride=(1, 1), padding=padding)(y) z, = Reshape(None, in_order=y.order, out_order=OrderNTC, out_shape=[y.shape[0], y.shape[1], y.shape[3]])(y) converter.set_variable(converter.get_output_tensor(k_op)[0], z)
def convert_layer_maxpooling2d(self, layer_config: Dict[str, object], inputs: List[Variable]) -> List[Variable]: """ Example: {'class_name': 'MaxPooling2D', 'config': {'data_format': 'channels_last', 'name': 'max_pooling2d_1', 'padding': 'valid', 'pool_size': [2, 2], 'strides': [2, 2], 'trainable': True}}, :param layer_config: :param inputs: :return: """ assert len(inputs) == 1 input = inputs[0] name: str = layer_config["name"] ksize: Tuple[int, int] = tuple(layer_config["pool_size"]) stride: Tuple[int, int] = tuple(layer_config["strides"]) padding_keras: str = layer_config["padding"] # valid or same if padding_keras == "valid": padding = (0, 0) elif padding_keras == "same": padding = (ksize[0] // 2, ksize[1] // 2) else: raise ValueError("Unknown padding") max_pooling_2d_opr = MaxPooling2D(name, ksize=ksize, stride=stride, padding=padding) y, = max_pooling_2d_opr(input) return [y]
def _convert_max_pooling2d(converter: KerasConverter, k_op: "keras.layers.MaxPooling2D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) if k_op.data_format == "channels_first": assert x.order == OrderNCHW elif k_op.data_format == "channels_last": assert x.order == OrderNHWC else: raise ValueError( f"[KerasConverter] Unknown data format: {k_op.data_format}") ksize = tuple(k_op.pool_size) stride = tuple(k_op.strides) if k_op.padding == "valid": padding = (0, 0) elif k_op.padding == "same": padding = (ksize[0] // 2, ksize[1] // 2) else: raise ValueError(f"[KerasConverter] Unknown padding: {k_op.padding}") y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=padding)(x) converter.set_variable(converter.get_output_tensor(k_op)[0], y)
def template(x_order=OrderNHWC, y_order=OrderNHWC, description: str = ""): vx = np.random.rand(2, 4, 6, 8) vy = np.empty((2, 2, 3, 8)) KH, KW = (2, 2) SH, SW = (2, 2) PH, PW = (0, 0) for n, h2, w2, c in itertools.product(range(vy.shape[0]), range(vy.shape[1]), range(vy.shape[2]), range(vy.shape[3])): v = -float("inf") for (kh, kw) in itertools.product(range(KH), range(KW)): h1 = (h2 * SH - PH) + kh w1 = (w2 * SW - PW) + kw v = max(v, 0 if (h1 < 0 or h1 >= 4 or w1 < 0 or w1 >= 6) else vx[n, h1, w1, c]) vy[n, h2, w2, c] = v x = Variable(vx.shape, order=x_order) y, = MaxPooling2D(None, ksize=(KH, KW), stride=(SH, SW), padding=(PH, PW))(x) y.change_order(y_order) generate_kernel_test_case( description=f"Max Pooling {description}", backend=["webgpu", "webgl", "webassembly", "fallback"], graph=Graph([x], [y]), inputs={x: np.transpose(vx, [OrderNHWC.axes_dict[a] for a in x.order.axes])}, expected={y: np.transpose(vy, [OrderNHWC.axes_dict[a] for a in y.order.axes])}, )
def test_general(): vx = np.random.rand(2, 4, 6, 8) vy = np.empty((2, 3, 4, 8)) for n, h2, w2, c in itertools.product(range(vy.shape[0]), range(vy.shape[1]), range(vy.shape[2]), range(vy.shape[3])): v = -float("Infinity") for (kh, kw) in itertools.product(range(3), range(3)): h1 = (h2 * 2 - 1) + kh w1 = (w2 * 2 - 1) + kw v = max( v, 0 if (h1 < 0 or h1 >= 4 or w1 < 0 or w1 >= 6) else vx[n, h1, w1, c]) vy[n, h2, w2, c] = v x = Variable(vx.shape, order=OrderNHWC) y, = MaxPooling2D(None, ksize=3, padding=1, stride=2)(x) generate_kernel_test_case(description=f"Max Pooling", backend=["webgpu", "webassembly", "fallback"], graph=Graph([x], [y]), inputs={x: vx}, expected={y: vy})
def _convert_max_pool(converter: ONNXConverter, onnx_op: INodeProto): x = converter.get_variable(onnx_op.input[0]) x.order.unify(OrderNCHW) attrs = attribute_dict(onnx_op) ksize = list(attrs["kernel_shape"].ints) dilations = list(attrs["dilations"].ints) if any(d != 1 for d in dilations): raise NotImplementedError( "[ONNXConverter] MaxPool is supported only when dilations are 1.") stride = list(attrs["strides"].ints) pad = list(attrs["pads"].ints) if len(pad) == 2: # NOTE: # In PyTorch, pads is generated as tuple of 2 integers, but ONNX spec says that pads contains 2*N integers where N is the number of # padded dimension. It's maybe PyTorch's bug. pass else: if any(pad[2 * i] != pad[2 * i + 1] for i in range(len(pad) // 2)): raise NotImplementedError( "[ONNXConverter] odd-size padding is not supported.") pad = [pad[0], pad[2]] y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=pad)(x) converter.set_variable(onnx_op.output[0], y)
def _convert_max_pool(converter: ONNXConverter, onnx_op: INodeProto): x = converter.get_variable(onnx_op.input[0]) x.order.unify(OrderNCHW) attrs = attribute_dict(onnx_op) ksize = list(attrs["kernel_shape"].ints) stride = list(attrs["strides"].ints) pad = list(attrs["pads"].ints) if len(pad) == 2: # NOTE: # In PyTorch, pads is generated as tuple of 2 integers, but ONNX spec says that pads contains 2*N integers where N is the number of # padded dimension. It's maybe PyTorch's bug. pass else: if any(pad[2 * i] != pad[2 * i + 1] for i in range(len(pad) // 2)): raise NotImplementedError( "[ONNXConverter] odd-size padding is not supported.") pad = [pad[0], pad[2]] # https://github.com/onnx/onnx/blob/master/docs/Operators.md # output_spatial_shape[i] = floor((input_spatial_shape[i] + pad_shape[i] - kernel_spatial_shape[i]) / strides_spatial_shape[i] + 1) # In PyTorch, nn.MaxPool2d(2) with input size 11 produces output size 5, # where kernel_shape=2, pads=0, strides=2 is set as onnx attributes. # It corresponds to cover_all=False. y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=pad, cover_all=False)(x) converter.set_variable(onnx_op.output[0], y)
def max_pool_handler(converter: TensorFlowConverter, tf_op: "tf.Operation"): x = converter.get_variable(tf_op.inputs[0]) data_format = tf_op.get_attr("data_format") check_data_format(x, data_format) ksize = tuple(tf_op.get_attr("ksize")) # type: Tuple[int,...] assert ksize[x.order.axes_dict[Axis.N]] == 1 assert ksize[x.order.axes_dict[Axis.C]] == 1 ksize = (ksize[x.order.axes_dict[Axis.H]], ksize[x.order.axes_dict[Axis.W]]) stride = tuple(tf_op.get_attr("strides")) # type: Tuple[int,...] assert stride[x.order.axes_dict[Axis.N]] == 1 assert stride[x.order.axes_dict[Axis.C]] == 1 stride = (stride[x.order.axes_dict[Axis.H]], stride[x.order.axes_dict[Axis.W]]) padding = ( parse_padding(tf_op.get_attr("padding"), ksize[0], 1), parse_padding(tf_op.get_attr("padding"), ksize[1], 1), ) x, padding = convert_odd_padding_to_concat(x, padding=padding, value=-1.0e10) y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=padding, cover_all=False)(x) converter.set_variable(tf_op.outputs[0], y)
def _convert_global_max_pooling2d(converter: KerasConverter, k_op: "keras.layers.GlobalMaxPooling2D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) check_data_format(x, k_op.data_format) y, = MaxPooling2D(None, ksize=(x.shape_dict[Axis.H], x.shape_dict[Axis.W]), stride=(1, 1), padding=(0, 0))(x) # flatten without changing memory layout z = y.reshape([y.shape[0], mul(y.shape[1:])], OrderNC) converter.set_variable(converter.get_output_tensor(k_op)[0], z)
def _convert_global_max_pooling1d(converter: KerasConverter, k_op: "keras.layers.GlobalMaxPooling1D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) y = x.reshape([x.shape[0], x.shape[1], 1, x.shape[2]], OrderNHWC) y, = MaxPooling2D(None, ksize=(x.shape[1], 1), stride=(1, 1), padding=(0, 0))(y) # flatten without changing memory layout z = y.reshape([y.shape[0], mul(y.shape[1:])], OrderNC) converter.set_variable(converter.get_output_tensor(k_op)[0], z)
def __call__(self, inputs: List[Variable]) -> Tuple[Variable]: conv_opr = MaxPooling2D(generate_unique_name(self.cfunc.label), ksize=(self.cfunc.kh, self.cfunc.kw), stride=(self.cfunc.sy, self.cfunc.sx), padding=(self.cfunc.ph, self.cfunc.pw)) opr_out, = conv_opr(inputs[0]) opr_out.change_order(OrderNCHW) return opr_out,
def _convert_global_max_pooling1d(converter: KerasConverter, k_op: keras.layers.GlobalMaxPooling1D): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) # FIXME: More effective implementation y, = Reshape(None, in_order=OrderNTC, out_order=OrderNHWC, out_shape=[x.shape[0], x.shape[1], 1, x.shape[2]])(x) y, = MaxPooling2D(None, ksize=(x.shape[1], 1), stride=(1, 1), padding=(0, 0))(y) # flatten without changing memory layout z, = Reshape(None, in_order=y.order, out_order=OrderNC, out_shape=[y.shape[0], mul(y.shape[1:])])(y) converter.set_variable(converter.get_output_tensor(k_op)[0], z)
def _convert_max_pooling1d(converter: KerasConverter, k_op: "keras.layers.MaxPooling1D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) y = x.reshape([x.shape[0], x.shape[1], 1, x.shape[2]], OrderNHWC) ksize = (k_op.pool_size[0], 1) stride = (k_op.strides[0], 1) padding = (parse_padding(k_op.padding, ksize[0], 1)[0], 0) y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=padding)(y) z = y.reshape([y.shape[0], y.shape[1], y.shape[3]], OrderNTC) converter.set_variable(converter.get_output_tensor(k_op)[0], z)
def _convert_max_pooling2d(converter: KerasConverter, k_op: "keras.layers.MaxPooling2D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) check_data_format(x, k_op.data_format) ksize = tuple(k_op.pool_size) stride = tuple(k_op.strides) padding = (parse_padding(k_op.padding, ksize[0], 1), parse_padding(k_op.padding, ksize[1], 1)) y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=padding)(x) converter.set_variable(converter.get_output_tensor(k_op)[0], y)
def _convert_max_pooling2d(converter: KerasConverter, k_op: "keras.layers.MaxPooling2D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) check_data_format(x, k_op.data_format) padding = ( parse_padding(k_op.padding, k_op.pool_size[0], 1), parse_padding(k_op.padding, k_op.pool_size[1], 1) ) x, padding = convert_odd_padding_to_concat(x, padding=padding, value=-1.0e10) y, = MaxPooling2D(None, ksize=k_op.pool_size, stride=k_op.strides, padding=padding, cover_all=False)(x) converter.set_variable(converter.get_output_tensor(k_op)[0], y)
def _convert_max_pooling2d(converter: ChainerConverter, c_op: "chainer.functions.MaxPooling2D"): x = converter.get_variable(c_op.inputs[0]) x.order.unify(OrderNCHW) pool_opr = MaxPooling2D(None, ksize=(c_op.kh, c_op.kw), stride=(c_op.sy, c_op.sx), padding=(c_op.ph, c_op.pw), cover_all=c_op.cover_all) y, = pool_opr(x) converter.set_variable(c_op.outputs[0](), y)
def main(k, s, p, n, h1, w1, c1, expected_shape_dict: Dict[Axis, int]): orders = [OrderNHWC, OrderHWNC, OrderHWCN, OrderNCHW, OrderCNHW, OrderCHWN] for order_x in orders: op = MaxPooling2D(None, ksize=k, stride=s, padding=p) x = Variable((n, h1, w1, c1), OrderNHWC) x.change_order(order_x) y, = op(x) for axis in y.order.axes: assert y.shape_dict[axis] == expected_shape_dict[axis]
def _convert_max_pooling2d(converter: KerasConverter, k_op: "keras.layers.MaxPooling2D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) x, padding = convolution_handler_preprocess(x, ksize=k_op.pool_size, padding=k_op.padding, dilation_rate=(1, 1), data_format=k_op.data_format) y, = MaxPooling2D(None, ksize=k_op.pool_size, stride=k_op.strides, padding=padding, cover_all=False)(x) converter.set_variable(converter.get_output_tensor(k_op)[0], y)
def _convert_max_pooling2d(converter: ChainerConverter, c_op: "chainer.functions.MaxPooling2D"): if not c_op.cover_all: raise NotImplementedError("'cover_all=False' property in 'MaxPooling2D' is not supported.") x = converter.get_variable(c_op.inputs[0]) unify_order(x.order, OrderNCHW) pool_opr = MaxPooling2D(None, ksize=(c_op.kh, c_op.kw), stride=(c_op.sy, c_op.sx), padding=(c_op.ph, c_op.pw)) y, = pool_opr(x) converter.set_variable(c_op.outputs[0](), y)
def _convert_global_max_pooling2d(converter: KerasConverter, k_op: keras.layers.GlobalMaxPooling2D): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) if k_op.data_format == "channels_first": assert x.order == OrderNCHW elif k_op.data_format == "channels_last": assert x.order == OrderNHWC else: raise ValueError(f"[KerasConverter] Unknown data format: {k_op.data_format}") y, = MaxPooling2D(None, ksize=(x.shape_dict[Axis.H], x.shape_dict[Axis.W]), stride=(1, 1), padding=(0, 0))(x) # flatten without changing memory layout z, = Reshape(None, in_order=y.order, out_order=OrderNC, out_shape=[y.shape[0], mul(y.shape[1:])])(y) converter.set_variable(converter.get_output_tensor(k_op)[0], z)
def _convert_max_pooling2d(converter: ChainerConverter, c_op: "chainer.functions.MaxPooling2D"): if not c_op.cover_all: raise NotImplementedError("'cover_all=False' property in 'MaxPooling2D' is not supported.") x = converter.get_variable(c_op.inputs[0]) x.order.unify(OrderNCHW) pool_opr = MaxPooling2D(None, ksize=(c_op.kh, c_op.kw), stride=(c_op.sy, c_op.sx), padding=(c_op.ph, c_op.pw)) if c_op.cover_all == False: console.warning( "[MaxPooling2D] MaxPooling2D in WebDNN is always calculated as cover_all=True mode. " "Therefore the result may be difference from chainer's output.") y, = pool_opr(x) converter.set_variable(c_op.outputs[0](), y)
def _convert_max_pooling2d(converter: KerasConverter, k_op: "keras.layers.MaxPooling2D"): x = converter.get_variable(converter.get_input_tensor(k_op)[0]) if k_op.data_format == "channels_first": x.order.unify(OrderNCHW) elif k_op.data_format == "channels_last": x.order.unify(OrderNHWC) else: raise ValueError( f"[KerasConverter] Unknown data format: {k_op.data_format}") ksize = tuple(k_op.pool_size) stride = tuple(k_op.strides) if k_op.padding == "valid": padding = (0, 0) elif k_op.padding == "same": # https://www.tensorflow.org/api_guides/python/nn#convolution if x.shape_dict[Axis.H] % stride[0] == 0: pad_h = max(ksize[0] - stride[0], 0) else: pad_h = max(ksize[0] - (x.shape_dict[Axis.H] % stride[0]), 0) if x.shape_dict[Axis.W] % stride[1] == 0: pad_w = max(ksize[1] - stride[1], 0) else: pad_w = max(ksize[1] - (x.shape_dict[Axis.W] % stride[1]), 0) padding = (pad_h // 2, pad_w // 2) else: raise ValueError(f"[KerasConverter] Unknown padding: {k_op.padding}") y, = MaxPooling2D(None, ksize=ksize, stride=stride, padding=padding)(x) converter.set_variable(converter.get_output_tensor(k_op)[0], y)