Tensorflow數(shù)據(jù)讀取

讀取機(jī)制

Tensorflow中數(shù)據(jù)讀取機(jī)制可見(jiàn)下圖

關(guān)于這張圖旧巾,這篇文章已經(jīng)介紹的非常詳細(xì)耸序,簡(jiǎn)而言之,Tensorflow為了不讓數(shù)據(jù)讀取成為代碼的事件瓶頸鲁猩,用了兩個(gè)隊(duì)列來(lái)進(jìn)行文件的讀瓤补帧:

  1. 文件隊(duì)列,通過(guò)tf.train.string_input_producer()函數(shù)來(lái)創(chuàng)建廓握,文件名隊(duì)列不包含文件的具體內(nèi)容搅窿,只是在隊(duì)列中記錄所有的文件名,所以可以在這個(gè)函數(shù)中對(duì)文件設(shè)置多個(gè)epoch隙券,并對(duì)其進(jìn)行shuffle男应。這個(gè)函數(shù)只是創(chuàng)建一個(gè)文件隊(duì)列,并指定入隊(duì)的操作由幾個(gè)線程同時(shí)完成娱仔。真正的讀取文件名內(nèi)容是從執(zhí)行了tf.train.start_queue_runners()開(kāi)始的沐飘,start_queue_runners返回一個(gè)op,一旦執(zhí)行這個(gè)op拟枚,文件名隊(duì)列就開(kāi)始被填充了薪铜。
  2. 內(nèi)存隊(duì)列众弓,這個(gè)隊(duì)列不需要用戶手動(dòng)創(chuàng)建恩溅,有了文件名隊(duì)列后,start_queue_runners之后谓娃,Tensorflow會(huì)自己維護(hù)內(nèi)存隊(duì)列并保證用戶時(shí)時(shí)有數(shù)據(jù)可讀脚乡。
    典型的代碼如下:
import tensorflow as tf 

# 新建一個(gè)Session
with tf.Session() as sess:
    # 我們要讀三幅圖片A.jpg, B.jpg, C.jpg
    filename = ['A.jpg', 'B.jpg', 'C.jpg']
    # string_input_producer會(huì)產(chǎn)生一個(gè)文件名隊(duì)列
    filename_queue = tf.train.string_input_producer(filename, shuffle=False, num_epochs=5)
    # reader從文件名隊(duì)列中讀數(shù)據(jù)。對(duì)應(yīng)的方法是reader.read
    reader = tf.WholeFileReader()
    key, value = reader.read(filename_queue)
    # tf.train.string_input_producer定義了一個(gè)epoch變量,要對(duì)它進(jìn)行初始化
    tf.local_variables_initializer().run()
    # 使用start_queue_runners之后奶稠,才會(huì)開(kāi)始填充隊(duì)列
    threads = tf.train.start_queue_runners(sess=sess)
    i = 0
    while True:
        i += 1
        # 獲取圖片數(shù)據(jù)并保存
        image_data = sess.run(value)
        with open('read/test_%d.jpg' % i, 'wb') as f:
            f.write(image_data)

注意string_input_producer()中的shuffle是文件級(jí)別的俯艰,如果要讀取的文件是TFRecord文件,一個(gè)文件中就包含幾千甚至更多條數(shù)據(jù)锌订,那么這里的shuffle和我們平時(shí)訓(xùn)練數(shù)據(jù)時(shí)說(shuō)的shuffle還是不一樣的竹握。

TODO: 把讀取出的數(shù)據(jù)組成batch的代碼

slim數(shù)據(jù)讀取接口

用slim讀取數(shù)據(jù)分為以下幾步:

  1. 給出數(shù)據(jù)來(lái)源的文件名并據(jù)此建立slim.Dataset,邏輯上Dataset中是含有所有數(shù)據(jù)的辆飘,當(dāng)然物理上并非如此啦辐。
  2. 根據(jù)slim.Dataset建立一個(gè)DatasetDataProvider,這個(gè)class提供接口可以讓你從Dataset中一條一條的去取數(shù)據(jù)
  3. 通過(guò)DatasetDataProvider的get接口拿到獲取數(shù)據(jù)的op蜈项,并對(duì)數(shù)據(jù)進(jìn)行必要的預(yù)處理(如有)
  4. 利用從provider中g(shù)et到的數(shù)據(jù)建立batch芹关,此處可以對(duì)數(shù)據(jù)進(jìn)行shuffle,確定batch_size等等
  5. 利用分好的batch建立一個(gè)prefetch_queue
  6. prefetch_queue中有一個(gè)dequeue的op紧卒,沒(méi)執(zhí)行一次dequeue則返回一個(gè)batch的數(shù)據(jù)侥衬。

下面我們通過(guò)代碼來(lái)一一介紹具體如何使用。
1.建立slim.Dataset
根據(jù)官方文檔跑芳,slim.Dataset包含data_sources轴总,reader,decoder博个,num_samples肘习,descriptions五個(gè)部分,其中data_sources是一系列文件名坡倔,代表組成數(shù)據(jù)集全體的文件名漂佩;reader,針對(duì)文件的類(lèi)型罪塔,選擇合適的reader投蝉;decoder,一個(gè)解釋器征堪,用于將文件中存儲(chǔ)的數(shù)據(jù)轉(zhuǎn)換為T(mén)ensor類(lèi)型瘩缆;num_samples,指明數(shù)據(jù)集中一共含有多少條數(shù)據(jù)佃蚜;descriptions可以添加一些對(duì)于數(shù)據(jù)的額外備注和說(shuō)明庸娱,非必須。下面是一段典型的建立Dataset的代碼谐算,假設(shè)我們的數(shù)據(jù)由多個(gè)TFRecord文件組成熟尉,每個(gè)TFRecord存儲(chǔ)若干數(shù)據(jù),在TFRecord中洲脂,每條數(shù)據(jù)都是一個(gè)TFExample類(lèi)型:

def get_split(split_name, dataset_dir, file_pattern, num_samples, reader=None):
    dataset_dir = util.io.get_absolute_path(dataset_dir)
    
    if util.str.contains(file_pattern, '%'):
        # 處理有多個(gè)文件的情況斤儿,file_pattern是文件名list
        file_pattern = util.io.join_path(dataset_dir, file_pattern % split_name)
    else:
        file_pattern = util.io.join_path(dataset_dir, file_pattern)
    # Allowing None in the signature so that dataset_factory can use the default.
    if reader is None:
        reader = tf.TFRecordReader
    keys_to_features = {
        'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
        'image/format': tf.FixedLenFeature((), tf.string, default_value='jpeg'),
        'image/filename': tf.FixedLenFeature((), tf.string, default_value=''),
        'image/shape': tf.FixedLenFeature([3], tf.int64),
        'image/object/bbox/label': int64_feature(labels),
    }
    items_to_handlers = {
        'image': slim.tfexample_decoder.Image('image/encoded', 'image/format'),
        'shape': slim.tfexample_decoder.Tensor('image/shape'),
        'filename': slim.tfexample_decoder.Tensor('image/filename'),
        'object/label': slim.tfexample_decoder.Tensor('image/object/bbox/label')
    }
    # slim.Decoder可以給兩個(gè)參數(shù),兩個(gè)都是dict,第一個(gè)參數(shù)指定要如何解析每個(gè)Example往果,第二個(gè)參數(shù)可以把讀取出的數(shù)據(jù)進(jìn)一步簡(jiǎn)單處理或者組合成需要的數(shù)據(jù)
    decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers)

    items_to_descriptions = {
        'image': 'A color image of varying height and width.',
        'shape': 'Shape of the image',
        'object/label': 'A list of labels, one per each object.',
    }
    ## 建立并返回一個(gè)Dataset
    return slim.dataset.Dataset(
            data_sources=file_pattern,
            reader=reader,
            decoder=decoder,
            num_samples=num_samples,
            items_to_descriptions=items_to_descriptions,
            num_classes=2,
            labels_to_names=labels_to_names)

2. 建立DatasetDataProvider

# 下面用到的dataset就是我們上面建立的slim.dataset.Dataset疆液,num_readers是指定線程數(shù)目,即如果后續(xù)
# 要多線程讀數(shù)據(jù)的話陕贮,最多可以有5個(gè)的get可以被同時(shí)調(diào)用來(lái)填充數(shù)據(jù)堕油。capacity是provider自己維護(hù)的
# 隊(duì)列的大小,get操作相當(dāng)于dequeue操作肮之,enqueue操作由provider自己完成
provider = slim.dataset_data_provider.DatasetDataProvider(dataset, num_readers=5, \
                common_queue_capacity=10, common_queue_min=1, shuffle=True)
# 每調(diào)用一次get馍迄,得到一條數(shù)據(jù)。同樣局骤,這里的get得到的依然是一個(gè)Tensor的op攀圈,不是一個(gè)實(shí)實(shí)在在的張量
[image, shape, label] = provider.get(['image', 'shape', 'object/label'])

3. 必要的預(yù)處理

# 此處可以做一些預(yù)處理,數(shù)據(jù)就一條峦甩,沒(méi)有第一維的batch維度
[image, shape, label] = preprocess(image, shape, label)

4. 建立batch
根據(jù)官方文檔赘来,train.batch是維護(hù)有自己的隊(duì)列的,所以它也可以開(kāi)多個(gè)線程從provider中獲取數(shù)據(jù)凯傲,num_threads就是這個(gè)意思犬辰,capacity自然就是隊(duì)列大小。

# 官方還有tf.train.shuffle_batch等接口冰单,提供shuffle數(shù)據(jù)等功能
b_image, b_label = tf.train.batch([image, label], batch_size=32, num_threads=4, capacity=200)

5. 建立prefetch_queue

batch_queue = slim.prefetch_queue.prefetch_queue([b_image, b_label], capacity = 20) 

其實(shí)這個(gè)地方我有一個(gè)不解幌缝,既然第四步已經(jīng)將數(shù)據(jù)都分好的batch放進(jìn)了隊(duì)列,理論上只要執(zhí)行batch返回的的op就可以直接得到數(shù)據(jù)诫欠,為了還要再包一層隊(duì)列涵卵,產(chǎn)生一個(gè)batch_queue呢?根據(jù)官方的解釋荒叼,prefetch_queue的作用是把batch后的數(shù)據(jù)聚合到一起(assemble)轿偎,保證用戶在讀取數(shù)據(jù)時(shí)不需要再花時(shí)間assemble。
看來(lái)Tensorflow早就想到了這個(gè)被廓,并且外面再包一層也是有道理的坏晦,但是我本人理解batch后的數(shù)據(jù)就是assemble之后的,不知道它的batch操作是怎么樣的等研究過(guò)代碼再說(shuō)吧嫁乘。(TODO)

6. 運(yùn)行dequeue的op獲取數(shù)據(jù)

b_images, b_labels = batch_queue.dequeue()
with tf.Sesstion() as sess:
    images, labels = sess.run(images, labels)
    print(images)
    print(labels)

tf.data.Dataset接口

slim提供的數(shù)據(jù)讀取接口其實(shí)也不夠簡(jiǎn)潔昆婿,看看生一部分的六個(gè)步驟就知道過(guò)程還有有些繁瑣的,想要熟練運(yùn)用蜓斧,不了解一些Tensorflow的實(shí)現(xiàn)是有點(diǎn)難的仓蛆。但是tf.data.Dataset則不然,他隱藏了所有Tensorflow處理數(shù)據(jù)流的細(xì)節(jié)法精,用戶只需要幾步簡(jiǎn)單的操作就可以輕松讀到數(shù)據(jù)多律,這使得數(shù)據(jù)讀取更加容易上手且寫(xiě)出的代碼更加簡(jiǎn)潔、易懂搂蜓。tf.data.Dataset的介紹將會(huì)在另外一篇文章中講解狼荞。

參考文獻(xiàn)

  1. 十圖詳解Tensorflow數(shù)據(jù)讀取機(jī)制——知乎
  2. Tensorflow數(shù)據(jù)處理——CSDN
  3. Tensorflow中的README文檔
  4. Tensorflow官網(wǎng)
  5. PixelLink源碼
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市帮碰,隨后出現(xiàn)的幾起案子相味,更是在濱河造成了極大的恐慌,老刑警劉巖殉挽,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丰涉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡斯碌,警方通過(guò)查閱死者的電腦和手機(jī)一死,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)傻唾,“玉大人投慈,你說(shuō)我怎么就攤上這事」诮荆” “怎么了伪煤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)凛辣。 經(jīng)常有香客問(wèn)我抱既,道長(zhǎng),這世上最難降的妖魔是什么扁誓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任防泵,我火速辦了婚禮,結(jié)果婚禮上蝗敢,老公的妹妹穿的比我還像新娘择克。我一直安慰自己,他們只是感情好前普,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布肚邢。 她就那樣靜靜地躺著,像睡著了一般拭卿。 火紅的嫁衣襯著肌膚如雪骡湖。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天峻厚,我揣著相機(jī)與錄音响蕴,去河邊找鬼。 笑死惠桃,一個(gè)胖子當(dāng)著我的面吹牛浦夷,可吹牛的內(nèi)容都是我干的辖试。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼劈狐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼罐孝!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肥缔,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤莲兢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后续膳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體改艇,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年坟岔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谒兄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡社付,死狀恐怖舵变,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瘦穆,我是刑警寧澤纪隙,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站扛或,受9級(jí)特大地震影響绵咱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熙兔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一悲伶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧住涉,春花似錦麸锉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至媳握,卻和暖如春碱屁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛾找。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工娩脾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人打毛。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓柿赊,卻偏偏與公主長(zhǎng)得像俩功,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碰声,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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