TensorFlow データ読込実用編

## 初めに

+ 転職して約1ヶ月経過[^chjob]
+ 仕事でバリバリ [TensorFlow](https://www.tensorflow.org/ "TensorFlow -- an Open Source Software Library for Machine Intelligence") 活用中
+ ↑にともなって月の前半は TF(再)勉強
+ その成果の紹介
    + 主にデータ収集・読込回りの情報共有
    + あと[Qiitaスライドモード](http://qiita.com/Qiita/items/4ff5873776992f0511d6)のテストも兼

[^chjob]: ていうか転職しました。機械学習ライフを満喫しています。

---

## `tf.train.batchXXX()`

+ 指定したバッチサイズのミニバッチをどんどん生成してくれる「データプロバイダ」を生成する関数
+ 内部でキューを利用して、先読み・送出する仕組み
+ 複数のデータを同時（同期）的に送出可能（画像とラベルとか）
+ 参照：[Batching at the end of an input pipeline](https://www.tensorflow.org/api_docs/python/io_ops.html#batching-at-the-end-of-an-input-pipeline)

---

### サンプル

---

```py
# データ・ラベルの定義
data = tf.random_uniform([2], -1, 1)
label = tf.cast(tf.reduce_prod(data)>=0, tf.float32)

# バッチデータプロデューサ（バッチサイズ：1000）
X_batch, Y_batch = tf.train.batch(
      [data, label], batch_size=1000)
```

---

```py
# セッション開始
with tf.Session() as sess:
    # 入力 enqueue スレッド開始
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(
                    sess=sess, coord=coord)

    try:
        # ミニバッチ処理ループ
        while not coord.should_stop():
            XData, YData = sess.run([X_batch, Y_batch])
            # 学習等の処理

    finally:
        coord.request_stop()
        coord.join(threads)

```

---

## `tf.XxxReader`

+ ファイルからデータを読み込むキューを生成するクラス
+ `reader.read()` メソッドにファイル名（のキュー）を渡すことで、ファイルの内容（の一部（を表す tensor））が得られる
+ ファイル（やレコード）の種類によって各種のクラスが用意されている
+ 参照：[Readers](https://www.tensorflow.org/api_docs/python/io_ops.html#readers)

---

### サンプル

sample.csv からデータを読み込んで処理する例

---

```py
# CSV ファイル名（※1ファイルでもキュー生成必要）
filename_queue = tf.train.string_input_producer(
                    ["sample.csv"])
# TextLineReader 生成（1行ずつ読み込む Reader）
reader = tf.TextLineReader()
key, value = reader.read(filename_queue)

# CSVデコード（列は3列、いずれも実数値、という指定↓）
x1, x2, y = tf.decode_csv(value, [[1.0], [1.0], [1.0]])
x = tf.pack([x1, x2])

# バッチデータプロデューサ（バッチサイズ：1000）
X_batch, Y_batch = tf.train.batch(
      [x, y], batch_size=1000)

```

※あとは先ほどと同様

---

## キューの仕組み

+ `tf.train.QueueRunner`
    + ↑のキューへの enqueue 操作を繰り返し実行するスレッド（を作成するクラス）
    + セッション開始後、`tf.train.start_queue_runners()` で開始する[^start_queue]
+ `tf.train.Coordinator`
    + 複数スレッドを束ね、停止などの同期をとるクラス。

[^start_queue]: 逆に`tf.train.start_queue_runners()`せずにキュー（を利用した操作）を利用しようとすると、enqueue待ちで無限ループ発生するので注意！

---

### サンプル

（先ほどのコードの再掲）

---

```py
# セッション開始
with tf.Session() as sess:
    # 入力 enqueue スレッド開始
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(
                    sess=sess, coord=coord)

    try:
        # ミニバッチ処理ループ
        while not coord.should_stop():
            XData, YData = sess.run([X_batch, Y_batch])
            # 学習等の処理

    finally:
        coord.request_stop()
        coord.join(threads)

```

---

## オリジナルレコードファイルの利用

TensorFlow 独自のレコード形式を利用する方法の紹介。

---

### `tf.TFRecordReader`
+ TensorFlow 独自のレコード形式で保存したファイルから、1レコードずつ読み込む `Reader` クラス。
+ レコードの値（string）にシリアライズしたオブジェクトを格納（し、それをデコード）することで、バイナリデータ等様々なデータを利用可能[^serialize]。

[^serialize]: シリアライズの利点は、複数の画像ファイル等を1つのレコードファイルにまとめることでディスクIOが減り、結果的にパフォーマンス改善につながる（ことが期待される）こと。

---

### `tf.python_io.TFRecordWriter`

+ 先述のレコード形式ファイルを書き出す Writer クラス（Python IO）
+ （事前処理として）実行してレコードファイルを作成するのに使用（＝セッションとは無関係）

---

### `tf.train.Example`

+ 複雑なデータをシリアライズ/デシリアライズするためのクラス
    + `writer.write()`時は、`tf.train.Example`のインスタンスを生成し、シリアライズして書き込み
    + `reader.read()`時は、`tf.parse_example()`や`tf.parse_single_example()`を利用してデシリアライズ＆データを（`Dict`として）取得して利用

---

### サンプル

---

#### ファイル書込（シリアライズ）

```py
# filename: 書込先ファイル名（例："sample.tfr"）
with tf.python_io.TFRecordWriter(filename) as writer:
    while True:
        # img_file: 画像ファイル名（例："data01.jpg"）
        image = open(img_filename).read()
        label = ... # 何らかの方法で取得or算出

        # tf.train.Example 生成
        example = tf.train.Example(features=tf.train.Features(feature={
                'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[label])),
                'image_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image]))}))

        # レコード書込
        writer.write(example.SerializeToString())
        
```

---

#### ファイル読込（デシリアライズ）

```py
# tf.TFRecordReader 生成、レコード読込
reader = tf.TFRecordReader()
result.key, value = reader.read(filename_queue)

# デシリアライズ、Dict 取得
features = tf.parse_single_example(value, features={
    'label': tf.FixedLenFeature([], tf.int64),
    'image': tf.FixedLenFeature([], tf.string),
})

# 結果取得
label = tf.cast(features['label'], tf.int32)
image = tf.image.decode_jpeg(features['image_raw'], channels=result.depth)

```

---

## 参考

+ [Inputs and Readers](https://www.tensorflow.org/api_docs/python/io_ops.html) (API リファレンス)
+ [Threading and Queues](https://www.tensorflow.org/how_tos/threading_and_queues/index.html)
    + 参考日本語訳：[TensorFlow : How To : スレッディングとキュー](http://tensorflow.classcat.com/2016/03/03/tensorflow-how-to-threading-and-queues/)
+ [Reading data](https://www.tensorflow.org/how_tos/reading_data/index.html)
    + 参考日本語訳：[TensorFlow : How To : データを読む](http://tensorflow.classcat.com/2016/02/13/tensorflow-how-tos-reading-data/)