Tensorflow使用筆記(2): 如何構(gòu)建TFRecords并進(jìn)行Mini Batch訓(xùn)練

引言

  1. 前段時(shí)間在做一門課程的期末大作業(yè)的時(shí)候,用到了TensorFlow,構(gòu)建了含有兩層卷積層的神經(jīng)網(wǎng)絡(luò)去做 交通標(biāo)志的識(shí)別,一開始使用 24x24 的圖像作為輸入(把數(shù)據(jù)集的圖像都resize為24x24)后來感覺應(yīng)該設(shè)計(jì)大一點(diǎn)會(huì)可靠一點(diǎn),那就想把輸入的圖像都改為 64x64 的大小,相應(yīng)修改了網(wǎng)絡(luò)一些參數(shù)后,run的時(shí)候發(fā)現(xiàn)出問題了,我還以為是代碼沒有改好,仔細(xì)看一下提示信息:run out of memory 。原來是是內(nèi)存不足

  2. 那在了解到情況后,就上網(wǎng)找方法,于是乎,找到了個(gè)普遍的解決方法:使用mini_batch方法訓(xùn)練,好,那下面就是一些整理網(wǎng)上的資料了

什么是Mini_Batch方法

點(diǎn)這里可以看比較簡(jiǎn)略的介紹。

先簡(jiǎn)單介紹一下這三個(gè)常見的名詞:batch_size ,iteration,epoch

  1. batchsize:批大小。在深度學(xué)習(xí)中,一般采用SGD訓(xùn)練,即每次訓(xùn)練在訓(xùn)練集中取batchsize個(gè)樣本訓(xùn)練;
  2. iteration:1個(gè)iteration等于使用batchsize個(gè)樣本訓(xùn)練一次;
  3. epoch:1個(gè)epoch等于使用訓(xùn)練集中的全部樣本訓(xùn)練一次

總體來說Mini_Batch就是介于SGD(隨機(jī)梯度下降)和BGD(批梯度下降)之間的一種比較不錯(cuò)的方法,batch_size選擇合適了,既能提高訓(xùn)練速度,又能求得一個(gè)逼近全局最優(yōu)解的結(jié)果(但是在實(shí)際運(yùn)用中應(yīng)該要多次修改才能獲得合適的size),點(diǎn)這里可以看一些關(guān)于怎么選擇好batch_size的建議


怎么實(shí)現(xiàn)?

那問題來了,怎么在tensorflow中實(shí)現(xiàn)Mini_batch訓(xùn)練呢?一開始我的數(shù)據(jù)集是使用pickle存在硬盤上的

with open( 'images.pkl') as f:
    # training_images 就是存儲(chǔ)了很多的使用opencv讀取的圖像,它們的類型都是np.array
    training_images = pickle.load(f)

那如果是按照順序讀取,每次從取training_images的一部分,然后再取它的下一部分,似乎很容易實(shí)現(xiàn),但是如果每次喂進(jìn)去的樣本沒有隨機(jī)性的話,那似乎便失去了Mini Batch的意義,如果想實(shí)現(xiàn)隨機(jī)取的話,似乎不太容易,那我們可以利用tensorflow,制作適合Tensorflow的數(shù)據(jù)集TFRecords


簡(jiǎn)單制作TFRecords

使用TFRecord有什么好處,能把它轉(zhuǎn)成二進(jìn)制,tensorflow對(duì)它會(huì)加速。處理起來更快;可以配合tensorflow里的函數(shù),配合使用起來更方便
直接上代碼(參考網(wǎng)上)

with open('Training_images64x64.pkl') as f1,open('Training_labels.pkl') as f2:
    print "loading...,please waitting for few seconds"  
    images = pickle.load(f1)  # 數(shù)據(jù)
    labels = pickle.load(f2)  # 樣本標(biāo)簽

# 構(gòu)建一個(gè)writer,待會(huì)用來把TFRecords寫入硬盤的
writer = tf.python_io.TFRecordWriter("train.tfrecords")  

num = len(labels)
print "the total account of the samples:",num

for i in range(num):
    # build tf record
    label = eval(labels[i])  # 寫入的標(biāo)簽是要數(shù)字
    img = images[i]
    img_raw = img.tobytes()  # TFRecord 要把它轉(zhuǎn)化成字節(jié)
    # 重點(diǎn),開始映射了
    example = tf.train.Example(features=tf.train.Features(feature={
            "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label])),
            'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw]))
        }))
    # 寫入到硬盤
    writer.write(example.SerializeToString())  # Serialize To String
print "all Done"
# 結(jié)束,close。跟文件操作挺類似的
writer.close()

讀取并使用TFRecords

首先先定義一個(gè)讀取 TFRecords的函數(shù)吧

# 使用隊(duì)列讀取數(shù)據(jù),這個(gè)隊(duì)列在tensorflow里面有特別的含義
# 把數(shù)據(jù)放在隊(duì)列里有很多好處,可以完成訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)的解耦
def read_and_decode(filename):
    # 根據(jù)文件名生成一個(gè)隊(duì)列
    filename_queue = tf.train.string_input_producer([filename])
    # 定義reader,跟之前定義writer是對(duì)應(yīng)的
    reader = tf.TFRecordReader()
    _, 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),
                                       })

    img = tf.decode_raw(features['img_raw'], tf.uint8)
    # 下面這個(gè)reshape 可以根據(jù)自己的需要來決定要不要重新定義大小
    # img = tf.reshape(img, [224, 224, 3])
    
    # 轉(zhuǎn)化為tensorflow 的 float32,
    img = tf.cast(img, tf.float32) * (1. / 255) - 0.5
    label = tf.cast(features['label'], tf.int32)

    return img, label

接下來是怎么使用的問題了。假設(shè)已經(jīng)定義好計(jì)算圖了

假設(shè)計(jì)算圖是已經(jīng)定義好的 為 graph

# 先度硬盤上讀取出來,利用上面定義好的函數(shù)
img, label = read_and_decode("train.tfrecords") 
sess = tf.Session(graph=graph)  # 定義回話
init.run(session=sess)  # 初始化
# 啟動(dòng)隊(duì)列線程
threads = tf.train.start_queue_runners(sess=sess) 
sess.run(fetches=init,feed_dict={images_ph:training_images, labels_ph:training_labels})

# !!!劃重點(diǎn)了!!!
# 使用shuffle_batch,tensorflow可以有效地幫我們隨機(jī)從訓(xùn)練數(shù)據(jù)中隨機(jī)抽出batch_size個(gè)數(shù)據(jù)樣本
image_batch ,label_batch = tf.train.shuffle_batch([img, label] ,batch_size=30) 
# 上面這個(gè)函數(shù),還有capacity等其他參數(shù),作用還未明白
# image_train ,label_train = tf.train.shuffle_batch([img, label] ,batch_size=30, capacity=2000,min_after_dequeue=1000)

iteration_times = 2000 # 假定迭代次數(shù)為200

# 開始訓(xùn)練

for i in range(0,iteration_times):
    
    # 每次都要run一次,否則取不到說好的batch的數(shù)據(jù)喲
    sess.run([image_train,label_train])  
    
    # 接下來就可以把image_train,label_train
    # 喂給你的訓(xùn)練節(jié)點(diǎn)了
    _, loss_value =sess.run( 
        [train_op,loss],
        feed_dict={images_ph: image_train, labels_ph:label_train}
    )
    # 其他代碼
    

訓(xùn)練完之后別忘記保存模型參數(shù)喲,具體的介紹可以看我的另一篇文章

關(guān)于隊(duì)列的詳細(xì)介紹,可以看這里,還挺復(fù)雜的
參考代碼


小結(jié)一下

我在自己的問題上,在實(shí)際訓(xùn)練的時(shí)候,使用實(shí)驗(yàn)室的帶GPU的服務(wù)器,訓(xùn)練的結(jié)果有些慢,損失loss震蕩比較厲害,一直沒有收斂,可能還是網(wǎng)絡(luò)設(shè)計(jì)的有問題吧。
后來在提交作業(yè)的時(shí)候,選擇了32x32的輸入圖像(梯度下降法,沒有使用分批訓(xùn)練),反而在測(cè)試集上的準(zhǔn)確率比較高,對(duì)于Mini_batch的訓(xùn)練方法。batch_size的選擇還是缺少指導(dǎo)方針呀。大過小都不好,實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)呀,此次作業(yè)收獲良多。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容