手写识别(一)

news/2024/10/22 9:51:06/

文章目录

  • 前言
  • 一、环境介绍
  • 二、训练模型
    • 2.1 前向传播
    • 2.2 后向传播
    • 2.3 测试模型
    • 2.4 提取图像
  • 三、提取参数
  • 总结

前言

  纸上得来终觉浅,绝知此事要躬行。我们已经学习了神经网络理论知识,从这节课开始试着使用FPGA来部署神经网络模型,实现手写识别项目,来看看FPGA的优势。

一、环境介绍

软件

  1. Tensorflow1.15.0
  2. PyCharm Community Edition 2021.3.1
  3. Python3.7.9
  4. opencv-python
  5. pillow
  6. scipy1.2.1
  7. numpy

二、训练模型

2.1 前向传播

  1. 编写mnist_backward.py文件
# -*- coding:utf-8 -*-
import tensorflow as tf# 前向传播算法  两层全连接卷积网络
# 28 × 28 个像素点,表示图片的像素值
INPUT_NODE = 784
# 表示输出10个数,表示0-9出现的概率     最大概率对应的下标表示识别结果
OUTPUT_NODE = 10# 定义隐藏层个数
# LAYER_NODE1 = 500LAYER_NODE1 = 64def forward(x, regularizer):  # regularizer表示正则化系数,值由 mnist_backward 模块传入的def get_weight(shape, regularizer):  # 生成权重矩阵 元素为服从正态分布的随机值# 随机生成参数,去掉偏离过大的正态分布w = tf.Variable(tf.truncated_normal(shape, stddev=0.1))# 加入正则化if regularizer:tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(regularizer)(w))return w# 设定偏置项def get_bias(shape):  # 生成偏置矩阵 元素为服从正态分布的随机值b = tf.Variable(tf.zeros(shape))return b# 由输入层到隐藏层的参数w1形状为[784,500]w1 = get_weight([INPUT_NODE, LAYER_NODE1], regularizer)  # 输入像素与权重进行卷积# 由输入层到隐藏的偏置b1形状为长度500的一维数组,b1 = get_bias([LAYER_NODE1])  # 偏置层# 前向传播结构第一层为输入 x与参数 w1矩阵相乘加上偏置 b1 ,再经过relu函数激活 ,得到隐藏层输出 y1。ny1 = tf.nn.relu(tf.matmul(x, w1) + b1)  # 对第一层输出进行激活# 由隐藏层到输出层的参数w2形状为[500,10]w2 = get_weight([LAYER_NODE1, OUTPUT_NODE], regularizer)  # 对第一次的结果再次卷积# 由隐藏层到输出的偏置b2形状为长度10的一维数组b2 = get_bias([OUTPUT_NODE])  # 再次偏置# 前向传播结构第二层为隐藏输出 y1与参 数 w2 矩阵相乘加上偏置 矩阵相乘加上偏置 b2,得到输出 y。# 由于输出 。由于输出 y要经过softmax oftmax 函数,使其符合概率分布,故输出y不经过 relu函数y = tf.matmul(y1, w2) + b2  # 输出结果  不激活return y

2.2 后向传播

  1. 编写mnist_forward.py文件并运行
# -*- coding:utf-8 -*-# 后向传播算法
import os
import tensorflow as tf
from tensorflow_core.examples.tutorials.mnist import input_dataimport mnist_forward# 每轮输入的文件数量
BATCH_SIZE = 200
# 设定初始的学习率
LEARNING_RATE_BASE = 0.8  # 学习率太小,权重调整幅度太小,训练速度太慢;学习率太大,损失函数会不理想,无法收敛,出现振荡现象
# 设定学习率的衰减率
LEARNING_RATE_DECAY = 0.99  #
# 设定正则化系数
REGULARIZER = 0.0001
# 训练轮数   一共训练50000轮
STEPS = 50000
# 设定滑动平均率
MOVING_AVG_DECAY = 0.99
# 训练好的模型保存路径
MODEL_SAVE_PATH = 'models'
# 训练好的模型保存名称
MODEL_NAME = 'mnist_model'def backward(mnist):# 用placeholder给输入的训练数据x和 标签y_ 占位x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])# 调用mnist_forward文件中的前向传播过程forword()函数,并设置正则化,计算训练数据集上的预测结果yy = mnist_forward.forward(x, REGULARIZER)# 当前计算轮数计数器,设定为不可训练类型global_step = tf.Variable(0, trainable=False)# global_step :全局迭代次数,是一个不可训练的tf变量,在每次的训练迭代过程中,global_step作为优化器的 minimize 函数的参数传入,# 并且会自动自加1,学习率 learning_rate 也会随之改变# 构建交叉熵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,  # 初始学习率 0.8global_step,  # 总共的迭代次数mnist.train.num_examples / BATCH_SIZE,  # 喂入多少轮BATCH_SIZE后,更新一次学习率,此处设为:总样本数/BATCH_SIZELEARNING_RATE_DECAY,  # 学习率的衰减率 0.99staircase=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())# 在模型训练时引入滑动平均可以使模型测试数据上表现更加健壮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)# 从模型路径加载已有的训练结果,恢复到当前sessionckpt = 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数据集 每次喂入 BATCH_SIZE 组训练数据 和 标签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:  # 每隔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():# 在main()函数中,先加载指定路径下的训练数据集,并调用backward函数进行模型训练mnist = input_data.read_data_sets('data', one_hot=True)backward(mnist)if __name__ == '__main__':main()
  1. 后向传播生成的模型文件

在这里插入图片描述

2.3 测试模型

  1. 编写predict.py文件并运行
# -*- coding:utf-8 -*-from PIL import Image
import tensorflow as tf
import numpy as np
from tensorflow_core.examples.tutorials.mnist import input_data
import mnist_backward
import mnist_forward
import timedef restore_model(pic_array):# 加载数据集mnist = input_data.read_data_sets('data', one_hot=True)# 利用tf.Graph()复现之前定义的计算图with tf.Graph().as_default() as gph:# 利用placeholder给训练数据 x 占位x = tf.placeholder(tf.float32, [None, mnist_forward.INPUT_NODE])# 调用mnist_forward文件中的前向传播过程forword()函数y = mnist_forward.forward(x, None)y_ = tf.placeholder(tf.float32, [None, mnist_forward.OUTPUT_NODE])# y的最大值对应的索引号,就是预测的数字的值pre_value = tf.argmax(y, 1)variable_avg = tf.train.ExponentialMovingAverage(mnist_backward.MOVING_AVG_DECAY)variable_to_restore = variable_avg.variables_to_restore()saver = tf.train.Saver(variable_to_restore)# 计算模型在测试集上的准确率correct_prediction = tf.equal(tf.argmax(y, 1), tf.arg_max(y_, 1))accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))with tf.Session() as sess:# 加载指定路径下的ckptckpt = tf.train.get_checkpoint_state(mnist_backward.MODEL_SAVE_PATH)# 若模型存在,则加载出模型到当前对话,在测试数据集上进行准确率验证,并打印出当前轮数下的准确率if ckpt and ckpt.model_checkpoint_path:saver.restore(sess, ckpt.model_checkpoint_path)# 预测操作global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]accuracy_score = sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})print("After %s training step(s), test accuracy = %g" % (global_step, accuracy_score))pre_value = sess.run(pre_value, feed_dict={x: pic_array})# print(pre_value)return pre_valuedef pre_dic(pic_path, wg_bg=False):# 读取图片img = Image.open(pic_path)# 用消除锯齿的方式,将图片resize 为28 × 28reIm = img.resize((28, 28), Image.ANTIALIAS)# 将resize的图片转换为灰度图,并转换为矩阵的方式img_array = np.array(reIm.convert('L'))print("img_array:\n", img_array)# mnist训练的图片要求黑底白字,因此训练之后的模型也只接收黑底白字的图片# 当推测的是白底黑字的单色通道图片时,需要对图片进行反色,变成黑底白字,只留下纯白和纯黑点# 如果推测的是黑底白字的图片,可以不用进行反色# 如果推测的是rgb彩色图片,则推测功能不可用# 该推测代码基于白底黑字的图片,所以需要进行反色。if not wg_bg:threshold = 50for i in range(28):for j in range(28):img_array[i][j] = 255 - img_array[i][j]if img_array[i][j] < threshold:# 黑点img_array[i][j] = 0else:# 白点img_array[i][j] = 255# 将图片整理为1 × 784的矩阵nm_array = img_array.reshape([1, 784])# 转换为浮点型nm_array = nm_array.astype(np.float32)# 将rbg从0-255变为1-255的数img_ready = np.multiply(nm_array, 1.0 / 255.0)return img_readydef application(image_path, wg_bg=False):pic_array = pre_dic(image_path, wg_bg=wg_bg)pre_val = restore_model(pic_array)print("predict result is", pre_val)# print(pre_val[0])if __name__ == '__main__':# 黑底白字的图片推测# application('4.bmp', wg_bg=True)# application('2.bmp', wg_bg=True)# application('6.bmp', wg_bg=True)# application('0.png', wg_bg=False)# application('1.png', wg_bg=False)# application('2.png', wg_bg=False)# application('3.png', wg_bg=False)# application('4.png', wg_bg=False)# application('5.png', wg_bg=False)# application('7.png', wg_bg=False)# application('9.png', wg_bg=False)start = time.time()application('test/test_0.png', wg_bg=True)end = time.time() - startprint("usage of the time is ", end, "s")
  1. 测试结果与推理时间

在这里插入图片描述
  推理结果显示,推理一张图像要使用大概0.8s,每次运行推理图像的时间略有不同。

2.4 提取图像

  1. 编写extract_mnist.py文件并运行
from tensorflow_core.examples.tutorials.mnist import input_data
from scipy import misc
import numpy as np
import osmnist = input_data.read_data_sets('data',one_hot=True)if not os.path.exists('train'):         #创建 train 文件夹作为训练数据集,将训练集压缩包里的图片解压到 train 文件夹中os.mkdir('train')
if not os.path.exists('test'):          #创建 test 文件夹作为测试数据集,将测试集压缩包里的图片解压到 test 文件夹中os.mkdir('test')for (idx, img) in enumerate(mnist.train.images):        #将压缩数据集中的 训练数据图片 解压到 train 文件夹下img_arr = np.reshape(img, [28,28])misc.imsave('train/train_' + str(idx) + '.png',img_arr)for (idx, img) in enumerate(mnist.test.images):         #将压缩数据集中的 测试数据图片 解压到 test 文件夹下img_arr = np.reshape(img, [28,28])misc.imsave('test/test_' + str(idx) + '.png',img_arr)
  1. 提取图像数据运行结果

在这里插入图片描述

三、提取参数

  高层次综合(High Level Synthesis,HLS)是使用高级语言如c,c++进行门级电路的设计,意在和verilogHDL功能一致,只是站在软件工程师的角度进行硬件电路的设计。所以本章节的提取参数就是要把神经网络模型参数转换成c/c++能够处理的文件,比如头文件。

  1. 编写read_params.py并运行,读取权重和偏置
import tensorflow as tfif __name__ == '__main__':reader = tf.compat.v1.train.NewCheckpointReader('models/mnist_model-49001')print("reader",reader)all_variables = reader.get_variable_to_shape_map()quantized_conv_list = ['Variable', 'Variable_1', 'Variable_2', 'Variable_3']params = ['layer1_weight', 'layer1_bias', 'layer2_weight', 'layer2_bias']params_num = [784*64,64,64*10,10]for key, item,params_num in zip(quantized_conv_list, params,params_num):with open(item+".h", 'w+') as f:# f.write(str(reader.get_tensor(key).tolist()))new_str1 = str(reader.get_tensor(key).tolist())new_str2 = new_str1.replace('[','')new_str3 = new_str2.replace(']','')f.write("float "+str(item)+"["+str(params_num)+"]={"+new_str3+"};")f.close()
  1. 提取权重和偏置结果

在这里插入图片描述

  1. 编写readpic_array.py并运行,读取图像数据
from PIL import Image
import numpy as npdef 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_readyif __name__ == '__main__':pic_name = ["test_0.png","test_1.png","test_2.png","test_3.png","test_4.png","test_5.png","test_6.png","test_7.png","test_8.png","test_9.png"]save_pic_name = ["input_0","input_1","input_2","input_3","input_4","input_5","input_6","input_7","input_8","input_9"]for i in range(10):array = pre_dic('test/'+pic_name[i])with open(save_pic_name[i] + '.h', 'w') as f:new_str1 = str(array.tolist())new_str2 = new_str1.replace('[', '')new_str3 = new_str2.replace(']', '')f.write("float " + save_pic_name[i] + "[784]={" + new_str3 + "};")f.close()
  1. 提取图像参数运行结果

在这里插入图片描述
注意:图像一定要归一化

总结

  手写识别第一部分内容到此结束,打包好所有的头文件,为手写识别(二)课程做准备。敬请期待!


http://www.ppmy.cn/news/695522.html

相关文章

android手写输入

http://www.iteye.com/topic/288045 你可以在线测试&#xff1a; http://quake.3322.org/我最先google到一个开源的手写输入引擎是Tomoe: http://tomoe.sourceforge.jp&#xff0c;它包括识别引擎&#xff0c;整套的用户界面&#xff08;GTK&#xff09;以及SCIM的输入法模块&…

手写输入和软键盘输入

今天项目需要完成一个支持手写和键盘都可以输入的功能&#xff0c;点击键盘切换到软键盘输入&#xff0c;此时按钮会变成手写&#xff0c;在点击就回到手写。按钮是checkbox。功能很简单。 支持手写和键盘输入,其实并不难&#xff0c;就是布局麻烦点&#xff0c;还有就是画布麻…

手写输入---随手写

手写输入—随手写 最近做的是关于教育的app&#xff0c;分老师版和学生版&#xff1b;学生版有做题功能&#xff0c;领导最近看到别的app里面有手写输入功能&#xff0c;保留原笔迹&#xff0c;也就是说写成什么样就是什么样&#xff0c;然后保存&#xff0c;上传。 思路1&…

手写apply

在自己实现apply之前&#xff0c;我们需要先了解一下对函数使用了apply之后完成了那些效果&#xff0c;再考虑怎么自己编写代码去实现这些效果。 let name "张三" let personObj {name: "李四", } function getPersonName(){console.log(this)console.lo…

中文手写输入 (一)

最近业余时间在学习android的系统&#xff0c;想尝试将中文手写输入支持加到android上去&#xff0c;将最近的学习结果记录下来。 先上个截图: [img]/upload/attachment/56882/6bec42ff-5f30-3a2b-8baf-cc5030009305.png[/img] 你可以在线测试&#xff1a; [url]http://quake.…

如何在Windows 10上使用手写输入

Windows 10’s handwriting keyboard allows you to enter text into any application with a pen or other stylus. It even works on old desktop applications. Windows 10的手写键盘允许您使用笔或其他手写笔在任何应用程序中输入文本。 它甚至可以在旧的桌面应用程序上运行…

Go 接口声明

Go 语言中的接口是一种非常强大的特性&#xff0c;它允许我们定义一组方法&#xff0c;然后将其绑定到不同的数据类型上。本文将详细介绍 Go 语言中接口的声明、实现和使用方法&#xff0c;包括接口的定义、接口实现、接口类型断言等方面。 接口的定义 在 Go 语言中&#xff…

系列二、RocketMQ基本概念 系统架构

一、基本概念 1.1、消息&#xff08;Message&#xff09; 消息是指&#xff0c;消息系统所传输信息的物理载体&#xff0c;生产和消费数据的最小单位&#xff0c;每条消息必须输入一个主题。 1.2、主题&#xff08;Topic&#xff09; Topic表示一类消息的集合&#xff0c;每个…