#include "./network.h" #include "megbrain/opr/tensor_manip.h" using namespace mgb; SymbolVar Network::add_conv( SymbolVar f, size_t output_channels, KernSize kern_size, DType out_dtype, bool has_relu, Stride stride, Padding padding) { static int weight_idx = 0; static int bias_idx = 0; size_t input_channels = f.node()->shape()[1]; auto weight = add_cvar( ssprintf("w%d", weight_idx).c_str(), {output_channels, input_channels, kern_size[0], kern_size[1]}); auto bias = add_cvar(ssprintf("b%d", bias_idx).c_str(), {1, output_channels, 1, 1}); if (out_dtype.category() == DTypeCategory::QUANTIZED) { weight = add_type_cvt(weight, out_dtype); bias = add_type_cvt(bias, dtype::QuantizedS32{1.f}); } opr::ConvBias::Param param; param.stride_h = stride[0], param.stride_w = stride[1]; param.pad_h = padding[0], param.pad_w = padding[1]; if (has_relu) { param.nonlineMode = opr::ConvBias::Param::NonlineMode::RELU; } else { param.nonlineMode = opr::ConvBias::Param::NonlineMode::IDENTITY; } SymbolVar conv; if (out_dtype.category() == DTypeCategory::QUANTIZED) { conv = opr::ConvBias::make( f, weight, bias, param, {}, OperatorNodeConfig{out_dtype}); } else { conv = opr::ConvBias::make(f, weight, bias, param, {}); } weight_idx++; bias_idx++; return conv; } SymbolVar Network::add_group_conv( SymbolVar f, size_t output_channels, size_t groups, KernSize kern_size, DType out_dtype, bool has_relu, Stride stride, Padding padding) { static int weight_idx = 0; static int bias_idx = 0; size_t input_channels = f.node()->shape()[1]; auto weight = add_cvar( ssprintf("w%d", weight_idx).c_str(), {groups, output_channels / groups, input_channels / groups, kern_size[0], kern_size[1]}); auto bias = add_cvar(ssprintf("b%d", bias_idx).c_str(), {1, output_channels, 1, 1}); if (out_dtype.category() == DTypeCategory::QUANTIZED) { weight = add_type_cvt(weight, out_dtype); bias = add_type_cvt(bias, dtype::QuantizedS32{1.f}); } opr::ConvBias::Param param; param.sparse = opr::ConvBias::Param::Sparse::GROUP; param.stride_h = stride[0], param.stride_w = stride[1]; param.pad_h = padding[0], param.pad_w = padding[1]; if (has_relu) { param.nonlineMode = opr::ConvBias::Param::NonlineMode::RELU; } else { param.nonlineMode = opr::ConvBias::Param::NonlineMode::IDENTITY; } weight_idx++; bias_idx++; SymbolVar conv; if (out_dtype.category() == DTypeCategory::QUANTIZED) { conv = opr::ConvBias::make( f, weight, bias, param, {}, OperatorNodeConfig{out_dtype}); } else { conv = opr::ConvBias::make(f, weight, bias, param, {}); } weight_idx++; bias_idx++; return conv; } SymbolVar Network::add_deconv( SymbolVar f, size_t ratio, size_t output_channels, DType out_dtype) { static int weight_idx = 0; size_t kernel = ratio * 2 - ratio % 2; size_t pad = ratio / 2; size_t input_channels = f.node()->shape()[1]; auto weight = add_cvar( ssprintf("w%d", weight_idx).c_str(), {input_channels, output_channels, kernel, kernel}); if (out_dtype.category() == DTypeCategory::QUANTIZED) { weight = add_type_cvt(weight, out_dtype); } opr::ConvolutionBackwardData::Param param; param.stride_h = param.stride_w = ratio; param.pad_h = param.pad_w = pad; auto deconv = opr::ConvolutionBackwardData::make( weight, f, param, {}, OperatorNodeConfig{out_dtype}); weight_idx++; return deconv; } SymbolVar Network::add_elemwise( const SymbolVarArray inps, DType out_dtype, opr::Elemwise::Param::Mode mode) { using ElemMode = opr::Elemwise::Param::Mode; using MultiMode = opr::ElemwiseMultiType::Param::Mode; static const ThinHashMap map = { {ElemMode::ADD, MultiMode::QADD}, {ElemMode::FUSE_ADD_RELU, MultiMode::QFUSE_ADD_RELU}}; if (out_dtype.category() == DTypeCategory::QUANTIZED) { MultiMode alter_mode = map.at(mode); return opr::ElemwiseMultiType::make( inps, {alter_mode}, OperatorNodeConfig{out_dtype}); } else { return opr::Elemwise::make(inps, mode); } } SymbolVar Network::add_pooling( SymbolVar f, Window window, Stride stride, Padding padding, opr::Pooling::Param::Mode mode) { opr::Pooling::Param param; param.window_h = window[0], param.window_w = window[1]; param.stride_h = stride[0], param.stride_w = stride[1]; param.pad_h = padding[0], param.pad_w = padding[1]; param.mode = mode; return opr::Pooling::make(f, param); } SymbolVar Network::add_type_cvt(SymbolVar f, DType out_dtype) { return opr::TypeCvt::make(f, out_dtype); } SymbolVar Network::add_concat(SymbolVar f, SymbolVar g, int axis) { return opr::Concat::make({f, g}, axis); } SymbolVar Network::add_dimshuffle(SymbolVar f, std::vector pattern) { return opr::Dimshuffle::make(f, pattern); } SymbolVar Network::add_axisaddremove(SymbolVar f) { return opr::AxisAddRemove::make( f, {{opr::AxisAddRemove::AxisDesc::Method::REMOVE, {0}}}); } SymbolVar Network::add_subtensor(SymbolVar f) { using AIdx = opr::indexing::AxisIndexer; return opr::Subtensor::make( f, {AIdx::make_interval(0, f.make_scalar(0), None, None)}); } SymbolVar Network::add_reshape(SymbolVar f) { auto shp = opr::GetVarShape::make(f); return opr::Reshape::make(f, shp); } SymbolVar Network::add_broadcast(SymbolVar f) { auto shp = opr::GetVarShape::make(f); return opr::Broadcast::make(f, shp); } SymbolVar Network::add_copy(SymbolVar f) { return opr::Copy::make(f); } SymbolVar mgb::create_block( Network& network, SymbolVar f_in, size_t stride, size_t num_outputs1, bool has_proj, DType out_dtype) { auto proj = f_in; if (has_proj) { proj = network.add_conv( f_in, num_outputs1, {1, 1}, out_dtype, false, {stride, stride}); } auto f = network.add_conv( f_in, num_outputs1, {3, 3}, out_dtype, true, {stride, stride}, {1, 1}); f = network.add_conv(f, num_outputs1, {3, 3}, out_dtype, true, {1, 1}, {1, 1}); f = network.add_elemwise({f, proj}, out_dtype, opr::Elemwise::Mode::FUSE_ADD_RELU); return f; } SymbolVar mgb::make_resnet18(Network& network, size_t batch, DType out_dtype) { auto data = network.add_var("data", {batch, 4, 224, 224}); if (out_dtype.category() == DTypeCategory::QUANTIZED) data = network.add_type_cvt(data, dtype::QuantizedS8{1.f}); auto first = out_dtype; if (out_dtype.category() == DTypeCategory::QUANTIZED) first = dtype::QuantizedS8{1.f}; auto f = network.add_conv(data, 64, {7, 7}, first, true, {2, 2}, {3, 3}); if (out_dtype.enumv() == DTypeEnum::QuantizedS4 || out_dtype.enumv() == DTypeEnum::Quantized4Asymm) { f = network.add_type_cvt(f, out_dtype); } f = network.add_pooling(f, {3, 3}, {2, 2}, {1, 1}); using Vector = SmallVector; Vector stages = {2, 2, 2, 2}; Vector mid_outputs = {64, 128, 256, 512}; Vector enable_stride = {0, 1, 1, 1}; for (size_t i = 0; i < 4; ++i) { auto s = stages[i]; auto o = mid_outputs[i]; auto es = enable_stride[i]; for (size_t j = 0; j < s; ++j) { size_t stride = !es || j > 0 ? 1 : 2; bool has_proj = j > 0 ? false : true; f = create_block(network, f, stride, o, has_proj, out_dtype); } } f = network.add_pooling( f, {7, 7}, {7, 7}, {0, 0}, opr::Pooling::Param::Mode::AVERAGE); f = network.add_type_cvt(f, dtype::Float32()); return f; } namespace { SymbolVarArray make_pyramids(Network& network, size_t batch, DType out_dtype) { SymbolVarArray pyramids; auto data = network.add_var("data", {batch, 3, 256, 256}); data = data + (-128.f); if (out_dtype.category() == DTypeCategory::QUANTIZED) data = network.add_type_cvt(data, dtype::QuantizedS8{1.f}); auto first = out_dtype; if (out_dtype.category() == DTypeCategory::QUANTIZED) first = dtype::QuantizedS8{1.f}; auto f = network.add_conv(data, 16, {3, 3}, first, true, {2, 2}, {1, 1}); f = network.add_conv(f, 16, {3, 3}, first, true, {1, 1}, {1, 1}); f = network.add_conv(f, 32, {3, 3}, first, true, {2, 2}, {1, 1}); if (out_dtype.enumv() == DTypeEnum::QuantizedS4 || out_dtype.enumv() == DTypeEnum::Quantized4Asymm) { f = network.add_type_cvt(f, out_dtype); } using Vector = SmallVector; Vector stages = {3, 6, 6, 3}; Vector mid_outputs = {32, 64, 128, 256}; Vector enable_stride = {0, 1, 1, 1}; for (size_t i = 0; i < 4; ++i) { auto s = stages[i]; auto o = mid_outputs[i]; auto es = enable_stride[i]; for (size_t j = 0; j < s; ++j) { size_t stride = !es || j > 0 ? 1 : 2; bool has_proj = j > 0 ? false : true; f = create_block(network, f, stride, o, has_proj, out_dtype); } pyramids.push_back(f); } for (size_t i = 0; i < pyramids.size(); ++i) { pyramids[i] = network.add_type_cvt(pyramids[i], first); } return pyramids; } SymbolVarArray fusion_pyramids_feature( Network& network, SymbolVarArray pyramids, size_t fpn_conv_channels) { bool touch = false; SymbolVar x; SymbolVarArray fpn; for (int i = 5; i >= 3; --i) { auto f = network.add_conv( pyramids[i - 2], fpn_conv_channels, {1, 1}, dtype::QuantizedS8{1.f}, false, {1, 1}, {0, 0}); if (!touch) { x = f; touch = true; } else { x = network.add_deconv(x, 2, 16, dtype::QuantizedS8{1.f}); x = network.add_elemwise( {x, f}, dtype::QuantizedS8{1.f}, opr::Elemwise::Mode::ADD); } fpn.push_back(x); } x = fpn[0]; for (int i = 6; i < 8; ++i) { x = network.add_conv( x, fpn_conv_channels, {3, 3}, dtype::QuantizedS8{1.f}, true, {2, 2}, {1, 1}); } return fpn; } } // namespace SymbolVarArray mgb::make_det(Network& network, size_t batch, DType out_dtype) { SymbolVarArray outputs; auto pyramids = make_pyramids(network, batch, out_dtype); auto fpn_hv = fusion_pyramids_feature(network, pyramids, 16); auto fpn_plate = fusion_pyramids_feature(network, pyramids, 16); outputs.insert(outputs.end(), fpn_hv.begin(), fpn_hv.end()); outputs.insert(outputs.end(), fpn_plate.begin(), fpn_plate.end()); return outputs; } SymbolVar mgb::bottleneck( Network& network, SymbolVar f, size_t input_channels, size_t channels, size_t t, size_t stride, DType out_dtype) { size_t in_channels = f.node()->shape()[1]; SymbolVar x = f; if (t != 1) { x = network.add_conv( f, input_channels * t, {1, 1}, out_dtype, true, {1, 1}, {0, 0}); } x = network.add_group_conv( x, input_channels * t, input_channels * t, {3, 3}, out_dtype, true, {stride, stride}, {1, 1}); x = network.add_conv(x, channels, {1, 1}, out_dtype, false, {1, 1}, {0, 0}); if (stride == 1 && in_channels == channels) x = f + x; return x; } SymbolVar mgb::bottleneck_group( Network& network, SymbolVar f, size_t input_channels, size_t channels, size_t stages, size_t s, size_t t, DType out_dtype) { SymbolVar x = f; for (size_t i = 0; i < stages; ++i) { size_t stride = i == 0 ? s : 1; x = bottleneck(network, x, input_channels, channels, t, stride, out_dtype); input_channels = channels; } return x; } namespace { size_t make_divisible(size_t v, size_t divisor) { size_t min_value = divisor; size_t new_v = std::max(min_value, (v + divisor / 2) / divisor * divisor); if (new_v < 0.9 * v) new_v += divisor; return new_v; } } // namespace SymbolVar mgb::make_mobilenet_v2(Network& network, size_t batch, DType out_dtype) { auto data = network.add_var("data", {batch, 3, 224, 224}); if (out_dtype.category() == DTypeCategory::QUANTIZED) { data = network.add_type_cvt(data, dtype::QuantizedS8{1.f}); } constexpr size_t round_nearest = 8; auto x = network.add_conv( data, make_divisible(32, round_nearest), {3, 3}, out_dtype, true, {2, 2}, {1, 1}); x = bottleneck(network, x, 32, make_divisible(16, round_nearest), 1, 1, out_dtype); x = bottleneck_group( network, x, 16, make_divisible(24, round_nearest), 2, 2, 6, out_dtype); x = bottleneck_group( network, x, 24, make_divisible(32, round_nearest), 3, 2, 6, out_dtype); x = bottleneck_group( network, x, 32, make_divisible(64, round_nearest), 4, 2, 6, out_dtype); x = bottleneck_group( network, x, 64, make_divisible(96, round_nearest), 3, 1, 6, out_dtype); x = bottleneck_group( network, x, 96, make_divisible(160, round_nearest), 3, 2, 6, out_dtype); x = bottleneck_group( network, x, 160, make_divisible(320, round_nearest), 1, 1, 6, out_dtype); x = network.add_conv( x, make_divisible(1280, round_nearest), {1, 1}, out_dtype, true, {1, 1}, {0, 0}); if (out_dtype.category() == DTypeCategory::QUANTIZED) { x = network.add_type_cvt(x, dtype::Float32()); } return x; } // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}