python+tensorflow对mnist数据集的神经网络训练和推理 加参数提取----规范版
一、数据集获取
extract_mnist.py
from tensorflow_core.examples.tutorials.mnist import input_data
from scipy import misc
import numpy as np
import os
mnist = 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.前向传播
mnist_forward.py
# -*- coding:utf-8 -*-
import tensorflow as tf
# 前向传播算法 两层全连接卷积网络
# 28 × 28 个像素点,表示图片的像素值
INPUT_NODE = 784
# 表示输出10个数,表示0-9出现的概率 最大概率对应的下标表示识别结果
OUTPUT_NODE = 10
# 定义隐藏层个数
# LAYER_NODE1 = 500
LAYER_NODE1 = 64
def 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。n
y1 = 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.反向传播
mnist_backward.py
# -*- coding:utf-8 -*-
# 后向传播算法
import os
import tensorflow as tf
from tensorflow_core.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
#训练轮数 一共训练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()函数,并设置正则化,计算训练数据集上的预测结果y
y = 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.8
global_step, # 总共的迭代次数
mnist.train.num_examples/BATCH_SIZE, #喂入多少轮BATCH_SIZE后,更新一次学习率,此处设为:总样本数/BATCH_SIZE
LEARNING_RATE_DECAY, # 学习率的衰减率 0.99
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())
# 在模型训练时引入滑动平均可以使模型测试数据上表现更加健壮
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数据集 每次喂入 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()
运行反向传播代码会自动调用正向传播的代码
三、预测
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
def 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:
#加载指定路径下的ckpt
ckpt = 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_value
def pre_dic(pic_path, wg_bg=False):
# 读取图片
img = Image.open(pic_path)
# 用消除锯齿的方式,将图片resize 为28 × 28
reIm = 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 = 50
for 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] = 0
else:
# 白点
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_ready
def 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)
application('6.png', wg_bg=True)
四、参数提取
1.权重和偏置
read_params.py
import tensorflow as tf
if __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()
2.图片参数提取(归一化)
readpic_array.py
from PIL import Image
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__':
pic_name = [
"0.png",
"1.png",
"2.png",
"3.png",
"4.png",
"5.png",
"6.png",
"7.png",
"8.png",
"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(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()
五、完整工程下载
链接:https://pan.baidu.com/s/1CTKUaGsBG1Kw3N5mnjZcyA
提取码:3333