| 25 | using namespace MNN; |
| 26 | using namespace MNN::Express; |
| 27 | static void reference_conv2d(const std::vector<float>& input, const std::vector<float>& weight, |
| 28 | const std::vector<float>& bias, std::vector<float>& output, std::vector<float>& outputDataSeparateBias, int batch, int ic, int oc, |
| 29 | int ih, int iw, PadMode mode, int pad_h, int pad_w, int kh, int kw, int stride, |
| 30 | int dilation, int group, ConvertFP32 functor) { |
| 31 | int oh, ow; |
| 32 | if (mode == PadMode_SAME) { |
| 33 | oh = (ih + stride - 1) / stride; // oh = ceil(ih / stride) |
| 34 | ow = (iw + stride - 1) / stride; // ow = ceil(iw / stride) |
| 35 | pad_h = ((oh - 1) * stride + (kh - 1) * dilation + 1 - ih) / 2; |
| 36 | pad_w = ((ow - 1) * stride + (kw - 1) * dilation + 1 - iw) / 2; |
| 37 | } else { |
| 38 | if (mode == PadMode_VALID) { |
| 39 | pad_h = pad_w = 0; |
| 40 | } |
| 41 | oh = (ih + 2 * pad_h - (kh - 1) * dilation - 1) / stride + 1; |
| 42 | ow = (iw + 2 * pad_w - (kw - 1) * dilation - 1) / stride + 1; |
| 43 | } |
| 44 | |
| 45 | MNN_ASSERT(oc % group == 0 && ic % group == 0); |
| 46 | if (oh <= 0 || ow <= 0) { |
| 47 | output.clear(); |
| 48 | return; |
| 49 | } |
| 50 | output.resize(batch * oh * ow * oc); |
| 51 | /* |
| 52 | In CPUConvolutionDepthwise, bias function 'MNNAxByClampBroadcastUnit' is called separately with MNNConvRunForLineDepthwise, |
| 53 | this would affect the precision when using bf16 or fp16. |
| 54 | winograd convolution also did this. |
| 55 | we keep the two result for checking. |
| 56 | */ |
| 57 | outputDataSeparateBias.resize(batch * oh * ow * oc); |
| 58 | |
| 59 | int ocGroup = oc / group, icGroup = ic / group; |
| 60 | for (int b = 0; b < batch; ++b) { |
| 61 | for (int oz = 0; oz < oc; ++oz) { |
| 62 | int gId = oz / ocGroup; |
| 63 | for (int oy = 0; oy < oh; ++oy) { |
| 64 | for (int ox = 0; ox < ow; ++ox) { |
| 65 | float sum = 0; |
| 66 | auto destOffset = ((b * oc + oz) * oh + oy) * ow + ox; |
| 67 | for (int sz = gId * icGroup; sz < (gId + 1) * icGroup; ++sz) { |
| 68 | for (int ky = 0; ky < kh; ++ky) { |
| 69 | for (int kx = 0; kx < kw; ++kx) { |
| 70 | int ix = ox * stride + kx * dilation - pad_w, iy = oy * stride + ky * dilation - pad_h; |
| 71 | float xValue = 0.0f; |
| 72 | if (ix >= 0 && ix < iw && iy >= 0 && iy < ih) { |
| 73 | xValue = input[(((b * ic + sz) * ih + iy) * iw + ix)]; |
| 74 | } |
| 75 | float convertX = functor(xValue); |
| 76 | float convertW = functor(weight[(((gId * ocGroup + oz % ocGroup) * icGroup + sz % icGroup) * kh + ky) * kw + kx]); |
| 77 | sum += convertX * convertW; |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | output[destOffset] = functor(sum + functor(bias[oz])); |
| 83 | outputDataSeparateBias[destOffset] = functor(functor(sum) + functor(bias[oz])); |
| 84 | } |