MLFlow Tracking with PyTorch入門

# はじめに
こんにちは。
インターン先でMLFlowを用いることになったので、備忘録的な感じてつらつらしていこうと思います。
ここでは具体的な使い方をメインに書いていくんで、もっと知りたい方は公式ドキュメント(https://mlflow.org/)を読んでください。
勉強中ですので間違ってる箇所等ありましたら教えてください...。

# MLFlowとは
MLflowは、機械学習タスクにおける実験結果や実行環境などの管理を効率化できるOSSです。
現在は機械学習といえばPythonが最も使われて(多分)いますが、MLFlowはRやJavaでも利用できます。以下は公式サイトに載ってたやつです。
<img width="1076" alt="スクリーンショット 2020-07-03 0.45.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/461848/3a931d85-1c9c-1876-1c19-56591c39adea.png">

最近では機械学習フレームワークも様々な言語で整備されてきていて、普段Pythonをあまり使わずに機械学習をする人も増えてきていると思うので、こういうとこは普通に便利ですね。
また、言語だけでなくフレームワークも選ばずに利用できます。今回はPyTorchで実装しますが、TensorFlowなどでも利用できるってことですね。

他にMLFlowを用いる利点としては、「自分以外のメンバーに共有しやすい」ことや「どんなクラウド上でも同様に動かすことができる」という点があります。

# MLFlowの主な機能
MLFlowには大きく分けて3つの機能があります。

① MLFlow Tracking ・・・ コード、実験結果の管理、共有
② MLFlow Projects ・・・ 環境を管理し、パッケージ化
③ MLFLow Models ・・・ モデルデプロイの方法

ここでは、MLFlow Trackingを見ていこうと思います。(他二つについてはそのうち追記します...)

## MLFlow Tracking
MLFLow Trackingでは、実験結果やモデルのハイパーパラメータなどの保存、管理ができます。
またこの機能の便利な点として、サーバ上にプロセス立てて専用のuiを用いて保存した結果を見ることができます。(これに関しては実際見た方が早いので下で載せます)

## 実装
ヤイヤイ言うより見た方が多分早いので早速使ってみます。
今回はPytorchでMNISTを雑にやっていきます(本質はMLFlowなので学習はかなり雑です)。
コードはこちら(https://github.com/yk-amarly-20/practice/tree/master/mlflow/torch_mlflow)。

ネットワークはこんな感じ。とても単純なやつです。

```python:Net.py
class Net(nn.Module): 

    """ 
    ニューラルネット定義 
    """ 

    def __init__(self): 
        super(Net, self).__init__()

        self.conv1 = nn.Conv2d(1, 10, 5) 
        self.conv2 = nn.Conv2d(10, 20, 5) 
        self.fc = nn.Linear(320, 10) 

    def forward(self, x): 
        x = F.relu(F.max_pool2d(self.conv1(x), 2)) 
        x = F.relu(F.max_pool2d(self.conv2(x), 2))|
        x = x.view(-1, 320) 
        x = F.relu(self.fc(x)) 

        return F.log_softmax(x) 
```

実行関数はこんな感じ。

```python:main.py
def main():
    """
    実行関数
    """
    
    #コマンドライン引数
    args = make_parse().parse_args()
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    # data_loader
    train_loader, test_loader = generate_loader(args.batch_size)

    # model
    model = Net()

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(model.parameters(), lr=args.lr)
    scheduler = optim.lr_scheduler.MultiStepLR(
        optimizer,
        milestones=[int(args.epochs*0.8), int(args.epochs*0.9)],
        gamma=0.1
    )

    model = model.to(device)
    
    # 学習、精度評価 
    results = train(
        epochs=args.epochs, 
        model=model, 
        train_loader=train_loader,
        test_loader=test_loader,
        criterion=criterion,
        optimizer=optimizer,
        scheduler=scheduler,
        device=device
    )

    # mlflow
    with mlflow.start_run() as run:

        for key, value in results.items():
            mlflow.log_metric(key, value)

        for key, value in vars(args).items():
            mlflow.log_param(key, value)

        mlflow.pytorch.log_model(model, 'model')
```

注目してほしいのは``with mlflow.start_run() as run:``以下です。
ここの部分でMLFlow Trackingの機能を使っています。
パラメータを保存したい場合は``mlflow.log_param``, lossやaccuracyを保存したい場合は``mlflow.log_metric``を使用します。
main.pyを実行すると, 直下に``mlruns``という名前のディレクトリが作られ, その下に``0``というディレクトリが作られます。このディレクトリはexperimentと呼ばれ、この下に保存したパラメータなどがどんどん追加されていきます。

次に、保存したものをuiで確認してみましょう。``mlruns``を含むディレクトリで次のコマンドを叩いてください。
``mlflow ui --port 5000 --host 0.0.0.0``
urlにアクセスするとこんな画面が表示されます。

<img width="1078" alt="スクリーンショット 2020-07-03 1.41.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/461848/0fa3d22d-6ebe-6426-b9bf-7e48ede00503.png">

各結果について
<img width="563" alt="スクリーンショット 2020-07-03 1.43.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/461848/fcd27063-e501-6095-fae8-aa89a49f2792.png">
こんな感じで可視化できて見やすいですし、共有フォルダに``mlruns``を作っておけば、他のメンバーとの共有も容易です。

既存の``mlruns``ディレクトリに保存したり、experimentを使用したい場合は

```python
# mlrunsディレクトリ指定
tracking_uri = './mlruns'    # パス
mlflow.set_tracking_uri(tracking_uri)

# experiment指定
experiment_name = 'example'    # experimentの名前
mlflow.set_experiment(experiment_name)

```
を保存前に突っ込んであげるとできます。
他にもいろいろ機能あるので調べてみてください。

# 終わりに
機械学習タスクではハイパーパラメータをGrid Search的に探索する機会も多いので、複数人で共有する場合はもちろんのこと個人で進める分にも便利だと考えています。(それこそkaggleでパラメータ探索するのにも便利だと思う)
興味を持っていただけたら、ぜひ公式リファレンスなどを読んで実際に動かしてみるといいと思います。