tensorflowjs+GANで誰でもギャルになれるWebアプリを作ってみた

![BeGyaruTranslate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178673/9fdf70e3-11bf-f106-5c3b-087645fa3040.png)
##1. はじめに

 こんにちは、大学生の[Nd](https://twitter.com/INT21184293)です。私は機械学習に興味があり、いろいろな人に遊んでもらえるツールが作れないものかと考えていました。そこで今回、だれでもギャルになれる[Begyaru](https://begyaru.com/)というサイトを作ってみました。本記事ではこのサイトの遊び方と使った技術の簡単な紹介をしていきます。

##目次
[1.はじめに](#1-はじめに)
[2.なにができるの？・どんな問題が解決できるの？](#2-なにができるのどんな問題が解決できるの)
[3.使い方](#3-使い方)
[4.サービスの仕組み](#4-サービスの仕組み)
[5.どういった仕組みで変換しているのか](#5-どういった仕組みで変換しているのか)
[6.tensorflowjsを使う際のポイント](#6-tensorflowjsを使う際のポイント)
[7.おわりに](#7-おわりに)

##2. なにができるの？・どんな問題が解決できるの？

みなさんはガングロギャルという単語をご存知でしょうか。ガングロギャルとは90年代から2000年代に流行った、真っ黒に日焼けして髪の色を金髪やピンクにするギャルたちのことです。 
<img width="40%" alt="ganguro_gal.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178673/3f2e79b8-d72a-0c79-51a5-cfcff8c1f9b6.png">
このガングロメイクですが、メイクに非常に手間がかかったり、紫外線で焼く場合は肌に大きな負担がかかる問題があります。そこで、今回作ったサイトでは、肌を焼いたりせずとも顔の画像をアップロードするだけでブラウザ上で顔検知および変換を行い、ガングロギャルになった姿を見ることができます。
また、今回作ったサイトでは個人情報の流出を防ぐことができます。

<img width="40%" alt="kojinjouhou_pc_lock.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178673/554c394b-4f2b-e109-d229-0260b73adcfb.png">
これはTensorflowjsというライブラリを使用してユーザのPCおよびスマホ上で画像の変換を行っているからです。言い換えると、アップロードした画像は外部に送信されないので、安心して自分の画像を使い遊ぶことができます。さらに、開発者にとっても、個人情報の流出という問題と推論を行うのに必要なサーバがいらなくなるという大きなメリットがあります。

##3. 使い方
[Begyaru](https://begyaru.com/)にアクセスすると以下のページが開かれると思うので、"ファイルを選択"のボタンをおして、<b><span style="color: #1464b3">正面を向いた一人だけ映っている画像</span></b>をアップロードしてください。変換が始まり5~20秒程度で変換できます。
![BegyaruFileButton.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178673/6e7281a0-a80a-70fa-566e-b5162974da47.png)


※Chrome, Firefox, Edgeでは動作確認しています。
※Windows, Android, Macでは動作確認できていますが、<span style="color: #ff0000">iPhoneだと動かない</span>場合があります。(iPhoneを持っていないのでデバッグができません...)
 

##4. サービスの仕組み

 サービスの仕組みについて以下の図を使いながら紹介します。
![BegyaruArchitecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178673/ad360c80-0d90-a2aa-6c70-ca9381a24cf9.png)


まず図の左上の水色の範囲をご覧ください。これはサイトのWebサーバを表しています。サイトのWebサーバには主に
・機械学習モデル
・推論用のjavascriptコード
が配置されています。まず、1つ目の機械学習モデルはこのサイトの核となるギャル変換を実現するためのモデルです。こちらは自分でデータを集めて学習させたモデルで、顔写真を入力すると出力がギャルとなって出てきます。2つ目の推論用のjavascriptコードは、ユーザのブラウザ上で機械学習モデルをロードし、推論するために必要となるソースコードです。主に画像の加工を行ったり、tensorflowjsの関数を呼び出したりします。
続いて左下はCDNサービスを表しており、このネットワークからTensorflowjsのライブラリやCSSフレームワークのBootstrapが提供されます。
そして最後に、右側のユーザ側では端末上のブラウザが先ほどのWebサーバとCDNから必要なファイルをロードし、サービスを利用できる状態となります。
##5. どういった仕組みで変換しているのか
変換には敵対的生成ネットワーク(GAN)という技術を使いました。以下の図を使って簡単な説明をします。
![GAN_shikumi.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178673/05f36b43-0163-afb3-9f64-025591a6b9f7.jpeg)

まずGANでは二人の主要キャラクターがいます。一人は左側のイラストレーターである生成器、もう一人は右側の鑑定士である識別器です。まずイラストレーターは普通の顔写真をもらってガングロギャルの絵に書き換えます。そして書き換えられた偽物の写真は鑑定士に渡されます。鑑定士は偽物と本物のガングロギャル写真がランダムに与えられ見抜けるように学習します。最初、イラストレーターは超初心者であるため、ガングロギャルとは思えない下手な画像を描いてきます。したがって鑑定士側は容易に本物か見抜けるわけですが、しばらくしているとイラストレーターは経験を積み鑑定士を騙すような本物そっくりの画像を送ってきます。そこで鑑定士側も負けじと以前より細かい部分まで特徴をチェックするようになります。このようにイラストレーターと鑑定士がお互いに敵対しあって学習を進めることで、最終的にイラストレーターは人間ですら本物と見間違うような本物そっくりのガングロギャルを描いてくれるまでに成長します。これが敵対的生成ネットワーク(GAN)の仕組みです。(さらに詳しい技術については「CycleGAN」で調べてみてください。)
##6. tensorflowjsを使う際のポイント
続いて、tensorflowjsを利用して気付いた点を紹介します。
<b>・学習させたモデルをtensorflowjsで扱えるモデルに変換する。</b>
学習済みのモデルはそのままではtensorflowjsで利用できないためtensorflowjs_converterを使って変換を行う必要があります。
kerasを利用した場合、モデルは以下のようにh5形式で保存します。

```python
model.save('my_model.h5')
```

この際、<b>モデル全体を保存する</b>ように気を付けてください。つまり

```python
model.save_weights('my_model_weights.h5')
```

は使わないようにしてください。これはsave_weights()を使うとモデルの構造が失われてしまい、tensorflowjsモデルに変換できなくなるためです。
続いて、実際にtensorflowjsモデルに変換していきます。まずtensorflowjsのインストールをします。

```python
pip install tensorflowjs
#成功すると"Successfully installed tensorflowjs-x.x.x"と表示される。
```

こちらが成功したらいよいよ変換です。以下のコマンドを実行してみましょう。

```python
tensorflowjs_converter --input_format=keras --output_format=tfjs_layers_model
 h5_model_file_path tfjs_model_output_path
```
コマンドオプションについて、`--input_format`はkerasを指定、`--output_format`はtfjs_layers_modelを指定します。続いて、h5ファイルのパスと、出力先のパスを指定してあげましょう。なお、この設定のままコマンドを実行すると32bitFloat型でモデルが保存されるのですが、サイズを小さくしたい場合は`--quantize_uint16`や`--quantize_uint8`オプションを利用してみましょう。多少推論精度が落ちてしまいますが、半分や1/4の大きさまで圧縮することができます。ほかにもいろいろなオプションがあるので、

```python
tensorflowjs_converter --help
```
で適宜自分の使いたい機能を利用していきましょう。

<b>・バックエンドの指定</b>
tensorflowjsでは使用できるバックエンドが`cpu`,`wasm`,`webgl`の中から選択できますが、極力`webgl`を利用するようにしましょう。

```javascript
tf.setBackend('webgl');
```
WebGLを利用することでGPUを利用することができ、cpuバックエンドと比較して10倍近く速い推論が実現できます。もしWebGL Backendが無理な場合はWebAssembly(`wasm`)を利用しましょう。

<b>・Tensorflowjsでは自分でメモリ開放をする必要がある</b>
pythonでtensorflowやnumpyを利用したとき、あまりメモリ開放について考えることはないでしょう。ですがtensorflowjsでは既に使った配列(テンソル)はメモリ開放してあげないといけません。
例えば以下のような場合

```javascript
//まずモデルをロードする
const model = await tf.loadLayersModel(model_path);
//tensorを入力し推論結果をpredictionに代入
const prediction = await model.predict(tensor);

//なんらかの処理
//predictionは利用済み
tf.dispose(prediction); //predictionを破棄し、メモリ開放する。
```
最後の処理のように`tf.dispose()`関数を利用してメモリ開放してあげてください。

##7. おわりに
ブログを書くのは初めてだったのですが、作ったツールについて紹介することができたので良かったです。
ブログの内容に関する指摘や実際にツールを使ってみた感想等はTwitterや以下のコメント欄にお寄せください。