使用自己的數據集創建神經網絡訓練模型


該內容來自---https://blog.csdn.net/weixin_43974748/article/details/89600269

 

 

使用Tensorflow創建自己的數據集,並訓練

介紹環境

win10 + pycharm + CPU

介紹背景

要求用卷積神經網絡對不同水分的玉米進行分類(最后的目標是實現回歸,以后研究),神經網絡雖然是科研神器,但是在工業上的應用效果遠遠不如實驗室中的好。我們找到的教程無非是mnist,表情識別,等官方的數據集。對於一個小白來說雖然上手容易,但是收獲這得有限。這篇博客我希望把每個知識都講到盡量的通俗易懂,希望這篇處女作可以給小白指導。文中部分代碼參考了ywx1832990,在此感謝。受限於水平,有講解錯誤的地方,也歡迎留言探討。

話不多說 直接上代碼

step1:建立兩個TFrecords

# pycharm中此模塊名為genertateds.py

import os
import tensorflow as tf
from PIL import Image
# 源數據地址
cwd = r'C:\Users\pc\Desktop\orig_picture'
# 生成record路徑及文件名
train_record_path =r"C:\Users\pc\Desktop\outputdata\train.tfrecords"
test_record_path =r"C:\Users\pc\Desktop\outputdata\test.tfrecords"
# 分類
classes = {'11.8','13','14.8','16.5','18','20.6','22.8','26.1','28.7','30.6'}

def _byteslist(value): 
    """二進制屬性"""
    return tf.train.Feature(bytes_list = tf.train.BytesList(value = [value]))

def _int64list(value):
    """整數屬性"""
    return tf.train.Feature(int64_list = tf.train.Int64List(value = [value]))

def create_train_record():
    """創建訓練集tfrecord"""
    writer = tf.python_io.TFRecordWriter(train_record_path)     # 創建一個writer
    NUM = 1                                     # 顯示創建過程(計數)
    for index, name in enumerate(classes):
        class_path = cwd + "/" + name + '/'
        l = int(len(os.listdir(class_path)) * 0.7)      # 取前70%創建訓練集
        for img_name in os.listdir(class_path)[:l]:
            img_path = class_path + img_name
            img = Image.open(img_path)
            img = img.resize((128, 128))                # resize圖片大小
            img_raw = img.tobytes()                     # 將圖片轉化為原生bytes
            example = tf.train.Example(                 # 封裝到Example中
                features=tf.train.Features(feature={
                    "label":_int64list(index),          # label必須為整數類型屬性
                    'img_raw':_byteslist(img_raw)       # 圖片必須為二進制屬性
                }))
            writer.write(example.SerializeToString())
            print('Creating train record in ',NUM)
            NUM += 1
    writer.close()                                      # 關閉writer
    print("Create train_record successful!")

def create_test_record():
    """創建測試tfrecord"""
    writer = tf.python_io.TFRecordWriter(test_record_path)
    NUM = 1
    for index, name in enumerate(classes):
        class_path = cwd + '/' + name + '/'
        l = int(len(os.listdir(class_path)) * 0.7)
        for img_name in os.listdir(class_path)[l:]:     # 剩余30%作為測試集
            img_path = class_path + img_name
            img = Image.open(img_path)
            img = img.resize((128, 128))
            img_raw = img.tobytes()  # 將圖片轉化為原生bytes
            # print(index,img_raw)
            example = tf.train.Example(
                features=tf.train.Features(feature={
                    "label":_int64list(index),
                    'img_raw':_byteslist(img_raw)
                }))
            writer.write(example.SerializeToString())
            print('Creating test record in ',NUM)
            NUM += 1
    writer.close()
    print("Create test_record successful!")

def read_record(filename):
    """讀取tfrecord"""
    filename_queue = tf.train.string_input_producer([filename])     # 創建文件隊列
    reader = tf.TFRecordReader()                                    # 創建reader
    _, serialized_example = reader.read(filename_queue)
    features = tf.parse_single_example(
        serialized_example,
        features={
            'label': tf.FixedLenFeature([], tf.int64),
            'img_raw': tf.FixedLenFeature([], tf.string)
        }
    )
    label = features['label']
    img = features['img_raw']
    img = tf.decode_raw(img, tf.uint8)
    img = tf.reshape(img, [128, 128, 3])
    img = tf.cast(img, tf.float32) * (1. / 255) - 0.5       # 歸一化
    label = tf.cast(label, tf.int32)
    return img, label

def get_batch_record(filename,batch_size):
    """獲取batch"""
    image,label = read_record(filename)
    image_batch,label_batch = tf.train.shuffle_batch([image,label],         # 隨機抽取batch size個image、label
                                                     batch_size=batch_size,
                                                     capacity=2000,
                                                     min_after_dequeue=1000)
    return image_batch,label_batch

def main():
    create_train_record()
    create_test_record()
if __name__ == '__main__':
    main()

注1:這里值得一提的是 from PIL import Image 在jupyter中某次更新后,會出現無法使用的現象。建議使用jupyter的朋友不要更新。如果更新了可以卸載,重新安裝之前的版本

注2: windows下 cwd = r’C:\Users\pc\Desktop\orig_picture’的執行可能會產生一些操作系統層面的格式錯誤,在下面必須嚴格遵守 class_path = cwd + “/” + name + ‘/’ 這種格式,否則會出現格式錯誤。

解析

  1.  train_record_path =r"C:\Users\pc\Desktop\outputdata\train.tfrecords"
     test_record_path =r"C:\Users\pc\Desktop\outputdata\test.tfrecords"
   這兩句代碼的意思是定義了兩個路徑,因為這個模塊的作用就是創建TFrecord(一種Tensorflow中管理數據的格式,只要想使用Tensorflow,就需要用TFrecord。TFRecord內部使用的是二進制編碼,它可以很好的把數據一次性通過一個二進制文件讀取進來,而不是一張圖片一張圖片的讀取,節省了時間,增加了效率。)所以需要告訴計算機一個保存的路徑

  2.  class 中不同的數字 classes = {‘11.8’,‘13’,‘14.8’,‘16.5’,‘18’,‘20.6’,‘22.8’,‘26.1’,‘28.7’,‘30.6’} 就是不同的水分

  3.  writer = tf.python_io.TFRecordWriter() 這一部分是一個TFRecord的生成器,一般伴隨着writer.write(),使用完之后需要關閉生成器,即:writer.close()

  4.  tf.train.Example:可以理解為一個包含了Features的內存塊,並通過feature將圖片的二進制數據和label進行統一封裝(把數據和標簽統一存儲), 然后將example轉化為一種字符串的形式, 使用tf.python_io.TFRecordWriter() 寫入到TFRecords文件中。

  5.  def read_record(filename) 意思是把剛剛創建的tfrecord讀取進來 ,至於這里面的函數具體定義了什么,不要深究,我們不是造輪子,會修改其中的關鍵信息即可,如reshape成128*128的三通道圖片。

  6.  tf.cast() 的意思是把tensorflow中的張量數據做一個類型轉換,轉換成了float32類型

  7.  def get_batch_record 獲取批的圖片數據和標簽 。在這里使用了剛剛定義的read_record。tf.train.shuffle_batch是不按照順序的從隊列中讀取數據,最后找兩個變量image_batch和label_batch接收一下。capacity參數的意義是隊列中元素的最大數量,這個無所謂,別設置太小,也別太大,根據你的數據集來決定。

step2:配置圖片的參數+定義前向傳播過程

#這個模塊在pycharm中的名字是forward.py

import tensorflow as tf
# 配置參數
# 圖片size
IMAGE_SIZE = 128
NUM_CHANNELS = 3
NUM_LABELS = 10
# 第一層卷積層的尺寸和深度
CONV1_DEEP = 64
CONV1_SIZE = 5
# 第二層卷積層的尺寸和深度
CONV2_DEEP = 128
CONV2_SIZE = 5
# 全連接層的節點個數
FC_SIZE = 10
def get_Weight(shape,regularizer_rate = None):     # 定義weight如需正則化需傳入zhengzehualv默認值為None
    Weight = tf.Variable(tf.truncated_normal(shape=shape,stddev=0.1),dtype=tf.float32)    # tensorflow API推薦隨機初始化

    if regularizer_rate != None:
        regularizer = tf.contrib.layers.l2_regularizer(regularizer_rate)
        tf.add_to_collection('losses',regularizer(Weight))

    return Weight

def get_biase(shape):       # 定義biase
    biase = tf.Variable(tf.constant(value=0.1,shape=shape),dtype=tf.float32)    # tensorflow API推薦初始化0.1
    return biase

def create_conv2d(x,w):     # 定義卷積層
    conv2d = tf.nn.conv2d(x,w,strides=[1,1,1,1],padding='SAME')     # 步幅為1、SAME填充
    return conv2d

def max_pooling(x):         # 定義最大值池化
    pool = tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')   # ksize為2、步幅為2、SAME填充
    return pool

def create_fc(x,w,b):       # 定義全連接層
    fc = tf.matmul(x,w) + b
    return fc

# 定義前向傳播的過程
# 這里添加了一個新的參數train,用於區分訓練過程和測試過程。

def inference(input_tensor, train, regularizer_rate):
    with tf.variable_scope('layer1-conv1'):
        conv1_Weights = get_Weight([CONV1_SIZE,CONV1_SIZE,NUM_CHANNELS,CONV1_DEEP])     # 5*5*64
        conv1_baises = get_biase([CONV1_DEEP])
        conv1 = tf.nn.bias_add(create_conv2d(input_tensor,conv1_Weights),conv1_baises)
        conv1 = tf.nn.relu(conv1)       # 使用ReLu激活函數

    with tf.name_scope('layer2-pool1'):        # 64*64*64
        pool1 = max_pooling(conv1)

    with tf.variable_scope('layer3-conv2'):
        conv2_Weights = get_Weight([CONV2_SIZE,CONV2_SIZE,CONV1_DEEP,CONV2_DEEP])       # 5*5*128
        conv2_biases = get_biase([CONV2_DEEP])
        conv2 = tf.nn.bias_add(create_conv2d(pool1,conv2_Weights),conv2_biases)
        conv2 = tf.nn.relu(conv2)

    with tf.name_scope('layer4-pool2'):         # 32*32*128
        pool2 = max_pooling(conv2)

        pool_shape = pool2.get_shape().as_list()
        # pool_shape為[batch_size,32,32,128]
        # 計算將矩陣拉直成向量之后的長度,這個長度就是矩陣長度及深度的乘積。
        nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]
        # 通過tf.reshape函數將第四層的輸出變成一個batch的向量
        reshaped = tf.reshape(pool2, [pool_shape[0], nodes])

    # 聲明第五層全連接層的變量並實現前向傳播過程
    with tf.variable_scope('layer5-fc1'):
        fc1_Weights = get_Weight([nodes,FC_SIZE],regularizer_rate)
        fc1_biases = get_biase([FC_SIZE])
        fc1 = tf.nn.relu(create_fc(reshaped,fc1_Weights,fc1_biases))
    # 訓練過程添加dropout防止過擬合
        if train:
            fc1 = tf.nn.dropout(fc1, 0.5)

    # 聲明第六層全連接層的變量並實現前向傳播過程
    with tf.variable_scope('layer6-fc2'):
        fc2_Weights = get_Weight([FC_SIZE,NUM_LABELS],regularizer_rate)
        fc2_biases = get_biase([NUM_LABELS])
        logit = create_fc(fc1,fc2_Weights,fc2_biases)
        # fc2 = tf.nn.relu(fc2)     需要softmax層時使用激活函數


        return logit

 

  注1:在寫代碼的過程中,我們應該把需要配置的參數在開頭聲明出來(一邊寫,寫到哪個參數,就返回開頭定義變量)

  1.  IMAGE_SIZE = 128 呼應了上一個模塊中的reshape
  2.  NUM_CHANNELS = 3 這里涉及到圖像的知識,簡單來說,設置為3就是彩色圖片,設置為1就是灰度圖
  3.  NUM_LABELS = 10 就是你所提前設置好的標簽數(即需要分成多少個類)

  4.  CONV1_DEEP = 64 這里的conv卷積層,其實就是我們所說的filter。這里深度deep的意思就是一次性使用多少個filter (就是過濾器的個數,使用幾個不同的過濾器,就是確定了深度是幾)
  5.  CONV1_SIZE = 5 確定卷積(filter)的size,即使用55的"觀察窗口" 這樣我們第一層的信息量就變成了了55*64

第二層卷積層同理(不懂的小伙伴可以留言)

  6.  FC_SIZE = 10 定義全連接層的個數,全連接層的一般是兩層,最后一層全連接層就是輸出層。全連接層的作用就是特征的加權,最后10個類的所有權值加在一起應該是等於1的。

  7.  def get_Weight 這里定義的是后面需要的權重值。
  8.  tf.truncated_normal(shape, mean, stddev) :shape表示生成張量的維度(這一部分后面用到了,注意一下),mean是均值,stddev是標准差。這個函數產生正太分布,均值和標准差自己設定。
  9.  regularizer_rate的意思是選擇一種正則化方式,沒有指定的話默認使用l2正則化。l2正則化簡單理解就是可以去掉權重值中的超過兩次冪的高此項,個人理解是防止過擬合用的。

  10.  def get_biase(shape): 定義一個偏置
  11.  tf.constant用來定義一個常量,這里的value一般初始化成一個常數或者一個數組。這里初始化成0.1是沒有為什么的,不一定非要使用0.1。shape參數和卷積層的深度有關(即,和使用的filter數量有關,不能像0.1一樣寫死了,是需要變化的)
  12.  tf.Variable(initializer,name)用來初始化一個變量。參數initializer是初始化參數,name是可自定義的變量名稱

  13.  def create_conv2d(x,w)定義一個卷積層,這部分也是很核心的部分
  14.  tf.nn.conv2d(x,w,strides=[1,1,1,1],padding=‘SAME’)
    x是輸入的張量數據 w是權值
    strides=[1,1,1,1] (解釋一下這個參數,其實不需要過多的糾結,只需要記住,strides在官方定義中就必須是一個一維且具有四個元素的張量,其規定前后必須為1,所以四個參數直接定死了兩個,中間的兩個參數一般而言都是相同的,步長為1就寫 strides=[1,1,1,1] ,步長為2就寫 strides=[1,2,2,1] ,由於步長不能過大,因為會損失大量的信息,所以一般選擇步長要么為1,要么為2)
    padding = ‘SAME’ 同樣不需要糾結,就使用SAME即可。本質上說padding是一種filter進行卷積時的策略,一共只有兩種策略可以選擇,這部分到后面的調優環節再考慮不遲,通常情況下,直接使用SAME是完全沒有問題的

  15.  def max_pooling(x) 池化層的表面形式來看,十分類似於之前的卷積層。但是二者做的事情是不一樣的。池化層的作用是為了避免無關的信息過多,干擾結果,或者影響計算速度所進行的一種降維,是一種特征工程。注意:池化層的輸入就是卷積層的輸出,這里隱含了一個條件,就是池化層必須在卷積層之后。
  16.  tf.nn.max_pool(value, ksize, strides, padding, name=None)中 第一個參數value是最需要注意的,這里的value輸入的是feature map(在卷積層中,數據是以一維或者三維的形式存在的。RGB圖片可以理解為3個二維圖片疊在一起,其中每一個稱為一個feature map。而灰度圖就僅僅只存在一個feature map)
  17.  ksize是池化窗口的大小,取一個四維向量,一般是[1, 1, 1, 1]或者[1, 2, 2, 1]。如果在這里你還在糾結為什么是[1, 1, 1, 1]或者[1, 2, 2, 1],大可不必。在學習的初期,我們不應該被這種細枝末節的細節去分散走我們大多的精力,耗散我們的耐心,降低我們的成就感。你只需要知道,大家都這么用就好。
  18.  strides和padding參數的設置同上(沒明白的請留言)

  19.  def create_fc(x,w,b): 定義一個全連接層,全連接層做的事情,前面已經有提過這里不做贅述,還希望深入了解全連接層作用的同學可以自行查找一些資料

  20.  def inference(input_tensor, train, regularizer_rate): 定義一個函數,構造神經網絡:神經網絡的結構為:輸入→卷積層→池化層→卷積層→池化→全連接→全連接。
  21.  train這個參數在后面坐了一次if判斷,關系到是否要進行dropout
     dropout可以理解為在防止過擬合。他的具體過程簡要來說,就是不一次性給機器看到所有的數據,每次訓練都隨機較少一部分數據。
  22.  with tf.name_scope的意思就是方便你看代碼的,類似於命名空間,不理解可以不看這部分,照着寫就可以了。無關痛癢。

在代碼運行的中間部分位置使用了一種激活函數–relu。relu函數可以理解為一種可以讓機器學習到非線性特征的激活函數。因為我們之前做的無非是矩陣運算,這些都是屬於線性運算(要不為啥是線性代數呢…)然而真是的世界中,事物往往都不是線性存在的,加入relu之后我們可以讓機器學到這種非線性的特征

step3:定義反向傳播網絡

如果說之前我們在做的的是從前往后推導,模擬的是人類神經元的思維過程。那么反向傳播網絡在我看來,就已經超出了人類生物模型的范圍,而變成了一種數學上的推導。可以說,在從前往后推導中我們已經建立一種可以用的模型,但是這個模型可以說是“慘不忍睹”,准確率低的令人大致,loss高得不忍直視。我們的前輩想到一種方法,提高模型的准確率。就是所謂的反向傳播。通俗解釋就是,基於現在的准確率,反向優化建模中所有的參數,再用經過優化的參數,重復建模的過程,再得到一個准確率,一般來說,經過一次優化的參數會提高模型的准確率。那么不斷重復之前的過程,重復n多次,在不出現過擬合的前提下,我們最終就可以得到一個很優秀的模型。下面上代碼

#這個模塊命名為backward.py
#這里需要注意,import genertateds 和 import forward 導入的是剛才定義好的模塊,這里體現出了python的繼承特點。對python理解不深的同學需要花時間理解一下什么是繼承

import tensorflow as tf
import forward
import os
import genertateds
# 定義神經網絡相關參數
BACTH_SIZE = 100
LEARNING_RATE_BASE=0.1
LEARNING_RATE_DECAY = 0.99
REGULARAZTION_RATE = 0.0001
TRAINING_STEPS = 150
MOVING_AVERAGE_DECAY = 0.99
train_num_examples = 500
# 模型保存的路徑和文件名
MODEL_SAVE_PATH = "LeNet5_model_of_corn1234/"
MODEL_NAME = "LeNet5_model_of_corn1234"
# 定義訓練過程
def train():
    # 定義輸入輸出的placeholder
    x = tf.placeholder(tf.float32, [
        BACTH_SIZE,
        forward.IMAGE_SIZE,
        forward.IMAGE_SIZE,
        forward.NUM_CHANNELS])
    y_ = tf.placeholder(tf.int32, [None], name='y-input')       # label為int類型

    y = forward.inference(x,True,REGULARAZTION_RATE)            # 訓練過程需要使用正則化

    global_step = tf.Variable(0, trainable=False)               # 記錄step、不可訓練的變量

    # 定義滑動平均類
    variable_average = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    variable_average_op = variable_average.apply(tf.trainable_variables())
    # 定義損失函數
    # cross_entropy_mean = tf.reduce_mean(tf.square(y - y_))    # 使用softmax層時的loss函數

    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=y_)
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    loss = cross_entropy_mean + tf.add_n(tf.get_collection('losses'))
    # 定義指數衰減學習率
    learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,
                                               global_step,
                                               train_num_examples/BACTH_SIZE,
                                               LEARNING_RATE_DECAY,
                                               staircase=True)
    # 使用AdamOptimizer優化器、記錄step
    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss,global_step=global_step)
    # 控制計算流程(自己這么理解的...)
    with tf.control_dependencies([train_step, variable_average_op]):
        train_op = tf.no_op(name='train')

    # 初始化TensorFlow持久化類
    saver = tf.train.Saver()
    # 讀取訓練集
    image_batch,label_batch = genertateds.get_batch_record(genertateds.train_record_path,100)

    with tf.Session() as sess:
        # 初始化所有變量
        init_op = tf.global_variables_initializer()
        sess.run(init_op)
        # 斷點檢查
        ckpt = tf.train.get_checkpoint_state(MODEL_SAVE_PATH)
        # 有checkpoint的話繼續上一次的訓練
        if ckpt and ckpt.model_checkpoint_path:
            saver.restore(sess,ckpt.model_checkpoint_path)
        # 創建線程
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess,coord)
        # 開始訓練
        for i in range(TRAINING_STEPS):
            xs, ys = sess.run([image_batch,label_batch])
            _, loss_value, step = sess.run([train_op, loss, global_step],
                                           feed_dict={x:xs,y_:ys})
            # 每20輪保存一次模型
            if i % 20 == 0:
                # 輸出當前的訓練情況
                print("After %d training step(s),loss on training batch is %g." % (step, loss_value))
                # 保存當前模型
                saver.save(
                    sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)
        # 關閉線程
        coord.request_stop()
        coord.join(threads)
def main():
    train()

if __name__ == '__main__':
    main()

 

  1.  BACTH_SIZE = 100
    這個參數是批尺寸。這是在說,每次拿全部數據中的的一部分過來訓練。減少單次的數量,減少時間。如果本身的數據不是很多的話,完全可以直接把全部數據拿過來訓練,不需要BACTH_SIZE 這個參數。其實我本身的數據也不是很多,設置這個參數是為了通用性。需要注意的是BACTH_SIZE的值不能太大,也不能太小。太大內存吃不消,也打不到分批的效果。太小可能會出現由於每次取的batch差異性太大,從而使梯度互相抵消,造成結果不收斂。

  2.  LEARNING_RATE_BASE=0.1 (學習率的基值)
  3.  LEARNING_RATE_DECAY = 0.99 (一般設為接近於1的值)
    這兩個參數可以合在一起說,因為一般而言,在訓練模型的初期,學習率是比較大的,隨着訓練的步數越來越多,再使用很大的學習率會出現無法收斂的現象(步子太大,本來很小的步子就可以達到最優點,可是直接邁過了最優點。所以經驗而談,最開始可以大步慢走,越深入,越接近最優點,就應該使用小步快跑的方式)這兩個參數在后面就是tensorflow中一個調整學習率的API需要的參數。所謂的LEARNING_RATE_BASE=0.1就是初始學習率,LEARNING_RATE_DECAY = 0.99就是學習率的衰減率

  4.  REGULARAZTION_RATE = 0.0001 這個參數可以參看前面我對forward.py的描述

  5.  TRAINING_STEPS = 150 這個很好理解,就是后面訓練的步數

  6.  MOVING_AVERAGE_DECAY = 0.99
  7.  train_num_examples = 500
    這兩個參數我們暫時按下不表,后面馬上會說到

  8.  MODEL_SAVE_PATH = “LeNet5_model_of_corn1234/”
  9.  MODEL_NAME = “LeNet5_model_of_corn1234”
    以上這兩步看着不起眼,但是確實是整個系統中十分重要的一步,也是反向傳播網絡的靈魂。筆者開始也是在這個位置沒有深入理解,導致后面的思維偏離正規。我們需要再次確認,我們現在定義的這個神經網絡是在干什么?我希望看到這篇文章的人都可以先不要往后看。先思考5分鍾。明白神經網絡在干什么,這是對一個新手來說是非常非常重要的。

那么我們下面公布答案,最簡單的話說,就是建模。

不管我們在神經網絡中,把程序寫的多么花哨,多么天花亂墜,神經網絡做的事情也僅僅是建模。為什么在網上看到神經網絡,最后輸出的往往是“准確率”? 難道不是應該對我們的輸入進行分類,最后給出一個明確的結果嗎?為什么會是一個所謂的“准確率”呢?

不是的,在我們這個構建神經網絡的過程中,我們在做的是建模,后面的准確率的本質是,你的測試集在對你構建的模型進行評估,評估模型的准確率。在這里其實並不涉及到具體的分類。

那么具體的分類怎么做呢?答案是,把訓練好的模型保存下來,然后再寫另外一套別的程序(這部分,我自己還沒有具體的理性認識,研究明白了后面會寫新的博客)然后我們就要說道剛剛的兩句代碼了,這兩句的代碼其實就是在聲明一個模型保存的路徑和文件名,便於我們以后加載自己的模型,對新的輸入進行評估。

  10.  def train():這個 模塊整個就是在具體的表現訓練的過程
  11.  x和 y_是兩個占位符。所謂占位符,也是不需要太過糾結的細節問題,簡單說,就是數據不會是上來一次性全都給你的,而是需要在不同的時間點,分批次給你的。也就是說,輸入是在變化的,那么這樣,我們在寫程序的時候就不可以把程序的輸入寫死。定義占位符可以完美的解決問題,數據不斷的被輸入到占位符,程序每次向占位符要數據,然后占位符中的數據更新。

  12.  y = forward.inference(x,True,REGULARAZTION_RATE) 這里用了forward.py模塊中的一個函數,不理解的同學大可以向上翻。
  13.  global_step = tf.Variable(0, trainable=False) 在這里定義了一個“不可訓練的變量” ,這個不可訓練的變量需要人為設置,並將該參數傳入我們的模型。這個參數在后面馬上可以用到。

  14.  variable_average = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
      variable_average_op = variable_average.apply(tf.trainable_variables())
    這兩句代碼定義了一個滑動平均類,什么是滑動平均類呢?簡而言之,它也是用來更新參數的一個函數,而這種更新參數的方式就是滑動平均,這僅僅是一個名字而已。這個函數初始化需要提供一個衰減速率(decay),用於控制模型的更新速度。MOVING_AVERAGE_DECAY就是這里的衰減速度。可以看到,第二不到嗎,等式左邊的參數是可以更新的變量,global不參與這里的更新,所以把它設置為不可更新。

  15.  cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=y_)
      cross_entropy_mean = tf.reduce_mean(cross_entropy)
      loss = cross_entropy_mean + tf.add_n(tf.get_collection(‘losses’))
    這一部分是在定義交叉熵損失函數,交叉熵softmax是在做什么事呢?其實softmax說白了就是在做回歸處理。神經網絡的本來輸出的並不是概率,而是一些經過了復雜的加權,加偏置,非線性處理之后的一個值而已,如何才能讓這樣一個值變成我們理解的概率值?這就需要softmax。softmax=一部分/一個整體,最后輸出的自然就是一個概率值了。(這里不寫公式,想看的話網上到處都有) loss最后就等於經過交叉熵之后對張量取平均

  16.  learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE,global_step,train_num_examples/BACTH_SIZE, LEARNING_RATE_DECAY, staircase=True)
    這里再定義指數衰減學習率,LEARNING_RATE_BASE是學習率基礎值。global_step就是那個手動指定的不能訓練的參數
  17.  train_num_examples/BACTH_SIZE是衰減的速度,即更新學習率的速度(注意區分,不是學習率本身的變化幅度,而是多長時間需要去變一次。即總訓練樣本/每次的feed的batch)LEARNING_RATE_DECAY前面提過,是學習率的衰減率(即學習率本身變化的幅度)staircase=True的意思就是梯度衰減,False為平滑衰減。這里借用一張圖來解釋,何為梯度衰減,何為平滑衰減橫坐標是訓練次數

  18.  train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss,global_step=global_step)
    這里需要說明的是,我們最常使用的優化器有兩種:隨機梯度下降法(SGD) 與 AdamOptimizer,這里我們僅僅需要站在巨人的肩膀上就好。
    隨機梯度下降的意思,就是在不斷的靠近局部(全局)最優點的過程,不斷的求導,求導,來達到導數為0的點。每一步求導,基於當前在隨機數據批量上的損失,一點點的對參數進行調節
    而我們使用AdamOptimizer優化器、記錄step。它屬於梯度下降的一個分支。它利用梯度的一階矩估計和二階矩估計動態調整每個參數的學習率。可以更好地控制學習速度,使得每一次迭代的學習率都會在一個范圍內,不會過大或者過小。

  19.  with tf.control_dependencies([train_step, variable_average_op]):
      train_op = tf.no_op(name=‘train’)
    這里是一個上下文管理器(我自己依舊是按照命名空間來理解的)這部分代碼完全不需要背,可以理解為一個固定流程

  20.  saver = tf.train.Saver()
      image_batch,label_batch = genertateds.get_batch_record(genertateds.train_record_path,100)
    這兩步,對於有基礎的人,應該比較好理解。無非是一個保存模型,一個用來讀取訓練集中的圖片和標簽。

  21.  with tf.Session() as sess:
    這里面在做的事有:

      1.初始化所有變量(必須加,不要問為什么,加就好了。Tensorflow要求你必須加這兩步,否則不work)
      2.斷點檢查 這個意思是你可以在上次訓練的模型基礎上,接着訓練,不需要每次都從頭開始
      3.創建線程 對於線程,進程,攜程不理解的同學需要自行google,這里把這個知識點展開了將不現實(如果需要以后開新的文章介紹) 總而言之,就是你不能把所用東西一次性丟給計算機,而是需要建立一個線程,主線程執行你本應該執行的代碼,子線程可以去分批次的拿數據,子線程拿過來一點,主線程就訓練一點,邊拿邊訓練。(就好像是本來只有一只手干活,這是手是主線程。主線程忙着的時候,子線程就可以去做一些輔助主線程的工作,來配合子線程)
      4.訓練,每10輪保存一次模型(隨時可以中斷,再開啟時會自動從上次中斷的位置來,生成ckpt文件)

step4:輸出測試集的測試結果

#這個模塊我命名為test.py
#導入之前的三個模塊,分別是genertateds,forward,backward
import time
import forward
import backward
import genertateds
import tensorflow as tf

# 等待時間
TEST_INTERVAL_SECS = 5
# 總測試集樣本數量
test_num_examples = 20
def test():
    with tf.Graph().as_default() as g:
        x = tf.placeholder(tf.float32,[test_num_examples,
                                       forward.IMAGE_SIZE,
                                       forward.IMAGE_SIZE,
                                       forward.NUM_CHANNELS])
        y_ = tf.placeholder(tf.int64,[None])
        # 測試過程不需要正則化和dropout
        y = forward.inference(x,False,None)
        # 還原模型中的滑動平均
        variable_average = tf.train.ExponentialMovingAverage(backward.MOVING_AVERAGE_DECAY)
        variable_average_restore = variable_average.variables_to_restore()
        saver = tf.train.Saver(variable_average_restore)
        # 計算准確率
        correct_prediction = tf.equal(tf.argmax(y,1),y_)
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
        image_batch,label_batch = genertateds.get_batch_record(genertateds.test_record_path,20)

        while True:
            with tf.Session() as sess:

                ckpt = tf.train.get_checkpoint_state(backward.MODEL_SAVE_PATH)
                if ckpt and ckpt.model_checkpoint_path:

                    coord = tf.train.Coordinator()
                    threads = tf.train.start_queue_runners(sess, coord)

                    image, label = sess.run([image_batch, label_batch])

                    saver.restore(sess,ckpt.model_checkpoint_path)
                    # 從文件名稱中讀取第幾次訓練
                    global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
                    accuracy_score = sess.run(accuracy,feed_dict={x:image,y_:label})

                    coord.request_stop()
                    coord.join(threads)
                    print("After %s training step(s),test accuray = %g"%(global_step,accuracy_score))
                else:
                    time.sleep(TEST_INTERVAL_SECS)

def main():
    test()
if __name__ == '__main__':
    main()

 

  1.  #等待時間
      TEST_INTERVAL_SECS = 5
  2.  #總測試集樣本數量
    test_num_examples = 20
  3.  這里再聲明兩個變量,聲明等待時間在后面會用到。time.sleep(TEST_INTERVAL_SECS)的意思是讓程序執行到這里,休眠5秒鍾,什么都不做。(這個時候cpu可以去處理其他的任何事,只是當前的程序需要休眠)。  test_num_examples = 20是后面的占位符需要的參數

  4.  def test():
    這個函數里做了什么事呢?
    首先明確一點,模塊名字是test,那么自然,這個函數做的就是驗證模型的好壞。怎么評價模型的還壞?自然是對比着驗證集來看模型准確率
    x和y_是兩個占位符,前面已經提過
    y由於是在測試,沒有正則化處理

  5.  variable_average = tf.train.ExponentialMovingAverage(backward.MOVING_AVERAGE_DECAY)
      variable_average_restore=variable_average.variables_to_restore()
      saver = tf.train.Saver(variable_average_restore)
    這里做的之前也有講過,不再贅述

  6.  tf.argmax(input,axis)用來返回返回每行或者每列最大值的索引
  7.  tf.equal是一個判等函數
    結合起來就是在判斷,最大索引值,和測試集中傳進來的值是否一致

  8.  accuracy= tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
    image_batch,label_batch=genertateds.get_batch_record(genertateds.test_record_path,20)

    這兩句代碼的細節在上個模塊中也提到了,忘記了不妨回看,加深印象

  9.  while True:循環
  不斷的從ckpt中找到訓練的模型,用來提供給后面,進行test

到此 一個完整的使用數據集搭建神經網絡的過程就完成了閉環。


免責聲明!

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



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