GoogleColaboratoryで動くgit+Pytorch-lightning+hydra+wandbの1実験1スクリプト実験環境を作成する。

# 初めに

　この記事は以下のような欲求を満たします。

- **colab (pro)を計算リソースとして利用したい（これが一番大きい）**
- 実験コード管理はgitでやりたい
- ハイパラ管理はymlファイルでやりたい
- 実験結果や学習済みモデルは専用のサービスで管理したい
- 1実験1スクリプトで実験をやりたい

具体的には以下のようなディレクトリ構成になっており、

![スクリーンショット 2021-07-17 22.35.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/381097c4-2ead-226f-3462-b869a3ce4db4.png)


下画像のようにymlファイルでハイパラや実験に関しての情報を管理し
![スクリーンショット 2021-07-17 22.51.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/8872edc9-498e-6c13-a16c-10d331dd0eca.png)


このようにcolabでコードを実行すると、
![スクリーンショット 2021-07-17 22.51.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/64b7eb9b-f9c5-22df-1752-c44d12399d5d.png)


自動的にlogが下画像のように生成され、
![スクリーンショット 2021-07-17 22.52.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/fe96612a-7fdb-50b2-38a3-642b0ef1488b.png)

![スクリーンショット 2021-07-17 22.52.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/c9c2101b-dc56-203e-0523-1b8ecf1a8df0.png)


さらにモデル管理することがきます。
![スクリーンショット 2021-07-17 22.53.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/451558ca-4a9c-e9c0-a6f8-32dfe475cd18.png)


また、1実験1スクリプトを徹底するため、実験結果と対応するコードがすぐにわかります。
（ちなみに1実験1スクリプトのメリットについては下記の記事が非常に参考になります。）

https://zenn.dev/fkubota/articles/f7efe69fd2044d



##　なぜcolabで実験？

　コスパが最強だからです。

 例えばcolab proとGCEと比較してみましょう。
 colab proではP100かV100が割り振られます。
 ここでは週5で1日12時間で1ヶ月稼働させるとしましょう。

|machine  |GPU  |価格/1時間  | 価格/1月　|
|---|---|---|---|
|  Colab  |  P100   |    _  |\$9.9 |
|  GCE    |  P100   | \$2.48 | \$595.2 |
|  Colab  |  V100   |   _   |\$9.9 |
|  GCE    |  V100   |  \$1.46| \$350.2 |


 **マジで安すぎる...!**
P100でもガッツリ使えば月3~4万は浮きそう...

 また上記のGCEの計算はGPUだけの価格ですので、広いメモリや高性能なCPUが必要な場合、その価格はさらに膨れ上がります。

## colab（というかnotebook）の微妙なところ

**実験管理が破綻する点です。**
例えば以下のようなシチュエーションに遭遇したことはありませんか？（筆者は死ぬほどあります）

```
「なんかめっちゃ精度いいモデルできたけど再現できない...」
「このモデル、たまに変な挙動するけどどういう処理で学習させたんだっけ？」
「あれ、このモデル、あの処理を試したやつだっけ...？」
「人に自分の実験結果について共有したいけど、notebookが散らばりすぎて不可能...」
```

notebookはちょっとモデルを作ったり、EDAをすることには向いていますが、ガッツリモデル作成の実験をすることには不向きです。

# まとめ（成果物）

この記事では以下の構成を作成します。

- **colab (pro)を計算リソースとして利用**
- 実験コードはgitで管理
- 1実験1スクリプトで実験管理
- ハイパラはhydraで管理
- 実験結果や学習済みモデルはwandbで管理


https://github.com/harasooo/colab_exp_qiita

https://drive.google.com/drive/folders/1eQyD-XdW6WJsf4hQGRy8aUPzPNAu0qmS?usp=sharing


#目次
| 目次     | 
|:-----------------|
| [全体の構成について](#全体の構成について)  |     
| [実験コードについて](#実験コードについて)         | 
| [実行環境（google drive上）について](#実行環境（google drive上）について)       | 
| [main.pyについて](#main.pyについて)     | 
| [pytorch-lightning](#pytorch-lightning)       | 
| [hydra(congi.yaml)](#hydra(congi.yaml))       | 
| [wandb](#wandb)       | 
| [Colab（google drive）上での作業について](#Colab（google drive）上での作業について)     | 
| [実際に実験を行ってみる](#実際に実験を行ってみる) |
| [終わりに](#終わりに) | 

# 実験コードについて

まず実験コードについては以下のようにしています。
一つの実験が`exp_xxx`で区切られており、以下のファイルを保持しています。

- main.py：実際に処理を行うスクリプト
- config.ymal：ハイパーパラメータやメタ情報を管理するyamlファイル
- requirements.txt：必要なパッケージ群

poetryの導入も考えましたが、手順がかなり煩雑になるため今回は見送りました。（1.poetryで仮想環境を作ってその中で実行 2.poetryでそのままシステムにインストールする、を試した両方ともかなりカオスになったので泣く泣くお蔵入りです。colabでのパッケージ管理に関して詳しい方がいたらコメント等で教えてください...）


<pre>
hoge_project_src
├── exp_1
│   ├── main.py
│   ├── config.yaml
│   └── requirements.txt
├── exp_2
│   ├── main.py
│   ├── config.yaml
│   └── requirements.txt
├── exp_3
│   ├── main.py
│   ├── config.yaml
│   └── requirements.txt
└── .gitignore
</pre>

# 実行環境（google drive上）について


実行環境（google drive）では以下のようにディレクトリを構成します。
それぞれについて説明すると

- data：学習データや、学習済みモデル等の置き場
- main.ipynb：実行主体
- hoge_project_src：実験コードをcloneしてきます。

<pre>
hoge_progect
├── data
│   ├── hoge.csv
│   └── hoge.csv
├── maim.ipynb
└── hoge_project_src
    ├── exp_1
    │   ├── main.py
    │   ├── config.yaml
    │   └── requirements.txt
    ├── exp_2
    │   ├── main.py
    │   ├── config.yaml
    │   └── requirements.txt
    ├── exp_3
    │   ├── main.py
    │   ├── config.yaml
    │   └── requirements.txt
    └── .gitignore
</pre>

# main.pyについて

まずmain.pyは以下のようになります。
ここではサンプルとしてlivedoorのテキスト分類を学習済み日本語BERTを用いて行うとしましょう。

```main.py
import os

import hydra
import numpy as np
import pandas as pd
import pytorch_lightning as pl
import torch
import torch.nn as nn
import torch.optim as optim
from omegaconf import DictConfig
from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint
from pytorch_lightning.loggers import WandbLogger
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, Dataset
from torchmetrics.functional import accuracy, auroc
from transformers import BertModel, BertTokenizer


class BertDataset(Dataset):
    def __init__(
        self,
        df: pd.DataFrame,
        tokenizer: BertTokenizer,
        max_length: int,
        text_column_name: str,
        label_column_name: str,
    ):
        self.df = df
        self.tokenizer = tokenizer
        self.max_length = max_length
        self.text_columm_name = text_column_name
        self.label_column_name = label_column_name

    def __len__(self):
        return len(self.df)

    def __getitem__(self, index: int):
        df_row = self.df.iloc[index]
        text = df_row[self.text_columm_name]
        labels = df_row[self.label_column_name].astype(int)

        encoding = self.tokenizer.encode_plus(
            text,
            add_special_tokens=True,
            max_length=self.max_length,
            padding="max_length",
            truncation=True,
            return_token_type_ids=False,
            return_attention_mask=False,
            return_tensors="pt",
        )

        return encoding["input_ids"].flatten(), torch.tensor(labels)


class CreateDataModule(pl.LightningDataModule):
    def __init__(
        self,
        train_df,
        valid_df,
        batch_size,
        max_length,
        text_column_name: str = "text",
        label_column_name: str = "label",
        pretrained_model="cl-tohoku/bert-base-japanese-whole-word-masking",
    ):
        super().__init__()
        self.train_df = train_df
        self.valid_df = valid_df
        self.batch_size = batch_size
        self.max_length = max_length
        self.tokenizer = BertTokenizer.from_pretrained(pretrained_model)
        self.text_columm_name = text_column_name
        self.label_column_name = label_column_name

    def setup(self):
        self.train_dataset = BertDataset(
            self.train_df,
            self.tokenizer,
            self.max_length,
            self.text_columm_name,
            self.label_column_name,
        )
        self.vaild_dataset = BertDataset(
            self.valid_df,
            self.tokenizer,
            self.max_length,
            self.text_columm_name,
            self.label_column_name,
        )

    def train_dataloader(self):
        return DataLoader(
            self.train_dataset,
            batch_size=self.batch_size,
            shuffle=True,
            num_workers=os.cpu_count(),
        )

    def val_dataloader(self):
        return DataLoader(
            self.vaild_dataset, batch_size=self.batch_size, num_workers=os.cpu_count()
        )


class CustumBert(pl.LightningModule):
    def __init__(
        self,
        n_classes: int,
        d_model: int,
        learning_rate: float,
        max_length: int,
        pretrained_model="cl-tohoku/bert-base-japanese-whole-word-masking",
    ):
        super().__init__()

        self.bert = BertModel.from_pretrained(pretrained_model)
        self.classifier = nn.Linear(d_model, n_classes)
        self.lr = learning_rate
        self.criterion = nn.CrossEntropyLoss()
        self.n_classes = n_classes

        for param in self.bert.parameters():
            param.requires_grad = False

        for param in self.bert.encoder.layer[-1].parameters():
            param.requires_grad = True

        for param in self.classifier.parameters():
            param.requires_grad = True

    def forward(self, inputs):
        outputs = self.bert(inputs)[0]
        preds = self.classifier(outputs[:, 0, :])
        return preds

    def training_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self.forward(inputs=x)
        loss = self.criterion(y_hat, y)
        return {"loss": loss, "batch_preds": y_hat, "batch_labels": y}

    def validation_step(self, batch, batch_idx):
        x, y = batch
        y_hat = self.forward(inputs=x)
        loss = self.criterion(y_hat, y)
        return {"loss": loss, "batch_preds": y_hat, "batch_labels": y}

    def training_epoch_end(self, outputs, mode="train"):
        epoch_y_hats = torch.cat([x["batch_preds"] for x in outputs])
        epoch_labels = torch.cat([x["batch_labels"] for x in outputs])
        epoch_loss = self.criterion(epoch_y_hats, epoch_labels)
        self.log(f"{mode}_loss", epoch_loss)

        _, epoch_preds = torch.max(epoch_y_hats, 1)
        epoch_accuracy = accuracy(epoch_preds, epoch_labels)
        self.log(f"{mode}_accuracy", epoch_accuracy)

        epoch_auroc = auroc(epoch_y_hats, epoch_labels, num_classes=self.n_classes)
        self.log(f"{mode}_auroc", epoch_auroc)

    def validation_epoch_end(self, outputs, mode="val"):
        epoch_y_hats = torch.cat([x["batch_preds"] for x in outputs])
        epoch_labels = torch.cat([x["batch_labels"] for x in outputs])
        epoch_loss = self.criterion(epoch_y_hats, epoch_labels)
        self.log(f"{mode}_loss", epoch_loss)

        _, epoch_preds = torch.max(epoch_y_hats, 1)
        epoch_accuracy = accuracy(epoch_preds, epoch_labels)
        self.log(f"{mode}_accuracy", epoch_accuracy)

        epoch_auroc = auroc(epoch_y_hats, epoch_labels, num_classes=self.n_classes)
        self.log(f"{mode}_auroc", epoch_auroc)

    def configure_optimizers(self):
        return optim.Adam(self.parameters(), lr=self.lr)


def make_callbacks(min_delta, patience, checkpoint_path):

    checkpoint_callback = ModelCheckpoint(
        dirpath=checkpoint_path,
        filename="{epoch}",
        save_top_k=3,
        verbose=True,
        monitor="val_loss",
        mode="min",
    )

    early_stop_callback = EarlyStopping(
        monitor="val_loss", min_delta=min_delta, patience=patience, mode="min"
    )

    return [early_stop_callback, checkpoint_callback]


@hydra.main(config_path=".", config_name="config")
def main(cfg: DictConfig):
    cwd = hydra.utils.get_original_cwd()
    wandb_logger = WandbLogger(
        name=("exp_" + str(cfg.wandb.exp_num)),
        project=cfg.wandb.project,
        tags=cfg.wandb.tags,
        log_model=True,
    )
    checkpoint_path = os.path.join(
        wandb_logger.experiment.dir, cfg.path.checkpoint_path
    )
    wandb_logger.log_hyperparams(cfg)
    df = pd.read_csv(cfg.path.data_file_name, sep="\t").dropna().reset_index(drop=True)
    df[cfg.training.label_column_name] = np.argmax(df.iloc[:, 2:].values, axis=1)
    df[[cfg.training.text_column_name, cfg.training.label_column_name]]
    train, test = train_test_split(df, test_size=cfg.training.test_size, shuffle=True)
    data_module = CreateDataModule(
        train,
        test,
        cfg.training.batch_size,
        cfg.model.max_length,
        cfg.training.text_column_name,
    )
    data_module.setup()

    call_backs = make_callbacks(
        cfg.callbacks.patience_min_delta, cfg.callbacks.patience, checkpoint_path
    )
    model = CustumBert(
        n_classes=cfg.model.n_classes,
        d_model=cfg.model.d_model,
        learning_rate=cfg.training.learning_rate,
        max_length=cfg.model.max_length,
    )
    trainer = pl.Trainer(
        max_epochs=cfg.training.n_epochs,
        gpus=1,
        progress_bar_refresh_rate=30,
        callbacks=call_backs,
        logger=wandb_logger,
    )
    trainer.fit(model, data_module)


if __name__ == "__main__":
    main()
```

# pytorch-lightning

Pytorch-lightningそのものについては基本的に解説しません。
他にわかりやすい記事があるのでそちらを参照してください。
...が、ModelCheckpointのpathだけわかりにくいのでそこだけ後述します。

その他は普通にPytorch-lightningのコードを書いて、hydraを使ってyamlファイルからパラメータを渡せばいいだけです。

# hydra(congi.yaml)

hydraを使うと学習に使用するパラメータをyamlで管理することができます。

使用方法は簡単で、以下のようにyamlファイルを設定し、

```config.yaml
wandb:
  project: colab_exp
  exp_num: 6
  tags: [bert]
path:
  data_file_name: データセットの絶対パス
  checkpoint_path: checkpoints
model:
  d_model: 768
  n_classes: 9
  max_length: 512
training:
  test_size: 0.3
  n_epochs: 30
  batch_size: 32
  learning_rate: 0.0001
  text_column_name: article
  label_column_name: label
callbacks:
  patience: 5
  patience_min_delta: 0

```

以下のようにmain関数にデコレータを付与してあげます。

```python
@hydra.main(config_path=".", config_name="config")
def main(cfg: DictConfig):
    print(cfg.wandb.project)
```

例えば`print(cfg.wandb.project)`は`colab_exp`と出力してくれます。

ここで一つ注意することは**学習データのパスとして絶対パスを渡してやることです**（相対パスからでもいけるがちょっとだけめんどくさい）

これはhydraの仕様によりカレントディレクトリが変更されてしまうことが原因です。（具体的には `hoge_project_src/exp_xxx/outputs/2021-07-07/12-23-23`、など）

一応
`hydra.utils.get_original_cwd()`
で変更前のカレントディレクトリを参照することもできますが、今回はその一つ上の階層である`data`を参照したいので、絶対パスをconfigに書いてしまう方が楽だと思います。

例）

```config.yaml
path:
  data_file_name: /content/drive/MyDrive/colab_exp/data/livedoor.tsv
```


# wandb

wandbは機械学習の結果等を管理してくれるツールです。
実験結果がリモートサーバーに自動で保存されるので、非常に使いやすいです。
事前登録が必要なので下記で済ませておいてください。
https://wandb.ai/site

wandbについて最低限の解説を行います。

### なぜwandb？

同様のサービスとしてMLflowなどがありますが、pytorch_lightningを使うのであればwandbが圧倒的に楽です。
というのも連携が非常に優秀で以下のようにpytorch_lightningに組み込まれています。

```python
from pytorch_lightning.loggers import WandbLogger
```

Trainerの引数に渡してやると、基本的な設定は完了です。簡単すぎる...

```python
trainer = pl.Trainer(
        max_epochs=cfg.training.n_epochs,
        gpus=1,
        progress_bar_refresh_rate=30,
        callbacks=call_backs,
        logger=wandb_logger,
    )
```

Trainerに渡した後はtraining_epoch_end等の中で

```python
self.log(f"val_loss", epoch_loss)
```

のように記述すると勝手にlogをとってくれます。

最後に以下の部分だけ解説します。

```python
wandb_logger = WandbLogger(
        name=("exp_" + str(cfg.wandb.exp_num)),
        project=cfg.wandb.project,
        tags=cfg.wandb.tags,
        log_model=True,
    )
    checkpoint_path = os.path.join(
        wandb_logger.experiment.dir, cfg.path.checkpoint_path
    )
    wandb_logger.log_hyperparams(cfg)
```

まず

```python
wandb_logger = WandbLogger(
        name=("exp_" + str(cfg.wandb.exp_num)),
        project=cfg.wandb.project,
        tags=cfg.wandb.tags,
        log_model=True,
    )
```

の部分ではwandbを初期化しています。
ここで重要なのが`log_model=True`です。

`WandbLogger.experiment.dir`以下の`*.ckpt`を全て、wandbのリモートサーバに保存してくれます。

ちなみにhydraを使っている以上、カレントディレクトが`hoge_project_src/exp_xxx/outputs/XXXX-XX-XX/XX-XX-XX`
になっているので
`WandbLogger.experiment.dir==hoge_project_src/exp_xxx/outputs/XXXX-XX-XX/XX-XX-XX/wandb`
になります。

よって、wandbの`log_model`を機能させるためには

```python
    checkpoint_path = os.path.join(
        wandb_logger.experiment.dir, cfg.path.checkpoint_path
    )
```
のようにpytorchのmodel checkpointのpathは`WandbLogger.experiment.dir`以下に設定してやる必要があります。

（一応イニシャライズした後でも`WandbLogger.experiment.dir`を変更できるのらしいので、ディレクトリ構成に納得いかない方は適宜カスタムしてください。）

最後に以下のポイントですが`log_hyperparams`という関数を作利用することで、ハイパーパラメータをwandbに保存できる上に、wandb上のtableなどで利用することができます。

```python
wandb_logger.log_hyperparams(cfg)
```

実際にはwandbのtableとして以下のように表示することができます！

![スクリーンショット 2021-07-17 22.52.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/331d48f6-5647-995a-d371-6408f8f19c95.png)


比較しやすくてめっちゃ便利ですね！


# Colab（google drive）上での作業について

次にColab（google drive）上での作業についてですが、以下の手順です。

###　1 ディレクトリ構成
まず以下のようにディレクトリを構成してください。
ここでdataには

- 学習データ
- 評価データ
- 外部モデル

などを入れます。

<pre>
hoge_progect
├── data
│   └── hoge.csv
└──  maim.ipynb
</pre>

###　2 maim.ipynb

次にmain.ipynbの中身は以下のようになります。

処理としては大きく以下に別れています。
1. driveをマウント
2. hoge_project_src以下に実験コードをclone
3. パッケージをインストール
4. main.pyを実行
5. モデル以外の生成物をpush 

```main.py
from google.colab import drive
import os
drive.mount('/content/drive')

USER_NAME = githubのusername
PASSWORD =  githubのpassword
REPO_NAME = githubのrepo name
EMAIL = githubに登録しているemail
CWD = google grive上のカレントディレクトリ（main.ipynbを配置しているディレクトリ)
SRC = 実験コードをcloneするディレクトリ（main.ipynbを配置しているディレクトリの一階層下）
EXP = 実験番号（"exp_01"など）
CWD_SRC = os.path.join(CWD,SRC)
CWD_EXP=os.path.join(CWD_SRC,EXP)

%cd "/content"
!rm -rf {CWD_SRC}
!git clone https://{USER_NAME}:{PASSWORD}@github.com/{USER_NAME}/{REPO_NAME}.git {CWD_SRC}
%cd {CWD_EXP}
!pip install -r requirements.txt
!pip install hydra-core --upgrade
!python main.py

%cd ..
!git config --global user.email {EMAIL}
!git config --global user.name {USERNAME}
!git add .
!git commit -m "finish."
!git push origin main
```

なお、`.gitignore`に`*.ckpt`を追加するのを忘れないでください。（基本的にモデルは重いのでgitで管理しない方がいいです）

また、hydraが吐き出すファイルは（今回の手順だと）正直いらないので、最後のgit add ~ git pushの下りは省略しても大丈夫です。

更に、下記パラメータについて少し補足します。

```python
CWD = google grive上のカレントディレクトリ（main.ipynbを配置しているディレクトリ)
SRC = 実験コードをcloneするディレクトリ（main.ipynbを配置しているディレクトリの一階層下）
EXP = 実験番号（"exp_01"など）
```
下記のような構成でexp_2を実行したい場合、

![スクリーンショット 2021-07-17 22.35.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/5e8d9cfc-5b41-000b-5378-25c74b13e2be.png)
各パラメータは以下のようになります。
```python
CWD = "/content/drive/MyDrive/colab_exp"
SRC = "colab_exp_src"
EXP = "exp_2"
```



以上で終了です！！！

# 実際に実験を行ってみる

ここでは実際に実験を行ってみましょう。
livedoorのtext分類をlearning rateを変更して行います。

- exp_01ではlr:0.01
- exp_02ではlr:0.001

なお以下のレポジトリとgoogle driveから再現することができます。

https://github.com/harasooo/colab_exp_qiita

https://drive.google.com/drive/folders/1eQyD-XdW6WJsf4hQGRy8aUPzPNAu0qmS?usp=sharing


###　1 wandbでアカウント作成&プロジェクト作成

wandb（url）にアクセスし、アカウント作成
その後新規プロジェクトを作成し、以下のような画面になればOKです。

![スクリーンショット 2021-07-17 21.15.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/6abca28a-6af8-fccb-ecf6-0b758bc70bb8.png)

###　2 実験コードを作成

github上でレポジトリを作成して、実験コードを作成します。
ここでconfig.yamlには先ほど設定したwandbのプロジェクト名を書いておきます。

![スクリーンショット 2021-07-17 21.23.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/5d88ed7d-7113-b042-4d0d-15ea4f96575e.png)


今回は以下のようなコードを作成しました。

https://github.com/harasooo/colab_exp_qiita/tree/main/exp_1

### 3 google dirve上でディレクトリを構成しcolabで実行

以下のように構成したのち、main.ipynbを前述の通り編集します。（コピペして、大文字の変数だけカスタムしてください）


<pre>
colab_exp
├── data
│   └── train.csv
└──  maim.ipynb
</pre>

一応サンプルのnotebookを貼り付けておきます。

https://colab.research.google.com/drive/1YVv0ouzsQEjHsk5vC9LjO_uTjI7L_Pla?usp=sharing

後は実行すれば完了です。

wandbに関して以下のような表示が出ますが、2を選択し、指示されたkeyを貼り付ければOKです。

![スクリーンショット 2021-07-17 21.37.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/1146737c-1631-c8d6-fe87-0bc56bbfb11d.png)

![スクリーンショット 2021-07-17 21.38.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/ab285b33-7a9b-1bb5-808b-8fadc75e8b1d.png)



### 4 次の実験へ（実験コード）

次はgit pullした後新たにexp_02というフォルダを作成し、そこに実験コードを作成していきます。
ここでconfig.ymalのnameと学習率を変更しておきます。

![スクリーンショット 2021-07-17 22.14.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540234/da9a2cf6-ce62-c766-7050-685774779f63.png)




### 5 次の実験へ（colab）

main.ipynbの

```main.ipynb
EXP = "exp_1"
```
を

```main.ipynb
EXP = "exp_2"
```

に変更して実行すれば完了です！

### 6 実験結果の確認

以下のように実験結果を確認することができます。

https://wandb.ai/haraso/colab_exp_qiita

また、モデルも保存されています。

https://wandb.ai/haraso/colab_exp_qiita/runs/28biy3ng/files/checkpoints


# 終わりに

今回はcolaboratoryで動くgit+Pytorch-lightning+hydra+wandbの1実験1スクリプト実験環境を作成しました。

正直colabがインタラクティブな操作を期待しているので、かなり黒魔術色が濃い記事になってしまったと思います。が、それを踏まえてもcolab proのコスパは魅力的ですので、もしよければ試してみてください。

改善点等多々あると思いますので、是非コメントにて指摘してくださると非常に助かります。

# 参考文献

https://zenn.dev/fkubota/articles/f7efe69fd2044d

https://ymym3412.hatenablog.com/entry/2020/02/09/034644

https://zerebom.hatenablog.com/entry/2020/12/11/174527