TensorFlow pb模型修改和优化
TensorFlow 模型训练完成后,通常会通过frozen过程保存一个最终的pb模型。保存的pb模型是以GraphDef数据结构保存的,可以序列化保存为二进制pb模型或者文本pbtxt模型。GraphDef本质上是一个DAG有向无环图,里面主要是存放了一个算子node list,每个算子具有名称,attr等内容,以及通过input包含了node之间的连接关系。整个GraphDef的输入节点是以Placeholder节点来标识的,模型参数权重通常是以Const节点来保存的。不同于onnx,GraphDef没有对输出进行标识,好处是可以通过node_name:idx来引用获取任意一个节点的输出,缺点是一般需要通过netron手动打开查看模型输出,或者通过代码分析没有输出节点的node作为模型输出节点。下面简单介绍下pb模型常用的一些处理方法。
pb模型保存
构建模型和保存
当然一般用其他方式而不是raw_ops构建模型。
pb模型读取
node信息打印
常用信息:
对于node的input,一般用node_name:idx如node_name:0来表示输入来自上一个算子的第idx个输出。:0省略则是默认为第0个输出。 名称前面加^符号是控制边。这个input是一个string list,这里面的顺序也对应这个node的各个输入的顺序。
创建node
创建GraphDef和添加node
没有onnx模型往graph里面添加节点的topo排序要求
设置placeholder的shape
参考前面创建node部分,通过修改Placeholder的shape属性。
模型shape推导
需要导入模型到tf:tf.import_graph_def(graph_def, name='')。当然需要先设置正确的pld的shape。
然后获取node的输出tensor:graph.get_tensor_by_name(node_name + ":0")。
最后可以从tensor里面获取shape和dtype。
pb模型图优化
思路一般比较简单:
1,子图连接关系匹配,比如要匹配conv2d+bn+relu这个pattern连接关系。由于每个node只保存其输入的node连接关系,要进行DFS/BFS遍历图一般需要每个node的输入输出,这可以首先读取所有的node连接关系并根据input信息同时创建一个output信息map。
2,子图替换,先创建新的算子,再把旧的算子替换为新的算子。这个需要创建新的node或者直接修改原来的node。旧的不要的算子可以创建个新图拷贝时丢弃,新的node可以直接extend到graph_def。
3,如果替换为TF内置的算子,算子定义可以参考tensorflow raw_ops中的定义,但是有些属性(例如数据类型attr "T")没有列出来:
当然也可以替换为自定义算子,这就需要用户开发和注册自定义算子:
如上所述,TensorFlow的pb模型修改优化可以直接使用python代码实现,极大简化开发过程。当然TensorFlow也可以注册grappler和post rewrite图优化pass在C++层面进行图优化,后者除了可以用于推理,也可以用于训练优化。
saved model与pb模型的相互转换
可以参考:
saved model保存的是一整个训练图,并且参数没有冻结。而只用于模型推理serving并不需要完整的训练图,并且参数不冻结无法进行转TensorRT等极致优化。当然也可以saved_model->frozen pb->saved model来同时利用两者的优点。
pb转onnx
使用tf2onnx库
- 点赞
- 收藏
- 关注作者
评论(0)