使用C语言实现模型的推理(一)
WHY?
现在推理框架其实已经有很多了,比如 tensorflow、pytorch、onnxruntime,作为一名搞边缘计算的嵌入式工程师,会更喜欢tflite micro、mnn、ncnn这样的用于移动端的推理引擎。因为这可以将 AI 应用于千家万户。然而,在实际应用中会发现,在微控制处理器上部署神经,即使推理引擎使用tflite micro,也不可避免的会占用过多的内存体积,无论是从模型上还是从推理框架上。
比如,只包含几个简单算子,使用-os
来编译tflite micro
,库体积也要占用几十KB
的内存。
因此,我打算用low-level的C语言来实现一套推理引擎,目标是:
- 【避繁就简】面向微控制处理器,相对简单的神经网络任务
- 【寸土寸金】占用内存资源最小( Flash 的 text 段和 data 段)实测比
tflite micro
要小很多 - 【大道至简】全部聚焦于模型推理计算,不做一丝丝多余的操作
我没有系统的写过推理框架,相当于是从零开始。这一系列博客我会比较详细的记录我的想法、思路和尝试过程,希望对大家有所帮助。
我在整个过程中大量使用了chatGPT来梳理思路和编写代码,向chatGPT致敬
思路整理
从怎么把大象放到冰箱里开始
这是一个经典的幽默悖论问题,一般的回答是分三步:
1. 打开冰箱门。
2. 把大象放进去。
3. 关上冰箱门。
这个问题的幽默之处在于它看似是一个复杂的问题,但实际上的解决方案却非常简单。
在我看来,比较关键的分两个部分,一个是动作的顺序
,一个是动作本身
。
怎么让模型推理跑起来
思路就很简单了:要想跑一个模型,首先要理清楚算子之间的依赖关系
,其次要有各个算子的实现
。
本文先整理前者的内容。
生成一个模型
考虑到实际应用中tflite模型用的比较多,我们先生成一个 tflite 模型。
import tensorflow as tf
from tensorflow.keras import layers
# 定义模型输入
input1 = layers.Input(shape=(4,), name='input1')
input2 = layers.Input(shape=(4,), name='input2')
# 分别经过全连接层和激活层
x1 = layers.Dense(8, activation='relu', use_bias=True, bias_initializer='ones')(input1)
x2 = layers.Dense(8, activation='relu')(input2)
# 二者结果相加
x = layers.Add()([x1, x2])
# 最后经过softmax层得到结果
output = layers.Dense(2, activation='softmax', name='output')(x)
# 创建模型
model = tf.keras.Model(inputs=[input1, input2], outputs=output)
# 编译模型
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# 打印模型的结构
model.summary()
# 转换模型为TFLite格式
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS] # 不希望看到DELEGATE操作,在转换和运行模型时不使用硬件加速。只使用TensorFlow Lite内建的操作
tflite_model = converter.convert()
# 保存TFLite模型
with open('model_test.tflite', 'wb') as f:
f.write(tflite_model)
其中,model.summary()
打印出的模型信息如下:
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==============================================================