# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # pylint: disable=invalid-name, unused-argument, unused-variable """Gradient of pooling in python""" import numpy as np def pool_grad_nchw( a_np, out_grad_np, pool_size, strides, padding, pool_type, ceil_mode, count_include_pad=True ): """pool_grad for NCHW layout in python""" dtype = a_np.dtype n, ic, ih, iw = a_np.shape kh, kw = pool_size sh, sw = strides pt, pl, pb, pr = padding pad_np = np.zeros(shape=(n, ic, ih + pt + pb, iw + pl + pr)).astype(dtype) no_zero = (range(n), range(ic), (range(pt, ih + pt)), (range(pl, iw + pl))) pad_np[np.ix_(*no_zero)] = a_np _, _, oh, ow = out_grad_np.shape pool_grad_np = np.zeros(shape=a_np.shape) pad_pool_grad_np = np.zeros(shape=pad_np.shape) if pool_type == "avg": for i in range(oh): for j in range(ow): if count_include_pad: shape = pad_np[:, :, i * sh : i * sh + kh, j * sw : j * sw + kw].shape # this can be different from kh*kw if input size cannot divide stride pad_count = shape[2] * shape[3] else: pad_count = np.sum( pad_np[:, :, i * sh : i * sh + kh, j * sw : j * sw + kw] > 0, axis=(2, 3) ) # take the first element, as they are the same across batch and channel pad_count = pad_count.ravel()[0] pad_pool_grad_np[:, :, i * sh : i * sh + kh, j * sw : j * sw + kw] += out_grad_np[ :, :, i, j ].reshape(n, ic, 1, 1) / np.maximum(pad_count, 1) elif pool_type == "max": for i in range(oh): for j in range(ow): a_patch = pad_np[:, :, i * sh : i * sh + kh, j * sw : j * sw + kw] a_patch = np.reshape(a_patch, (n, ic, -1)) max_indices = np.argmax(a_patch, axis=2) c_idx, n_idx = np.meshgrid(range(ic), range(n), sparse=True) h_idx, w_idx = np.unravel_index(max_indices, (kh, kw)) pad_pool_grad_np[:, :, i * sh : i * sh + kh, j * sw : j * sw + kw][ n_idx, c_idx, h_idx, w_idx ] += out_grad_np[n_idx, c_idx, i, j] for i in range(pool_grad_np.shape[2]): for j in range(pool_grad_np.shape[3]): pool_grad_np[:, :, i, j] = pad_pool_grad_np[:, :, i + pt, j + pl] return pool_grad_np