SuprSonicJetBoy's blog

いろいろです。

TensorFlow の CIFAR-10で実際に予測してみる

TensorFlowのCIFAR-10チュートリアルを最後まで終えると、学習済みデータをテストデータで評価することができます。
その次の段階としては、実際に学習済みデータを使って、入力された画像の予測ラベルを出力できると実用的なものとなります。

入力画像に対する予測は、TensorFlowのチュートリアルにないので、評価部分の関数を改造して簡単に実装してみました。

もくじ

学習済みデータを用意する

チュートリアルに従って学習します。
Convolutional Neural Networks  |  TensorFlow

C:\>python cifar10_train.py
2017-04-30 17:22:40.654860: step 0, loss = 4.68 (5.6 examples/sec; 22.668 sec/batch)
2017-04-30 17:22:51.564441: step 10, loss = 4.62 (117.3 examples/sec; 1.091 sec/batch)
2017-04-30 17:23:13.666061: step 20, loss = 4.45 (57.9 examples/sec; 2.210 sec/batch)
2017-04-30 17:23:28.059816: step 30, loss = 4.37 (88.9 examples/sec; 1.439 sec/batch)
2017-04-30 17:23:46.472412: step 40, loss = 4.33 (69.5 examples/sec; 1.841 sec/batch)
2017-04-30 17:23:58.002931: step 50, loss = 4.31 (111.0 examples/sec; 1.153 sec/batch)
2017-04-30 17:24:12.224520: step 60, loss = 4.26 (90.0 examples/sec; 1.422 sec/batch)
2017-04-30 17:24:27.601215: step 70, loss = 4.10 (83.2 examples/sec; 1.538 sec/batch)
2017-04-30 17:24:38.973096: step 80, loss = 4.23 (112.6 examples/sec; 1.137 sec/batch)
2017-04-30 17:24:51.000095: step 90, loss = 4.32 (106.4 examples/sec; 1.203 sec/batch)
2017-04-30 17:25:03.198763: step 100, loss = 4.18 (104.9 examples/sec; 1.220 sec/batch)

だいたい2000stepくらい回してみました。
そのままの状態の実行すると、/tmp/cifar10_train に、600秒毎に保存されます。

精度はこんな具合。
結構低いですね。。。

C:\>python cifar10_eval.py
2017-04-30 18:31:56.477270: precision @ 1 = 0.665

予測用の関数

評価用のcifar10_eval.pyがあるのでこれを改造します。

まず、eval_once()を変更します。

-def eval_once(saver, summary_writer, top_k_op, summary_op):
+def eval_once(saver, summary_writer, logits, summary_op, labels):
+  classes = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

  with tf.Session() as sess:
    ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_dir)
    if ckpt and ckpt.model_checkpoint_path:
      # Restores from checkpoint
      saver.restore(sess, ckpt.model_checkpoint_path)
      # Assuming model_checkpoint_path looks something like:
      #   /my-favorite-path/cifar10_train/model.ckpt-0,
      # extract global_step from it.
      global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]
    else:
      print('No checkpoint file found')
      return

    # Start the queue runners.
    coord = tf.train.Coordinator()
    try:
      threads = []
      for qr in tf.get_collection(tf.GraphKeys.QUEUE_RUNNERS):
        threads.extend(qr.create_threads(sess, coord=coord, daemon=True,
                                         start=True))

-      num_iter = int(math.ceil(FLAGS.num_examples / FLAGS.batch_size))
-      true_count = 0  # Counts the number of correct predictions.
-      total_sample_count = num_iter * FLAGS.batch_size
-      step = 0
-      while step < num_iter and not coord.should_stop():
-        predictions = sess.run([top_k_op])
-        true_count += np.sum(predictions)
-        step += 1

+      _summary_op, prediction, correct = sess.run([summary_op, logits, labels])
+      num = 3
+      for i in range(num):
+        value = sess.run(tf.argmax(prediction[i], 0))
+        print("image: {0}/{1}".format((i+1), num))
+        print('Correct:   ', classes[correct[i]])
+        print('Prediction:', classes[value])
+        print(prediction[i], "\n")

      # Compute precision @ 1.
-      precision = true_count / total_sample_count
-      print('%s: precision @ 1 = %.3f' % (datetime.now(), precision))

      summary = tf.Summary()
-      summary.ParseFromString(sess.run(summary_op))
+      summary.ParseFromString(_summary_op)
-      summary.value.add(tag='Precision @ 1', simple_value=precision)
      summary_writer.add_summary(summary, global_step)
    except Exception as e:  # pylint: disable=broad-except
      coord.request_stop(e)

    coord.request_stop()
    coord.join(threads, stop_grace_period_secs=10)

まず、top_k_oplogitsに変更し、ラベルを受け取れるようにしたいので引数を増やします。

結果をわかりやすくするため、CIFAR-10では、0~9の数値にクラス名が割り振られているので、以下の順でリストを用意します。
['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
ラベルの値が[3]であれば、クラス名は[cat]になります。

今回入力する画像は、既にラベル付けされているテストデータを使います。
ただし、テストデータは画像とラベルがバイナリで固められているので、どの画像が入力されたかは簡単にはわかりません。
念のため、取り出した画像とラベルが正しい組み合わせか確認したいので、ログを書き出して、TensorBoardで表示してみます。

とはいっても、既存のコードに書き出し部分が記述されているのでそのままでもTensorBoardで確認できます。
ただし、このままでは入力される画像と、TensorBoardで確認できる画像がズレてしまうので、sess.run()にsummary_opも一緒に渡してやります。
この一緒にっていうのがミソです。

とりあえず今回は、3つの画像で予測を行います。
画像を増やしたい場合は、ループの回数と、ログの出力部分を変更してやります。
ログの出力は、cifar10_input.py_generate_image_and_label_batch() にある、tf.summary.image('images', images)で行われています。
10枚ならtf.summary.image('images', images, max_outputs=10)といった感じに変更します。(ただの確認なので修正しなくても影響はありません)

で、呼び出し側の evaluate() の方です。

def evaluate():  
  """Eval CIFAR-10 for a number of steps."""
  with tf.Graph().as_default() as g:
    # Get images and labels for CIFAR-10.
    eval_data = FLAGS.eval_data == 'test'
    images, labels = cifar10.inputs(eval_data=eval_data)

    # Build a Graph that computes the logits predictions from the
    # inference model.
    logits = cifar10.inference(images)

    # Calculate predictions.
-    top_k_op = tf.nn.in_top_k(logits, labels, 1)

    # Restore the moving average version of the learned variables for eval.
    variable_averages = tf.train.ExponentialMovingAverage(
        cifar10.MOVING_AVERAGE_DECAY)
    variables_to_restore = variable_averages.variables_to_restore()
    saver = tf.train.Saver(variables_to_restore)

    # Build the summary operation based on the TF collection of Summaries.
    summary_op = tf.summary.merge_all()

    summary_writer = tf.summary.FileWriter(FLAGS.eval_dir, g)

+    eval_once(saver, summary_writer, logits, summary_op, labels)
-    while True:
-      eval_once(saver, summary_writer, top_k_op, summary_op)
-      if FLAGS.run_once:
-        break
-      time.sleep(FLAGS.eval_interval_secs)

こんな感じです。
余計なのを消して、1回だけ呼び出すようにします。

テストデータで予測してみる

C:\>python cifar10_eval.py
image: 1/3
Correct:    airplane
Prediction: airplane
[ 3.62620592  1.53768694  1.48563719 -1.0087955  -1.08129168 -2.64975023
 -1.27237046 -2.95979309  3.30913401 -0.78325409]

image: 2/3
Correct:    cat
Prediction: cat
[-1.12269092 -1.88968229  0.24695219  3.0334115  -0.24946031  2.12394643
  0.70795071 -0.63257164 -0.17022569 -1.98787034]

image: 3/3
Correct:    ship
Prediction: ship
[ 2.59685659  4.38015366 -1.17230809 -1.95569634 -2.00066233 -3.12228751
 -2.66573024 -2.98842955  4.54536724  2.56416297]

100%の正答率。

念のため、TensorBoardで入力画像の確認。

C:\>tensorboard --logdir=/tmp/cifar10_eval
Starting TensorBoard b'52' at http://localhost:6006
(Press CTRL+C to quit)

f:id:suprsonicjetboy:20170430185142j:plain 2枚目が猫かどうか怪しいですが、たぶん猫です。
[飛行機、猫、船]と、入力画像も正しいので、ばっちり当たっています。

自前の画像で予測してみる

今度は自前の画像を使ってみます。
evaluate() の画像の入力部分で、tf.read_file() で読み込んで、tf.image.decode_jpeg() で変換してやります。

こんな感じです。

def evaluate():  
  with tf.Graph().as_default() as g:
    f = tf.read_file('./input.jpg')
    image = tf.image.decode_jpeg(f, channels=3)
    image = tf.image.resize_images(image, (24, 24))

    tf.summary.image('images', [image])
    logits = cifar10.inference([image])

    variable_averages = tf.train.ExponentialMovingAverage(cifar10.MOVING_AVERAGE_DECAY)
    variables_to_restore = variable_averages.variables_to_restore()
    saver = tf.train.Saver(variables_to_restore)

    summary_op = tf.summary.merge_all()

    summary_writer = tf.summary.FileWriter(FLAGS.eval_dir, g)

    eval_once(saver, summary_writer, logits, summary_op)

今回はcifar10_input.pyを使わないので、ログに画像が書き出されませんので、tf.summary.image('images', [image])でログに書き出します。
なくてもいいです。
ラベルのデータはないので、eval_once() から、labelsの記述を削除します。

するとこんな感じ。

C:\>python cifar10_eval.py
image: 1/1
Prediction: cat
[-25.54330444 -24.77457619   9.08094788  27.7165451   14.62664127
   9.53357697  24.73740768 -15.22198772   2.13212824 -19.82105827]

ちなみに入力画像はこちら。 f:id:suprsonicjetboy:20170430200754j:plain
Image by フリー素材ぱくたそ

トリミングもなしで、このままぶち込みます。 実際に入力される画像は、28x28なのでここまで低画質になりますが、正確に判断してくれています。 f:id:suprsonicjetboy:20170430201054j:plain

TensorFlowをGPUで回すと落ちる場合の改善策

TensorFlowをGPUで回すとPCが落ちてしまうので、仕方なくCPUで回していたのですが、どうにもこうにも遅すぎるし、せっかく対応しているGPUがあるので、ちゃんと回るように対策をしてみました。

もくじ

症状

TensorFlowのチュートリアルCIFAR-10」をGPUで回すと、パソコンが落ちる。
ただし、少数枚のCNNだと回る。

改善策

先に改善策だけ。
ドライバのアップデート。
これがすべてでした。。。

環境

OS: Windows 10
GPU: NVIDIA Quadro K2000D (Compute Capability 3.0)
Python: 3.5.2
TensorFlow: tensorflow (1.0.1)、tensorflow-gpu (1.1.0rc1)
CUDA: 8.0.61
cuDNN: 5.1.5

原因を探る

まず、落ちた原因を探ってみます。
Windowsであればイベントビューアーで探せるのですが、エラーイベントが多すぎて探すのが面倒なので、コントロール パネル\システムとセキュリティ\セキュリティとメンテナンス\解決策の確認で、重大なエラーのみが出るのでこちらで見てみます。

こんな感じでありました。

説明
ハードウェアの問題により、Windows は正しく動作しなくなりました。

問題の署名
問題イベント名:  LiveKernelEvent
コード:  ab
パラメーター 1:   a
パラメーター 2:   1d0
パラメーター 3:   1d0
パラメーター 4:   300000003
OS バージョン: 10_0_14393
Service Pack:   0_0
製品: 768_1
OS バージョン: 10.0.14393.2.0.0.768.101
ロケール ID:    1041

LiveKernelEventをググると、 answers.microsoft.com なので、ほぼほぼGPUが原因です。

GPUの状態を確認する

搭載されているGPUの状態を、nvidia-smi.exe で確認します。

C:\>"C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe"
Sun Apr 23 16:25:36 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 369.30                 Driver Version: 369.30                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name            TCC/WDDM | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Quadro K2000D      WDDM  | 0000:01:00.0      On |                  N/A |
| 30%   27C    P8    N/A /  N/A |    270MiB /  2048MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

ちなみに、少数枚のCNNで起動するとこのような感じで使用量が増えます。

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 369.30                 Driver Version: 369.30                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name            TCC/WDDM | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Quadro K2000D      WDDM  | 0000:01:00.0      On |                  N/A |
| 30%   35C    P0    N/A /  N/A |   1796MiB /  2048MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

この状態だと、約88%のGPUメモリを使用しています。
Volatile GPU-Utilは0%のままですが、これは一定期間、GPUがどれだけアクティブだったかを示すみたいです。
nvidia-smi.exe -l 5とかやると、5秒間隔で更新されるので、増減は確認できます。 stackoverflow.com

しかし、GPUは間違いなく認識されていますが、GPUで回しているはずが「No running processes found」と出ています。
使用量があるのに、プロセスが立っていません。
もしかすると、これがなんらかの問題を誘発しているのかもしれません。

ただ、TensorFlowをGPUで回すと認識はされています。

2017-04-23 16:37:28.231018: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:887] Found device 0 with properties:
name: Quadro K2000D
major: 3 minor: 0 memoryClockRate (GHz) 0.954
pciBusID 0000:01:00.0
Total memory: 2.00GiB
Free memory: 1.67GiB
2017-04-23 16:37:28.231128: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:908] DMA: 0
2017-04-23 16:37:28.232202: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:918] 0:   Y
2017-04-23 16:37:28.232667: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:977] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Quadro K2000D, pci bus id: 0000:01:00.0)

No running processes found

serverfault.com github.com

根本的な解決策はわかりませんでしたが、とりあえずドライバをアップデートをするといいみたいです。
CUDA 7.5であれば、ドライバのバージョンが、352.xxかそれ以上の必要があるようです。(この情報は公式サイトでは探せませんでした)
nvidia-smi: No running processes found · Issue #8877 · tensorflow/tensorflow · GitHub
いま使用しているCUDAは8.0.xxなので、要件がわかりませんがとりあえず上げてみます。

念のため、PCを再起動。

Sun Apr 23 17:43:01 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 377.11                 Driver Version: 377.11                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name            TCC/WDDM | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Quadro K2000D      WDDM  | 0000:01:00.0      On |                  N/A |
| 30%   29C    P8    N/A /  N/A |    264MiB /  2048MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      3700  C+G   ...ost_cw5n1h2txyewy\ShellExperienceHost.exe N/A      |
|    0      4008  C+G   ...am Files (x86)\Microsoft VS Code\Code.exe N/A      |
|    0      7044  C+G   Insufficient Permissions                     N/A      |
|    0      8956  C+G   C:\Windows\explorer.exe                      N/A      |
|    0      9096  C+G   ...indows.Cortana_cw5n1h2txyewy\SearchUI.exe N/A      |
|    0      9760  C+G   ...le Japanese Input\GoogleIMEJaRenderer.exe N/A      |
|    0      9824  C+G   ...x86)\Google\Chrome\Application\chrome.exe N/A      |
|    0      9932  C+G   ...pple\Internet Services\iCloudServices.exe N/A      |
+-----------------------------------------------------------------------------+

プロセスが表示されました!
「Insufficient Permissions」がありますが、これは「dwm.exe」のようです。

GPU Memory Usageは、N/Aが出ています。
nvidia-smi.exe -qで詳細を見てみると、WDDMでは、メモリの使用量を把握できないようなので無視してOKです。

    Processes
        Process ID                  : 3700
            Type                    : C+G
            Name                    : C:\Windows\SystemApps\ShellExperienceHost_cw5n1h2txyewy\ShellExperienceHost.exe
            Used GPU Memory         : Not available in WDDM driver model
        Process ID                  : 7044
            Type                    : Insufficient Permissions
            Name                    : Insufficient Permissions
            Used GPU Memory         : Not available in WDDM driver model
        Process ID                  : 8956
            Type                    : C+G
            Name                    : C:\Windows\explorer.exe
            Used GPU Memory         : Not available in WDDM driver model
        Process ID                  : 9096
            Type                    : C+G
            Name                    : C:\Windows\SystemApps\Microsoft.Windows.Cortana_cw5n1h2txyewy\SearchUI.exe
            Used GPU Memory         : Not available in WDDM driver model
        Process ID                  : 9760
            Type                    : C+G
            Name                    : C:\Program Files (x86)\Google\Google Japanese Input\GoogleIMEJaRenderer.exe
            Used GPU Memory         : Not available in WDDM driver model
        Process ID                  : 9824
            Type                    : C+G
            Name                    : C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
            Used GPU Memory         : Not available in WDDM driver model
        Process ID                  : 9932
            Type                    : C+G
            Name                    : C:\Program Files (x86)\Common Files\Apple\Internet Services\iCloudServices.exe
            Used GPU Memory         : Not available in WDDM driver model

この状態で、TensorFlowを動かすと、

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      3700  C+G   ...ost_cw5n1h2txyewy\ShellExperienceHost.exe N/A      |
|    0      4008  C+G   ...am Files (x86)\Microsoft VS Code\Code.exe N/A      |
|    0      7044  C+G   Insufficient Permissions                     N/A      |
|    0      8064    C   ...Local\Programs\Python\Python35\python.exe N/A      |
|    0      8956  C+G   C:\Windows\explorer.exe                      N/A      |
|    0      9096  C+G   ...indows.Cortana_cw5n1h2txyewy\SearchUI.exe N/A      |
|    0      9760  C+G   ...le Japanese Input\GoogleIMEJaRenderer.exe N/A      |
|    0      9824  C+G   ...x86)\Google\Chrome\Application\chrome.exe N/A      |
|    0      9932  C+G   ...pple\Internet Services\iCloudServices.exe N/A      |
+-----------------------------------------------------------------------------+

Pythonのプロセスが起動!
CIFAR-10のチュートリアルも問題なくGPUで動くようになりました!

試しに、画像128x128のバッチサイズ128で、400枚で試してみたところ、

2017-04-23 19:34:37.489835: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:908] DMA: 0
2017-04-23 19:34:37.491576: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:918] 0:   Y
2017-04-23 19:34:37.492104: I c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\gpu\gpu_device.cc:977] Creating TensorFlow device (/gpu:0) -> (device: 0, name: Quadro K2000D, pci bus id: 0000:01:00.0)
2017-04-23 19:34:42.443068: W c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\bfc_allocator.cc:217] Allocator (GPU_0_bfc) ran out of memory trying to allocate 3.02GiB. The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory is available.
2017-04-23 19:34:52.936364: W c:\tf_jenkins\home\workspace\nightly-win\device\gpu\os\windows\tensorflow\core\common_runtime\bfc_allocator.cc:273] Allocator (GPU_0_bfc) ran out of memory trying to allocate 96.00MiB.  Current allocation summary follows.

GPUのメモリ不足で「正常」に終了してしまいました。。。
こればっかりはGPUのスコアの問題なので仕方ないですが、PCが落ちること無く終了してくれるようになりました。
一応問題は解決です。

結論

ドライバのアップデートで、今まで回すとPC自体が落ちてしまうCIFAR-10を回すことに成功しました。 (もしかすると、バージョンを落とすのがいい場合もあるかもしれませんが )
一応、CIFAR-10は、1000ステップまで試しましたが問題なく進み、CPUの3倍くらい速く処理しています。

あとは、処理中はGPUの使用率を上げないために、ChromeとかVS Codeとか閉じれるものは閉じて回すようにしてみました。
これだけで、100MBは違うので。

低火力で回らないのは、画像サイズやバッチサイズのチューニングでなんとかなるかもしれません。
128x128の画像も、400枚程度だと96x96のバッチサイズ34ぐらいに変えると回ります。
これを1000枚、10000枚となると微妙ですが、Compute Capability 3.0のGPUだと、そもそもキツイって話ですね。。。

TensorFlowで画像をTFRecordsで読み書きしてみる

TensorFlowでCNNを試しているのですが、教師データのJPEG画像をTFRecords形式への変換と読み書きに四苦八苦したので、そのメモ。

もくじ

まえおき

学習時にいろいろな画像サイズで試したくて、1枚の画像につき、

  • 32x32
  • 64x64
  • 96x96
  • 128x128

をそれぞれ用意していたのですが、どうにも面倒くさい。
さらに、ラベルは訳あって画像分のXMLファイルから学習時に毎回読み込んでいたので、読み込みにかなり時間がかかっていました。

訳というのは、教師データを作るために、まず最初に、SSD(Single Shot MultiBox Detector)で対象の領域を推測し、その推測された領域(ground truth bounding box)のデータとラベルをPASCAL VOCのAnnotation形式のXMLファイルに落として、labelimgを使って正確に修正して、最後に切り抜いています。
そのためにラベルが別になっているのですが、画像とラベルを一緒に格納できればかなり便利です。(普通はそうします)

最初はCIFAR-10のような形式も考えたのですが、TFRecordsが便利そうなのでこれを試してみました。

環境
・ tensorflow_gpu-1.1.0rc1
・ Python 3.5.2
・ Windows 10

TFRecords形式に書き出し

tf.python_io.TFRecordWriter() というのを使います。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys
from os.path import join, relpath
from glob import glob
from PIL import Image
import numpy as np
import tensorflow as tf


FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_string('directory', 'data', """Directory where to write *.tfrecords.""")


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]))


def convert_to(dataset, name):
    filename = os.path.join(FLAGS.directory, name + '.tfrecords')
    writer = tf.python_io.TFRecordWriter(filename)

    for data in dataset:
        image_path = data[0]
        label = int(data[1])

        image_object = Image.open(image_path)
        image = np.array(image_object)

        height = image.shape[0]
        width = image.shape[1]
        depth = 3
        image_raw = image.tostring()

        example = tf.train.Example(features=tf.train.Features(feature={
            'height': _int64_feature(height),
            'width': _int64_feature(width),
            'depth': _int64_feature(depth),
            'label': _int64_feature(label),
            'image_raw': _bytes_feature(image_raw)}))
        writer.write(example.SerializeToString())

    writer.close()


def main(unused_argv):
    if not tf.gfile.Exists(FLAGS.directory):
        tf.gfile.MakeDirs(FLAGS.directory)

    label_data = [
        ['dog', 0],
        ['cat', 1]
    ]

    img_data = []
    for n, v in label_data:
        path = os.path.join('images', n)
        
        for file in [relpath(x, path) for x in glob(join(path, '*.jpg'))]:
            img_data.append([os.path.join(path, file), v])

    convert_to(img_data, 'train')


if __name__ == '__main__':
    tf.app.run()

画像の読み込みは、PILを使っています。
こうすると、train.tfrecordsで書き出されます。

中身を復元して確認する

書き出されたtfrecordsが正しく書き出されているか、実際に画像に復元して確かめてみます。

読み込みには、tf.TFRecordReader() というのを使います。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import sys
from os.path import join, relpath
from glob import glob
from PIL import Image
import numpy as np
import tensorflow as tf


FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_string('directory', 'data', """Directory where to read *.tfrecords.""")


def read_and_decode(filename_queue):
    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_queue)

    features = tf.parse_single_example(
        serialized_example,
        features={
            'height': tf.FixedLenFeature([], tf.int64),
            'width': tf.FixedLenFeature([], tf.int64),
            'label': tf.FixedLenFeature([], tf.int64),
            'depth': tf.FixedLenFeature([], tf.int64),
            'image_raw': tf.FixedLenFeature([], tf.string),
        })

    image_raw = tf.decode_raw(features['image_raw'], tf.uint8)
    height = tf.cast(features['height'], tf.int32)
    width = tf.cast(features['width'], tf.int32)
    depth = tf.cast(features['depth'], tf.int32)
    label = tf.cast(features['label'], tf.int32)

    image = tf.reshape(image_raw, tf.stack([height, width, depth]))

    return image, label


def inputs():
    if not FLAGS.directory:
        raise ValueError('Please supply a directory')

    tfrecords_filename = 'train'
    filename = os.path.join(FLAGS.directory, tfrecords_filename + '.tfrecords')
    filename_queue = tf.train.string_input_producer([filename])

    image, label = read_and_decode(filename_queue)

    return image, label


def main(unused_argv):
    if not os.path.exists('output'):
        os.mkdir('output')

    images, labels = inputs()

    init = tf.global_variables_initializer()

    with tf.Session() as sess:

        sess.run(init)

        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(coord=coord)

        try:
            for i in range(6):
                e, l = sess.run([images, labels])
                img = Image.fromarray(e, 'RGB')
                img.save(os.path.join('output', "{0}-{1}.jpg".format(str(i), l)))
        finally:
            coord.request_stop()
            coord.join(threads)


if __name__ == '__main__':
    tf.app.run()

ポイントは、

tf.train.string_input_producer([filename])

でキューを作成し、

reader = tf.TFRecordReader()
reader.read(filename_queue)

で読み込み、

coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(coord=coord)

coord.request_stop()
coord.join(threads)

で処理をします。

画像を含めたサンプルを上げました。 github.com

参考

Bash on Ubuntu on Windows で cpanm が使えないのを解決できた

Bash on Ubuntu on Windows を試していたところ、cpanm でPerlモジュールをインストールすると、謎のエラーが発生。
その解決策。

cpanm がすんなり入らなかった

$ curl -L https://cpanmin.us | perl - --sudo App::cpanminus

上手く入らず、エラーが発生。
ここで気付いていればよかったのですが、面倒だったので apt-get でやってしまいました。

$ sudo apt-get install cpanminus

上手く入りました。

モジュールのインストール

JSON を入れてみます。

$ cpanm JSON
--> Working on JSON
Fetching http://www.cpan.org/authors/id/M/MA/MAKAMAKA/JSON-2.90.tar.gz ... OK
Configuring JSON-2.90 ... OK
Building and testing JSON-2.90 ... FAIL
! Installing JSON failed. See /home/suprsonicjetboy/.cpanm/work/1476965938.6921/build.log for details. Retry with --force to force install it.

FAIL!!!

ログを見ると、(一部抜粋)

#   Failed test 'use JSON;'
#   at t/00_load.t line 8.
#     Tried to use 'JSON'.
#     Error:  Can't locate JSON/backportPP.pm in @INC (you may need to install the JSON::backportPP module) (@INC contains: /home/suprsonicjetboy/.cpanm/work/1476965938.6921/JSON-2.90/blib/lib ......) at (eval 7) line 2.
#  at t/00_load.t line 8.
# Compilation failed in require at t/00_load.t line 8.
# BEGIN failed--compilation aborted at t/00_load.t line 8.

Files=58, Tests=207,  5 wallclock secs ( 0.12 usr  0.31 sys +  1.45 cusr  2.23 csys =  4.11 CPU)
Result: FAIL
Failed 40/58 test programs. 3/207 subtests failed.
make: *** [test_dynamic] エラー 255
-> FAIL Installing JSON failed. See /home/suprsonicjetboy/.cpanm/work/1476965938.6921/build.log for details. Retry with --force to force install it.

どうやら、JSON/backportPP.pm が上手く入らないようですが、JSON/backportPP.pm は直接入れられません。
JSON/backportPP.pm はJSON モジュールに含まれているからです。

$ cpanm JSON::backportPP
! Finding JSON::backportPP on cpanmetadb failed.
! Finding JSON::backportPP () on mirror http://www.cpan.org failed.
! Couldn't find module or a distribution JSON::backportPP

the JSON distribution will include JSON::backportPP for backwards computability.
http://search.cpan.org/~makamaka/JSON-2.90/lib/JSON.pm#note

パスが通っていない?

間違っていなく通したはず。。。

$ export PERL_CPANM_OPT="--local-lib=~/perl5"
$ export PATH=$HOME/perl5/bin:$PATH;
$ export PERL5LIB=$HOME/perl5/lib/perl5:$PERL5LIB;

$ echo $PERL_CPANM_OPT
--local-lib=~/perl5
$ echo $PATH
/home/suprsonicjetboy/perl5/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
$ echo $PERL5LIB
/home/suprsonicjetboy/perl5/lib/perl5:

CAPN で入れてみる

$ cpan JSON

。。。ダメだ…

upgdate してみる

cpan> upgrade

上手くいかない。。。
CPAN が機能していない、というよりも、Bash on Ubuntu on Windows がおかしいのではないか…?

issues

とりあえず、issue を探してみます。
github.com

これっぽい。
github.com

$ sudo vim /usr/lib/perl/5.18.2/Config.pm

94行目。
デフォルトが、dont_use_nlink => undef になっているので、dont_use_nlink => 'define' に変更。

違うissueもありました。
github.com
こっちは、undef を 1 にします。

'define' でも 1 でも、true になればOK。
true はないけど。

一応、ドキュメントには、1 とあります。
File::Find - search.cpan.org

解決

$ cpanm JSON
--> Working on JSON
Fetching http://www.cpan.org/authors/id/M/MA/MAKAMAKA/JSON-2.90.tar.gz ... OK
Configuring JSON-2.90 ... OK
Building and testing JSON-2.90 ... OK
Successfully installed JSON-2.90
1 distribution installed

原因?

Ubuntu on Windowsファイルシステムの問題っぽい?

dont_use_nlink に関係する気になる issue がありました。
File::Findで再帰的に検索できない問題。
github.com

$ perl -e 'use File::Find;find(sub {print $File::Find::name,$/;},".");'

解決策として、これも、dont_use_nlink => 'define' の設定で改善するとのこと。

もしかすると、このあたりのタイミングで修正されたのかもしれません。

Build 14901
Fixed
Added mount and unmount for tmpfs, procfs and sysfs file systems
Bash on Ubuntu on Windows - Release Notes

つまり

Vagrant / VirtualBox を使ったほうがよさそう。。。