【Tensorflow_DL_Note13】TensorFlow中資料的讀取方式(1)

馬衛飛發表於2018-05-08

一 Tensorflow中資料的讀取方式

       在Tensorflow中,程式讀取資料的方式一共有三種:

            [1]供給資料讀取方式(Feeding):在Tensorflow程式執行的每一步,利用Python程式碼來供給/提供資料.

            [2]從檔案讀取資料:在Tensorflow圖的開始,讓一個輸入管線從檔案中讀取相應的資料

            [3]預載入資料:在Tensorflow圖中定義常量或變數來儲存所有的資料,僅適用於資料量比較小的情況

二 供給資料的讀取方式:feed_dict引數

      Tensorflow的【資料供給機制】,允許你在Tensorflow計算圖中,將【資料】注入到任一【張量】中。因此,python運算可以把資料直接設定到Tensorflow的計算圖。

      通過run()或者eval()函式輸入feed_dict引數,可以啟動運算過程。如下面的程式碼所示:

def train(mnist):
    #【1】定義tf.placeholder佔位符,為Tensorflow的【資料供給機制】做準備
    x  = tf.placeholder(tf.float32, [None, mnist_inference.INPUT_NODE],  name='x-input')
    y_ = tf.placeholder(tf.float32, [None, mnist_inference.OUTPUT_NODE], name='y-input')

    regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
    y           = mnist_inference.inference(x, regularizer)
    global_step = tf.Variable(0, trainable=False)
    variable_averages     = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    variables_averages_op = variable_averages.apply(tf.trainable_variables())
    cross_entropy         = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=tf.argmax(y_,1),logits=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,
                                              mnist.train.num_examples / BATCH_SIZE,
                                              LEARNING_RATE_DECAY,staircase=True)
    train_step            = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,
                                              global_step=global_step)
    with tf.control_dependencies([train_step, variables_averages_op]):
        train_op = tf.no_op(name='train')
    saver = tf.train.Saver()
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        for i in range(TRAINING_STEPS):
            #【2】將從批處理操作中讀取到的資料儲存愛xs和ys中
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            #【3】在Tensorflow中,將計算圖中的資料注入到張量x和y_中,根據【Tensorflow的資料供給機制】而得
            _, loss_value, step = sess.run([train_op, loss, global_step], feed_dict={x: xs, y_: ys})
            if i % 1000 == 0:
                print('After {0:d} training step(s), '
                      'loss on training batch is {1:g} '.format(step, loss_value))
                saver.save(sess, os.path.join(MODEL_SAVE_PATH, MODEL_NAME), global_step=global_step)
#========================================================================================================
#模組說明:
#       主程式的入口點
#========================================================================================================
def main(argv=None):
    print('[Info]TensorFlow_Version is',tf.__version__)     #[1]定義處理MNIST資料集的類,這個類在初始化時會自動下載資料
    mnist = input_data.read_data_sets('F:/MnistSet/',one_hot=True)
    train(mnist)                                            #[2]呼叫train函式進行模型的訓練
#========================================================================================================
#模組說明:
#       Tensorflow提供的一個主程式入口,tf.app.run函式將會呼叫上面的main函式
#========================================================================================================
if __name__ == '__main__':
    tf.app.run()

       雖然,我們可以使用【常量:tf.constant】和【變數:tf.Variable】來替換任何一個張量,但最好的做法應該是使用 tf.placeholder佔位符節點。Tensorflow設計tf.placeholder節點的唯一的意圖就是為了提供【資料的供給方法:Feeding】。tf.placeholder節點被宣告的時候是未被初始化的,也不包含資料,如果沒有為它提供資料,則Tensorflow進行運算的時候會產生錯誤,所以,千萬不能忘了給tf.placeholder提供資料.

三 從檔案讀取資料

        一個典型的檔案讀取管線,一般包含下面這些步驟:

        1:檔名列表

        2:可配置的檔名亂序(shuffling)

        3:可配置的最大訓練迭代數(Epoch Limit)

        4:檔名佇列

        5:針對輸入檔案格式的閱讀器

        6:記錄解析器

        7:可配置的前處理器

        8:樣本佇列

3.1 檔名列表,亂序,最大迭代訓練數

        可以使用字串張量,比如["file0","file1"],[("file%d"%i) for in range(2)]或者tf.train.match_filenames_once函式來產生【檔名列表】。將【檔名列表】交給tf.train.string_input_producer函式,tf.train.string_input_producer函式來生成一個先入先出的佇列,檔案閱讀器在需要時,藉助它來讀取資料。

        tf.train.string_input_producer函式提供的【可配置引數】來【設定檔名亂序】和【最大的迭代數】,QueueRunner會為每次迭代(Epoch)將【所有的檔名】加入到【檔案佇列中】,如果shuffle=True的話,會對【檔名】進行【亂序處理】。這一過程是比較均勻的,因此,它可以產生【均衡的檔名佇列】。

       這個QueueRunner的工作執行緒是獨立於檔案閱讀器的執行緒,因此,【亂序】和將檔名推入到【檔名佇列】這些過程不會阻塞【檔案閱讀器】執行。

3.2 檔案格式

      根據你的【檔案格式】,選擇對應的【檔案閱讀器】,然後,將【檔名佇列】提供給【閱讀器】的read的方法。閱讀器的read方法會輸出一個【key】來表徵輸入的檔案和其中的紀錄,同時得到一個【字串標量】,這個【字串標量】可以被一個或多個解析器或者【轉換操作】將其解碼為【張量】並且【構造成樣本】。

3.3 CSV檔案的建立於讀取

      CSV檔案是最常用的一個檔案儲存方式。逗號分隔值(Comma-Sepaeated Values,CSV)檔案以純文字形式儲存表格資料。純文字意味著改檔案是一個字元序列,不包含必須向二進位制數字那樣被解讀的資料。CSV檔案由任意數目的【記錄】組成,記錄間以某種換行符分割;【每條記錄】由【欄位】組成,【欄位間的分隔符】是其他字元或字串,最常見的是逗號和製表符。通常,所有記錄都有完全相同的欄位序列。

3.3.1 CSV檔案的建立

      對於CSV檔案的建立,Python語言有較好的方法對其進行實現,而這裡只需要按需求對其格式進行整理即可。

      再此塊,Tensorflow的CSV檔案讀取主要用作對【所需要載入檔案】的【地址】和【標籤】進行【記錄】。示例程式碼如下所示:

import os                  #[1]os模組是一個Python的系統程式設計的操作模組,可以處理【檔案】和【目錄】這些我們日常需要手動做的操作

path     = 'jpg'
filename = os.listdir(path)#[2]列出【當前目錄】下的【檔案】和【資料夾】
strText  = ""

with open("train_list.csv","w") as fid:
    for a in range(len(filename)):
        strText = path+os.sep+filename[a]+","+filename[a].split('_')[0]+"\n"
        fid.write(strText)
    fid.close()

3.3.2 CSV檔案的讀取

     對應在Tensorflow中使用CSV檔案,則需要使用特殊的CSV讀取。這通常是為了讀取硬碟上圖片檔案而使用的,方便Tensorflow框架在使用時能夠一邊讀取圖片一邊對圖片資料進行處理。這樣做的好處是能夠防止一次性讀入過多的資料造成框架資源被耗盡。

      對於從CSV檔案中讀取資料,需要使用TextLineReader和

#========================================================================================================
#模組說明:
#       [1]tf.train.string_input_producer函式用來生成【一個先入先出的檔案佇列】,【檔案閱讀器】在需要的時候藉助它
#          來讀取資料.
#       [2]tf.TextLineReader()例項化一個【文字行閱讀器類】的【類物件】,這個類中對應的read函式,每一次讀取一行內容,
#          並且使用tf.decode_csv函式來對讀取的每一行內容進行解析
#========================================================================================================
filename_queue = tf.train.string_input_producer(["file0.csv", "file1.csv"])
reader         = tf.TextLineReader()
key, value     = reader.read(filename_queue)

record_defaults = [[1], [1], [1], [1], [1]]
col1, col2, col3, col4, col5 = tf.decode_csv(value, record_defaults=record_defaults)
features = tf.concat(0, [col1, col2, col3, col4])

with tf.Session() as sess:
  # Start populating the filename queue.
  coord   = tf.train.Coordinator()
  threads = tf.train.start_queue_runners(coord=coord)

  for i in range(1200):
    # Retrieve a single instance:
    example, label = sess.run([features, col5])

  coord.request_stop()
  coord.join(threads)
       tf.TextLineReader類中的read函式,每次都會從檔案中讀取一行內容,decode_csv函式會解析這一行的內容並將其轉化為張量列表。如果輸入的引數有缺失,record_defaults引數可以根據張量的型別來設定預設值(這一功能非常方便).在呼叫run函式或者eval去執行read之前,我們必須呼叫tf.train.start_queue_runners來將檔名填充到佇列。否則read操作會被阻塞到檔名佇列中有值為止。

3.4 從二進位制檔案中讀取固定長度

       從二進位制檔案中讀取固定長度的記錄,可以使用tf.FixedLengthRecordReader的tf.decode_raw操作。decode_raw操作可以將【一個字串】轉化為一個uint8的張量.

       舉例來說,CIFAR-10的檔案格式定義是:每條記錄的長度都是固定的,一個位元組的標籤,後面的3072位元組的影象資料。uint8的張量的標準操作就可以從中獲取影象並且根據需要進行重組。

3.5 標準的Tensorflow資料格式

       在Tensorflow中,另一種儲存記錄的方法,可以允許你將任意的資料轉換為Tensorflow所支援的格式,這種方法可以使Tensorflow的資料集更容易與網路應用架構相匹配。這種建議的方法就是使用TFRecords問價,TFRecords檔案包含了tf.train.Example協議記憶體塊(protocol buffer)(協議記憶體塊包含了欄位Features)。你可以寫一段程式碼獲取你的資料,將資料填入到Example協議記憶體塊中,將【協議記憶體塊】序列化為一個【字串】,並且通過tf.python_io.TRecordReader類寫入到TFRecord檔案張。

    從TFRecordes檔案中讀取資料,可以使用tf.TFRecordReader的tf.parse_single_example解析器。這個parse_single_example操作可以將Example協議記憶體塊解析為張量。MNIST的例子就使用了convert_to_records所構建的資料。

3.5.1 TFRecord格式介紹

        TFRecord檔案中的資料都是通過tf.train.Example Protocol Buffer的格式儲存的。以下程式碼給出了tf.train.Example的定義。

message Example
{
    Features features = 1;
};
message Features
{
    map<string,Feature> feature = 1;
};
message Feature
{
    oneof kind
    {
        BytesList bytes_list = 1;
        FloatList float_list = 2;
        Int64List int64_list = 3;
    }
}
       從以上程式碼中可以看出,tf.train.Example的資料結構是比較簡單的。tf.train.Example中包含了一個從【屬性名稱】到【取值】的字典。其中【屬性名稱】為【一個字串】,【屬性的取值】可以為【字串ByteList】、【實數列表FloatList】或者【  整數列表Int64List】。比如將【一張解碼前的影象】存為【一個字串】,【影象所對應的類別編號】存為【整數列表】。

3.5.2 TFRecord樣例程式

       此塊,將給出一個具體的樣例程式來讀寫TFRecord檔案。下面的這個程式架構給出如何將MNIST輸入資料轉化為TFRecord的格式。

#========================================================================================================
#檔案說明:
#       【1】此程式可以將MNSIT資料集中所有的訓練資料儲存到一個TFRecord檔案中。當資料量較大時,也可以將資料寫入多個TFRecord
#            檔案。Tensorflow對從檔案列表讀取資料提供了很好的支援。
#       【2】將資料儲存到TFRecord檔案中
#========================================================================================================
import tensorflow as tf
import numpy      as np
import tensorflow.examples.tutorials.mnist.input_data as input_data
#========================================================================================================
#函式說明:
#       將資料轉換為一個屬性,生成整數型的屬性
#========================================================================================================
def _int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))
#========================================================================================================
#函式說明:
#       將資料轉換為一個屬性,生成字串型的屬性
#========================================================================================================
def _bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

mnist        = input_data.read_data_sets('F:/MnistSet/',one_hot=True)
filename     = 'F:/MnistSet/output.tfrecords'
images       = mnist.train.images
labels       = mnist.train.labels           #[1]訓練資料所對應的正確答案,可以作為一個【屬性】儲存在TFRecord中
pixels       = images.shape[1]              #[2]訓練資料的影象解析度,這裡可以作為Example中的一個屬性
num_examples = mnist.train.num_examples

                                            #[3]建立一個writer來寫TFRecord檔案
writer       = tf.python_io.TFRecordWriter(filename)
for index in range(num_examples):
    image_raw= images[index].tostring()     #[1]將影象矩陣轉化為一個字串
                                            #[2]將一個樣例轉化為Example Protocol Buffer,並將所有的資訊寫入這個資料結構
    example  = tf.train.Example(features=tf.train.Features(feature={
        'pixels':_int64_feature(pixels),
        'label' :_int64_feature(np.argmax(labels[index])),
        'image_raw':_bytes_feature(image_raw)
    }))
    writer.write(example.SerializeToString())
writer.close()
#========================================================================================================
#檔案說明:
#       從TFRecord檔案中讀取資料
#========================================================================================================
import tensorflow as tf
import numpy      as np
import tensorflow.examples.tutorials.mnist.input_data as input_data

reader        = tf.TFRecordReader()   #[1]建立一個reader來讀取TFRecord檔案中的樣例
                                      #[2]建立一個佇列來維護輸入檔案列表
filename_queue      = tf.train.string_input_producer(['F:/MnistSet/output.tfrecords'])
                                      #[3]從檔案中讀出一個樣例,也可以使用read_up_to函式一次性讀取多個樣例
_,serialized_example= reader.read(filename_queue)
                                      #[4]解析讀入的一個樣例,如果需要解析多個樣例,可以使用parse_example函式
features = tf.parse_single_example(
    serialized_example,
    features={
        'image_raw':tf.FixedLenFeature([],tf.string),
        'pixels':tf.FixedLenFeature([],tf.int64),
        'label':tf.FixedLenFeature([],tf.int64)
    })
                                      #[5]tf.decode_raw函式可以將字串解析成影象對應的畫素陣列
images = tf.decode_raw(features['image_raw'],tf.uint8)
labels = tf.cast(features['label'],tf.int32)
pixels = tf.cast(features['pixels'],tf.int32)

sess = tf.Session()
coord= tf.train.Coordinator()         #[6]啟動多執行緒處理輸入資料
threads= tf.train.start_queue_runners(sess=sess,coord=coord)

for i in range(10):                   #[7]每次執行,可以讀取TFRecord檔案中的一個樣例,當所有的樣例
                                      # 讀完之後,會重新從頭讀取
    image,label,pixel = sess.run([images,labels,pixels])
    print(image,label,pixel)



相關文章