玩转Ascend310模型推理(一)模型训练与离线转换
Ascend310系列推理芯片,广泛用于人脸识别、图像分类、智能摄像领域,为智慧城市、智慧交通、智慧金融等场景提供超强AI推理能力,主要产品包括Atlas 300I 推理卡、Atlas 200 AI加速模块、Atlas 200 DK 开发者套件等,参考官方介绍[Atlas 300I](https://e.huawei.com/cn/products/cloud-computing-dc/atlas/atlas-300-ai)。
本文通过使用tensorflow,以手写数字识别为例,通过tensorflow常用的3种建模方式分别演示和介绍模型训练、模型保存、以及离线模型转换三个步骤,在下一篇(二)模型加载与运行推理中介绍转换后的模型如何部署与运行推理。
运行环境: Ubuntu 18.04 x86_64
训练和模型保存,代码参考。
项目目录结构:
├── data │ ├── data.py │ ├── mnist_images.png │ └── mnist_labels_uint8 ├── raw │ └── model.py ├── keras │ └── model.py ├── keras_seq │ └── model.py └── train.py
数据准备
手写字数据集由2个文件组成,一个图片文件,一个标签文件,图像文件为png格式,按照28*28(784)单通道黑白连续存储65000张图片,即65000*784个数据,合并为一个png文件方式存储,标签文件中每个手写数字由一个10字节数组表示,数字所在元素值为1,其他为0,一共65000*10个数据。
数据获取部分代码参考data/data.py。
def get_train(self, size): if size > self.size: size = self.size img = self.image[: size * IMG_W * IMG_H].reshape((size, IMG_W, IMG_H, 1)) lab = self.label[: size * LAB_W].reshape((size, LAB_W)) return img, lab
模型建立与训练
RAW模型
RAW模型为使用tensorflow基础构图模型建立,使用底层API实现,代码参考raw/model.py。
初始化变量:
self.X = tf.compat.v1.placeholder(dtype=tf.float32, shape=[None, 28, 28, 1], name='x_img') self.Y = tf.compat.v1.placeholder(dtype=tf.float32, shape=[None, 10], name='y_num') ... self.CW1 = tf.Variable(tf.random.normal([5, 5, 1, 32]), name='conv_1_weight') self.CB1 = tf.Variable(tf.random.normal([32]), name='conv_1_bias') ...
定义卷积、池化等操作:
def conv2d(x, kernel, bias, strides=1, name='conv2d'): ishape = x.get_shape() convo = tf.nn.conv2d(x, kernel, strides, padding='VALID') biaso = tf.nn.bias_add(convo, bias) oshape = biaso.get_shape() print('op %s shape %s ==> %s' % (name, ishape, oshape)) return biaso
构图:
c1 = conv2d(self.X, self.CW1, self.CB1, name='conv2d_1') p1 = maxpool2d(c1, name='max_pool2d_1') ... self.prediction = tf.nn.softmax(out, name='prediction') self.out_node_name = 'prediction'
构造损失函数:
self.loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=out, labels=self.Y)) optimizer = tf.compat.v1.train.AdamOptimizer(learning_rate=0.009) self.train_op = optimizer.minimize(self.loss_op) correct_pred = tf.equal(tf.argmax(self.prediction, 1), tf.argmax(self.Y, 1)) self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)
训练:
for step in range(5): for batch in range(int(size / batch_size)): sess.run(self.train_op, feed_dict={self.X: img[batch * batch_size: batch * batch_size + batch_size], self.Y: lab[batch * batch_size: batch * batch_size + batch_size], self.D: 0.2})
执行训练:
python3 train.py
keras模型
keras模型为高度抽象模型,不需要像RAW模型那样定义底层变量,在构图中手动计算参数变量与tensor的关系,一切通过抽象好的模型叠加即可,参数在keras内部自动定义和计算,基本由构图和训练两部即可完成。
构图,注意要指定keras.Input否则导出的模型没有Placeholder:
X = keras.Input(shape=(28, 28, 1), name='x_img') c1 = layers.Conv2D(32, 5)(X) p1 = layers.MaxPool2D(2, 2)(c1) ... Y = layers.Dense(10, activation="softmax")(f2)
训练:
self.model.compile(optimizer=keras.optimizers.Adam(), loss=keras.losses.CategoricalCrossentropy(), metrics=[keras.metrics.CategoricalAccuracy()]) ... self.model.fit(img, lab, batch_size=batch_size, epochs=2, validation_data=(timg, tlab))
执行训练:
python3 train.py --model keras
keras sequence模型
sequence模型是更加抽象的针对串行执行模型定制的一种模式,其语法更加简洁。
构图,一样,要注意要指定keras.Input否则导出的模型没有Placeholder:
self.model = keras.Sequential() X = keras.Input(shape=(28, 28, 1), name='x_img') self.model.add(X) self.model.add(layers.Conv2D(32, 5)) self.model.add(layers.MaxPool2D(2, 2)) ... Y = layers.Dense(10, activation="softmax") self.model.add(Y)
训练:
self.model.compile(optimizer=keras.optimizers.Adam(), loss=keras.losses.CategoricalCrossentropy(), metrics=[keras.metrics.CategoricalAccuracy()]) ... self.model.fit(img, lab, batch_size=batch_size, epochs=2, validation_data=(timg, tlab))
执行训练:
python3 train.py --model keras_seq
模型保存
模型保存是一个需要特别注意的地方,目前tensorflow至少存在4种或者更多的模型或者训练结果的保存方式,与Ascend310兼容的模型格式是将参数与图以常量方式保存为二进制的proto文件方式,这样可以在离线推理中使用,这一点要特别注意,以下重点介绍。
执行完训练后,在代码路径下会生产如下目录模型保存的目录结构:
── saved_model ├── checkpoint #------------------> checkpoint模型结果 │ ├── checkpoint │ ├── saved_ckpt.data-00000-of-00001 │ ├── saved_ckpt.index │ └── saved_ckpt.meta ├── graph #------------------> 只有graph节点,没有weight │ └── saved_graph.pbtxt ├── trainsaver #------------------> saved_model格式,checkpoint的另一种形式 │ ├── saved_model.pb │ └── variables │ ├── variables.data-00000-of-00001 │ └── variables.index └── whole_graph #------------------> Ascend310兼容模型 ├── saved_model.pb └── saved_model.pbtxt
Ascend310兼容方式
通过给定训练完成的session与prediction的图节点,将prediction的子图和相关的weight参数转换为一张常量的图,然后序列化为二进制的protobuf文件。
def saved_graph_and_variable(self, sess, saved_dir): ... out_graph = tf.compat.v1.graph_util.convert_variables_to_constants(sess, sess.graph_def, [self.out_node_name]) with open(os.path.join(saved_dir, 'saved_model.pb'), 'wb+') as f: f.write(out_graph.SerializeToString()) f.close() tf.io.write_graph(out_graph, saved_dir, 'saved_model.pbtxt')
**注意,所有的模型必须有Placeholder,否则无法做离线转换,RAW方式是必选,keras要加keras.Input,不加虽然能训练成功,但是做离线模型转换时会失败**。
**keras的session获取**:
tf.compat.v1.keras.backend.get_session()
**keras的prediction节点名字获取**:
Y = layers.Dense(10, activation="softmax")(f2) self.out_node_name = Y.op.name
**keras_seq中的prediction节点名字获取**:
Y = layers.Dense(10, activation="softmax") self.model.add(Y) self.out_node_name = Y.output.op.name
其中saved_model.pb为二进制的protobuf文件,为可用Ascend310兼容模型文件,saved_model.pbtxt为文本格式,作为校验,可以查看其中数据,可以看到内部有图节点由weight值。
其他几种方式介绍
checkpoint模型文件,可以在训练过程中保存不同checkpoint,防止最终训练失败或者崩溃后,中间过程的训练结果丢失。
def saved_checkpoint(self, sess, saved_dir): ... saver = tf.compat.v1.train.Saver() saver.save(sess, os.path.join(saved_dir, 'saved_ckpt'))
saved_model也是一种checkpoint,是目前比较常用的方式,提供了save和restore:
def saved_model_save(self, sess, saved_dir): ... builder = tf.compat.v1.saved_model.Builder(saved_dir) builder.add_meta_graph_and_variables(sess, ["saved_model"]) builder.save()
graph,只保存了graph节点,没有weight,不能被使用:
def saved_graph(self, sess, saved_dir): ... tf.io.write_graph(sess.graph_def, saved_dir, 'saved_graph.pbtxt')
目前saved_model和checkpoint的结果还不能作为Ascend310离线推理使用,但可以在其他Ascend做在线推理用。
离线模型转换
前面训练号模型后,使用Ascend310提供的ATC模型转换工具,可以将tensorflow模型转换为Ascend310格式的模型文件,然后使用Ascend310提供的推理库加载并运行。
运行环境: Ubuntu x86_64
安装开发环境
Ascend社区已经提供了完整的开发环境安装手册,请参考[链接](https://support.huaweicloud.com/instg-cli-cann/atlascli_03_0023.html), 软件安装> 软件安装指南 (开发&运行场景, 通过命令行方式) > 安装开发环境(推理)> 安装前准备> Ubuntu x86_64系统。
以下操作建议在root用户下执行。
- 创建HwHiAiUser用户
# useradd -m HwHiAiUser
- 按照要求按照必备软件与Python
注意Python版本一定是3.7.5版本,一定要完整按照手册说明逐步完成。
- 准备软件包
参考手册中软件包准备章节,链接,下载`Ascend-cann-toolkit_20.1.rc1_linux-x86_64.run`这个文件。
- 安装软件包
# chmod +x Ascend-cann-toolkit_20.1.rc1_linux-x86_64.run # ./Ascend-cann-toolkit_20.1.rc1_linux-x86_64.run --install
记录一下这个路径,后面会用到: `/usr/local/Ascend/ascend-toolkit/latest`。
运行ATC模型转换
详细手册参考: 链接,推理场景> 开发辅助工具指南> ATC工具使用指南> 使用入门> 准备动作。
- 设置环境变量
install_path即为上面安软件包安装的路径,`/usr/local/Ascend/ascend-toolkit/latest`。
export install_path=/usr/local/Ascend/ascend-toolkit/latest export PATH=/usr/local/python3.7.5/bin:${install_path}/atc/ccec_compiler/bin:${install_path}/atc/bin:$PATH export PYTHONPATH=${install_path}/atc/python/site-packages:${install_path}/atc/python/site-packages/auto_tune.egg/auto_tune:${install_path}/atc/python/site-packages/schedule_search.egg:$PYTHONPATH export LD_LIBRARY_PATH=${install_path}/atc/lib64:$LD_LIBRARY_PATH export ASCEND_OPP_PATH=${install_path}/opp
- 执行模型转换
# atc --model=./saved_model/whole_graph/saved_model.pb --framework=3 --output=./output/hand_write --soc_version=Ascend310
手册的模板是这样写的,但是你会发现会报如下的错误:
ATC start working now, please wait for a moment. ATC run failed, Please check the detail log, Try 'atc --help' for more information E19000: Path[/usr/local/Ascend/ascend-toolkit/20.1.rc1/x86_64-linux/opp/op_impl/custom/ai_core/tbe/config/ascend310]'s realpath is empty, errmsg[The file path does not exist.] E10001: Invalid value for x_img[-1], maybe you should set input_shape to specify its shape.
莫慌,这个是因为tensorflow在训练的时候,我们指定的输入tensor x_img的shape是[None, 28, 28, 1],第一维是batch,这个是可变的,在转换的时候要手动指定一下:
# atc --model=./saved_model/whole_graph/saved_model.pb --framework=3 --input_shape x_img:1,28,28,1 --output=./output/hand_write --soc_version=Ascend310 ATC start working now, please wait for a moment. ATC run success, welcome to the next use.
OK,转换成功,--input_shape指定的x_img即输入的tensor名字,第一维可以根据自身需要指定。
总结
确保成功的几个注意点:
- 模型保存的方式非常重要,一定要使用Ascend310兼容的方式保存。
- 模型一定要设置Placeholder,keras对应为keras.Input。
- 开发环境安装一定严格按照手册指导完成。
- 转要手动指定input的shape。
好了,关于离线推理模型训练与转换就介绍到这里,下一篇继续介绍离线模型加载与运行,敬请期待。
著作权归作者所有,禁止转载
- 点赞
- 收藏
- 关注作者
评论(0)