使用工具说明:
Linux的虚拟机或者服务器
Quartus Standard 18.1
Intel SoC FPGA Embedded Development Suite Standard 18.1
DE-10 Standard开发板(Cyclone V)
Putty(远程登录工具)
Win32 磁盘映像工具
一、环境准备
- 设置OpenCL开发环境
①将Quartus Standard 18.1
和Intel SoC FPGA Embedded Development Suite Standard 18.1
安装在Linux(可以是Ubuntu,CentOS)的服务器或者虚拟机当中
②下载OpenCL的BSP
链接:https://pan.baidu.com/s/13ebaS7pU7y4pNczyyrdA8A
提取码:wlz3
进行解压,得到一个镜像文件和文件夹
③设置环境变量
在Linux服务器或虚拟机下
测试环境变量是否设置成功export QUARTUS_HOME=/opt/intelFPGA/18.1 export QSYS_ROOTDIR=/opt/intelFPGA/18.1/quartus/sopc_builder/bin export INTELFPGAOCLSDKROOT=/opt/intelFPGA/18.1/hld export QUARTUS_ROOTDIR=$QUARTUS_HOME/quartus export QUARTUS_64BIT=1 export AOCL_BOARD_PACKAGE_ROOT=$INTELFPGAOCLSDKROOT/board/cyclonev export LD_LIBARY_PATH=$LD_LIBARY_PATH:$INTELFPGAOCLSDKROOT/host/arm32/lib export PATH=$PATH:$QUARTUS_ROOTDIR/bin:$QUARTUS_HOME/embedded/ds-5/bin:$QUARTUS_HOME/embedded/ds-5/sw/gcc/bin:$INTELFPGAOCLSDKROOT/bin:$INTELFPGAOCLSDKROOT/host/arm32/bin
source ~/.bashrc aoc -list-board
- 设置开发板运行环境
①烧录镜像
使用Disk Imager,将OpenCL BSP的镜像文件烧录到sd卡上,并使用该sd卡启动开发板
将烧录好的SD卡,插入到板子对应的位置
②安装FT232R USB UART(连接好USB,电源)
驱动下载地址:http://www.ftdichip.com/Drivers/VCP.htm
未安装前
安装后
安装方式:再未安装前,选中有感叹号的进行更新,进入后,选择浏览本地进行安装
③通过串口进行登录
打开putty工具,进行相关设置
进入后,输入root就进入系统下 - 板子通过以太网连接网络
板子与PC直接通过网线直接连接
选择开始->设置->网络和连接
①WLAN的设置
设置完成后,应该会有一个提示信息,信息内容大概是电脑自动分配网关
②以太网的设置
避免后面出现问题,此处是手动对于以太网进行IP设置
③对比WLAN和以太网详细信息
选中WLAN和以太网后,右键选择状态,分别选中详细信息
- 测试网络是否连接成功
登录后,出现下面内容或者后面ifconfig没有IP地址
通过点击板子上面的WARM button
①查看PC下,以太网的IP(跟上面设置一致)
②查看板子的IP
③测试是否连通
板子跟PC之间和baidu
- 设置ssh
开启sshcd /etc/init.d sshd
①sshd re-exec requaires execution with an absolute path
解决方式:
②/etc/init.d/sshd
Could not load host key: /etc/ssh/ssh_host_key Could not load host key: /etc/ssh/ssh_host_rsa_key Could not load host key: /etc/ssh/ssh_host_dsa_key Disabling protocol version 1. Could not load host key Disabling protocol version 2. Could not load host key sshd: no hostkeys available — exiting
解决方式:cd .. ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key /etc/init.d/sshd start
③Could not load host key: /etc/ssh/ssh_host_key Could not load host key: /etc/ssh/ssh_host_rsa_key done
解决方式:ssh-keygen -t rsa -f /etc/ssh/ssh_host_dsa_key /etc/init.d/sshd restart
出现如下,就表示启动成功
二、训练神经网络
- 训练模型(此过程可以在PC机上面完成)
训练模型代码
训练过程结束之后,会在当前目录下生成models文件夹,这个文件夹就是训练完成的模型import os import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data import mnist_forward # 每轮输入的文件数量 BATCH_SIZE = 200 # 设定初始的学习率 LEARNING_RATE_BASE = 0.8 # 设定学习率的衰减率 LEARNING_RATE_DECAY = 0.99 # 设定正则化系数 REGULARIZER = 0.0001 STEPS = 50000 # 设定滑动平局率 MOVING_AVG_DECAY = 0.99 MODEL_SAVE_PATH = 'models' MODEL_NAME = 'mnist_model' def backward(mnist): x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE]) y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE]) y = mnist_forward.forward(x, 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('losses')) # 设定动态学习率 learning_rate = tf.train.exponential_decay( LEARNING_RATE_BASE, global_step, mnist.train.num_examples/BATCH_SIZE, LEARNING_RATE_DECAY, staircase=True ) train_step = tf.train.GradientDescentOptimizer( learning_rate).minimize(loss, global_step=global_step) # 设定滑动平均率 ema = tf.train.ExponentialMovingAverage(MOVING_AVG_DECAY, global_step) ema_op = ema.apply(tf.trainable_variables()) # 对train应用滑动平均率 with tf.control_dependencies([train_step, ema_op]): train_op = tf.no_op(name='train') # 初始化模型保存器 saver = tf.train.Saver() with tf.Session() as sess: init_op = tf.global_variables_initializer() sess.run(init_op) # 从模型路径加载已有的训练结果,恢复到当前session ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH) if ckpt and ckpt.model_checkpoint_path: saver.restore(sess, ckpt.model_checkpoint_path) for i in range(STEPS): # 读取mnist数据集 xs, ys = mnist.train.next_batch(BATCH_SIZE) # 进行对应的训练 _, loss_value, learning_rate_val, step = sess.run( [train_op, loss, learning_rate, global_step], feed_dict={x: xs, y_: ys}) if 0 == i % 1000: fmt = 'After {:05d} steps, loss is {:.09f}, learning rate is {:.09f}' print(fmt.format(step, loss_value, learning_rate_val)) # 将模型进行保存 saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step) def main(): mnist = input_data.read_data_sets('data', one_hot=True) backward(mnist) if __name__ == '__main__': main()
- 提取参数
模型训练出来的参数需要送入到神经网络进行计算,才可以执行推测操作。而Tensorflow训练完成的参数是numpy类型的数据,C/C++以及OpenCL无法直接读取,因此,我们需要将其转换,使之符合C/C++的语法规范。同时,OpenCL的限制,无法向OpenCL传递多维指针/数组,需要将这些数据处理为1维的数组。
参数提取代码
执行完成之后,会在当前目录生成几个文件:layer1_weight,layer1_bias,layer2_weight和layer2_bias,表示权重参数和偏置项参数。然后将文件统一修改为.h结尾的头文件,便于我们将其作为参数,送入到神经网络当中进行计算。由于这些文件当中的数据并没有变量名,需要在文件最开始的地方,添加变量名称定义。(规则:layer1_weight.h头文件当中的参数定义为layer1_weight)同时,需要将文件中的import tensorflow as tf import numpy as np if __name__ == '__main__': reader = tf.train.NewCheckpointReader('models/mnist_model-49001') all_variables = reader.get_variable_to_shape_map() #quantized_conv_list = ['Variable', 'Variable_1', 'Variable_2', 'Variable_3'] #params = ['weight_layer1', 'bias_layer1', 'weight_layer2', 'bias_layer2'] #for key, item in zip(quantized_conv_list, params): # with open(item, 'w+') as f: # f.write(str(reader.get_tensor(key).tolist())) with open('layer1_weight', 'w+') as f: layer1_weight = np.reshape(reader.get_tensor('Variable'), [1, 784*500]) f.write(str(layer1_weight.tolist())) with open('layer1_bias', 'w+') as f: layer1_bias = reader.get_tensor('Variable_1') f.write(str(layer1_bias.tolist())) with open('layer2_weight', 'w+') as f: layer2_weight = np.reshape(reader.get_tensor('Variable_2'), [1, 500*10]) f.write(str(layer2_weight.tolist())) with open('layer2_bias', 'w+') as f: layer2_bias = reader.get_tensor('Variable_3') f.write(str(layer2_bias.tolist()))
[]修改为{}
。
生成的头文件的说明:
layer1_weight包含了784×500个元素,layer1_bias包含500个元素,layer2_weight包含500×10个元素,layer2_bias包含10个元素。
特别注意,所有的变量都是一维数组,因此,需要将这些头文件当中多余的”{”和”}”删除掉。
- 转换图片数据
图片数据转换代码
执行程序,生成一个input的文件。图片数据是神经网络的输入,需要将input文件转换为一个合法的头文件。方法跟上面一样。from PIL import Image import tensorflow as tf import numpy as np def pre_dic(pic_path): img = Image.open(pic_path) reIm = img.resize((28, 28), Image.ANTIALIAS) img_array = np.array(reIm.convert('L')) nm_array = img_array.reshape([1, 784]) nm_array = nm_array.astype(np.float32) img_ready = np.multiply(nm_array, 1.0/255.0) return img_ready if __name__ == '__main__': array = pre_dic('2.bmp') with open('input', 'w') as f: f.write(str(array.tolist()))
三、编译神经网络的OpenCL程序
- 编译内核
在上面的Linux服务器或者虚拟机上
编译完成之后,将会生成mnist_c5.aocx文件cd opencl aoc -v -report -o mnist_c5.aocx mnist_c5.cl -board=de10_standard_sharedonly rm –rf mnist_c5 mnist_c5.aoco
- 编译主机端程序
主机端程序需要使用神经网络的参数,即上面所生成的头文件
编译mkdir include mv ../full/*.h include
生成开发板上的可执行文件mnist_c5make
四、FPGA进行神经网络加速实现手写数字识别
- 开发板设置环境变量
如果没有设置,会出现下面问题
使用putty登录
问题描述:执行后,完全没有反应,一直不变化source ./init_opencl.sh
解决方式:查看板子上SW3的配置是否跟下面的表格一致,不一致就更改为一样的设置
- 上传生成的mnist_c5和mnist_c5.aocx到开发板上(方式比较多)
推荐使用scp - 执行可执行程序
解决方式:
执行结果
最终识别的结果跟预期的结果不一致,但是,整个实现流程大概是这样的。文章内容是在完成后,才写的,可能在某些部分比较不够详细。