Files
2026-07-13 13:33:03 +08:00

264 lines
9.3 KiB
C++

#include <cmath>
#include <cstdio>
#include <fstream>
#include <memory>
#include <string>
#include <vector>
#include <MNN/Interpreter.hpp>
#include "MNN_generated.h"
#include "cli.hpp"
#include "onnx.pb.h"
static void addTensorShape(onnx::ValueInfoProto* valueInfo, const std::string& name, const std::vector<int>& dims) {
valueInfo->set_name(name);
auto* tensorType = valueInfo->mutable_type()->mutable_tensor_type();
tensorType->set_elem_type(onnx::TensorProto_DataType_FLOAT);
auto* shape = tensorType->mutable_shape();
for (int dim : dims) {
shape->add_dim()->set_dim_value(dim);
}
}
static void addFloatInitializer(onnx::GraphProto* graph, const std::string& name, const std::vector<int64_t>& dims,
const std::vector<float>& values) {
auto* tensor = graph->add_initializer();
tensor->set_name(name);
tensor->set_data_type(onnx::TensorProto_DataType_FLOAT);
for (auto dim : dims) {
tensor->add_dims(dim);
}
for (auto value : values) {
tensor->add_float_data(value);
}
}
static void addInt64Initializer(onnx::GraphProto* graph, const std::string& name, const std::vector<int64_t>& dims,
const std::vector<int64_t>& values) {
auto* tensor = graph->add_initializer();
tensor->set_name(name);
tensor->set_data_type(onnx::TensorProto_DataType_INT64);
for (auto dim : dims) {
tensor->add_dims(dim);
}
for (auto value : values) {
tensor->add_int64_data(value);
}
}
static bool saveModel(const onnx::ModelProto& model, const std::string& fileName) {
std::ofstream output(fileName, std::ios::binary | std::ios::trunc);
return model.SerializeToOstream(&output);
}
static bool compareVector(const float* got, const std::vector<float>& expected, float tolerance = 1e-5f) {
for (size_t i = 0; i < expected.size(); ++i) {
if (std::fabs(got[i] - expected[i]) > tolerance) {
std::fprintf(stderr, "mismatch at %zu, expect=%f, got=%f\n", i, expected[i], got[i]);
return false;
}
}
return true;
}
static bool runMNNModel(const std::string& modelPath,
const std::vector<std::pair<std::string, std::vector<float>>>& inputs,
const std::vector<float>& expectedOutput) {
std::unique_ptr<MNN::Interpreter> net(MNN::Interpreter::createFromFile(modelPath.c_str()));
if (!net) {
return false;
}
MNN::ScheduleConfig config;
config.type = MNN_FORWARD_CPU;
auto session = net->createSession(config);
if (!session) {
return false;
}
for (const auto& item : inputs) {
auto* inputTensor = net->getSessionInput(session, item.first.c_str());
if (!inputTensor) {
return false;
}
MNN::Tensor hostTensor(inputTensor, inputTensor->getDimensionType());
::memcpy(hostTensor.host<float>(), item.second.data(), item.second.size() * sizeof(float));
inputTensor->copyFromHostTensor(&hostTensor);
}
if (net->runSession(session) != MNN::NO_ERROR) {
return false;
}
auto* outputTensor = net->getSessionOutput(session, nullptr);
if (!outputTensor) {
return false;
}
MNN::Tensor hostOutput(outputTensor, outputTensor->getDimensionType());
outputTensor->copyToHostTensor(&hostOutput);
return compareVector(hostOutput.host<float>(), expectedOutput);
}
static bool convertOnnx(const std::string& onnxModel, const std::string& mnnModel) {
modelConfig config;
config.model = modelConfig::ONNX;
config.modelFile = onnxModel;
config.MNNModel = mnnModel;
config.keepInputFormat = true;
return MNN::Cli::convertModel(config);
}
static onnx::ModelProto makeConcatEinsumModel() {
onnx::ModelProto model;
model.set_ir_version(8);
model.mutable_opset_import()->Add()->set_version(13);
auto* graph = model.mutable_graph();
graph->set_name("ConcatEinsum");
addTensorShape(graph->add_input(), "x", {2, 3});
addTensorShape(graph->add_input(), "y", {2, 3});
addTensorShape(graph->add_output(), "out", {2, 3});
addFloatInitializer(graph, "weight", {2}, {1.5f, -0.5f});
addInt64Initializer(graph, "axes", {1}, {0});
auto* unsqueezeX = graph->add_node();
unsqueezeX->set_op_type("Unsqueeze");
unsqueezeX->add_input("x");
unsqueezeX->add_input("axes");
unsqueezeX->add_output("x_unsqueezed");
auto* unsqueezeY = graph->add_node();
unsqueezeY->set_op_type("Unsqueeze");
unsqueezeY->add_input("y");
unsqueezeY->add_input("axes");
unsqueezeY->add_output("y_unsqueezed");
auto* concat = graph->add_node();
concat->set_op_type("Concat");
concat->add_input("x_unsqueezed");
concat->add_input("y_unsqueezed");
concat->add_output("stacked");
auto* axis = concat->add_attribute();
axis->set_name("axis");
axis->set_i(0);
auto* einsum = graph->add_node();
einsum->set_op_type("Einsum");
einsum->add_input("weight");
einsum->add_input("stacked");
einsum->add_output("out");
auto* equation = einsum->add_attribute();
equation->set_name("equation");
equation->set_s("i,i...->...");
return model;
}
static onnx::ModelProto makeReduceEinsumModel() {
onnx::ModelProto model;
model.set_ir_version(8);
model.mutable_opset_import()->Add()->set_version(13);
auto* graph = model.mutable_graph();
graph->set_name("ReduceEinsum");
addTensorShape(graph->add_input(), "stacked", {2, 2, 3});
addTensorShape(graph->add_output(), "out", {2, 3});
addFloatInitializer(graph, "weight", {2}, {1.5f, -0.5f});
auto* einsum = graph->add_node();
einsum->set_op_type("Einsum");
einsum->add_input("weight");
einsum->add_input("stacked");
einsum->add_output("out");
auto* equation = einsum->add_attribute();
equation->set_name("equation");
equation->set_s("i,i...->...");
return model;
}
static onnx::ModelProto makeConcat3EinsumModel() {
onnx::ModelProto model;
model.set_ir_version(8);
model.mutable_opset_import()->Add()->set_version(13);
auto* graph = model.mutable_graph();
graph->set_name("Concat3Einsum");
addTensorShape(graph->add_input(), "x", {2, 3});
addTensorShape(graph->add_input(), "y", {2, 3});
addTensorShape(graph->add_input(), "z", {2, 3});
addTensorShape(graph->add_output(), "out", {2, 3});
addFloatInitializer(graph, "weight", {3}, {1.0f, -2.0f, 0.5f});
addInt64Initializer(graph, "axes", {1}, {0});
auto* unsqueezeX = graph->add_node();
unsqueezeX->set_op_type("Unsqueeze");
unsqueezeX->add_input("x");
unsqueezeX->add_input("axes");
unsqueezeX->add_output("x_unsqueezed");
auto* unsqueezeY = graph->add_node();
unsqueezeY->set_op_type("Unsqueeze");
unsqueezeY->add_input("y");
unsqueezeY->add_input("axes");
unsqueezeY->add_output("y_unsqueezed");
auto* unsqueezeZ = graph->add_node();
unsqueezeZ->set_op_type("Unsqueeze");
unsqueezeZ->add_input("z");
unsqueezeZ->add_input("axes");
unsqueezeZ->add_output("z_unsqueezed");
auto* concat = graph->add_node();
concat->set_op_type("Concat");
concat->add_input("x_unsqueezed");
concat->add_input("y_unsqueezed");
concat->add_input("z_unsqueezed");
concat->add_output("stacked");
auto* axis = concat->add_attribute();
axis->set_name("axis");
axis->set_i(0);
auto* einsum = graph->add_node();
einsum->set_op_type("Einsum");
einsum->add_input("weight");
einsum->add_input("stacked");
einsum->add_output("out");
auto* equation = einsum->add_attribute();
equation->set_name("equation");
equation->set_s("i,i...->...");
return model;
}
int main() {
const std::string concatOnnx = "/tmp/mnn_concat_einsum.onnx";
const std::string concatMnn = "/tmp/mnn_concat_einsum.mnn";
const std::string reduceOnnx = "/tmp/mnn_reduce_einsum.onnx";
const std::string reduceMnn = "/tmp/mnn_reduce_einsum.mnn";
const std::string concat3Onnx = "/tmp/mnn_concat3_einsum.onnx";
const std::string concat3Mnn = "/tmp/mnn_concat3_einsum.mnn";
bool ok = saveModel(makeConcatEinsumModel(), concatOnnx);
ok = saveModel(makeReduceEinsumModel(), reduceOnnx) && ok;
ok = saveModel(makeConcat3EinsumModel(), concat3Onnx) && ok;
ok = convertOnnx(concatOnnx, concatMnn) && ok;
ok = convertOnnx(reduceOnnx, reduceMnn) && ok;
ok = convertOnnx(concat3Onnx, concat3Mnn) && ok;
const std::vector<float> x = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
const std::vector<float> y = {6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f};
const std::vector<float> expected = {-1.5f, 0.5f, 2.5f, 4.5f, 6.5f, 8.5f};
ok = runMNNModel(concatMnn, {{"x", x}, {"y", y}}, expected) && ok;
const std::vector<float> stacked = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f};
ok = runMNNModel(reduceMnn, {{"stacked", stacked}}, expected) && ok;
const std::vector<float> z = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
const std::vector<float> expected3 = {-11.0f, -7.5f, -5.0f, -1.5f, 1.0f, 4.5f};
ok = runMNNModel(concat3Mnn, {{"x", x}, {"y", y}, {"z", z}}, expected3) && ok;
::remove(concatOnnx.c_str());
::remove(concatMnn.c_str());
::remove(reduceOnnx.c_str());
::remove(reduceMnn.c_str());
::remove(concat3Onnx.c_str());
::remove(concat3Mnn.c_str());
return ok ? 0 : 1;
}