Google DatalabコンテナにElixirを入れてTensorflexを動かそう

（この記事は、「[fukuoka.ex(その2) Elixir Advent Calendar 2017](https://adventar.org/calendars/2871")」の24日目、および[「ディープラーニングのエンジニアリング Advent Calendar 2017」](https://qiita.com/advent-calendar/2017/deep-learning-engineering)の6日目です）

昨日は、@tuchiroさんの[ElixirでSI開発入門 #9 Railsからのモデルの移行2(DDLをパースする)](https://qiita.com/tuchiro/items/07bc2ddb58812c62da15)でした

前回までElixirからRustlerを経由して外部言語インターフェースのさまざまな可能性を探ってきました。
今回はGCPの公式コンテナである「Cloud Datalab」にElixirをインストールして、ElixirのTensorFlowバインディングである「Tensorflex」を試してみます。

前回までの記事は、以下になります
[ElixirのGenStageに入門する #1](https://qiita.com/twinbee/items/12a61863ceef794996e0)
|> [ElixirのGenStageに入門する#2 バックプレッシャーを理解する](https://qiita.com/twinbee/items/eda7f70fa9e4651f2248)
|> [Elixir並列処理「Flow」の2段ステージ構造を理解する](https://qiita.com/twinbee/items/43b767520da666ee960d)
|> [Elixirから簡単にRustを呼び出せるRustler #1 準備編](https://qiita.com/twinbee/items/aabc11d0d667800fc0bb)
|> [Elixirから簡単にRustを呼び出せるRustler #2 クレートを使ってみる](https://qiita.com/twinbee/items/54e8a4ec73bc27abd10e)
|> [Elixirから簡単にRustを呼び出せるRustler #3 いろいろな型を呼び出す](https://qiita.com/twinbee/items/f94eb7f74ff39c781da0)
|> [Elixirから簡単にRustを呼び出せるRustler #4 SHIFT-JIS変換を行う](https://qiita.com/twinbee/items/75415203e44daee16fcc)
|> [Elixirから簡単にRustを呼び出せるRustler #5 NIFからメッセージを返す](https://qiita.com/twinbee/items/cd818fd509ace2ae0d0a)

---

4/27から、44日間に渡り、毎日お届けしている[「季節外れのfukuoka.ex Elixir Advent Calendar」](https://qiita.com/advent-calendar/2017/elixir-or-phoenix)と[「季節外れのfukuoka.ex(その2) Elixir Advent Calender」](https://adventar.org/calendars/2871)ですが、[Qiitaトップページトレンドランキング](https://qiita.com/trend)に13回入賞、[Elixirウィークリーランキング](https://qiita.com/tags/elixir)では7週連続で１／２／３フィニッシュを飾り、各種ランキング通算で、<font color="red"><b>トータル87回ものランクイン</b></font>を達成しています

みなさまの暖かい応援に励まされ、<font color="red"><b>合計616件ものQiita「いいね」</b></font>もいただき、fukuoka.exアドバイザーズとfukuoka.exキャストの一同、ますます季節外れのfukuoka.ex Advent Calendar、頑張っていきます:rocket:

[![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/bbe9d349-c978-e9e8-ba7d-f8feda74be91.png)](https://adventar.org/calendars/2871)

----

## オールインワン機械学習Dockerコンテナ

Googleによって提供される[Colud Datalab](https://cloud.google.com/datalab/)はTensorFlowに加え、Python2/3+Jupytor Notebook+Python向け機械学習用プラグインが入った**オールインワン**のコンテナです。**TensorFlowは最新版にメンテされてます**。

既に環境構築が終わっている方でも、TensorFlowの新しめのAPIであるEstimator、KerasAPIや、Tensorfow-hubなどの機能を試すこともできます。

Elixirに興味がない方でも、のローカル環境への**Datalab１行構築**は必見なので是非見て行ってください。

このコンテナはGCP上にデプロイすれば、煩雑なGPUドライバーのインストールも不必要なうえ、BigQueryやCloud Storageが統合された高度な環境をすぐに使い始めることができます。[(参考：TensorFlowのGPU環境をGCPでさくっと作る方法)](https://qiita.com/kazunori279/items/b17d16a2bfbec5e72984)

このDatalab。実は、ローカルのDockerにもインストールできる優れものなんです。(今回GPUが使えるかどうかは不明)

一般的な開発に機械学習用の環境を整備しようとすると、まずPythonのバージョンが問題になってきます。PythonはNode.jsのCコンパイル環境で使われていたりするので、**バージョン管理が必須**となってきます。パッケージのアップデートも頻繁なので、迂闊にアップデートしてしまうと、pythonが起動しないなんてことも、多々あります。

Datalabを使えば、そのような煩雑な管理から解き放たれるのです。

**お金がかからない**ローカルの環境にオールインワンの環境を作りましょう。

後半では、Elixir界隈で話題となったElixirのTensorFlow bindingsである「Tensorflex」をこのコンテナにインストールして動かしてみます。さて、いかなるものなのでしょうか？

## Docker環境

まずは、Docker環境が必要です。
Dockerについてはここでは扱いませんので、各OSに合わせたインストールをお願いします。


## ローカルインストール

以下長々と書いてはいますが、実際**実行するのは1行のみです**。安心して読み進めてください。

※筆者はWindows 10 Home EditionでDocker Toolboxを使用しています。
実はDocker Toolboxはサポート外なのですが、今回の使用範囲では問題がなかったことをご報告しておきます。


では、始めましょう。

まずはターミナルを立ち上げます。(Win10 Home Editionの方は`Docker Quickstart Terminal`)

以下のコマンドを入れます。`<PROJECT_ID>`はローカルにインストールする場合は、任意の英数字を入れて下さい。

```bash
docker run -it -p "127.0.0.1:8081:8080" -v "${HOME}:/content" \
  -e "PROJECT_ID=<PROJECT_ID>" \
  gcr.io/cloud-datalab/datalab:local

```

- Win10 Home Edition (Docker Toolbox)利用の場合は

```bash
docker run -it -p "8081:8080" -v "${HOME}:/content" \
  -e "PROJECT_ID=<PROJECT_ID>" \
  gcr.io/cloud-datalab/datalab:local
```

※ [Datalab公式のローカルインストール方法はこちらになります]
(https://cloud.google.com/datalab/docs/quickstarts/quickstart-local?hl=ja)


### フォルダーの指定について(Windowsの場合)

Windowsでマイドキュメントの配下をコンテナと共有したい方だけ、ここをお読み下さい。

Windowsでマイドキュメントの内にdatalabフォルダを作って、コンテナと共有したい場合は次のようにします。共有フォルダの場所は、作業効率に地味に影響しますので、Dockerに慣れたら吟味しましょう。

```
export MYFOLDER=${HOME}/My\ Documents/datalab
```

Dockerのオプション

```
-v "${MYFOLDER}:/content"
```

---

### Jupytor NoteBookにつないでみる

しばらく待って、以下のようなメッセージが出たら、コンテナの起動完了です。

```
Open your browser to http://localhost:8081/ to connect to Datalab.
```

次のような画面が現れたら、インストール成功です！
**Dockerだと、１行で終了ですね！**

このまま、Jupytor notebook(IPython)としてお使いいただけます。

Notebookの内容も、共有フォルダに書き込まれるので、コンテナを終了しても作業内容は残りますので、ご安心下さい。

ブラウザから`http://localhost:8081/`でつないでみてください。
(Docker toolboxの場合は`http://192.168.99.100:8081/`)

![datalab-init.png](https://qiita-image-store.s3.amazonaws.com/0/66748/7e0b385a-825b-f592-8bd8-9c0b88bf30e0.png)

うまく、つながったでしょうか？
次のセクションからは、Elixirのインストールになるので、ご興味がない方は[最終章](#コンテナの終了)をご覧ください。

---

## Datalabコンテナに侵入する

コンテナが無事起動できたところで、今度はElixirをインストールすべく、コンテナに侵入します。

- 通常の環境の場合

 Ctrl+p を押して Ctrl+q を押す

- Docker Toolboxの場合

 Toolboxで上記Ctrl+pqをやると、ターミナルが固まってしまいます。
　`Docker Quickstart Terminal`で別のシェルを立ち上げます。

`docker ps`コマンドで、datalabコンテナが確認できます。

```bash
docker ps
```


![Datalab Docker PS .png](https://qiita-image-store.s3.amazonaws.com/0/66748/2294f85a-d7a1-a215-718b-4e51748b5551.png)

DatalabのコンテナIDの2桁(他のIDと区別できる最小文字数)を控えて、次のコマンドを実行して下さい。上記コンテナでは**9b**なので、以下のようにします。

```bash
docker exec -it 9b bash
```

これは、コンテナの中に新たにbashを立ち上げて、コンソールで操作を行うという意味です。

コンテナへの侵入が成功すると、次のようになります。
![Docker exec entered.png](https://qiita-image-store.s3.amazonaws.com/0/66748/f6aa7b58-259f-cbf8-adb3-1955b1359147.png)

「/contentフォルダ以外の変更はpipやapt-getを含めコンテナを終了したら無効になりますよ」という親切な案内です。

---

## コンテナ探索

DatalabコンテナのOSはubuntu 16.04です。
anacondaが入っているので、環境を見てみましょう。

```bash
/content> conda env list
# conda environments:
#
base                  *  /usr/local
py2env                   /usr/local/envs/py2env
py3env                   /usr/local/envs/py3env
```

`pyenv2`と`pyenv3`で環境の切り替えが可能なことがわかります。
sourceコマンドで環境を有効化できます。

```bash
/content> source activate py3env
(py3env) /content> pip3 freeze
```

以下抜粋ですが、メジャーどころのパッケージはほぼ入ってます。

```
matplotlib==2.1.2
numpy==1.14.0
pandas==0.22.0
scipy==1.0.0
scikit-learn==0.19.1
seaborn==0.7.0
tensorflow==1.8.0
```

---

## Elixirをインストールする

[Elixir公式サイト](https://elixir-lang.org/install.html)の手順どおりでubuntu用のelixirをインストールします。ルートユーザーなのでsudoは外す必要があります。

インストールの手順は以下の通り、コマンドを流すだけです。
esl-erlangのインストールで追加で359MBストレージを使う確認が出てきますが、大文字で「Y」を入力してください。

```bash
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && dpkg -i erlang-solutions_1.0_all.deb

apt-get update
apt-get install esl-erlang
apt-get install elixir
```

iexでこのように表示されれば、インストール成功です。Ctrl+\で抜けましょう。

```
(py3env) /content> iex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.6.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
```

---

### Tensorflow C bindingsのインストール

次は、Tensorflexを動かすためにTensorflow C bindingsを入れます。
以下は`sudo`を取り除いてあります。

```
TF_TYPE="cpu"
OS="linux"
TARGET_DIRECTORY="/usr/local"
curl -L \
  "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-${TF_TYPE}-${OS}-x86_64-1.8.0.tar.gz" |
  tar -C $TARGET_DIRECTORY -xz
```

インストールが成功したら、ldconfigを実行しておきましょう。

```bash
ldconfig
```

[公式サイトのインストール手順はこちら](https://www.tensorflow.org/install/install_c)


### Tensorflexのインストール


TensorflexはTensorFlowをElixirから使うライブラリとして、あるカンファレンスのスライドで取り上げられたものです。ここでは、Tensorflexの実行環境を作ります。


#### ※ Tensorflex version 0.1.2現在 

`mix.exs`のdepsに書きを追加してください。
```elixir:mix.exs
{:tensorflex, "~> 0.1.2"}
```

シェルから次のコマンドを実行すれば、インストール完了です。
```
mix deps.get
```

#### ※ 以前のやり方

まず、tensorflexのリポジトリをクローンします。

```
git clone https://github.com/anshuman23/tensorflex.git

cd tensorflex
```

Makefile内の`ERLANG_PATH`がデフォルトでは7.3になっています。

```makefile
ERLANG_PATH = /usr/lib/erlang/erts-7.3/include
```

sedで9.3に書き換えます

```
sed -i -e 's/erts-7.3/erts-9.3/' Makefile
```

あとは

```
mix deps.get
mix compile
```
でインストール完了です。

### 動かしてみる

早速対話環境で実行してみましょう。

```elixir
$ iex -S mix

Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:10] [hipe] [kernel-poll:false]

make: 'priv/Tensorflex.so' is up to date.
Interactive Elixir (1.6.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> m = Tensorflex.create_matrix(2,3,[[2.2,1.3,44.5],[5.5,6.1,3.333]])
#Reference<0.343416744.1264451585.214396>
iex(2)> Tensorflex.matrix_pos(m,2,1)
5.5
iex(3)> Tensorflex.size_of_matrix m
{2, 3}
iex(4)>
```

無事実行できました。

※ 実行時に以下のエラーが出る場合は、
`deps\priv\Tensorflex.so`を削除して`cd deps\tensorflex`でディレクトリ移動後`make`を実行して、soファイルを再ビルドして下さい。
```
21:49:42.478 [warn]  The on_load function for module Elixir.Tensorflex.NIFs returned:
{:error, {:bad_lib, 'Library version (2.14) not compatible (with 2.13).'}}
```

Tensorflexのソースをひと通り眺めたところ、現在出来ることは、README.mdに書いてあることが全てのようです。つい数日前にSessionが実装されました。もともと、静的グラフデータのロードは出来ていたのでSessionが走るのは大きな進歩でしょう。 低レベルのAPIを中心に着実に機能が増えつつあります。

Elixirの作者José Valimさんとも頻繁にコミュニケーションを取ってるようで、今後の機能充実に期待です。

**2018/8/21追記** - v0.1.2では、**jpgファイルのロードが可能**になり、Inception-v3を使った分類が可能になっています。

ElixirのGPU対応といえば、fukuoka.exコアメンバーの @zacky1972 氏こと、北九州市立大学の山崎進准教授のOpenCL対応も忘れてはいけません。ElixirからRustでGPU駆動した記事を改めてご紹介しておきます。
[「ZEAM開発ログv0.1.6 Elixir から Rustler で GPU を駆動しよう〜ElixirでAI/MLを高速化」](https://qiita.com/zacky1972/items/c998cad40ff9f2a42fde)

---
## コンテナのElixir環境を保存する

Elixirの作業環境を保存しておきたい方は、以下を進めてください。
`Ctrl+\`でElixirを抜け、exitでコンテナから抜けます。

下記のコマンドで、「datalab-elixir:latest」をタグ名を付けてDocker Imageを保存することができます。 ※コンテナのhashは上記の例でいけば`9b`となります。

```
docker commit [コンテナのhashの一部] datalab-elixir:latest
```

次回からは、以下のコマンドでElixir入りの機械学習環境を楽しむことができます。

```
docker run -it -p "8081:8080" -v "${HOME}:/content" \
  -e "PROJECT_ID=<PROJECT_ID>" \
  datalab-elixir:latest
```


## コンテナの終了

Elixirのインストールをしてない場合は、`Open your browser`の表示のところでCtrl+Cを押すとコンテナ終了が終了します。

Docker Toolbox利用の場合は、exitで`docker exec`を行ったコンテナを抜けてから、上記を行ってください。

今回の設定の場合、コンテナは使い終わっても破棄されないため、`docker ps -a`で止まっているコンテナを確認して、`docker rm`で削除しておきましょう。

```
docker ps -a
docker rm 9b
```


---
## 終わりに

Datalabでの環境構築いかがだったでしょうか。皆様の開発にお役にたてれば幸いです。

明日は @takasehideki さんの[**「ElixirでIoT#5：関数型言語からGroveモジュールを使ってみた」**](https://qiita.com/takasehideki/items/bc6051a86fe1ac8f4464)になります。