TensorFlowのTFRecordを用いた「学習」をDataset API で行う

Pocket

機械学習の「学習」で用いるデータは、バッチと呼ばれる単位で処理することが必要になります。
以前に、『TensorFlow推奨フォーマット「TFRecord」の作成と読み込み方法』という記事で紹介した方法では、TFRecordファイルの作成と読み込みを行いましたが、データを1レコードずつ順番に取り出すことしかできていません。

 

そこで、今回は、TensorFlowで推奨されている Dataset API を利用して、マルチスレッド(並列処理)で、バッチ単位にデータを取得する手法で、(機械学習の)「学習」を行っていきます。

※データの扱い方について、以前の記事では、キューを扱うことを明示的に記載したコーディング(本記事では、以後「キューベースパイプライン」と呼ぶこととします)となっていましたが、TensorFlowバージョン1.4(2017年11月リリース)から、Dataset APIを利用することが推奨されています。

参考:TensorFlow API Documentation「Threading and Queues」

※この記事では、(機械学習の)「学習」を以下の条件で行うものとします。

・入力データとなる、モデルの学習に用いる「学習用データ」と、学習済みモデルを評価するのに用いる「評価用データ」を、別に分けて用意します。

・同じモデルを利用して、上記2つの入力データを切り替えて、「学習」と「評価」を実行します。

「キューベースパイプライン」の場合、入力データの切り替えがなかなか難しく、困ったのですが、今回のDataset APIではその切り替え簡単に行えます。

TFRecordの学習をDataset APIで行う流れ

  1. MNISTデータ(10000件)から、学習用データと評価用データをTFRecord形式で準備します。
    (※TFRecordの作成方法は、こちらの記事に記載しているのでご覧ください

    1. 学習用データ: MNISTの先頭から7000件目までのデータ
    2. 評価用データ: MNISTの7001件目から最後までのデータ
  2. 上記1のデータを用いて、学習と評価を実施します。
    (※後述のサンプルコードは、この内容のみを含んでいます)

    1. 入力データの解析処理
      • 学習用データのTFRecordファイルを解析し、読み込める状態にします。
    2. モデルの作成
      • TFRecordファイルを入力にしたモデルを構築します。
      • 入力ファイルは、100レコードを1バッチサイズとして扱います。
    3. 学習と評価
      1.  このモデルでの学習は、1000ステップ行います。
        • 入力ファイル「学習用データ」の取り扱いイメージは、次の通り。
          1. ファイルの先頭レコードから、ステップ毎にバッチサイズ分のレコードを、順次「学習」に利用します。
          2. ステップが進み、レコードの最後まで到達すると、先頭からもう一度取得するようにします。

            バッチサイズの説明
            (画像をクリックすると大きく表示します)

      2. 学習が済んだら、評価を行います。
        • 前述のように学習済みである同じモデルを用います。
        • 入力ファイルを「評価用データ」に切り替えます
        • 推定値と正解ラベルで、答え合わせをします。

サンプルコード(全体)

サンプルコードの解説 

1)TFRecordファイルを解析する

TFRecordファイルを作成した際に各項目を定義した内容で、バイナリーデータを解析し、読み込めるようにします。

◇32~40行目のコード

TFRecordの各要素の定義を指定して、値を取得します。

  • features:TFRecord形式でファイルを作成した時の各要素の定義情報。

    • 「label」要素(フィーチャ):MINSTの各画像の教師ラベル。

    • 「image」要素(フィーチャ):MINSTの各画像データ。

  • tf.parse_single_example:各要素の定義情報を基に、データ構造を解析。

◇42~52行目のコード

TFRecordから取得した値を加工します。

  • label:
    • one-hot表現に変換します。
      つまり、分類10種類の内、該当する値を1にし、その他を0にしたテンソルにします。
  • image:
    • 値を255で割って、0~1の範囲に収まるように正規化しています。
    • データの形状を、[ 高さ, 幅 , チャネル数 ] に変形しています。

2)モデルの作成

入力画像に対して、畳み込みニューラルネットワークで処理するようにしています。

  1. 畳み込み層
    • 重み:3(高さ)×3(幅)×1(チャネル数)×32
  2. プーリング層
    • 最大値プーリング
    • パディングあり
    • 出力結果をフラット化
  3. 全結合層(1)
    • 出力:100
  4. 全結合層(2)
    • 出力:10
  5. 出力層
    • ソフトマックス関数

◇57~67行目のコード

  • 入力ファイルを、「tf.placeholder」を用いて指定しています。
    • これにより、入力ファイルの切り替えが可能となります。
    • 従来の方法「キューベースパイプライン」では、エラーが発生して利用できませんでした。
  • dataset.map(_parse_function, NUM_THREADS)

    • TFRecordのデータ定義に従い、データを取得しています。

    • 前述で定義した関数「_parse_function」で処理します。
    • スレッド数(= NUM_THREADS)を指定し、マルチスレッド(並列処理)を行っています。
  • dataset.map(read_image, NUM_THREADS)

    • データに対して、自分でカスタマイズした加工を行っています。

    • 前述で定義した関数「read_image」で処理します。
    • スレッド数(= NUM_THREADS)を指定し、マルチスレッド(並列処理)を行っています。
  • dataset.batch(BATCH_SIZE)

    • バッチサイズ(= BATCH_SIZE)を指定し、まとまったデータを取得しています。

  • dataset.repeat(-1)

    • データセットを繰り返す回数を指定しています。

    • 「-1」を指定することで、無限に繰り返すことを意味しています。

  • tf.contrib.data.Iterator.from_structure(dataset.output_types, dataset.output_shapes)

    • データセットを繰り返し取得するためのイテレータを、データ型とデータ形状を指定し、作成します。
    • この時点で、データは含んでいません。
    • データの初期化は、以下で行っています。
      • iterator.make_initializer(dataset)

3)学習と評価

これまでの内容で、入力ファイルを読み込む準備と、そのファイルを利用したモデルの作成が終わりましたので、これから実際に学習を行います。学習が済んだら、入力ファイルを評価用データに切り替えて、評価を行います。

◇116行目のコード

◇128行目のコード

sess.run(init_op, feed_dict={filenames: [(ファイル名)]})

  • ここで、入力ファイルを初期化しています。

  • このコードを実行することで、入力に用いる TFRecordファイルを、切り替えることができます。
  • このコードは for ループの外側で実行しています。
    ※ループ内で実行してしまうと、毎回初期化され、毎回先頭レコードからバッチサイズ分のデータが、「学習」に繰り返し利用されてしまいます。

まとめ

ここまで見てきたように、TFRecordDataset APIを用いることで、大量のデータを扱った、マルチスレッド(並列処理)によるミニバッチ学習を、簡潔なコードでコーディングできるうえ、入力データを切り替えて、同じモデルを「学習」処理と「評価」処理などで再利用することが簡単にできるようになります。

お問い合わせ先

執筆者プロフィール

Oose Takashi
Oose Takashitdi デジタルイノベーション技術部
入社以来、C/S型の業務システム開発に従事してきました。ここ数年は、SalesforceやOutSystemsなどの製品や、スクラム開発手法に取り組み、現在のテーマは、DeepLearning/機械学習です。
Pocket

関連記事