基于FPGA实现手写数字的识别——OpenCL

文章目录

  • 一、环境准备
  • 二、训练神经网络
  • 三、编译神经网络的OpenCL程序
  • 四、FPGA进行神经网络加速实现手写数字识别

使用工具说明:
Linux的虚拟机或者服务器
Quartus Standard 18.1
Intel SoC FPGA Embedded Development Suite Standard 18.1
DE-10 Standard开发板(Cyclone V)
Putty(远程登录工具)
Win32 磁盘映像工具

一、环境准备

  1. 设置OpenCL开发环境
    ①将Quartus Standard 18.1Intel 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
    
  2. 设置开发板运行环境
    ①烧录镜像
    使用Disk Imager,将OpenCL BSP的镜像文件烧录到sd卡上,并使用该sd卡启动开发板
    基于FPGA实现手写数字的识别——OpenCL_第1张图片
    将烧录好的SD卡,插入到板子对应的位置
    ②安装FT232R USB UART(连接好USB,电源)
    驱动下载地址:http://www.ftdichip.com/Drivers/VCP.htm
    未安装前
    基于FPGA实现手写数字的识别——OpenCL_第2张图片
    安装后
    基于FPGA实现手写数字的识别——OpenCL_第3张图片
    安装方式:再未安装前,选中有感叹号的进行更新,进入后,选择浏览本地进行安装
    ③通过串口进行登录
    打开putty工具,进行相关设置
    基于FPGA实现手写数字的识别——OpenCL_第4张图片
    进入后,输入root就进入系统下
  3. 板子通过以太网连接网络
    板子与PC直接通过网线直接连接
    选择开始->设置->网络和连接
    ①WLAN的设置
    基于FPGA实现手写数字的识别——OpenCL_第5张图片设置完成后,应该会有一个提示信息,信息内容大概是电脑自动分配网关
    ②以太网的设置
    避免后面出现问题,此处是手动对于以太网进行IP设置
    基于FPGA实现手写数字的识别——OpenCL_第6张图片
    ③对比WLAN和以太网详细信息
    选中WLAN和以太网后,右键选择状态,分别选中详细信息
    基于FPGA实现手写数字的识别——OpenCL_第7张图片
  4. 测试网络是否连接成功
    登录后,出现下面内容或者后面ifconfig没有IP地址
    在这里插入图片描述
    通过点击板子上面的WARM button
    ①查看PC下,以太网的IP(跟上面设置一致)
    ②查看板子的IP
    基于FPGA实现手写数字的识别——OpenCL_第8张图片
    ③测试是否连通
    板子跟PC之间和baidu
    基于FPGA实现手写数字的识别——OpenCL_第9张图片
  5. 设置ssh
    开启ssh
    cd /etc/init.d
    sshd
    
    基于FPGA实现手写数字的识别——OpenCL_第10张图片
    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
    
    基于FPGA实现手写数字的识别——OpenCL_第11张图片
    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
    
    基于FPGA实现手写数字的识别——OpenCL_第12张图片
    出现如下,就表示启动成功基于FPGA实现手写数字的识别——OpenCL_第13张图片

二、训练神经网络

  1. 训练模型(此过程可以在PC机上面完成)
    训练模型代码
    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()
    
    
    训练过程结束之后,会在当前目录下生成models文件夹,这个文件夹就是训练完成的模型
    基于FPGA实现手写数字的识别——OpenCL_第14张图片
  2. 提取参数
    模型训练出来的参数需要送入到神经网络进行计算,才可以执行推测操作。而Tensorflow训练完成的参数是numpy类型的数据,C/C++以及OpenCL无法直接读取,因此,我们需要将其转换,使之符合C/C++的语法规范。同时,OpenCL的限制,无法向OpenCL传递多维指针/数组,需要将这些数据处理为1维的数组。
    参数提取代码
    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,layer1_bias,layer2_weight和layer2_bias,表示权重参数和偏置项参数。然后将文件统一修改为.h结尾的头文件,便于我们将其作为参数,送入到神经网络当中进行计算。由于这些文件当中的数据并没有变量名,需要在文件最开始的地方,添加变量名称定义。(规则:layer1_weight.h头文件当中的参数定义为layer1_weight)同时,需要将文件中的[]修改为{}
    基于FPGA实现手写数字的识别——OpenCL_第15张图片
    生成的头文件的说明:
    layer1_weight包含了784×500个元素,layer1_bias包含500个元素,layer2_weight包含500×10个元素,layer2_bias包含10个元素。
    特别注意,所有的变量都是一维数组,因此,需要将这些头文件当中多余的”{”和”}”删除掉。
  3. 转换图片数据
    图片数据转换代码
    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()))
    
    执行程序,生成一个input的文件。图片数据是神经网络的输入,需要将input文件转换为一个合法的头文件。方法跟上面一样。

三、编译神经网络的OpenCL程序

  1. 编译内核
    在上面的Linux服务器或者虚拟机上
    cd opencl
    aoc -v -report -o mnist_c5.aocx mnist_c5.cl -board=de10_standard_sharedonly
    rm –rf mnist_c5 mnist_c5.aoco
    
    编译完成之后,将会生成mnist_c5.aocx文件
  2. 编译主机端程序
    主机端程序需要使用神经网络的参数,即上面所生成的头文件
    mkdir include
    mv ../full/*.h include
    
    编译
    make
    
    生成开发板上的可执行文件mnist_c5

四、FPGA进行神经网络加速实现手写数字识别

  1. 开发板设置环境变量
    如果没有设置,会出现下面问题在这里插入图片描述
    使用putty登录
    source ./init_opencl.sh
    
    问题描述:执行后,完全没有反应,一直不变化
    解决方式:查看板子上SW3的配置是否跟下面的表格一致,不一致就更改为一样的设置
    基于FPGA实现手写数字的识别——OpenCL_第16张图片
  2. 上传生成的mnist_c5和mnist_c5.aocx到开发板上(方式比较多)
    推荐使用scp在这里插入图片描述
  3. 执行可执行程序在这里插入图片描述
    解决方式:在这里插入图片描述
    执行结果基于FPGA实现手写数字的识别——OpenCL_第17张图片
    最终识别的结果跟预期的结果不一致,但是,整个实现流程大概是这样的。文章内容是在完成后,才写的,可能在某些部分比较不够详细。

你可能感兴趣的:(FPGA,OpenCL)