Tensorflow實現LeNet-5、Saver保存與讀取


一、 LeNet-5

  • LeNet-5是一種用於手寫體字符識別的非常高效的卷積神經網絡。
  • 卷積神經網絡能夠很好的利用圖像的結構信息。
  • 卷積層的參數較少,這也是由卷積層的主要特性即局部連接和共享權重所決定。

  LeNet-5共有7層,不包含輸入,每層都包含可訓練參數;每個層有多個Feature Map,每個FeatureMap通過一種卷積濾波器提取輸入的一種特征,然后每個FeatureMap有多個神經元。

 

數據集:mnist

  • train-images-idx3-ubyte 訓練數據圖像 (60,000)
  • train-labels-idx1-ubyte 訓練數據label
  • t10k-images-idx3-ubyte 測試數據圖像 (10,000)
  • t10k-labels-idx1-ubyte 測試數據label

每張圖像是28*28像素:

  我們的任務是使用上面數據訓練一個可以准確識別手寫數字的神經網絡模型,並使用Tensorflow對訓練過程各個參數的變化進行可視化。

1. 准備數據集、定義超參數等准備工作

(1)導入需要使用的包

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

# gpu設置
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
config = tf.ConfigProto(allow_soft_placement = True)
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction = 0.33)
config.gpu_options.allow_growth = True

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

上述代碼的意思是使用GPU設備0,最多給GPU分配總共內存的百分之33,並且允許GPU按需申請內存。也就是說,假設一個程序使用一塊GPU內存百分之10就夠了,如果我們沒有指定allow_growth=True,那么程序會直接占用GPU內存的百分之33,因為這個是我們給它分配的。如果我們連0.33,也就是GPU內存的百分之33都沒有指定,那么程序會直接占用整個GPU設備0。雖然占用這么多沒有用,但是我就占着,屬於“占着茅坑不拉屎”。所以,為了充分利用資源,特別是一幫人使用一個服務器的時候,指定下這些參數就很有必要了。

2. 數據處理

(1)創建輸入數據的占位符,分別創建特征數據xs,標簽數據ys

  在tf.placeholder()函數中傳入了3個參數,第一個是定義數據類型為float32;第二個是數據的大小,特征數據是大小784的向量,標簽數據是大小為10的向量,None表示不確定死大小,到時候可以傳入任何數量的樣本;第3個參數是這個占位符的名稱。

sess=tf.Session()

# 創建輸入數據的占位符,分別創建特征數據xs,標簽數據ys
with tf.name_scope('input'):
    xs=tf.placeholder(tf.float32,[None,784])
    ys=tf.placeholder(tf.float32,[None,10])

keep_prob=tf.placeholder(tf.float32)

  mnist下載好的數據集就是很多個1*784的向量,就是已經對28*28的圖片進行了向量化處理。

(2)使用tf.summary.image保存圖像信息

  特征數據其實就是圖像的像素數據拉升成一個1*784的向量,現在如果想在tensorboard上還原出輸入的特征數據對應的圖片,就需要將拉升的向量轉變成28 * 28 * 1的原始像素了,於是可以用tf.reshape()直接重新調整特征數據的維度:

  將輸入的數據轉換成[28 * 28 * 1]的shape,存儲成另一個tensor,命名為image_shaped_input。為了能使圖片在tensorbord上展示出來,使用tf.summary.image將圖片數據匯總給tensorbord。tf.summary.image()中傳入的第一個參數是命名,第二個是圖片數據,第三個是最多展示的張數,此處為10張。

# 保存圖像信息
with tf.name_scope('input_reshape'):
    image_shaped_input = tf.reshape(x, [-1, 28, 28, 1])
    tf.summary.image('input', image_shaped_input, 10)

3. 初始化參數並保存參數信息到summary

(1)初始化參數w和b

  在構建神經網絡模型中,每一層中都需要去初始化參數w,b,為了使代碼簡介美觀,最好將初始化參數的過程封裝成方法function。 創建初始化權重w的方法,生成大小等於傳入的shape參數,標准差為0.1,遵循正態分布的隨機數,並且將它轉換成tensorflow中的variable返回。

def weight_variable(shape): 
    initial=tf.truncated_normal(shape,stddev=0.1) #tf.truncted_normal產生隨機變量來進行初始化,類似normal  
    return tf.Variable(initial)

  創建初始換偏執項b的方法,生成大小為傳入參數shape的常數0.1,並將其轉換成tensorflow的variable並返回。

def bias_variable(shape): 
    initial=tf.constant(0.1,shape=shape) 
    return tf.Variable(initial)

(2)記錄訓練過程參數變化

  我們知道,在訓練的過程在參數是不斷地在改變和優化的,我們往往想知道每次迭代后參數都做了哪些變化,可以將參數的信息展現在tenorbord上,因此我們專門寫一個方法來收錄每次的參數信息。

# 繪制參數變化
def variable_summaries(var):
    with tf.name_scope('summaries'):
        # 計算參數的均值,並使用tf.summary.scaler記錄
        mean = tf.reduce_mean(var)
        tf.summary.scalar('mean', mean)

        # 計算參數的標准差
        with tf.name_scope('stddev'):
            stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
        # 使用tf.summary.scaler記錄記錄下標准差,最大值,最小值
        tf.summary.scalar('stddev', stddev)
        tf.summary.scalar('max', tf.reduce_max(var))
        tf.summary.scalar('min', tf.reduce_min(var))
        # 用直方圖記錄參數的分布
        tf.summary.histogram('histogram', var)

4. 搭建神經網絡層 

(1)定義卷積層、max_pool層

  定義卷積,tf.nn.conv2d函數是tensoflow里面的二維的卷積函數,x是圖片的所有參數,W是此卷積層的權重,然后定義步長strides=[1,1,1,1]值,strides[0]和strides[3]的兩個1是默認值,中間兩個1代表padding時在x方向運動一步,y方向運動一步,padding采用的方式是SAME。

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

  接着定義池化pooling,為了得到更多的圖片信息,padding時我們選的是一次一步,也就是strides[1]=strides[2]=1,這樣得到的圖片尺寸沒有變化,而我們希望壓縮一下圖片也就是參數能少一些從而減小系統的復雜度,因此我們采用pooling來稀疏化參數,也就是卷積神經網絡中所謂的下采樣層。pooling 有兩種,一種是最大值池化,一種是平均值池化,本例采用的是最大值池化tf.max_pool()。池化的核函數大小為2x2,因此ksize=[1,2,2,1],步長為2,因此strides=[1,2,2,1]

def max_pool_2x2(x): 
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

(2)建立卷積層conv1,conv2、建立全連接層fc1,fc2

建立卷積層conv1,conv2

# 建立卷積層conv1,conv2
with tf.name_scope('conv1'):
    W_conv1=weight_variable([5,5,1,32])#patch/kernel 5*5,in size 1是image的厚度,out size 32
    b_conv1=bias_variable([32]) #一個kernel一個bias
    h_conv1=tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)#output size 28*28*32

with tf.name_scope('max_pool1'):    
    h_pool1=max_pool_2x2(h_conv1) #output size 14*14*32  

with tf.name_scope('conv2'):
    W_conv2=weight_variable([5,5,32,64])#kernel 5*5,in size 32,out size 64
    b_conv2=bias_variable([64])#output size 14*14*64
    h_conv2=tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)#output size 7*7*64

with tf.name_scope('max_pool2'):
    h_pool2=max_pool_2x2(h_conv2)

建立全連接層fc1,fc2

#建立全連接層fc1,fc2
#[n_samples,7,7,64]->>[n_samples,7*7*64]
with tf.name_scope('fc1'):
    h_pool2_flat=tf.reshape(h_pool2,[-1,7*7*64]) 
    W_fc1=weight_variable([7*7*64,1024]) 
    b_fc1=bias_variable([1024])
    h_fc1=tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)
    h_fc1_drop=tf.nn.dropout(h_fc1,keep_prob)
    tf.summary.scalar('dropout_keep_probability',keep_prob)

with tf.name_scope('fc2'):
    W_fc2=weight_variable([1024,10]) 
    b_fc2=bias_variable([10])
    prediction=tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2) #最終的結果
    tf.summary.histogram('prediction',prediction)

  

模型可視化:

預測值分布:

預測值直方圖:

 

 

 

5. 損失函數與優化

#交叉熵
with tf.name_scope('cross_entropy'):
    cross_entropy=tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1]))
    tf.summary.scalar('loss',cross_entropy)

#優化器
with tf.name_scope('train'):
    train_step=tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

  

損失值變化:

 

6. 訓練

#所有變量初始化
# summaries合並
merged = tf.summary.merge_all()

# 寫到指定的磁盤路徑中
train_writer = tf.summary.FileWriter('E:/nxf_anaconda_base_jupyter_ht/LeNet/train',sess.graph)
test_writer = tf.summary.FileWriter('E:/nxf_anaconda_base_jupyter_ht/LeNet/test',sess.graph)

sess.run(tf.global_variables_initializer())

# 計算准確率
with tf.name_scope('accuracy'):
    with tf.name_scope('correct_prediction'):
        # 分別將預測和真實的標簽中取出最大值的索引,相同則返回1(true),不同則返回0(false)
        correct_prediction = tf.equal(tf.argmax(prediction, 1), tf.argmax(ys, 1))
    with tf.name_scope('accuracy'):
        # 求均值即為准確率
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
         
tf.summary.scalar('accuracy', accuracy)

  

7. 所有變量初始化、送入數據集

  feed_dict用於獲取數據,如果是train==true,也就是進行訓練的時候,就從mnist.train中獲取一個batch大小為100樣本,並且設置dropout值為0.9。如果是不是train==false,則獲取minist.test的測試數據,並且設置dropout為1,即保留所有神經元開啟。

  同時,每隔10步,進行一次測試,並打印一次測試數據集的准確率,然后將測試數據集的各種summary信息寫進日志中。 其余的時候,都是在進行訓練,將訓練集的summary信息並寫到日志中。

def feed_dict(train):
    """Make a TensorFlow feed_dict: maps data onto Tensor placeholders."""
    if train:
        x, y = mnist.train.next_batch(10)
        k = 0.9 #dropout
    else:
        x, y = mnist.test.images[100:200], mnist.test.labels[100:200]
        k = 1.0
    return {xs: x, ys: y, keep_prob: k}
 
for i in range(1000):
    if i % 10 == 0:  #記錄測試集的summary與accuracy
        summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
        test_writer.add_summary(summary, i)
        print('Accuracy at step %s: %s' % (i, acc))
    else:  # 記錄訓練集的summary
        summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True))
        train_writer.add_summary(summary, i)
        
train_writer.close()
test_writer.close()

 

 

 

  

二、saver的save和restore

TensorFlow提供了一個非常方便的api,tf.train.Saver()用來保存和還原一個機器學習模型。

程序會生成並保存四個文件:

  • checkpoint :文本文件,記錄了模型文件的路徑信息列表
  • save_net.ckpt.data-00000-of-00001:網絡權重信息
  • save_net.ckpt.index:.data和.index這兩個文件是二進制文件,保存了模型中的變量參數(權重)信息
  • save_net.ckpt.meta:二進制文件,保存了模型的計算圖結構信息(模型的網絡結構)protobuf

建立模型

import tensorflow as tf
import numpy as np

## Save to file
# remember to define the same dtype and shape when restore
W = tf.Variable([[1,2,3],[3,4,5]], dtype=tf.float32, name='weights')
b = tf.Variable([[1,2,3]], dtype=tf.float32, name='biases')

# init= tf.initialize_all_variables() # tf 馬上就要廢棄這種寫法
# 替換成下面的寫法:
init = tf.global_variables_initializer() 

變量的保存

saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(init)
    save_path = saver.save(sess, "nxf_net/save_net.ckpt")
    print("Save to path: ", save_path)

"""    
Save to path:  nxf_net/save_net.ckpt
"""

變量的重載

# restore variables
# reduce the same shape and same type for your variables
# 先建立W,b的容器
W = tf.Variable(np.arange(6).reshape((2, 3)), dtype=tf.float32, name="weights")
b = tf.Variable(np.arange(3).reshape((1, 3)), dtype=tf.float32, name="biases")

# 這里不需要初始化步驟 init= tf.initialize_all_variables()

saver = tf.train.Saver()
with tf.Session() as sess:
    # 提取變量
    saver.restore(sess, "my_net/save_net.ckpt")
    print("weights:", sess.run(W))
    print("biases:", sess.run(b))

"""
weights: [[ 1.  2.  3.]
          [ 3.  4.  5.]]
biases: [[ 1.  2.  3.]]
""" 

 

 

 

 

 

參考文獻:

【1】莫煩python

【2】Tensorflow模型保存和模型使用

【3】06:Tensorflow的可視化工具Tensorboard的初步使用

【4】Tensorflow API 講解——tf.estimator.Estimator

【5】Tensorflow實戰(一):打響深度學習的第一槍 – 手寫數字識別(Tensorboard可視化)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM