Elixirでディープラーニング②：Nxのdefn＋XLAでGPUを動か…せなかった(T_T)


[Elixir Digitalization Implementors／fukuoka.ex／kokura.ex](https://fukuokaex.connpass.com/)のpiacereです
ご覧いただいて、ありがとうございます :bow:

引き続き、Elixirの2021年を占うポジションであるニューラルネットワーク／ディープラーニング用ライブラリ「Nx」についてです

[前回は、Nxの導入](https://qiita.com/piacerex/items/db33fbe80751fdd30e48)を行ったので、今回は、Nxで新しく入った構文「defn」で行列処理専用関数を作ったり、Nxで提供されるTensorFlow用コンパイラ「XLA（Accelerated Linear Algebra）」バインディングを通じたGPU駆動を試していきます

なおNxは、昨日（2021年2月18日）出たばかりのライブラリで、hexdocも未だ無いような状態ですが、ソースコード中のdoctestを参考にしながら実験していきます

<b>Nxソースコード</b>
https://github.com/elixir-nx/nx/tree/main/nx/lib

内容が、面白かったり、役に立ったら、「LGTM」よろしくお願いします :wink:

<h3>:ocean::ocean::ocean: Advent Calendar、fukuoka.ex1位、Elixir2位達成ヽ(=´▽`=)ﾉ :ocean::ocean::ocean:</h3>

fukuoka.ex Advent Calendar、Webテクノロジーカテゴリで堂々1位 … 各コラムぜひお読みください
https://qiita.com/advent-calendar/2020/fukuokaex
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/8a22a81f-2917-6072-10bd-525e0fc24b75.png)](https://qiita.com/advent-calendar/2020/neosvr_reso)

そして、プログラミング言語カテゴリは、1位がRust、2位がElixir、3位がGoとモダン言語揃い踏みでのトップ3、熱いネー:laughing:
https://qiita.com/advent-calendar/2020/elixir
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/ea185203-c525-c778-dc10-6d2ecc391616.png)](https://qiita.com/advent-calendar/2020/elixir)

# 本コラムの検証環境

本コラムは、以下環境で検証しています（Windowsで実施していますが、Linuxやmacでも動作する想定です）

- Windows 10
- WSL2（配下はWSL2上で導入）　※<b>[最新版のインストール手順はコチラ](a)</b>
 - Ubuntu 18.04 LTS
 - Erlang/OTP 23.2.3-1（erts-11.1.7）
 - Elixir 1.11.2
- git 2.30.1.windows.1　　　　　　※<b>[最新版のインストール手順はコチラ](https://qiita.com/piacerex/items/1a9cbcc740ca3707eaec#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%ABpc%E3%81%A7git%E3%82%92%E4%BD%BF%E3%81%88%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B)</b>

あと下記コラムシリーズの続きとして実施するので、未実施であれば、事前に実施しておいてください

|> [<b>Elixirでディープラーニング①：革新的ライブラリ「Nx」の導入</b>](https://qiita.com/piacerex/items/db33fbe80751fdd30e48)

# Nxの「defn」で行列処理専用関数を作成

前回はiexで直接Nxを使いましたが、今度は行列処理関数のための新しい構文（マクロ）である「defn」を使って、softmax関数を作ってみます

```elixir:lib/nx_review.ex
defmodule NxReview do
  import Nx.Defn

  defn softmax( matrix ) do
    Nx.exp( matrix ) / Nx.sum( Nx.exp( matrix ) )
  end
end
```

このdefn関数を呼び出すときは、こんな感じです

```elixir
iex> NxReview.softmax( Nx.tensor( [ [ 0.123, 0.456 ], [ 0.789, 1.234 ] ] ) )
#Nx.Tensor<
  f64[2][2]
  [
    [0.135520130365152, 0.18907054376721744],
    [0.26378125835705013, 0.4116280675105804]
  ]
>
```

パイプを使うことも、当然できます

なんていうか、こんな形で行列処理できるって、もう、この先、イイ予感しかしませんよね:wink:

```elixir
iex> Nx.tensor( [ [ 0.123, 0.456 ], [ 0.789, 1.234 ] ] ) |> NxReview.softmax
#Nx.Tensor<
  f64[2][2]
  [
    [0.135520130365152, 0.18907054376721744],
    [0.26378125835705013, 0.4116280675105804]
  ]
>
```

ちなみに、``Nx.tensor``で作られた行列では無く、中身のリストとかを渡すと、こんな感じで怒られます … defnが、数値か行列（テンソル）しか扱えないことが分かります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/fe82e83d-6c97-7774-1b4d-d9c5a7b32bf7.png)

# Nxで提供される「XLA」バインディングを動かす

Nxでは、「XLA（Accelerated Linear Algebra）」と呼ばれる、線形代数のためのコンパイラのバインディングが含まれているため、Python同様、XLAによるTensorFlowモデルとGPUの操作が可能です

なお、XLAが何者かについての詳細は、下記スライドが分かりやすいです

<b>TensorFlow XLAの可能性</b>
https://www.slideshare.net/ssuser479fa3/tensorflow-xla-78874656

さて、ここも前回同様、結構トラブってます … 恐らくは、Windows以外の環境でもコケると思われるのですが、未検証なので、検証していただける方、本コラムを参考にしながら、よろしくお願いします（私メンションを付けてくれたら嬉しい）

## EXLAでGPUを動かすコード

EXLAを使ったコードは、下記の通り、``@defn_compiler {EXLA, client: :host}``の1行が関数に追加されるだけです

たった、これだけでGPUが利用可能になります

```elixir:lib/nx_review.ex
defmodule NxReview do
  import Nx.Defn

  @defn_compiler {EXLA, client: :host}
  defn softmax( matrix ) do
    Nx.exp( matrix ) / Nx.sum( Nx.exp( matrix ) )
  end
end
```

以降では、このコードをビルドして動かすための準備をします

## EXLAのライブラリ追加

では、[EXLAのREADME（下記）](https://github.com/elixir-nx/nx/tree/main/exla)の通りに導入していきます
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/b8e4fb5a-f417-63d6-e01c-f1be8c846efe.png)](https://github.com/elixir-nx/nx/tree/main/exla)

まず、EXLAをdepsに追加し、Nxの方も、``override: true``を追記します

```elixir:mix.exs
…
  defp deps do
    [
      {:exla, "~> 0.1.0-dev", github: "elixir-nx/nx", sparse: "exla"},
      {:nx, "~> 0.1.0-dev", github: "elixir-nx/nx", sparse: "nx", override: true}
    ]
…
```

## gcc等のビルドツールのインストール

Ubuntuへのインストール手順と同じでOKなので、[下記コラムの「ビルドツールをインストール」](https://qiita.com/piacerex/items/98e1bde676263f5f9f81#%E3%83%93%E3%83%AB%E3%83%89%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)のところをご参考ください

<b>WindowsからElixir IoT端末を作ってみた：「Raspberry Pi OS」を入れた後、Elixir IoTフレームワーク「Nerves」へ</b>
https://qiita.com/piacerex/items/98e1bde676263f5f9f81

## XLA用ビルドツール「bazel」のインストール

次に、[bazelの公式サイトにあるUbuntuインストール手順](https://docs.bazel.build/versions/4.0.0/install-ubuntu.html)でインストールします

ちなみにbazelは、Google製のビルドツールで、makeを置き換えるような存在です（って言う触れ込みのツール、この25年で幾つ見てきただろう？ … って感じですがｗ）

```
$ sudo apt install gnupg
$ curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
$ sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
$ echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
$ sudo apt update
$ sudo apt install bazel -y
```

## Python系のインストール

それから、Python／pip／NumPyをインストールします

```
$ sudo apt install python3.8 -y
$ sudo apt install python-pip
$ pip install numpy
$ which python
/usr/bin/python
$ export PYTHON_BIN_PATH=/usr/bin/python
```

## EXLA付きElixir PJをビルドすると…

必要ツールはインストールしたので、ビルドしてみるとエラー … bazelのバージョンが違うと怒られました

```
$ iex -S mix
…
==> exla
rm -f /home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/tensorflow/compiler/xla/exla
ln -s "/mnt/c/piacere/site/nx_review/deps/exla/exla/c_src/exla" /home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/tensorflow/compiler/xla/exla
rm -f c_src/exla/erts
ln -s "/usr/lib/erlang/erts-11.1.7/include" c_src/exla/erts
cd /home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96 && \
        bazel build --define "framework_shared_object=false" -c opt   //tensorflow/compiler/xla/exla:libexla.so
ERROR: The project you're trying to build requires Bazel 3.1.0 (specified in /home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/.bazelversion), but it wasn't found in /usr/bin.

You can install the required Bazel version via apt:
  sudo apt update && sudo apt install bazel-3.1.0

If this doesn't work, check Bazel's installation instructions for help:
  https://docs.bazel.build/versions/master/install-ubuntu.html
Makefile:25: recipe for target 'all' failed
make: *** [all] Error 1
could not compile dependency :exla, "mix compile" failed. You can recompile this dependency with "mix deps.compile exla", update it with "mix deps.update exla" or clean it with "mix deps.clean exla"
==> nx_review
** (Mix) Could not compile with "make" (exit status: 2).
You need to have gcc and make installed. If you are using
Ubuntu or any other Debian-based system, install the packages
"build-essential". Also install "erlang-dev" package if not
included in your Erlang/OTP version. If you're on Fedora, run
"dnf group install 'Development Tools'".
```

bazel 3.1.0を改めてインストールし直します（EXLAのREADMEには、bazel-3.7.xと書いてあるけど、エラーメッセージがきっと正しいでしょう）

```
$ sudo apt remove bazel -y
$ sudo apt install bazel-3.1.0 -y
```

bazelは、[手順内に書いている](https://docs.bazel.build/versions/master/install-ubuntu.html)通り、バージョン指定してインストールすると、コマンドが``bazel-3.1.0``のような感じでインストールされるので、シンボリックリンクを作ります

```
sudo ln -s /usr/bin/bazel-3.1.0 /usr/bin/bazel
```

改めてビルドすると、またエラー … ``local_config_git``リポジトリのフェッチ中？

```
$ iex -S mix
…
==> exla
…
ERROR: An error occurred during the fetch of repository 'local_config_git':
   Traceback (most recent call last):
        File "/home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/third_party/git/git_configure.bzl", line 64
                _fail(result.stderr)
        File "/home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/third_party/git/git_configure.bzl", line 14, in _fail
                fail(<1 more arguments>)
…
ERROR: /home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/tensorflow/core/util/BUILD:368:1: //tensorflow/core/util:version_info_gen depends on @local_config_git//:gen/branch_ref in repository @local_config_git which failed to fetch. no such package '@local_config_git//': Traceback (most recent call last):
        File "/home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/third_party/git/git_configure.bzl", line 64
                _fail(result.stderr)
        File "/home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/third_party/git/git_configure.bzl", line 14, in _fail
                fail(<1 more arguments>)
Git Configuration Error: Traceback (most recent call last):
  File "/home/piacere/.cache/bazel/_bazel_piacere/bd39af2ecb695c5256163d02cb0ae088/external/org_tensorflow/tensorflow/tools/git/gen_git_source.py", line 32, in <module>
    from builtins import bytes  # pylint: disable=redefined-builtin
ImportError: No module named builtins
…
```

エラーメッセージの中身をよく見ていくと、``from builtins import bytes``、これはPython 2.x系で書かれたコードが、Python 3.x系で厳格に定義されたbytesで通らないときに使う「future」という、Python 2.x系とPython 3.x系のギャップを埋め合わせるPythonパッケージを使うときのimportで、このモジュールが無いと言われてます

futureは、自分で入れないと入らないハズなのでインストールします

（昔、Python 2.x系コードを、Python 3.x系に移行するタスクを結構やってたので、こんなところで役に立った）

```
$ pip install --upgrade setuptools
$ pip install future
```

改めてビルドすると、今度は成功しました（ちなみにビルド時間は結構かかります）

```
$ iex -S mix
…
==> exla
…
Target //tensorflow/compiler/xla/exla:libexla.so up-to-date:
  bazel-bin/tensorflow/compiler/xla/exla/libexla.so
INFO: Elapsed time: 1462.841s, Critical Path: 140.02s
INFO: 5790 processes: 5790 local.
INFO: Build completed successfully, 6134 total actions
INFO: Build completed successfully, 6134 total actions
mkdir -p priv
cp -f /home/piacere/.cache/exla/tf-6af836f407f546cf2f9ab3b5fcb7a8285bda5c96/bazel-bin/tensorflow/compiler/xla/exla/libexla.so priv/libexla.so
Compiling 16 files (.ex)
Generated exla app
==> nx_review
Compiling 12 files (.ex)
Generated nx_review app
```

## NVIDIAドライバー／CUDAのインストール

下記コラムの通り、WSL2でGPUを使うための導入を行います

<b>待ってました CUDA on WSL 2</b>
https://qiita.com/ksasaki/items/ee864abd74f95fea1efa

そしてココで、Windows 10 Insider Previewのインストール完了に、一晩待たなければならないということをスッカリ忘れてて、手詰まりました … 強制的に、また明日:sob:

<!--
## cuDNNのインストール

```
$ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
$ sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
$ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
$ sudo add-apt-repository "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/ /"
$ sudo apt update 
$ sudo apt-get install libcudnn8 -y
```

## EXLA付きのdefn関数を試す

それでは、EXLA付きのdefn関数を試してみましょう

```elixir:lib/nx_review.ex
defmodule NxReview do
  import Nx.Defn

  @defn_compiler {EXLA, client: :host}
  defn softmax( matrix ) do
    Nx.exp( matrix ) / Nx.sum( Nx.exp( matrix ) )
  end
end
```

実行してみましょう

```elixir
iex> Nx.tensor( [ [ 0.123, 0.456 ], [ 0.789, 1.234 ] ] ) |> NxReview.softmax
#Nx.Tensor<
  f64[2][2]
  [
    [0.135520130365152, 0.18907054376721744],
    [0.26378125835705013, 0.4116280675105804]
  ]
>
```
-->

# 最後に

ニューラルネットワーク／ディープラーニング用ライブラリ「Nx」で、defnによる行列処理専用関数を作ったり、XLAバインディングのインストールを行いました

今回は、EXLAのインストールで苦戦し、Windows 10 Insider Previewのインストール待ちで立ち止まり、残念ながら、GPU駆動とニューラルネットワーク開発まで辿り着けませんでした

なので[次回こそは、<strike>NxでGPU駆動を完了させ、ニューラルネットワーク開発に入りたい</strike> Elixir創始者José ValimからWindows版Nxの修正版の連絡を直接もらったのでその対応](https://qiita.com/piacerex/items/d6d207deeda6d2a894d5)です

# p.s.このコラムが、面白かったり、役に立ったら…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/67568d54-6584-ad33-77c7-b3b3afe70287.png) や ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/f5b819ef-d655-ef72-294c-b1a9d2c5b563.png) にて、どうぞ応援よろしくお願いします:bow: