CUDA/Dockerの環境構築
目標
初期状態のUbuntu18.04 LTSにCuda, Docker, nvidia-dockerの環境を構築する。
想定環境
Step0 : 前準備
$ sudo apt update $ sudo apt -y upgrade
Step1 : GPUドライバ(CUDA)のインストール
Cuda Toolkitの公式サイトから以下のように選択し、表示されるものに従ってダウンロード。(ここではCuda10.2)
過去のバージョンはLegacy Releasesからインストール手順が参照可能。
$ 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 " aptレポジトリに追加 $ sudo add-apt-repository "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/ /" $ sudo apt-get update " CUDAのインストール $ sudo apt-get -y install cuda " 確認(表示されればOK) $ nvidia-smi " Pathを通す $ export PATH="/usr/local/cuda/bin:$PATH" $ export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
Step2 : Dockerのインストール
公式のインストールガイドに従ってdocker-ceをインストールする。
" (任意) 必要に応じてaptのアップデート $ sudo apt-get update " HTTPSのためのソフトフェアをインストール $ sudo apt-get -y install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common " Docker公式のGOG keyを追加 $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo apt-key fingerprint 0EBFCD88 " aptレポジトリに追加 $ sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" " aptのアップデート $ sudo apt-get update " docker-ceのインストール $ sudo apt-get -y install docker-ce docker-ce-cli containerd.io " (任意) 正しくインストールされたか確認 $ sudo docker run --rm hello-world " (任意) 必要に応じてuserグループに追加 " 追加するとsudoを使用する必要がなくなる。 $ sudo usermod -aG docker $USER $ exit
【余談】docker-ceとは?
DockerはCE(コミュニティエディション)とEE(エンタープライズエディション)があり、無料版と有料版という違いがある.(個人用か商業用かの違いみたいなもの)
Step3 : nvidia-dockerのインストール
githubにある公式のレポジトリにあるREADMEのQuickStartを参照しながらインストールを行う。
最新のDocker19.03のリリースで、Nvidia GPUがDockerでネイティブにサポートされるようになったためnvidia-docker2の使用は非推奨になりました。
Docker19.03の説明は次の記事がわかりやすい。
nvidia-docker2では--runtime=nvidia
のオプションを使いますが、19.03では--gpus
のオプションが追加されたのでこれを使う。
" レポジトリのパッケージを追加する $ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) $ curl -sL https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - $ curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list $ sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit $ sudo systemctl restart docker " (任意) 正しくインストールされたか確認 $ docker run --gpus all nvidia/cuda:9.0-base nvidia-smi
nvidia-smiの画面が出れば完了!!
PyTorchのSchedulerまとめ[torch v1.1.0]
目次
- PyTorch公式のscheduler一覧
- 本題に移る前に
- v1.1.0の問題点について
- LambdaLR
- StepLR
- MultiStepLR
- ExponentialLR
- CosineAnnealingLR
- ReduceLROnPlateau
- CyclicLR
- CosineAnnealingWarmRestarts
- Schedulerの自作
以前の記事のアクセス数がこのブログ全体の90%を占めているので最新版に更新します。ちなみに前のは v0.4だったかと思います。
Pytorchのscheduler公式ドキュメントは こちら
v1.1.0のソースはこちら
PyTorch公式のscheduler一覧
- LambdaLR
- StepLR
- MultiStepLR
- ExponentialLR
- CosineAnnealingLR
- ReduceLROnPlateau
- CyclicLR <- New
- CosineAnnealingWarmRestarts <- New
本題に移る前に
今回のschedulerのコードはgoogle colabで公開してあります。自分でパラメータをいじりながらschedulerの動きを見たい場合は、以下の手順に従ってもらえればと思います。colabについて分からなければ、「ググればええねん」って、ゆたぼんが言ってます。
まず、Googleアカウントがある前提として、以下を開いてください。
左上のPLAYGROUNDで開くを押してもらえれば使えます。
実行する時に、なんか色々出てきますが適当にぽちぽちしたら使えます、たぶん。
v1.1.0の問題点について
残念ながらv1.1.0のStepLR, MultiStepLRの二つがバグってます。各自で修正してください。
masterブランチやv1.1以外では正常に動作するので、pip show torch
等のコマンドで場所調べて、直接ライブラリにコピペなど。
どんなバグかというと、学習率が下がるタイミングのとき、下げたい割合の二乗分下が ります るように見えます(見えるだけ)。
以下では、StepLR, MultiStepLRだけv1.01における説明になります。
[追記(2019/07/24)]
scheduler側からget_lrで学習率を取得すると上記のようにおかしな挙動になりますが、optimizer側から学習率を取得すると期待通りの学習率が得られますので学習自体には問題はないと思われます。学習率を記録する際は気をつけましょう。@hara_pets さん、ご報告ありがとうございます。
LambdaLR
引数一覧
- optimizer : 最適化のインスタンスを指定
- lr_lambda : ラムダ式や関数を指定
- last_epoch : 指定したことないのでいじらなくていいと思う(個人的意見)。深く理解したい人はソースを見てください。
このスケジューラはlr_lambda
にオリジナルの式を入れられるのが最大のポイントです。引数には、stepが呼ばれた回数が与えられ、戻り値としてbase_lr
(optimizerに指定したlr
)からどれほど変化させたいかの比率を返すようにしてください。つまり、学習率はbase_lr * 戻り値
となります。
また、継承を用いる事で、自作のschedulerを簡単に作成することができます。 継承でLambdaLRを使う際は pytorch_transformers/optimization.py を参考にしてみるといいかもしれません。
example
ラムダ式を与えた場合
scheduler = LambdaLR(optimizer, lr_lambda = lambda epoch: 0.95 ** epoch)
関数を渡した場合
def func(epoch): if epoch < 40: return 0.5 elif epoch < 70: return 0.5**2 elif epoch < 90: return 0.5**3 else: return 0.5**4 scheduler = LambdaLR(optimizer, lr_lambda = func)
継承を用いた場合
class WarmupConstantSchedule(torch.optim.lr_scheduler.LambdaLR): # Reference : https://github.com/huggingface/pytorch-transformers/blob/master/pytorch_transformers/optimization.py#L33 """ Linear warmup and then constant. Linearly increases learning rate schedule from 0 to 1 over `warmup_steps` training steps. Keeps learning rate schedule equal to 1. after warmup_steps. """ def __init__(self, optimizer, warmup_steps, last_epoch=-1): def lr_lambda(step): if step < warmup_steps: return float(step) / float(max(1.0, warmup_steps)) return 1. super(WarmupConstantSchedule, self).__init__(optimizer, lr_lambda, last_epoch=last_epoch) optimizer = torch.optim.SGD(model.parameters(), lr=0.05, momentum=0.9, weight_decay=1e-5) scheduler = WarmupConstantSchedule(optimizer, warmup_steps=10) for step in range(100): scheduler.step()
StepLR
引数一覧
- optimizer : 省略
- step_size : 何ステップごとに学習率を減少させるかの値
- gamma : 学習率の減少率
- last_epoch : 省略
example
scheduler = StepLR(optimizer, step_size=200, gamma=0.5)
MultiStepLR
引数一覧
- optimizer : 省略
- milestones : 減少させたいstepのリスト
- gamma : 学習率の減少率
- last_epoch : 省略
StepLRは減衰ステップが一つに対し、これは複数取れます。注意点として、milestonesには、ステップの小さい順のリストを与えてください。 つまり、10,30,50のステップ数で減衰させたい場合は、[10,30,50]と与えてください。
このschedulerはImageNet等のベンチマークでSOTAなモデルの実験でも使われていて、最適化関数はmomentumSGD、全体のepoch数に対して50%, 75%くらいで学習率を0.1倍にしていることが多いような気がします。
example
scheduler = MultiStepLR(optimizer, milestones=[200, 350], gamma=0.5)
ExponentialLR
引数一覧
- optimizer : 省略
- gamma : 学習率の減少率
- last_epoch : 省略
簡潔に説明するとgamma**step回数
が学習率に乗算されます。
example
scheduler = ExponentialLR(optimizer, gamma=0.95)
CosineAnnealingLR
引数一覧
- optimizer : 省略
- T_max : 半周期のステップサイズ
- eta_min : 下限学習率
- last_epoch : 省略
example
scheduler = CosineAnnealingLR(optimizer, T_max=100, eta_min=0.001)
ReduceLROnPlateau
引数一覧
- optimizer : 省略
- mode : 監視されている要因が'min'だと下がっているか、'max'だと上がっているかを指定
- factor : 学習率の減衰率
- patience : 何ステップ向上しなければ減衰するかの値
- verbose : 減衰するとき出力してれるかどうか
- threshold : threshold_modeで説明するのに使われてる値
- threshold_mode : 'rel'と'abs'の2種類のどちらかを取ります。
'rel'の場合、減衰の起因となる閾値をmaxモードだと
best*(1+threshold)
、minモードだとbest*(1-threshold)
となります。'abs'の場合は、maxモードだとbest-threshold
、minモードだとthresh+threshold
となります。 - cooldown : 学習率が下がってから、factorを再監視するまでのステップ数
- min_lr : 最小学習率
- eps : nanとかInf回避用の微小数
また、監視する要因はschedulerの関数に存在するstepの引数に与えます。
また、大げさに例をあげると、 - mode : 'min' - patience : 10 - threshold : 0.5 - threshold_mode : 'abs' のように指定すると、監視している値のbestが0.01とすると、 0.01+0.5=0.51よりも高い値を10回連続で取ると減衰します。
example
scheduler = ReduceLROnPlateau(optimizer, 'min') for epoch in range(10): scheduler.step(val_loss) #val_lossが下がらなければ減衰
CyclicLR
引数一覧
- optimizer : 省略
- base_lr (float or list) : 下限学習率(初期値)
- max_lr : 上限学習率
- step_size_up : 学習率上昇のサイクル数
- step_size_down : 学習率減少のサイクル数。Noneの時は、step_size_upと一致。
- mode : {"triangular", "triangular2", "exp_range"}から選択。
scale_fn
がNone
でなければ、無視される。 - gamma : modeが
'exp_range'
の場合の減少率 - scale_fn : 任意のスケールを満たしたい場合、関数(ラムダ式等)を渡す。渡した場合、
mode
は無視される。 - scale_mode : {'cycle', 'iterations'}から選択。
scale_fn
の引数がcycle数なのかiteration数なのかを決定。 - cycle_momentum :
True
ならば、base_momentum
とmax_momentum
を逆に循環させる。また、max_momentum
を初期値にする。 - base_momentum : 最適化関数におけるmomentumのサイクルの下限
- max_momentum : momentumのサイクルの上限
- last_epoch : 省略
新たに追加されたschedulerです。Fast Geometric Ensembling等で使われています。
example1
scheduler = CyclicLR(optimizer, base_lr=0.001, max_lr=0.1, step_size_up=50, step_size_down=100, mode='triangular')
example2
scheduler = CyclicLR(optimizer, base_lr=0.001, max_lr=0.1, step_size_up=50, step_size_down=None, mode='triangular2')
example3
scheduler = CyclicLR(optimizer, base_lr=0.001, max_lr=0.1, step_size_up=50, step_size_down=None, mode='exp_range', gamma=0.995)
CosineAnnealingWarmRestarts
引数一覧
- optimizer : 省略
- T_0 : 初期の繰りかえし回数
- T_mult : サイクルのスケール倍率
- eta_min : 下限学習率
- last_epoch : 省略
これも新たに追加されたschedulerです。SnapShot Ensemble等で使われています。
example
scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=50, T_mult=2, eta_min=0.001)
Schedulerの自作
Schedulerを自作する場合、_LRScheduler
または LambdaLR
を継承すると便利です。LambdaLR
の継承を使った自作は上で書いたので、ここでは_LRScheduler
を継承した場合での作成を行います。
optimizerの学習率更新は、scheduler.step()が呼ばれた際に更新されますが、その際、self.last_epochがインクリメントされます。厳密には、このget_lrが呼ばれる前に行われます。step内でself.get_lrが呼ばれ、その戻り値を新たな学習率に置き換えるように設計されてます。
つまり、自作関数でschedulerを作成するならば、get_lrを自作するのにほとんど等しいです。
ということで、ここではCosineAnnealingWarmRestarts
にWarmupとサイクル毎に上限学習率を減らしていくschedulerを作成します。
githubにもあります。 github.com
class CosineAnnealingWarmUpRestarts(_LRScheduler): def __init__(self, optimizer, T_0, T_mult=1, eta_max=0.1, T_up=0, gamma=1., last_epoch=-1): if T_0 <= 0 or not isinstance(T_0, int): raise ValueError("Expected positive integer T_0, but got {}".format(T_0)) if T_mult < 1 or not isinstance(T_mult, int): raise ValueError("Expected integer T_mult >= 1, but got {}".format(T_mult)) if T_up < 0 or not isinstance(T_up, int): raise ValueError("Expected positive integer T_up, but got {}".format(T_up)) self.T_0 = T_0 self.T_mult = T_mult self.base_eta_max = eta_max self.eta_max = eta_max self.T_up = T_up self.T_i = T_0 self.gamma = gamma self.cycle = 0 super(CosineAnnealingWarmUpRestarts, self).__init__(optimizer, last_epoch) self.T_cur = last_epoch def get_lr(self): if self.T_cur == -1: return self.base_lrs elif self.T_cur < self.T_up: return [(self.eta_max - base_lr)*self.T_cur / self.T_up + base_lr for base_lr in self.base_lrs] else: return [base_lr + (self.eta_max - base_lr) * (1 + math.cos(math.pi * (self.T_cur-self.T_up) / (self.T_i - self.T_up))) / 2 for base_lr in self.base_lrs] def step(self, epoch=None): if epoch is None: epoch = self.last_epoch + 1 self.T_cur = self.T_cur + 1 if self.T_cur >= self.T_i: self.cycle += 1 self.T_cur = self.T_cur - self.T_i self.T_i = (self.T_i - self.T_up) * self.T_mult + self.T_up else: if epoch >= self.T_0: if self.T_mult == 1: self.T_cur = epoch % self.T_0 self.cycle = epoch // self.T_0 else: n = int(math.log((epoch / self.T_0 * (self.T_mult - 1) + 1), self.T_mult)) self.cycle = n self.T_cur = epoch - self.T_0 * (self.T_mult ** n - 1) / (self.T_mult - 1) self.T_i = self.T_0 * self.T_mult ** (n) else: self.T_i = self.T_0 self.T_cur = epoch self.eta_max = self.base_eta_max * (self.gamma**self.cycle) self.last_epoch = math.floor(epoch) for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()): param_group['lr'] = lr
example1
scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=150, T_mult=1, eta_max=0.1, T_up=10, gamma=0.5)
example2
scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=50, T_mult=2, eta_max=0.1, T_up=10, gamma=0.5)
example3
scheduler = CosineAnnealingWarmUpRestarts(optimizer, T_0=100, T_mult=1, eta_max=0.1, T_up=10, gamma=0.5)
AlbumentationsのTransformsをまとめたレポジトリを公開しました!!
PyTorchのSchedulerまとめ
[追記:2019/07/24] 最新版更新してます。
目次
- PyTorchライブラリ内にあるscheduler
- 基本設定
- LambdaLR
- StepLR
- MultiStepLR
- ExponentialLR
- CosineAnnealingLR
- ReduceLROnPlateau
- 自作scheduler
- おわりに
最近暇な時間にPyTorchのReferenceを細かくみたり実装をみたりしているのですが、 今回スケジューラを見ていて、グラフとかあった方嬉しいね(百聞は一見にしかず)と思ったので、グラフをまとめて作りました。
Pytorchのscheduler公式ドキュメントは こちら
PyTorchライブラリ内にあるscheduler
PyTorchでもともと存在するschedulerは以下のとおり
- LambdaLR
- StepLR
- MultiStepLR
- ExponentialLR
- CosineAnnealingLR
- ReduceLROnPlateau
今回は、ReduceLROnPlateau以外の可視化を説明を交えて紹介します。 (ReduceLROnPlateauはloss等が関係するので可視化がめんど... 難しかったので割愛で)
基本設定
今回の記事で使われるパラメータとして
- epoch数: 100
- 初期の学習率: 0.01
model = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2)) optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=0.01, momentum=0.9)
LambdaLR
引数一覧
- optimizer : 最適化のインスタンスを指定
- lr_lambda : ラムダ式や関数を指定
- last_epoch : 指定したことないのでいじらなくていいと思う(個人的意見)。深く理解したい人はソースを見てください。
このスケジューラはlr_lambda
にオリジナルの式を入れられるのが最大のポイントです。引数には、stepが呼ばれた回数が与えられ、戻り値としてbase_lr
(optimizerに指定したlr
)からどれほど変化させたいかの比率を返すようにしてください。つまり、学習率はbase_lr * 戻り値
となります。
example
- ラムダ式を与えた場合
scheduler = LambdaLR(optimizer, lr_lambda = lambda epoch: 0.95 ** epoch) for epoch in range(0, 100): #ここは以下省略 scheduler.step()
- 関数を渡した場合
def func(epoch): if epoch < 40: return 0.5 elif epoch < 70: return 0.5**2 elif epoch < 90: return 0.5**3 else: return 0.5**4 scheduler = LambdaLR(optimizer, lr_lambda = func)
StepLR
引数一覧
- optimizer : 省略
- step_size : 何ステップごとに減衰させるかの値
- gamma : 減衰率
- last_epoch : 省略
これは見てもらった方が早いと思います。
example
scheduler = StepLR(optimizer, step_size=20, gamma=0.5)
MultiStepLR
引数一覧
- optimizer : 省略
- milestones : 減衰させたいstepのリスト
- gamma : 減衰率
- last_epoch : 省略
StepLRは減衰ステップが一つに対し、これは複数取れます。注意点として、milestonesには、ステップの小さい順のリストを与えてください。
つまり、10,30,50のステップ数で減衰させたい場合は、[10,30,50]と与えてください。
example
scheduler = MultiStepLR(optimizer, milestones=[10, 20, 40], gamma=0.5) # 10 < 20 < 40
ExponentialLR
引数一覧
- optimizer : 省略
- gamma : 減衰率
- last_epoch : 省略
簡潔に説明するとbase_lr**step回数
が学習率に与えられる。
example
scheduler = ExponentialLR(optimizer, gamma=0.95)
CosineAnnealingLR
引数一覧
- optimizer : 省略
- T_max :
1半周期のステップサイズ - eta_min : 最小学習率(極小値)
- last_epoch : 省略
これは、kaggleやsignateの画像コンペでしばしば解法で登場するsnapshot ensemblingで使用されてます。もちろんそのほかでも使われます。
秋頃(2018年9月ころ)にpytorchになかった気もしなくもなく(見つけてなかっただけかも)、じぶんで実装した記憶があります。
これも見ていただいた方がわかりやすい
example
scheduler = CosineAnnealingLR(optimizer, T_max=20, eta_min=0.001)
ReduceLROnPlateau
引数一覧
- optimizer : 省略
- mode : 監視されている要因が'min'だと下がっているか、'max'だと上がっているかを指定
- factor : 学習率の減衰率
- patience : 何ステップ向上しなければ減衰するかの値
- verbose : 減衰するとき出力してれるかどうか
- threshold : threshold_modeで説明するのに使われてる値
- threshold_mode : 'rel'と'abs'の2種類のどちらかを取ります。
'rel'の場合、減衰の起因となる閾値をmaxモードだと
best*(1+threshold)
、minモードだとbest*(1-threshold)
となります。'abs'の場合は、maxモードだとbest-threshold
、minモードだとthresh+threshold
となります。 - cooldown : 学習率が下がってから、factorを再監視するまでのステップ数
- min_lr : 最小学習率
- eps : nanとかInf回避用の微小数
また、監視する要因はschedulerの関数に存在するstepの引数に与えます。
また、大げさに例をあげると、 - mode : 'min' - patience : 10 - threshold : 0.5 - threshold_mode : 'abs' のように指定すると、監視している値のbestが0.01とすると、 0.01+0.5=0.51よりも高い値を10回連続で取ると減衰します。
example
scheduler = ReduceLROnPlateau(optimizer, 'min') for epoch in range(10): scheduler.step(val_loss) #val_lossが下がらなければ減衰
自作scheduler
PyTorchでは自分でschedulerを作成することも容易です。torch.optim.lr_scheduler._LRSchedulerを継承したクラスを作成すると、上に紹介したようなschedulerを自作することが容易にできます。
今回わかりやすい例として、Loss Surfaces, Mode Connectivity, and Fast Ensembling of DNNs で使われているようなlinear cyclical learning rate scheduleを作成して見たいと思います。
実装
from torch.optim.lr_scheduler import _LRScheduler class LinearCyclicalLR(_LRScheduler): def __init__(self, optimizer, T_max, eta_min=0, last_epoch=-1): self.T_max = T_max self.eta_min = eta_min super(LinearCyclicalLR, self).__init__(optimizer, last_epoch) def get_lr(self): last_step = self.last_epoch % self.T_max point = self.T_max // 2 return [-(base_lr-self.eta_min)* last_step / (point-0.5) + base_lr if last_step < point else (base_lr-self.eta_min)*last_step/(point-0.5) + self.eta_min - base_lr for base_lr in self.base_lrs]
まず、pytorchのschedulerの自作にあたり、_LRSchedulerを継承します。optimizerの学習率更新は、scheduler.step()が呼ばれた際に更新されますが、その際、self.last_epochがインクリメントされます。厳密には、このget_lrが呼ばれる前に行われます。step内でself.get_lrが呼ばれ、その戻り値を新たな学習率に置き換えるように設計されてます。
つまり、自作関数でschedulerを作成するならば、get_lrを自作するのにほとんど等しいです。
今回のget_lrの戻り値のリスト内包表記は中学1年で習うような一次関数なので説明は省略。引数はCosineAnnealingLRを丸パクリ。
これを実際に学習率をプロットすると
scheduler = LinearCyclicalLR(optimizer, T_max=20, eta_min=0.001)
おわりに
いかがだったでしょうか。 最近TLにPyTorchはじめた方が散見されていましたので書いて見ました。個人的にはPyTorch書きやすい/読みやすい/普及しているの3点でおすすめです。 ちなみに、学習率の取得は、scheduler.get_lr()で返ってきます。
しかし、chainer(chainercvなども含める)と比べられると自分で実装しないといけないことが多いかもしれません。どちらも使えるように自分も頑張りたいと思います。
kaggleに初挑戦したお話
はじめに
この記事では、具体的な解法は書いておりませんのでご了承を。 基本的には、初挑戦した感想を書いております。 アドバイス等ございましたらぜひ@katsura_jpまでご教授お願いいたします。
初参戦したコンペ概要
初挑戦コンペはTGS Salt Identification Challengeでした。 どんなものかというと、地震画像(sesmic image)から塩分が含まれている箇所をセマンティックセグメンテーションしようという内容です。 あらかじめ報告しておくと、ゴールド圏内に入ることができました。
参加するきっかけ
まず、なぜ参加しようと決めたのかというお話からしたいと思います。 私自身、Kaggleは1年ほど前に存在を知り、なんとなくアカウントを作成していました。 しかし、自信もなく、具体的に何から始めればいいの?と感じていました。
しかし、今回参加することを決めたのは、一つのことがきっかけとなりました。
私は今、筑波大の情報科学類に所属しています。学群学類というのは実際は他大学でいう学科に該当します。そこで今年「情報特別演習」という授業を履修したのですが、
どんな授業かというと、(情報学で)興味あることやったら単位あげるよ!という内容です。私は、コンピュータビジョン系でミーハーなのでディープラーニングに興味があったので、
同大学で関連している教員をクラスの担任から紹介されて、その先生にお願いしました。
その研究室でディープなら学生の方が詳しいからとの理由で紹介していただいたのがKaggle masterのlyakaap先輩で、先生からkaggleやってみればいいじゃんと言われたのが参加のきっかけです。
参加する前の知識量
参加する前後あたりでの私の知識ですが、実際はとても貧しいものでした。 5月ごろには先輩とkerasでCNNでMNISTのクラス分類を行い、「おぉ〜」と感動したのを今でも覚えていて、5~6月は、kerasの使い方とDLの基本(順伝播、逆伝播、CNN、最適化、活性化関数あたり)を勉強しました。正直、最適化に関しては、数式をみてもわからない部分も多少、いやたくさんあってこころが折れてました。そして、7月ころ、授業の中間発表のためにそれとなくやってみたのが、U-Netで人物をセマンティックセグメンテーション。実際そのくらいの知識量です。
参加した直後
参加したのは、ちょうど夏休みに入る直前でした。実際は夏休み入ってからやろうと思ってたのですが、課題を早めに終わらせていたので暇だからやるかとなって早めました。 当初の目標がブロンズメダル取れたらいいなくらいの気持ちでした。
こちらが初めてのsubmission
たしか、順位的には50%くらいだったかな。kernelの最高スコアより低くて凹んだ記憶があります。
このときは、kernelをほぼコピペでそれにflipのみのaugmentを加えました。が、その直後、それより高いkernelが登場して、凹んだ記憶があります。
参戦して、約一週間、semantic segmentationに関連する記事をあさり、Kfoldを試してみるとスコアが伸びて、シルバー圏内へ。 とても嬉しい気もちになり、目標をブロンズからシルバーへ。
コンペ終了まで
しかし、ここから1ヶ月間休戦へと入る。理由としては、飽きたからではない。もうアイディアが無いからだ。1ヶ月間放置した結果、44位から350位くらいまで下がっていて、 気持ちが鬱に。。。私はkernelばかり漁っていたのだが、8月末からdiscussion漁りを開始する。そこでいろんな情報を提供していたのが、カエルアイコンのHeng先生。 しかし、ResNetの存在すら知らなかった私は、はじめ何を言っているのか理解できなかった。
8月末~9月上旬にかけて、ネットワークの知識を増やそうと思いネットサーフィン。ありがたいことに夏休み期間で時間もあったので、一週間めちゃくちゃ知識を詰め込んだ。 ResNetをはじめSENet,ResNeXtなど。ここで、discussionをみて見ると、Pytorch使ってる人おおいなと思い、Pytorchを使い始めようと思った。
ここ二日、pytorchの使い方とかドキュメントに起きている時間の7割くらい費やしてて、ご飯を食べ損ねる
— catla (@katsura_jp) 2018年9月1日
約一週間Kaggle外で知識を詰め込み、はじめて英論文も読んだ。が、専門用語が多すぎて初めは1本読むのに丸1日。読んだが理解をしたかと言われると、していない。 一番なんだよこれ、と思ったのが"state-of-the-art".
そんなこんなで、充電期間を終え、回し切ったモデルを提出。
もともと、画像サイズを256x256でinputしていたので、128x128でやろうかチームマージしたいと考えてるなか、マージしたのが、お世話になってるlyakaap先輩。
マージ直後は、マージ相手を探していたのでやったーという気分だった。が、その気分もすぐに冷める。マージして思い知ったのが、自分のDLML知識の少なさ。
soft votingやoofの意味を知らず、やばいと感じた。一人でやっていたのは、すべてhard votingだったのもここで気づく。
ここでの心境としては、先輩の足を引っ張るわけにはいかないという焦り。また、数日たち、ギリシャ人の人とマージをする。英語でコミュニケーションをしたことがなかったため、不安も多かった。しかし、チームメイトは、こんな私にも優しかった。
kaggle mastar5人とnovice1人。議論を共にする自信が0を下回ると、食らいついていこうという気持ちが2147483647に変わった。 基本的にDL系は他の人に任せて後処理に回った(が、終わってから後処理部分の核となっていたものが機能していない説が出てややアイデンテティを消失気味になっている笑)
[2018/10/22 追記]
チームメイトが検証くれた結果、後処理なしではゴールド圏に入れなかった模様。
結果
結果として、当初ブロンズ目標だったものが、ゴールドを手にしていた。嬉しい気持ちもあるが、悔しいという気持ちもある。今回に関しては、個人戦だとシルバー入れていたのかも不安だし、謙遜しすぎかもしれないがおこぼれをいただいた感もある。自分がもっと知識をつけていたらもっと上にいけたはずだと感じもした。
ただ、topの人たちが、どのようにスコアを上げるかを近くでみれたのはもっとも大きな収穫だ。
これから
私はまだ大学2年。時間もたくさんあるし、1、2年で単位も順調に取っているので、来年以降はもっと時間がある。lyakaap先輩を慕っているので、院卒業時にはkaggle masterなりたいと野望を心の中に秘めていたが学部卒までになってやると変えた。
また、これからは、画像認識に関する論文を読んで、知識をもっと入れて、いつか今回のメンバーとチームを組むにしろ、戦うにしろ、肩を並べられる存在になりたいと強く思った。
さいごに
やや、ポエム気味な文章になってしまったのは申し訳ない。。。
今回のチームメイトには最大の感謝とこれからの健闘を祈りたい。
Thank you "Leaning the Future"!!
追記[2019-02-10]
Kaggle Meetup Tokyo#5で発表したスライドです。解法載せてあります。