tf.data学習ガイド(超実用、超詳細)

42087 ワード

tf.dataはtensorflow 2でさえ極めて使いやすいデータ読み出しパイプで構築されたAPIである.0では、キューなどの他の方法に完全に代わっています.このAPIを使用してデータパイプを構築し、主に2つのAPIに依存します.
  • tf.data.Dataset
  • tf.data.Iterator

  • tf.data.Datasetは、データの読み込み、前処理、batchやepochなどの通常の操作の調整に使用されますが、読み取りデータはIteratorインタフェースに依存します.両者の関係はPytorchのDatasetとDataLoaderに似ている.なお、Tensorflow 2とtensorflow 1世代の特性が異なるため、本稿では2つの部分に分けてtfについて説明する.dataの2つのバージョンでの使い方.

    共通の基礎知識


    どのバージョンにおいても、1世代と2世代はデータセットを構築する上で同じであり、異なる点はtfにある.data.Iteratorがどのように使うか、つまりデータを読み取る方法が違います.このセクションでは、tfを構築する方法について説明します.data.Datasetモジュール.
  • ファイル名とラベルは既知で、dataで各ファイルのアドレスを保存し、labelで各ファイルに対応するラベルを保存します.dataとlabelはいずれもリストであり、data=[‘xxxx.jpg’,‘qqq.jpg’,...]の形式である.label = [0,2,3,4,1,…]
  • import tensorflow as tf
    import tensorflow.contrib.eager as tfe
    import os
    # tf.enable_eager_execution()
    
    file_path = r'E:\dataset\DAVIS\JPEGImages\480p\bear'
    data= [os.path.join(file_path,i) for i in os.listdir(file_path)]
    label = [0]*len(data)
    print(data)
    print(len(label))
    
    #['E:\\dataset\\DAVIS\\JPEGImages\\480p\\bear\\00000.jpg', 'E:\\dataset\\DAVIS\\JPEGImages\\480p\\bear\\00001.jpg', 
    ......]
    #82
    

    上記のコードにより,訓練データの画像アドレスが得られたが,labelについては,私が例示しただけなので,全0のlabelが生成され,長さは読み取った画像アドレスと一致した.
    まず、最も簡単で最もよく使われる方法を見て、tfを使用します.data.Dataset.from_tensor_slices
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    print(datset)
    #  
    

    これはdatasetを構築したとしても.tf.data.Datasetクラスにはいくつかの関数がよく使用されます.
  • batch()
  • repeat()
  • shuffle()
  • map()
  • zip()/後で紹介しますが、あまり使われていませんが、機能は強いです.

  • batch():パラメータとして整数値を用いてbatchのbatch sizeを記述した.repeat():パラメータは同じ整数値であり、dataset全体が何回(epoch)繰り返される必要があるかを記述し、パラメータがなければ無限に繰り返される.shuffle():名前の通りmap():プリプロセッシング、画像復号などの操作としてよく用いられ、パラメータは関数ハンドルであり、datasetの各要素はこの関数を通って新しいtensorに元の要素の代わりになる.
    次に,Iteratorと組み合わせて以上の4つの手法の使用を理解する.

    Tensorflow 1.xでのパイプの構築


    データセットdatasetタイプのデータインタフェースを取得するには、Iteratorを使用します.Datasetタイプは、反復器を直接生成する関数を提供します.
  • tf.data.Dataset.make_one_shot_iterator()
  • tf.data.Dataset.make_initializable_iterator()
  • make_one_shot_iteratorはユーザ表示で初期化する必要はありませんが、データセットを1回だけ反復(遍歴)できます.
  • make_initializable_iteratorは、ユーザが表示して初期化する必要があり、初期化時に間で定義されたplaceholderに送ることができ、より柔軟な使用を達成することができる.

  • 反復器にget_がありますnextメソッドは、データのtensorsを取得します.このtensorsの意味は、データセットの構築に使用されるfrom_tensors_sliceのパラメータ形式.
    2つの例を見てみましょう.
    import tensorflow as tf
    import tensorflow.contrib.eager as tfe
    import os
    # tf.enable_eager_execution()  #       
    
    file_path = r'E:\dataset\DAVIS\JPEGImages\480p\bear'
    data = [os.path.join(file_path,i) for i in os.listdir(file_path)]
    label = [0]*len(data)
    # print(data)
    # print(len(label))
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    # print(dataset)
    iterator = dataset.make_one_shot_iterator()
    img_name, label = iterator.get_next()
    
    with tf.Session() as sess:
        while 1:
            try:
                name, num = sess.run([img_name,label])
                print(name)
                assert num == 0, "fail to read label"
            except tf.errors.OutOfRangeError:
                print("iterator done")
                break
    
    # b'E:\\dataset\\DAVIS\\JPEGImages\\480p\\bear\\00000.jpg'
    # b'E:\\dataset\\DAVIS\\JPEGImages\\480p\\bear\\00001.jpg'
    # b'E:\\dataset\\DAVIS\\JPEGImages\\480p\\bear\\00002.jpg'
    # .....
    # iterator done
    

    私たちがdatasetに送った内容が画像の経路情報であるため、出力されたのは画像名であることがわかりましたが、今は画像データを読み取りたいと思っています.これはmapメソッドを使用する必要があります.まず、画像を読み込む関数をカスタマイズします.
    def _parse_function(filename, label):
      image_string = tf.read_file(filename)
      image_decoded = tf.image.decode_jpeg(image_string,channels=3)
      image_resized = tf.image.resize_images(image_decoded, [224, 224])
      return image_resized, label
    
    file_path = r'E:\dataset\DAVIS\JPEGImages\480p\bear'
    data = [os.path.join(file_path,i) for i in os.listdir(file_path)]
    label = [0]*len(data)
    # print(data)
    # print(len(label))
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    dataset = dataset.map(_parse_function)   #      
    # print(dataset)
    iterator = dataset.make_one_shot_iterator()
    img, label = iterator.get_next()
    
    with tf.Session() as sess:
        while 1:
            try:
                image, num = sess.run([img,label])
                print(image.shape)
                assert num == 0, "fail to read label"
            except tf.errors.OutOfRangeError:
                print("iterator done")
                break
    
    
    # ....
    # (224, 224, 3)
    # (224, 224, 3)
    # (224, 224, 3)
    # iterator done
    

    次にdatasetのbatchメソッドを用いて,batchを1回ずつ読み出す.
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    dataset = dataset.map(_parse_function)
    dataset = dataset.batch(5)
    
    #     ,   
    (5, 224, 224, 3)
    (5, 224, 224, 3)
    (2, 224, 224, 3)
    iterator done
      822        batch。
    

    次にrepeatを使用してデータセットを2回繰り返します.次に、合計をグローバル変数countで記録し、82*2=164に等しいかどうかを確認します.
    file_path = r'E:\dataset\DAVIS\JPEGImages\480p\bear'
    data = [os.path.join(file_path,i) for i in os.listdir(file_path)]
    label = [0]*len(data)
    # print(data)
    # print(len(label))
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    dataset = dataset.map(_parse_function)
    dataset = dataset.batch(1)   #   1,    
    dataset = dataset.repeat(2)   #        
    # print(dataset)
    iterator = dataset.make_one_shot_iterator()
    img, label = iterator.get_next()
    
    count = 0
    with tf.Session() as sess:
        while 1:
            try:
                image, num = sess.run([img,label])
                print(image.shape)
                count += 1
            except tf.errors.OutOfRangeError:
                print("iterator done")
                print("count is ",count)  #   conut     
                break
    
    # ....
    (1, 224, 224, 3)
    (1, 224, 224, 3)
    iterator done
    count is  164
    

    OK、確かに私たちが考えているように.
    またtf.data.Dataset.make_initializable_iterator()この方法は初期化可能な反復器を返します.ユーザーはsessを使用する必要があります.run(iterator.initializer)は初期化を表示します.
    import tensorflow as tf
    import tensorflow.contrib.eager as tfe
    import os
    # tf.enable_eager_execution()
    from read import _parse_function as _parse_function
    file_path = r'E:\dataset\DAVIS\JPEGImages\480p\bear'
    data = [os.path.join(file_path,i) for i in os.listdir(file_path)]
    label = [0]*len(data)
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    dataset = dataset.map(_parse_function)
    batch_size = tf.placeholder(tf.int64,shape=[])
    dataset = dataset.batch(batch_size)
    dataset = dataset.repeat(1)
    iterator = dataset.make_initializable_iterator()
    img, label = iterator.get_next()
    
    with tf.Session() as sess:
        sess.run(iterator.initializer, feed_dict={
         batch_size: 20})
        while 1:
            try:
                image, num = sess.run([img,label])
                print(image.shape)
            except tf.errors.OutOfRangeError:
                print("second iterator done")
                break
        sess.run(iterator.initializer, feed_dict={
         batch_size: 40})   #   batchsize    
        while 1:
            try:
                image, num = sess.run([img,label])
                print(image.shape)
            except tf.errors.OutOfRangeError:
                print("first iterator done")
                break
    #    
    # (20, 224, 224, 3)
    (20, 224, 224, 3)
    (20, 224, 224, 3)
    (20, 224, 224, 3)
    (2, 224, 224, 3)
    first iterator done
    (40, 224, 224, 3)
    (40, 224, 224, 3)
    (2, 224, 224, 3)
    second iterator done
    

    反復器を初期化したとき、batchsizeが20であったことがわかり、最初に反復したbatchは20サンプルで、全部で82個が故障していないことがわかります.2回目に反復器を初期化するとき、batchsizeは40に設定されているので、2回目の反復batchは40サンプルで、全部で82サンプルで、問題はありません.
    Note:tf.Dataには、反復器とfeeding反復器を再初期化することもできます.基本的には以上の2つの反復器に機能的に代わることができるので、紹介しません.詳細についてはhereを参照してください
    また、反復器の状態は保存できます.例えば、訓練が半分になり、サンプルがどこに読み込まれたのか、この状態を保存し、次はインポートモデルを実行し、その後、前回の状態に続いて実行します.
     saveable_iter  = tf.data.experimental.make_saveable_from_iterator(iterator)
     tf.compat.v1.add_to_collections(tf.GraphKeys.SAVEABLE_OBJECTS,saveable_iter)
     saver = tf.train.Saver()
     with tf.Session() as sess:
     	.....
     	saver.save(path_to_checkpoint)
     
     # restore the state of iterator
     with tf.Session() as sess:
     	saver.restore(sess,path_to_checkpoint)
    

    Tensorflow 2.0でパイプを作成


    TF2.0はPyTrochと同様のeagerモードをサポートするので,このモードではsessionとplacehoderは破棄される.この場合、取得データの読み出しインタフェースは、tf 1のみとなる.xは少し違いますが、もっと簡単で使いやすいです.
    私たちはdatasetを構築した後、python内蔵関数iterだけでいいです.例:
    import tensorflow as tf
    import tensorflow.contrib.eager as tfe
    import os
    tf.enable_eager_execution()   #   eager  ,    1.13.1,      2.0,     
    from read import _parse_function as _parse_function
    
    file_path = r'E:\dataset\DAVIS\JPEGImages\480p\bear'
    data = [os.path.join(file_path,i) for i in os.listdir(file_path)]
    label = [0]*len(data)
    dataset = tf.data.Dataset.from_tensor_slices((data,label))
    dataset = dataset.map(_parse_function)
    dataset = dataset.batch(40)
    dataset = dataset.repeat(1)
    iterator = iter(dataset)
    
    while 1:
        try:
            image, _ = next(iterator)
            print(image.shape)
        except StopIteration:   # python            
            print("iterator done")
            break;
            
    #    
    (40, 224, 224, 3)
    (40, 224, 224, 3)
    (2, 224, 224, 3)
    iterator done
    

    import tensorflowも使えます.contrib.eager as tfeがサポートするIteratorクラス生成反復器
    ....   #      
    iterator = tfe.Iterator(dataset)
    
    for img, _ in iterator:
        print(img.shape)
    
    # (40, 224, 224, 3)
    (40, 224, 224, 3)
    (2, 224, 224, 3)
    

    リファレンスリンク


    [0.2]Tensorflow踏坑記の頭痛のtf.Data TensorFlowの新しいデータ読み出し方式:Dataset API入門チュートリアルGitHubの前の例cs 230カリキュラムTensorFlow tf.dataインポートデータ(tf.data公式チュートリアル)