diff --git a/extra/onnx_ops.py b/extra/onnx_ops.py index 745f67c8ba..a0cdd2916d 100644 --- a/extra/onnx_ops.py +++ b/extra/onnx_ops.py @@ -242,24 +242,25 @@ def _format_padding(onnx_pads, ndims=None, axes=None): return np_pads def _padding(X: Tensor, pads=None, auto_pad="NOTSET", axes=None, constant_value=0., strides=None, kernel_shape=None, dilations=None, ceil_mode=0): - if auto_pad != "NOTSET": pads = _auto_pad(X, auto_pad, strides, kernel_shape, dilations, ceil_mode) + if auto_pad != "NOTSET": pads = _auto_pad(X, auto_pad, strides, kernel_shape, dilations) + elif ceil_mode and auto_pad=="NOTSET": # stupid ceil_mode case + if strides is not None: strides = [strides]*len(kernel_shape) if isinstance(strides, int) else strides if strides else [1]*len(kernel_shape) + if dilations is not None: dilations = [1]*len(kernel_shape) if dilations == 1 else dilations + out_spatial_shape = [math.ceil((sh - dil * (ker-1)-1)/st + 1) if ceil_mode else math.floor((sh - dil * (ker-1)-1)/st + 1) for sh, st, ker, dil in zip(X.shape[-len(kernel_shape):], strides, kernel_shape, dilations)] + pad_shape = [(osh-1)*st+((ks-1)*dil+1)-ish for osh, st, ks, dil, ish in zip(out_spatial_shape, strides, kernel_shape, dilations, X.shape[-len(kernel_shape):])] + pad_shape = flatten([[sh//2, sh-sh//2] for sh in pad_shape]) + pads = pad_shape[::2] + pad_shape[1::2] if pads is None: return X pads = _format_padding(pads, ndims=len(X.shape), axes=axes) return X.pad(tuple(pads), value=constant_value) -# TODO works but hacky and messy, think of cleaner way to do this -def _auto_pad(X: Tensor, auto_pad, strides, kernel_shape, dilations, ceil_mode=0): +def _auto_pad(X: Tensor, auto_pad, strides, kernel_shape, dilations): strides = [strides]*len(kernel_shape) if isinstance(strides, int) else strides if strides else [1]*len(kernel_shape) dilations = [1]*len(kernel_shape) if dilations == 1 else dilations if auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": pad_shape = [(math.ceil(sh/st)-1)*st+((ks-1)*di+1)-sh for sh, st, ks, di in zip(X.shape[-len(kernel_shape):], strides, kernel_shape, dilations)] pad_shape = flatten([[sh//2, sh-sh//2] for sh in pad_shape]) return pad_shape[::2] + pad_shape[1::2] if auto_pad == "SAME_UPPER" else pad_shape[1::2] + pad_shape[::2] - elif auto_pad == "VALID": - out_spatial_shape = [math.ceil((sh - ((ker-1)*dil+1)) / st) + 1 if ceil_mode else math.floor((sh - ((ker-1)*dil+1)) / st) + 1 for sh, st, ker, dil in zip(X.shape[-len(kernel_shape):], strides, kernel_shape, dilations)] - pad_shape = [(osh-1)*st+((ks-1)*dil+1)-ish for osh, st, ks, dil, ish in zip(out_spatial_shape, strides, kernel_shape, dilations, X.shape[-len(kernel_shape):])] - pad_shape = flatten([[sh//2, sh-sh//2] for sh in pad_shape]) - return pad_shape[::2] + pad_shape[1::2] else: raise NotImplementedError(f"auto_pad={auto_pad} not implemented") def Pad(x: Tensor, pads: Union[Tensor, Tuple[int, ...]], constant_value: Tensor=None, axes: Tensor=None, mode="constant", value: float=0.): @@ -306,19 +307,16 @@ def Pad(x: Tensor, pads: Union[Tensor, Tuple[int, ...]], constant_value: Tensor= return _padding(x, seq_pads, axes=seq_axes, constant_value=constant_value) def AveragePool(X: Tensor, kernel_shape, auto_pad="NOTSET", ceil_mode=0, count_include_pad=0, dilations=1, pads=None, strides=1): - if dilations != 1: raise NotImplementedError(f"dilations != 1 not supported, dilations:{dilations}") - pixel_axes = tuple(range(len(X.shape)))[-len(kernel_shape):] - if ceil_mode: auto_pad = "SAME_UPPER" - padding_included = _padding(X, pads, auto_pad, axes=pixel_axes, strides=strides, kernel_shape=kernel_shape, dilations=dilations).avg_pool2d(kernel_shape, stride=strides) + pixel_axes = tuple(range(len(X.shape)))[2:] + ret = _padding(X, pads, auto_pad, axes=pixel_axes, strides=strides, kernel_shape=kernel_shape, dilations=dilations, ceil_mode=ceil_mode).avg_pool2d(kernel_shape, stride=strides, dilation=dilations) if count_include_pad: - return padding_included + return ret else: - div = _padding(Tensor.ones(*X.shape), pads, auto_pad, axes=pixel_axes, strides=strides, kernel_shape=kernel_shape, dilations=dilations).avg_pool2d(kernel_shape, stride=strides) - return padding_included / div + div = _padding(Tensor.ones(*X.shape), pads, auto_pad, axes=pixel_axes, strides=strides, kernel_shape=kernel_shape, dilations=dilations, ceil_mode=ceil_mode).avg_pool2d(kernel_shape, stride=strides, dilation=dilations) + return ret / div def MaxPool(X: Tensor, kernel_shape, auto_pad="NOTSET", ceil_mode=0, dilations=1, pads=None, storage_order=0, strides=1): - if ceil_mode and auto_pad == "NOTSET": auto_pad="VALID" - ret = _padding(X, pads, auto_pad, constant_value=float("-inf"), axes=tuple(range(len(X.shape)))[-len(kernel_shape):], strides=strides, kernel_shape=kernel_shape, dilations=dilations, ceil_mode=ceil_mode) + ret = _padding(X, pads, auto_pad, constant_value=float("-inf"), axes=tuple(range(len(X.shape)))[2:], strides=strides, kernel_shape=kernel_shape, dilations=dilations, ceil_mode=ceil_mode) ret = ret.max_pool2d(kernel_shape, stride=strides, dilation=dilations) ret_len, X_len = ret.numel(), X.numel() indices = ((ret.flatten().unsqueeze(1).expand(ret_len, X_len) == X.flatten().reshape(1, X_len).expand(ret_len, X_len)) * Tensor.arange(X_len).reshape(1, X_len).expand(ret_len, X_len)).sum(1).reshape(ret.shape).cast(dtypes.int64) diff --git a/test/external/external_test_onnx_backend.py b/test/external/external_test_onnx_backend.py index 45fc51220b..6c1d139c5c 100644 --- a/test/external/external_test_onnx_backend.py +++ b/test/external/external_test_onnx_backend.py @@ -145,8 +145,6 @@ backend_test.exclude('test_resize_upsample_scales_cubic_*') # unsure how to impl backend_test.exclude('test_resize_upsample_sizes_cubic_*') # unsure how to implement cubic # rest of the failing tests -backend_test.exclude('test_averagepool_2d_dilations_*') # dilations != 1 not supported for avgpool in tensor.py -backend_test.exclude('test_averagepool_3d_dilations_*') # dilations != 1 not supported for avgpool in tensor.py backend_test.exclude('test_regex_*') # does not support string Tensors backend_test.exclude('test_convtranspose_autopad_same_cpu') # TODO geohotstan has no idea how this is done, autopad requires output_shape but output_shape requires pads from autopad backend_test.exclude('test_optional_has_element_empty_optional_input_cpu') # Attempts to create Tensor from None diff --git a/tinygrad/tensor.py b/tinygrad/tensor.py index 6d14c10148..50aa5eb48b 100644 --- a/tinygrad/tensor.py +++ b/tinygrad/tensor.py @@ -497,7 +497,7 @@ class Tensor: return xup.permute(*range(len(prefix)), *[len(prefix)+i*2 for i in range(len(k_))], *[len(prefix)+i*2+1 for i in range(len(k_))]) # NOTE: these work for more than 2D - def avg_pool2d(self, kernel_size=(2,2), stride=None): return self._pool(make_pair(kernel_size), stride if stride is not None else kernel_size).mean(axis=tuple(range(0-len(make_pair(kernel_size)), 0))) + def avg_pool2d(self, kernel_size=(2,2), stride=None, dilation=1): return self._pool(make_pair(kernel_size), stride if stride is not None else kernel_size, dilation).mean(axis=tuple(range(0-len(make_pair(kernel_size)), 0))) def max_pool2d(self, kernel_size=(2,2), stride=None, dilation=1): return self._pool(make_pair(kernel_size), stride if stride is not None else kernel_size, dilation).max(axis=tuple(range(0-len(make_pair(kernel_size)), 0))) def conv_transpose2d(self, weight:Tensor, bias:Optional[Tensor]=None, groups=1, stride=1, dilation=1, padding=0, output_padding=0) -> Tensor: