Tensorflow: MNIST数据集实现DNN、CNN、LSTM神经网络

最近学了一下tensorflow的基本用法,这里做一下总结

全连接深度神经网络(FC-DNN)

全连接深度神经网络,每一层的神经元直接都是全连接,并且不共享权值。在普通的分类的问题中表现的不错,但是对于图片处理等具有网格形式的数据,最好采用CNN(卷积神经网络),对于序列化数据如NLP(自然语言处理)、文字分析等采用RNN(循环神经网络)表现更佳。

DNN用tensorflow的实现代码如下。

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np
# 数据分析

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)


LOG_DIR = "logs"
IMAGE_NUM = 10
# 每个批次大小
batch_size = 100
n_batch = mnist.train.num_examples // batch_size
print(n_batch)
# 定义两个placeholder
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])


dropout = tf.placeholder(tf.float32)
learning_rate = tf.Variable(1e-3)
hidden_num = 20
# 输入到隐藏层
w_hidden = tf.Variable(tf.truncated_normal([784, hidden_num], stddev=0.2))
b_hidden = tf.Variable(tf.zeros([hidden_num]) + 0.1)
o_hidden = tf.nn.sigmoid(tf.matmul(x, w_hidden) + b_hidden)
h_dropout = tf.nn.dropout(o_hidden, dropout)
# # 隐藏层到输出层的权重
w = tf.Variable(tf.truncated_normal([hidden_num, 10], stddev=0.1))
b = tf.Variable(tf.zeros([10])+0.1)
o = tf.matmul(h_dropout, w) + b
prediction = o
# 定义代价函数
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=prediction))

# 使用梯度下降法
train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)

correct_predict = tf.equal(tf.argmax(y, 1), tf.argmax(prediction, 1))
accuracy = tf.reduce_mean(tf.cast(correct_predict, tf.float32))

with tf.Session() as sess:
    #writer = tf.summary.FileWriter('logs/', sess.graph)
    sess.run(tf.global_variables_initializer())
    for epoch in range(n_batch):
        sess.run(tf.assign(learning_rate, (learning_rate * 0.95)))
        for batch in range(batch_size):
            batch_xs, batch_ys = mnist.train.next_batch(100)
            sess.run([train_step], feed_dict={x: batch_xs, y: batch_ys, dropout: 1})
        print('epoch : ', epoch, ' accuracy: ', sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels, dropout: 1}))

我这里用的数据集是tensorflow自带的MNIST数字识别数据集,包括我下面几篇博客的数据集,也都采用MNIST数据集。

“//” 符号

在Python中//符号是一个双目运算符,a // b 相当于 int(a / b),保留除法过后的整数部分。

tensorflow中的数值定义

  • x = tf.Variable(1) 表示定义了一个tensor,x,x是tensorflow中的变量,初始值是1
  • x = tf.truncated_normal(shape=[2,3],stddev=0.1)表示x是一个2×3的矩阵,里面的值是一个方差为0.1的[-2,2]之内的正态分布值,truncated是截断的意思,截断的正态分布。需要注意,这里的x不是一个tensor变量,它不可以改变,初始化为什么值,就是什么值。如果想让其的训练过程中改变,需要写成x = tf.Variable(tf.truncated_normal(shape=[2,3],stddev=0.1))
  • x = tf.placeholder(dtype=tf.float32,shape=[None, 784])表示这个一个占位符,是个变化的矩阵,这里的None表示不确定行,但是确定列是784列。训练之前,需要告诉tensorflow,x的具体值是什么。

损失(Loss)

  • 我这里用的是的隐藏层的激励函数是sigmoid函数, 1/(1-exp(-x)),输出层的损失函数是softmax_cross_entropy_with_logits_v2,被softmax转化成概率型之后求交叉熵代价,交叉熵是一种描述预测值和真是值的差异的对数函数,具体请看这篇博客。除此之外,还有其他的代价函数,比如说常用的平方损失tf.square(y - prediction),表示(1/2)(y – prediction)平方、sigmoid_cross_entropy_with_logits,表示先对输出值sigmoid,在求交叉熵。需要注意,这里的y和prediction都是矩阵,矩阵里的各个元素分别求代价。最后的损失值是这些代价的平均值,别忘了Loss = tf.reduce_mean(cost)

Dropout

  • 为了避免神经元个数太多导致的过拟合现象,我们可以为每层神经元的输出进行dropout,tf.nn.dropout(rate),里面的参数是总数中多少的神经元加入下一次的输入,比如0.5表示只用一半的神经元加入下一次的输入。

tf.equal(a,b)

a和b都是矩阵,并且a.shape == b.shape, 但会一个bool矩阵B,B.shape == a.shape == b.shapw。表示每一位上的元素值是否相等。

tf.cast(a, dtype=)

a是个矩阵,表示把a中元素的值转化乘dtype类型,比如a中都是Bool类型,dtype=tf.float32,True转化乘1.0,False转化成0.0。

训练(Train)

  • tensorflow提供了很多内置的优化器,我这里采用的是tf.train.AdamOptimizer,它的好处是它可以利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率,经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳,它需要一个非常小的初始学习率,比如1e-3;tf.train.GradientDescentOptimizer,梯度下降优化器,它的好处是优化的比较稳,越到后面越小,能够很好的预测局部最小值,是的,它可能无法跳出局部最小值达到全局最小值还有其他的等等。具体的原理请参考这篇博客
  • 你必须告诉优化器你要优化什么,通常,我们调用Optimizer的minimize(Loss)方法,去最小化损失。

Session

  • tf.Session()建立一个和tensorflow的会话session,通过session可以执行你的模型训练。如果你在模型中用tf.Variable定义了变量,那么就要用sess.run(tf.global_variables_initializer())来初始化全局的变量。
  • result = sess.run([param]),param是一个Tensor类型的变量,Tensor会在tensorflow中执行,并返回执行结果。

CNN(卷积神经网络)

卷积神经网络在tensorflow中的实现很简单,主要是理解卷积神经网络的概念。

概念

  • RNN包括卷积层、非线性处理、池化层、分类器这四部分。
    • 卷积层是卷积核乘上一个池化的输出。
    • 非线性处理最多的是使用ReLU(线性整流单元)
    • 池化层就是将ReLU的结果进一步缩小维度,常用的有MaxPooling等等
    • 分类器以最后一个池化层的输出作为输入,可能是一个全连接网络,也可能是一个SVM分类器等等。

下面是tensorflow用来实现CNN代码。

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

# 获取卷积核
def getWeight(shape):
    return tf.Variable(tf.truncated_normal(shape, dtype=tf.float32, stddev=0.1))

# 获取Biases
def getBiases(shape):
    return tf.Variable(tf.constant(0.1, shape=shape))

# 卷积操作
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

# 池化操作
def max_pooling(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')


batch_size = 100
batchs = mnist.train.num_examples // batch_size
max_epoch = 20
hidden_layyer_num = 1024
# 定义输入和输出
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])

# 第一层
# 转换维度[betch,height,weight,channels]
image_x = tf.reshape(x, [-1, 28, 28, 1])
# 输入层 5x5x1的采样窗口,32个卷积核
W_1 = getWeight([5, 5, 1, 32])
# 因为有32个卷积核,所以输出是32个平面,对应32个偏置
B_1 = getBiases([32])
# 卷积层的输出做线性整流
Conv_1 = tf.nn.relu(conv2d(image_x, W_1) + B_1)
# 这里因为是2x2的pooling,输出是32个14x14的平面
Max_1 = max_pooling(Conv_1)

# 第二层
# 5x5x32的卷积核64个,生成64个平面
W_2 = getWeight([5, 5, 32, 64])
B_2 = getBiases([64])
Conv_2 = tf.nn.relu(conv2d(Max_1, W_2) + B_2)
# 这里输出是64个7x7的平面
Max_2 = max_pooling(Conv_2)


# dropout比率
dropout_rate = tf.placeholder(tf.float32)
learning_rate = tf.placeholder(tf.float32)
# 全连接层
fc_input = tf.reshape(Max_2, [-1, 64*7*7])
w_hidden = getWeight([64*7*7, hidden_layyer_num])
b_hidden = getBiases([hidden_layyer_num])
hidden_dropout = tf.nn.dropout(tf.nn.tanh(tf.matmul(fc_input, w_hidden) + b_hidden), dropout_rate)
# 全连接输出层
w_output = getWeight([hidden_layyer_num, 10])
b_output = getBiases([10])
output = tf.nn.softmax(tf.matmul(hidden_dropout, w_output) + b_output)


# 损失,softMax和交叉熵联合损失
Loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=output))

# 学习,利用Adam优化
train_step = tf.train.AdamOptimizer(learning_rate).minimize(Loss)

# 准确率
accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(output, 1), tf.argmax(y, 1)), dtype=tf.float32))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(100):
        for batch in range(batchs):
            x_train, y_train = mnist.train.next_batch(batch_size)
            sess.run([train_step], feed_dict={x: x_train, y: y_train, dropout_rate: 0.5, learning_rate: 1e-3})
        print(i, 'accuracy : ', sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels, dropout_rate: 0.5}))

conv2d

  • tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME'), conv2d的话,x的格式必须是这样色儿的(batchs, height , width, channels)。
  • W是卷积核的权重,比x是(30, 28,28,1),表示图片是28x28x1像素的。w可以是(5,5,1,32),表示卷积核是5x5x1的平面,32表示这样的卷积核有32,因此会生成32个feature map,第三维(这里是1)通常和前面x的第三维(channels)保持一致。
  • strides表示步长,strides = [b,h,w,c]中,参数b表示样本步长(1表示每个样本都参与计算卷积),参数h表示在高度方向上的步长,表示在宽度方向上的步长,c表示通道步长(1表示每个通道都会计算卷积)
  • padding表示加入外围的0,有两个取值 ,'SAME''VALID',SAME表示卷积后的平面的高宽要和原来是平面的高宽相同,需要加外围的0,而VALID表示不用加外围的0。

max_pool

  • tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME'),有了conv2d,这里的max_pool就好理解了
  • ksize=[1, 2, 2, 1],第一个1表示在每个batch中分别做池化,最后一个1表示在每个channel中都做池化,中间的两个2表示是一个2×2的max_pooling平面。

LSTM (Long Short-term Memory)

LSTM是一种最常见的RNN(循环神经网络)实现,对于序列化的样本,比如语音,文字的处理有非常好的表现。

特征

  • 他的神经元细胞可以记住前一次的输出
  • 输入门、输出门、遗忘门,三个门结构,遗忘门可以选择性的记忆之前的信息。

tensorflow实现LSTM的代码

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

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
# 输入次数,对应一张图片的行数
max_times = 28
# 每行多少少个数据
input_n = 28
# 每一批多少个数据
batch_size = 50
# 批次数
batchs = mnist.train.num_examples // batch_size
# LSTM网络中隐藏神经元的个数
hidden_cells = 100
# 输出的类别数
class_num = 10
# 训练周期
epochs = 5

def lstm(X, weight, biases):
    # LSTM的输出规范[batch_size,max_times,n_input]这里的-1是指X的元素总个数/(max_times*input_n)
    inputs = tf.reshape(X, [-1, max_times, input_n])
    # 定义LSTM神经元
    lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(hidden_cells, forget_bias=1.0, state_is_tuple=True)
    #lstm_cell = tf.contrib.rnn.core_rnn_cell.BasicLSTMCell(hidden_cells)
    # 获得输出,和最终的神经元状态
    # cell_state[0]是cell state
    # cell_state[1]是hidden_state
    output, cell_state = tf.nn.dynamic_rnn(lstm_cell, inputs, dtype=tf.float32)
    return tf.nn.softmax(tf.matmul(cell_state[1], weight) + biases)


learning_rate = tf.placeholder(dtype=tf.float32)
x = tf.placeholder(tf.float32, [None, max_times * input_n])
y = tf.placeholder(tf.float32, [None, class_num])

weight = tf.Variable(tf.truncated_normal([hidden_cells, class_num], stddev=0.1))
biases = tf.Variable(tf.constant(0.1, shape=[class_num]))

prediction = lstm(x, weight, biases)
Loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=prediction))
train_step = tf.train.AdamOptimizer(learning_rate).minimize(Loss)

accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(prediction, 1), tf.argmax(y, 1)), dtype=tf.float32))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(epochs):
        for batch in range(batchs):
            train_x, train_y = mnist.train.next_batch(batch_size)
            sess.run(train_step, feed_dict={x: train_x, y: train_y, learning_rate: 1e-4})
            #print('prediction: ', sess.run(prediction, feed_dict={x: mnist.test.images, y: mnist.test.labels})[0])
        print(sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels, learning_rate: 1e-4}))

输入格式

  • LSTM的神经网络输入必须是[batch_size,max_times,n_input]这种格式,满足序列化的要求,max_times表示最多记忆的次数因为我们一张图片是28×28的,序列化它的话,可以将每一行作为一个输入,28行看成28个元素组成的序列。
  • tf.nn.rnn_cell.BasicLSTMCell(hidden_cells, forget_bias=1.0, state_is_tuple=True),里面的参数hidden_cells表示需要多少个隐藏层细胞,forget_bias=1.0,遗忘门的初始偏置设为1.0,state_is_tuple=True神经元的状态是以tuple的形式。
output, cell_state = tf.nn.dynamic_rnn(lstm_cell, inputs, dtype=tf.float32)
return tf.nn.softmax(tf.matmul(cell_state[1], weight) + biases)

这两句代码,返回值是一个tuple(output, cell_state), cell_state表示细胞的最终状态,output表示每层的输出,有如下关系LSTM的输出 = output[:, -1, :] = state[0].h,具体是为什么,参考这篇博客加粗样式

点赞