摘要: LeNet-5 是 Yann LeCun 在 1998 年设计的用于手写数字识别的卷积神经网络, 当年美国大多数银行就是用它来识别支票上面的手写数字的, 它是早期卷积神经网络中最有代表性的实验系统之一. 可以说, LeNet-5 就相当于编程语言入门中的 "Hello world!".
华为的昇腾训练芯片一直是大家所期待的, 目前已经开始提供公测, 如何在昇腾训练芯片上运行一个训练任务, 这是目前很多人都在采坑过程中, 所以我写了一篇指导文章, 附带上所有相关源代码. 注意, 本文并没有包含环境的安装, 请查看另外相关文档.
环境约束: 昇腾 910 目前仅配套 TensorFlow 1.15 版本.
基础镜像上传之后, 我们需要启动镜像命令, 以下命令挂载了 8 块卡 (单机所有卡):
docker run -it --net=host --device=/dev/davinci0 --device=/dev/davinci1 --device=/dev/davinci2 --device=/dev/davinci3 --device=/dev/davinci4 --device=/dev/davinci5 --device=/dev/davinci6 --device=/dev/davinci7 --device=/dev/davinci_manager --device=/dev/devmm_svm --device=/dev/hisi_hdc -v /var/log/npu/slog/container/docker:/var/log/npu/slog -v /var/log/npu/conf/slog/slog.conf:/var/log/npu/conf/slog/slog.conf -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ -v /usr/local/Ascend/driver/tools/:/usr/local/Ascend/driver/tools/ -v /data/:/data/ -v /home/code:/home/local/code -v ~/context:/cache ubuntu_18.04-docker.arm64v8:v2 /bin/bash
设置环境变量并启动手写字训练网络:
- #!/bin/bash
- export LD_LIBRARY_PATH=/usr/local/lib/:/usr/local/HiAI/runtime/lib64
- export PATH=/usr/local/HiAI/runtime/ccec_compiler/bin:$PATH
- export CUSTOM_OP_LIB_PATH=/usr/local/HiAI/runtime/ops/framework/built-in/tensorflow
- export DDK_VERSION_PATH=/usr/local/HiAI/runtime/ddk_info
- export WHICH_OP=GEOP
- export NEW_GE_FE_ID=1
- export GE_AICPU_FLAG=1
- export OPTION_EXEC_EXTERN_PLUGIN_PATH=/usr/local/HiAI/runtime/lib64/plugin/opskernel/libfe.so:/usr/local/HiAI/runtime/lib64/plugin/opskernel/libaicpu_plugin.so:/usr/local/HiAI/runtime/lib64/plugin/opskernel/libge_local_engine.so:/usr/local/H
- iAI/runtime/lib64/plugin/opskernel/librts_engine.so:/usr/local/HiAI/runtime/lib64/libhccl.so
- export OP_PROTOLIB_PATH=/usr/local/HiAI/runtime/ops/built-in/
- export DEVICE_ID=2
- export PRINT_MODEL=1
- #export DUMP_GE_GRAPH=2
- #export DISABLE_REUSE_MEMORY=1
- #export DUMP_OP=1
- #export SLOG_PRINT_TO_STDOUT=1
- export RANK_ID=0
- export RANK_SIZE=1
- export JOB_ID=10087
- export OPTION_PROTO_LIB_PATH=/usr/local/HiAI/runtime/ops/op_proto/built-in/
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/Ascend/fwkacllib/lib64/:/usr/local/Ascend/driver/lib64/common/:/usr/local/Ascend/driver/lib64/driver/:/usr/local/Ascend/add-ons/
- export PYTHONPATH=$PYTHONPATH:/usr/local/Ascend/opp/op_impl/built-in/ai_core/tbe
- export PATH=$PATH:/usr/local/Ascend/fwkacllib/ccec_compiler/bin
- export ASCEND_HOME=/usr/local/Ascend
- export ASCEND_OPP_PATH=/usr/local/Ascend/opp
- export SOC_VERSION=Ascend910
- rm -f *.pbtxt
- rm -f *.txt
- rm -r /var/log/npu/slog/*.log
- rm -rf train_url/*
python3 mnist_train.py
以下训练案例中我使用的 lecun 大师的 LeNet-5 网络, 先简单介绍 LeNet-5 网络:
LeNet5 诞生于 1994 年, 是最早的卷积神经网络之一, 并且推动了深度学习领域的发展. 自从 1988 年开始, 在多年的研究和许多次成功的迭代后, 这项由 Yann LeCun 完成的开拓性成果被命名为 LeNet5.
LeNet-5 包含七层, 不包括输入, 每一层都包含可训练参数 (权重), 当时使用的输入数据是 32*32 像素的图像. 下面逐层介绍 LeNet-5 的结构, 并且, 卷积层将用 Cx 表示, 子采样层则被标记为 Sx, 完全连接层被标记为 Fx, 其中 x 是层索引.
层 C1 是具有六个 5*5 的卷积核的卷积层 (convolution), 特征映射的大小为 28*28, 这样可以防止输入图像的信息掉出卷积核边界. C1 包含 156 个可训练参数和 122304 个连接.
层 S2 是输出 6 个大小为 14*14 的特征图的子采样层 (subsampling/pooling). 每个特征地图中的每个单元连接到 C1 中的对应特征地图中的 2*2 个邻域. S2 中单位的四个输入相加, 然后乘以可训练系数 (权重), 然后加到可训练偏差 (bias). 结果通过 S 形函数传递. 由于 2*2 个感受域不重叠, 因此 S2 中的特征图只有 C1 中的特征图的一半行数和列数. S2 层有 12 个可训练参数和 5880 个连接.
层 C3 是具有 16 个 5-5 的卷积核的卷积层. 前六个 C3 特征图的输入是 S2 中的三个特征图的每个连续子集, 接下来的六个特征图的输入则来自四个连续子集的输入, 接下来的三个特征图的输入来自不连续的四个子集. 最后, 最后一个特征图的输入来自 S2 所有特征图. C3 层有 1516 个可训练参数和 156 000 个连接.
层 S4 是与 S2 类似, 大小为 2*2, 输出为 16 个 5*5 的特征图. S4 层有 32 个可训练参数和 2000 个连接.
层 C5 是具有 120 个大小为 5*5 的卷积核的卷积层. 每个单元连接到 S4 的所有 16 个特征图上的 5*5 邻域. 这里, 因为 S4 的特征图大小也是 5*5, 所以 C5 的输出大小是 1*1. 因此 S4 和 C5 之间是完全连接的. C5 被标记为卷积层, 而不是完全连接的层, 是因为如果 LeNet-5 输入变得更大而其结构保持不变, 则其输出大小会大于 1*1, 即不是完全连接的层了. C5 层有 48120 个可训练连接.
F6 层完全连接到 C5, 输出 84 张特征图. 它有 10164 个可训练参数. 这里 84 与输出层的设计有关.
LeNet 的设计较为简单, 因此其处理复杂数据的能力有限; 此外, 在近年来的研究中许多学者已经发现全连接层的计算代价过大, 而使用全部由卷积层组成的神经网络.
LeNet-5 网络训练脚本是 mnist_train.py, 具体代码:
- import os
- import numpy as np
- import tensorflow as tf
- import time
- from tensorflow.examples.tutorials.mnist import input_data
- import mnist_inference
- from npu_bridge.estimator import npu_ops #导入 NPU 算子库
- from tensorflow.core.protobuf.rewriter_config_pb2 import RewriterConfig #重写 tensorFlow 里的配置, 针对 NPU 的配置
- batch_size = 100
- learning_rate = 0.1
- training_step = 10000
- model_save_path = "./model/"
- model_name = "model.ckpt"
- def train(mnist):
- x = tf.placeholder(tf.float32, [batch_size, mnist_inference.image_size, mnist_inference.image_size, mnist_inference.num_channels], name = 'x-input')
- y_ = tf.placeholder(tf.float32, [batch_size, mnist_inference.num_labels], name = "y-input")
- regularizer = tf.contrib.layers.l2_regularizer(0.001)
- y = mnist_inference.inference(x, train = True, regularizer = regularizer) #推理过程
- global_step = tf.Variable(0, trainable=False)
- cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits = y, labels = tf.argmax(y_, 1)) #损失函数
- cross_entropy_mean = tf.reduce_mean(cross_entropy)
- loss = cross_entropy_mean + tf.add_n(tf.get_collection("loss"))
- train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step = global_step) #优化器调用
- saver = tf.train.Saver() #启动训练
- # 以下代码是 NPU 所必须的代码, 开始配置参数
- config = tf.ConfigProto(
- allow_soft_placement = True,
- log_device_placement = False)
- custom_op = config.graph_options.rewrite_options.custom_optimizers.add()
- custom_op.name = "NpuOptimizer"
- custom_op.parameter_map["use_off_line"].b = True
- #custom_op.parameter_map["profiling_mode"].b = True
- #custom_op.parameter_map["profiling_options"].s = tf.compat.as_bytes("task_trace:training_trace")
- config.graph_options.rewrite_options.remapping = RewriterConfig.OFF
- # 配置参数结束
- writer = tf.summary.FileWriter("./log_dir", tf.get_default_graph())
- writer.close()
- # 参数初始化
- with tf.Session(config = config) as sess:
- tf.global_variables_initializer().run()
- start_time = time.time()
- for i in range(training_step):
- xs, ys = mnist.train.next_batch(batch_size)
- reshaped_xs = np.reshape(xs, (batch_size, mnist_inference.image_size, mnist_inference.image_size, mnist_inference.num_channels))
- _, loss_value, step = sess.run([train_step, loss, global_step], feed_dict={x:reshaped_xs, y_:ys})
- #每训练 10 个 epoch 打印损失函数输出日志
- if i % 10 == 0:
- print("****************************++++++++++++++++++++++++++++++++*************************************\n" * 10)
- print("After %d training steps, loss on training batch is %g, total time in this 1000 steps is %s." % (step, loss_value, time.time() - start_time))
- #saver.save(sess, os.path.join(model_save_path, model_name), global_step = global_step)
- print("****************************++++++++++++++++++++++++++++++++*************************************\n" * 10)
- start_time = time.time()
- def main():
- mnist = input_data.read_data_sets('MNIST_DATA/', one_hot= True)
- train(mnist)
- if __name__ == "__main__":
- main()
本文主要讲述了经典卷积神经网络之 LeNet-5 网络模型和迁移至昇腾 D910 的实现, 希望大家快来动手操作一下试试看!
点击关注, 第一时间了解华为云新鲜技术~
来源: https://www.cnblogs.com/huaweiyun/p/13186753.html