[PocketFlow]解決TensorFLow在COCO數據集上訓練掛起無輸出的bug


1. 引言


因項目要求,需要在PocketFlow中添加一套PeleeNet-SSD和COCO的API,具體為在datasets文件夾下添加coco_dataset.py, 在nets下添加peleenet_at_coco.py和peleenet_at_coco_run.py。其中網絡結構和backbone等在師兄把項目交付給我之前已經基本完成,所以我主要的工作就是處理COCO的數據(轉換成tfrecord文件)和簡單更改一些調用的接口。另外PocketFlow中已經包含在VOC上的數據處理,且在檢測任務上,VOC和COCO的數據集標注很接近,唯一的區別就是feature_map:

# VOC
feature_map = {
        'image/encoded': tf.FixedLenFeature([], dtype=tf.string, default_value=''),
        'image/format': tf.FixedLenFeature([], dtype=tf.string, default_value='jpeg'),
        'image/filename': tf.FixedLenFeature((), dtype=tf.string, default_value=''),
        'image/height': tf.FixedLenFeature([1], dtype=tf.int64),
        'image/width': tf.FixedLenFeature([1], dtype=tf.int64),
        'image/channels': tf.FixedLenFeature([1], dtype=tf.int64),
        'image/shape': tf.FixedLenFeature([3], dtype=tf.int64),
        'image/object/bbox/xmin': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/ymin': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/xmax': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/ymax': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/label': tf.VarLenFeature(dtype=tf.int64),
        'image/object/bbox/difficult': tf.VarLenFeature(dtype=tf.int64),
        'image/object/bbox/truncated': tf.VarLenFeature(dtype=tf.int64),
    }

# COCO 
# change filename(string) to image_id(int64) and remove difficult and truncated
feature_map = {
        'image/encoded': tf.FixedLenFeature([], dtype=tf.string, default_value=''),
        'image/format': tf.FixedLenFeature([], dtype=tf.string, default_value='jpeg'),
        'image/image_id': tf.FixedLenFeature([1], dtype=tf.int64), # 
        'image/height': tf.FixedLenFeature([1], dtype=tf.int64),
        'image/width': tf.FixedLenFeature([1], dtype=tf.int64),
        'image/channels': tf.FixedLenFeature([1], dtype=tf.int64),
        'image/shape': tf.FixedLenFeature([3], dtype=tf.int64),
        'image/object/bbox/xmin': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/ymin': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/xmax': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/ymax': tf.VarLenFeature(dtype=tf.float32),
        'image/object/bbox/label': tf.VarLenFeature(dtype=tf.int64),
    }

所以講道理,即使對TensorFLow並不熟,完成這些API的難度也不大,確實也只用了半天就寫完了,不過之后的調試用了半個月

2. 程序掛起無輸出


最開始是訓練時一開始程序就卡住,和掛起差不多,而且也沒有報錯信息,運行截圖如下:

因為TensorFlow的計算圖機制導致在session.run()之前,所有的輸出都是node和tensor的信息,所以個人覺得debug比較困難。尤其是你一上來就整個什么都不輸出,我確實有點懵逼,不知道怎么做。上Google搜了搜TensorFlow 卡住 掛起等關鍵詞,基本上有兩種:

  • TensorFlow計算和數據讀取是異步的,讀不到數據導致進程掛起,在代碼中加上tf.train.start_queue_runners(sess=sess)開啟隊列啥的
  • 計算圖中有新增節點的操作,導致每次推導時內存占用越來越多,最后進程掛起

但比較氣人的是,師兄寫的PeleeNet-SSD和VOC的API就沒有這種情況,而且我隨便改batchsize都可以正常跑起來,所以網絡結構應該沒問題。至於隊列,因為用的是dataset那一套的API,兩者也沒什么相關,加上之后還是沒卵用。不過peleenet那塊沒問題,那大概是讀取數據的API有問題。

於是我一行一行的對比(PyCharm Compare With...)后來新增的腳本和師兄之前寫的在VOC上的那套,檢查了3遍,是真的看不出有什么問題...然后又檢查了將COCO數據集轉換為tfrecord的腳本,同上,數據應該沒錯。不過唯一的收獲就是發現bbox的坐標計算有錯,導致gt有問題,隨后改過來了。后來想想,如果gt是錯的話,訓練的模型准確率再高,預測時還是gg,這bug也挺難發現的

最后求助於師兄,他給我演示了一下怎么用session.run()調試程序,找出程序卡在哪里。他首先判斷因為讀不到數據,所以排除peleenet_at_coco.py和peleenet_at_coco_run.py的問題(xxx_run.py幾個腳本基本都一樣,也不會出什么錯,另外一個主要定義的網絡結構,是讀取數據之后的操作,所以這兩個都不用怎么檢查)。之后着重調試coco_dataset.py,他找到把數據傳入網絡的最后一步,是一個定義在coco_dataset.py的函數parse_fn,然后注釋掉中間的代碼,只原樣返回最初的輸入,發現能用session.run()讀到數據之后再逐步解開剩下代碼的注釋。這樣卡在那一步就能說明哪一塊有問題了,最后發現是卡在了數據預處理preprocess_image()

我感覺有點開竅了,但是自己之后又調試了下,發現加上預處理的部分,數據也能讀出來...最后的結論是不加預處理數據可以讀,加上之后有時可以讀,有時會卡住,甚至有點隨機的感覺。把預處理換成了簡單的resize()統一圖片大小之后,batchsize大點就卡的早點,小的話就晚點。那真的有意思,只能接着看預處理部分了。

3. 報錯:Reduction axis 0 is empty in shape [0, 5028]


預處理主要包括:

  • 隨機扭曲顏色
  • 隨機裁剪(bbox坐標值相應變化)
  • 隨機水平翻轉
  • 縮放到固定尺寸
  • 減去ImageNet訓練集的RGB均值

PocketFlow項目中的數據預處理(數據增強)來自HiKapok/SSD.TensorFlow, 這哥們代碼寫的很好,還有單元測試,屬實良心。另外知乎上也有兩篇相關的文章:Inside TF-Slim(13) preprocessing(圖像增強相關)SSD-TensorFlow 源碼解析,以后認真學習一下。

僅保留縮放操作之后,運行COCO訓練的腳本,程序報以下錯誤(VOC仍無問題):

google之后也沒找到什么有參考價值的答案,只能看預處理的代碼了。因為剛接觸目標檢測,TensorFLow也用的不熟,讀的比較吃力,不過幸好有VOC的單元測試,可以把預處理之后的圖像保存到本地,還能在圖片上直接繪制bbox和label,稍微修改之后就能用在COCO上了,效果如下:

測試時發現,COCO保存若干張圖片后會卡住,而VOC能一直運行。現在終於能確定是數據的問題了,碰巧在COCO保存的圖片中發現有少數圖片沒有bbox框,大概知道問題出在哪里了。看了看之前檢查數據的腳本,媽的居然寫錯了,修改之后,發現了1020張沒有label和bbox標注的圖片,根據image_id在COCO的instances_train2014.json文件的images列表找到了這些圖片,然而在json文件的annotations列表中居然搜不到這些圖片的標注!(如:COCO_train2014_000000542614.jpg)清除tfrecord文件里的這些圖片的相關數據之后,在COCO上也能正常訓練了!

所以調用argmax()時,這些圖像的bbox為[],就會出現shape的維度為0而報錯的情況。而程序卡住也是因為圖像增強時處理到了這些圖片,batchsize越大,就越容易遇到(1020/118230)。之前因為輸出過自己轉換的tfrecord的文件結構,而且隨機抽取的圖片也能正常顯示,所以就一味的相信數據沒有問題,還是沒有考慮全面,所以浪費了很多時間。

判斷tfrecord文件是否含不帶bbox的圖片的代碼

import os
import tensorflow as tf

file_list = tf.gfile.Glob(os.path.join('/DATA/coco/tfrecords', 'val-?????-of-?????'))
i = 0
count = 0
for file in file_list:
    for string_record in tf.python_io.tf_record_iterator(file):
        example = tf.train.Example()
        example.ParseFromString(string_record)

        i += 1
        image_id = example.features.feature['image/image_id'].int64_list.value
        xmax = example.features.feature['image/object/bbox/xmax'].float_list.value
        # xmin = example.features.feature['image/object/bbox/xmin'].float_list.value
        # ymax = example.features.feature['image/object/bbox/ymax'].float_list.value
        # ymin = example.features.feature['image/object/bbox/ymin'].float_list.value

        if xmax == []:
            print(image_id)
            count += 1
    print(file, 'has been checked.')

print(i, 'images have been checked.')
print(count, 'annotations without bbox.')

4. 總結


  1. TensorFlow框架雖然有限制,但調試還是可以調試的,首先要熟悉程序的結構,弄清參數傳遞的過程
  2. 不要糾纏細節,調試的目的是快速且理性的判斷bug的位置
  3. 寫程序時要寫單元測試,調試時會方便許多
  4. TensorFLow, to make your life difficult:)


免責聲明!

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



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