# Copyright 2019 Huawei Technologies Co., Ltd # # Licensed 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. import sys import numpy as np def random_gaussian(size, miu=3, sigma=1): """ Generate random array with absolution value obeys gaussian distribution """ if sigma <= 0: sys.stderr.write("Error: Expect positive sigmal for gaussian distribution. but get %f\n" % sigma) sys.exit(1) rgn = np.random.RandomState(2019) ret = rgn.normal(miu, sigma, size) for x in np.nditer(ret, op_flags=['readwrite']): if np.random.randint(0, 2): continue x[...] = x * -1 return ret def conv_param_prepare(conv_param): stride = 1 pad = 0 dilation = 1 if 'stride' in conv_param: stride = conv_param['stride'] if 'pad' in conv_param: pad = conv_param['pad'] if 'dilation' in conv_param: dilation = conv_param['dilation'] if isinstance(stride, int): stride = [stride] * 2 elif isinstance(stride, (list, tuple)) and 1 == len(stride): stride = list(stride) * 2 elif isinstance(stride, (list, tuple)) and 2 == len(stride): pass else: raise RuntimeError('stride para illegal !!!') if isinstance(pad, int): pad = [pad] * 4 elif isinstance(pad, (list, tuple)) and 1 == len(pad): pad = list(pad) * 4 elif isinstance(pad, (list, tuple)) and 4 == len(pad): pass else: raise RuntimeError('pad para illegal !!!') if isinstance(dilation, int): dilation = [dilation] * 2 elif isinstance(dilation, (list, tuple)) and 1 == len(dilation): dilation = list(dilation) * 2 elif isinstance(dilation, (list, tuple)) and 2 == len(dilation): pass else: raise RuntimeError('dilation para illegal !!!') return stride, pad, dilation def conv_shape_4d(fm_shape, w_shape, pad, stride, dilation): S_h, S_w = stride P_top, P_bottom, P_left, P_right = pad D_h, D_w = dilation IN, IC, IH, IW = fm_shape C0 = 16 IC = ((IC + C0 - 1) // C0) * C0 WN, WC, WH, WW = w_shape WN = ((WN + C0 - 1) // C0) * C0 WC = ((WC + C0 - 1) // C0) * C0 ON = IN OC = WN WHD = (WH - 1) * D_h + 1 WWD = (WW - 1) * D_w + 1 OH = (IH + P_top + P_bottom - WHD) // S_h + 1 OW = (IW + P_left + P_right - WWD) // S_w + 1 fm_shape = [IN, IC, IH, IW] w_shape = [WN, WC, WH, WW] out_shape = [ON, OC, OH, OW] return fm_shape, w_shape, out_shape def conv_tensor_4d_to_5d(x, w, b, out): IN, IC, IH, IW = x.shape WN, WC, WH, WW = w.shape ON, OC, OH, OW = out.shape C0 =16 ''' transpose to 5D - NC1HWC0 ''' feature = x.reshape(IN, IC // C0, C0, IH, IW).transpose(0, 1, 3, 4, 2).copy() ''' transpose to 5D - C1HWNC0 ''' filter = w.reshape(WN, WC // C0, C0, WH, WW).transpose(1, 3, 4, 0, 2).copy() filter = filter.reshape(WC // C0 * WH * WW, WN // 16, 16, C0) bb = b.reshape(1, WN // 16, 1, 1, 16) ''' transpose to 5D - NC1HWC0 ''' output = out.reshape(ON, OC // C0, C0, OH, OW).transpose(0, 1, 3, 4, 2).copy() return feature, filter, bb, output def conv_forward_naive(x, w, b, conv_param): stride, pad, dilation = conv_param_prepare(conv_param) P_top, P_bottom, P_left, P_right = pad[0:4] S_h, S_w = stride[0:2] D_h, D_w = dilation[0:2] fm_shape, w_shape, out_shape = conv_shape_4d(x.shape, w.shape, pad, stride, dilation) N, C, H, W = fm_shape WN, WC, WH, WW = w_shape _, F, Ho, Wo = out_shape WHD = (WH - 1) * D_h + 1 WWD = (WW - 1) * D_w + 1 x_pad = np.zeros((N, C, H + P_top + P_bottom, W + P_left + P_right)) x_pad[:, :, P_top:P_top + H, P_left:P_left + W] = x out = np.zeros((N, F, Ho, Wo)) for f in range(F): for i in range(Ho): for j in range(Wo): # N*C*HH*WW, C*HH*WW = N*C*HH*WW, sum -> N*1 out[:, f, i, j] = np.sum(x_pad[:, :, i * S_h: i * S_h + WHD: D_h, j * S_w: j * S_w + WWD: D_w] * w[f, :, :, :], axis=(1, 2, 3)) if b is not None: out[:, f, :, :] += b[f] return out