読者です 読者をやめる 読者になる 読者になる

SuprSonicJetBoy's blog

いろいろです。

vagrant up で mount: unknown filesystem type 'vboxsf' が出る場合

すぐ忘れるのでメモ。

環境

Owner: Windows 10 Home Edition
Guest: CentOS7
VirtualBox: 5.1.22
Vagrant: 1.9.5

エラー

Boxは、CentOS7
マニュアル通り初期化。

C:\Users\suprsonicjetboy\dev>vagrant init centos/7
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Vagrantfileは、ほぼ初期状態で、config.vm.synced_folder だけを追記。

Vagrant.configure("2") do |config|
  config.vm.box = "centos/7"
  config.vm.synced_folder ".", "/vagrant", type: "virtualbox"
end

起動。

C:\Users\suprsonicjetboy\dev>vagrant up --provider virtualbox
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'centos/7' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Loading metadata for box 'centos/7'
    default: URL: https://atlas.hashicorp.com/centos/7
==> default: Adding box 'centos/7' (v1704.01) for provider: virtualbox
    default: Downloading: https://atlas.hashicorp.com/centos/boxes/7/versions/1704.01/providers/virtualbox.box
    default: Progress: 100% (Rate: 2318k/s, Estimated time remaining: --:--:--)
==> default: Successfully added box 'centos/7' (v1704.01) for 'virtualbox'!
==> default: Importing base box 'centos/7'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'centos/7' is up to date...
==> default: Setting the name of the VM: vm_default_1494922631778_58849
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default:
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.
==> default: Mounting shared folders...
    default: /vagrant => C:/Users/suprsonicjetboy/vm
Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attempted was:

mount -t vboxsf -o uid=1000,gid=1000 vagrant /vagrant

The error output from the command was:

mount: unknown filesystem type 'vboxsf'

解決

プラグイン vagrant-vbguest のインストールで一発。

C:\Users\suprsonicjetboy\dev>vagrant plugin install vagrant-vbguest
Installing the 'vagrant-vbguest' plugin. This can take a few minutes...
Fetching: micromachine-2.0.0.gem (100%)
Fetching: vagrant-vbguest-0.14.2.gem (100%)
Installed the plugin 'vagrant-vbguest (0.14.2)'!

C:\Users\suprsonicjetboy\dev>vagrant up --provider virtualbox
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'centos/7' is up to date...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
==> default: Machine booted and ready!
[default] No installation found.
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.iij.ad.jp
 * extras: ftp.iij.ad.jp
 * updates: ftp.iij.ad.jp
Package kernel-devel-3.10.0-514.16.1.el7.x86_64 already installed and latest version
Package gcc-4.8.5-11.el7.x86_64 already installed and latest version
Package binutils-2.25.1-22.base.el7.x86_64 already installed and latest version
Package 1:make-3.82-23.el7.x86_64 already installed and latest version
Package 4:perl-5.16.3-291.el7.x86_64 already installed and latest version
Package bzip2-1.0.6-13.el7.x86_64 already installed and latest version
Nothing to do
Copy iso file C:\Program Files\Oracle\VirtualBox\VBoxGuestAdditions.iso into the box /tmp/VBoxGuestAdditions.iso
mount: /dev/loop0 is write-protected, mounting read-only
Installing Virtualbox Guest Additions 5.1.22 - guest version is unknown
Verifying archive integrity... All good.
Uncompressing VirtualBox 5.1.22 Guest Additions for Linux...........
VirtualBox Guest Additions installer
Copying additional installer modules ...
Installing additional modules ...
vboxadd.sh: Starting the VirtualBox Guest Additions.
Redirecting to /bin/systemctl start  vboxadd.service
Redirecting to /bin/systemctl start  vboxadd-service.service
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => C:/Users/suprsonicjetboy/vm
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

最後に

Windows 10 Home Edition をグレードアップして、Docker for Windows を使いたい…

ブラウザ毎の最小フォントサイズを調べた

ブラウザのフォントサイズの最小は、いままで10pxだと思っていたけど、実は1pxでも表示できていました。
この際なので、自前のデバイスで確認できるだけ確認してみました。

書体は特に指定しておらず、ブラウザのデフォルトでの検証です。
単位は、px。

検証

ブラウザ OS バージョン 最小サイズ
Chrome Windows10 58.0.3029.96 (64-bit) 10px
Chrome macOS Sierra 58.0.3029.96 (64-bit) 10px
Chrome iOS10 58.0.3029.83 1px
Chrome Android5.1 58.0.3029.83 1px
FireFox Windows10 53.0.2 (64 ビット) 1px
FireFox macOS Sierra 53.0.2 (64 ビット) 1px
FireFox iOS10 7.4 1px
FireFox Android5.1 53.0.2 1px
Sleipnir Windows10 6.2.4.4000 10px
Sleipnir macOS Sierra 4.5.7015 1px
Sleipnir iOS10 4.3.4 1px
Sleipnir Android5.1 3.5.5 1px
Safari macOS Sierra 10.1 1px
Safari iOS10 (iOS10.3.1) 1px
Edge Windows10 38.14393.1066.0 1px
IE11 Windows10 11.1066.14393.0 1px

(2017/05/08 現在)

結果

最小値はブラウザ毎に異なっているとの認識はあったのですが、2017年にもなってそれが全ブラウザ10pxだと思い込んでいました。
普段使いのChromeのフォントの最小サイズが10pxだったので、いつの間にかに10pxだと刷り込まれていたようです。。。

現在でも、PC版のWindowsChromeSleipnirMacChromeにおいては下限が10pxとなっていますが、可読性を考えると10pxはそうそう使わないし、ましてや8pxとかなんて使ったことがないので、気にしなくてもいいのかなと。
あの小さいFacebookのいいねボタンですら11pxですし。

モバイルの場合は8pxでも読めなくはないので、レイアウトを気にするのであれば、アイコンあたりにはギリギリ使えるのかなと思います。

まとめ

思い込みはいけないですね。。。

TensorFlowでランダムな数値を作成する

TensorFlowでランダムな数値を作るのに手こずったのでメモ。

方法

_n = tf.random_uniform([1], minval=0, maxval=4, dtype=tf.int32, seed=None, name=None)
n = _n[0]


import numpy as np
_n = tf.random_shuffle(np.arange(4), seed=None, name=None)
n = _n[0]

この例では、0~3の範囲で、ランダムに出力します。

tf.random_uniform()は、minvalからmaxvalまでの連続値を取ります。
奇数とかのランダム値作りたい場合は、tf.random_shuffle()に奇数のリストを渡せばOKです。

ランダム系のメソッド

  • tf.random_normal
  • tf.truncated_normal
  • tf.random_uniform
  • tf.random_shuffle
  • tf.random_crop
  • tf.multinomial
  • tf.random_gamma
  • tf.set_random_seed

Constants, Sequences, and Random Values  |  TensorFlow

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 を使ったほうがよさそう。。。