PyTorchことはじめ

# PyTorch

[PyTorch](https://pytorch.org/): ニューラルネットワークの計算が出来る、深層学習フレームワーク。

PyTorchの環境を構築するためのインストールまでの手続きについてまとめた。(簡単な使いかたもいずれ書くかも)

# PyTorchのインストール

## pyenv + anaconda3-5.3.1 on CentOS Linux release 7.7.1908 (Core)
いつも、Linuxにはpyenvを導入し、anaconda3-5.3.1を入れて使っている。本家のページを見ると、`conda`コマンドを使っても入れられるらしく、例えば

```bash
# 本家のサイトで Stable(1.5), Linux, Conda, Python, None (cudaのオプション) で表示されるコマンド
# 最新版を入れたければ、本家サイトでコマンドは生成し直した方がよい
conda install pytorch torchvision cpuonly -c pytorch
```
でPyTorchが入る。
但し、この手続きでPyTorchを入れると、少なくとも私の環境(CentOS Linux release 7.7.1908 (Core))では、<font color="Red">anaconda内のipython, jupyterの環境を壊してしまう。</font>本末転倒である。したがって、他の道を探った。

## pyenv + 3.8.2 on CentOS Linux release 7.7.1908 (Core)
本家のページを見ると、`pip`でも入れられるとある。それなら、という事で、簡素なpythonを入れて、そこから`pip`で足していくことにした。
途中経過には興味がなく、とにかくインストールできればいい という人は、このサブセクションの最後のまとめへ。

pyenvを入れて(入れ方は'pyenv インストール'で調べれば無限に記事が見つかるので、それらを参照)、PATHを通す(無限にある記事の中に書かれている)。`pyenv install --list`で調べると、手持ちのpyenvでは3.8.2が最新だったので、これを選択することに。

```bash
pyenv install 3.8.2
```
でインストールを試みると、**インストールができない。**エラー、ワーニング(後々ビルドし直すのもだるいので、ワーニングもコスパの許す限り対応する)を確認すると、どうも`zlib`, `ssl`, `bzip2`, `realine`, `SQlite`が足りないよう。これよりも、前に`gcc`, `g++`, `gfortran`くらいは入れていたはずだけど、他にも既に入れたアプリ/ライブラリがあったかも。(Anacondaはシステムのライブラリを参照しないから、これまでインストールには問題なかったのかな。よくわからない。)これらのライブラリ群をインストールしようとして、`yum install zlib`等とすると、既にあると表示される。つまり、必要なのは`zlib-dev`なのだ。これは他のライブラリについても、大方そうである。なので、

```bash
yum install zlib-* 　  #zlib-*でzlib-dev等を入れる
yum install openssl-*
yum install bzip2-*
yum install readline-*
yum install sqlite-*
```
をしてから、上記のpythonインストールコマンドを入れると、無事にビルドされた。
anacondaしか使ったことなかったので、pyenvって圧縮されたバイナリを展開しているだけだと思ったけれど、ビルドしているのね。あと、`*-dev`が必要という事は、静的ライブラリをビルド時にリンクしているのかもしれない。

さぁ、そういうわけで

```bash
# 本家のサイトで Stable(1.5), Linux, Pip, Python, None (cudaのオプション) で表示されるコマンド
# 最新版を入れたければ、本家サイトでコマンドは生成し直した方がよい
pip install torch==1.5.0+cpu torchvision==0.6.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
```
で晴れてPyTorchが入るかな と思うと、コケる。どうも`ctypes`が上手く読み込めない とのこと。試しに`python3`から`import ctypes`を`ModuleNotFoundError: No module named '_ctypes'`のエラーを吐いてこける。`ctypes`って結構基本的なライブラリなので、3.8.2のビルドの所で入っていないわけはないので、何かが根本的なところがおかしそう。エラーメッセージも併せて色々調べてみると、どうも`libffi`が不足しているようである。そして、歴史は繰り返す。`libffi`自体はすでにインストールされていて、必要なのは`libffi-dev`なのだ。というわけで、

```bash
yum install libffi-*
```
をする。実はこれだけでは問題が解決していなくて、**このライブラリはpythonのビルド時にリンクされていなければいけない**(早く言ってよ)。なので、`pyenv uninstall 3.8.2`で消してから、`pyenv install 3.8.2`で無事`ctypes`がきちんとimport出来るようになる。ここまで、来ると上に書いたPyTorchの`pip`でのインストールコマンドが通る。
あとは、`jupyter`, `matplotlib`, `scipy`あたりを`pip`で入れておけば大方の機能は使えるだろう。`numpy`は多分PyTorchのインストール時の依存関係で入っているはず。

### まとめ
と、発見的に解決してきたPyTorchインストールの道のりをつらつらと書いてきた。しかし、答えが最初から分かっていれば、

```bash
yum install zlib-* openssl-* bzip2-* readline-* sqlite-* libffi-*
pip install torch==1.5.0+cpu torchvision==0.6.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
pip install jupyter
pip install matplotlib
pip install scipy
```
で、PyTorchと最低限の開発環境が揃うはず。(gcc g++あたりは、まだ入れてなければ、それも`yum install`しないといけないかも。)

Anacondaと食い合うのが嫌な場合は、PyTorchを使うディレクトリで、`pyenv local 3.8.2`にしておけば、よい。ほかのディレクトリに移れば、globalに指定してある環境になる。

## pyenv + 3.8.2 on Ubuntu 19.10
色々勘違いがあって、`sudo apt install libffi-dev`だけ入れた状態で`pyenv 8.3.2`をしたら、

```bash
WARNING: The Python bz2 extenWARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?sion was not compiled. Missing the bzip2 lib?
```
等々のエラーが出た。Ubuntuの場合は上の`yum`に対応するのは

```bash
sudo apt install libbz2-dev
sudo apt install libreadline-dev 
sudo apt install libsqlite3-dev
```
になる。これで、python 3.8.2がインストールされるので、あとは`pip`でPyTorch, jupyter, matplotlib, scipyあたりを入れれば、おしまい。
参考にさせていただいたサイト：[pyenvでPython3.7.0をインストールしようとしたらWARNING発生 - 自動化厨のプログラミングメモブログ│CODE-LIFE](https://code-life.hatenablog.com/entry/pyenv-install-3.7.0)

# Torch tensor

- `torch_array.shape`で形状取得
- `torch_array.size`で長さ取得

# モデルの作成
隠れ層が2層のモデルの例。`nn.Linear`に、活性化関数はReLU.

```python
import torch
import torch.nn as nn
import torch.nn.functional as F
class Model_2L(nn.Module):
    def __init__(self, input_size, output_size, ndim1 = 10, ndim2=10):
        super(Model_2L,self).__init__()
        self.n1 = ndim1
        self.n2 = ndim1
        self.fc1 = nn.Linear(input_size, self.n1)
        self.fc2 = nn.Linear(self.n1, self.n2)
        self.fc3 = nn.Linear(self.n2, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.fc2(F.relu(x))
        x = self.fc3(F.relu(x))
        return x
```

```python
model = Model_3L(input_size, output_size)
```

# トラブルシューティング
## `RuntimeError: size mismatch, m1: [1 x 15], m2: [1 x 1] at /pytorch/aten/src/TH/generic/THTensorMath.cpp:41`

引数のtorch tensoryの形が多分おかしい。
自分のケースではnumpy ndarrayで作ってから、`torch.from_numpy`でtorch tensorに変えていて、numpy ndarrayの時点での宣言がおかしかった。具体的には(Ndim,1)で作るべきところを、(Ndim,)で作っていたため。この変換は`x_array = x_array.reshape(Ndim,-1)`で実行できる。

## `RuntimeError: Expected object of scalar type Float but got scalar type Double for argument #2 'mat1' in call to _th_addmm`
多分、入力tensorの型が32bit実数になっていなくて、64bit実数になっているが、読んだ関数が64bit実数に対応していないことが問題。

[第2回　PyTorchのテンソル＆データ型のチートシート：PyTorch入門 - ＠IT](https://www.atmarkit.co.jp/ait/articles/2002/13/news006.html)によると、
> 種類は豊富だが、基本的に32bitのtorch.floatかtorch.intしか使わない。

とのことらしい。多くのnumpyはデフォルトでは`float64`を使っていると思うので、宣言部分で明示的に`dtype = float32`を付けるか、`x_array = np.float32(x_array)`で32bit実数に変換する必要がある。