#include "caffe2/utils/map_utils.h"
#include "caffe2/utils/proto_utils.h"
+#include <numeric>
#include <unordered_set>
namespace caffe2 {
{"Reshape", &OnnxExporter::CreateReshapeNodes},
{"Slice", &OnnxExporter::CreateSliceNodes},
{"ChannelShuffle", &OnnxExporter::CreateChannelShuffleNodes},
+ {"ReduceMean", &OnnxExporter::CreateReduceMeanNodes},
+ {"ReduceFrontMean", &OnnxExporter::CreateReduceMeanNodes},
+ {"ReduceBackMean", &OnnxExporter::CreateReduceMeanNodes},
{"ResizeNearest", &OnnxExporter::CreateUpsampleNodes}};
return kSpecialOperators;
}
return result;
}
+ConvertedResult OnnxExporter::CreateReduceMeanNodes(
+ const caffe2::OperatorDef& def,
+ const std::unordered_map<std::string, caffe2::TensorShape>& shapes) {
+ CAFFE_ENFORCE_GE(def.input_size(), 1);
+ CAFFE_ENFORCE_LE(def.input_size(), 2);
+ CAFFE_ENFORCE_EQ(def.input_size(), 1, "Input \"lengths\" is not supported.");
+ CAFFE_ENFORCE_GE(def.output_size(), 1);
+ const auto& x = def.input(0);
+ const auto& y = def.output(0);
+ const auto& dims = shapes.at(x).dims();
+
+ ConvertedResult result;
+ auto& nodes = result.first;
+ auto& const_tensors = result.second;
+ std::unordered_map<std::string, const caffe2::Argument*> args;
+ for (const auto& a : def.arg()) {
+ args.emplace(a.name(), &a);
+ }
+
+ std::vector<int64_t> axes;
+ int64_t keepdims = 1;
+
+ if (def.type() == "ReduceMean") {
+ // axes
+ auto it = args.find("axes");
+ if (it == args.end()) {
+ axes.resize(dims.size());
+ std::iota(axes.begin(), axes.end(), 0);
+ } else {
+ axes.assign(it->second->ints().begin(), it->second->ints().end());
+ }
+
+ // keepdims
+ it = args.find("keepdims");
+ if (it != args.end()) {
+ keepdims = it->second->i();
+ }
+ } else {
+ // num_reduce_dim
+ auto it = args.find("num_reduce_dim");
+ const int64_t num_reduce_dim = it == args.end() ? 1 : it->second->i();
+ CAFFE_ENFORCE_LE(num_reduce_dim, dims.size());
+ axes.resize(num_reduce_dim);
+
+ int64_t start_dim = 0;
+ if (def.type() == "ReduceFrontMean") {
+ start_dim = 0;
+ } else if (def.type() == "ReduceBackMean") {
+ start_dim = dims.size() - axes.size();
+ }
+ std::iota(axes.begin(), axes.end(), start_dim);
+
+ keepdims = 0;
+ }
+
+ nodes.emplace_back(MakeNode("ReduceMean",
+ { x },
+ { y },
+ {
+ MakeAttribute("axes", axes),
+ MakeAttribute("keepdims", keepdims),
+ },
+ def.name()));
+
+ return result;
+}
+
ConvertedResult OnnxExporter::CreateUpsampleNodes(
const caffe2::OperatorDef& def,
const std::unordered_map<std::string, caffe2::TensorShape>& shapes) {
.TensorInferenceFunction([](const OperatorDef& def,
const vector<TensorShape>& in) {
REDUCTION_OP_SHAPE_INFERENCE(true)
- });
+ })
+ .InheritOnnxSchema("ReduceMean");
OPERATOR_SCHEMA(ReduceFrontMeanGradient).NumInputs(2, 3).NumOutputs(1);
REGISTER_CPU_OPERATOR(ReduceBackMean, SumReduceDimsOp<CPUContext, false, true>);
.TensorInferenceFunction([](const OperatorDef& def,
const vector<TensorShape>& in) {
REDUCTION_OP_SHAPE_INFERENCE(false)
- });
+ })
+ .InheritOnnxSchema("ReduceMean");
OPERATOR_SCHEMA(ReduceBackMeanGradient).NumInputs(2, 3).NumOutputs(1);
#undef REDUCTION_OP_SHAPE_INFERENCE
output = c2_rep.run({"X": X, "Y": Y})
np.testing.assert_almost_equal(output["W3"], W_ref)
+ def test_reducemean(self):
+ X = np.random.randn(4, 6, 10, 5, 3).astype(np.float32)
+
+ predict_net = caffe2_pb2.NetDef()
+ predict_net.name = 'test-reducemean-net'
+ predict_net.external_input[:] = ['X']
+ predict_net.external_output[:] = [
+ 'reduce_front_mean',
+ 'reduce_back_mean',
+ 'reduce_mean_0',
+ 'reduce_mean_1',
+ ]
+ predict_net.op.extend([
+ core.CreateOperator(
+ 'ReduceFrontMean',
+ inputs=['X'],
+ outputs=['reduce_front_mean'],
+ num_reduce_dim=2,
+ ),
+ core.CreateOperator(
+ 'ReduceBackMean',
+ inputs=['X'],
+ outputs=['reduce_back_mean'],
+ num_reduce_dim=2,
+ ),
+ core.CreateOperator(
+ 'ReduceMean',
+ inputs=['X'],
+ outputs=['reduce_mean_0'],
+ axes=[1, 3],
+ keepdims=0,
+ ),
+ core.CreateOperator(
+ 'ReduceMean',
+ inputs=['X'],
+ outputs=['reduce_mean_1'],
+ axes=[1, 3],
+ keepdims=1,
+ ),
+ ])
+ ws, c2_outputs = c2_native_run_net(
+ init_net=None,
+ predict_net=predict_net,
+ inputs=[X])
+
+ onnx_model = c2_onnx.caffe2_net_to_onnx_model(
+ predict_net=predict_net,
+ value_info={
+ 'X': (onnx.mapping.NP_TYPE_TO_TENSOR_TYPE[X.dtype], X.shape)
+ })
+ onnx_outputs = c2.run_model(onnx_model, inputs=[X])
+ self.assertSameOutputs(c2_outputs, onnx_outputs)
+
def test_upsample(self):
X = np.random.randn(1, 1, 2, 2).astype(np.float32)
width_scale = 2.0