機械学習をやるのにGPUベースでやりつつもmacOSの画面で作業はしたい

# 概要

機械学習をやる、となるとCPUベースではなくてGPUベースでやりたい、という昨今ですが現状のApple製品ではNvidiaのGPUを載せていないことや事実上のデファクトスタンダードになっているTensorFlowがMetalに対応してないこと、NvidiaのGPUを載せているMacでもGPU自体が古いなど、いろいろと面倒なことになっています。なので、機械学習用に別途UbuntuでPCを組む（僕の場合はゲーミングPCみたいなやつを譲ってもらった）ことになるんですが、そうなると画面が別になるとかそういうことがあってまた面倒。というか使いたいときにさっと使えない、というのは（僕の場合は）モチベーションの維持が難しくなるので、できれば画面切り替えとかせずにさっと使いたいときにmacOSで使いたいわけです。そうなるといくつか選択肢があります。

1. リモートデスクトップで使う
2. Ubuntuにjupyter notebookを--no-browserをつけて起動して外からブラウザ経由で使う
3. 2の状態のところにsshのポートフォワードで接続する

1は、やってみましたがレスポンスが悪いのと外から使うのが面倒なので却下。
2は、ローカルネットワークならこれでいいんですが、外部からアクセスしたい場合にはjupyter notebookをWANにさらしている事になるのでこれも却下

というわけで3が一番安全そうです。外部から接続したい場合はsshのポートだけをルーターでマッピングしておくなりサーバを晒していたとしても、ポートの開放はsshだけとかにしておけばいいわけですね。本当はVPSで適当なサーバを用意してSoftEthaとかでVPNを組んでしまうとかするとさらに良い感じはしますが、その辺は目的と手段が逆転しそうなあたりなのでとりあえず手を出さない事にします。

# やりたい事

Ubuntuで動いてるjupyter notebookに、別のマシン(macOS)のブラウザでアクセスして機械学習を行いたい。ちなみにローカルネットワークでの接続を前提にしてるのでグローバルから接続するにはそれなりに読み替えたり注意したりする必要があります。

# 事前準備

## Ubuntu側でSSHでログインできる環境を作る

この辺りは割愛します。ポートの変更やルートログインの禁止など当たり前にやることはやっておきましょう。この辺りができない人はこの記事はそっ閉じしてください。

## Ubuntu側でJupyter notebook環境（というか機械学習の環境をつくる）

ちょっと記事的には古いのと、インストール対象がmacOSなのですが僕が書いた以下の記事を参考に読み替えたりしていくといいと思います。

https://qiita.com/dropcontrol/items/a469efd94c59007d3f61

もちろん「ubuntu tensorflow jupyter notebook インストール」みたいにしてググってもいいでしょう。ちなみに、僕の場合は、、、

* anyevnでpyenvの環境を作る
* pyenvでminiconda3-latestをインストールする
* condaで各種環境を管理する

という感じでやっています。

# 接続方法

上記のような環境が整いローカルでjupyter notebookが使えるぞ、というところまで準備が整ったら、

```
$ conda activate <環境名>
```

とかでtensorflowなどをインストールしてあるpythonの実行環境を有効に出来るようになったかと思います。次にjupyter notebookを以下のように起動します（これらはmacOSからSSHでログインしてTerminalで行ってもubuntu側のローカルで直接行っても同じです）。

```
$ jupyter notebook --no-browser --port=8080
```

--no-browserはjupyter notebook起動時にブラウザが起動してこないようにするため、--port=8080はjupyter notebookのポート番号です。

そしたら次にsshでポートフォワードを設定して、macOSのブラウザから起動したjupyter notebookにアクセスできるようにします。これはアクセスする側のmacOSから行う必要があります。

```
ssh -N -L 8080:localhost:8080 <USERNAME>@<SERVER_ADDRESS>
```

<USERNAME>@<SERVER_ADDRESS>のところはSSHでログインする側のubuntuのユーザー名とサーバー情報です。もしsshのconfigにエイリアス（？）を設定してある場合はhoge@serverみたいな感じでも大丈夫です。外部からアクセスする場合はここのサーバー情報が外側からアクセスできるIP Addressやサーバ名になります（僕は.ssh/configの中にserver1.local、server1.globalみたいにして設定を使い分けてます）。

ここまで出来ればあとはブラウザからlocalhost:8080にアクセスすればjupyter notebookをmacOSのブラウザで使うことができます。超快適。

**FYI: jupyter notebook自体の設定には触れていないので下記のURLをとかを参考されたし。**
https://qiita.com/Miggy/items/5466a2c1e968602f3ebe


# すぐに思いつく課題

1. ubuntu起動時によく使うpythonの環境のアクティベートとjupyter notebookの起動はしてしまいたい
2. 手動で起動するにしてもターミナルを閉じてもjupyter notebookが終了しないようにしたい

などが使っていると出てくると思います。前者についてはシェルスクリプトとSupervisorなどを使ってデーモンかすればいいかなと思いますが、その場合jupyter notebookをバックグラウンドで起動する必要があります。で、以下のページにjupyter notebookの便利な使い方がまとまっていましたのでそれを参考にします。

https://qiita.com/ishizakiiii/items/b98bbf8997f039f40058

上記の中で「バックグラウンドジョブで動かす」というのがありますのでそれを参考にして先ほどのjupyter notebookの起動コマンドを以下のように変えます。

```
nohup jupyter notebook --no-browser --port=8080 >> jupyter.log 2>&1 &
```

* `nohup` ... [端末を閉じてもログアウトしても処理を続ける](https://www.atmarkit.co.jp/ait/articles/1708/24/news022.html)
* `>>  jupyter.log 2>&1 &` ... コマンドからの出力結果をjupyter.logというファイルにバックグラウンドで出力する（この例だとjupyter notebookを実行したディレクトリに出力されます。特定の場所を指定するにはパスを書いてあげれば良いです。というか気がつくとログが肥大するので特に問題が出なければこれをつけなければログは出力されないのでそれでもいいかもしれません）

なので、とりあえずこのコマンドで起動しておけばいつでもローカルネットワークならポートフォワードした後に http://localhost:8080 にアクセスすればjupyter notebookが使えるようになりました。あとは上記のコマンドが面倒なのでshellのエイリアスで　jpn_start_bg などと名前をつけておけばいいのではないでしょうか？（condaで有効にする環境を指定したり、ポートを分けて複数立てたいとかもあるかもなので、その場合はそういう起動用のシェルスクリプトを組むとかになるでしょうが、その辺は自分が必要になったらまたまとめます）。

というわけでこれでめんどくさいことが一つ減りました！！

**追記・根気のある人は下記のURLを参考にしてdemonizeしてしまう、という手もあります**
https://towshif.github.io/site/tutorials/Python/setup-Jupyter/

が、condaで環境切り替える、とか考えだすとそこまでやるのもな、、、という気持ちになります。どっちみち動いてるのはjupyter notebookなので、demonizeするから安定するとかそういうことではきっとないだろうし（調べていませんので確かな話ではありませんが）。