解决由于历史原因解析tflite失败的问题
文章目录
- 0. 背景
- 1. tflite 历史遗留问题
- 2. schema
- 3. flatbuffers 编译器
- 3.1 安装 FlatBuffers 编译器
- 3.2. 编译 FlatBuffers schema 文件
- 3.3 使用生成的 Python 文件
- 4 问题未解决
- 终极解决方案
写在最前面:解决方法是升级tensorflow版本,重新生成tflite。
0. 背景
今天遇到了这么一个问题:
File "***/scripts/tflite/schema.py", line 9366, in Tensors
x = self._tab.Indirect(x)
File "***/anaconda3/lib/python3.8/site-packages/flatbuffers/table.py", line 46, in Indirect
return off + encode.Get(N.UOffsetTFlags.packer_type, self.Bytes, off)
File "***/anaconda3/lib/python3.8/site-packages/flatbuffers/encode.py", line 26, in Get
return packer_type.unpack_from(memoryview_type(buf), head)[0]
struct.error: unpack_from requires a buffer of at least 1574732 bytes for unpacking 4 bytes at offset 1574728 (actual buffer size is 853240)
我是在解析某个tflite模型的时候遇到这个问题的,而我在解析别的iflite文件的时候是正常的。
于是分析到是tensorflow的schema文件版本差异的问题,在此记录一下解决过程。
1. tflite 历史遗留问题
最开始的时候,tflite 认为 opcode
不会超过 256 个,所以使用了 u8
来存储。后来就超过了 256 个,改成 int
来存储。但是又不能直接改,所以搞了个巧妙的方式:如果 opcode
小于 256,那么还是用 u8
来存,但是大于 256 的就必须走新的 field
了。
不太好的一点就是,他们把老的那个 field
换了名字,而新的 field
直接用了原来的名字,导致直接调用原来的接口名字的函数在不适配他的逻辑的情况下生成的就是全 0 的东西。
这也就是为什么我解析这个 tflite 模型得到的算子全是 ADD
算子。
2. schema
schema.fbs
是一个 FlatBuffers schema 文件,它定义了数据结构。这个文件通常由你自己创建,以匹配你的特定需求。
对于TensorFlow,它们的 FlatBuffers schema 文件是公开的,可以从它们的源代码库中获取。
我在 https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/schema 找到了 schema.fbs
这个文件,将其下载下来。
同时在这里也可以看到schema之前是有很多版本的。
需要确保 schema.fbs 文件与正在使用的 TensorFlow Lite 版本匹配。如果 schema.fbs 文件与你的 TensorFlow Lite 版本不匹配,可能会遇到问题。
3. flatbuffers 编译器
要使用 FlatBuffers 在 Python 中生成 schema 文件,需要先安装 FlatBuffers 编译器。这个编译器可以将 FlatBuffers schema 文件编译成 Python 类或其他语言的类。
我是使用python,所以在这里记录python。
3.1 安装 FlatBuffers 编译器
在 FlatBuffers 的 GitHub 仓库下载预编译的二进制文件,或者从源代码编译。
使用以下命令从源代码编译 FlatBuffers:
git clone https://github.com/google/flatbuffers.git
cd flatbuffers
mkdir build
cd build
cmake ..
make flatc
编译完成后,FlatBuffers 编译器(flatc
)将位于 flatbuffers 目录。
3.2. 编译 FlatBuffers schema 文件
假设你的 FlatBuffers schema 文件名为 schema.fbs,你可以使用以下命令编译它:
./flatc --python schema.fbs
这在当前目录生成一个名为 tflite 的文件夹。
或者,使用
./flatc -p --gen-onefile schema.fbs
得到文件schema_generated.py,这样子好管理版本,但是使代码变得难以管理和阅读,因为所有的代码都在一个文件中。schema.fbs 文件中定义了很多的表和结构,生成的 Python 代码会非常长。
我个人是喜欢用前者生成一个python文件,需要参考源码的时候去后者生成的文件夹里找源码看。
3.3 使用生成的 Python 文件
import schema
# 读取 TFLite 模型文件。
with open('your_model.tflite', 'rb') as f:
buf = f.read()
# 使用 schema 模块解析模型。
model = schema.Model.GetRootAsModel(buf, 0)
# 现在,你可以使用 model 对象来访问模型的各种属性。
# 例如,你可以获取模型中的操作符数量:
print(model.OperatorCodesLength())
# 或者,你可以获取模型中的第一个操作符的类型:
op_code = model.OperatorCodes(0)
print(op_code.BuiltinCode())
4 问题未解决
我更换了其他版本的 schema.fbs
之后,问题还是没有解决。。。
看来不是schema的问题
需要在解析的时候做以下修改:
tfliteOpSet[tfliteOp->opcode_index]->builtin_code
改成
static_cast<tflite::BuiltinOperator>(tfliteOpSet[tfliteOp->opcode_index]->deprecated_builtin_code)
-
tfliteOpSet[tfliteOp->opcode_index]->builtin_code
:这里使用的是builtin_code,这是TensorFlow Lite中内建操作的枚举值。这种方式是推荐的,因为builtin_code是最新的,包含了所有的TensorFlow Lite内建操作。 -
static_cast<tflite::BuiltinOperator>(tfliteOpSet[tfliteOp->opcode_index]->deprecated_builtin_code)
:这里使用的是 deprecated_builtin_code,这是在旧版本的TensorFlow Lite中使用的,现在已经被标记为弃用(deprecated)。static_cast<tflite::BuiltinOperator>
是将 deprecated_builtin_code 转换为 BuiltinOperator 枚举类型。这种方式不推荐使用,因为 deprecated_builtin_code 可能不包含所有的TensorFlow Lite内建操作。
终极解决方案
升级tensorflow版本,重新生成tflite。