1. 程式人生 > >TensorFlow學習筆記——LeNet-5(訓練自己的資料集)

TensorFlow學習筆記——LeNet-5(訓練自己的資料集)

  在之前的TensorFlow學習筆記——影象識別與卷積神經網路(連結:請點選我)中瞭解了一下經典的卷積神經網路模型LeNet模型。那其實之前學習了別人的程式碼實現了LeNet網路對MNIST資料集的訓練。而這篇文章是想自己完成LeNet網路來訓練自己的資料集。LeNet主要用來進行手寫字元的識別與分類,下面記錄一下自己學習的過程。

  我的學習步驟分為以下四步:

  • 1,溫習LeNet-5的網路層
  • 2,使用LeNet-5訓練MNIST資料集
  • 3,使用LeNet-5訓練TFRecord格式的MNIST資料集
  • 4,使用LeNet-5訓練自己的資料集(TFRecord格式)

  LeNet是出自論文Gradient-Based Learning Applied to Document Recognition,是一種用於手寫體字元識別的非常高效的卷積神經網路。那我要訓練的是印刷體的數字和字母,可能難點就是字母大小尺寸不一。下面我嘗試使用LeNet-5來進行識別。首先學習其網路結構。

1,LeNet-5的網路層解析

  LeNet-5這個網路雖然很小,但是包含了深度學習的基本模組:卷積層,池化層,全連線層。是其他深度學習模型的基礎。LeNet網路奠定了現代卷積神經網路的基礎。LeNet-5模型總共有7層,這裡我們對LeNet-5進行深入分析。

  下圖展示了以MNIST資料集為例的LeNet-5模型架構:

   下面再囉嗦一次。

第一層:卷積層(C1層)

  這一層為原始的影象元素,LeNet-5模型接受的輸入層大小為32*32*1,第一層卷積層過濾器的尺寸為5*5,深度為6,不使用全0填充,所以這一層的輸出的尺寸為32-5+1=28面四個並列印為6,這一個卷積層總共有5*5*1*6+6=156個引數,其中6個未偏置項引數,因為下一層的節點矩陣有28*28*6=4704個結點,每個節點和5*5=25個當前層節點相連,每個神經元對應一個偏置項(這就是5*5+1的原因)所以本卷積層共有(5*5+1)*6*(28*28)=122304個連線。

  那麼也就是說,過濾器尺寸為[5, 5],通道為1,深度為6。(除去輸入層和輸出層,我們有六個特徵平面,包括兩個卷積層,兩個池化層,兩個全連線層),特徵圖有6個,說明6個不同的卷積核,所以深度為6。

第二層:池化層(S2層)

  池化層又叫做下采樣層,目的是壓縮資料,降低資料維度。

  這一層的輸入為第一層的輸出,是一個28*28*6的節點矩陣,本層採用的過濾器大小為2*2,長和寬的步長均為2,所以本層的輸出矩陣大小為14*14*6

  6個14*14的特徵圖,每個圖中的每個單元與C1特徵圖中的一個2*2鄰域相連線,不重疊。因此S2中每個特徵圖的大小是C1中特徵圖大小的1/4。

第三層:卷積層(C3層)

  本層輸入的矩陣大小為14*14*6,使用的過濾器大小為5*5,深度為16.本層不使用全0填充,步長為1.本層的輸出矩陣大小為10*10*16。按照標準的卷積層,本層應該有5*5*6*16+16=2416個引數,10*10*16*(25+1)=41600個連線。

第四層:池化層(S4層)

  本層的輸入矩陣大小為10*10*16,採用的過濾器大小為2*2,步長為2,本層的輸出矩陣大小為5*5*16

第五層:全連線層(C5層)

  本層的輸入矩陣大小為5*5*16,在LeNet-5模型的論文中將這一層稱為卷積層,但是因為過濾器的大小就是5*5,所以和全連線層沒有區別,在之後的TensorFlow程式實現中也會將這一層看成全連線層。如果將5*5*16矩陣中的節點拉成一個向量,那麼這一層和之前學習的全連線層就是一樣的了。

  本層的輸出節點個數為120個,總共有5*5*16*120+120=48120個引數。

  這一層還是卷積層,且有120個神經元,可以看做是120個特徵圖,每張特徵圖的大小為5*5,每個單元與S4層的全部16個單元的5*5領域相連,因此正好和池化層匹配(S4和C5之間的全連線)。

第六層:全連線層(F6層)

  本層的輸入節點個數為120個,輸出節點個數為84個,總共引數為120*84+84=10164個。

  之所以有84個單元,是因為輸出層的設計,與C5層全相連,F6層計算輸入向量和權重向量之間的點積,再加上一個偏置,所以84=7*12

第七層:全連線層(F7層)

  本層的輸入節點個數為84個,輸出節點為10個,總共引數為84*10+10=850個。

  該層有十個神經元,可以理解這是對於手寫體10個數,哪個輸出的數大,那個神經元代表的數字就是輸出。

1,卷積層tf.nn.conv2d()

  函式型別如下:

def conv2d(  # pylint: disable=redefined-builtin,dangerous-default-value
    input,
    filter=None,
    strides=None,
    padding=None,
    use_cudnn_on_gpu=True,
    data_format="NHWC",
    dilations=[1, 1, 1, 1],
    name=None,
    filters=None):

  引數說明:

  • data_format:表示輸入的格式,有兩種分別為:“NHWC”和“NCHW”,預設為“NHWC”

  • input:輸入是一個4維格式的(影象)資料,資料的 shape 由 data_format 決定:當 data_format 為“NHWC”輸入資料的shape表示為[batch, in_height, in_width, in_channels],分別表示訓練時一個batch的圖片數量、圖片高度、 圖片寬度、 影象通道數。當 data_format 為“NHWC”輸入資料的shape表示為[batch, in_channels, in_height, in_width]

  • filter:卷積核是一個4維格式的資料:shape表示為:[height,width,in_channels, out_channels],分別表示卷積核的高、寬、深度(與輸入的in_channels應相同)、輸出 feature map的個數(即卷積核的個數)。

  • strides:表示步長:一個長度為4的一維列表,每個元素跟data_format互相對應,表示在data_format每一維上的移動步長。當輸入的預設格式為:“NHWC”,則 strides = [batch , in_height , in_width, in_channels]。其中 batch 和 in_channels 要求一定為1,即只能在一個樣本的一個通道上的特徵圖上進行移動,in_height , in_width表示卷積核在特徵圖的高度和寬度上移動的布長,即 。

  • padding:表示填充方式:“SAME”表示採用填充的方式,簡單地理解為以0填充邊緣,當stride為1時,輸入和輸出的維度相同;“VALID”表示採用不填充的方式,多餘地進行丟棄。

2,池化層 tf.nn.max_pool()  /  tf.nn.avg_pool()

  • value:表示池化的輸入:一個4維格式的資料,資料的 shape 由 data_format 決定,預設情況下shape 為[batch, height, width, channels]

  • ksize:表示池化視窗的大小:一個長度為4的一維列表,一般為[1, height, width, 1],因不想在batch和channels上做池化,則將其值設為1。

  • 其他引數與上面類似。

3,softmax函式

  舉個簡單的例子,假設一個五分類,然後一個樣本I的標籤  y = [0, 0, 0, 1, 0],也就是說樣本I的真實標籤是4,假設模型預測的結果概率(softmax的輸出)p=[0.1,0.15,0.05,0.6,0.1],可以看出這個預測是對的,那麼對應的損失L=-log(0.6),也就是當這個樣本經過這樣的網路引數產生這樣的預測p時,它的損失是-log(0.6)。那麼假設p=[0.15,0.2,0.4,0.1,0.15],這個預測結果就很離譜了,因為真實標籤是4,而你覺得這個樣本是4的概率只有0.1(遠不如其他概率高,如果是在測試階段,那麼模型就會預測該樣本屬於類別3),對應損失L=-log(0.1)。那麼假設p=[0.05,0.15,0.4,0.3,0.1],這個預測結果雖然也錯了,但是沒有前面那個那麼離譜,對應的損失L=-log(0.3)。我們知道log函式在輸入小於1的時候是個負數,而且log函式是遞增函式,所以-log(0.6) < -log(0.3) < -log(0.1)。簡單講就是你預測錯比預測對的損失要大,預測錯得離譜比預測錯得輕微的損失要大。

   下面簡單說一下損失函式 softmax loss:

   首先,L是損失,Sj是softmax的輸出向量S的第j個值,那Sj表示這個樣本屬於第j個類別的概率。yj 前面有個求和符號,j的範圍是1~T(即類別1到類別T),因此y是一個1*T的向量,裡面的T個值,而且只有1個值是1,其他 T-1個值都是0,那麼那個位置的值是1呢?真實標籤對應的位置的那個值是1,其他都是0,所以這個公式可以改為:

   當然,此時要限定 j是指當前樣本的真實標籤。

4,cross  entropy交叉熵

  損失函式是在訓練過程中,得到的輸出值與理想輸出值的差距,這裡有很多衡量的標準,比如均方誤差的方法,也就是輸出值和理想值的方差來表示:

loss = tf.reduce_mean(tf.square(yout - Y))

  公式如下:

   TensorFlow使用的函式如下:

tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None,name=None) 

  引數意義:

  • 第一個引數x:指輸入 
  • 第二個引數keep_prob: 設定神經元被選中的概率,在初始化時keep_prob是一個佔位符,  keep_prob = tf.placeholder(tf.float32) 。tensorflow在run時設定keep_prob具體的值,例如keep_prob: 0.5
  • 第五個引數name:指定該操作的名字。

5,執行訓練

  執行訓練的過程就是讓已有的輸入和輸出資料一次又一次的填入模型中,根據資料進行訓練。什麼時候進行呢?那就是需要搭建一個平臺,讓它在這個平臺上執行,這個平臺叫做會話,tf.Session(),就好比你修好了路,當然不作為擺設,你要在某個時間段內開始通車。

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(1000):
    	sess.run(optimizer, feed_dict = {X : x, Y : y})

  

6,LeNet-5的侷限性

  卷積網路在本質上是一種輸入到輸出的對映,它能夠學習大量的輸入與輸出之間的對映關係,而不需要任何輸入和輸出之間的精確的數學表示式。

  CNN能夠得出原始影象的有效表徵,這使得CNN能夠直接從原始畫素中,經過極少的預處理,識別視覺上面的規律。然而,由於當時缺乏大規模訓練資料,計算機的計算能力也跟不上,LeNet-5對於複雜問題的處理結果並不理想。

  2006年起,人們設計了很多方法,想要克服難以訓練深度CNN的困難。其中,最出名的時,Krizhevsky et al 提出了一個經典的CNN結構,並在影象識別任務上取得了重大突破,其方法的整體框架叫做AlexNet,與LeNet-5類似,但是要加深一點,此後深度卷積如雨後春筍一般,出現了很多。

 

 2,Lenet-5實現MNIST資料訓練(使用官方格式資料)

  首先,我們熱個手,使用MNIST資料的官方格式,直接進行訓練。這裡資料沒有進行處理,也沒有轉化。

  程式碼:

# _*_coding:utf-8_*_
'''
LeNet是第一個成功應用於數字識別問題的卷積神經網路
LeNet模型總共有7層,在MNIST上LeNet-5模型可以達到99.2%的正確率
'''
import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data
import time
import os

os.environ['CUDA_VISIBLE_DEVICES'] = '1,2'


# 獲取mnist資料
mnist_data_set = input_data.read_data_sets('MNIST_data', one_hot=True)
# 宣告輸入圖片資料型別,mnist的手寫體圖片大小為28*28=784
# None表示行向量的維度是任意的,也就是一次可以輸出多張圖片
# placeholder 基本都是佔位符,先定義,後面會用到的
x = tf.placeholder('float', [None, 784])
# y_為網路的輸出,數字十個類別
y_ = tf.placeholder('float', [None, 10])

# 把輸入的資料轉變成二維形式,用於卷積計算
# -1表示一次可以儲存多張照片,1表示影象的通道數為1
x_image = tf.reshape(x, [-1, 28, 28, 1])

# 卷積核初始化,大小為6個5*5的卷積核,1和x_image的1對應,即為影象的通道數
filter1 = tf.Variable(tf.truncated_normal([5, 5, 1, 6]))
# 偏置項
bias1 = tf.Variable(tf.truncated_normal([6]))
# 二維卷積計算
conv1 = tf.nn.conv2d(x_image, filter1, strides=[1, 1, 1, 1], padding='SAME')
h_conv1 = tf.nn.sigmoid(conv1 + bias1)
# 池化層
maxPool2 = tf.nn.max_pool(h_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

filter2 = tf.Variable(tf.truncated_normal([5, 5, 6, 16]))
bias2 = tf.Variable(tf.truncated_normal([16]))
conv2 = tf.nn.conv2d(maxPool2, filter2, strides=[1, 1, 1, 1], padding='SAME')
h_conv2 = tf.nn.sigmoid(conv2 + bias2)

maxPool3 = tf.nn.max_pool(h_conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

filter3 = tf.Variable(tf.truncated_normal([5, 5, 16, 120]))
bias3 = tf.Variable(tf.truncated_normal([120]))
conv3 = tf.nn.conv2d(maxPool3, filter3, strides=[1, 1, 1, 1], padding='SAME')
h_conv3 = tf.nn.sigmoid(conv3 + bias3)

# 全連線層,權重初始化
W_fc1 = tf.Variable(tf.truncated_normal([7 * 7 * 120, 80]))
# 偏置項
b_fc1 = tf.Variable(tf.truncated_normal([80]))
# 將卷積的輸出展開
h_pool2_flat = tf.reshape(h_conv3, [-1, 7 * 7 * 120])
h_fc1 = tf.nn.sigmoid(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

W_fc2 = tf.Variable(tf.truncated_normal([80, 10]))
b_fc2 = tf.Variable(tf.truncated_normal([10]))
# 輸出層,使用softmax進行多分類
y_conv = tf.nn.softmax(tf.matmul(h_fc1, W_fc2) + b_fc2)

# 損失函式,交叉熵
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
# 使用梯度下降法來更新權重,學習速率為0.001,改為0.01的話會導致權重更新有問題,準確率會滴
train_step = tf.train.GradientDescentOptimizer(0.001).minimize(cross_entropy)

sess = tf.InteractiveSession()

# 測試準確率,tf.argmax() 計算行或列的最大值,返回最大值下標的向量
# tf.equal() 計算兩個向量對應的元素是否相等,返回資料為bool
# tf.cast() 把bool資料轉化為浮點型
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))

# 對所有變數進行初始化
sess.run(tf.global_variables_initializer())

start_time = time.time()
for i in range(50000):
    # 獲取訓練資料,每次100張
    # 我們後面取資料的時候,直接獲取feed_dict={x: batch[0], y_true: batch[1]}
    # batch = mnist_data_set.train.next_batch(60)
    batch_xs, batch_ys = mnist_data_set.train.next_batch(100)
    train_step.run(feed_dict={x: batch_xs, y_: batch_ys})
    # 每個100次輸出當前的準確率
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict={x: batch_xs, y_: batch_ys})
        print('step %d, training accuracy %g' % (i, train_accuracy))
        # 計算時間間隔
        end_time = time.time()
        print('time: ', (end_time - start_time))
        start_time = end_time

print("Test accuracy: {}".format(accuracy.eval(session=sess,
                                               feed_dict={
                                                   x: mnist_data_set.test.images,
                                                   y_: mnist_data_set.test.labels})))

# 關閉會話
sess.close()

  訓練過程如下:

step 48200, training accuracy 1
time:  0.240248441696167
step 48300, training accuracy 1
time:  0.24075675010681152
step 48400, training accuracy 1
time:  0.3005177974700928
step 48500, training accuracy 1
time:  0.2406449317932129
step 48600, training accuracy 1
time:  0.24107027053833008
step 48700, training accuracy 0.99
time:  0.24148797988891602
step 48800, training accuracy 0.98
time:  0.24003911018371582
step 48900, training accuracy 0.99
time:  0.23934340476989746
step 49000, training accuracy 1
time:  0.28536438941955566
step 49100, training accuracy 0.98
time:  0.24154186248779297
step 49200, training accuracy 1
time:  0.24214410781860352
step 49300, training accuracy 0.96
time:  0.24277353286743164
step 49400, training accuracy 1
time:  0.2415931224822998
step 49500, training accuracy 1
time:  0.2908365726470947
step 49600, training accuracy 1
time:  0.24100923538208008
step 49700, training accuracy 1
time:  0.2422494888305664
step 49800, training accuracy 1
time:  0.24294066429138184
step 49900, training accuracy 0.99
time:  0.24169301986694336
Test accuracy: 0.9772999882698059

  訓練的準確率基本在98%~100%之間,並且測試集達到了97.7%的準確率,效果還是不錯的。

  注意:

# *****************   改進1  *****************
x = tf.placeholder('float', shape=[None, 28 * 28])
y_ = tf.placeholder('float', shape=[None, 10])

# 當然這裡定義資料型別的時候也可以這樣寫
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])


# *****************   改進2  *****************
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
#這裡用Adam優化器優化函式 也可以使用隨機梯度下降優化函式
learning_rate = 0.001
optimizer= tf.train.GradientDescentOptimizer(learning_rate).minimize(loss)


# *****************   改進3  *****************
# 通過tf.reshape函式將第五層的輸出變成一個batch的向量
# reshaped = tf.reshape(relu3, [pool_shape[0], nodes])
     改為:(不然會報錯)
reshaped = tf.layers.flatten(relu3)        

  

3,使用LeNet-5訓練MNIST資料集(將MNIST資料轉化為TFRecord格式)

  大多數書上和網路上例項就是使用已經封裝好的mnist資料集來訓練卷積神經網路。但是這裡我想用神經網路來訓練自己的資料集,所以如何將自己的資料集匯入神經網路?這裡我首先將MNIST資料轉化為TFRecord資料格式進行訓練,下一步訓練自己的資料集。

  我的解決思路如下:

1,將自己的資料集轉換為TensorFlow支援的神經網路輸入格式TFRecord。

2,重建LeNet-5卷積神經網路

3,對神經網路進行訓練

1,製作TFRecord的MNIST資料集

  首先,將自己的資料集轉化為TFRecord,我之前的部落格有說,這裡不再學習。

  想學習,請參考部落格:高效讀取資料的方法(TFRecord)

  將MNIST資料集中的所有檔案儲存到一個TFRecord檔案中,然後我們用的時候,直接讀取封裝好的MNIST資料的TFRecord檔案。

   但是後面需要使用如何讀取TFRecord檔案的函式,這裡注意,你生成TFRecord檔案的函式和讀取的函式一定要保持一致,不然會報錯。

  將MNIST資料集格式轉化為TFRecord格式,前面有講過,這裡不再贅述。

  想學習,請參考部落格:使用TensorFlow操作MNIST資料(2)

 

  這裡貼上一下處理MNIST資料的程式碼:

1,讀取MNIST資料,並轉化為TFRecord格式:

#_*_coding:utf-8_*_
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import os
import numpy as np

# 生成整數的屬性
def _int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

# 生成字串型別的屬性
def _bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def create_records(images, labels, num_example, outpout):
    '''
    實現將MNIST資料集轉化為records
    注意:讀取的影象資料預設為uint8,然後轉化為tf的字串型BytesList儲存
    :return:
    '''
    # 訓練影象的解析度,作為example的屬性
    pixels = images.shape[1]
    # 建立一個writer來寫TFRecord檔案
    writer = tf.python_io.TFRecordWriter(outpout)

    # 將每張圖片都轉化為一個Example
    for i in range(num_example):
        # 將影象轉化為字串
        image_raw = images[i].tostring()

        # 將一個樣例轉化為Example,Protocal Buffer並將所有資訊寫入這個資料結構
        example = tf.train.Example(features=tf.train.Features(
            feature={
                'pixels': _int64_feature(pixels),
                'labels': _int64_feature(np.argmax(labels[i])),
                'image_raw': _bytes_feature(image_raw)
            }
        ))

        # 將Example寫入TFRecord檔案
        writer.write(example.SerializeToString())
    print("data processing success")
    writer.close()



if __name__ == '__main__':
    if not os.path.exists('mnistrecord'):
        os.mkdir('mnistrecord')
    # 匯入MNIST資料集
    mnist = input_data.read_data_sets('MNIST_data/', dtype=tf.uint8, one_hot=True)
    train_images = mnist.train.images
    train_labels = mnist.train.labels
    train_num_example = mnist.train.num_examples
    # 儲存train_TFRecord檔案的地址
    train_filename = 'mnistrecord/trainmnist28.tfrecords'
    create_records(train_images, train_labels, train_num_example, train_filename)

    test_images = mnist.test.images
    test_labels = mnist.test.labels
    test_num_example = mnist.test.num_examples
    # 儲存train_TFRecord檔案的地址
    test_filename = 'mnistrecord/testmnist28.tfrecords'
    create_records(test_images, test_labels, test_num_example, test_filename)

 

2,讀取MNIST的TFRecord檔案

# _*_coding:utf-8_*_
import tensorflow as tf


def read_record(filename):
    '''
    讀取TFRecord檔案
    :return:
    '''
    # 建立一個佇列來維護輸入檔案列表
    filename_queue = tf.train.string_input_producer([filename])
    # 建立一個reader來讀取TFRecord檔案中Example
    reader = tf.TFRecordReader()
    # 從檔案中讀取一個Example
    _, serialized_example = reader.read(filename_queue)

    # 用FixedLenFeature 將讀入的Example解析成tensor
    features = tf.parse_single_example(
        serialized_example,
        features={
            'image_raw': tf.FixedLenFeature([], tf.string),
            'pixels': tf.FixedLenFeature([], tf.int64),
            'labels': tf.FixedLenFeature([], tf.int64)
        }
    )

    # tf.decode_raw將字串解析成影象對應的畫素陣列
    images = tf.decode_raw(features['image_raw'], tf.uint8)
    labels = tf.cast(features['labels'], tf.int32)
    pixels = tf.cast(features['pixels'], tf.int32)

    init_op = tf.global_variables_initializer()

    with tf.Session() as sess:
        sess.run(init_op)
        # 啟動多執行緒處理輸入資料
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)

        # 每次執行讀出一個Example,當所有樣例讀取完之後,再次樣例中程式會重頭 讀取
        for i in range(10):
            # 在會話中會取出image 和label
            image, label = sess.run([images, labels])
        coord.request_stop()
        coord.join(threads)
        print("end code")


if __name__ == '__main__':
    train_filename = 'mnistrecord/trainmnist28.tfrecords'
    test_filename = 'mnistrecord/testmnist28.tfrecords'
    read_record(filename=train_filename)

  

 

2,定義神經網路結構

  定義LeNet-5的結構,注意之前已經說過了,這裡不再贅述,只貼程式碼。其實網上各個部落格啊,書啊寫的程式碼都大同小異,雖然定義形式不同,但是LeNet-5的結構確是一步都不少,所以各位還是按照自己寫程式碼的風格寫就行。

  下面貼上一個TensorFlow實戰中定義的LeNet-5結構的定義程式碼:

# _*_coding:utf-8_*_
import tensorflow as tf

# 配置神經網路的引數
INPUT_NODE = 784  # 這裡輸入的引數是圖片的尺寸 這裡是28*28=784
OUTPUT_NODE = 51  # 這裡是輸出的圖片型別,總共51類

IMAGE_SIZE = 28
NUM_CHANNELS = 3
NUM_LABELS = 51

# 第一層卷積層的尺寸和深度
CONV1_DEEP = 6
CONV1_SIZE = 5

# 第三層卷積層的尺寸和深度
CONV2_DEEP = 16
CONV2_SIZE = 5

# 第五層卷積層的尺寸和深度
CONV3_DEEP = 120
CONV3_SIZE = 5

# 全連線層的節點個數
FC_SIZE = 84


# 定義卷積神經網路的前向傳播過程
# 這裡新增一個新的引數train,用於區分訓練過程和測試過程
# 在這個程式中將用到Dropout方法,dropout可以進一步提升模型可靠性,並防止過擬合
# 注意dropout層只能在訓練過程中使用
def inference(input_tensor, train, regularizer):
    '''
    宣告第一層卷積層的變數並實現前向傳播過程,通過使用不同的名稱空間來隔離不同層的變數
    這可以讓每一層中變數命名只需要考慮在當前層的作用,而不需要擔心命名重複的問題
    和標準的LeNet-5模型不太一樣,這裡定義的卷積層輸入為28*28*1的原始MNIST圖片箱數
    因為卷積層使用了全0填充,所以輸出為28*28*6的矩陣
    :param input_tensor:
    :param train:
    :param regularizer:
    :return:
    '''
    with tf.variable_scope('layer1-conv1'):  # [5,5,3,6]
        conv1_weights = tf.get_variable("weight",
                                        [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],
                                        initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv1_biases = tf.get_variable('bias', [CONV1_DEEP],
                                       initializer=tf.constant_initializer(0.0))

        # 使用邊長為5, 深度為6的過濾器,過濾器移動的步長為1,且使用全0填充
        conv1 = tf.nn.conv2d(input_tensor, conv1_weights,
                             strides=[1, 1, 1, 1], padding='SAME')
        relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))

    # 實現第二層池化層的前向傳播過程,這裡選用最大池化層
    # 池化層過濾器的邊長為2,使用全零填充且移動的步長為2,這一層的輸入為上一層的輸出
    # 也就是28*28*6的矩陣  輸出為14*14*6的矩陣
    with tf.name_scope('layer2-pool1'):
        pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],
                               padding='SAME')

    # 宣告第三層卷積層的變數並實現前向傳播過程,這一層輸入為14*14*6的矩陣
    # 因為卷積層沒有使用全零填充,所以輸出為10*10*16的矩陣
    with tf.variable_scope('layer3-conv2'):  # [5,5,3,16]
        conv2_weights = tf.get_variable("weight",
                                        [CONV2_SIZE, CONV2_SIZE, NUM_CHANNELS, CONV2_DEEP],
                                        initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv2_biases = tf.get_variable('bias', [CONV2_DEEP],
                                       initializer=tf.constant_initializer(0.0))

        # 使用邊長為5, 深度為16的過濾器,過濾器移動的步長為1,bububu不使用全0填充
        conv2 = tf.nn.conv2d(pool1, conv2_weights,
                             strides=[1, 1, 1, 1], padding='VALID')
        relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_biases))

    # 實現第四層池化層的前向傳播過程,這一層和第二層的結構是一樣的
    # 這裡輸入10*10*16的矩陣,輸出為5*5*16的矩陣
    with tf.name_scope('layer4-pool2'):
        pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],
                               padding='VALID')

    # 宣告第五層全連線層(實際上為卷積層)的變數並實現前向傳播過程
    # 這一層輸入是5*5*16的矩陣,因為沒有使用全0填充,所以輸出為1*1*120
    with tf.name_scope('layer5-conv3'):
        conv3_weights = tf.get_variable("weight",
                                        [CONV3_SIZE, CONV3_SIZE, NUM_CHANNELS, CONV3_DEEP],
                                        initializer=tf.truncated_normal_initializer(stddev=0.1))
        conv3_biases = tf.get_variable('bias', [CONV3_DEEP],
                                       initializer=tf.constant_initializer(0.0))

        # 使用邊長為5, 深度為6的過濾器,過濾器移動的步長為1,bububu不使用全0填充
        conv3 = tf.nn.conv2d(pool2, conv3_weights,
                             strides=[1, 1, 1, 1], padding='VALID')
        relu3 = tf.nn.relu(tf.nn.bias_add(conv3, conv3_biases))

    # 將第五層卷積層的輸出轉化為第六層全連線層的輸入格式
    # 第五層的輸出為1*1*120的矩陣,然而第六層全連線層需要的輸出格式為向量
    # 所以這裡需要將這個1*1*120的矩陣拉直成一個向量
    # relu3.get_shape函式可以得到第五層輸出矩陣的維度而不需要手工計算。
    # 注意因為每一層神經網路的輸入輸出都為一個batch的矩陣,
    # 所以這裡得到的維度也包含了一個batch中資料的個數。
    pool_shape = relu3.get_shape().as_list()

    # 計算將矩陣拉直成向量之後的長度,這個長度就是矩陣長度及深度的乘積
    # 注意這裡pool_shape[0]為一個batch中資料的個數
    nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
    # 通過tf.reshape函式將第五層的輸出變成一個batch的向量
    reshaped = tf.reshape(relu3, [pool_shape[0], nodes])

    # 宣告第六層全連線層的變數並實現前向傳播過程,這一層的輸入是拉直之後的一組向量
    # 向量的長度為1120,輸出是一組長度為84的向量
    # 這一層和之前的LeNet基本一致,唯一的區別就是引入的dropout層
    # dropout在訓練時會隨機將部分節點的輸出改為0
    # dropout可以避免過擬合問題,從而使得在測試資料上的效果更好
    # dropout一般只在全連線層而不是卷積層或者池化層使用
    with tf.variable_scope('layer6-fc1'):
        fc1_weights = tf.get_variable('weight', [nodes, FC_SIZE],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        # 只有全連線層的權重需要加入正則化
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc1_weights))

        fc1_biases = tf.get_variable('bias', [FC_SIZE])
        initializer = tf.constant_initializer(0.1)
        fc1 = tf.nn.relu(tf.matmul(reshaped, fc1_weights) + fc1_biases)
        if train:
            fc1 = tf.nn.dropout(fc1, 0.5)

    # 宣告第七層全連線的變數並實現前向傳播過程,這一層的輸入為一組長度為為84的向量
    #輸出為51的向量,這一層的輸出通過softmax之後就得到了最後的分類結果
    with tf.variable_scope('layer7-fc2'):
        fc2_weights = tf.get_variable('weight',
                                      [FC_SIZE, NUM_LABELS],
                                      initializer=tf.truncated_normal_initializer(stddev=0.1))
        if regularizer != None:
            tf.add_to_collection('losses', regularizer(fc2_weights))
        fc2_biases = tf.get_variable('bias', [NUM_LABELS])
        initializer = tf.constant_initializer(0.1)
        logit = tf.matmul(fc1, fc2_weights) + fc2_biases

    # 返回第七層的輸出
    return logit

  

3,輸入資料的解析和預處理

  下面對輸入的資料進行解析。

# _*_coding:utf-8_*_
import tensorflow as tf
import os

# os.environ['CUDA_VISIBLE_DEVICES'] = '1'

# 1,輸入資料的解析和預處理
def read_records(filename, resize_height, resize_width, type=None):
    '''
    解析record檔案:原始檔的影象資料是RGB,uint8 【0, 255】一般作為訓練資料時,需要歸一化到[0, 1]
    :param filename:
    :param resize_height:
    :param resize_width:
    :param type: 選擇影象資料的返回型別
                 None:預設將uint8-[0,255]轉為float32-[0,255]
                 normalization:歸一化float32-[0,1]
                 centralization:歸一化float32-[0,1],再減均值中心化
    :return:
    '''
    # 建立檔案佇列,不限讀取的數量
    filename_queue = tf.train.string_input_producer([filename])
    # create a reader from file queue
    reader = tf.TFRecordReader()
    # reader 從檔案佇列中讀入一個序列化的樣本
    _, serialized_example = reader.read(filename_queue)
    # get feature from serialized example
    # 解析符號化的樣本
    features = tf.parse_single_example(
        serialized_example,
        features={
            'image_raw': tf.FixedLenFeature([], tf.string),
            'height': tf.FixedLenFeature([], tf.int64),
            'width': tf.FixedLenFeature([], tf.int64),
            'depth': tf.FixedLenFeature([], tf.int64),
            'label': tf.FixedLenFeature([], tf.int64)
        }
    )
    tf_image = tf.decode_raw(features['image_raw'], tf.uint8)  # 獲得影象原始的資料

    tf_height = features['height']
    tf_width = features['width']
    tf_depth = features['depth']
    tf_label = tf.cast(features['label'], tf.int32)
    # PS: 恢復原始影象資料,reshape的大小必須與儲存之前的影象shape一致,否則出錯
    tf_image = tf.reshape(tf_image, [resize_height, resize_width, 3])  # 設定影象的維度

    # 儲存的影象型別為uint8,TensorFlow訓練時資料必須是tf.float32
    if type is None:
        tf_image = tf.cast(tf_image, tf.float32)
    elif type == 'normalization':  # [1]若需要歸一化請使用:
        # 僅當輸入資料是uint8,才會歸一化[0,255]
        # tf_image = tf.image.convert_image_dtype(tf_image, tf.float32)
        tf_image = tf.cast(tf_image, tf.float32) * (1. / 255.0)  # 歸一化
    elif type == 'centralization':
        # 若需要歸一化,且中心化,假設均值為0.5,請使用:
        tf_image = tf.cast(tf_image, tf.float32) * (1. / 255) - 0.5  # 中心化

        # 這裡僅僅返回影象和標籤
        # return tf_image, tf_height,tf_width,tf_depth,tf_label
    return tf_image, tf_label


def get_batch_images(images, labels, batch_size, labels_nums, one_hot=False,
                     shuffle=False, num_threads=1):
    '''
    :param images: 影象
    :param labels: 標籤
    :param batch_size:
    :param labels_nums: 標籤個數
    :param one_hot: 是否將labels轉化為one_hot 的形式
    :param shuffle: 是否打亂順序,一般train時,shuffle=True,驗證時shuffle=False
    :param num_threads:
    :return: 返回batch的images和labels
    '''
    min_after_dequeue = 200
    # 保證 capacity必須大於 min_after_dequeue的引數值
    capacity = min_after_dequeue + 3 * batch_size
    if shuffle:
        images_batch, labels_batch = tf.train.shuffle_batch([images, labels],
                                                            batch_size=batch_size,
                                                            capacity=capacity,
                                                            min_after_dequeue=min_after_dequeue,
                                                            num_threads=num_threads)
    else:
        images_batch, labels_batch = tf.train.batch([images, labels],
                                                    batch_size=batch_size,
                                                    capacity=capacity,
                                                    num_threads=num_threads)
    if one_hot:
        labels_batch = tf.one_hot(labels_batch, labels_nums, 1, 0)
    return images_batch, labels_batch

  處理之後,返回的時批量的image和對應的label。

  這裡我沒有用上面的資料訓練,其實是這樣的。就是說,我做了一個實驗,將MNIST的資料讀出來,並寫成圖片,我發現,每個字母和數字均在一個資料夾下面。而不是說這10類,每個都在自己的資料夾下面,這麼整齊。所以我沒有直接將MNIST寫成TFRecord格式,然後讀取TFRecord格式,我發現這是不可行的。

  所以,我將MNIST資料轉化為圖片,並且每個數字放在每個數字的資料夾下面,然後訓練。這樣就可以按照我想要的方式訓練了,而且可以驗證一下MNIST的準確率,是否和上面的接近,畢竟對於源資料,我只做了的操作就是,將其轉換為圖片,再轉化為TFRecord格式再訓練。如果類似,那就沒問題。下面嘗試。

4,MNIST資料轉換為圖片

  結果如圖,即轉化為這樣:

   第二層,(隨便進一個目錄)

   第三層(隨便進入一個目錄)

  程式碼如下:

# _*_coding:utf-8_*_
import cv2
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials import mnist
from tensorflow.examples.tutorials.mnist import input_data
from PIL import Image
import os


# save raw image
def save_raw():
    # read data from mnist. if data not exist, will download automatically
    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
    # we save image raw data to data/raw/ folder
    # if the folder not there, create it
    save_dir = 'MNIST_data/raw/'
    if os.path.exists(save_dir) is False:
        os.makedirs(save_dir)

    # save 20 images in training dataset
    for i in range(20):
        # please attention,mnist.train.images[i, :] is ith image, sequence started from 0
        image_array = mnist.train.images[i, :]
        # the image in MNIST of TensorFlow, image is 784 length vector, we recover it to 28x28 image
        image_array = image_array.reshape(28, 28)
        # save image as mnist_train_0.jpg, mnist_train_1.jpg, ... ,mnist_train_19.jpg
        filename = save_dir + 'mnist_train_%d.jpg' % i
        # save image_array as image
        # use Image.fromarray to convert image,then call save function to save
        # because Image.fromarray is not good to support float, we have to convert it to uint8 and then read as 'L'
        Image.fromarray((image_array * 255).astype('uint8'), mode='L').convert('RGB').save(filename)


def convert_mnist_img_raw(data, data1, save_path):
    for i in range(data.images.shape[0]):
        # TensorFlow中的MNIST圖片是一個784維的向量,我們重新把它還原為28x28維的影象。
        image_array = data.images[i, :]
        image_array = image_array.reshape(28, 28)
        img_num = (image_array * 255).astype(np.uint8)
        # img_num = (image_array * 255).astype('uint8')
        label = data1.labels[i]
        # cv2.imshow('image', img)
        # cv2.waitKey(500)
        filename = save_path + '/{}_{}.jpg'.format(label, i)
        print(filename)
        cv2.imwrite(filename, img_num)
        # Image.fromarray(img_num, mode='L').save(filename)


def convert_mnist_img(data, data1, save_path):
    for i in range(data.images.shape[0]):
        # TensorFlow中的MNIST圖片是一個784維的向量,我們重新把它還原為28x28維的影象。
        image_array = data.images[i, :]
        image_array = image_array.reshape(28, 28)
        img_num = (image_array * 255).astype(np.uint8)
        # img_num = (image_array * 255).astype('uint8')
        label = data1.labels[i]
        if not os.path.exists(os.path.join(save_path, str(label))):
            os.mkdir(os.path.join(save_path, str(label)))
        # cv2.imshow('image', img)
        # cv2.waitKey(500)
        filename = save_path + '/' + str(label) + '/{}_{}.jpg'.format(label, i)
        print(filename)
        cv2.imwrite(filename, img_num)
        # Image.fromarray(img_num, mode='L').save(filename)




if __name__ == '__main__':
    mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
    mnist1 = input_data.read_data_sets('MNIST_data/')
    content_name = ['MNIST_train', 'MNIST_test', 'MNIST_validation']
    # print(mnist.validation.images.shape[0])
    for i in content_name:
        if not os.path.exists(i):
            os.mkdir(i)
    # convert_mnist_img_raw(mnist.validation, mnist1.validation, 'MNIST_validation')  # 55000
    convert_mnist_img(mnist.train, mnist1.train, 'MNIST_train')  # 55000
    print('convert training data to image complete')
    convert_mnist_img(mnist.test, mnist1.test, 'MNIST_test')  # 10000
    print('convert test data to image complete')
    convert_mnist_img(mnist.validation, mnist1.validation, 'MNIST_validation')  # 5000
    print('convert validation data to image complete')

  轉化之後呢,我們其實就和下面要學習的訓練自己的資料集一樣了。這裡就不貼上訓練程式碼了,因為下面訓練自己的資料的程式碼和這個其實一樣,只不過是資料集變了而已。

5,使用LeNet-5訓練TFRecord格式的MNIST資料

  下面只展示一下,使用上面定義的LeNet-5模型,和呼叫slim中谷歌官方定義好的LeNet-5模型訓練的結果,看是否類似。(如果效果不好,就調參,這種模型,我將其訓練批次調大了,學習率提高了,比較模型深度不是很深)

1,slim自帶的LeNet-5模型訓練結果

  訓練結果如下:

2019-12-07 17:24:49.511197: Step [90100]  train Loss : 0.034825, training accuracy :  0.99
2019-12-07 17:24:51.960556: Step [90200]  train Loss : 0.031647, training accuracy :  0.98
2019-12-07 17:24:54.401640: Step [90300]  train Loss : 0.013821, training accuracy :  1
2019-12-07 17:24:56.827365: Step [90400]  train Loss : 0.009416, training accuracy :  1
2019-12-07 17:24:59.285245: Step [90500]  train Loss : 0.050181, training accuracy :  0.99
2019-12-07 17:25:00.231120: Step [90500]  val Loss : 0.036682, val accuracy :  0.9886
2019-12-07 17:25:02.613078: Step [90600]  train Loss : 0.043965, training accuracy :  1
2019-12-07 17:25:05.262525: Step [90700]  train Loss : 0.003886, training accuracy :  1
2019-12-07 17:25:07.643164: Step [90800]  train Loss : 0.008936, training accuracy :  0.99
2019-12-07 17:25:10.086635: Step [90900]  train Loss : 0.026223, training accuracy :  1
2019-12-07 17:25:12.562384: Step [91000]  train Loss : 0.044388, training accuracy :  1
2019-12-07 17:25:13.759702: Step [91000]  val Loss : 0.036466, val accuracy :  0.989
------save:mnistmodelslenet/best_models_91000_0.9890.ckpt
2019-12-07 17:25:16.260234: Step [91100]  train Loss : 0.004893, training accuracy :  1
2019-12-07 17:25:18.732957: Step [91200]  train Loss : 0.008099, training accuracy :  1
2019-12-07 17:25:21.232881: Step [91300]  train Loss : 0.010900, training accuracy :  1
2019-12-07 17:25:23.690347: Step [91400]  train Loss : 0.006691, training accuracy :  1
2019-12-07 17:25:26.077798: Step [91500]  train Loss : 0.019277, training accuracy :  1
2019-12-07 17:25:27.110041: Step [91500]  val Loss : 0.036495, val accuracy :  0.9884
2019-12-07 17:25:29.541535: Step [91600]  train Loss : 0.005347, training accuracy :  1
2019-12-07 17:25:32.070571: Step [91700]  train Loss : 0.030143, training accuracy :  0.99
2019-12-07 17:25:34.517069: Step [91800]  train Loss : 0.001880, training accuracy :  1
2019-12-07 17:25:36.974632: Step [91900]  train Loss : 0.013069, training accuracy :  1
2019-12-07 17:25:39.431848: Step [92000]  train Loss : 0.017495, training accuracy :  1
2019-12-07 17:25:40.314588: Step [92000]  val Loss : 0.036441, val accuracy :  0.9892
------------save:mnistmodelslenet/model.ckpt--92000
------save:mnistmodelslenet/best_models_92000_0.9892.ckpt
2019-12-07 17:25:42.887467: Step [92100]  train Loss : 0.020958, training accuracy :  0.99
2019-12-07 17:25:45.348507: Step [92200]  train Loss : 0.017127, training accuracy :  1
2019-12-07 17:25:47.756854: Step [92300]  train Loss : 0.019064, training accuracy :  1
2019-12-07 17:25:50.219324: Step [92400]  train Loss : 0.032903, training accuracy :  0.99
2019-12-07 17:25:52.707338: Step [92500]  train Loss : 0.011051, training accuracy :  1
2019-12-07 17:25:53.819837: Step [92500]  val Loss : 0.036477, val accuracy :  0.9888
2019-12-07 17:25:56.214045: Step [92600]  train Loss : 0.005283, training accuracy :  1
2019-12-07 17:25:58.574659: Step [92700]  train Loss : 0.038343, training accuracy :  0.99
2019-12-07 17:26:01.022120: Step [92800]  train Loss : 0.029472, training accuracy :  1
2019-12-07 17:26:03.614888: Step [92900]  train Loss : 0.029430, training accuracy :  1
2019-12-07 17:26:06.048187: Step [93000]  train Loss : 0.005918, training accuracy :  1
2019-12-07 17:26:07.162769: Step [93000]  val Loss : 0.037015, val accuracy :  0.9886
2019-12-07 17:26:09.589461: Step [93100]  train Loss : 0.013980, training accuracy :  1
2019-12-07 17:26:11.909845: Step [93200]  train Loss : 0.022814, training accuracy :  1
2019-12-07 17:26:14.209764: Step [93300]  train Loss : 0.051712, training accuracy :  0.99
2019-12-07 17:26:16.525746: Step [93400]  train Loss : 0.003427, training accuracy :  1
2019-12-07 17:26:18.908377: Step [93500]  train Loss : 0.049566, training accuracy :  0.97
2019-12-07 17:26:19.907547: Step [93500]  val Loss : 0.037099, val accuracy :  0.9882
2019-12-07 17:26:22.199820: Step [93600]  train Loss : 0.005968, training accuracy :  1
2019-12-07 17:26:24.493318: Step [93700]  train Loss : 0.026799, training accuracy :  0.99
2019-12-07 17:26:26.861115: Step [93800]  train Loss : 0.040578, training accuracy :  1
2019-12-07 17:26:29.220762: Step [93900]  train Loss : 0.035674, training accuracy :  1
2019-12-07 17:26:31.592105: Step [94000]  train Loss : 0.018340, training accuracy :  1
2019-12-07 17:26:32.606197: Step [94000]  val Loss : 0.036546, val accuracy :  0.9886
------------save:mnistmodelslenet/model.ckpt--94000
2019-12-07 17:26:35.125010: Step [94100]  train Loss : 0.026360, training accuracy :  0.99
2019-12-07 17:26:37.567095: Step [94200]  train Loss : 0.041744, training accuracy :  0.99
2019-12-07 17:26:40.025393: Step [94300]  train Loss : 0.031769, training accuracy :  0.99
2019-12-07 17:26:42.449197: Step [94400]  train Loss : 0.031708, training accuracy :  1
2019-12-07 17:26:44.815271: Step [94500]  train Loss : 0.011965, training accuracy :  1
2019-12-07 17:26:45.691042: Step [94500]  val Loss : 0.036568, val accuracy :  0.9886
2019-12-07 17:26:48.046238: Step [94600]  train Loss : 0.098086, training accuracy :  0.98
2019-12-07 17:26:50.442090: Step [94700]  train Loss : 0.003194, training accuracy :  1
2019-12-07 17:26:52.843926: Step [94800]  train Loss : 0.011078, training accuracy :  1
2019-12-07 17:26:55.218067: Step [94900]  train Loss : 0.046635, training accuracy :  0.99
2019-12-07 17:26:57.702115: Step [95000]  train Loss : 0.085678, training accuracy :  0.99
2019-12-07 17:26:58.642553: Step [95000]  val Loss : 0.036330, val accuracy :  0.9888
2019-12-07 17:27:01.059867: Step [95100]  train Loss : 0.005422, training accuracy :  1
2019-12-07 17:27:03.646401: Step [95200]  train Loss : 0.033761, training accuracy :  0.99
2019-12-07 17:27:06.157741: Step [95300]  train Loss : 0.012471, training accuracy :  1
2019-12-07 17:27:08.633951: Step [95400]  train Loss : 0.006597, training accuracy :  1
2019-12-07 17:27:11.085771: Step [95500]  train Loss : 0.006471, training accuracy :  1
2019-12-07 17:27:12.274676: Step [95500]  val Loss : 0.036216, val accuracy :  0.9886
2019-12-07 17:27:14.697503: Step [95600]  train Loss : 0.010805, training accuracy :  1
2019-12-07 17:27:17.166638: Step [95700]  train Loss : 0.095174, training accuracy :  0.98
2019-12-07 17:27:19.631921: Step [95800]  train Loss : 0.011734, training accuracy :  1
2019-12-07 17:27:22.005459: Step [95900]  train Loss : 0.004943, training accuracy :  1
2019-12-07 17:27:24.429525: Step [96000]  train Loss : 0.041252, training accuracy :  0.99
2019-12-07 17:27:25.490167: Step [96000]  val Loss : 0.036082, val accuracy :  0.9892
------------save:mnistmodelslenet/model.ckpt--96000
2019-12-07 17:27:28.031883: Step [96100]  train Loss : 0.013173, training accuracy :  1
2019-12-07 17:27:30.490614: Step [96200]  train Loss : 0.135211, training accuracy :  0.99
2019-12-07 17:27:32.920059: Step [96300]  train Loss : 0.008340, training accuracy :  1
2019-12-07 17:27:35.366707: Step [96400]  train Loss : 0.029178, training accuracy :  0.99
2019-12-07 17:27:37.778924: Step [96500]  train Loss : 0.023783, training accuracy :  1
2019-12-07 17:27:38.662121: Step [96500]  val Loss : 0.035978, val accuracy :  0.989
2019-12-07 17:27:41.007801: Step [96600]  train Loss : 0.020418, training accuracy :  1
2019-12-07 17:27:43.373862: Step [96700]  train Loss : 0.005956, training accuracy :  1
2019-12-07 17:27:45.718948: Step [96800]  train Loss : 0.037369, training accuracy :  1
2019-12-07 17:27:48.053417: Step [96900]  train Loss : 0.001790, training accuracy :  1
2019-12-07 17:27:50.418627: Step [97000]  train Loss : 0.012481, training accuracy :  1
2019-12-07 17:27:51.278477: Step [97000]  val Loss : 0.035982, val accuracy :  0.9886
2019-12-07 17:27:53.621815: Step [97100]  train Loss : 0.036737, training accuracy :  1
2019-12-07 17:27:55.998817: Step [97200]  train Loss : 0.018639, training accuracy :  1
2019-12-07 17:27:58.385280: Step [97300]  train Loss : 0.094415, training accuracy :  0.99
2019-12-07 17:28:00.782485: Step [97400]  train Loss : 0.054417, training accuracy :  1
2019-12-07 17:28:03.282768: Step [97500]  train Loss : 0.018801, training accuracy :  1
2019-12-07 17:28:04.231569: Step [97500]  val Loss : 0.035901, val accuracy :  0.989
2019-12-07 17:28:06.564398: Step [97600]  train Loss : 0.015873, training accuracy :  1
2019-12-07 17:28:08.946638: Step [97700]  train Loss : 0.045842, training accuracy :  1
2019-12-07 17:28:11.314134: Step [97800]  train Loss : 0.032827, training accuracy :  1
2019-12-07 17:28:13.673403: Step [97900]  train Loss : 0.031645, training accuracy :  1
2019-12-07 17:28:16.060070: Step [98000]  train Loss : 0.021970, training accuracy :  1
2019-12-07 17:28:17.015618: Step [98000]  val Loss : 0.035966, val accuracy :  0.9888
------------save:mnistmodelslenet/model.ckpt--98000
2019-12-07 17:28:19.395503: Step [98100]  train Loss : 0.019510, training accuracy :  0.99
2019-12-07 17:28:21.769422: Step [98200]  train Loss : 0.004839, training accuracy :  1
2019-12-07 17:28:24.167367: Step [98300]  train Loss : 0.041200, training accuracy :  1
2019-12-07 17:28:26.626932: Step [98400]  train Loss : 0.003081, training accuracy :  1
2019-12-07 17:28:29.028384: Step [98500]  train Loss : 0.008532, training accuracy :  1
2019-12-07 17:28:30.105535: Step [98500]  val Loss : 0.036248, val accuracy :  0.9888
2019-12-07 17:28:32.468981: Step [98600]  train Loss : 0.005621, training accuracy :  1
2019-12-07 17:28:34.832019: Step [98700]  train Loss : 0.036646, training accuracy :  0.98
2019-12-07 17:28:37.111677: Step [98800]  train Loss : 0.013360, training accuracy :  1
2019-12-07 17:28:39.440640: Step [98900]  train Loss : 0.008248, training accuracy :  1
2019-12-07 17:28:41.834450: Step [99000]  train Loss : 0.027080, training accuracy :  1
2019-12-07 17:28:42.989248: Step [99000]  val Loss : 0.036153, val accuracy :  0.9894
------save:mnistmodelslenet/best_models_99000_0.9894.ckpt
2019-12-07 17:28:45.395565: Step [99100]  train Loss : 0.019798, training accuracy :  1
2019-12-07 17:28:47.756673: Step [99200]  train Loss : 0.002656, training accuracy :  1
2019-12-07 17:28:50.155678: Step [99300]  train Loss : 0.011562, training accuracy :  0.99
2019-12-07 17:28:52.494549: Step [99400]  train Loss : 0.052770, training accuracy :  0.99
2019-12-07 17:28:54.866737: Step [99500]  train Loss : 0.015489, training accuracy :  1
2019-12-07 17:28:56.039745: Step [99500]  val Loss : 0.036166, val accuracy :  0.9892
2019-12-07 17:28:58.406051: Step [99600]  train Loss : 0.016589, training accuracy :  0.99
2019-12-07 17:29:00.762953: Step [99700]  train Loss : 0.034062, training accuracy :  0.99
2019-12-07 17:29:03.175084: Step [99800]  train Loss : 0.013509, training accuracy :  0.99
2019-12-07 17:29:05.604738: Step [99900]  train Loss : 0.004104, training accuracy :  1
2019-12-07 17:29:08.008282: Step [100000]  train Loss : 0.009301, training accuracy :  1
2019-12-07 17:29:09.053212: Step [100000]  val Loss : 0.036054, val accuracy :  0.989
------------save:mnistmodelslenet/model.ckpt--100000

  

2,上面定義的LeNet-5模型訓練結果

   訓練結果如下:

2019-12-07 16:37:20.799084: Step [94100]  train Loss : 0.207280, training accuracy :  0.97
2019-12-07 16:37:23.077235: Step [94200]  train Loss : 0.288277, training accuracy :  0.94
2019-12-07 16:37:25.308687: Step [94300]  train Loss : 0.230948, training accuracy :  0.95
2019-12-07 16:37:27.574248: Step [94400]  train Loss : 0.149708, training accuracy :  0.96
2019-12-07 16:37:29.899668: Step [94500]  train Loss : 0.215018, training accuracy :  0.93
2019-12-07 16:37:30.988016: Step [94500]  val Loss : 0.202200, val accuracy :  0.9526
2019-12-07 16:37:33.308174: Step [94600]  train Loss : 0.310347, training accuracy :  0.94
2019-12-07 16:37:35.577415: Step [94700]  train Loss : 0.171983, training accuracy :  0.96
2019-12-07 16:37:37.849625: Step [94800]  train Loss : 0.160050, training accuracy :  0.94
2019-12-07 16:37:40.166671: Step [94900]  train Loss : 0.186521, training accuracy :  0.95
2019-12-07 16:37:42.523405: Step [95000]  train Loss : 0.402581, training accuracy :  0.9
2019-12-07 16:37:43.603988: Step [95000]  val Loss : 0.192111, val accuracy :  0.9556
2019-12-07 16:37:45.922971: Step [95100]  train Loss : 0.194871, training accuracy :  0.97
2019-12-07 16:37:48.187889: Step [95200]  train Loss : 0.215393, training accuracy :  0.97
2019-12-07 16:37:50.445268: Step [95300]  train Loss : 0.158608, training accuracy :  0.98
2019-12-07 16:37:52.774339: Step [95400]  train Loss : 0.238952, training accuracy :  0.96
2019-12-07 16:37:55.142354: Step [95500]  train Loss : 0.190960, training accuracy :  0.94
2019-12-07 16:37:56.216869: Step [95500]  val Loss : 0.192757, val accuracy :  0.9556
2019-12-07 16:37:58.410845: Step [95600]  train Loss : 0.139895, training accuracy :  0.94
2019-12-07 16:38:00.652184: Step [95700]  train Loss : 0.314228, training accuracy :  0.96
2019-12-07 16:38:03.027260: Step [95800]  train Loss : 0.430718, training accuracy :  0.94
2019-12-07 16:38:05.350156: Step [95900]  train Loss : 0.289241, training accuracy :  0.93
2019-12-07 16:38:07.706012: Step [96000]  train Loss : 0.239347, training accuracy :  0.93
2019-12-07 16:38:08.765761: Step [96000]  val Loss : 0.184876, val accuracy :  0.9594
------------save:mnistmodelslenet/model.ckpt--96000
------save:mnistmodelslenet/best_models_96000_0.9594.ckpt
2019-12-07 16:38:11.129844: Step [96100]  train Loss : 0.219303, training accuracy :  0.91
2019-12-07 16:38:13.435976: Step [96200]  train Loss : 0.141345, training accuracy :  0.97
2019-12-07 16:38:15.708837: Step [96300]  train Loss : 0.133305, training accuracy :  0.97
2019-12-07 16:38:18.028155: Step [96400]  train Loss : 0.179352, training accuracy :  0.97
2019-12-07 16:38:20.361665: Step [96500]  train Loss : 0.175561, training accuracy :  0.96
2019-12-07 16:38:21.455463: Step [96500]  val Loss : 0.196051, val accuracy :  0.9538
2019-12-07 16:38:23.723929: Step [96600]  train Loss : 0.207480, training accuracy :  0.96
2019-12-07 16:38:26.097808: Step [96700]  train Loss : 0.289101, training accuracy :  0.92
2019-12-07 16:38:28.399593: Step [96800]  train Loss : 0.205839, training accuracy :  0.95
2019-12-07 16:38:30.676930: Step [96900]  train Loss : 0.162070, training accuracy :  0.97
2019-12-07 16:38:32.984553: Step [97000]  train Loss : 0.287059, training accuracy :  0.89
2019-12-07 16:38:34.081359: Step [97000]  val Loss : 0.187286, val accuracy :  0.9586
2019-12-07 16:38:36.332393: Step [97100]  train Loss : 0.289821, training accuracy :  0.93
2019-12-07 16:38:38.690675: Step [97200]  train Loss : 0.180355, training accuracy :  0.94
2019-12-07 16:38:41.001430: Step [97300]  train Loss : 0.133220, training accuracy :  0.95
2019-12-07 16:38:43.329969: Step [97400]  train Loss : 0.226960, training accuracy :  0.95
2019-12-07 16:38:45.693958: Step [97500]  train Loss : 0.194481, training accuracy :  0.93
2019-12-07 16:38:46.738666: Step [97500]  val Loss : 0.187791, val accuracy :  0.9576
2019-12-07 16:38:49.010076: Step [97600]  train Loss : 0.172692, training accuracy :  0.94
2019-12-07 16:38:51.285696: Step [97700]  train Loss : 0.150980, training accuracy :  0.95
2019-12-07 16:38:53.544084: Step [97800]  train Loss : 0.150606, training accuracy :  0.96
2019-12-07 16:38:55.860818: Step [97900]  train Loss : 0.191217, training accuracy :  0.93
2019-12-07 16:38:58.213331: Step [98000]  train Loss : 0.252721, training accuracy :  0.94
2019-12-07 16:38:59.307352: Step [98000]  val Loss : 0.191428, val accuracy :  0.9552
------------save:mnistmodelslenet/model.ckpt--98000
2019-12-07 16:39:01.620290: Step [98100]  train Loss : 0.218954, training accuracy :  0.95
2019-12-07 16:39:04.016540: Step [98200]  train Loss : 0.353565, training accuracy :  0.92
2019-12-07 16:39:06.314311: Step [98300]  train Loss : 0.169997, training accuracy :  0.99
2019-12-07 16:39:08.590750: Step [98400]  train Loss : 0.201064, training accuracy :  0.96
2019-12-07 16:39:10.943120: Step [98500]  train Loss : 0.100543, training accuracy :  1
2019-12-07 16:39:12.034701: Step [98500]  val Loss : 0.191718, val accuracy :  0.9548
2019-12-07 16:39:14.344574: Step [98600]  train Loss : 0.220537, training accuracy :  0.95
2019-12-07 16:39:16.655703: Step [98700]  train Loss : 0.195116, training accuracy :  0.94
2019-12-07 16:39:18.929639: Step [98800]  train Loss : 0.203131, training accuracy :  0.96
2019-12-07 16:39:21.243188: Step [98900]  train Loss : 0.167681, training accuracy :  0.95
2019-12-07 16:39:23.582402: Step [99000]  train Loss : 0.339591, training accuracy :  0.93
2019-12-07 16:39:24.708781: Step [99000]  val Loss : 0.188099, val accuracy :  0.9536
2019-12-07 16:39:26.917352: Step [99100]  train Loss : 0.359073, training accuracy :  0.93
2019-12-07 16:39:29.100260: Step [99200]  train Loss : 0.283282, training accuracy :  0.94
2019-12-07 16:39:31.351049: Step [99300]  train Loss : 0.289421, training accuracy :  0.93
2019-12-07 16:39:33.642279: Step [99400]  train Loss : 0.240311, training accuracy :  0.92
2019-12-07 16:39:35.913139: Step [99500]  train Loss : 0.192217, training accuracy :  0.94
2019-12-07 16:39:36.802653: Step [99500]  val Loss : 0.191198, val accuracy :  0.958
2019-12-07 16:39:39.002652: Step [99600]  train Loss : 0.188396, training accuracy :  0.97
2019-12-07 16:39:41.357152: Step [99700]  train Loss : 0.111778, training accuracy :  0.96
2019-12-07 16:39:43.685702: Step [99800]  train Loss : 0.178156, training accuracy :  0.94
2019-12-07 16:39:46.043234: Step [99900]  train Loss : 0.124271, training accuracy :  0.97
2019-12-07 16:39:48.407596: Step [100000]  train Loss : 0.094911, training accuracy :  0.97
2019-12-07 16:39:49.310749: Step [100000]  val Loss : 0.193183, val accuracy :  0.9572
------------save:mnistmodelslenet/model.ckpt--100000

  總的來說,人家谷歌封裝在slim中:LeNet-5 比我自己搭建的Lenet-5模型效果好點,但是呢,我的大體上已經達到要求了,就不強求了,哈哈哈。

  到此為止,我們使用LeNet-5模型訓練了MNIST資料集,下面使用LeNet-5訓練自己的模型。其實已經很簡單了。直接更換資料集即可。

4,使用LeNet-5訓練自己的資料集(TFRecord格式)

  這裡的資料集處理格式就貼上了,前面有講,而且有MNIST資料格式轉化的程式碼。

  這裡的LeNet-5模型是使用 slim 中官方定義的模型。

  (注意:這裡自己的資料集通道為3,但是MNIST資料集的通道為1,看是否一致)

  最後使用LeNet-5模型訓練自己的資料集,這裡我將資料集尺寸大小重置為28*28,比較LeNet-5預設定義的圖片大小為28*28,而這裡資料使用之前的51類進行分類訓練,下面直接貼上程式碼:

# _*_coding:utf-8_*_
import tensorflow as tf
import numpy as np
import pdb
import os
from datetime import datetime
import slim.nets.lenet as lenet
from create_tf_record import *
import tensorflow.contrib.slim as slim

labels_nums = 51  # 類別個數
batch_size = 16
resize_height = 28  # mobilenet_v1.default_image_size 指定儲存圖片高度
resize_width = 28  # mobilenet_v1.default_image_size 指定儲存圖片高度
depths = 3
data_shape = [batch_size, resize_height, resize_width, depths]

# 定義input_images為圖片資料
input_images = tf.placeholder(dtype=tf.float32,
                              shape=[None, resize_height, resize_width, depths],
                              name='input')

# 定義input_labels為lables資料
# input_labels = tf.placeholder(dtype=tf.int32, shape=[None], name='label')
input_labels = tf.placeholder(dtype=tf.int32,
                              shape=[None, labels_nums],
                              name='label')

# 定義 dropout的概率
keep_prob = tf.placeholder(tf.float32, name='keep_prob')
is_training = tf.placeholder(tf.bool, name='is_training')


# 評價函式
def net_evaluation(sess, loss, accuracy, val_images_batch,
                   val_labels_batch, val_nums):
    val_max_steps = int(val_nums / batch_size)
    val_losses = []
    val_accs = []
    for _ in range(val_max_steps):
        val_x, val_y = sess.run([val_images_batch, val_labels_batch])
        # print('labels:',val_y)
        # val_loss = sess.run(loss, feed_dict={x: val_x, y: val_y, keep_prob: 1.0})
        # val_acc = sess.run(accuracy,feed_dict={x: val_x, y: val_y, keep_prob: 1.0})
        val_loss, val_acc = sess.run([loss, accuracy],
                                     feed_dict={input_images: val_x,
                                                input_labels: val_y,
                                                keep_prob: 1.0,
                                                is_training: False})
        val_losses.append(val_loss)
        val_accs.append(val_acc)
    mean_loss = np.array(val_losses, dtype=np.float32).mean()
    mean_acc = np.array(val_accs, dtype=np.float32).mean()
    return mean_loss, mean_acc


def step_train(train_op, loss, accuracy,
               train_images_batch, train_labels_batch, train_nums, train_log_step,
               val_images_batch, val_labels_batch, val_nums, val_log_step,
               snapshot_prefix, snapshot):
    '''
    迴圈迭代訓練過程
    :param train_op: 訓練op
    :param loss:     loss函式
    :param accuracy: 準確率函式
    :param train_images_batch: 訓練images資料
    :param train_labels_batch: 訓練labels資料
    :param train_nums:         總訓練資料
    :param train_log_step:   訓練log顯示間隔
    :param val_images_batch: 驗證images資料
    :param val_labels_batch: 驗證labels資料
    :param val_nums:         總驗證資料
    :param val_log_step:     驗證log顯示間隔
    :param snapshot_prefix: 模型儲存的路徑
    :param snapshot:        模型儲存間