深度学习入门(一)MNIST手写体识别两种实现(初级版和优化版)

初级版

# 使用一个简单的CNN网络结构如下,括号里边表示tensor经过本层后的输出shape:
#
# ·输入层(28 * 28 * 1)
# ·卷积层1(28 * 28 * 32)
# ·pooling层1(14 * 14 * 32)
# ·卷积层2(14 * 14 * 64)
# ·pooling层2(7 * 7 * 64)
# ·全连接层(1 * 1024)
# ·softmax层(10)


#coding:utf-8


import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data


import time

"""
权重初始化
初始化为一个接近0的很小的正数
"""
# W:
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1) #截断高斯分布
    return tf.Variable(initial)
# b:
def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)


"""
卷积和池化,使用卷积步长为1(stride size),0边距(padding size)
池化用简单传统的2x2大小的模板做max pooling
"""
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding = 'SAME')
    # tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
    # x(input)  : [batch, in_height, in_width, in_channels]
    # W(filter) : [filter_height, filter_width, in_channels, out_channels]
    # strides   : The stride of the sliding window for each dimension of input.
    #             For the most common case of the same horizontal and vertices strides, strides = [1, stride, stride, 1]
    #              (batch = 1,channels = 1)
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1],
                          strides = [1, 2, 2, 1], padding = 'SAME')
    # tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
    # x(value)              : [batch, height, width, channels]
    # ksize(pool大小)        : A list of ints that has length >= 4. The size of the window for each dimension of the input tensor.
    # strides(pool滑动大小)   : A list of ints that has length >= 4. The stride of the sliding window for each dimension of the input tensor.





start = time.clock() #计算开始时间
mnist = input_data.read_data_sets(r"./MNIST_data", one_hot=True) #MNIST数据输入

"""
第一层 卷积层

x_image(batch, 28, 28, 1) -> h_pool1(batch, 14, 14, 32)
"""
x = tf.placeholder(tf.float32,[None, 784])
x_image = tf.reshape(x, [-1, 28, 28, 1]) #最后一维代表通道数目,如果是rgb则为3,卷积结果形状大小不变28x28
W_conv1 = weight_variable([5, 5, 1, 32]) #32个卷积核 32个5x5x1
b_conv1 = bias_variable([32])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) #激活函数
# x_image -> [batch, in_height, in_width, in_channels]
#            [batch, 28, 28, 1]
# W_conv1 -> [filter_height, filter_width, in_channels, out_channels]
#            [5, 5, 1, 32]
# output  -> [batch, out_height, out_width, out_channels]
#            [batch, 28, 28, 32]
h_pool1 = max_pool_2x2(h_conv1)
# h_conv1 -> [batch, in_height, in_weight, in_channels]
#            [batch, 28, 28, 32]
# output  -> [batch, out_height, out_weight, out_channels]
#            [batch, 14, 14, 32]

"""
第二层 卷积层

h_pool1(batch, 14, 14, 32) -> h_pool2(batch, 7, 7, 64)
"""
W_conv2 = weight_variable([5, 5, 32, 64]) #卷积核2: 64个 5x5x32
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# h_pool1 -> [batch, 14, 14, 32]
# W_conv2 -> [5, 5, 32, 64]
# output  -> [batch, 14, 14, 64]
h_pool2 = max_pool_2x2(h_conv2)
# h_conv2 -> [batch, 14, 14, 64]
# output  -> [batch, 7, 7, 64]

"""
第三层 全连接层

h_pool2(batch, 7, 7, 64) -> h_fc1(1, 1024)
"""
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

"""
Dropout

h_fc1 -> h_fc1_drop, 训练中启用,测试中关闭
"""
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# keep_prob
# x中每个元素的输出概率,输出为原值或0。


"""
第四层 Softmax输出层
"""
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

"""
训练和评估模型

ADAM优化器来做梯度最速下降,feed_dict中加入参数keep_prob控制dropout比例
"""
y_ = tf.placeholder("float", [None, 10])
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv)) #计算交叉熵
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #使用adam优化器来以0.0001的学习率来进行微调
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) #判断预测标签和实际标签是否匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))

sess = tf.Session() #启动创建的模型
#sess.run(tf.initialize_all_variables()) #旧版本
sess.run(tf.global_variables_initializer()) #初始化变量

for i in range(5000): #开始训练模型,循环训练5000次
    batch = mnist.train.next_batch(50) #batch大小设置为50
    if i % 100 == 0:
        train_accuracy = accuracy.eval(session = sess,
                                       feed_dict = {x:batch[0], y_:batch[1], keep_prob:1.0})
        print("step %d, train_accuracy %g" %(i, train_accuracy))
    train_step.run(session = sess, feed_dict = {x:batch[0], y_:batch[1],
                   keep_prob:0.5}) #神经元输出保持不变的概率 keep_prob 为0.5

print("test accuracy %g" %accuracy.eval(session = sess,
      feed_dict = {x:mnist.test.images, y_:mnist.test.labels,
                   keep_prob:1.0})) #神经元输出保持不变的概率 keep_prob 为 1,即不变,一直保持输出

end = time.clock() #计算程序结束时间
print("running time is %g ") % (end-start)




优化版(LeNet5)

"""
        基于LeNet-5 的手写数字体MNIST识别
        联系qq 605686962
"""
import tensorflow as tf
import numpy as np
# import tensorflow.contrib.layers as tcl
from tensorflow.examples.tutorials.mnist import input_data

#MNIST数据输入
mnist = input_data.read_data_sets(r".\MNIST_data", one_hot=True)

# 训练参数初始化
batch_size = 100 #批大小
learning_rate = 0.01 #学习率
learning_rate_decay = 0.99 #学习衰减因子
max_steps = 5000 # 最大迭代次数

###############################设计LeNet5######################################################
def LeNet5(input_tensor,regularizer,avg_class,resuse):

    #创建第一个卷积层,得到特征图大小为 32 @ 28x28
    with tf.variable_scope("C1-conv",reuse=resuse):
        # 第一层卷积核:32 @ 5x5x1
        conv1_weights = tf.get_variable("weight", [5, 5, 1, 32],
                             initializer=tf.truncated_normal_initializer(stddev=0.1))
        # 第一层卷积偏置 32个1x1(常量)
        conv1_biases = tf.get_variable("bias", [32], initializer=tf.constant_initializer(0.0))
        # 卷积运算 步长为1 ,对全部输入项提取卷积响应
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding="SAME")
        # RELU激活函数
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))

    #创建第一个池化层,池化后的结果为 32 @ 14x14
    with tf.name_scope("S2-max_pool",):
        pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")

    # 创建第二个卷积层,得到特征图大小为64@14x14。注意,第一个池化层之后得到了32个
    # 特征图,所以这里设输入的深度为32,我们在这一层选择的卷积核数量为64,所以输出
    # 的深度是64,也就是说有64个特征图
    with tf.variable_scope("C3-conv",reuse=resuse):
        # 第三层卷积核:32 @ 5x5x32
        conv2_weights = tf.get_variable("weight", [5, 5, 32, 64],
                                     initializer=tf.truncated_normal_initializer(stddev=0.1))
        # 第三层偏置
        conv2_biases = tf.get_variable("bias", [64], initializer=tf.constant_initializer(0.0))
        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding="SAME")
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))

    # 创建第二个池化层,池化后结果为64@7x7
    with tf.name_scope("S4-max_pool",):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")


    # 卷积结果变形一维
        #get_shape()函数可以得到这一层维度信息,由于每一层网络的输入输出都是一个batch的矩阵,
        #所以通过get_shape()函数得到的维度信息会包含这个batch中数据的个数信息
        #shape[1]是长度方向,shape[2]是宽度方向,shape[3]是深度方向
        #shape[0]是一个batch中数据的个数,reshape()函数原型reshape(tensor,shape,name)
        shape = pool2.get_shape().as_list()
        nodes = shape[1] * shape[2] * shape[3]    #nodes=3136
        reshaped = tf.reshape(pool2, [-1, nodes])

    #创建第一个全连层
    with tf.variable_scope("layer5-full1",reuse=resuse):
        # 全连层权值1
        Full_connection1_weights = tf.get_variable("weight", [nodes, 512],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))

        #if regularizer != None:
        # 将该层正则化项添加至集合‘losses’
        tf.add_to_collection("losses", regularizer(Full_connection1_weights))
        # 全连层偏置1
        Full_connection1_biases = tf.get_variable("bias", [512],
                                                     initializer=tf.constant_initializer(0.1))

        # 是否开启滑动平均模型
        # 平滑权值:w(t) = decay*w(t-1)+(1-dacay)*w(truth)
        if avg_class ==None:
            Full_1 = tf.nn.relu(tf.matmul(reshaped, Full_connection1_weights) + \
                                                                   Full_connection1_biases)
        else:
            # 使用影子值
            Full_1 = tf.nn.relu(tf.matmul(reshaped, avg_class.average(Full_connection1_weights))
                                                   + avg_class.average(Full_connection1_biases))

    #创建第二个全连层
    with tf.variable_scope("layer6-full2",reuse=resuse):
        # 全连层权值
        Full_connection2_weights = tf.get_variable("weight", [512, 10],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        #if regularizer != None:
        # 将该层正则化项添加至集合‘losses’
        tf.add_to_collection("losses", regularizer(Full_connection2_weights))
        # 全连层偏置
        Full_connection2_biases = tf.get_variable("bias", [10],
                                                   initializer=tf.constant_initializer(0.1))
        # 滑动平均
        if avg_class == None:
            # 线性变换
            result = tf.matmul(Full_1, Full_connection2_weights) + Full_connection2_biases
        else:
            # 使用影子值
            result = tf.matmul(Full_1, avg_class.average(Full_connection2_weights)) + \
                                                  avg_class.average(Full_connection2_biases)
    return result

####################################网络设置##################################################

# 输入样本和输出标签
x = tf.placeholder(tf.float32, [None ,28,28,1],name="x-input")
y_ = tf.placeholder(tf.float32, [None, 10], name="y-input")

# 定义L2正则项 loss = loss + λ/2*num*(Σw^2)
regularizer = tf.contrib.layers.l2_regularizer(0.0001)

# 训练输出
# 第一次调用该LeNet5函数,网络已经初始化完成,内部机制不允许更改
y = LeNet5(x,regularizer,avg_class=None,resuse=False)

# 定义损失函数loss(softmax + 交叉熵)
# 对于唯一分类,使用sparse函数
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'))

# 训练步数(不可训练变量)(优化器会自动更新)
training_step = tf.Variable(0, trainable=False)

# 学习率优化
# 最终衰减值:decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
# 参数1:初始学习率 。 参数2:迭代次数。 参数3 :更新周期 参数4:学习率衰减系数(0,1) 参数6:指数项取整,会使学习率呈现阶梯状
# 这里采用一个epoch更新一次
learning_rate = tf.train.exponential_decay(learning_rate,
                                 training_step, mnist.train.num_examples /batch_size ,
                                 learning_rate_decay, staircase=True)

# 梯度下降法优化(学习率)
train_step = tf.train.GradientDescentOptimizer(learning_rate). \
    minimize(loss, global_step=training_step)

#### 滑动平均(用于测试输出) ####
# 开启滑动平均,衰减系数0.99
variable_averages = tf.train.ExponentialMovingAverage(0.99, training_step)
# 对所有可训练变量执行一次滑动平均 :'.apply()表示提取' , ‘.average()表示运用’
variables_averages_op = variable_averages.apply(tf.trainable_variables())

# 测试输出
# 第二次调用该LeNet函数,更改机制
average_y = LeNet5(x,regularizer,variable_averages,resuse=True)

# 控制流,当依赖项满足才运行下文
with tf.control_dependencies([train_step, variables_averages_op]):
    train_op = tf.no_op(name='train')# 占位不干事

# 测试
crorent_predicition = tf.equal(tf.arg_max(average_y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(crorent_predicition, tf.float32))

########################## 运行 ##########################################################
with tf.Session() as sess:
    #初始化
    sess.run(tf.global_variables_initializer())
    #tf.global_variables_initializer().run()
    for i in range(max_steps):
        # 测试
        if i %100==0:
            # 分批
            x_val, y_val = mnist.validation.next_batch(batch_size)
            # 输入数据变形矩阵
            reshaped_x2 = np.reshape(x_val, (batch_size,28,28, 1))
            # 喂食字典
            validate_feed = {x: reshaped_x2, y_: y_val}

            validate_accuracy = sess.run(accuracy, feed_dict=validate_feed)
            print("After %d trainging step(s) ,validation accuracy"
                  "using average model is %g%%" % (i, validate_accuracy * 100))
        # 训练
        x_train, y_train = mnist.train.next_batch(batch_size)
        reshaped_xs = np.reshape(x_train, (batch_size ,28,28,1))
        # train_op依赖于train_step和variables_averages_op,意味着训练同时也要滑动平均
        sess.run(train_op, feed_dict={x: reshaped_xs, y_: y_train})
        # print("当前迭代次数 :%d ..\n"%(i))

    # 测试集
    xtest = mnist.test.images
    ytest = mnist.test.labels
    XT = np.reshape(xtest,(-1,28,28,1))
    test_accuracy = sess.run(accuracy, feed_dict={x:XT,y_:ytest})
    print("测试集准确率:%g%%"%(test_accuracy*100))




结果展示

keras 版本

from keras.datasets import mnist
from keras.layers import Input, Conv2D, BatchNormalization, MaxPooling2D, Flatten, Dense, Dropout, Reshape
from keras.models import Model, load_model
from keras.utils.np_utils import to_categorical
from keras import backend as K
import numpy as np
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"


def LeNet5(x,n_classes=10):

    x = Conv2D(32, (3, 3), strides=(1, 1), activation='relu', padding='same')(x)
    x = BatchNormalization(axis=3)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Conv2D(64, (3, 3), strides=(1, 1), activation='relu', padding='same')(x)
    x = BatchNormalization(axis=3)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Flatten()(x)
    x = Dropout(0.2)(x)
    x = Dense(512, activation='relu')(x)

    x = Dropout(0.2)(x)
    x = Dense(512, activation='relu')(x)

    x = Dropout(0.2)(x)
    x = Dense(n_classes, activation='softmax')(x)

    return x

def get_model():
    input = Input(shape=(28,28,1))
    model = Model(inputs=input, outputs=LeNet5(input,N_CLASSES))
    model.summary()
    return model

def train():

    model = get_model()
    model.compile(optimizer='adam', loss="categorical_crossentropy",metrics=['accuracy'])
    model.fit(x_train,y_train,batch_size=BATCH_SIZE,epochs=EPOCHS)
    model.save(MODEL_FILE)

def test():
    model = load_model(MODEL_FILE)
    score = model.evaluate(x_test, y_test, batch_size=BATCH_SIZE)
    print('test score:',score)

if __name__ == "__main__":

    EPOCHS = 20
    BATCH_SIZE = 128
    N_CLASSES = 10
    MODEL_FILE = "./LeNet5_minist.h5"


    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    x_train = np.expand_dims(x_train,axis=3)
    x_test = np.expand_dims(x_test, axis=3)
    y_train = to_categorical(y_train,N_CLASSES)
    y_test = to_categorical(y_test,N_CLASSES)
    train()
    test()

结果

keras-score

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pika在线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值