AI_ML_DL’s diary

人工知能、機械学習、ディープラーニングの日記

Kaggle散歩(February 2021)

Kaggle散歩(February 2021)

HuBMAPコンペは3月26日が最終日。(日本時間:3月27日午前9時00分)

2月3日追記:最終日は新規データが追加されてから2か月後とのこと、現時点で新規データは追加されていないため、4月2日以降になることは確実。

2月24日追記:データの更新はまだのようだ。最終日はデータ更新から2か月後との方針に変わりなければ、最終日は4月末。

 

2月1日(月)

HuBMAP

昨日、efficientnet-UnetのB5, B6, B7, B8について、同一条件で計算した結果、LBスコアは、B5: 0.831, B6: 0.831, B7: 0.829, B8: 0.809となった。efficientnet-Unetを用いている公開コードでは、B4またはB5がベスト、となっているようである。スコアの大小関係は、画像サイズやaugmentationの複雑さなどによって変わる可能性があるので、いまは、どれがよいとはいえない。最終的には、public_LBではなく、private_LBだということも頭に入れておかないといけない。

DiceLoss, LovaszLoss, JaccardLoss, FocalLossなどを使って計算し、BCEWithLogitsLossとの比較を行ったところ、LBスコアは次のようになった。

BCE: 0.829, DiceLoss: 0.794, LovaszLoss: 0.830, JaccardLoss: 0.832, FocalLoss: 0.833

DiceLossのみ、他と少し違っている。なぜだろう。

明日は、segmentation modelの比較をやってみよう。

GPUの残り時間が7時間程度になったので、encoderを中くらいのefficientnetB4にして、Unet、Unet++, MAnet, Linknet, FPNなどのモデルの計算をしてみよう。

 

2月2日(火)

HuBMAP

efficientnetB4をEncoderにして、Unet, Unet++, MAnet, FPNのモデルについて計算したところ、LBスコアは、次のようになった。

Unet: 0.832, Unet++: 0.823, FPN: 0.830, MAnet: 0.825

もっと違った結果を予想(期待)していたのだが、どんぐりの背比べといったところか。

各モデルのパラメータは、ほぼ、デフォルトのままで、最適化していないので、今は動作確認しているだけ、ということにしておこう。

そんなことを言っている場合ではないかもしれない。

今日はBCELossを使ったのだが、この課題のmetricsはmean Dice coefficientなのだから、DiceLossを使ったほうが良かったのかもしれない。

しかし、それで違いが現れるかどうか、期待しているような結果が得られるかどうかはわからない。

さて、GPUの割当時間の残りが3時間くらいになったので、明日から3日間は、機能アップのためのコードを探して組み込もう。

Loss関数の組み合わせ(BCEとDice)は今日組み込んだ。

DiceLossとLovaszLoss, JaccardLoss, FocalLossなどとの組み合わせもテストできるように組み込んでおこう。

あとは、TTA、pseudo labelの作成と訓練、モデルのアンサンブルなどが課題となる。

最新のDiscussionから2つのことがわかった。1つは、データの更新が遅れていること(1月中旬の予定だった)で、2つめは、データ更新の2か月後が締め切りとなる予定とのことだから、締め切りは4月になりそうだということ。

Kaggleスタッフからの1か月前の情報だが、20個のデータが全て公開され、新たに10個のprivate_dataが追加されるとのことなので、訓練に使えるデータが増えることになる。これは、正確さ向上に役立つのだが、増えたデータを有効に使いこなす能力が問われることになり、使える計算資源が限られる者にはハンディーとなる可能性がある。しかしながら、計算資源は同じはずなのに、次元の違う結果を出す人もいる。どのコンペだったか忘れたがデータ量が多くて扱えないと悲鳴があがっていたとき、高速処理ができるパッケージやモジュールやツールが紹介されていた。コンペではあらゆる能力が試されている。不可能を可能にする能力が求められている。

 

2月3日(水)

HuBMAP

今日はLossの組み合わせと、TTAを実現しよう。

Effb4-Unetについて、BCEとDiceとLovaszとJaccardとFocalを等分に混合したLossを適用してみた。その結果、BCE単独のLBが0.832であったのに対し、0.827となった。結果はともかく、動作しているようだ。

中身がわからないままではだめなので、Lossについて学ぼう。

FocalLoss:Focal Loss for Dense Object Detection
Tsung-Yi Lin, Priya Goyal, Ross Girshick, Kaiming He, and Piotr Doll´ar

f:id:AI_ML_DL:20210203105733p:plain

f:id:AI_ML_DL:20210203115123p:plain

これが、FocalLossのパラメータ、alphaとgammaだろうと思う。

違うかもしれないな。

 

昨日「この課題のmetricsはmean Dice coefficientなのだから、DiceLossを使ったほうが良かったのかもしれない。」と書いたが、そうだろうか。

Google Scholarで検索した結果では、多くの場合、Dice lossのシンプルな定義を示した後、様々なオプションを付加したり、BCEなど他のLossと組み合わせている。segmentation modelsに含まれているDicelossは最もシンプルなタイプなので、Eff-Unetに対してLoss functionを比較したときにDice lossだけが少し低いLB値(0.794他の4種類は0.830~0.833)になったのは、種々の論文が示唆していることと整合している。

*文献で良い結果を出しているLoss functionのパラメータとモデルの組み合わせを試してみたが、ダメだった。LB=0.833を超えると予想していたら、0.810だった。UnetとUnet++の比較にしてもそうだ、Unet++の方が良いという結果は、まだ得られていない。

Lossとモデルの組み合わせにしても、試してみないとわからない事ばかりなので、効率よく試す、実験する、ということを念頭において、最小の計算時間で適切な組み合わせを見つける方法を考えなければいけない。

encoderはefficientnetだけしか使っていないが、それでも、b0からb8まであるし、weightも3種類(imagenet, advprop, noisy-student)ある。検討する内容、最適化する内容に応じて、どれを使うかを適切に決める必要がある。

チューニングが主たる作業だった頃は、0.0001でも上がりそうだと、すぐに飛びついて計算し、順位を1つでも上にあげようとしていた。その癖が残っていて、0.001くらい上がりそうだと、すぐにやってみたくなる。それが、GPU消費(浪費)時間の増大につながっている。0.001の積み上げは重要だが、そこで止まってしまうようなチューニングではなく、さらに上を目指せる課題を探すことにもっと注力すべきなのだろうと思う。

今ある5種類のLossについては単純な線形結合で、自由に組み合わせることができるようにした。(注:単純な線形結合ではうまく計算されていない可能性がある)Lossのパラメータ設定については十分調べることはできなかったが、パラメータの意味を理解しながら実験していくしかなさそうである。

明日は、TTAを組み込む。

GitHubで、test time augmentation pytorchで検索し、上位に出てきた次のサイトがよさそうだったので、使わせていただこう。

https://github.com/qubvel/ttach

すぐにコードを組み込むことができなくても、TTAに関する情報収集の基地として便利なサイトだと思う。

Datasetに入れて、notebookのinput dataに追加し、次の命令で、インターネットなしで、インストールすることができるようにした。

!pip install ../input/ttach-pytorch -f ./ --no-index

 

2月4日(木)

HuBMAP

TTAによって追加する画像の種類と枚数を決める基準は何だろう。 Chris Deotteさんが、枚数に対して効果をプロットとしていたような記憶がある。12~15枚がピークだったような気がする。モデルやハイパーパラメータ、augmentationの内容によって、効果は違うと思うので、自分で実験することが重要。

github.com/qubvel/ttachから引用:

Similar to what Data Augmentation is doing to the training set, the purpose of Test Time Augmentation is to perform random modifications to the test images. Thus, instead of showing the regular, “clean” images, only once to the trained model, we will show it the augmented images several times. We will then average the predictions of each corresponding image and take that as our final guess [1].

データ拡張がトレーニングセットに対して行っていることと同様に、テスト時間拡張(Test Time Augmentation)の目的は、テストイメージにランダムな変更を実行することです。 したがって、通常の「クリーンな」画像をトレーニング済みモデルに1回だけ表示する代わりに、拡張画像を数回表示します。 次に、対応する各画像の予測を平均し、それを最終的な推測として使用します[1]。by Google翻訳

HuBMAPコンペサイトに、TTAを非常にわかりやすく、具体的に説明したcodeが公開されている。Let's Understand TTA in Segmentation by @ joshi98kishan

この図面はすばらしい!

f:id:AI_ML_DL:20210204120424p:plain

github.com/qubvel/ttachに説明されている手順:reverse transformations for each batch of masks/labels、の意味が一目でわかって納得した。図中では、文字が小さくて読み取りにくいが、TTA in Segmentationのすぐ下に、Deaugmentationと書かれている。

要するに、変換して変形した形状を、再度変換することによって、元の形状に戻すということだ。

ところで、torchvision.transformとalbumentationの区別がつかなくて困っている。

それぞれTとAを前に付けていればわかるのだが、

trfm = A.Compose([
      A.Resize(NEW_SIZE,NEW_SIZE),
      A.HorizontalFlip(p=0.5),
      A.VerticalFlip(p=0.5),

これなら問題ないが、

transforms = Compose([
        HorizontalFlip( ),
        VerticalFlip( ),
        RandomRotate90( ),

これはどっちなんだろう。torchvisionには、RandomRotate90( )は無い。

このコードは、次のように両方のパッケージ/モジュールを呼び出していて、as Tとかas Aを使っていないので見づらくなっているように思う。

import torchvision
from torchvision import transforms

from albumentations import *

albumentationsのドキュメントでは、次のように例示している。

import albumentations as A

まずは、TTAの枠組みを作ろう。参考にするコードは2つあって、1つは、垂直と水平のフリップのみ、もう1つは、垂直と水平のフリップに加えて、回転と、ランダムに選んだ3種類の光学的条件(コントラスト、色相、明度など)のうちの1つ

どれが効果的なのかを調べるためにどうすればよいのか。

train time augmentation(TRAとしよう)の効果とtest time augmentation(TTA)の効果を個別に調べる、連動させて調べる、無関係に連動させて調べる。

TRAのみ。TTAのみ。TRAとTTAを一致させる。TRAとTTAを直交させる(同一種類の変更を用いない)

いずれにしても、まずは効果的なTRAの加工条件を探索する必要がある。

TTA加工条件は、TRA加工条件と連動できるようにする。(現状はtrainとinferenceがつながっているので容易にできる)

TTAのお手本のコードは、PyTorchでTPUを使っており、trainとinferenceを分けているので、TTAと一緒に、trainとinferenceの分離、および、TPUの使い方を学ぶことにする。

計算資源のことで不足があるなら、文句を言う前に、Kaggleが提供してくれている計算環境をすべて利用しなければならない。

https://www.kaggle.com/docs/tpu:ここに、How to Use Kaggle, Help and Documentationというのがあって、KerasとPytorchに分けて説明されている。

コードコンペでは、inferenceにTPUは使えないので、TPUを用いたtrainからGPUを用いたinferenceに引き継ぐ方法が説明されている。

TPUs in Code Competitions
Due to technical limitations for certain kinds of code-only competitions we aren’t able to support notebook submissions that run on TPUs, made clear in the competition's rules. But that doesn’t mean you can’t use TPUs to train your models!

A workaround to this restriction is to run your model training in a separate notebook that uses TPUs, and then to save the resulting model. You can then load that model into the notebook you use for your submission and use a GPU to run inference and generate your predictions.

コードコンペティションのTPU
特定の種類のコードのみのコンテストの技術的な制限により、コンテストのルールで明確にされているように、TPUで実行されるノートブックの提出をサポートすることはできません。 ただし、TPUを使用してモデルをトレーニングできないわけではありません。

この制限の回避策は、TPUを使用する別のノートブックでモデルトレーニングを実行し、結果のモデルを保存することです。 次に、そのモデルを送信に使用するノートブックにロードし、GPUを使用して推論を実行して予測を生成できます。by Google 翻訳

なんとかなりそうだな。これからは、TPUも使おう。 

 

早速、お手本予定のコードをTPUでRunさせたのだが、1回目は2エポックで停止、2回目は14エポックで停止した。

エラー箇所とエラーメッセージ:

---> 36 start_method = 'fork')

ProcessExitedException: process 3 terminated with signal SIGABRT

View Session Metricsを見ると、CPUが399%になっていたので、この負荷の大きさがエラーを引き起こしているかもしれないと思い、負荷を下げるためにbatch_sizeを32から16にしてみた。すると、止まらずに動くようになった。しかし、trainの進行が遅くなり、結果としてスコアも悪くなった。使っているversionのコードでは、trainの停止条件をエポック数だけで決めているので、スコアへの影響が大きいのだろう。

 

2月5日(金)

HuBMAP

今日もGPUは使えないので、TPUのコードで、augmentationの効果を調べる。trainだけのコードなので、val_diceで判断する。

当然、trainの場合にのみaugmentationを使うのだが、ここでふと思った。

validationのときに、TTAで使うaugmentationをためしてみてはどうかと。

trainのaugmentationとvalidationのaugmentationをそれぞれ、train_aug, val_augとしておけば、できるかもしれない。

簡単にはいかない。deaugmentationして平均をとる処理が必要であり、それをしないで、単に画像を増やしてもTTAのテストにはならない。

まずは、手元にある3組のaugmentationの効果を比べよう。

augmentation無しの場合のval_diceは0.85、2組はほぼ同等の0.86、幾何学的変形を強くした1組は0.79であった。現状はこの程度の効果であり、適切にやらないと、逆効果になってしまうということがわかった。augmentationによって、0.85が0.79になることがあるというのは、衝撃だ。augmentationすれば、予測性能は必ず上がるものと思っていた。EfficientnetB7-Unetで、AlbumentationのHPに紹介されている組み合わせの例をそのまま適用して、LBスコアが0.80に達しなかったとき、まさか、何もしない場合よりも悪いスコアになっているとは思わなかった。

data augmentationの本質を理解しよう。

data augmentationの本質を理解するためには、教師データの本質を理解している必要がある。機械学習モデルは、labelの正確さを超えることはない。画像とlabelは、pixelレベルで1対1に正しく対応していなければならない。機械学習モデルは、画像とlabelの対応関係を根拠として、新たな画像に対して、labelを推測する。labelが正確でない場合、機械学習モデルは、正確でないlabelを推測する。

無償で使わせていただいている立場では言いにくいことだが、このコンペのlabelは、ほぼ滑らかな曲線であるべき糸球体の境界を、不十分な辺の数の多角形で近似しているので、正しくない。スムージング処理によって改善されるかどうか調べてみようと思う。そのために、マスクの形状を加工する方法を探そう。

 

2月6日(土)

HuBMAP:964 teams, 2 months to go

良いaugmentationの方法はないものかと捜し歩いているがみつからない。

efficientnetb0-Unetは今日だけだが、LB=0.826がベストスコアだった。

このaugmentationの探索からは、TTAには、あまり期待できないが、3回の推測結果の平均値をとるという意味では、スコアアップの可能性はありそうだ。

 

2月7日(日)

HuBMAP

A.OneOf([
      A.RandomContrast( ),
      A.RandomGamma( ),
      A.RandomBrightness( ),
      A.ColorJitter(brightness=0.07, contrast=0.07, saturation=0.1, hue=0.1,      always_apply=False, p=0.3),
], p=0.3), 

最後のP=0.3としてあるところを、0.0から0.6まで変えた時の、efficientnetB0-Unetによる予測結果のLBスコアは、次のようになった。

0.0: 0.822, 0.1: 0.820, 0.2: 0.826, 0.3: 0.826, 0.4: 0.827, 0.5: 0.825, 0.6: 0.824

この結果から、この部分のaugmentationは効果があって、0.2から0.5くらいであれば大差ないように見える。

次は、p=0.4で固定して、efficientnetを複雑にしていくとどうなるかを調べてみよう。

b1からb5まで順に計算してみるつもりだ。気が変わらなければ。

 

2月8日(月)

HuBMAP:971 teams, 2 months to go

OneCycleLRを使っているのだが、性能が発揮できていないような気がしてきた。

論文を読んで理解しよう。

Super-Convergence: Very Fast Training of Neural Networks Using Large Learning Rates
Leslie N. Smith, Nicholay Topin, arXiv:1708.07120v3 [cs.LG] 17 May 2018

Abstract
In this paper, we describe a phenomenon, which we named “super-convergence”,
where neural networks can be trained an order of magnitude faster than with
standard training methods. The existence of super-convergence is relevant to
understanding why deep networks generalize well. One of the key elements of
super-convergence is training with one learning rate cycle and a large maximum
learning rate. A primary insight that allows super-convergence training is that large
learning rates regularize the training, hence requiring a reduction of all other forms
of regularization in order to preserve an optimal regularization balance. We also
derive a simplification of the Hessian Free optimization method to compute an
estimate of the optimal learning rate. Experiments demonstrate super-convergence
for Cifar-10/100, MNIST and Imagenet datasets, and resnet, wide-resnet, densenet,
and inception architectures. In addition, we show that super-convergence provides
a greater boost in performance relative to standard training when the amount of
labeled training data is limited. The architectures to replicate this work will be
made available upon publication.

概要
この論文では、ニューラルネットワークを標準的なトレーニング方法よりも1桁速くトレーニングできる、「超収束」と名付けた現象について説明します。超収束の存在は、なぜ深いネットワークがうまく一般化するのかを理解することに関連しています。超収束の重要な要素の1つは、1つの学習率サイクルと大きな最大学習率でのトレーニンです。スーパーコンバージェンストレーニングを可能にする主な洞察は、学習率が高いとトレーニングが正規化されるため、最適な正規化バランスを維持するために、他のすべての形式の正規化を減らす必要があるということです。また、最適な学習率の推定値を計算するために、ヘッセ行列のない最適化手法の簡略化を導き出します。実験は、Cifar-10 / 100、MNIST、Imagenetデータセット、およびresnet、wide-resnet、densenet、およびinceptionアーキテクチャの超収束を示しています。さらに、ラベル付けされたトレーニングデータの量が限られている場合、スーパーコンバージェンスが標準トレーニングと比較してパフォーマンスを大幅に向上させることを示します。この作業を複製するためのアーキテクチャは、公開時に利用可能になります。

by Google翻訳

OneCycleLRのexampleは、optimizerにSGDを使っているが、そのexampleをコピペして使っても、良い結果が得られない(まともな結果が得られない)ので、optimizerにAdamWを使っている。

論文では、optimizerはSGDだけであり、SGDの良さを強調している。

While deep neural networks have achieved amazing successes in a range of applications, understanding why stochastic gradient descent (SGD) works so well remains an open and active area of research. Specifically, we show that, for certain hyper-parameter values, using very large learning rates with the cyclical learning rate (CLR) method [Smith, 2015, 2017] can speed up training by as much as an order of magnitude. We named this phenomenon “super-convergence.” In addition to the practical value of super-convergence training, this paper provides empirical support and theoretical insights
to the active discussions in the literature on stochastic gradient descent (SGD) and understanding generalization.

ディープニューラルネットワークはさまざまなアプリケーションで驚くべき成功を収めていますが、確率的勾配降下法SGD)がうまく機能する理由を理解することは、オープンで活発な研究分野です。具体的には、特定のハイパーパラメータ値について、循環学習率(CLR)法[Smith、2015、2017]で非常に大きな学習率を使用すると、トレーニングを1桁も高速化できることを示します。 この現象を「超収束」と名付けました。 超収束トレーニングの実用的な価値に加えて、この論文は、確率的勾配降下法SGD)と一般化の理解に関する文献での活発な議論に対する経験的サポートと理論的洞察を提供します。 by Google翻訳

optimizerをSGDにして、lrとMax_lrを変更してみた。AdamWでのLBスコアが0.827となったモデルである。LBスコアは0.815と0.821で、他にもトライした2条件では、良い結果を期待できそうなval_lossの変化だったが、Submission Scoring Errorになってしまった。適切な値というのは、モデル、データ、課題などの組み合わせによって違ってくるので、計画的に実験しないと、なかなか、良い結果にたどり着けない。

max_lrの設定値が重要で、目安はoptimizerのデフォルトのlr値の10倍から30倍程度ということだろうと思う。SGDなら1から3、AdamWなら1e-2から3e-2程度という感触を得たので、明日、気が向いたら確かめてみようと思う。

 

2月9日(火)

HuBMAP

SGD:

Max_lr=0.3: LB=0.815

Max_lr=2: LB=0.824

Max_lr=3: LB=0.821

AdamW:

Max_lr=1e-3: LB=0.827(過去のデータ)

Max_lr=1e-2: LB=0.818

Max_lr=2e-2: LB=0.805

Max_lr=3e-2: LB=0.812

この3件、プログラムミスの影響を受けている可能性あり。

OneCycleLRの条件検討を行ってきたここまでの結果では、Max_Lrをデフォルトのlr(SGD: 0.1, AdamW: 1e-3)の10~30倍にすることによるLBスコアは、SGDにおいては少し上がったが、AdamWでは、逆に下がった。この手法は、SGD限定なのかもしれない。

 

気分転換に、Encoderを変えてみようと思う。

ResNet18, 34, 50, 101, 151, Senet154, Se_Resnet50, 101, 152, Se_Resnext50_32x4d, 101, Densnet121, 169, 201, 161, Inceptionresnetv2, Inceptionv4, Xception, Dpn68, 98, 131, VGG11, 13, 16, 19, などを使う準備をした。

 

2月10日(水)

HuBMAP

Efficientnet-UnetでLB~0.83を示すコードのEncoderを次のモデルで置き換え、他の条件は一切変えずに走らせた。

Resnet18: LB=0.813

Resnet34: LB=0.805

Resnet50: LB=0.810

Resnet101: LB=0.815

Rennet152: LB=0.812

この5件、プログラムミスの影響を受けている可能性あり。

次の論文は、最新のテクニックを駆使すれば、昔のモデル(ResNet)でも新しいモデル(EfficientNet)に匹敵するような性能を示す、ということを示している論文のようだが、そういうテクニックを持ち合わせていない場合は、新しい高性能なモデルを使う方が良い結果を得られますよ、というふうに読める。

Compounding the Performance Improvements of Assembled Techniques in a Convolutional Neural Network
Jungkyu Lee, Taeryun Won, Tae Kwan Lee, Hyemin Lee, Geonmo Gu, Kiho Hong

arXiv:2001.06268v2 [cs.CV] 13 Mar 2020

f:id:AI_ML_DL:20210210203558p:plain

f:id:AI_ML_DL:20210210203729p:plain

Autoaugという単語が気になる。

AutoAugment: Learning Augmentation Strategies from Data
Ekin D. Cubuk , Barret Zoph, Dandelion Man´e, Vijay Vasudevan, Quoc V. Le

arXiv:1805.09501v3 [cs.CV] 11 Apr 2019

 

明日の予定:気が変わらなければ、se_resnet50, 101, 152, se_resnext50_32x4d, 101をencoderに用いたUnetを使ってみる。

 

2月11日(木)

HuBMAP

①se_resnext50_32x4d: LB=0.822

②se_resnext101_32x4d: LB=0.809

➂senet154: LB=0.823

この3件、プログラムミスの影響を受けている可能性あり。

Resnetよりは、少し、良い結果になった。

ちなみに、trainの所要時間の比率は、①1.0 : ②1.5 : ➂2.6であった。

もっと良いモデルはないものか。 

ResNeSt: Split-Attention Networks
Hang Zhang et al., arXiv:2004.08955v2 [cs.CV] 30 Dec 2020

f:id:AI_ML_DL:20210211102800p:plain
7. Conclusion
This work proposes the ResNeSt architecture that leverages the channel-wise attention with multi-path representation into a single unified Split-Attention block. The model
universally improves the learned feature representations to boost performance across image classification, object detection, instance segmentation and semantic segmentation. Our Split-Attention block is easy to work with (i.e., dropin replacement of a standard residual block), computationally efficient (i.e., 32% less latency than EfficientNet-B7 but with better accuracy), and transfers well. We believe ResNeSt can have an impact across multiple vision tasks, as it has already been adopted by multiple winning entries in 2020 COCO-LVIS challenge and 2020 DAVIS-VOS chanllenge.

f:id:AI_ML_DL:20210211100031p:plain

こういうのを見ると、あらためて、EfficientNetは凄いんだな、と思う。

予定を変更して、EfficientNet-B7とResNeSt-269を比較してみよう。

EfficientNet-B7: 0.682

明らかにおかしな結果になったので、プログラムを見直したら、inferenceのところで実験的に変えていたパラメータを元に戻し忘れていた。2月9日から2月11日15時までの間に行った計算結果は、間違っている可能性がある。

以下は、元に戻したコードで計算した結果である。

ResNeSt-269: LB=0.829

EfficientNet-B7:0.652

???、何か変だ。

 

2月12日(金)

HuBMAP

EfficientNetB0-UnetのLBスコアのバッチサイズ(32, 16, 8, 4)による違いを調べてみた。(commitまで昨日行い、今朝9時すぎにsubmitした。)

Batch_size=4:   LB=0.819

Batch_size=8:   LB=0.647

Batch_size=16: LB=0.830

Batch_size=32: LB=0.826

ここでも、異常値が生じた。

これらの異常は、overfittingによって生じている可能性がある。

lrスケジューラーとして使っているOneCycleLRは、overfittingしにくく、再現性が高いので、信用しきって使っているが、パラメータは殆どデフォルト値で、エポック数は時間節約のために10に設定しているが、公開コードを見ていると、エポック数として25±5くらいが良く使われていることなどを勘案すると、問題発生の原因になっている可能性がある。OneCycleLRの適切なパラメータの探索が必要だ。

さらに、予測したマスクを画像に戻して、trained_modelが予測しているsegmentationがどうなっているのかを、画像として確認できるようにしよう。

メモ:予測結果を画像に変換して確認、K-fold, Auto-augmentation, TTA, DropBlock, Model-ensemble, pseud_label, ...

 

これはどうなんだろう。著者は、Google Research, Brain Teamとなっている。

EfficientNetの論文のときと似たような図がトップページに掲載されている。

EfficientDet: Scalable and Efficient Object Detection
Mingxing Tan, Ruoming Pang, and Quoc V. Le,  arXiv:1911.09070v7 [cs.CV] 27 Jul 2020

f:id:AI_ML_DL:20210212235542p:plain

While our EfficientDet models are mainly designed for object detection, we are also interested in their performance on other tasks such as semantic segmentation. Following
[19], we modify our EfficientDet model to keep feature level fP2; P3; :::; P7g in BiFPN, but only use P2 for the final per-pixel classification. For simplicity, here we only evaluate a EfficientDet-D4 based model, which uses a ImageNet pretrained EfficientNet-B4 backbone (similar size to ResNet-50). We set the channel size to 128 for BiFPN and
256 for classification head. Both BiFPN and classification head are repeated by 3 times. 

f:id:AI_ML_DL:20210212234921p:plain

f:id:AI_ML_DL:20210212235123p:plain

f:id:AI_ML_DL:20210212235207p:plain

この論文見てたら、「いつまでUnet使っているのか」「なぜ、FPNとかDeepLav3を使わないのか」さらに、「それらを飛ばして、EfficientDetを使ってみたらどうか」と言われているような気がする。

 

2月1日のデータだが、EfficientnetB7-Unetで、損失関数を評価した結果は、次のようになった。これで、DiceLossを使わなくなったのだが、

BCELoss:       LB=0.829(1月31日)

DiceLoss:      LB=0.794

LovaszLoss:  LB=0.830

JaccardLoss: LB=0.832

FocalLoss:    LB=0.833
いずれもOneCycleLRの10エポックで、Lossの収束具合から判断すると、FocalLossは収束が速く、10エポックでも十分という感じだが、他は、もっとエポック数を増やせば、よくなりそうな気配を感じる。

ということで、明日は、エポック数依存性を、DiceLossについて調べてみよう。

EncoderとDecoderの組み合わせをどうするか。

 

2月13日(土)

HuBMAP:990 teams, a month to go(データの更新から2か月:データ更新待ち)

今日から1週間のGPUの割当は、先週と同じで、43時間である。

まずは、DiceLossで、OneCycleLRのエポック数を10回から20回、30回に増やしてみよう。

B0とB7では、単位エポックあたりの計算時間が3倍くらい違うので、B0でテストしてみよう。現状、256x256で、8000枚程度の画像処理なので、B0で十分だろうと思う。

EfficientNetB0-Unet, DiceLoss:

epochs=10: LB=0.826

epochs=20: LB=0.830

epochs=30: LB=0.832

まあまあ、期待していたような結果になった。

それならば、BCELossは10エポックのデータがあるので、20エポックと30エポックの場合について計算してみよう。

BCEWithLogitsLoss:

epochs=10: LB=0.830(2月11日のデータ)

epochs=20: LB=0.822

epochs=30: LB=中止:計算が途中で停止し、かつ、Lossの変化も適正ではなかった!

BCELossは期待外れ、ということになった。

今日の最後は、JaccardLossの20エポックにしてみよう。

JaccardLoss:

epochs=10: LB=0.832(EfficientNetB7-Unet:2月1日計算)

epochs=20: LB=0.829(EfficientNetB0-Unet)

これらは比較できない!

B7の10エポックがB0の20エポックを少し上回っただけ。

複数の条件が異なる場合の比較は不可。

2月2日に、Decoderを比較したところ、以下のようになった。

LossFunction=BCE, lr_Scheduler=OneCycleLR(epochs=10)

Encoder:EfficientNetB4

Decoder:Unet: LB=0.832, Unet++: LB=0.823, FPN: LB=0.830, MAnet: LB=0.825

これらの結果に対しては、「動作確認しただけ」と書いたが、この後今日まで、Decoderは、Unetを使ってきた。

このときのメモをよく見ると、Unetのパラメータはデフォルトではなく、decoder_attention_type="scse"としていた。

 

明日は、DecoderとしてFPN、Loss functionとしてDiceLossを使ってみよう。

そろそろ、パーソナルベストのLB=0.833を超えたいな。

 

2月14日(日)

HuBMAP:994 teams

EfficientNetBn-FPN-DiceLoss-AdamW-OneCycleLR-soft_augmentation:

EfficientNetB3-FPN_10 epochs: LB=0.826

EfficientNetB4-FPN_10 epochs: LB=0.829

EfficientNetB5-FPN_10 epochs: LB=0.832:EfficientNetB5-FPN_20 epochs: LB=0.834

EfficientNetB6-FPN_10 epochs: LB=0.828

似たり寄ったりというところかな。

B3からB6の比較では、B5が良さそうという結果だが、Unetでは、B0でも十分という結果なのだが。Decoderによって、最適なEncoderのサイズが違うのかもしれない。

EncoderにEfficientNetB5、Loss functionにDiceLossを使って、9種類のDecorderとの組み合わせで、LBスコアの比較をしてみよう。DiceLossはBCEより収束が遅いので、ベースのエポック数を15にしよう。

 

2月15日(月)

HuBMAP: 998 teams

EfficientNetB5-Decoder-DiceLoss-AdamW-OneCycleLR_15epochs-soft_augmentation:

Unet_scse:      LB=0.827

Unet++_scse: LB=0.831

MAnet:           LB=0.831

Linknet:          LB=0.832

Linknetの論文を示しておこう。

LinkNet: Exploiting Encoder Representations for Efficient Semantic Segmentation

Abhishek Chaurasia and Eugenio Culurciello, postarXiv:1707.03718v1 [cs.CV] 14 Jun 2017

f:id:AI_ML_DL:20210215121009p:plain

f:id:AI_ML_DL:20210215121157p:plain

LinkNetは計算速度の速さが売りのようだが、1エポックあたりの計算時間はUnetより10%くらい短いだけだった。LBスコアは(今回の条件では)Unetより少しよさそうだ。


OneCycleLR

OneCycleLRは、モデルが小さい場合には、エポック数を多くする方が良いようだ。その理由は、モデルが小さいほど収束が遅いから、ということのようだ。

モデルが大きい場合には、エポック数を増やしていくと、overfittingして、スコアが低下することがあるようだ。

EfficientNetB0-Unetで、10, 20, 30のエポック数に対して、 LBスコアが、0.826, 0.830, 0.832となったのは、DiceLossが相対的に収束が遅く、かつ、小さなモデルであるB0と組み合わせたことによるのかもしれない。

BCELossを用いた場合には、DiceLossよりも収束が速いので、OneCycleLRのエポック数を増やすと、overfittingになりやすいようである。

ただし、これらの現象は、OneCycleLRと組み合わせるOptimizerの種類やMax_Lrの設定値によっても変わるようなので、適宜、チューニングすることが必要だろう。

組合せの数が多くて、収拾がつかなくなりそうだ。

これだけ多くのモデルを扱っているのだから、アンサンブルする方法を考えて、スコアアップにつなげていきたいものだ。

 

明日submitする予定の計算実施:

EfficientNetB5-Decoder-DiceLoss-AdamW-OneCycleLR_15epochs-soft_augmentation:

PSPNet: 

PAN: 1エポックの計算時間が長いため中止

DeepLabV3: GPUモリーオーバー

DeepLabV3+: 1エポックの計算時間が長いため中止

FPNは10エポックと20エポックの結果があるので、別の検討を行う。

EfficientNetB5-FPN-DiceLossを用いたMax_Lrの検討:

optimizer: SGD:max_lr=3(x30): エポック数を20、30、50とする。

optimizer: AdamW:max_lr=1e-2(x10): エポック数は30とする。

torch.optimのマニュアルでは、SGDのlrはlr=<required parameter>となっていて、デフォルト値は与えられていない。すぐ後のexampleに、lr=0.1とあるだけ。

AdamWのlrのデフォルト値はlr=0.001となっている。

 

明日から、今使っているコードのinferenceの部分を理解し、TTAを組み込む。 

 

2月16日(火)

HuBMAP

EfficientNetB5-Decoder-DiceLoss-AdamW-OneCycleLR_15epochs-soft_augmentation:

PSPNet: LB=0.821

この条件で計算した中では、PSPNetのLBスコアが最も低かった。

PSPNetの論文を見ておこう。

Pyramid scene parsing network
H Zhao, J Shi, X Qi, X Wang… - Proceedings of the IEEE …, 2017 - openaccess.thecvf.com

Abstract
Scene parsing is challenging for unrestricted open vocabulary and diverse scenes. In this paper, we exploit the capability of global context information by different-regionbased context aggregation through our pyramid pooling module together with the proposed pyramid scene parsing network (PSPNet). Our global prior representation is effective to produce good quality results on the scene parsing task, while PSPNet provides a superior framework for pixellevel prediction. The proposed approach achieves state-ofthe-art performance on various datasets. It came first in ImageNet scene parsing challenge 2016, PASCAL VOC 2012 benchmark and Cityscapes benchmark. A single PSPNet yields the new record of mIoU accuracy 85.4% on PASCAL VOC 2012 and accuracy 80.2% on Cityscapes.

f:id:AI_ML_DL:20210226091810p:plain

 

OneCycleLR(DiceLoss)のmax_lrの検討:

optimizer: SGD:max_lr=3(x30):

epochs=20: LB=0.826: (2/18 submit)

epochs=30: LB=0.829: (2/18 submit)

epochs=50: LB=0.828

optimizer: AdamW:max_lr=1e-2(x10):

epochs=30: LB=(submission scoring error): (2/18再度submit): LB=0.827

SGDでもAdamWに近い結果が得られるようになってきた。さらに、50エポックまで増やしても効果はなく、30エポックで十分だということを示すものとなった。

 

TTA

TTAを組み込んだコードのベースは、自分が使っているコードのベースと同一なので、単に移植するだけであれば、難しくないと思われる。

と、思って取り掛かってみたが、そう簡単ではない。

最初に、Lambda、が定義されていないとのエラー、次は、preprocess_inputでひっかかり、これらを使わない方法を考えていたのだが、get_preprocessing_fn、が気になって調べてみた結果、これらが重要な関数であることがわかった。

調べてみると、TTAを追加した人は、Segmentation Modelsのパッケージを使っており、そのパッケージのマニュアルには、次の例に従って入力データを前処理するように書かれているのだが、自分は同じSegmentation Modelsのパッケージを使っていながら、これらの関数を知らなかったために、エラーの意味するところが分からなかった、ということである。これは重要な情報である。今使っているコードを見直す必要があるかもしれない。(まずは、このコードの機能と使い方を理解しよう)

from segmentation_models_pytorch.encoders import get_preprocessing_fn

preprocess_input = get_preprocessing_fn('resnet18', pretrained='imagenet')

'resnet18' はexampleで、使用するEncoder名を入れる。

とりあえず、大きなエラーは解消できたので、TTAが機能するかどうか調べてみる。

といっても、inferenceの部分だから、commit, submitしないとわからないので、明日にしよう。

 

2月17日(水)

HuBMAP: 1,012 teams

TTAを搭載したコード、昨日はCPUで途中まで動くことを確認した。

GPUでinferenceまで完了。

EfficientNetB0-FPNで、inference所要時間は41分であった。

commitしてoutputを見て唖然!

何も出力されていなかった。

最終行に、submission.head( )、と書いておきましょう!

 

***バグ出し・コード修正作業中***

・バグ出しに必要なコードの知識が不足。

・必要と考えられる機能を全て追加してしまってからバグを見つける、ということができるような、コーディング技術は、まだ持っていない。

・動いているコードに、正確に作動する状態を保持しながら、TTAに必要な機能を、1行づつ(少しづつ)追加していく、というようなやりかたをとってみよう。

 

2月18日(木)

HuBMAP: 1,019 teams

TTAのセットで使われている次のコードの意味がわからない。

preprocess_input = Lambda(image = get_preprocessing_fn(encoder_name = ENCODER_NAME, pretrained = 'imagenet'))

TTAのコード(inference)の作者の(train)コードを見ると、そこでも使われている。

TTAの作者がtrainで用いているEncoderは'se_resnext50_32x4d'であり、自分が今使っているEncoder(EfficientNet)とは異なることに起因する何らかの不整合がエラーの原因となっている可能性がある。

ということで、TTAの作者と同じ、se_resnext50_32x4d-Unetを使って確かめてみよう。

これはピントはずれだった。

出力されなかった原因は、単純ミスだった。

TTA用の画像も、それに対する予測も正しく行われていることを確認した。

それでも、最終出力は空(empty)であった。このことから、次のコード「preds[x1:x2,y1:y2] = (pred > 0).astype(np.uint8)」が動作していないということが推測された。このコードは、次のforループ「for (x1,x2,y1,y2) in slices:」の中にあるべきものだが、それが、インデントの間違いによって、このforループの外に出てしまっていたのである。そのために、予測値が格納されず、空のままだったということである。

原因:TTAを含むコードの作者は、複数のFoldに対応するモデルによる予測値の平均を計算するためのforループを用いていたが、自分が使っているコードは、Foldを使っていないので、そのforループを除去したのだが、その作業中にそのforループの外側の行まで動かして(インデントの変更をして)しまったために、予測値の読み込みのコードが必要な時に呼び出されなくなってしまった、ということである。

 

ということで、ようやく解決した。

明日は、TTAの効果を調べたいところだが、GPUの残り時間が無くなった。

明日は、trainとinferenceの分離、モデルのアンサンブル、などについて検討する。

 

2月19日(金)

HuBMAP:1,028 teams, two months to go (To be determined)

 

trainとinferenceに分離:trainedパラメータの授受

train_code: パラメータの保存コードの例

torch.save(model.state_dict(), 'model.pth')

torch.save(model.state_dict(),"best.pth")

torch.save(model.state_dict(),f'FOLD-{fold}-model.pth')

inference_code: 保存したパラメータの読み込みコードの例

model.load_state_dict(state_dict)

model.load_state_dict(checkpoint['state_dict'])

model.load_state_dict(torch.load("../input/private_dataset/FOLD-{fold}-model.pth"))

 

モデルのアンサンブルについて:

現状のinferenceの所要時間は15分程度。TTAで3枚に増えると45分程度、モデルを4種類使うと180分程度となる。

TTAの参考にさせていただいたコードは4Foldの計算で、各Foldのパラメータを読み込んだモデルを用意しておいて、forループで順に予測し、予測値をループ毎に加算し、最後に4で除算している。この4Foldの代わりに、4種類のtrain済みのモデルを用意しておけばアンサンブルができそうな気がするのだが、どうだろう。

4Foldの場合は、inferenceコードでモデルを準備し、trainコードで作成し出力した各Foldのtrainedパラメータを読み込んだモデルをmodel_0, model_1, model_2, model_3のように準備し、これらのモデルを順に適用して予測し、最後に平均をとる。

モデルのアンサンブルの場合は、inferenceコードで複数のモデルを準備し、trainコードで作成した各モデルのtrainedパラメータを読み込んだモデルをmodel_A, model_B, model_C, model_Dのように準備し、これらのモデルを順に適用して予測し、最後に平均をとる。

モリーオーバーになりそうだし、そう簡単にはいかないかもしれないが、まずは、サイズの小さな、2種類のモデルを使って、やってみよう。(後日)

 

これなに?:

identity = rasterio.Affine(1, 0, 0, 0, 1, 0)

この括弧の中の6つ並んでいる1と0の数字は、下のマトリックスの(a, b, c, d, e, f)に対応していて、対角成分が1の対角行列を示しているようだ。

以下は、https://github.com/sgillies/affine、から引用したもので、適当に書き加えた。

The 3x3 augmented affine transformation matrix for transformations in two dimensions is illustrated below.

| x' |   | a  b  c | | x |
| y' | = | d  e  f | | y |
| 1  |   | 0  0  1 | | 1 |

>>> Affine.identity()
Affine(1.0, 0.0, 0.0,
           0.0, 1.0, 0.0) 

a=1, b=0, c=0, d=0, e=1, f=0, を代入すると上のマトリクスの式はつぎのようになる。

| x' |   | 1  0  0 | | x |
| y' | = | 0  1  0 | | y |
| 1  |   | 0  0  1 | | 1 |

 x'=1, y'=1

identity(同一)だから、こんな感じでいいのかな。

 

明日は、TTAの効果を試してみよう。

 

2月20日(土)

GPU: 43 h

HuBMAP:1,042 teams, two months to go

TTA

TTA-EfficientNetB0-FPN(decoder-dropout=0.5)-Batch_16-Epochs_10-Dice-AdW-trfm_1

TTA_0: no TTA: LB=0.825

TTA_1: identity + holrizontal_flip: LB=0.822

TTA_2: identity + horizontal_flip + vertival_flip: LB=0.827

TTA_3: identity + horizontal_flip + vertival_flip + rotate: LB=0.828

TTA_4: identity + horizontal_flip + vertival_flip + rotate + pixel_level_trfms: LB=0.830

プログラムは正しく動作しているようで、TTAの効果が認められた。

計算時間をできるだけ短くしようとしてB0を用いたが、それにはDiceLossは適切ではなく(10エポックでは不足)、LBスコアとしては、満足のいく結果にはならなかった。

大きなモデルを使うと予測時間が極端に長くかかり、この先、モデルのアンサンブルのことも考えると、計算速度は速く、モデルは小さく、する必要がある。

今回のモデルでは、TTA無しのときの予測時間が12.5分で、4枚追加すると予測には、45分くらいかかっている。仮に5つのモデルをアンサンブルすると、45x5=225分かかる。よくわかっていないのだが、submit中に、予測はprivate_dataに対しても行われ、private_dataがtest_dataの1.4倍ある場合には、225 x 2.4=540分=9時間となる。B0でギリギリなので、大きなモデルを使うには、アンサンブルするモデルの数を減らす、TTAの枚数を減らす、ということをする必要がある。B5などでは、予測時間が長くなって、とても使えそうにないと思う。

明日は、TTAなし、予測時間15分以内、LBスコア0.832以上をリストアップ/探索し、実際に0.832以上になった場合には、TTA_4を適用してみよう。

EfficientNetB0,B1,B2,B3,B4

Unet, Unet-scse, FPN, MAnet, Linknet,

FocalLoss, JaccardLoss, LoaszLoss, BCELoss, DiceLoss,

組み合わせとしては、おおよそ、この範囲内で、検討しよう。

 

過去の実験結果リマインド:OneCycleLRのエポック数とLoss_function:一例

EfficientNetB0-Unet, OneCycleLR (AdamW: lr=1e-4, Max_lr=1e-3)

BCEWithLogitsLoss:

epochs, public_LB = (10, 0.830) -> (20, 0.822) -> (30, no submittion)

at last epoch: (train_loss, val_loss) = (0.041, 0.053) -> (0.034, 0.047) -> (0.031, 0.054)

DiceLoss:

epochs, public_LB = (10, 0.826) -> (20, 0.830) -> (30, 0.832)

at last apoch: train_loss, val_loss = (0.106, 0.123) -> (0.101 0.113) -> (0.096, 0.111)

 

2月21日(日)

HuBMAP:1,054 teams, two months to go

GPU: 36h40m

EfficientNetB0-Decoder, FocalLoss, OneCycleLR(AdamW, 1e-4, max_lr=1e-3), TTA-4

Unet:          LB=0.830

Unet-scse:  LB=0.829

FPN:           LB=0.818

MAnet:      LB=0.828

Linknet:     LB= 0.820

TTA無しでのスコアが0.832以上になる組み合わせを探索したうえで、TTA-4を適用するつもりだったが、trainの段階でのFocalLossの値が良さそうだったので、条件出しせずにTTA-4の計算を行った結果は以上のようになった。

FocalLossを使ったのは、EfficientNetB7-Unetに(FocalLoss, JaccardLoss, LoaszLoss, BCELoss, DiceLoss)を適用したとき、最も収束が良く、LBスコアも高かったのがFocalLossだったためである。EfficientNetB0-FPN(とLinknet)のスコアがこんなに低くなるとは思わなかったが、train結果を見直してみると、EffB0-FPN(とLinknet)のval_lossの値は、他のDecoderの結果よりも明らかに大きい。ResNext152-FPNを扱った文献に、FocalLoss(α=0.25, β=2.0)が良好な結果を与える、と記述されていたのをメモっていたので、FocalLossとFPNとの相性は良いんだろうと思っていた。相性が良いのはResNext152やEfficientNetB7と組み合わせた場合に限る、ということかもしれない。

モデルが小さい場合、エポック数を増やす必要がある。

予測時間は、モデルサイズを大きくすると長くなるが、エポック数を増やしても、予測時間は変わらないと思い込んでいたが、実験してみると・・・。

小さなモデルで、エポック数を増やして、良い結果が得られるならば、その方が良い。

EfficientNetBn-FPNを用いて、Bnと予測時間の関係をTTA無しで比較してみよう。

EffB0-FPN: 11m19s(draft), 11m51s(commit)

EffB1-FPN: 13m11s(draft), 11m12s(commit)

EffB2-FPN: 12m29s(draft), 10m47s(commit)

EffB3-FPN: 13m15s(draft), 12m43s(commit)

この程度の時間差であれば、パラメータの増加等による性能アップの効果の方が大きいのではないだろうか。 

明日は、EncoderをEfficientNetB3に変更し、他は全く同じ条件のままで、計算してみようと思っていたが、上記のように予測時間のモデル依存性を調べた結果、B4, B5, B6, B7等についても調べ、そのうえで、これまで得られている結果も勘案して、これから使っていくEfficientNetのモデルを絞り込んでいこうと思う。 

 

2月22日(月)

HuBMAP:1,062 teams, two months to go

EfficientNetBn-FPNの予測時間

EffB4-FPN: 12m11s(draft), 14m49s(commit)

EffB5-FPN: 13m27s(draft), 15m25s(commit)

EffB6-FPN: 16m10s(draft), 15m42s(commit)

f:id:AI_ML_DL:20210222102631p:plain


f:id:AI_ML_DL:20210222085906p:plain

これにスコアを加えたいのだが、Decoder、エポック数、損失関数、augmentationの種類等によって変わるので、難しい。

ここからは、OneCycleLRのエポック数は30、EncoderはEfficientNetB5、損失関数はDiceLoss、をbaseとしよう。

EfficientNetB5-Decoder, DiceLoss, OneCycleLR(AdamW, 1e-4, max_lr=1e-3, 30 epochs), batch_size=16, with soft augmentation, without TTA

Unet:      LB=0.836 (15 epochs: 0.827)

Unet++: LB=0.836 (15 epochs: 0.831)

FPN:       LB=0.830 (20 epochs: 0.834)

MAnet:  LB=0.834 (15 epochs: 0.831)

Linknet: LB=0.836 (15 epochs: 0.832)

Unet, MAnet, Linknetについて、さらに、エポック数を増やしてみようかなと思うのだが、overfittingによってスコアは反転するだろうから、そこを見極める必要がある。overfittingしてから、適度なdata_augmentationができれば、スコアはさらに上げることができるかもしれない。 

FPNは、Decoderのdropoutを0.5にしたのがまずかったかもしれないので(20エポックもdropout=0.5)、0.2に戻してみよう。

GPUの残りが8.5時間になった。明日、Linknet, Unet, MAnetのエポック数を増やすだけで使い切ってしまいそうだ。

その後の3日間は、trainと、inferenceのコードを作り、K-Foldとアンサンブルができるように準備しよう。

 

2月23日(火)

HuBMAP:1,070 teams, two months to go(まだデータは更新されていないようだ)

エポック数を50にしてみた。1エポックあたり1分を少し超えるので、trainで60分くらいかかる。データ前処理の5分とinferenceの15分を足すと約80分を要す。

commitも同じ時間を要するので、パラメータ設定時間等も加えると、1つのスコアを得るのに、3時間くらいかかる。

ということで、今日は、Linknet, Unet, Manetの50エポックの計算で終了予定。

OneCycleLRにおけるエポック数:プログラムのTTAの動作確認のために3エポックでtrainを終え、commit,submitしてみたら、LBスコアが0.80くらいになってたいへん驚いたことがある。10エポックで0.82+のモデルである。エポック数を増やす実験を、30くらいまで、何度かやってみたところ、このブログにも部分的に記録を残しているが、EncoderがB0でDiceLossの場合、エポック数が10、20、30と増えるにつれてスコアは上がったが、EncoderがB5以上でBCELossの場合には20以上ではスコアは低下する傾向がみられた。といっても、OneCycleLRの性能は、用いるoptimizerとそのパラメータだけでなく、Max_lrによっても大きく変わるので、エポック数の最適値がどこにあるかは、実験してみるしかない、ということになる。

Linknet:15エポックでLB=0.832、30エポックでLB=0.836だったのに対し、50エポックまで増やすと、LB=0.831となった。エポック数を40にするか50にするか迷ったのだが、これによって、50以下にピークがあることがわかった、ということにしておこう。

この結果をみると、UnetやMAnetは50エポックではなく、40エポックくらいにしてみよう、となるかもしれないが、既に、Unetは50エポックで計算し、commit, submitしており、MAnetは50エポックで計算中である。

Unet: 30エポックでLB=0.836だったのに対し、50エポックまで増やすと、LB=0.834となった。

MAnet: 30エポックでLB=0.834だったのに対し、50エポックまで増やすと、LB=0.830となった。

以上、いずれのDecoderも、50エポックまで増やすことによって、publicLBスコアは下がった。LinknetとMAnetのLBスコアのピークは30エポック前後、UnetのLBスコアのピークは40エポック近傍であると推測される。

最後に、B5-UnetのLB=0.836のときと同じ条件でtrainingした後で、TTA_4を適用してみよう。

inferenceに要した時間は約54分で、B0-Unetの41分の約1.3倍となった。

TTA_4を追加することによって、0.84+となることを期待したが、LB=0.836となり、スコアは上がらなかった。

GPUの残り時間が30分以下となったので、ここで終了!

これまでの実験結果を簡単にまとめると次のようになる。

1.TTAが可能になった。

2.EfficientNetB0-FPNのモデルを用いて、TTAの効果を調べ、TTA無しでLB=0.825だったのが、TTA_4ではLB=0.830となり、TTAによってLBスコアが向上することを確認した。

3.OneCycleLRで常用してきた10エポックを30エポックまで増やすことによって、LBスコアが0.836まで向上したので、そのモデルにTTA_4を適用することでさらにスコアが上がることを期待したが、TTA無しと同じスコア0.836となった。

検討:

TTAの効果を最初に調べたモデルでのLBスコアは、TTA無し: 0.825、TTA_1: 0.822, TTA_2:0.827, TTA_3: 0.828, TTA_4: 0.830であった。数字は追加画像の枚数である。

これに対して今日のモデルは、TTA無しでもLBスコアが0.836と高いことが関係しているのか、それとも、TTA無しで0.836というのは別の計算なので、TTA_4を適用したときのモデルの性能が、TTA無しのときのモデルの性能よりも低かったということか。

TTA適用無し:30エポック目: train_loss=0.094, val_loss=0.107

TTA_4適用: 30エポック目: train_loss=0.096, val_loss=0.113となっていた。

両者は、trainまでは、全く同じコードの筈なのだが、この最終エポックでのlossの値からは、TTA適用無しのtrainedモデルの方が良いと判定される。すなわち、TTAを適用する前のモデルの予測性能が劣っていたために、あたかも、TTAの効果が皆無であったかのような結果になったということではないだろうか。

最初にTTAの効果を調べたときも、TTA無しと各TTA有は、全てその都度trainを行なっている。それゆえ、毎回、trainedモデルの性能は異なっていた可能性が高い。実際、TTA_1のスコア0.822は、TTA無しのスコア0.825よりも低かった。この時の実験をこれでやめていたら、TTAの効果は無い、TTAをすることで、かえってスコアは悪くなる、2枚の予測結果の処理の仕方を見直そう、などと考えたかもしれない。しかし、TTAの枚数を増やしていった結果、枚数が多くなればスコアが上がるという結果となり、それによって、TTAによりLBスコアは良くなる、と判断することになった。

今のコードは、trainとinferenceが連結されているので、このような現象が起きる。

trainとinferenceが分離されていれば、同一のtrainedモデルを使うことができるので、inferenceの条件の違いが明確に現れ、同一のモデルを使っているにもかかわらず、TTAの効果が現れたり、消えたり、マイナスになったりする、ということは生じないはずである。

ということで、明日から、train専用モデルと、inference専用モデルを作るとともに、アンサンブルの方法も検討していくことにする。

 

2月24日(水)

HuBMAP:1,082 teams, two months to go

trainコード

・inferenceを切り離す(不要なセルを消去するのではなく、markdownに変更する)。

・trained_modelをsaveする。公開コードには次のようなコードが使われている。

torch.save(model.state_dict(), 'model.pth')

torch.save(model.state_dict(),f'FOLD-{fold}-model.pth')

・Foldを使う。公開コードで使われているコード例。容易ではないのでもう少しコードに慣れてからチャレンジする。

from sklearn.model_selection import GroupKFold

group_kfold = GroupKFold(n_splits = 3)

inferenceコード:

・Trainコードで、次のように識別しやすいファイル名で出力したstate.dict( )ファイルを、データセットにして、Inferenceコードのinput_datasetに加える。

torch.save(model.state_dict(), 'EfficientNetB5-Unet_model.pth') 

・inferenceコードで同じモデルを次のようにして準備し、データセットから、state_dictを読み込む。

model = smp.Unet('efficientnet-b5', encoder_weights='imagenet', classes=1)

path = '../input/state_dict_dataset/EfficientNetB5-Unet_model.pth'

state_dict = torch.load(path)

model.load_state_dict(state_dict)

明日、trainのコードセルをmarkdownに変更し、これらのコードを追加して、inferenceできるかどうか試してみよう。

 

2/21に計算し、commitしておいたものをsubmitしてみた。

EfficientNetBn-FPN-FocalLoss-OneCycleLR(AdamW, epoch=10)

B0: LB=0.793,  B1: LB=0.570,  B2: LB=0.817,  B3: LB=0.812

B4: LB=0.813,  B5: LB=0.822,  B6: LB=0.823

B0(epoch=10, BCELoss): LB=0.82+, B4(epoch=10, BCELoss): LB=0.830

*EfficientNetB7-Unetに対して、Focal Lossは他のLoss_functionより良いスコアを与えたこと、および、Focal Lossの論文を斜め読みして、Focal LossはFPNとの相性が良い、と思い込んだ結果がこれである。

 

Twitterで @Deep Learning Weeklyがtweetしていた論文:興味深い:

Unsupervised Semantic Segmentation by Contrasting Object Mask Proposals
Wouter Van Gansbeke1* Simon Vandenhende1* Stamatios Georgoulis2 Luc Van Gool1,2
1KU Leuven/ESAT-PSI 2ETH Zurich/CVL, TRACE,  arXiv:2102.06191v1 [cs.CV] 11 Feb 2021

f:id:AI_ML_DL:20210225002045p:plain

f:id:AI_ML_DL:20210225001407p:plain

f:id:AI_ML_DL:20210225001704p:plain

f:id:AI_ML_DL:20210225002150p:plain

 

糸球体の領域を過不足なく囲うことができるヒトは、糸球体をどう定義しているのだろうか。そのヒトは、1枚の光学顕微鏡像から、3次元構造を正しく想像することができているのだろう。それが正しくできるためには、細胞レベルでの腎臓の正しい3次元構造を知っていることが必要である。さらに、加工(包埋/凍結・切断)による変形・変質、染色等の化学処理による変形・変色などについての正しい知識を適切に適用することで、より正確に糸球体の境界を判定することが可能になるのだろう。同じことをコンピュータが自動的に行えるようにするためには、最初に与えられた課題と画像から必要な知識領域を判定し、課題解決に必要な知識を収集し、課題解決に必要な知識を集約し、知識を解析手段に変換し、画像を解析し、課題を解決するまでのプロセスを自動的に実行する。

 

2月25日(木)

HuBMAP: 1,090 teams, two months to go(締切日は、データ更新から2か月後)

今日は、trainedモデルのstate_dict( )をデータセットに格納し、inferenceコードにそのデータセットを追加し(+ add data)、そのデータセットからstate_dict( )を読み込み、inferenceモデルにstate_dict( )を読み込んで、inferenceを行う。

今日のテスト用に作ったtrained_modelは、EfficientNetB0-UnetとEfficientNetB0-MAnetであり、それらの学習済みモデルのパラメータ、EffB0-Unet-SBCE-model.pthとEffB0-MAnet-SBCE-model.pthは、データセットに入れた。

model_U = smp.Unet('efficientnet-b0', encoder_weights='imagenet', classes=1)

model_M = smp.MAnet('efficientnet-b0', encoder_weights='imagenet', classes=1)

path_U = '../input/state_dict_dataset/EffB0-Unet-SBCE-model.pth'

path_M = '../input/state_dict_dataset/EffB0-MAnet-SBCE-model.pth'

state_dict_U = torch.load(path_U)

state_dict_M = torch.load(path_M)

model_U.load_state_dict(state_dict_U)

model_M.load_state_dict(state_dict_M)

こんな感じで、2つの学習済みモデルをinferenceに使うことができるのだろうか。

試してみようとしたが、CPUだけだとメモリーオーバーになりやすく、かつ、予測も進まないので、土曜日の午前9時にGPUが使えるようになるまで本件は休止。

 

2月26日(金)

HubMAP:1,104 teams, two months to go

Albumentations Documentationsの入り口あたりをちょっと眺めていた。

import albumentations as A
import cv2

transform = A.Compose([
A.RandomCrop(width=256, height=256),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.2),
])

AutoAlbument is an AutoML tool that learns image augmentation policies from data using the Faster AutoAugment algorithm. It relieves the user from manually selecting augmentations and tuning their parameters. AutoAlbument provides a complete ready-to-use configuration for an augmentation pipeline.

AutoAlbument supports image classification and semantic segmentation tasks. The library requires Python 3.6 or higher.

The source code and issue tracker are available at https://github.com/albumentations-team/autoalbument

この論文を読んでみよう。

Faster AutoAugment: Learning Augmentation Strategies using Backpropagation
Ryuichiro Hataya, Jan Zdenek, Kazuki Yoshizoe, and Hideki Nakayama

arXiv:1911.06987v1 [cs.CV] 16 Nov 2019  

 

2月27日(土)

GPU: 38h:≒ 5.4h/day

HuBMAP:1,111 teams, two months to go

1st public LB=0.896: my public LB=0.836: Δ=-0.06: very large

この差を埋める方法を探す:

現状を知る:予測結果を画像化する:難しい:検討中

 

画像サイズ、縮小率など、基本的なところを、検討することが必要である。画素数を、256, 320, 384, 448, 512と変えてみて、スコアがどうなるかを調べてみよう。といっても、画素数の大きな画像を扱うには小さなモデルを使うとか、バッチサイズを小さくするとかしないとメモリーオーバーになるので、スコアアップにつながる組み合わせを見つけるのは容易ではない。

 

EfficientNetB5-Decoderを計算したときに結果が出なかったPAN, DeepLabV3,および  DeepLabV3Plusの性能が気になっている。PSPNetも含め、DiceLoss, 30エポックの結果を調べてみたいと思って、再度、検討してみた。PSPNetはDiceLossの30エポックでLB=0.822となり、他のモデルより0.01以上小さい。PAN, DeepLabV3, およびDeepLabV3Plusは、前回調べたときには、GPUの残り時間が少なくなっていなかったので、少し待つだけでデータが出ないとあきらめていたが、今回は、1エポック目の結果が出るまで待ってみた、その結果、1エポックあたりのtrain時間が、他のDecoderの場合の10倍以上になることがわかった。以上のようにPSPNetは明らかに他よりスコアが低いこと、PAN, DeepLabV3,およびDeepLabV3Plusは、理由はわからないが、計算時間が長すぎるので、これ以上追求しないことにする。

 

inference専用コード:作成中

 

2月28日(日)

HuBMAP:1,115 teams, two months to go

素数増大の効果:

元画像の切り出しと縮小は、ここまで、公開コードをフォークしたときの値を変更せずに使ってきた。切り出しが1024x1024、これを256x256に解像度を下げて使っている。

EfficientNetB5-FPNで、256x256で、LB=0.835であったものを、320x320, 384x384, 512x512と変えてみた。GPUのメモリー占有量は、画素数を反映しているので、コードは正しく動作していると思うのだが、LBスコアは、0.829から0.835の間であった。

モリーオーバーを避けるために、B4またはB5、バッチ数は16または32とした。

コンペのディスカッションでも、低解像度でバッチ数を増やしてtrainingした方がLBスコアが高いと報告している人もいる。

HuBMAPコンペサイトのDiscussionから引用:

LB 0.837 : batch size =16 / size=320x320
LB 0.845 : batch size =32 / size=256x256

これは、ぜひ、検討してみよう。256x256のbatch=32だと、EfficientNetB5-Unetではメモリーオーバーになるので、B4EfficientNetB4-Unetにする必要がある。他のDecoderについては、調べる必要がある。

EfficientNetの番号を決めるときの根拠の1つになる。画素数が多くなるとメモリーを消費するのでバッチ数を上げられなくなる。それでもバッチ数を上げようとすると、EfficientNetの番号の小さいモデルを選ぶ必要が出てくる。最適な組み合わせは実験によってしか得られない。

FoldもTTAもアンサンブルも適切なタイルの作成も後処理もNoisyStudentもpseudo labelsもSelf-trainingも検討中。augmentationもoptimizerもlr-schedulerも検討中。

2月の目標:LB=0.84+には届かなかった。

 

f:id:AI_ML_DL:20210131221532p:plain

style=163 iterations=500

f:id:AI_ML_DL:20210131221655p:plain

style=163 iterations=50

f:id:AI_ML_DL:20210131221742p:plain

style=163 iterations=5