TensorFlow實操之--MNIST手寫體數字圖像識別

問題描述

MNIST是一個手寫體數字識別數據集,它是由圖片組成,每一個圖片上寫的是0~9中的一個數字,我們的問題就是根據圖片上的數字對圖片進行10分類,也就是識別出這些圖片上寫的數字是幾。

解決思路

MNIST數據集有三個部分組成,訓練數據,測試數據,驗證數據。一般而言,訓練集用於訓練模型參數,測試集用於估計模型對樣本的泛化誤差,驗證集用於“訓練”模型的超參數。

具體地,訓練集是用來訓練參數的,一般是用來梯度下降的。而驗證集基本是在每個epoch完成後,用來測試一下當前模型的準確率。事實上,對於一個模型來說,其參數可以分爲普通參數和超參數。在不引入強化學習的前提下,那麼普通參數就是可以被梯度下降所更新的,也就是訓練集所更新的參數。另外,還有超參數的概念,比如網絡層數、網絡節點數、迭代次數、學習率等等,這些參數不在梯度下降的更新範圍內。多數情況下我們還是自己人工根據驗證集來調。

經過訓練集和驗證集訓練完模型之後,就可以用測試集來查看模型的好壞了,即泛化能力如何。
一般滴,識別思路是

  1. 獲取數據,包括訓練數據和測試數據。
  2. 模型建立
  3. 訓練
  4. 模型驗證

下面,我們一步步進行代碼實操。

單層神經網絡的TensorFlow代碼實操

數據獲取

MNIST數據很容易在官網上獲取。代碼如下:

import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True)

我們看一下數據的大小

print('train data:',mnist_data.train.images.shape,mnist_data.train.labels.shape)
print('validation data:',mnist_data.validation.images.shape,mnist_data.validation.labels.shape)
print('test data:',mnist_data.test.images.shape,mnist_data.test.labels.shape)
train data: (55000, 784) (55000, 10)
validation data: (5000, 784) (5000, 10)
test data: (10000, 784) (10000, 10)

從結果得知,數據集被分成三個部分:55000行的訓練數據集(mnist_data.train),5000個驗證數據和10000行的測試數據集(mnist_data.test)。每一張圖片均包含了28像素x28像素,用數組表示圖像爲長度爲784的張量。
通過代碼調試,我們可以看到mnist_data的具體內容。如下圖所示:
在這裏插入圖片描述

模型建立

假設我們的網絡只有輸入層和輸出層,那麼針對輸入數據x,輸出y_可以通過y=f(xw+b)y_{-} = f(x*w+b)來表示,其中函數ff 是激活函數。我們知道x的shape是(,784),y的shape是(,10), 那麼w的shape和b的shape應該是(784,10)和(1,10)。

1. 首先我們來定義必要的參數。

batch_size=100     #每一輪數據量大小
learning_rate=0.01   #初始學習率
max_steps=1000  #最大訓練步數
x=tf.placeholder('float',[None,784])   #訓練數據集的輸入,有784維個特徵
y_ = tf.placeholder('float', [None, 10]) #定義一個新的佔位符用於輸入訓練數據的標籤,10分類
w=tf.Variable(tf.truncated_normal([784,10],stddev=0.1),name='weight')  #權重
b=tf.Variable(tf.truncated_normal([1,10],stddev=0.1),name='biases')     #偏值

由上面代碼可以看到,用placeholder來對訓練數據的數據和標籤進行佔位,並設計權重和偏值的維數。

2. 神經網絡輸出函數表示

我們採用單層神經網絡,並用softmax來作爲激活函數。則訓練數據進入神經網絡後的輸出爲

y=tf.nn.softmax(tf.matmul(x,w) + b)              #訓練數據經過softmax計算之後的結果。

以上的參數設置實現的網絡模型如下所示:
在這裏插入圖片描述

3. 定義損失函數,求得最佳參數配置
我們這裏採用交叉熵函數來定義損失函數,目標是通過梯度下降來最小化該函數。

cross_entropy=-tf.reduce_sum(y_*tf.log(y)) #交叉熵函數來定義損失函數
train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy) #梯度下降來最小化該函數

4. 設置驗證模型
對於輸出結果,我們需要驗證一下效果如何。其方法是通過測試集來測試準確率如何。我們把測試集的數據輸入模型後產生的結果與其標籤做對比,即可獲得準確率

correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))  # 這行代碼的目的是對比預測值y與標籤y_是否匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction,'float'))  # 這行代碼會給我們一組布爾值。爲了確定正確預測項的比例,我們可以把布爾值轉換成浮點數,然後取平均值。例如,[True, False, True, True] 會變成 [1,0,1,1] ,取平均值後得到 0.75.

模型訓練

模型建立好之後,下一步是給模型喂數據,然後訓練了。首先初始化所有參數,然後創建session,然後訓練。

init=tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)
    for i in range(max_steps):  # 每batch_size數據作爲一個批次進行訓練
        batch_x,batch_y=mnist_data.train.next_batch(batch_size)
        sess.run(train_step,feed_dict={x:batch_x, y_:batch_y})

模型驗證

我們用測試集對模型進行測試,即執行驗證模型。每100不迭代查看一次結果。

if i%100==0:
   print(' accuracy is ',sess.run(accuracy, feed_dict=x:mnist_data.test.images,y_:mnist_data.test.labels}))  # 評估模型準確率

完整的代碼如下

import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True) #獲取mnist數據

#設置超參數
batch_size=100     #每一輪數據量大小
learning_rate=0.01   #初始學習率
max_steps=1000  #最大訓練步數

x=tf.placeholder('float',[None,784])   #訓練數據集的輸入,有784維個特徵
y_ = tf.placeholder('float', [None, 10]) #定義一個新的佔位符用於輸入訓練數據的標籤,10分類
w=tf.Variable(tf.truncated_normal([784,10],stddev=0.1),name='weight')  #權重
b=tf.Variable(tf.truncated_normal([1,10],stddev=0.1),name='biases')     #偏值


y=tf.nn.softmax(tf.matmul(x,w) + b)                                   #訓練數據經過計算之後的結果。

cross_entropy=-tf.reduce_sum(y_*tf.log(y))

train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy)

#評估模型
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))  # 這行代碼的目的是對比預測值y與標籤y_是否匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction,
                                  'float'))  # 這行代碼會給我們一組布爾值。爲了確定正確預測項的比例,我們可以把布爾值轉換成浮點數,然後取平均值。例如,[True, False, True, True] 會變成 [1,0,1,1] ,取平均值後得到 0.75.
init=tf.initialize_all_variables()
with tf.Session() as sess:
    sess.run(init)
  
    for i in range(max_steps):
        batch_x,batch_y=mnist_data.train.next_batch(batch_size)
        sess.run(train_step,feed_dict={x:batch_x, y_:batch_y})

        if i%100==0:
            print(' accuracy is ',sess.run(accuracy, feed_dict={x:mnist_data.test.images,y_:mnist_data.test.labels}))  # 評估模型準確率
 accuracy is  0.9224

從輸出結果可以看到,準確率大概在92%左右,其實並不算高。另外的一個問題是,我們沒有利用數據集裏的驗證數據集,在數據珍貴的時代,這太浪費了。同時我們的神經網絡沒有隱藏層。下面我們把驗證集加入到訓練中,同時設置多隱藏層,看看能不能提升準確率。

多隱藏層神經網絡手寫數字識別

同樣的,多隱藏層神經網絡的手寫數字識別思路也是如下四步,只不過是在每一步裏有所不同。 1. 獲取數據,包括訓練數據和測試數據。 2. 模型建立 3. 訓練 4. 模型驗證
通過設置一個隱藏層,在訓練中更新參數,並通過正則化,我們得到較好的訓練效果,準確率提升到97%。具體代碼如下:

import warnings
warnings.filterwarnings('ignore')

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

mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True) #獲取mnist數據

#設置超參數
batch_size=100     #每一輪數據量大小
learning_rate=0.8   #初始學習率
learning_rate_decay=0.999 #學習率的衰減
max_steps=1000  #最大訓練步數
training_step=tf.Variable(0,trainable=False)

#定義隱藏層函數
def hidden_layer(input_tensor,weights1,biases1,weights2,biases2,layer_name):
    layer1=tf.nn.relu(tf.matmul(input_tensor,weights1)+biases1)  #第一層隱藏層採用了relu激活函數
    return tf.matmul(layer1,weights2)+biases2                    #第二層沒有采用激活函數,即輸出層

x=tf.placeholder(tf.float32,[None,784])   #訓練數據集的輸入,有784維個特徵
y_=tf.placeholder(tf.float32,[None,10])  #訓練數據的標籤,10分類

weights1=tf.Variable(tf.truncated_normal([784,500],stddev=0.1),name='weight1')  #權重1
biases1=tf.Variable(tf.constant(0.1,shape=[500]) )    #偏值1
weights2=tf.Variable(tf.truncated_normal([500,10],stddev=0.1),name='weight2')  #權重2
biases2=tf.Variable(tf.constant(0.1,shape=[10]),name='biases2')     #偏值2

y=hidden_layer(x,weights1,biases1,weights2,biases2,'y')       #訓練數據經過計算之後的結果。

#參數更新
averages_class=tf.train.ExponentialMovingAverage(0.99,training_step)
averages_op=averages_class.apply(tf.trainable_variables())

average_y=hidden_layer(x,averages_class.average(weights1),
                        averages_class.average(biases1),
                        averages_class.average(weights2),
                        averages_class.average(biases2),
                        'average_y')
#交叉熵
cross_entropy=tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.argmax(y_,1))
#正則化
regular=tf.contrib.layers.l2_regularizer(0.0001)
regulation=regular(weights1)+regular(weights2)
#總的損失函數
loss=tf.reduce_mean(cross_entropy)+regulation

#學習率更新
learning_rate=tf.train.exponential_decay(learning_rate,training_step,mnist_data.train.num_examples/batch_size,learning_rate_decay)
train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=training_step)

# train_op=tf.group(train_step)
with tf.control_dependencies([train_step,averages_op]):
    train_op=tf.no_op(name='train')

#評估模型
init=tf.global_variables_initializer()
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))  # 這行代碼的目的是對比預測值y與標籤y_是否匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction,  tf.float32))
# 這行代碼會給我們一組布爾值。爲了確定正確預測項的比例,我們可以把布爾值轉換成浮點數,然後取平均值。例如,[True, False, True, True] 會變成 [1,0,1,1] ,取平均值後得到 0.75.

with tf.Session() as sess:
    sess.run(init)
    validate_feed={x:mnist_data.validation.images,y_:mnist_data.validation.labels}
    test_feed={x:mnist_data.test.images,y_:mnist_data.test.labels}

    for i in range(max_steps):
        if i % 100 == 0:
            validate_accuracy=sess.run(accuracy, feed_dict=validate_feed)
            print('validate_accuracy',validate_accuracy)
        batch_x,batch_y=mnist_data.train.next_batch(batch_size)
        sess.run(train_op,feed_dict={x:batch_x, y_:batch_y})

    print(' accuracy is ',sess.run(accuracy, feed_dict=test_feed))  # 評估模型準確率

輸出結果如下:

validate_accuracy 0.9634
validate_accuracy 0.9682
validate_accuracy 0.9702
validate_accuracy 0.972
validate_accuracy 0.9736
validate_accuracy 0.9762
validate_accuracy 0.9776
 accuracy is  0.9753

CNN 卷積神經網絡的手寫數字識別

我們的識別對象是圖片,而CNN卷積神經網絡是進行圖像識別的一個非常好的算法。現在,我們嘗試用CNN來對手寫數字識別。CNN的核心結構如圖所示:
在這裏插入圖片描述
在本示例中,根據CNN,在網絡結構中,有卷積層和池化層,這裏我們採用兩層卷積層和池化層,卷積層採用5×5的濾波器對全0補齊的原數據進行卷積,卷積深度,即卷積核的個數初始爲32,而池化層採用2×2的的最大池化。自己畫的圖太醜,先用文字來說明把,等我哪天會畫漂亮圖了在補上。
我們的數字圖像數據是28×28維度的,用5*5大小的卷積核來卷積,在不使用全0填充時,卷積後的大小爲28-5+1=24維。在使用全0填充時,卷積後的大小與原來圖像矩陣一樣大,即28×28.。採用2×2池化後,圖像矩陣大小變爲14×14。在進行一輪卷積池化,圖像大小變爲7×7.
模型結構爲兩層卷積層+池化層,一個全連接層,一個輸出層。具體代碼如下:

import warnings
warnings.filterwarnings('ignore')

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

mnist_data=input_data.read_data_sets('mnist_data/',one_hot=True) #獲取mnist數據

#卷積和池化處理函數
def conv2d(x, W): #定義卷積層
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME') #步長設爲1,邊距填充爲0
#x:輸入數據,4維變量,[a,b,c,d],a爲數據index,b*c爲圖片大小,d爲圖像深度,灰色爲1,RGB爲3
#W:卷積核,4維變量,[a,b,c,d],a*b爲卷積核大小,d爲輸入矩陣深度,接輸入數據;d爲通道數,本層過濾器的深度
#strides 爲不同維度上過濾器移動的步長,[a,b,c,d]。a和d要求爲1,只對長寬有效。b爲長方向移動步長,c爲寬方向步長
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')

x=tf.placeholder(tf.float32,[None,784])   #訓練數據集的輸入,有784維個特徵
y_=tf.placeholder(tf.float32,[None,10])  #訓練數據的標籤,10分類
x_image = tf.reshape(x, [-1,28,28,1])#x的維度應該和W對應,其中第2、3維對應圖片的寬和高,最後 顏色通道數
#第一層卷積
#第一層的結構包括一個卷積層加一個最大池化層。
W_conv1 = tf.Variable(tf.truncated_normal([5, 5, 1, 32],stddev=0.1),name='W_conv1') # 前兩個維度代表patch大小,1代表通道數目,32是輸出的通道數目
b_conv1 = tf.Variable(tf.truncated_normal([32],stddev=0.1),name='b_conv1')#對應上面每一個輸出的通道

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)#把x和全職進行卷積,再加上偏置項,應用RELU激活函數防止線性化
h_pool1 = max_pool_2x2(h_conv1)#添加池化層

#第二層卷積層
W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 32, 64],stddev=0.1),name='W_conv2')
b_conv2 = tf.Variable(tf.truncated_normal([64],stddev=0.1),name='b_conv2')
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

#密集連接層
W_fc1 = tf.Variable(tf.truncated_normal([7 * 7 * 64, 1024],stddev=0.1),name='W_fc1')#圖片尺寸由28減少到了7,原因是經歷了兩次2x2的最大池化
b_fc1 = tf.Variable(tf.truncated_normal([1024],stddev=0.1),name='b_fc1')
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
#這一層的目的是防止模型過擬合,過擬合的模型會影響泛化能力
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

#輸出層
#卷積神經網絡的最後輸出層依然採取全連接的形式
W_fc2 = tf.Variable(tf.truncated_normal([1024, 10],stddev=0.1),name='W_fc2')
b_fc2 = tf.Variable(tf.truncated_normal([10],stddev=0.1),name='b_fc2')
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
init=tf.initialize_all_variables()
#訓練和評估模型
sess = tf.InteractiveSession()
sess.run(init)
for i in range(2000):
    batch = mnist_data.train.next_batch(100)
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
        print('step %d, training accuracy %g' % (i, train_accuracy))
    train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print("test accuracy %g" % accuracy.eval(feed_dict={x: mnist_data.test.images, y_: mnist_data.test.labels, keep_prob: 1.0}))

通過2000次的訓練,可以看到測試數據準確率提升到98%。

step 0, training accuracy 0.04
step 100, training accuracy 0.89
step 200, training accuracy 0.91
step 300, training accuracy 0.86
step 400, training accuracy 0.95
step 500, training accuracy 0.94
step 600, training accuracy 1
step 700, training accuracy 0.95
step 800, training accuracy 0.94
step 900, training accuracy 0.98
step 1000, training accuracy 0.96
step 1100, training accuracy 0.98
step 1200, training accuracy 0.98
step 1300, training accuracy 0.98
step 1400, training accuracy 1
step 1500, training accuracy 0.99
step 1600, training accuracy 0.96
step 1700, training accuracy 0.99
step 1800, training accuracy 0.97
step 1900, training accuracy 0.99
test accuracy 0.9812

總結

本文針對手寫數字圖像識別問題,採用了全連接、多隱藏層神經網絡、CNN卷積神經網絡等方法進行識別,準確率也逐步提升。通過該例子,我們熟悉了深度學習問題處理的一般思路 1. 獲取數據,包括訓練數據和測試數據。2. 模型建立 3. 訓練 4. 模型驗證,並熟悉TensorFlow的基本用法。同時,在練習中,我們也發現了很多值得去探討的問題。包括激活參數的選擇,超參數的調整,優化目標損失函數的選擇,模型網絡的結構搭建,泛化的避免等等,這些都是深度學習的基本並且核心的問題,未來將逐一探討。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章