Tensorflow: MNIST資料集實現DNN、CNN、LSTM神經網路
阿新 • • 發佈:2018-11-20
最近學了一下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中的變數,初始值是1x = tf.truncated_normal(shape=[2,3],stddev=0.1)
表示x是一個2x3的矩陣,裡面的值是一個方差為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表示是一個2x2的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表示最多記憶的次數因為我們一張圖片是28x28的,序列化它的話,可以將每一行作為一個輸入,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
,具體是為什麼,參考這篇部落格加粗樣式