TensorFlow を使ってブラックホールとポンデリングを見分ける (追記あり)

# はじめに

ハワイ、南米、南極など色々な箇所に設置された電波望遠鏡が協力し合う国際プロジェクト「イベント・ホライズン・テレスコープ」が、5500万光年彼方も向こうにある銀河の中心に位置する超大質量ブラックホールの撮影に成功したとの事。ブラックホールが直接撮影されたのは史上初の快挙との事で巷でも賑わっております。

https://japanese.engadget.com/2019/04/10/8/

さてこれだけ盛り上がると Twitter でも色々話題になっていて「ポンデリングに似ている」とか「ポンデリングじゃん」とか「ポンデリングにしか見えない」など色々と言われております。

# それポンデリングじゃねぇの？

これはもしかしてブラックホールを見た事のない我々に対して、ポンデリングの画像を見せられ騙されているのではないかとさえ思い始めました。Twitter 上にも数多くのポンデリング画像が散見されています。そこで TensorFlow の力を借りて「画像がブラックホールなのかポンデリングなのかを判別するモデル」を作ろうと思い立ちました。

とは言っても筆者は宗教上の理由により Python を書かない事にしているので今回の記事では一切 Python のコードは書きません。しかし便利な世の中になった物で、Python のコードを一切書かなくても機械学習を使った画像判別が出来る様になりました。

本記事ではそんな「誰でも簡単にできる画像判別」をやってみたいと思います。

# TensorFlow Hub とは

TensorFlow に関するモデル集的なもので、この中にサンプルとして画像分類の為のスクリプトが置いてあります。

[How to Retrain an Image Classifier for New Categories](https://www.tensorflow.org/hub/tutorials/image_retraining)

この例では花の画像を使っていますが、今回はこれをブラックホールおよびポンデリングの画像でやろうという話です。

# Python をインストールする

Python は書きませんが Python を使う必要があるので conda を使ってインストールします。筆者は Windows ユーザなので miniconda を使いました。

https://docs.conda.io/en/latest/miniconda.html

インストーラに従って進めると conda というコマンドが使える様になっています。以下のコマンドを使って tensorflow 専用の環境を作ります。

```
conda create --name=tensorenv python=3.7 tensorflow tensorflow_hub tensorboard
```

コマンドが終了したら以下のコマンドで tensorflow 専用環境にスイッチします。

```
activate tensorflow
```
UNIX だと `conda activate tensorflow` になります。必要な Python のライブラリは上記のコマンドによりインストールされているはずです。

# tensorflow_hub の使い方

tensorflow_hub のライブラリはインストールされているのでチュートリアルスクリプトをダウンロードします。

```
wget https://raw.githubusercontent.com/tensorflow/hub/master/examples/image_retraining/retrain.py
```

この `retrain.py` というスクリプトを使うと、一切 Python を書く事なく画像判別ができる様になるのです。

`retrain.py` を使う為には、カテゴリ毎に画像がフォルダ分けされたフォルダ構造が必要です。

```
─ images
 ├── blackhole
 │   ├── 0b92ef18217c6831.png
 │   ├── 0d46c5da430d4f6f.png
 │   ├──    …
 │   ├── 448604dd6ae451c8.png
 │   └── fb0c345e37e8b2b4.png
 └── pondering
     ├── 5f371bb5a81ce468.png
     ├── 6d405524581ee189.png
     ├──    …
     ├── 70ef4015d901ec2b.png
     └── f85b5aa49b2923a5.png
```

この図の様に、blackhole の中にブラックホールの画像を、pondering の中にポンデリングの画像を集めます。機械学習には多くの画像が必要なのですが、retrain.py では最低でも20枚を集める必要があります。

# とにかく画像を集めなさい

画像判別をするのであれば、とにかく画像を集めて下さい。

まずはブラックホール画像20枚

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/18305bba-98bd-ec3a-b1f7-9bec115c55e0.png)

そしてポンデリングの画像も20枚 [^1]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/358eb50f-d4ee-8f5d-18f9-5ce454b969d5.png)

これとは別に、検証に使う為の画像を数枚用意します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/9900b61f-99e6-2710-d8ba-585a0f508121.png)

# 学習しましょう

難しい事は必要ありません。以下のコマンドを実行して下さい。

```
python retrain.py --image_dir images --how_many_training_steps 1000
```

`--image_dir` にカテゴリ毎に分けられたディレクトリの親ディレクトリを、`--how_many_training_steps` にどれくらい学習させるかを指定します。画像枚数も少ないので1000回もあれば十分です。

実行したらしばらく時間が必要です。テレビを見るでもいいし、スマホでゲームするでもいいし、ちょっとオシャレに機械学習してる雰囲気を味わいたい人は tensorboard コマンドを起動するといいでしょう。

```
tensorboard --logdir /tmp/retrain_logs
```

`http://localhost:6006` にアクセスするとカッコいい画面が出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/27dd1192-9d43-3bf0-ca17-f3e4bd485d2a.png)

学習が終わると `/tmp/output_graph.pb` と `/tmp/output_labels.txt` が出来ているはずです。

# さぁ判別しよう

判別の為のスクリプトも既に良い物があります。Python は書かなくても構いません。

```
wget https://github.com/tensorflow/tensorflow/raw/master/tensorflow/examples/label_image/label_image.py
```

以下の様に実行します。

```
python label_image.py \
--graph /tmp/output_graph.pb --labels /tmp/output_labels.txt \
--input_layer Placeholder \
--output_layer final_result \
--image input.jpg
```

この `input.jpg` に判別したい画像を渡します。

![sample1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/c9c2ac45-66b4-8828-440b-8a6b47f2d8a5.jpeg)

```
pondering 0.9957397
blackhole 0.0042603137
```
ポンデリング

![sample2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/d475fc6c-8340-5884-20d9-512fb1cf9f12.jpeg)

```
pondering 0.99942964
blackhole 0.0005703046
```
ポンデリング

![sample3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/ab1aac37-8b07-8449-90c6-9e2bcd38c642.jpeg)

```
pondering 0.99130476
blackhole 0.008695241
```
ポンデリング

![sample4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/0c318fb4-f843-addc-d388-345b3e2e1da9.jpeg)

```
blackhole 0.99925417
pondering 0.00074583775
```

ブラックホール

![sample4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/52243280-c8d8-fabc-0740-59ddabf47cf0.jpeg)

```
blackhole 0.99878365
pondering 0.001216362
```

ブラックホール

いずれも高い確率で判別できている事が分かります。画像を集めている時はこれ数枚は間違うかもしれないと思ったりもしましたが、良い確率が出て良かったですね。

# おわりに

TensorFlow Hub を使って一切 Python を書かずに画像判別をやってみました。これだけ高い確率だと、「今日のオヤツはポンデリングよ」とお母さんがブラックホールを差し出してきても騙される事はないので安心ですね。良かったですね。

# 追記 2019/04/13

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/8c888db0-8563-b13c-d389-159e3c5fa763.png)

ブラックホール、ポンデリング、オニオンリングの3クラスで判別してみました。集めたオニオンリングの画像はこちら。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/8d98f379-afc6-4825-5e61-4e0eb3455cd8.png)

`images/onionring` ディレクトリに格納して再度学習しました。

<table>
<tr>
<td>
<img src="https://camo.qiitausercontent.com/9c149237f9df59b171fca82e570cc6ce7f348aba/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3636352f63396332616334352d363662342d383832382d343430622d3861366234376632643861352e6a706567" />
</td>
<td>
pondering 0.84498084<br />
onionring 0.14338708<br />
blackhole 0.011632051<br />
</td>
<td>
ポンデリング
</td>
</tr>

<tr>
<td>
<img src="https://camo.qiitausercontent.com/1823f56be097af2343309f7d881f09c788003fc4/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3636352f64343735666336632d383334302d353838342d323064392d3531326662316366396631322e6a706567" />
</td>
<td>
pondering 0.9974367<br />
onionring 0.001937004<br />
blackhole 0.00062626286<br />
</td>
<td>
ポンデリング
</td>
</tr>

<tr>
<td>
<img src="https://camo.qiitausercontent.com/f3797764c0221beca6d8fb2f351ff5019e2d0b8e/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3636352f61623161616333372d386230372d383434392d393063362d3965326263643338633634322e6a706567" />
</td>
<td>
pondering 0.9910465<br />
blackhole 0.0070514176<br />
onionring 0.0019021431<br />
</td>
<td>
ポンデリング
</td>
</tr>

<tr>
<td>
<img src="https://camo.qiitausercontent.com/d748bfd71e7b25d6253b53667243927577dfa816/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3636352f30633331386662342d663834332d616464632d643338382d3334356233653265316461392e6a706567" />
</td>
<td>
blackhole 0.99832636<br />
pondering 0.001540566<br />
onionring 0.00013305261<br />
</td>
<td>
ブラックホール
</td>
</tr>

<tr>
<td>
<img src="https://camo.qiitausercontent.com/504a5fcdc8b67902bab25e124702d232d10a0320/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3636352f35323234333238302d633864382d666162632d303734302d3539646461626634376366302e6a706567" />
</td>
<td>
blackhole 0.99802303<br />
pondering 0.0014418605<br />
onionring 0.0005351493<br />
</td>
<td>
ブラックホール
</td>
</tr>

<tr>
<td>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/0a23de82-fcde-264c-d75b-31e3875a961f.jpeg" />
</td>
<td>
onionring 0.9961351<br />
pondering 0.003439871<br />
blackhole 0.00042503988<br />
</td>
<td>
オニオンリング
</td>
</tr>

<tr>
<td>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665/69b50a61-2df8-3fac-7daa-ce153f99dfcb.jpeg" />
</td>
<td>
onionring 0.97317266<br />
pondering 0.02610269<br />
blackhole 0.0007246907<br />
</td>
<td>
オニオンリング
</td>
</tr>
</table>

オニオンリングを混ぜた事で正解率は若干さがりましたが間違いはありませんでした。これならば「今日のオヤツはポンデリングよ」とお母さんがオニオンリングを差し出してきても騙される事はないので安心ですね。良かったですね。

# 追記 2019/04/19

[mruby から TensorFlow Lite を操りブラックホールとポンデリングとオニオンフライを見分ける](https://qiita.com/mattn/items/0da60e9b133aad2632bf) という記事を書きました。コードは必要だけど面白いので遊んで下さい。

[^1]: 一部、ポンデリングでない物も含まれますが気にしてはいけません。本質を見失ってしまいます。