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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值