{"id":15899935,"url":"https://github.com/nihui/fxxk-tensorflow-lite-note","last_synced_at":"2025-04-02T19:22:30.260Z","repository":{"id":139969171,"uuid":"283508951","full_name":"nihui/fxxk-tensorflow-lite-note","owner":"nihui","description":"add custom op in both tensorflow and tensorflow-lite","archived":false,"fork":false,"pushed_at":"2020-07-29T10:45:37.000Z","size":116,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-08T09:47:05.845Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nihui.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-29T13:38:32.000Z","updated_at":"2024-01-25T02:21:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"ea151041-abb6-40db-abfd-d3828ba1f2ee","html_url":"https://github.com/nihui/fxxk-tensorflow-lite-note","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nihui%2Ffxxk-tensorflow-lite-note","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nihui%2Ffxxk-tensorflow-lite-note/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nihui%2Ffxxk-tensorflow-lite-note/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nihui%2Ffxxk-tensorflow-lite-note/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nihui","download_url":"https://codeload.github.com/nihui/fxxk-tensorflow-lite-note/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246876097,"owners_count":20848105,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-06T10:41:08.008Z","updated_at":"2025-04-02T19:22:30.236Z","avatar_url":"https://github.com/nihui.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# 1. Linux 环境和重装\n\n## 1.1 系统安装\n\npopos：custom 安装/ /boot swap 不用安装/home \n\n## 1.2 启动表修复\n\ngrub-repair 修复启动表 进入linux\n\ngurb customizer 修复win启动表\n\n## 1.3 挂载home盘\n\n自动挂载原home盘 runtime覆盖原有根目录下自带home盘\n\n```shell\nvim /etc/fstab \n# 添加如下行\n/dev/sda5  /home  ext4  defaults  0  0 \n```\n\n## 1.4 应用修复 zsh修复\n\n```shell\napt install vim tmux ranger zsh flameshot ntpdate make\nchsh -s /bin/zsh #输入密码 重启 修复zsh\n```\n\n安装chrome vscode 搜狗输入法 卸载ibus 安装fcitx\n\n## 1.5 统一双系统时间\n\n```shell\nsudo ntpdate time.windows.com \nsudo hwclock --localtime --systohc \n```\n\n\n## 1.6 设置免密码sudo     \n\n```shell\nvim /etc/sudoers  \n# 更改用户组的nopasswd 除了root全部改称nopasswd格式\n%admin ALL=(ALL) NOPASSWD: ALL\n```\n\n## 1.7 安装邮箱\n\nmailspring\n\nqq邮箱验证码xwsxzxowqgupbddc当作密码\n\n# 2. GCC\n\ngcc 7.3 \n\n## 2.1 源代码make 或者直接apt install\n\n```shell\nwget https://ftp.gnu.org/gnu/gcc/gcc???  # 下载镜像源\nsudo make \nsudo make check\nsudo make install  #一定要sudo 否则奇怪问题 \n```\n\ninstall 的 bin文件在/usr/local/bin/gcc   并不在/usr/bin（添加源之后apt install 的gcc在这个路径）\n\n## 2.2 多版本并存切换\n\n```sh\nsudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 40 \nsudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50\nsudo update-alternatives --install /usr/bin/gcc gcc /usr/local/bin/gcc-7 60\nsudo update-alternatives --config gcc  # 控制默认gcc版本 g++同上\n```\n\n# 3. Anaconda虚拟环境\n## 3.1 安装\n\nsh文件装好 \n\n.zshrc  或者.bashrc注册  source刷新\n 注意添加sock5的支持 与本机代理同步：\n\n```shell\nunset all_proxy \u0026\u0026 unset ALL_PROXY\npip install pysocks  #添加sock支持\n```\n\n## 3.2 设置虚拟环境\n\n```shell\ncreate -n name python=3.6   #3.6对应了tf1.7版本 \npip install --upgrade pip\n```\n\n## 3.3 初次使用添加bazel构建的支持\n\n新建立的conda虚拟环境中 预先安装对应的库\n\n```shell\npip install keras_applications==1.0.4 --no-deps\npip install keras_preprocessing==1.0.2 --no-deps\npip install h5py==2.8.0\n```\n\n注意：尽量不要再base环境安装tensorflow和jupyter notebook之类的包，防止虚拟环境没有安装而去上级base环境找到直接使用引起版本错误\n\n# 4. TensorFlow安装和构建\n\n注意：pip方式安装容易引起和本地custom op的补丁版本不一致 建议本地同版本build成whl后安装\n\n对齐GCC和bazel版本\n\n## 4.1 bazel\n\n安装\n\nhttps://github.com/bazelbuild/bazel/releases  下载对应版本的installer-linux-x86_64.sh\n\n```shell\nchmod +x bazelxxxxx.sh \n./bazelxxxxxx.sh --user\n```\n\n卸载\n\n```shell\nrm -rf ~/.bazel\nrm -rf ~/bin\nrm -rf /usr/bin/bazel    \n```\n\n一般只是切换不需要更改.zshrc文件 先卸载再安装后 用bazel version验证版本\n\n## 4.2 TensorFlow源码的编译配置configure\n\n### 4.2.1 ./configure先自动配置生成配置文件\n\n第一步询问的python解释器地址 是conda env的解释器 所以需要先启动虚拟环境\n\nclang可以选y    XLA可以选false  cuda为GPU支持  其他都是默认N\n\nworkspace 在build安卓aar时使用 本地tensorflow和lite不需要设置 默认N即可\n\n### 4.2.2 vim .tf_configure.bazelrc 再手动修改配置文件\n\n参考GCC手册：对于cpu优化标记  https://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/i386-and-x86_002d64-Options.html\n\n更改  build:opt --copt=-march=native（本地支持）   为   build:opt --copt=-mtune=generic（通用支持）\n\n更改  build:opt --host_copt=-march=native   为   build:opt --host_copt=-mtune=generic\n\n注：上述修改的场景是服务器打包并准备在本地使用，服务器cpu和本地cpu不同\n\n## 4.3 build相关\n\ntensorflow目录下的bazel-bin等目录是~/.cache/bazel/...的链接 \n\n如果更换tensorflow版本，需要用bazel clean命令清除残留缓存\n\n### 4.3.1 构建tensorflow\n\n```shell\nbazel build //tensorflow/tools/pip_package:build_pip_package --cxxopt=\"-D_GLIBCXX_USE_CXX11_ABI=0\"   #构建pip打包器 ABI参数为gcc5以上 兼容旧版本 单独环境可以无视\n\n./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg   #打为whl包\n\nsource activate xxx\npip install /tmp/tensorflow_pkg/tensorflow-cp36xxxx.whl    #安装tensorflow\n```\n\n本地机器初次build（i5 cpu 8gb内存）约7小时，需要限制bazel线程数（参见bazel文档），并配好梯子和sock5代理\n\n海外服务器初次build打包速度约2小时左右，原因是bazel动态下载所需的库和文件\n\n修改源文件后，增量build时间很短，具体时间视修改所关联的文件不同，单独文件修改大约十秒级别\n\n### 4.3.2 单独构建tensorflow custom op的.so共享库\n\ntensorflow/core/user_ops目录下\n\n添加新算子的.cc源文件 格式参见tf官网\n\n```shell\nTF_CFLAGS=( $(python -c 'import tensorflow as tf; print(\"\".join(tf.sysconfig.get_compile_flags()))') )\nTF_LFLAGS=( $(python -c 'import tensorflow as tf; print(\"\".join(tf.sysconfig.get_link_flags()))') )\ng++ -std=c++11 -shared zerof.cc -o zerof.so -fPIC ${TF_CFLAGS[@]} ${TF_LFLAGS[@]} -D_GLIBCXX_USE_CXX11_ABI=0 -O2\n```\n\n这里直接使用了g++构建 zerof.so文件 原因是本地使用bazel会出现google protobuf依赖找不到等诡异问题\n\n```python\ntf.load_op_library('??/zerof.so').zerof()  #调用so中的op\n```\n\n### 4.3.3 单独构建python tensorflow lite解释器的.so共享库\n\n注意：\n\n​    之前版本使用SWIG进行cpp和python之间的交互   后续tf官方将改为pybind11的方式实现交互\n\n​    单独更新该库 即可实现tensorflow lite之中的算子  包括：增加custom的op 以及 覆盖builtin的op   无需整体编译tf whl\n\n```shell\nbazel build --config opt //tensorflow/lite/python/interpreter_wrapper:tensorflow_wrap_interpreter_wrapper\n```\n\n构建结果为/bazel-bin/tensorflow/lite/python/interpreter_wrapper/_tensorflow_wrap_interpreter_wrapper.so\n\n替换到~/anaconda3/envs/tf20/lit/python3.6/site-packages/tensorflow_core/lite/python/interpreter_wrapper/ 路径下\n\n注意：对于tf 2.1 包路径是tensorflow_core 而不是之前的tensorflow\n\n#### 增添custom op\n\ntf 1.14  2.1 验证     \n\n##### a. 编写算子源文件 \n\n格式为：\n\n```c++\n// zero.cc\n#include \"tensorflow/lite/c/c_api_internal.h\"\n#include \"tensorflow/lite/kernels/internal/tensor.h\"\n#include \"tensorflow/lite/kernels/kernel_util.h\"\n\nnamespace tflite {\nnamespace ops {\nnamespace custom {    // 需要放在custom命名空间\nnamespace zerof {\nTfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {}                             \nTfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {}\n}  // namespace zerof\nTfLiteRegistration* Register_ZEROF() {\n  static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr,\n                                 zerof::Prepare, zerof::Eval};\n  return \u0026r;                     \n}\n}  // namespace custom\n}  // namespace ops\n}  // namespace tflite\n```\n\n如果加载模型报错：tensorflow_wrap_interpreter_wrapper.so: undefined symbol: _ZN6tflite3ops6custom30Register_EXTRACT_IMAGE_PATCHES\n\n这是因为命名空间namespace custom 也许写成了builtin，混入了别的命名空间而无法被识别\n\n在kernels路径下\n\n##### b. BUILD文件添加规则\n\n```python\ncc_library(\n    name = \"builtin_op_kernels\",\n    srcs = [\n        \"activations.cc\",\n        \"add.cc\",\n        \"add_n.cc\",\n        \"arg_min_max.cc\",\n....\n         \"zerof.cc\",   ## 添加在这里 打包所有的算子\n    ],\n    hdrs = [\n    ],\n    copts = tflite_copts() + tf_opts_nortti_if_android() + EXTRA_EIGEN_COPTS,\n```\n\n##### c. 添加声明\n\n在 (builtin_opkernels.h 后经验证不用)   custom_ops_register.h 添加\n\n```c++\nTfLiteRegistration* Register_ZEROS();\n```\n\n##### d. 注册算子\n\n在 register.cc   register_ref.cc 添加\n\n```c++\nnamespace custom {\nTfLiteRegistration* Register_ZEROF();\n}\n\nBuiltinOpResolver::BuiltinOpResolver(){\nAddCustom(\"Zerof\", tflite::ops::custom::Register_ZEROF());  \n    // Zerof 这里我们需严格使用大驼峰命名 例如: ExtractImagePatches\n    // 在编译为解释器之后 算子名将被自动转化为下划线所谓snack格式 例如: extract_image_patches\n    // 另注: Register_ZEROF这个名称没有上面两条的限制 随便定义 统一即可\n}\n```\n\n##### e. polish编译验证流程\n\n使用bazel build一次之后，tensorflow的源代码文件夹下已经包含了解释器的so文件\n\n跳转到conda虚拟环境的tensorflow代码位置：~/anaconda3/envs/tf2.0/lib/python3.6/site-packages/tensorflow_core/lite/python/interpreter_wrapper\n\n（注意：tf2.x以上在...site-packages/tensorflow_core/...    tf1.x则是在...site-packages/tensorflow/..）\n\n为解释器so文件建立软连接\n\n```shell\nln -s /home/gx/myproj/tensorflow/bazel-bin/tensorflow/lite/python/interpreter_wrapper/_tensorflow_wrap_interpreter_wrapper.so _tensorflow_wrap_interpreter_wrapper.so\n```\n\n每次更改算子kernel源代码 -\u003e bazel build -\u003e restart解释环境 例如jupyter notebook 即可使用更新的op算子\n\n##### f. 算子增加属性\n\n在tflite中custom op的属性和tf中custom op的属性不同，是因为保存模型的格式不同\n\ntflite使用flexbuffer进行模型保存 是flatbuffer的一个精简子集\n\n以extract_image_patches算子为例，tf中该算子的属性包括\n\n```c++\n std::vector\u003cint32\u003e ksizes_;\n std::vector\u003cint32\u003e strides_;\n std::vector\u003cint32\u003e rates_;\n string padding_;\n```\n\n为增加四个属性，需要在custom op的kernel源代码中实现Init和Free方法\n\n首先，在命名空间中定义一个结构体用来暂存各个属性\n\n```c++\nnamespace extract_image_patches {\ntypedef struct {\n  std::vector\u003cint32\u003e ksizes_;\n  std::vector\u003cint32\u003e strides_;\n  std::vector\u003cint32\u003e rates_;\n  string padding_;\n} TfLiteEIPOParams;\n...\n```\n\n第二，在Init方法中从tflite的flexbuffer格式模型中读出属性值，存入上述结构体\n\n```c++\n// 加上相关include\n#include \"flatbuffers/flexbuffers.h\"  // TF:flatbuffers\n#include \u003cvector\u003e\n#include \"tensorflow/lite/kernels/internal/optimized/optimized_ops.h\"\n#include \"tensorflow/lite/kernels/internal/reference/reference_ops.h\"\n#include \"tensorflow/lite/kernels/op_macros.h\"\n\n\nvoid* Init(TfLiteContext* context, const char* buffer, size_t length) {\n  auto* data = new TfLiteEIPOParams;//开辟一块空间存属性值\n  const uint8_t* buffer_t = reinterpret_cast\u003cconst uint8_t*\u003e(buffer);//属性存在模型的buffer里面\n  const flexbuffers::Map\u0026 m = flexbuffers::GetRoot(buffer_t, length).AsMap();//参考flexbuffer官网文档 m可以理解为一个json\n  \n  //这里需要用AsTypedVector读出，如果用AsVector会失败，原因不明，可能flexbuffer做了奇怪的优化\n  //注意：此处读出为vector其实不是cpp的std vector 而是flexbuffer的vector \n  //     所以还需通过AsInt32等方式获取为数值\n  //     同样的AsString()之后需要用c_str()转化为cpp的格式\n  data-\u003erates_.push_back(m[\"rates\"].AsTypedVector()[0].AsInt32());\n  data-\u003erates_.push_back(m[\"rates\"].AsTypedVector()[1].AsInt32());\n  data-\u003erates_.push_back(m[\"rates\"].AsTypedVector()[2].AsInt32());\n  data-\u003erates_.push_back(m[\"rates\"].AsTypedVector()[3].AsInt32());\n\n  data-\u003eksizes_.push_back(m[\"ksizes\"].AsTypedVector()[0].AsInt32());\n  data-\u003eksizes_.push_back(m[\"ksizes\"].AsTypedVector()[1].AsInt32());\n  data-\u003eksizes_.push_back(m[\"ksizes\"].AsTypedVector()[2].AsInt32());\n  data-\u003eksizes_.push_back(m[\"ksizes\"].AsTypedVector()[3].AsInt32());\n\n  data-\u003estrides_.push_back(m[\"strides\"].AsTypedVector()[0].AsInt32());\n  data-\u003estrides_.push_back(m[\"strides\"].AsTypedVector()[1].AsInt32());\n  data-\u003estrides_.push_back(m[\"strides\"].AsTypedVector()[2].AsInt32());\n  data-\u003estrides_.push_back(m[\"strides\"].AsTypedVector()[3].AsInt32());\n    \n  data-\u003epadding_ = m[\"padding\"].AsString().c_str();\n\n  return data;\n}\n\nvoid Free(TfLiteContext* context, void* buffer) {\n  delete reinterpret_cast\u003cTfLiteEIPOParams*\u003e(buffer);//最后释放空间\n}\n```\n\n第三，在Prepare和Eval方法中，查寻该属性\n\n```c++\nTfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {\n...\nauto* params = reinterpret_cast\u003cTfLiteEIPOParams*\u003e(node-\u003euser_data);\n//其实属性被解释器读取后 放在node的user_data之中 读出并用TfLiteEIPOParams格式化\nauto temp = params-\u003eksizes_[0]; //使用属性值\n...\n}\n```\n\n##### g. TfLite  输出tensor维度修改\n\nTfLite将模型的tensor统一用指针形式保存到context里面，对于一个op想输出指定维度的tensor，需要用到context类自带的ResizeTensor方法，参见源代码其他op的示例\n\n需要特定的参数TfLiteIntArray* dims，指定resize目标tensor的维度\n\nTfLiteIntArray结构包含int size和int data[] 依次指定即可，例如：\n\n```c++\nTfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {\n...\n  TfLiteIntArray* ret = TfLiteIntArrayCreate(input-\u003edims-\u003esize);\n  ret-\u003edata[0] = 1;\n  ret-\u003edata[1] = 2;\n  ret-\u003edata[2] = 2;\n  ret-\u003edata[3] = 1;\n\n  return context-\u003eResizeTensor(context, output,\n                               TfLiteIntArrayCopy(ret));\n}\n```\n\n\n\n### 4.3.4 构建Android解释器aar包\n\njitbrain Android Studio(AS)（自带安卓skd ） +  ndk18b（验证可用 网上说必须15 垃圾）  +    bazel    tf2.1\n\n配置tf Download a fresh release of clang : y   自配workspace 指定上面安好的 sdk ndk 路径\n\n必要时修改配置文件.tf_configure.bazelrc： build --action_env ANDROID_NDK_API_LEVEL = 21 \n\n```shell\nbazel build --cxxopt='--std=c++11' -c opt        \\\n  --fat_apk_cpu=x86,x86_64,arm64-v8a,armeabi-v7a   \\    # 两个arm基本包含大部分安卓手机\n  //tensorflow/lite/java:tensorflow-lite\n```\n\n构建 .aar文件 结果保存在\n\nbazel-genfiles/tensorflow/contrib/lite/java/tensorflow-lite.aar\n\n在AS导入并在build gradle里写入依赖：implementation project(':tensorflow-lite')\n\n#### 增添custom op\n\n##### a. 编写算子源文件\n\n直接将算子源文件 改扩展名为zerof.h  并放置在tensorflow/lite/java/src/main/native路径下\n\n##### b. BUILD文件添加规则\n\n```python\n# *This includes all ops. If you want a smaller binary, you should copy and*\n# *modify builtin_ops_jni.cc.  You should then link your binary against both*\n# *\":native_framework_only\" and your own version of \":native_builtin_ops\".*\n# 如果要精简op集 进一步的减小tflite模型大小 参见下文c部分\ncc_library(\n    name = \"native\",\n    srcs = [\n        \"builtin_ops_jni.cc\",\n    ],\n    hdrs = [\n        \"zerof.h\",    ## 头文件的build说明添加在这里 给builtin_op_jni.cc添加该算子\n    ],\n    copts = tflite_copts(),\n    deps = [\n        \":native_framework_only\",\n        \"//tensorflow/lite/kernels:builtin_ops\",\n    ],\n    alwayslink = 1,\n)\n```\n\n##### c. 注册算子\n\n在builtin_op_jni.cc 文件内添加头文件声明\n\n```c++\n#include \"tensorflow/lite/kernels/register.h\"\n#include \"tensorflow/lite/java/src/main/native/zerof.h\"    #改成头文件 添加在这里\nnamespace tflite {\n...\n```\n\n##### d. 精简op集\n\nregister.h 的 BuiltinOpResolver 类的构造方法  官方已经默认在register.cc中实现  \n\n为了精简op集 可以自己overload该方法 \n\nbuiltin_op_jni.cc 原始代码：\n\n```c++\nstd::unique_ptr\u003cOpResolver\u003e CreateOpResolver() {  // NOLINT\n  return std::unique_ptr\u003ctflite::ops::builtin::BuiltinOpResolver\u003e(\n      new tflite::ops::builtin::BuiltinOpResolver());\n}\n```\n\n在原本的return里面 替换为：\n\n```c++\n\nstd::unique_ptr\u003cOpResolver\u003e CreateOpResolver() {  // NOLINT\n  return std::unique_ptr\u003ctflite::ops::builtin::BuiltinOpResolver\u003e(\n    new tflite::ops::builtin::BuiltinOpResolver::BuiltinOpResolver() {\n      AddBuiltin(BuiltinOperator_ABS, Register_ABS());\n      AddBuiltin(BuiltinOperator_MATRIX_SET_DIAG, Register_MATRIX_SET_DIAG());\n    ... //删减一些算子\n      AddCustom(\"Mfcc\", tflite::ops::custom::Register_MFCC());\n      AddCustom(\"Zerof\", tflite::ops::custom::Register_ZEROF());\n    } // end of BuiltinOpResolver(){}\n  );  // end of return\n} \n```\n\n# 5. TensorFlow模型格式及转换\n\nfrozen固化模型是为了移动端部署实现的 但是lite也实现了该功能 \n\n推荐方法简单处理后 将模型保存为 saved_model 并转化为lite模型\n\n在tf2.x环境直接支持tf1.x模型运行：\n\n```python\nimport tensorflow.compat.v1 as tf\ntf.disable_v2_behavior()\n```\n\n\n\n## 5.1 处理模型 保存为saved_model\n\n```python\ninput_image_holder = tf.placeholder(tf.float32, [1,512,1360,3], name='input_image_holder') \n#设置输入占位符 类型 维度 node名称 \n...\ntf.saved_model.simple_save(sess,\n\t\"./save_model_path\",  #输出路径\n\tinputs={\"input_image_holder\": input_image_holder},  # node名 输入对象 \n\toutputs={\"saturate_cast\": output}) # 输出node名（根据实际情况） 输出对象\n```\n\n## 5.2 将saved_model 转化为lite模型\n\n给lite添加custom op 分两种情况：\n\n1. tensorflow含有该算子 而lite没有  \n\n   ​\t\t可以参照官网 使用tf算子库 后果是模型变得很大 官方还有白名单 有些op并不支持添加\n\n   ​\t\t可以自己在lite中实现该op  推荐\n\n2. tensorflow不包含该算子 想在lite之中添加\n\n   ​        需要先在官方tf库中 利用.so文件添加该算子  接着在lite的python解释器中添加  最后在移动端解释器添加\n\n```python\ntf.load_op_library('??/zerof.so')  ## 必须先导入custom op 否则无法识别 \nconverter = tf.lite.TFLiteConverter.from_saved_model('./save_model_path_zerof') # 导入\nconverter.allow_custom_ops = True  ## 事实上这里是：允许tf包含 但是lite不包含的op被打包进lite模型\n# converter.post_training_quantize = True   ## 模型后处理量化开关\ntflite_model = converter.convert()\nopen(\"./1.tflite\", \"wb\").write(tflite_model)\n```\n\n## 5.3 pc机python环境导入并运行lite模型\n\n```python\ninterpreter = tf.lite.Interpreter(model_path='/home/gx/myproj/generative_inpainting-master/1.tflite')  \n# 导入模型创建解释器 如果custom op在kernels目录的某处没有注册 会提示不认识的符号\ninterpreter.allocate_tensors()  # 分配张量\ninput_details = interpreter.get_input_details()\nprint(str(input_details))\noutput_details = interpreter.get_output_details()\nprint(str(output_details))\n\ninput_shape = input_details[0]['shape'] \ninput_data = np.array(np.random.random_sample(input_shape), dtype=np.int32) # 随机输入\nprint(input_data)\ninterpreter.set_tensor(input_details[0]['index'], input_data)\ninterpreter.invoke()\noutput_data = interpreter.get_tensor(output_details[0]['index']) # 取输出观察\nprint(output_data)\n```\n\n# 6. TfLite模型手动解析\n\n## 6.1 flatbuffer和flexbuffer\n\n### 6.1.1 简介及schema文件\n\nflatbuffer是在高速场景下替代json的序列化开源库，因为是二进制格式，不需要像json建立诸多对象，存取速度很快\n\ntflite模型存储格式为flatbuffer的精简版本flexbuffer  官方文档链接如下\n\nhttps://google.github.io/flatbuffers/flatbuffers_guide_tutorial.html\n\nflatbuffer需要由模板文件schema来定义，该文件详细定义了在二进制buffer中以怎样的步进读取数据，此处不赘述\n\ntensorflowlite的模型存储模板schema文件在tensorflow源代码中可以找到：\n\n\u003csource_root\u003e/tensorflow/lite/schema/schema.fbs\n\n### 6.1.2 解析工具flatc\n\n确保cmake已经正确安装 推荐官网二进制版本\n\n克隆flatbuffer的源代码仓库git clone https://github.com/google/flatbuffers.git\n\n```shell\ncmake -G \"Unix Makefiles\" //生成MakeFile\nmake //生成flatc\nmake install //安装flatc\nflat --version\n```\n\n解析tflite文件到JSON格式：注意--后的空格\n\n```shell\n./flatc -t schema.fbs -- my.tflite\n```\n\n该模型my.tflite将被解析为my.json\n\n```shell\n./flatc -b schema.fbs my.json\n```\n\n该文件my.json将被反解析回my.lite模型\n\n上述两个转化操作均会自动覆盖更新转化的目标文件\n\n## 6.2 TfLite模型定义\n\nTfLite模型的关键数据结构定义如下（部分省略）：\n\n```json\nModel\n  operator_codes                     //列出用到的全部算子 对应的代码\n    builtin_code \n  subgraphs                          //子图\n    tensors                          //列出用到的所有张量（图的边）\n      shape\n      buffer                         //该张量参数存储使用到的buffer编号\n      name                           //张量名 定义模型时用name命名 或者自动命名 netron等工具查看\n      quantization              \n    inputs                           //输入节点（node）编号\n    outputs                          //输出节点编号\n    operators                        //列出用到的所有节点\n      opcode_index                   //该节点的算子代码序号 对应operator_codes 中序号\n      inputs                         //该节点输入张量编号\n      outputs                        //该节点输出张量编号\n      ...                            //其他属性等\n  buffers                            //参数仓库\n```\n\n例如需要手动修改移除某节点，流程如下：\n\na. 将tflite模型通过flatc转换成json格式\n\nb. 在json文件中找到需要移除的节点（model subgraph operators xxx）把他的输入张量对接给他的下层节点，\n\nc. 删除该operators防止悬空节点出现\n\nd. 在operator_codes等处做相应修改\n\ne. 使用flatc工具更新tflite模型\n\n## 6.3 TfLite解释器加载与解析\n\n解释器加载tflite模型，放入内存只读区域，也有一些需要动态更新的buffer另开辟动态区域，解释器主要处理张量和节点两大内容\n\na. 对于tensor，首先解析为TfLiteTensor，并且将所有的TfLiteTensor整合为一张TfLiteContext表\n\n​\t在TfLiteContext中保存的是tensor的大小格式等信息和指向内存中加载模型真实张量的指针\n\nb. 对于node，首先解析operators为TfLiteNode\n\n​\t并为之对应的匹配存在于TfLiteRegistration中的算子的kernel指针\n\n至此完成了模型的加载\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnihui%2Ffxxk-tensorflow-lite-note","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnihui%2Ffxxk-tensorflow-lite-note","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnihui%2Ffxxk-tensorflow-lite-note/lists"}