表达式是一个延迟计算引擎,它提供如下功能:
- 模型推理
- 数值计算
- 模型搭建
API 设计上使用"响应式编程",修改输入的值之后,在对应的输出节点取值即可,没有显示的计算调用。
用户操作的数据类型为 VARP,可按Tensor去读取它的值,按保存时的方式不同,分成三类
Input
: 由_Input
创建,或者加载模型而得,在保存时仅存储维度信息(shape),可以写入值Const/Trainable
: 由_Const
或_TrainableParam
创建,或者加载模型而得,在保存时存储数值,不能写入,只能读取Function
: 非输入或者常量,一切由计算而得的变量,不能写入,在保存时存储与之相关的计算图Function
变量可通过fix
调用转换为相应类型,转换时将值计算出来,并去除前置节点依赖。
- 模型读取
static std::vector<VARP> load(const char* fileName); static std::map<std::string, VARP> loadMap(const char* fileName); static std::vector<VARP> load(const uint8_t* buffer, size_t length); static std::map<std::string, VARP> loadMap(const uint8_t* buffer, size_t length);
- 模型保存
static void save(const std::vector<VARP>& vars, const char* fileName);
- 节点替换
static void replace(VARP dst, VARP src);
- 创建变量
static VARP create(EXPRP expr, int index = 0); // include/MNN/expr/NeuralNetWorkOp.hpp 中的函数 VARP _Input(INTS shape = {}, Dimensionformat data_format = NC4HW4, halide_type_t dtype = halide_type_of<float>()) ; ... VARP _Col2Im(VARP x, VARP outputShape, INTS kernelSize, INTS dilate, INTS pads, INTS stride); // include/MNN/expr/MathOp.hpp 中的函数 VARP _Add(VARP x, VARP y); ... VARP _Histogram(VARP x, int bin, int min, int max, int channel = -1);
- 获取变量信息
struct Info { Dimensionformat order = NHWC; INTS dim; halide_type_t type; int size; void syncSize(); }; const Variable::Info* Variable::getInfo();
- 读取变量数据
template <typename T> const T* readMap();
- 向变量写数据
template <typename T> T* writeMap();
- 转换变量类型
bool fix(InputType type) const;
可以通过模型加载函数将模型转换为表达式计算图,对输入的VARP
写入数据后,对输出VARP
执行读取操作,即可完成模型推理过程。
代码示例如下:
#include <MNN/expr/ExprCreator.hpp>
using namespace MNN::Express;
// 加载 model.mnn ,保存 prob 的计算部分
void splitDemp() {
auto varMap = Variable::loadMap("model.mnn");
std::vector<VARP> vars = std::vector<VARP> {varMap["prob"]};
Variable::save(vars, "part.mnn");
}
// 保存变量数据
void saveOutput(float* data0, size_t n0, float* data1, size_t n1) {
VARP input0 = _Const(data0, NHWC, {n0});
VARP input1 = _Const(data1, NHWC, {n1});
Variable::save({input0, input1}, "result.mnn");
}
// 加载输入输出分别为 input 和 output 的 model.mnn ,输入数据到 input ,计算 output
void loadAndCompute() {
auto varMap = Variable::loadMap("model.mnn");
float* inputPtr = varMap["input"]->writeMap<float>();
size_t inputSize = varMap["input"]->getInfo()->size;
for (int i=0; i<inputSize; ++i) {
inputPtr[i] = (float)i/(float)1000;
}
auto outputPtr = varMap["output"]->readMap<float>();
auto outputSize = varMap["output"]->getInfo()->size;
for (int i=0; i<outputSize; ++i) {
printf("%f, ", outputPtr[i]);
}
}
可以通过NeuralNetWorkOp.hpp
和MathOp.hpp
中创建变量的函数组合构造计算图,完成数值计算任务。
代码示例如下:
#include <MNN/expr/ExprCreator.hpp>
using namespace MNN::Express;
void demo() {
auto varp = _Input({1, 3, 224, 224}, NHWC);
{
// Init value init
auto ptr = varp->writeMap<float>();
auto size = varp->getInfo()->size;
for (int i=0; i<size; ++i) {
ptr[i] = (float)i / 100.0f;
}
}
auto input = varp * _Scalar<float>(1.0f/255.0f);
output = input * input + input;
// fix input 之后,1.0f / 255.0f 的预处理不会保存到计算图里面
input.fix(VARP::INPUT);
// graph.mnn 描述 x * x + x 这个计算图
Variable::save({output}, "graph.mnn");
// fix output 之后,保存输出的数值而非计算图
output.fix(VARP::CONSTANT);
Variable::save({varp}, "output.mnn");
}
在MNN/tools/cv
中提供了OpenCV-like
的函数集合,这些函数操作的基本数据类型为VARP
,使用方式与VARP
数值计算相似,因此可以结合cv
中的函数实现图像处理功能。
代码示例如下:
#include <MNN/expr/ExprCreator.hpp>
#include "tools/cv/include/cv/cv.hpp"
using namespace MNN;
void demo() {
auto img = CV::imread("cat.jpg");
auto rgb = CV::cvtColor(img, COLOR_BGR2RGB);
auto input = CV::resize(rgb, {224, 224});
input = Express::_Cast(input, halide_type_of<float>);
input = input * _Scalar<float>(1.0f/255.0f);
for (int i = 0; i < 10; i++) {
printf("%f, ", input->readMap<float>()[i]);
}
}
完整的示例代码可以参考demo/exec/
文件夹中的以下源码文件:
expressDemo.cpp
使用Expr
执行模型推理expressMakeModel.cpp
使用Expr
构建模型segment.cpp
使用Session
进行图像分割,使用Expr
进行后处理pictureRecognition_module.cpp
使用Module
执行图像分类,使用Expr
进行后处理