AI_ML_DL’s diary

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

Kaggle散歩(1st March to 10th May 2021)

3月1日(月)

HuBMAP1,130 teams, two months to go

今日はBatch_sizeの16と32に違いがあるかどうかを調べる。

EfficientNetB5-UnetのLB=0.836のコードがBatch_size=16だったので、これを32に変更したがGPUのメモリーオーバーとなったため、B4で実行した:結果はLB=0.836となった。

これで改善が認められれば他のDecoderでもやってみるつもりだったが、・・・。

次は、Encoderのpre-training weights('imagenet'を常用している)によって違いがあるかどうかを調べる。

'noisy-student':LB=0.831:0.005下がった。

'advprop':LB=0.823:0.013下がった。

こういうのは、実際に使ってみないとわからないものだ。ただし、エポック数など、最適化できていないパラメータに起因して、スコアが低い、という可能性もある。

次は、pre-trained weightsではなく、random initializationでやってみよう。

A. Geron氏のテキストには、Glorot and He initialization, LeCun initialization, Xavier initializationなどが紹介されている。今使っているSegmentation Modelsでは、これらの initializationには対応していないようである。

random initialization(epochs=30):LB=0.819 :意外に健闘しているようにみえる。

今日のデータはすべて30エポックだが、random initializationについては、明日、エポック数を増やしてみよう。

 

3月2日(火)

HuBMAP:1,142 teams, two months to go

random initialization(epochs=50):LB=0.778:期待外れ。これ以上追求しない。

(lr_schedulerは、OneCycleLRを使い続けているので、エポック数は、OneCycleLRの設定エポック数である。このスケジューラーは、early_stopで監視する必要が無く、極端なoverfittingにもならないようで、便利だが、Max_lrとエポック数の最適化は必須。合間に最適化を試みているが、まだ使いこなせていないように感じている。)

 

ここまで、"An activation function to apply after the final convolution layer. "をNoneにして、 score > 0、で評価してきたが、activation functionを"sigmoid"にして、score > thresholdとして、thresholdの最適化をやってみる。

ちょっとやってみたが、うまくいかない。何かおかしい。間違ったことをしているかもしれない・・・。

 

best single modelというタイトルのdiscussionにおいて、LB=0.84+から0.86+くらいのスコアのモデル(encoder+decoder)、optimizer、loss function、epochsなど概要が紹介されている。自分の結果、0.83+とは最大で0.03くらい違う。前処理、後処理などの情報が少ないので何を参考にすればよいのかわからないが、気になるのは、スコアが高い0.85+の4件が、EncoderにEfficientNetを使っていないことと、開発年代の古いEncoderを用いて、エポック数を多くしていることである。さらに、resnet34-UnetでLB=0.857というスコアを得ているのは驚きだ。FPNは1件で、そのほかはUnetを使っている。

Googleが昨年発表した論文"EfficientDet: Scalable and Efficient Object Detection"で提案している新しいモデルEfficientDetは、FPNから派生したもののようである"we propose a weighted bi-directional feature pyramid network (BiFPN)"。

 

3月3日(水)

HuBMAPに集中している間に、いろいろなコンペが通り過ぎていった。今日、新しいテーマのコンペ案内メールが来ていた。化学系には興味があるので調べてみる。

Bristol-Myers Squibb – Molecular Translation
Can you translate chemical images to text?

chemical imagesというのは構造式、textというのは、InChIのこと。構造式は画像として与えられる。InCHIについて、wikipediaの説明は以下の通り。

InChI(International Chemical Identifier)は、標準的かつ人間が読める方法で分子情報を提供し、またウェブ上でのデータベースからの情報の検索機能を提供する。元々、2000年から2005年にIUPACとNISTによって開発され、フォーマットとアルゴリズムは非営利であり、開発の継続は、IUPACも参画する非営利団体のInChI Trustにより、2010年までサポートされていた。現在の1.04版は、2011年9月にリリースされた。

1.04版の前までは、ソフトウェアはオープンソースGNU Lesser General Public Licenseで無償で入手できたが[3]、現在は、IUPAC-InChI Trust Licenseと呼ばれる固有のライセンスとなっている[4]。ウイキペディアより引用。

構造式を含む文献を電子化する場合に必要となる技術の1つかもしれないが、InChIでは3次元構造は原理的に表現しきれないようであり、InChI表記は無味乾燥であり、機械学習で推測したInChIに学術的価値がどれだけあるのかも理解できないので、やってみようという気にならない。構造式の画像から近似的に3次元分子構造モデルを再現するということであれば楽しく取り組めそうだが、・・・。

 

HuBMAP:1,155 teams, two months to go(データ修正のため順延中)

昨日の取り組み「activation functionを"sigmoid"にして、score > thresholdとして、thresholdの最適化をやってみる。」のつづき。

threshold=0.5で、BCELossとFocalLossで10エポックの場合について調べた。

B5-Unet, BCELoss, AdamW, 10 epochs:LB=0.822 (output_file_ size=5.1 MB)

B5-Unet, FocalLoss, AdamW, 10 epochs:LB=0.799 (output_file_ size=4.94 MB) 

いずれも、出力データのサイズは4.9-5.1 MBとなり、sigmoidを使う前よりも小さい。sigmoidを使わずにLB=0.836となったときの出力データのサイズは、5.5-5.7 MBであった。データサイズとLBスコアとの間には必ずしも比例関係は無いが、今回の場合は、thresholdを下げればLBスコアが上がる可能性がありそうである。

GPUの割当は使い果たしたので、この確認は土曜日以降になる。

activation functionとしては、 “sigmoid”の他に, “softmax”, “logsoftmax”, “tanh”,などが使える。"tanh"は -1 < tanh < 1で、中央値付近の微分係数がsigmoidの4倍になるため、sigmoidよりも収束が速くなりそうだし、感度も高くなるかもしれないので、試してみよう。

 

3月4日(木)

HuBMAP: 1,167 teams, two months to go(データ更新待ち)

GPUが使えないのでCPUで計算する。:B5はメモリーオーバーになるので、計算時間も考えて、B0を使うことにする。B0-UnetでのLBスコアは0.827前後である。

最初にtanhを試す。

B0-Unet(tanh: threshold=0.0)-E10-B16 : LB=0.827

tanhも使えることがわかった。出力データは5.64 MBで、sigmoidの場合よりも大きい。

LBスコアは、sigmoidを使った場合よりも高くなっている。

次に、sigmoidで、thresholdの効果を試す。1サイクルに6時間くらいかかる(GPUなら60分くらい)ので、以下の3つの計算の結果が出そろうのは明日になる。

B0-Unet(sigmoid: threshold=0.50)-E10-B16: LB=0.819(このスコアで閾値を調整しても、とは思うのだが、何事も経験しておくことは大切だ、ということで、・・・。)

B0-Unet(sigmoid: threshold=0.45)-E10-B16: LB=0.820

B0-Unet(sigmoid: threshold=0.40)-E10-B16: LB=0.821

threshold依存性はありそうだ。しかし、このスコアでは、使う気にならない。

 

semantic segmentationについてもっと勉強しよう。 

Deep Semantic Segmentation of Natural and Medical Images: A Review
Saeid Asgari Taghanaki, Kumar Abhishek, Joseph Paul Cohen, Julien Cohen-Adad and Ghassan Hamarneh, arXiv:1910.07655v3 [cs.CV] 3 Jun 2020, 

f:id:AI_ML_DL:20210305000412p:plain

f:id:AI_ML_DL:20210305000510p:plain

Cross Entropy -->  Weighted Cross Entropy --> Focal Loss (Lin et al. (2017b) added the term (1 − pˆ)γ to the cross entropy loss)

Overlap Measure based Loss Functions:Dice Loss / F1 Score -->  Tversky Loss(Tversky loss (TL) (Salehi et al., 2017) is a generalization of the DL(Dice Loss) --> Exponential Logarithmic Loss --> Lovasz-Softmax loss(a smooth extension of the discrete Jaccard loss(IoU loss))

上記のような種々の損失関数の解説の最後に次のように書かれている。

The loss functions which use cross-entropy as the base and the overlap measure functions as a weighted regularizer show more stability during training. 

cross-entropy系をベースに、overlap系を加えることが推奨されている。Fig.12左側のcross-entropyとfocalの曲線は、相補的な特性を示しているように見える。これも組み合わせると良い、ということになるのだろうか。

 

3月5日(金)

HuBMAP:1,176 teams, two months to go(データ更新待ち)

シングルモデルでLB=0.85+となることを目標に、DeepLabV3+に集中してみよう。

DeepLabV3+の性能を発揮させるためには、パラメータを適正に設定しなければならない筈なので、論文を丁寧に読んで、モデルの中身を理解しなければならない。

ここまで、Unet以外の8種類のモデルのどれもが、Unetのスコアを超えられないのは、各モデルのパラメータを適切に設定することができていないからなのだろうと思う。

Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation
Liang-Chieh Chen, Yukun Zhu, George Papandreou, Florian Schroff, and Hartwig Adam
Google Inc. ECCV 2018

Abstract.

Spatial pyramid pooling module or encode-decoder structure are used in deep neural networks for semantic segmentation task. The former networks are able to encode multi-scale contextual information by probing the incoming features with filters or pooling operations at multiple rates and multiple effective fields-of-view, while the latter networks can capture sharper object boundaries by gradually recovering the spatial
information.

In this work, we propose to combine the advantages from both methods. Specifically, our proposed model, DeepLabv3+, extends DeepLabv3 by adding a simple yet effective decoder module to refine the segmentation results especially along object boundaries. We further explore the Xception model and apply the depthwise separable convolution
to both Atrous Spatial Pyramid Pooling and decoder modules, resulting in a faster and stronger encoder-decoder network.

空間ピラミッドプーリングモジュールまたはエンコードデコーダー構造は、セマンティックセグメンテーションタスクのディープニューラルネットワークで使用されます。 前者のネットワークは、フィルターまたは複数のレートと複数の有効視野でのプール操作を使用して着信機能をプローブすることにより、マルチスケールのコンテキスト情報をエンコードできます。後者のネットワークは、空間情報を徐々に回復することにより、より鋭いオブジェクト境界をキャプチャできます。

この作業では、両方の方法の利点を組み合わせることを提案します。 具体的には、提案されたモデルであるDeepLabv3 +は、シンプルでありながら効果的なデコーダーモジュールを追加することで、DeepLabv3を拡張し、特にオブジェクトの境界に沿ってセグメンテーション結果を改善します。 Xceptionモデルをさらに調査し、深さ方向に分離可能な畳み込みをAtrous Spatial Pyramid Poolingとデコーダーモジュールの両方に適用して、より高速で強力なエンコーダー-デコーダーネットワークを実現します。

以上、google翻訳

 

3月6日(土)

GPU: 43h

HuBMAP:1,182 teams, two months to go(データ更新待ち)

HuBMAPコンペは、糸球体を細胞群から抽出するという単純な作業なんだが、糸球体の周辺/背景は、似たような細胞で満たされている。内部と外部は類似しているので、境界層の識別能力を高めることが重要なのだろうと思うのだが、どうやったら実現できるのか。上記のabstractに、DeepLabV3+は境界層の識別能力を改善したと書かれているので期待して使ってみよう。

問題は、計算時間で、EncoderにEfficientNetB3を使っても、1エポックに8分以上かかる。他のDecoderでは2分以内である。CPUでの計算時間は1エポックが20分くらいで、他のモデルと変わらないので、他のモデルよりもCPUへの負荷が大きいということのようだが、よくわからない。

DeepLabV3+の論文では、EncoderにXceptionが使われているので、EfficientNetからXceptionに変えてみた。しかし、意外なことに、Xceptionに対応していないというメッセージが現れ、停止した。あらためて論文を見ると、Xceptionはモディファイして用いていると書いてある。

論文には、Xceptionよりスコアは低いが、ResNet-101を用いて計算した例も示されているので、EncoderをResNet-101に変更して計算してみた。CPUでは1エポックに30分くらいかかり、さすがにこれでは使えないのでGPUに切り替えてみた。そうすると、これも意外なことに、1エポックあたりの計算時間が、約60秒であった。これなら問題なく条件検討ができる。

計算結果:LB=0.811:BCE+0.5*Focal

256x256を同じ視野で解像度を384x384にすると、LB=0.816になった。

Atrous convolution:設定パラメータは"decoder_atrous_rates"となっていて、デフォルトは (12, 24, 36)である。開発者の論文では (6, 12, 18) となっている。

encoder_output_stride:デフォルトの16以外の設定32や8を試しているが、詳細不明

decoder_channels:256をデフォルトにして、128ではスコアが下がるという記述はあるが、系統的に調べたというふうでもない。

計算の合間に論文を見ていたのだが、納得できる説明が見当たらない。上記のReviewで目立っていたので、このモデルを中心に据えて、じっくりと、取り組もうと思って始めてみたが、原著者らによるモデルの説明があいまいで、あまり可能性を感じないし、デフォルトで計算したLBスコアは良くないし、何よりも、DecoderにEfficientNetが使えない(GPUを使ったときの計算時間が長すぎる)など、メリットが感じられないので、DeepLabV3+は、このへんでやめておこうかと思う。

 

"COCO"が気になって調べてみた。

Microsoft COCO: Common Objects in Context
Tsung-Yi Lin Michael Maire Serge Belongie Lubomir Bourdev Ross Girshick
James Hays Pietro Perona Deva Ramanan C. Lawrence Zitnick Piotr Dollar´

arXiv:1405.0312v3 [cs.CV] 21 Feb 2015
Abstract—We present a new dataset with the goal of advancing the state-of-the-art in object recognition by placing the question of object recognition in the context of the broader question of scene understanding. This is achieved by gathering images of complexeveryday scenes containing common objects in their natural context. Objects are labeled using per-instance segmentations to aid in precise object localization. Our dataset contains photos of 91 objects types that would be easily recognizable by a 4 year old. With a total of 2.5 million labeled instances in 328k images, the creation of our dataset drew upon extensive crowd worker involvement via novel user interfaces for category detection, instance spotting and instance segmentation. We present a detailed statistical analysis of the dataset in comparison to PASCAL, ImageNet, and SUN. Finally, we provide baseline performance analysis for bounding box and segmentation detection results using a Deformable Parts Model. 

f:id:AI_ML_DL:20210306094212p:plain

COCOデータセットを使ったコンペが継続的に開催されている。

Tasks: Detection | DensePose | Keypoints | Stuff | Panoptic | Captions

Leaderboard: Detection | Keypoints | Stuff | Panoptic | Captions

Panoptic segmentation aims to unify the typically distinct tasks of semantic segmentation (assign a class label to each pixel) and instance segmentation (detect and segment each object instance). Existing metrics are specialized for either semantic or instance segmentation and cannot be used to evaluate the joint task involving both stuff and thing classes. Rather than using a heuristic combination of disjoint metrics for the two tasks, the panoptic task introduces a new Panoptic Quality (PQ) metric. PQ evaluates performance for all categories, including both stuff and thing categories, in a unified manner.

Panoptic Leaderboardのトップのチームのpaper

Joint COCO and Mapillary Workshop at ICCV 2019: Panoptic Segmentation Challenge Track Technical Report: Explore Context Relation for Panoptic Segmentation
Shen Wang1,2∗ Tao Liu1,3∗ Huanyu Liu1∗ Yuchen Ma1 Zeming Li1 Zhicheng Wang1
Xinyu Zhou1 Gang Yu1 Erjin Zhou1 Xiangyu Zhang1 Jian Sun

 

f:id:AI_ML_DL:20210306112638p:plain

これくらいの論文を書いてみたいものだ。

さて、LB=0.836のモデル、EfficientNetB5-Unet、に戻って、シングルモデルのチューニングしてみよう。損失関数と画像分解能などで、LB=84+を目指そう。

 

3月7日(日)

HuBMAP:1,189 teams, two months to go(データ更新待ち)

LB=0.836を超えるために:

モデル:Unetは、"decoder_attention_type"に"scse"を設定することによって、Attention moduleを組み込んだUnetになる。scseの効果を系統的に比較できていないので、scseの効果を見極めたい。

損失関数:Diceをベースに、BCEやJaccardとの組み合わの効果を調べる。

スライス(1024x1024)とダウンサイズ(256x256):ダウンサイズの際の解像度を最大で512x512まで上げることを検討する。モデルサイズやバッチサイズの変更の効果と相殺されるので、可能な範囲で調べる。

モデル:種類もサイズも、まったく、最適化できていない。いろいろ調べたつもりだが、encode-decoderの組み合わせごとにある程度パラメータフィッティングしないと、判断を誤る可能性が高い。

バッチサイズ:モデルサイズと相反するので選択範囲は狭くなる。

Run1:scseの効果:LB=0.832:0.004下がった。:train中のloss(train_loss及びval_loss)は、scseを使う前よりも下がっているのでスコアは上がると期待していたのだが、どういうことだろうか。

Run2:scseは使わないで、解像度を256x256から320x320に変更:LB=0.834:0.002下がった。こちらも、train中のloss(train_loss及びval_loss)は、解像度を上げる前よりも下がっているのだが、どうなっているのだろうか。

Run1もRun2もoverfittingしている可能性が高い。Encoderを小さくするか、エポック数を少なくする必要があると思う。

次は、損失関数だが、上記のReviewには、「The loss functions which use cross-entropy as the base and the overlap measure functions as a weighted regularizer show more stability during training.」と書かれていた。今使っているものでいえば、overlap系はDice, Jaccard, Lovaszとなる。試してみるしかないのだろう。MAnetの論文では、BCEとDiceの比率は1対4であったように思う。Diceをベースに、BCEを加えている。LB=0.836を得ているのは全て、DiceLossのみなので、手始めに、MAnetの論文をまねてみるのもよいかもしれない。

と言いつつ、さきほど、Run2のDiceLossを、5つの損失関数を全て足したものに置き換えて、trainingした。その結果、val_lossがtrain_lossの1.75倍にもなってしまった。overfitting間違いなしだ。Diceが最も収束が遅いことはわかっていたのだから、エポック数は、少なくとも、30から20くらいまでは下げておくべきだったかもしれない。train_lossは、個々の損失関数のtrain_lossの和に近い値となっているので、正しく動作していることは確認できた。

Run3:Run2のDiceを、Dice + BCE + Lovasz + Jaccard + Focalとした:LB=0.828:train_lossとval_lossとの乖離が大きかった(1対1.75)ので、overfittingになり、もっと低いスコアになると予想していたので、このスコアには多少驚いた。

明日は、Run1, Run2, Run3のエポック数をいずれも30から25に減らしてみよう。さらに、20に減らしてみよう。Run1もRun2もスコアは上がるはずだと思っている。Run3は、エポック数を減らすとともに、学習速度が他よりも速いBCEとFocalの割合(寄与率)を下げてみよう(たとえば:Dice  + Lovasz + Jaccard + BCE/2 + Focal/2)。

 

3月8日(月)

HuBMAP:1,200 teams, 18 days to go (two months to go)

Run4:Unet ---> Unet_scseの効果を調べる:エポック数を30から25に減らしてみる。:LB=0.828:さらに0.004下がった。(0.836 -> 0.832 -> 0.828)

25エポックでの最終エポックのlossは、30エポックでの最終エポックのlossよりも大きいだけでなく、scseを使う前よりも大きな値になった。30エポックで、scseを使った場合、lossが下がったので、LBスコアは上がると予測したが、スコアは下がった。30エポックでは、overfittingになったと思ったのだが、そうではなかったのか。

振り返ると、モデルだけ変更した場合(例えばB5からB6に変更)、train_lossもval_lossも下がって、LBスコアが上がると期待するのだが、スコアが下がることもときどきあった。そういうときは、この課題にはB5が適切なのだろうと判断して先に進んできた。ここに問題/課題がありそうだが、・・・。

Run5:25エポックに下げてだめだったのだから、35エポックについても調べる必要がある。増やすという選択肢の他に、30エポックに戻して、収束の速いBCEやFocalと組み合わせる、という方法も考えられる。配合比をどうするか。まずは、30エポックに戻して、(Dice + BCE)を試してみよう。:LB=0.822:また下がった。

Run6:すなおにDiceのみ, 35エポックを試す。:LB=0.836:scse無しのスコアと同じだが、ほんの少し、前進できたような気がする。

Run7:では、40エポックにしてみよう。:LB=0.833:だめか。scseのメリットを見出せず。

次は、画像解像度の効果について調べる。320x320で検討するつもりだったが、現状のモデルとバッチ数ではこれ以上解像度を上げることができないので、512x512まで使える条件(モデルを小さく、バッチ数を少なく)で検討してみようと思う。

 

Bristol-Myers Squibb – Molecular Translation:138 teams, 3 months to go
Can you translate chemical images to text?

方針転換:参加しないと何も学べない --> 参加すれば、新たなデータと課題に触れる機会が得られ、新たな前処理手法、予測手法を学ぶことができる可能性がある。 --> 参加させていただくことにしよう。

以下、ウイキペディアより、

エタノール:CH3CH2OH:InChI=1S/C2H6O/c1-2-3/h3H,2H2,1H3 (standard InChI)

 全てのInChIは、”InChI=”という文字列から始まり、現在は1であるバージョンの数が続く。standard InChIでは、これにSの文字が続く。残りの情報は、レイヤーとサブレイヤーの配列として構造化され、各々のレイヤーは、1つの種類の情報を収める。レイヤーとサブレイヤーは、区切り文字 ”/” で隔てられ、(メインレイヤーの化学式サブレイヤーを除き)固有の接頭文字で始まる。6つのレイヤーと各々の重要なサブレイヤーは、以下の通りである。

1. メインレイヤー

化学式(接頭文字なし) - 全てのInChIに現れる唯一のサブレイヤー
原子の繋がり(接頭文字:”c”) - 化学式中の水素原子以外の原子には番号が付与される。このサブレイヤーでは、原子が他のどの原子と結合されているかを記述する。
水素原子(接頭文字:”h”) - 各々の原子にいくつの水素原子が結合しているかを記述する。

f:id:AI_ML_DL:20210308223005p:plain

L-アスコルビン酸

InChI=1S/C6H8O6/c7-1-2(8)5-3(9)4(10)6(11)12-5/h2,5,7-8,10-11H,1H2/t2-,5+/m0/s1 (standard InChI)

3. 立体化学レイヤー

二重結合とクムレン(接頭文字:”b”)
原子の四面体配置とアレーン(接頭文字:”t”, ”m”)
立体化学の種類の情報(接頭文字:”s”)

構造式からInChI式を推測させようとするこの課題は、非常にレベルが高いと感じる。

さらに、コンペで提供される構造式の画像は、上記アスコルビン酸の構造式のように明瞭なものではない。

課題は、構造式の画像とInChI式(ラベル)を用いてモデルを訓練することにより、構造式からInChI式を予測すること。

数字の並びは、IUPACの規則に従う。 

 

3月9日(火)

HuBMAP:1,205 teams, two months to go(データ更新作業開始)

Run8:EfficientNetB5-Unet, batch=8, 512x512

現在午前11時4分、submitできない。Discussionをみると、新しいデータが届き、更新作業に入ったとのこと。

We've got a new private test set incoming. Some of the old test samples will be moved to train. The process is currently underway and may take some time. Submissions are disabled while this is taking place. We will notify you when the update is complete. Thank you for your patience!

 

BMS149 teams, 3 months to go

画像からテキストへの変換だから、image-captioningのモデルを使うことができるようである。イメージを文字で表現する。分子構造式から、元素の種類と、数と、分子の形状と、官能基の結合位置と水素の結合位置を、InChIの表現形式に則って配置する。

captioningといっても、言葉ではなく、元素記号、結合位置を表す数字やハイフン、丸カッコ、意味内容の区切るスラッシュ、などが1列に並んだものである。アスコルビン酸だと、C, O, Hの3種類だが、N, P, S, Cl, F, Brなどの元素もあり、結合も1重、2重、3重があり、4員環、5員環、6員環、7員環、鎖状、なども区別して表示される。

 

3月10日(水)

HuBMAP:1,205 teams, 2 months to go(データのアップデート完了

さて、再始動だ!と思ってプログラムを動かしたら、早速、エラーが発生した。そう、train/に、マスク無しのデータが存在する(旧test data)ために生じたエラーだ。

借り物のコードを、部分的にしか理解できていない状態で使っているので、エラー解消は容易ではないが、コードを理解する良い機会だ。

 

***別件作業中***

 

3月11日(木)

 

***別件作業中***

 

HuBMAP:1,210 teams, 2 months to go : May 10, 2021 - Final submission deadline.

データセットが更新されることによって、マスクの不具合が修正され、train_dataが増えたことから、LBスコアが跳ね上がったようだ。今の1位はLB=0.921。休眠状態だったつわものたちが本格的に動き始めるとさらに上がっていくのだろう。

さて、データ修正前の値だが、自分の0.836では勝負にならない。望みがあるとすれば、現在LB=0.90+の方が使用しているモデルがEfficientNetB4-Unetで解像度が512x512だということで、特殊なモデルを使っているわけではなく、自分も同様の条件で試したことがあるというところだ。しかし、それは、参加者みんながわかっていることなので、そこまでは到達して当たり前みたいな感じだとすると、やはり、現状では勝ち目はない。

 

別件を済ませて、これに集中したいのだが、・・・。

 

3月12日(金)

HuBMAP:1,211 teams, 2 months to go (gold: 0.916+)

train_dataに更新前のtest_dataが追加されたという情報から、勝手に、マスク無しのtrain_dataとして追加されたと思い込んでいたが、マスクデータは全てのtrain_dataにセットされている。したがって、エラーの原因はマスクではない。

image = dataset.read([1,2,3], <---ここでエラーが生じているようだが、・・・。

IndexError: band index 2 out of range (not in (1,))

よくわからんが、次の3種類のフォーマットが混在しているために、エラーになっているのだろうと思う。

[height, width, channel]

[channel, height, width]

[1, 1, channel, height, width]

ここからは、さらに踏み込んで、コードを、書き換えなければだめだろう。

 

とりあえず、エラーが生じる直前までの8個のtrain_dataだけを用いることができるようにして、計算させてみた。

プログラムが最後まで走るのか、さらに、commit, submitを経て、LBスコアが得られるのかは全く分からないが、train_lossは、データ更新前には見られなかったくらい小さくなっている。そのぶん、極端なoverfittingになっているようにみえる。

trainからinferenceに移ったら、trainのときと同様のエラーが発生した。データの読み込みを、データフォーマットに合わせるように、プログラムしなおす必要がありそうだ。

trainの場合は、フォーマットの違うtrain_dataを読み込まないようにしたのだが、inferenceの場合は、フォーマットの違うtest_dataを避けたらスコアが正しく計算されなくなってしまう。

データ更新前に投稿したものは、更新されたデータを使って再計算されることになっているようだが、自分のプログラムは、更新されたデータには対応していないので、再計算されないように思う。

train_modelを読み込んで、inferenceだけKaggle kernelを使っているチームの場合、Kaggleスタッフでは、trainのやりなおしができないので、あまり、意味がないような気がする。

更新前のデータに対して動いていたのだから、更新されたデータに対しても動いてくれなければ困る、と言いたいところだが、だれも言わないだろうな。それくらいはできないとだめですよ、と言われそうだ。優しいスタッフが、更新されたデータに対応できないチームのために、変換コードを用意してくれるかもしれない。

いやいや、スタッフが乗り出す前に、Kaggler仲間が、解決策を提案してくれている。

参加者どうしで助けあっている!

これが、Kaggleだ!

 

3月13日(土)

HuBMAP:1,213 teams, 2 months to go(現在のLBスコア1位:0.926)

train_dataとしては、15個のうちの8個を使っているだけだが、データ更新前と比べて、train_lossが70%くらいになっているようである。val_lossも相応に下がればうれしいのだが、逆に大きくなっているので、明らかに、overfittingの状態になっている。

マスクの精度が向上していると思うので、train_lossが下がるのは想定どおりであるが、教師データに学んだ結果が、validation_dataに正しく反映されないというのはどういうことなのか。ふつうに、overfittingということで片付けておく。

transformの程度を強くしてみたら、val_lossが明らかに小さくなった。これは良い兆候である。

前処理で、データの切り出しで、MIN_OVERLAPという値があり、これによってスコアが大きく変わったというコメントがあったので、試してみることにする。

切り出しの際の重なりを大きくするということは、データ数を増やしていることになるのだろうと思う。

初期値の32から、64, 128, 192と変えてみた。MIN_OVERLAP=192では、RAMの使用量が約2.5GB増えた。

結果は、若干、overfitting側に変化しているようにみえる。

いずれにしても、現状では、train_dataの全体を読み込むことができず、さらに、もっと困ったことには、肝心のtest_dataが読み込めない。これでは、先に進めない。

明日は、Discussionと公開コードを参考にして、フォーマットが変わってしまったtrain_dataとtest_dataの読み込みができるようにする予定。

 

overfitting、underfitting、スコアアップ: A. Geron氏のテキスト参照(chapter 7: Ensemble Learning and Random Forests):実験第一!

・エポック数(early stopping, OneCycleLRのエポック数)

・モデルの大きさ:B0, B1, B2, B3, B4, B5, B6, B7, B8, 18, 34, 50, 101, 152, 121, 169, 201:パラメータの数を増やせば、より詳細なところまで見えるが、overfittingしやすくなり、汎用性は低下することがある。

・データ量(augmentationによる増量)

・augmentationの種類、変形強度(ElasticTransform, GlidDistortion, OpticalDistortion)

・dropout(ユニット、層、画像内部)

・ensemble(K-fold、stacking, voting, randomsampling, 

・ モデルの初期値(random, Glorot and He, LeCun, Xavier)

・pretrained_model(データの種類、量、質)

・out of bag evaluation

・random patches, random subspaces

 

from today's Twitter:

Deep Learning Weekly@dl_weekly
From this week's issue: Algorithms are meaningless without good data. The public can exploit that to demand change.

 

3月14日(日)

HuBMAP:1,219 teams, 2 months to go(Goldは0.92+)

今日は、Discussionと公開コードを参考にして、フォーマットの違うものが含まれているtrain_dataとtest_dataを読み込むことができるようにする。それができれば、データ更新後の初LBスコアを取得する。

公開コードを参考にして、作業を始めた。自分がベースにしているコードの作者をフォローしようと思って、そのコードを見に行ったら、2日前に更新されており、新たに加わったフォーマットに対応できるように変更されていた。有難い。しかも、そのコードは非常にスマートで、たった1行追加されているだけである。

ということで、更新データに、対応できるようになった。ただし、新たな課題が生じた。train_dataが増えたのはありがたいが、RAMに読み込んで動かしているため、13GBの制限に引っかかって、半分強くらいしか使えない。

さらに、train_lossとval_lossの差が大きい。更新データに対応したコードの製作者も、同様な結果になっていて、更新前のデータに対して使っていたパラメータのままでは、LBスコアは、かえって、悪くなる。

EfficientNetB5-Unetを試してみたが、今日は、LB=0.512が最大であった。いくらなんでもこれは無いだろう、と思った。リーダーボードのスコアがどんどん上がっているので、更新前のデータに対して用いていたのと同じ条件で計算しても、自分のスコアも、当然、上がるものだと思い込んでいた。

今日は、submitできるようになって、良かった。

しかし、明日から、overfittingとの戦いだ!

 

3月15日(月)

HuBMAP:1,223 teams

今頃は、LB=0.85+だと思ったのだが、更新データでの最良値は、なんと、LB=0.531である。原因は、train_dataの半分弱のデータを使っていないことにあるのかもしれない。

更新前と同じ8件のtrain_dataを使っているのだが、更新前と同じデータは5件あるが、3件は異なっている。

15件全部のtrain_dataを使いたいところだが、今の使い方ではメモリーオーバーになるので、まずは、更新前と同じデータ名のtrain_data:8件を使ってみよう。

更新前のrain_dataのみを読み込むようにコードを書き替えた。次のようなコードを追加して、新しいtrain_dataのファイルを読み飛ばすようにした。

if filename == '26dc41664':
    continue

その結果、データ更新前のモデルをそのまま使っても、train中の極端なoverfittingの傾向はなくなった。こんなにも違う結果になるとは、思わなかった。

さらに、train_lossもval_lossもデータ更新前よりも、かなり小さくなったので、データ更新前よりも、良いスコアが得られるのではないかと期待したが、LB=0.831となり、残念ながら、データ更新前のLB=0.836には届かなかった。

これで、ようやく、スタートラインに立てたように思う。

test_dataに類似したtrain_dataを使えば、スコアが良くなるのと同様に、test_dataと似ていないtrain_dataを使った場合にはスコアは上がらない、ということがおきていたということだろうと思う。試験範囲と外れたところを勉強しても良い点は取れない。

こういうことを経験すると、漫然と訓練していても良いモデルは作れない、という気がしてきた。test_dataを適切に処理できるためにどのように訓練しておけば良いのか。テストデータがわかっていれば、訓練データとの類似性や特徴などを調べておくことが重要になりそうだ。Kaggleのコンペがスタートすると、データ解析をしてみせて、訓練データとテストデータの特徴に違いがある場合には、Discussionで注意喚起し、その理由や対処方法まで丁寧に説明してくださるKagglerもおられる。

多値分類だと、分類するクラス毎の訓練データ数を同じになるようにするのだが、バックグラウンドとの識別の場合には、バックグラウンドの面積が何倍にもなる場合がある。そういうとき、segmentationの精度にはどんな影響があるのだろうか。精度に影響する因子にどのようなものがあって、どのように対処すればよい結果が得られるのだろうか。

 

明日は、

1.コードの学習:画像の前処理

2.LB=0.831を超えよう! 

 

3月16日(火)

HuBMAP1,232 teams (May 10, 2021 - Final submission deadline.)

1.コードの学習:3時間程度の予定:Resnet-Unetのようなモデルだが、DecoderにはFPNが使われているようだった。FPNのチャンネル数やUnetのチャンネル数の設定値を見ていて、自分もUnetとFPNを使っているが、デフォルトのままで、自分で設定したことがない。Unetのデフォルトはdecoder_channels=(256, 128, 64, 32, 16)となっているが、公開コードでは、もっと多くのチャンネル数が用いられているように見える。FPNにしても、デフォルトは、decoder_pyramid_channels=256, decoder_segmentation_channels=128となっているが、公開コードでは512チャンネルも使われているように見える。このあたりのパラメータを変えてみて、性能がどう変化するかを調べてみようと思う。

2.更新前のtrain_dataの8件に、新たに追加されたtrain_dataの7件から、メモリー一杯まで読み込んでtrainingすることによって、LBスコアが上がるかどうか調べる。

2件までしか追加できなかったが、とりあえず、train and inferenceを試してみることにする。

train_data=10件:LB=0.839になった。(EfficientNetB4-Unet-DiceLoss)

15件のtrain_dataを全て使うにはどうすれば良いのだろうか。

今使っているプログラムの良いところは、パラメータを変えてタイルを自由に作り変えることができるところだ。全てのtrain_dataを使えるようにすることを最優先に、タイルサイズを小さくし、オーバーラップをゼロにすることによって、15件のtrain_dataの全てを使うことができるようになった。

タイルサイズを小さくすると、1枚のタイルに含まれる糸球体が少なくなり、糸球体の多くが分割されてしまうのではないかと思う。このことが、糸球体のセグメンテーションにおいてどう作用するのだろう。

タイルサイズを小さくして計算した結果、出力ファイルのサイズが標準の5MBを大きく超えて、7.5MBとか21MBになってしまい、前者はエラー、後者はLB=0.675となった。commit中にもタイルサイズに関する警告が出ていたので、これは1024に戻すことを優先しようと思う。 

画像の分割は、非常に重要な作業のように思う。全ての糸球体の外周を分断することなく、完全な形のままで、画像を切り出す方法を調べてみよう。

 

明日は、これ(train_data=10件:LB=0.839になった。(EfficientNetB4-Unet-DiceLoss))を超える方法を検討しよう。その1つの手段として、Unetのdecoder_channelsの変更を行ってみる。

(それにしても、上位との差は歴然!:最終日、トップは0.95+になっていて、自分は0.85+に到達して喜んでいるかもしれない!:あと50日間でこの差を埋めて逆転するためにはどうすればよいのかを考えよう)

 

3月17日(水)

HuBMAP: 1,236 teams (May 10 final submission deadline)

1.コードの学習

@iafoss氏の公開コード:class UneXt50(nn.Module)というものを構築している。パーツとしてFPN, UnetBlock, ASPPが使われている。encoderはResNet50で、decoderはUnetだが、ASPPとFPNが仕込まれている。

高度な機能を組み合わせることで、高度なモデルができるということを示している。


2.Unetのdecoder_channelsの効果

デフォルトのdecoder_channels=(256, 128, 64, 32, 16)を、2倍、decoder_channels=(512, 256, 128, 64, 32)にしてみた。出力データサイズは4.9 MBから5.34 MBに増えた。ノイズが増えたように見える。結果はLB=0.815であった。チャンネル数を増やせば保持できる情報量が増えてデメリットは無いように思うが、overfittingという落とし穴がある。

次は、チャンネル数を半分、decoder_channels=(128, 64, 32, 16, 8)にしてみた。出力データサイズは10.66 MBに増えた。ノイズはさらに増えているように見える。結果は、LB=0.231であった。チャンネル数は各深さにおける特徴量を保持するためのメモリーのようなものだと思うので、メモリーが少なくて情報を保持できないということだろうと思う。train_lossとval_lossの値からは、ここまでスコアが悪くなるとは思わなかった。非常に驚いている。

もう少し遊んでみよう。チャンネル数を減少ではなく増大させるとどうなるのだろう。decoder_channels=(16, 32, 64, 128, 256)としてみると、出力データサイズは6 MBで、ノイズが多そうなデータにみえたが、LB=0.798と、思ったほどはひどくはなかった。

ならば、平均値あたりに揃えたらどうなるのか。decoder_channels=(128, 128, 128, 128, 128)としてみた。LB=0.821

オリジナルの論文を見ると、チャンネル数はこんなもんじゃない。最低が64だから、5段であれば、decoder_channels=(1024, 512, 256, 128, 64)となる。

バカなことして遊んでいる場合じゃなかった。

このセットdecoder_channels=(1024, 512, 256, 128, 64)で計算してみたら、悪くはなかった。明日、submitしてみよう。:LB=0.813:

実は、このセット(512, 256, 128, 64, 32)の次に1024のセットを準備していたが、512のセットのスコアが期待外れだったので、遊びに転じてしまった。

以下に結果をまとめて示す: 

decoder_channels=(128, 64, 32, 16, 8):LB=0.231

decoder_channels=(256, 128, 64, 32, 16):LB=0.831

decoder_channels=(512, 256, 128, 64, 32):LB=0.815

decoder_channels=(1024, 512, 256, 128, 64):LB=0.813

decoder_channels=(16, 32, 64, 128, 256):LB=0.798

decoder_channels=(128, 128, 128, 128, 128):LB=0.821

decoder_channels=(256, 256, 256, 256, 256):LB=0.790

この結果から何か言うとすれば、「いろいろ試してみれば、良い組み合わせが見つかるかもしれない」。

U-Net: Convolutional Networks for Biomedical Image Segmentation
Olaf Ronneberger, Philipp Fischer, and Thomas Brox

f:id:AI_ML_DL:20210317165706p:plain

このUnetのオリジナル論文を検索しているときに、Unetの新しいバージョンに出くわした。

UNET 3+: A FULL-SCALE CONNECTED UNET FOR MEDICAL IMAGE SEGMENTATION
Huimin Huang 1, *Lanfen Lin1, Ruofeng Tong1, *Hongjie Hu2, Qiaowei Zhang2, Yutaro Iwamoto3, Xianhua Han3, *Yen-Wei Chen3,4,1, Jian Wu1

f:id:AI_ML_DL:20210317182919p:plain

このUnet 3+のコードは、GitHubにある:https://github.com/ZJUGiveLab/UNet-Version

新たにコードを開発した研究者や技術者は、開発したコードをGitHubで公開することが多く、論文のabstractの下端に公開サイトのURLを示すことが多い。

 

3月18日(木)

HuBMAT:1,245 teams

今日は、Unet 3+を使ってみよう。https://github.com/ZJUGiveLab/UNet-Versionの、UNet-Version/models/には、次のコードが入っている。

UNet.py
UNet_2Plus.py
UNet_3Plus.py
init_weights.py
layers.py

さらに、UNet-Version/loss/には、次のコードが入っている。

bceLoss.py
iouLoss.py
msssimLoss.py

論文では、hybrid segmentation lossとして、focal lossとMS-SSIM lossとIOU lossを組み合わせたhybrid segmentation lossが提案されている。

UNet_3Plus.pyには、UNet_3Plusの他に、Unet_3Plus_DeepSupとUnet_3Plus_DeepSup_CGMが含まれている。それぞれ、ベースコード、with deep supervision、with deep supervision and class-guided moduleであり、論文中で追加機能として説明されているものである。

道具は揃った。

さて、このUnet_3Plus_DeepSup_CGMを使うにはどうすればよいのか。

まず、簡単なところから始めよう。

1.ライセンスの確認:

https://github.com/ZJUGiveLab/UNet-Versionのreadmeには、次の記述があるだけなので、このURLと論文を引用すれば十分ではないかと思う。

README.md
UNet 3+
Code for ICASSP 2020 paper ‘UNet 3+: A full-scale connected unet for medical image segmentation’

Requirements
python 3.6.2
pytorch 1.3.1

2.UNet.pyを使うための準備(最終的にはUnet_3Plus_DeepSup_CGMを使ってみたいが、使用方法を検討しやすい小さいプログラムを使う)

Unet.pyの最初に、次のコードが書かれている。

import torch
import torch.nn as nn
import torch.nn.functional as F
from layers import unetConv2, unetUp, unetUp_origin
from init_weights import init_weights
from torchvision import models
import numpy as np

UNet.pyをnotebookにコピペして走らせてみると、次のメッセージが出て停止した。

ModuleNotFoundError: No module named 'layers'

from layers import unetConv2, unetUp, unetUp_origin:ここでひっかかっている。

'layers.py'と'init_weight.py'の2つのモジュールを、カレントディレクトリに置かなければならないようだ。

Pythonのドキュメントの、 6. モジュール 、に次の説明がある。

モジュールは Python の定義や文が入ったファイルです。ファイル名はモジュール名に接尾語 .py がついたものになります。モジュールの中では、(文字列の) モジュール名をグローバル変数 __name__ で取得できます。例えば、お気に入りのテキストエディタを使って、現在のディレクトリに以下の内容のファイル fibo.py を作成してみましょう:

次に Python インタプリタに入り、モジュールを以下のコマンドで import しましょう:

つまり、UNet.pyが参照している関数が含まれている"layers.py"と"init_weights.py"をKaggle kernelのカレントディレクトリ―"/kaggle/working/"に入れておけば、importによって呼び出すことができるということのようだ。

そのためには、"layers.py"と"init_weights.py"をKaggleのデータセットに入れて、コードのinput_dataにアップロードすればよい。そうして、次のコードでカレントディレクトリ―にコピーする。

!cp ../input/unet-3plus-pytorch/models/init_weights.py /kaggle/working/
!cp ../input/unet-3plus-pytorch/models/layers.py /kaggle/working/

形式的には、こんな感じだが、これで、

from layers import unetConv2, unetUp, unetUp_origin
from init_weights import init_weights

この2つはエラーなく実行されたようである。

3.UNetを使う

これで、https://github.com/ZJUGiveLab/UNet-Versionの中のUNetを使う準備は整ったと思う。

model = UNet( )

model.to(DEVICE)

 

loss_fnには、 nn.BCEWithLogitsLoss( )を使ったのだが、どうも、うまく動作していないようである。lossが下がらない、すなわち学習が進まない。

とりあえず、先に進もう。

UNet_3Plus.pyは、ベースとなるUNet_3Plusの他に、オプションを付加した、Unet_3Plus_DeepSupとUnet_3Plus_DeepSup_CGMがある。

UNet_3Plusを動かしてみた。batch_size=16ではメモリーオーバーになった、batch_size=8ではGPUのRAMは14.7 GBで動いた。1エポックに7分以上かかっており、lossがわずかづつしか下がらず。

Unet_3Plus_DeepSupとUnet_3Plus_DeepSup_CGMは、GPUのRAMはそれぞれ11.4 GBと12.5 GBでUNet_3Plusより少ないが、いずれも次のようなエラーメッセージが現れて停止した。

AttributeError: 'tuple' object has no attribute 'size'

loss = loss_fn(output, target):この計算の過程でエラーが生じているようである。

とりあえず、オプションなしのUNet_3Plusは動くようになったので、引き続き検討していこう。lossの変化が小さいのは、出口の活性化関数'sigmoid'によるものだろうと思う。他のモデルでも’sigmoid'を使うとlossが減少しにくくなり、LBスコアも0.01くらい下がるので、ここに何か問題がありそうだと思っている。

 

明日は、LB=0.916の公開コードに学ぼう。

何か本質的なところで、自分のtraining方法(もしくはtrainingコード)に本質的な間違いがあるような気がしている。

 

3月19日(金)

HuBMAP:1,251 teams, to May 10

LB=0.916の公開コードに学ぶ:

EfficientNetB2-Unet or FPN:パラメータは activation=sigmoid 以外デフォルト

optimizer=Adam,

loss=bce_dice or bce_jaccard, 

以上の条件は、自分が調べた範囲に、ほぼ、含まれている。これらの条件では、自分は、LB=0.83+止まりであった。

ReduceLROnPlateau, EarlyStopping, 

これらを適切に使えば、自分が使っているOneCycleLRよりも0.01~0.02くらいは高くなる可能性はあるかもしれない。(3月25日修正:OneCycleLRはすぐれもので、適切に使えば上回る可能性もある)

KFold, GroupKFold

これも適切に使えば、0.01~0.02くらい上がるかもしれない。

TTA

これも、0.01~0.02くらい高くなる可能性があるだろうか。TTAを+4でやってみたときには、+0.005程度、上がったことがある。

augmentation(albumentations)

メジャーなものは同じなので、差がつくとしても0.01以下であろうと思うが(3月25日追記/修正:HuBMAPコンペでは強すぎる幾何学的変形は性能を下げる。カラー調整やボケ具合やコントラストは適切に用いると0.02+の向上は可能である)、あまり使われていないものの中には、よいものがあるかもしれない。複数のブロックで画像を隠す方法(3月25日追記:aibumentationに"cutout"というのがある)を使ってみたいと思っているのだがまだ手が出せないでいる。

以上の4つの効果を足すと、0.04~0.07ほど高くなる可能性があり、0.83を足すと、0.87~0.90くらいになる可能性があるということになるが・・・。

もっと、具体的なコードの中身まで検討するつもりだったが、明日以降の宿題とする。

明日からの1週間は、EfficientNetB2-Unetから離れないで、チューニングしてみようと思う。

 

3月20日(土)~3月26日(金)GPU 41h

HuBMAPEfficientNetB2-Unetに固定してチューニングを行ってみよう。

Unetは、activation="sigmoid"にして、他はデフォルトを用いる。

上記のLB=0.916の公開コードでは、loss=bce_dice or bce_jaccard, となっているが、コードをよく見ると、bce_weight=1となっているので、BCE一択のようである。これは、自分の最近の経験と一致する。すなわち、DiceLossとJaccardLossは、activationを"sigmoid"にすると、自分が使っているコードでは、全く機能しない。

BCEと組み合わせるなら、FocalLossやLovaszLossであろう。まずは、BCE単独で進めてみよう。SoftBCEWithLogitsLoss( )

optimizerは、AdamWをOneCycleLRとの組み合わせで使っていて、上記LB=0.916のコードで使われているAdamとは、weight_decayがデフォルトかどうかの違いだけで、大差ないように思う。

OneCycleLRは、比較のために、他のlr_schedulerも使ってみようかと思う。PyTorchは、種類が豊富で迷ってしまう。

簡単に使えて、有用だと思うもの。

torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1, verbose=False)

>>> # Assuming optimizer uses lr = 0.05 for all groups
>>> # lr = 0.05 if epoch < 30
>>> # lr = 0.005 if 30 <= epoch < 60
>>> # lr = 0.0005 if 60 <= epoch < 90
>>> # ...
>>> scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
>>> for epoch in range(100):
>>> train(...)
>>> validate(...)
>>> scheduler.step()

これは、step_size 毎に、lrが、lr x gammaになる。

ReduceLROnPlateau

val_lossをモニターして、変動幅が設定値より小さくなると、lr x gammaを行う方法で、StepLRを自動化したようなもの。

val_acc、val_dice等をモニタしながらlrを変更することもできる。

EarlyStopping, 

 

元のプログラムを書き替えるときに、その場しのぎの書き換えをやったことで、エラーは出ないが、動作は、正しくないということがおきているかもしれない。桁落ちの問題はなかったかな。(TPUの桁落ち対策のことを思い出した)

 

3月20日(土)

LB=0.911の公開コードのtrain_codeに学ぶ。(max: LB=0.918)

EfficientNetB4-Unet(activationはデフォルトのNoneか?)

loss_fn=FocalLoss(alpha=0.25, gamma=2.0, reduction='mean')、この設定(alpha=0.25)はMAnetの論文で見たような気がする。

optimizer=ranger

Ranger - a synergistic optimizer combining RAdam (Rectified Adam) and LookAhead, and now GC (gradient centralization) in one optimizer.

 

EfficientNetB2-Unet:

Unetにactivation="sigmoid"とし、OneCycleLR(AdamW, max_lr=1e-3. epochs=20...), SoftBCEWithLogitsLoss, batch_size=16, train_dataは、データ更新前の8件を用いて計算した結果、LB=0.808となった。最終エポックにおいて、train_loss=0.668, val_loss=0.669となり、14エポック以降は殆ど変化していない。

さすがにこれ(LB=0.808)では、続ける気にならないので、activationを外すことにして、他は何も変えずに計算してsubmitしてみたら、LB=0.850となった。驚いた!!!

最終エポックでtrain_loss=0.0219, val_loss=0.0356となっており、これらは約1.6倍異なっているので、overfittingにしかみえず、これまでであれば、commit, submitしなかったと思う。しかし、今回は、これをスタートラインにしようと思っていたので、commit, submitした。これが、自己ベスト(LB=0.839)を0.011も上回ったことは非常に大きな驚きである。train中に Dice coefficientを計算して表示していれば、もっと早く、このレベルの結果が得られていたかもしれない。

ここで、train_dataを2件追加(CPUの許容範囲ギリギリ)し、他の条件は変えずに計算してcommit, submitしたところ、LB=0.865までスコアが上がった。なにも変わったことはしていないので、なぜ上がっているのかわからない。データが更新された後、更新データが使えるようになったときにtrain_dataを8件から10件に増やしたときには、LBスコアが0.83+から0.839に上がっていたので、今回も上がることは期待していたが、ここまで(0.015も)上がるとは思わなかった。

しかし、トップの、LB=0.928まで、あと、0.063もある。

明日は、OneCycleLRのmax_lrを1e-3の付近で変えてみよう。今日、max_lr=2.5e-4を試してみたら、LB=0.853となった。ちょっと下げすぎたかもしれない。max_lr=5e-4, 2.5e-3, 5e-3あたりを調べてみる。さらに、FocalLossとLovaszLossを試してみようと思う。

 

3月21日(日)

OneCycleLRのmax_lrを変えた時のLBスコアを調べてみた。

lrは、デフォルト設定でepochs=20の場合、max_lr/25からスタートし、6エポックの計算直後にmax_lrに達し、そこからlrは減衰し、最終的にmax_lr/25/1e4となるようである。

結果は次のようになった。数値だけではわかりにくいので、プロットしてみる。

max_lr=2.5e-4:LB=0.853

max_lr=5.0e-4:LB=0.866

max_lr=1.0e-3:LB=0.865

max_lr=2.5e-3:LB=0.855

max_lr=5.0e-3:LB=0.863

プロットすると下図のようになる。LBスコアはmax\lrが5e-4と1e-3の間で最大になりそうだが、5e-3より大きい値にするとどうなるのかも気になる。

f:id:AI_ML_DL:20210321105826p:plain

f:id:AI_ML_DL:20210321104528p:plain

この挙動は、エポック数によっても変わる可能性がある。

max_lrを小さくすると、全過程においてlrは小さいので、収束速度が遅くなると推測される。もしそうであれば、max_lr=2.5e-4の条件でエポック数を増やせばLBスコアはもっと高くなるはずである。

max_lrが大きい場合、6エポック前後でのlrが標準的な値を超えているときには、不安定な動きをしているようにみえる。減衰しはじめてから標準的なlr値になるまでに時間がかかるため、標準的なlr値に戻ってから最終エポックまでの計算量が少なくなるために、学習が不十分という状況が生じているかもしれない。

OneCycleLRのエポック数を増やしても、lrの変化は相似形になるので、max_lrが大きい場合にエポック数を増やすと、適正なlrになるエポック数は増えるが、適正値を超えるlrになっているエポック数も増える。といっても、適正値を超えるlrになっているエポック数が増えることが、最終的な学習結果にどう影響するかはわからない。

すべては、やってみないとわからない、としておこう。

細かすぎるような気もするが(目標は遥か彼方)、次の2つの条件を追加してみる。

max_lr=7.5e-4:LB=0.857

max_lr=1.0e-2:LB=0.858

f:id:AI_ML_DL:20210321153650p:plain

f:id:AI_ML_DL:20210321153724p:plain

期待しかなかったのだが、笑うしかない。

 

次は、1サイクルではなく、複数サイクルのスケジューラーを使ってみよう。

torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr, max_lr, step_size_up=2000, step_size_down=None, mode='triangular', gamma=1.0, scale_fn=None, scale_mode='cycle', cycle_momentum=True, base_momentum=0.8, max_momentum=0.9, last_epoch=-1, verbose=False) 

f:id:AI_ML_DL:20210323215518p:plain
ちょっと動かしてみただけだが、optimizerとしてAdamを使おうとしたら、momentumが使えないoptimizerは使えない、というようなメッセージが現れて停止した。そのため、SGDを nesterov=Trueとして使ってみた。base_lr=0.1, max_lr=3, epochs=20に設定し、他はデフォルトで使ってみたら、20エポックでは1サイクルの途中までだった。そこで、20エポックで3サイクルになるようにstep_size_up=500(デフォルトは2000)として計算してみたら、lr=3付近で異常値を示した。想像の域を出ないが、急激なlrの増大が良くなかったのではないかと思う。max_lrを3から2に下げたら、正常に動き、最終の20エポック目のlossもそれなりの値を示した。

サイクルの繰り返しによって、val_lossが下がっているので、max_lrとstep_size_upとエポック数の最適な組み合わせを探してみよう。

 

3月22日(月)

lr_scheduler.CyclicLR、20エポックで、max_lr=2とし、1サイクル、2サイクル、3サイクルになるようにstep_size_upを調整し、計算した結果をsubmitした。結果は以下のようになった。カッコの中は、最終エポックのtrain_lossとval_lossである。

3サイクル:LB=0.869 : (0.267, 0.314)

2サイクル:LB=0.839 : (0.258, 0.301)

1サイクル:LB=0.856 : (0.243, 0.296)

自己ベストが出たのはよかったが、lossの値からは、サイクル数が少ないほどLBスコアは高くなると予想していたので、あてが外れてしまった。 

これならいけるだろうと思って意気込んで計算してsubmitしたのが次の結果である。

30エポック:3サイクル:max_lr=3:LB=0.835 : (0.253, 0.353):val_lossが高すぎ!

最初から、base_lr=0.1, max_lr=3 : 20エポック&3サイクルと決めていたが、20エポック&3サイクルでは、max_le=3に設定すると、lr=3付近でval_lossが発散した。そこで、max_lr=3でも発散しない条件を調べて、30エポックであれば良い結果が得られそうだと思ってトライした。val_lossが0.353となり、かなり大きい値だが、29エポック目では、(0.251, 0.303)だったので、いけるかもしれないと思い、commit, submitした。残念。

ステップ数を正しく計算していなかったために、30エポックの最後は最少のlrが適用されるべきところが、base_lrからの立ち上がりの30ステップぶんのlrが適用されたために学習した重みが若干かく乱された可能性がある。少なくともステップ数は正しく設定しておくべきであった。(745とすべきところを740と設定していた)

他の条件についても正確に計算すると、偶然だが、LB=0.869となったものだけが、最終エポックがlrの減少中に終わっていた。494と入力するつもりのところに、500と入力されていたのが幸いしたのかもしれない。LB=0.835となったものは、max_lr=3であったことも、悪い方に作用したようである。

OneCycleLRを使っているときに、max_lr=3が頭にこびりついていて、今回のCyclicLRでも、base_lr=0.1, max_lr=3がベストと思い込んでしまっていたようだ。Leslie N. Smith氏の論文では、0.1-0.5, 0.1-1, 0.1-1.5, 0.1-2, 0.1-2.5, 0.1-3, 0.1-3.5, 0.1-4, 0.09-0.9など、いろいろ試した例が表に載っている。

Leslie N. Smith氏の2編の論文より、

Furthermore, there is a certain elegance to the rhythm of these cycles and it simplifies the decision of when to drop learning rates and when to stop the current training
run. Experiments show that replacing each step of a constant learning rate with at least 3 cycles trains the network weights most of the way and running for 4 or more cycles
will achieve even better performance. Also, it is best to stop training at the end of a cycle, which is when the learning rate is at the minimum value and the accuracy peaks.

3サイクル以上とすること(追記:要検討)、および、lrが最小値になるエポックで止めること、と書かれている。自分が気付いた注意点としては、ステップ数を正しく計算し、最終エポックの計算中に、lrが立ち上がっていく領域が含まれないようにすることが重要である。

3/24追記:cyclicの後に1 sycleの発想が出てきている。ここまでの自分の実験からは、サイクル数、学習率の範囲と変化形状、などの最適値は、用いるデータの量や質に依存し、モデルや画像処理にも依存するので、実験を適切に繰り返しながら見つけよう。

CyclicLRのエポック数は12から100くらいまで、OneCycleLRのエポック数は100から800くらいまで表に記載されている。max_lrが大きいほど、エポック数は多い傾向にある。自分が計算した例では、max_lrを3にすると異常値が現れてモデルが壊れることがあった。そういうときは、エポック数を増やすか、max_lrを下げるかする必要がある。

 

ずっと気になっていたのだが、train_lossとval_lossしか計算していないので、明日は、train_dice, val_diceを計算して表示できるようにしよう。

 

3月23日(火)

GPU : 17h 38m available of 41h 

HuBMAP

20エポック、3サイクル、0.1-2、(傾斜:(max_lr-base_lr)/step_size_up:(2-0.1)/500):LB=0.869:(昨日の結果)

20エポック、3サイクル、0.1-1、(傾斜:(max_lr-base_lr)/step_size_up:(2-0.1)/500):LB=0.845:(昨日の結果)

20エポック、4サイクル、0.1-2、(傾斜:(max_lr-base_lr)/step_size_up:(2-0.1)/373):LB=0.851:傾斜が1.34倍(max_lr=2.65と同等)

傾斜もmax_lrも同じにして、4サイクルにする:

27エポック、4サイクル、0.1-2、step_size_up=503:LB=0.846:

train_loss=0.0249, val_loss=0.0296:lossから予想されるLBスコアとの乖離は非常に大きいと感じる。

 

LB=0.869の再現実験:

1.条件設定を完全に一致させる:

2.train_lossもval_lossも表示範囲で一致、出力も見える範囲で完全一致

3.LB=0.869は、再現された。

max_lrを2から1にしただけで、LBスコアは、0.869から0.845に下がり、傾斜もmax_lrも同じにして、サイクルを1つ増やしただけで、0.869から0.846に下がった。

max_lr=2.25とmax_lr=1.75を試してみよう。

20エポック、3サイクル、max_lr=2.25 : LB=0.863 : (train_loss=0.0269, val_loss=0.0324)

20エポック、3サイクル、max_lr=1.75 : LB=0.869 : (train_loss=0.0266, val_loss=0.0352)

3月27日追記:20エポック、3サイクルでは、max_lrは2より小さい方が良いのか、もう少し調べてみたい。

OneCycleLRはAdamWを使っていた。CyclicLRでは、AdamWが使えないので仕方なくSGD(nesterov=True)を使っているのだが、良い結果が得られているので、OneCycleLRでも、再度、SGDを使ってみようかなと思う。

 

エポック数:

Leslie N. Smith氏の論文の表を見ると、エポック数を25、50、75、100、150と増やせば、確実に、スコアは上がっている。データ量が多い場合はこういう傾向になるのだろう。他方で、データ量が少ないときには、20エポックくらいで、CyclicLRやOneCycleLRが良い結果を与えることを、実験して確かめているようだ。

 

diceの計算:次のサイトから借用させていただく

https://github.com/UCAS-Vincent/UNet-3Plus/blob/master/metrics.py

def dice_coef(output, target):
      smooth = 1e-5

      if torch.is_tensor(output):
      output = torch.sigmoid(output).data.cpu().numpy()
      if torch.is_tensor(target):
      target = target.data.cpu().numpy()
      #output = torch.sigmoid(output).view(-1).data.cpu().numpy()
      #target = target.view(-1).data.cpu().numpy()

      intersection = (output * target).sum()

      return (2. * intersection + smooth) / (output.sum() + target.sum() + smooth)

......

dices = []

for image, target in loader:

      ......

      dice =dice_coef(output, target)

      dices.append(dice.item())

np.array(dices).mean()

動作確認中!!!

 

明日は、30エポック、3サイクル、0.1-3、1サイクルの前後の割合を30:70と70:30にした結果を比較してみる(OneCycleLRのデフォルトは30:70である:pct_start=0.3)。さらに、base_lrを0.1より小さくした場合についても調べる予定。OneCycleLRでも、SGDを試してみよう。

 

3月24日(水)

HuBMAP:1,291 teams:48 days to go : 0.869 ---> ???

remaining time of GPU:7 h

30エポック3サイクル、0.1-3、30:70:LB=0.857 : (train_loss=0.0253, val_loss=0.0368)

30エポック3サイクル、0.1-3、50:50:LB=0.835 : (train_loss=0.0253, val_loss=0.0353)

30エポック3サイクル、0.1-3、70:30:LB=0.834 : (train_loss=0.0270, val_loss=0.0310)

50:50は既出。昨日までのCyclicLRの結果は、すべて、50:50である。

lossは、最終エポックの平均値であり、モデルの最終パラメータは最終ステップ(バッチ)の学習結果だと思うので、これだけでも、対応関係は慎重に判断しないといけないように思う(この見立てが正しければ)。dice_coefficintの計算も、エポック毎の計算だと、予測性能の評価指標として不十分になる可能性はlossとかわらないだろう。今使っているCyclicLRは、最終エポックと最終ステップ(バッチ)における評価値(lossやacc)の差が、大きくなりやすい。(ということなのだろう)

30:70が良さそうだということで、ここまででLBスコアが最も高かった条件で、50:50を30:70に変えただけで計算し、commit, submitした。

20エポック3サイクル、0.1-2、30:70 : LB=0.849 : (train_loss=0.0252, val_loss=0.0301)

lossがエポック毎の平均値だからあてにならないとはいえ、つい、この値で判断してしまう。

submitするときにこのlossを見て、自己記録更新を確信した。

LB=0.849を見て、落胆した。

まさか、0.02も下がるとは思わなかった。

public_test_dataとの相性が悪いのだ、と思いたいが、汎用性が低い、すなわちoverfitting状態だということだろうと思う。

lossの全体としての変化の傾向から判断することにしよう。

この場合はoverfittingになったと仮定して、それを解消する方法として、少し条件のきついtransformを適用してみよう。transformの条件を強くし、他の条件はそのままで計算してみよう。

計算の結果、最終エポックでのlossは、train_loss=0.0266, val_loss=0.0372となった。train_lossが少し大きくなっているのは期待通りだが、val_lossは、平均値としてみても、あるいは1つ手前のエポックでの値と比較しても、少し大きすぎるように思う。

結果は、LB=0.875となった。自己新記録だ!

これは、30:70:LB=0.849からの0.026ものジャンプアップだ。

それならば、50:50 : LB=0.869に対して、この条件を強めたtransformを適用するとどうなるだろうか。明日計算してみよう。

もし、50:50でoverfittingになっていないのであれば、それに対して強めのtransformを適用すると、underfittingになる可能性もある。実験してみよう!

GPUの残りは5時間、20エポックで35-40分、commitがあるから70-80分、残り4条件程度となる。

OneCycleLRからCyclicLRへと移ってみたが、CyclicLRの完成系の1つが、OneCycleLRのようである。自分の計算結果を見直すと、train_lossもval_lossもOneCycleLR(optimizer=AdamW)を用いた場合の方が小さくなっている。

OneCycleLRを用いたときのLBスコアが0.86+で頭打ちになっていたのは、overfittingへの対策が不十分だったということだろうと思う。

ということで、明日は、OneCycleLR(optimizer=AdamW)と強めのtransformを用いて計算してみる。

 

3月25日(木)

HuBMAP: 1,295 teams:47 days to go : 0.875 ---> ???

remaining time of GPU:3.5 h

CyclicLRとSGDを組み合わせてある程度うまくいったので、OneCycleLRもSGDと組み合わせて使ってみよう。

OneCycleLRとAdamWとの組み合わせでLB=0.865となったコードをそのまま使う。

OneCycleLR(AdamW, maxlr=0.001) : LB=0.865 (train_loss=0.0238, val_loss=0.0311)

OneCycleLR(SGD, nesterov=True, max_lr=3) : LB=0.861 (train_loss=0.0252, val_loss=0.0286)

train_lossが小さいAdamWの方に、強めのtransformを適用してみよう。

OneCycleLR(AdamW, max_lr=0.001) : LB=0.869 (train_loss=0.0251, val_loss=0.0322)

0.004のアップ。期待したほどではなかった。

SGDのOneCycleLRを使ったものに対しても強めのtransformを試してみよう。

OneCycleLR(SGD, nesterov=True, max_lr=3) : LB=0.862 (train_loss=0.0262, val_loss=0.0328)

0.001のアップ。LB=0.875を超えることを期待したが、そうはならなかった。

 

次の1週間(3月27日(土)~4月2日(金))は、次のような、細かいチューニングをやってみよう。

⇒ OneCycleLR(SGD):max_lr=3を2.5, 2.0, 1.5,1.0等に変える。

⇒ OneCycleLR(AdamW) :epochs=20を18, 22, 24等に変える。

⇒ OneCycleLR(SGD), (AdamW) : pct_start=0.3を0.4, 0.5等に変える。

⇒ CyclicLR(SGD):3cycle epochs=3n (18, 21, 24, ... )  

⇒ CyclicLR(SGD):epochs=20, 3 cycle (step_size_up=297, step_size_down=696), max_lr=2で、mode="triangular2"を試す。

⇒ CyclicLR(SGD):base_momentum=0.85, max_momentum=0.95,を試す。

⇒ augmentationのCutout, blurなどのパラメータを変えてみる。

 

⇒ batch_size=16 --> 12 --> 8 --> 4 --> 2 -->1 ???

⇒ 画像解像度:EfficientNetB2でバッチ数を下げて解像度を上げる。256x256 --> 320x320 --> 384x384 --> 448x448 --> 512x512:overlapも増やす、32 --> 48 --> 64 --> 96 --> 128

⇒ B2 --> B3 --> B4:これは、B2で、十分に検討した後で!

⇒ train_dataの使用量を増やす。

 

overfittingの程度によって、augmentationの効果は変わる。OneCycleLRはエポック数によってoverfitting側にもnderfitting側にもある程度制御しながら振ることができるので、今日実験した系に対して、追加で試してみようと思っている。

20エポック、3サイクル、0.1-2、50:50 : LB=0.869に対して強めのtransformを試す予定だったが、OneCyleLR(SGD)を試したことで、GPUを使い切ったため、土曜日以降になる。

CyclicLRは、OneCycleLRよりも自由度が高く、良い値も得られているので、続けてみようと思う。CyclicLRには3種類のモードがあって、"triangler"は三角形の頂点の学習率が変わらないが、"triangler2"は、三角形の頂点の学習率が、1, 1/2, 1/4と減衰する。さらに、“exp_range”では、三角形の頂点の学習率が指数関数的に減衰する。gammaとステップ数によって減衰率を変えることができるのだが、ピーク値がどういう値になるかは与式に従って計算しておく必要がある。

1サイクルが300+700チャンネルで3サイクルまでの場合:

gamma=0.999:各サイクルのピーク値は、0.74, 0.27, 0.10となる。

gamma=0.9993:0.81, 0.40, 0.20となり、"triangle2"と似たような変化率になる。

gamma=0.99965:0.90, 0.63, 0.45 (1.0 : 0.7 : 0.5):ちょっと試してみたくなる。

Cyclicと1 Cycleの比較:

Cyclicのデフォルト:base_momentum=0.8, max_momentum=0.9

1 Cycleのデフォルト:base_momentum=0.85, max_momentum=0.95

Cyclicのbase_lr:固定かつデフォルト無し:論文では0.1が多いように思う。

1 Cycleのbase_lr:可変:初期値はmax_lr/div_factor(デフォルトはmax_lr/25)、最終値はmax_lr/div_factor/final_div_factor(デフォルトはmax_lr/div_factor/1e4):1 Cycleでは、学習率の初期値も最終値も、Cyclicより小さく設定しているようである。この方が良いということであれば、Cyclicのbase_lrは0.1よりも小さい値にした方が良さそうである。実験結果では、一例に過ぎないが、20エポック、2サイクル、max_lr=2で、base_lr=0.1, 0.001, 0.00001にしたとき、LB=0.839, 0.853, 0.847となった。 

 

3月26日(金)

HuBMAP:1,296 teams, 46 days to go : top 0.934

augmentationを正しく理解したい。

基本的な考え方:augmentationを行う確率は100%より低い。ある割合は元画像をそのまま通過させるためだ。ただし、切り出しやサイズ変更では100%処理もありうる。データ量が多い場合は、augmentationの必要性は少なく、確率は10-30%で変化の強度も低くする。データ量が少ない場合は、それぞれ40-50%の確率で処理し、変化の強度も高くする。

画像を水増しするというが、1つの処理を50%の確率で行うと、倍になるのか。100枚の画像に対してノイズ添加を50%の確率で行うと、50%はノイズが加わり、50%がそのまま通過する。トータルは100枚のままである。水増しの文字通りの意味であれば、元画像はそのまま使い、そのうちの半分にノイズ添加をして元の画像に足せば、150枚の画像が出来上がることになる。あるいは、100枚にノイズを添加して元の画像に加えれば200枚になる。このあたりのことを具体的に何も知らないのがはがゆい。

自分が使っているコードの動きを見ている限りでは、指定した割合でデータ加工されているようで、加工の種類を増やすとそれだけ元画像の割合が少なくなるようであり、これでいいのかなと思ってしまう。

それだったら、オートフォーカスじゃないけど、全ての画像をCNN処理しやすいように前処理できればそれがベストということになるのか。前処理せずとも全ての画像を正しく処理できるモデルを構築するということになるのか。求められているのは後者だ。実画像にありそうな画像処理をしてモデルを訓練するということだ。

import albumentations as A

transform = A.Compose([
      A.RandomCrop(512, 512),
      A.RandomBrightnessContrast(p=0.3),
      A.HorizontalFlip(p=0.5),
])

3つの処理が上から順に並んでいる。処理は、この順番に行われる。

1.元画像から512x512の部分をランダムに切り取る。これは、100%行われる。

2.1.で処理された画像のうち、3割は、明るさやコントラストの変更がランダムに行われ、7割は、未処理で通過する。

3.2.を通過した画像のうち、5割は水平方向にフリップされ、5割はそのまま通過する。

これだと、入力画像数と出力画像数は同じで、cropされただけで通過する画像は35%、cropされてフリップされた画像が35%、cropされて明るさやコントラストを変更されただけの画像が15%、cropされ明るさやコントラストを変更されフリップされた画像が15%ということになる。

こうして、1式の画像を処理した画像がメモリーに保存されると、学習中、エポック単位の画像は変化しない。エポック毎にaugmentationを行うという方法が使えれば、エポックごとに違う画像で訓練することができて、良さそうに思うが、計算時間はそれなりに増えそうに思う。

このあたりの制御を的確に行うためには、ゼロからプログラムする技術、プログラムを正確に読む技術、プログラムを正確に組み込む技術、などが必要になるので、そこを意識してプログラムを読もう。

このコンペに必要な加工を考えて、augmentationのパラメータを決めよう。そのためには、モデルが予測した結果を画像化して、どのようなミスをしているのかを具体的に把握して対策していくことが必要かもしれない。LB_scoreトップの方が、セグメンテーションが難しい糸球体を特定し、それらの画像に特化した対策をすることによってLBスコアが上がったというようなことを言っておられたと思う。

 

3月27日(土)~ 4月2日(金)LB=0.875 ---> 0.88+

1.0.875となったモデルの周辺探索(epochs, augmentation, resolution, max_lr, ...) 

2.CyclicLRの、"triangular2 ", "exp_range"の検討(21エポック以下を重点的に)

 

3月27日(土)

HuBMAP:1,303 teams, 45 days to go :  top 0.934

GPU : 42 h

Run 1 : based on the model of LB=0.875 (0.0266, 0.0372) : E20 B16(149) 1024,32,256 trfm_4 b2-Unet CyclicLR SGD(nestelov) lr=0.1-2 triangular cycle=3 298,696 momentum=0.8-0.9 BCE train_data=8+26d...+4ef... : lr=0.1-2 --> lr=0.001-2 : LB=0.867 (-0.008) (0.0271, 0.0347)

Run 2 : triangular --> triangular2 : LB=0.861 : (0.0258, 0.0375)

Run 3 : batch_size=16 --> 8 : 4エポック目まで普通だったのだが、5エポック目からtrain_loss, val_lossともに異常値を示し、5エポックほど待ったが、全く改善されなかったので中止した。

バッチサイズが変わると1エポックあたりのステップ数(データ量÷バッチサイズ)が変わるので、パラメータを計算し直さないといけないのだが、それを怠ったことによるエラーであることが分かった。20エポック、6サイクルの設定になっていた。

Run 4 : batch_size=16 --> 24 : LB=0.867 (0.0255, 0.0333)

これも再計算を怠ったので、20エポック、2サイクルで動作したことになる。

Run 5 : momentum 0.80, 0.90 --> 0.85, 0.95 : LB=0.866 (0.0278, 0.0329)

Run 6 : image 256x256 --> 320x320 : LB=0.877 (0.0238, 0.0319)

0.002だけだが、自己新記録となった。

バッチサイズ効果については、パラメータを正しく設定してやり直す必要がある。

Run 1のbase_lrの値は、OneCycleLRの設定を真似て少し低くしてみたのだが、効果的ではなかったようだ。

Run 2のtriangular2は、サイクルごとに学習率lrのピーク値が半分になるので、ピーク値が同じ場合よりも収束しやすくなって良いかなと思ったが、こう思ってしまうのは、lrを段階的に下げるとか指数関数的に下げるという従来の方法が頭にこびりついているせいだろうなと思う。

バッチサイズは、ケースバイケースのようでよくわからず、試してみたいと思ったのだが、パラメータ設定をミスったので、明日にでも、やりなおしてみる。

momentumは、OneCycleLRとCyclicLRとでデフォルトが違っているので、試しに相手側のデフォルトにしてみたということ。今回は効果的ではなかったが、使える場面はあると思う。

画像解像度を上げることは、効果的だが、overfittingとの戦いになりそう。

 

3月28日(日)

HuBMAP:1,309 teams, 44 days to go : 0.057 to first place

今日は、昨日効果が認められた320x320より画素数を増やしてみよう。

B2-Unet, batch_size=16, 320x320, CyclicLR(SGD, epochs=20, up=298, down=696, 0.1-2.0, momentum=0.8, 0.9, triangular, ... : LB=877 (0.0238, 0.0319)

384x384 : LB=0.873 (0.0229, 0.0323)

448x448 : LB=0.860 (0.0212, 0.0303)

512x512 : LB=0.871 (0.0202, 0.0294)

train_lossもval_lossも小さくなっているので、スコアが上がってもよさそうなものだが、足ふみ状態だ。train_lossが高めであることから、いずれもoverfittingになっているのだろうと思う。

ここで、decoderを変えてみよう。

512x512に対する対応可否の確認も兼ねて512x512から始めよう。

Linknet:512x512 : LB=0.876 (0.0220, 0.0285)

PSPNet:収束せず。

FPN:収束が非常に悪いので中止。

MAnet:512x512 : LB=0.865 (0.0220. 0.0284)

PSPNetとFPNの応答が良くない。その原因を調べたいが、後日とする。

 

3月29日(月)

HuBMAP:1,313 teams, 43 days to go : 

512x512について、Unet, MAnetよりも良いLBスコアを示したLinknetについて、他の解像度についても実験してみる。

256x256 : (0.0301, 0.0356) : commitまで

320x320 : 見送り

384x384 : 見送り

448x448 : (0.0224, 0.0291) : LB=0.862

Linknetの256x256, 448x448のlossの値がUnetよりも高い。このことは、LinknetがUnetよりも性能が悪いことを示唆している。512x512でも、UnetよりLinknetのlossが大きく、それでもLBスコアが高いのは、Linknetの性能が少し悪いことによってoverfitが少し抑えられたためであると推測される。Linknetについては、ひとまず中断。

解像度の低い画像に対してはencoderを大きくし、解像度の高い画像に対してはencoderを小さくする、というのも1つの方法かもしれない。

 

encoderをb2からb3, b4に変更してみた。すると、大きな問題が生じた。

320x320にb3-Unetを用いると、次に示すように、3サイクル目の3エポック目からlossが1桁大きくなりそのまま収束しなくなった。

  1│ 0.1397│ 0.0703│ 1.70
  2│ 0.0531│ 0.1602│ 1.67
  3│ 0.0576│ 0.0428│ 1.67
  4│ 0.0378│ 0.0418│ 1.67
  5│ 0.0356│ 0.0394│ 1.66
  6│ 0.0316│ 0.0344│ 1.67
  7│ 0.0292│ 0.0327│ 1.66
  8│ 0.0288│ 0.0353│ 1.67
  9│ 0.0341│ 0.0370│ 1.67
10│ 0.0350│ 0.0405│ 1.67
11│ 0.0334│ 0.0339│ 1.67
12│ 0.0299│ 0.0304│ 1.67
13│ 0.0274│ 0.0355│ 1.67
14│ 0.0260│ 0.0322│ 1.67
15│ 0.0248│ 0.0337│ 1.66
16│ 0.0273│ 0.1100│ 1.68
17│ 0.3699│ 0.2800│ 1.68
18│ 0.2860│ 0.2863│ 1.67
19│ 0.2851│ 0.2800│ 1.67
20│ 0.2845│ 0.2840│ 1.67
21│ 0.2855│ 0.2821│ 1.69

数字は、左から、エポック数、train_loss, val_loss, 計算時間(分)を表している。

3サイクル目は15エポックから始まり、17エポック目の途中で学習率lrが最大になるように設定されている。2サイクル目まで問題ないように見えて、3サイクル目でこのような現象が生じるとは思わなかった。

20エポック、3サイクルで、max_lr=3以上に設定すると、学習率lrが最大になるエポックで異常値を示すということはわかっていたが、今回のように、2サイクルまで正常だったので、驚いた。

max_lrを2.0から1.5に下げて、他はそのままで計算してみた。

15│ 0.0243│ 0.0295│ 1.66
16│ 0.0266│ 0.0417│ 1.67
17│ 0.0300│ 0.0360│ 1.69
18│ 0.0269│ 0.0586│ 1.66
19│ 0.0262│ 0.0431│ 1.67
20│ 0.0241│ 0.0339│ 1.66
21│ 0.0228│ 0.0278│ 1.68

異常は生じずに、収束した。

encoderのサイズや画像の解像度によって、max_lrを最適化することが必要であることがわかった。

256x256の画像に対してencoderにEfficientNetB3を使ったときに、最終エポックのtrain_lossが少し大きくなっている(収束が良くない)ように感じたことがあった。このときも、症状は軽いながらも、異常が生じていたということだろうと思う。

ということであれば、4サイクルに増やした時に、4サイクル目で収束しなかったのも、同様の現象が生じていたのかもしれない。すなわち、サイクルを繰り返すと、訓練が進むが、訓練が進んでも同じmax_lrを適用していると、学習率lrを大きくした効果がなくなるだけでなく、かえって、モデルが悪化したり、収束しなくなるということだろう。学習が進んだモデルに大きすぎる学習率を適用すると、完成に近づいていたモデルが壊れてしまう、ということだろう。

そういうこともあって、サイクル毎にピーク値が半減する"triangular2"がオプションとして用意されているということなのだろう。

さらに、max_lrをサイクルごとに減衰させることができ、かつ、減衰の仕方を変えることができる"exp_range"というのも、オプションとして用意されている。

3サイクル、21エポック、学習率のベースと最大値:0.1-1.5

256x256 : b3 : (0.0266, 0.0315) : LB=0.858

320x320 : b3 : (0.0228, 0.0278) : LB=0.868

encoderを大きくすることで、val_lossが小さくなってoverfittingが抑制されることを期待したが、max_lrを下げざるを得なくなって、Super-Convergenceがうまく働くなってしまっているのだろうか。

 

overfitting対策のために、augmentationの調整をしてみよう。

早速augmentationのミスがあった。

Cutoutを使っているのだが、切り出しサイズのピクセル数が、256 x 0.1のように絶対値にしていた。そうすると、画像解像度が2倍になったとき、切り取りサイズが相対的に半分になる。面積で1/4。画像の画素数が多いほど、augmentationの効果は小さくなっていたということになる。

Cutoutのサイズを画素数に連動するよう変更するとともに、確率、サイズ、個数等の適切な値を探してみよう。

次の論文のFig. 1では、隠すのは1か所で、1/4面積の正方形で、はみだしOKで、100%適用している。はみだし無しで50%とか、サイズの異なるものも試みられているようである。Kaggleのコンペで見かけるのは、サイズを小さくして数を増やしたものが多い。

f:id:AI_ML_DL:20210329213646p:plain

f:id:AI_ML_DL:20210329213555p:plain

先入観にとらわれず、色々なパターンを実験してみるしかないように思う。借り物は,小さい正方形を10枚使っている。

面積1/4のサイズを1枚:1/2 x 1/2 :確率25%に設定。

1│ 0.1415│ 0.0802│ 2.08
2│ 0.0614│ 0.1015│ 2.05
3│ 0.0744│ 0.2674│ 2.05
4│ 0.0598│ 0.0492│ 2.06
5│ 0.0479│ 0.0665│ 2.04
6│ 0.0438│ 0.0423│ 2.03
7│ 0.0404│ 0.0439│ 2.04
8│ 0.0396│ 0.0905│ 2.05
9│ 0.0472│ 0.0615│ 2.05
10│ 0.0490│ 0.0418│ 2.06
11│ 0.0437│ 0.0417│ 2.07
12│ 0.0396│ 0.0459│ 2.04
13│ 0.0367│ 0.0365│ 2.05
14│ 0.0352│ 0.0389│ 2.04
15│ 0.0365│ 0.0804│ 2.06
16│ 0.0377│ 0.0459│ 2.07
17│ 0.0406│ 0.1307│ 2.09
18│ 0.0411│ 0.0389│ 2.07
19│ 0.0370│ 0.0420│ 2.06
20│ 0.0329│ 0.0397│ 2.07
21│ 0.0340│ 0.0365│ 2.08

0.03以下でないと使えない。

面積1/10 のサイズを1枚≒0.316x0.316:確率25%に設定。

1│ 0.1341│ 0.0948│ 1.91
2│ 0.0531│ 0.0647│ 1.88
3│ 0.0431│ 0.0494│ 1.89
4│ 0.0389│ 0.0364│ 1.86
5│ 0.0358│ 0.0354│ 1.87
6│ 0.0316│ 0.0367│ 1.88
7│ 0.0296│ 0.0295│ 1.88
8│ 0.0289│ 0.0412│ 1.87
9│ 0.0307│ 0.0409│ 1.86
10│ 0.0335│ 0.0333│ 1.87
11│ 0.0315│ 0.0369│ 1.88
12│ 0.0298│ 0.0325│ 1.86
13│ 0.0271│ 0.0337│ 1.89
14│ 0.0268│ 0.0330│ 1.87
15│ 0.0258│ 0.0397│ 1.88
16│ 0.0282│ 0.0364│ 1.87
17│ 0.0296│ 0.0818│ 1.88
18│ 0.0323│ 0.0323│ 1.87
19│ 0.0274│ 0.0340│ 1.87
20│ 0.0250│ 0.0366│ 1.88
21│ 0.0256│ 0.0295│ 1.88

LB=0.874:A.cutoutが効いたかどうかはまだ不明。P=0.のときの結果が必要。

3サイクル目のlossの推移が若干、不自然のようだ。前に書いたが、3つの山が同じ高さだと、学習がある程度進んだあとの3つ目の山で収束が悪くなることや、モデルが壊れてしまうことがある。うまくいってるように見えても、収束が悪くなっている可能性がある。

ただし、ピークの高さゆえの、Super-Convergenceを利用しているので、全体を低くしてしまうわけにはいかない。

21エポック、3サイクル、batch\size=16の場合に、"exp_range"でgamma=0.9999とすれば、3つの山のピークは、0.969, 0.873, 0.787となる。こういうのを試してみよう。

gamma=1.0 (=triangular)  1.0, 1.0, 1.0 : LB=0.874 (0.0256, 0.0295)

gamma=0.9999   : 0.969, 0.873, 0.787 : LB=0.863 (0.0256, 0.0294)

gamma=0.9998   : 0.939, 0.762, 0.619 : LB=0.852 (0.0248, 0.0312)

学習率のピーク高さを減衰させると、LBスコアが低下する、という傾向が認められた。

 

4月1日(木)

GPU : no time left

ようやく、val_dice_coefficientを表示することができるようになった。

 

4月2日(金)

GPU : no time left

CPUを使って途中まで計算した結果を眺めてみよう(16エポックで止めた)。

21エポックで3サイクルになるように設定した(1サイクルが7エポック)。

CyclicLR(SGD, lr : 0.1-2.0),loss_fn=BCE, EfficientNetB2-Unet

左から、エポック数、train_loss, val_loss, train_dice, val_dice, 計算時間(min)

  1│ 0.1181│ 0.0710│ 0.6080│ 0.7867│ 26.24
  2│ 0.0676│ 0.4731│ 0.7739│ 0.3525│ 26.23
  3│ 0.0789│ 0.0875│ 0.7445│ 0.8000│ 26.10
  4│ 0.0563│ 0.2749│ 0.8181│ 0.4594│ 25.97
  5│ 0.0479│ 0.0423│ 0.8418│ 0.8608│ 25.97
  6│ 0.0398│ 0.0418│ 0.8616│ 0.8545│ 25.82
  7│ 0.0356│ 0.0334│ 0.8742│ 0.8781│ 25.85
  8│ 0.0358│ 0.0420│ 0.8740│ 0.8638│ 26.04
  9│ 0.0434│ 0.0935│ 0.8593│ 0.7237│ 26.04
10│ 0.0495│ 0.0421│ 0.8410│ 0.8683│ 26.10
11│ 0.0426│ 0.0381│ 0.8549│ 0.8719│ 26.16
12│ 0.0361│ 0.0386│ 0.8747│ 0.8560│ 26.08
13│ 0.0339│ 0.0341│ 0.8789│ 0.8730│ 25.93
14│ 0.0317│ 0.0334│ 0.8867│ 0.8852│ 26.07
15│ 0.0313│ 0.0363│ 0.8873│ 0.8756│ 26.31
16│ 0.0362│ 0.0432│ 0.8784│ 0.8691│ 26.11

val_diceは、サイクルの最後のエポック数において、最大値を示している。

2サイクル目の最後のエポックでのスコアの方が高い。今は、3回繰り返した後で、commit, submitしている。

さて、3月28日のLB=0.877を超えるためにどうすれば良いか考えよう。

これまでにわかったこと(確実なことは1つもない、すべては相対的):

・train_dataは、当初の8件だけよりは、それに2件追加して10件にすることで良くなった。15件全部使ってみたいが、今のやり方では、メモリーの制限に引っかかり、1件も増やせない。解決策はある筈なので見つけたい。

・augmentationは、機械的変形を強くしすぎると逆効果になる。形状変化を伴わない適切な光学的処理がよさそうである。Cutoutも効いているようだが、要検討。

・LB=0.87+は、CyclicLR(SGD)のみであり、20または21エポックの3サイクルで、lrの範囲は0.1-2.0である。これがベストだとは思っていない。

・画像解像度は、1週間くらい前から検討しているが、overfitting状態である。

・EfficientNetB2-Unetを超えられないのがもどかしい。B3やB4、FPNやMAnet, Linknetなどを少し試したが、それぞれに、固有のチューニングが必要で、収拾がつかなくなりそうだと感じている。

・やっと計算できるようになったDaice coefficientとLBスコアとの関係が非常に気になる。discussionでは、CVとLBは近いとのこと。0.932:0.916, 0.94:0.932, 0.923:0.924などが報告されている。

・CyclicLRを使うことによって、LBスコアが0.87+になった。

・CyclicLRのサイクル数は、論文では3サイクル以上が良いと書かれていたが、3サイクル(v28 : LB=869 : 0.0267, 0.0314)から4サイクルに増やすとスコアが下がった(v36:LB=0.851 : 0.0255, 0.0306)ので、その時点(3月23日)でサイクル数を増やすのをやめた。最終エポックでのtrain_lossとval_lossは、4サイクルの方が小さいにも関わらずLBスコアが悪く、その原因が、4サイクル目の途中のlossの異常値(0.0282, 0.1107)にあると考えたためである。しかし、この判断は早計だったかもしれない。

 

4サイクルを止めた原因となった2つのデータを比較する。

LB=0.869のデータ:20エポックで3サイクルに設定しているので、1サイクルの最後、2サイクルの最後はサイクルの最終位置ではない。(バッチ単位(ステップ)でサイクルを決めているため)

  1│ 0.1264│ 0.0543│ 1.05
  2│ 0.0544│ 0.0593│ 1.03
  3│ 0.0446│ 0.0572│ 1.03
  4│ 0.0446│ 0.0766│ 1.03
  5│ 0.0481│ 0.0416│ 1.03
  6│ 0.0390│ 0.0375│ 1.03
  7│ 0.0329│ 0.0326│ 1.03
  8│ 0.0327│ 0.0378│ 1.03
  9│ 0.0337│ 0.0335│ 1.03
10│ 0.0343│ 0.0392│ 1.03
11│ 0.0339│ 0.0386│ 1.03
12│ 0.0306│ 0.0379│ 1.03
13│ 0.0289│ 0.0330│ 1.03
14│ 0.0270│ 0.0373│ 1.03
15│ 0.0274│ 0.0353│ 1.04
16│ 0.0278│ 0.0337│ 1.02
17│ 0.0324│ 0.0978│ 1.03
18│ 0.0348│ 0.0370│ 1.03
19│ 0.0282│ 0.0316│ 1.03
20│ 0.0267│ 0.0314│ 1.03

LB=0.851のデータ:これは、20エポックで4サイクルに設定しているので、最終サイクル位置は、5, 10, 15, 20エポックと一致する。

  1│ 0.1229│ 0.0606│ 1.10
  2│ 0.0552│ 0.0577│ 1.09
  3│ 0.0441│ 0.0484│ 1.07
  4│ 0.0399│ 0.0361│ 1.10
  5│ 0.0346│ 0.0315│ 1.12
  6│ 0.0319│ 0.0384│ 1.09
  7│ 0.0334│ 0.0421│ 1.07
  8│ 0.0351│ 0.0416│ 1.08
  9│ 0.0335│ 0.0314│ 1.11
10│ 0.0307│ 0.0314│ 1.09                           11│ 0.0281│ 0.0378│ 1.09
12│ 0.0294│ 0.0362│ 1.08
13│ 0.0315│ 0.0348│ 1.12
14│ 0.0297│ 0.0372│ 1.16
15│ 0.0268│ 0.0349│ 1.12
16│ 0.0258│ 0.0286│ 1.11
17│ 0.0282│ 0.1107│ 1.00
18│ 0.0323│ 0.0368│ 1.13
19│ 0.0277│ 0.0321│ 1.09
20│ 0.0255│ 0.0306│ 1.10

このときは、LBスコアが低かったことと、赤で示した0.1107から、4エポックはダメと判断したのだが、3サイクルの場合でも、赤で示したように 0.0978というのがある。
4サイクルにすることでlossは下がっているので、LBスコアが3サイクルより低くなった原因は他にあると考えて、サイクル数を増やしてみようと思う。
3サイクルと4サイクルを正しく比較するには、1サイクルあたりのエポック数を同じにしなければならない。1サイクルを何エポックにするかは最適化できていないのでこれも含めて実験する必要がある。1サイクルの中での学習率が最大になるピーク位置についても最適化が必要で、この2つの場合は1サイクルの中心だったが、ある時点(v43)から、OneCycleLRでのデフォルト0.3(3:7)になるようにステップ数(step_size_up, step_size_down)を設定している。

4月3日(土)

GPU : 36 h (5.14 h/day)

Run 1:LB=0.875の計算過程におけるDice coefficientの確認(20エポック3サイクル)と、3サイクル以降のtrainingの経過調査(40エポック6サイクルに設定し、40エポックでのDice_coeffが良ければcommit, submitする。

20エポックでのval_dice_coeff=0.888であった。これがLB=0.875だというのは納得できる値だと思う。40エポック目のval_dice_coeff=0.9011となったので、commit, submitしてみよう。その結果が良ければ、60エポック、あるいは80エポックを試してみよう。結果は、LB=0.799であった。こうなると、次の一手が分からなくなってしまう。 

勝手にCutoutを悪者扱いして、Cutoutの影響/効果を調べる。そのために、Cutoutの確率0.25と0.0を比較する。

Cutout=0.25:val_dice_coeff=0.895 : LB=0.862

Cutout=0.0   : val_dice_coeff=0.903 : LB=0.850

Cutoutは、overfittingを抑制する効果があったということかな。

それにしても、val_dice_coefficientがまったく役に立たないとは、困ったものだ。

明日は、手前のサイクル数で止めて、LBスコアを出してみよう。しかし、こんなことをしても、前の週からの進展は、殆ど期待できないような気がする。

 val_dice_coefficientを計算することで、条件検討が捗ると期待したが、これでは、なんにもならない。何の指標にもならないのか。それとも、プログラミングに問題があるのか。val_lossと同一の手順で、val_lossに併記しているので、validationの結果を計算できている筈なのだが。

今日は、最高に、期待外れの日だった。

最初に、20エポック3サイクルでLB=0.875となったモデルで、20エポックの先を見るために、40エポック6サイクルの計算を行い、40エポック後にcommit, submitしたら、LB=0.799となった。val_Diceは20エポックで0.888となり、40エポックでは0.9011まで上がったので、20エポックの0.888でLB=0.875だから、40エポックの0.901では、LB=0.885くらいになると予想していたのだが、LB=0.799という値となった。残念過ぎる結果だ。

20エポック3サイクルというのは、立ち上がりが2エポックで、立下りが6.666エポックである。これだと学習率の最小値や最大値がエポックの中間にあらわれるので、立ち上がりを2エポック、立下りを4エポックという組み合わせに変更した。これだと、6エポック単位でサイクルが繰り返されるので、たとえば、5,6サイクルくらいまで計算しておいて、良さそうなサイクル数を選んで再度計算するのに都合が良いと考えた。

ということで、30エポック5サイクルで計算し、augmentationを3段階に設定して計算した。LBスコアは、augmentationの弱い方から、0.850, 0.862, 0.847となった。これもまた平凡な値になった。これらのval_Diceは、0.903, 0.895, 0.896であった。

3サイクルで0.875のモデルを6サイクルまで訓練したら、性能がガタ落ちしたということである。overfittingだけでは片づけられない問題だと思う。

augmentationの条件を3段階変えた例では、0.852がoverfitting、0.862は、augmentationによる予測性能の向上もしくはoverfittingの抑制、0.847は過剰なaugmentationということか。何のことかわからないので、解決策がわからない。

不適切なaugmentationだが、結果としてoverfittingは抑制されてLBスコアが少し上がったが、augmentationを強めると不適切な操作であるため、予測性能は低下した、ということかもしれない。今回変更したのはCutoutで、1)使わない、2)10個の正方形で、各正方形の面積は画像の1%、確率は25%、3)同2%、確率25%。 1%や2%というのは、1次元だと小さく感じるが、2次元だと、ヒトの目には大きく感じるものである。

LB=0.875は、Cutout(25%)とAffine(12.5%)を含んでいる。3サイクルまでは、ある程度学習が進み、適度なaugmentationによって予測性能が向上した。そこからさらに3サイクルほど学習が進む間に、CutoutやAffineのような幾何学的な画像操作による変形を学習してしまい、スライスによる入力画像であるがゆえのアーティファクトに過剰に反応するようになった、ということは考えられないだろうか。

 

4月4日(日)

GPU : 28 h

今日は、5サイクル30エポック、(1)Cutout無し、(2)0.1、(3)0.1414、について検討する。30エポックまで訓練したモデルのLBスコアは、(1)0.850、(2)0.862、(3)0.847だった。これを過剰訓練により、汎用性が失われたと推測する。過剰訓練によるものかどうかを確認するには、5サイクルより前のサイクルで終了させたモデルの予測性能をLBスコアで評価すればよい。

最初に、(1)Cutout無しについて、1サイクル後(6エポック後)のモデルで予測した結果を調べることから始めてみよう。lossを見ると、計算してみようとか、調べてみようとか思わないのだが、val_Diceの値を見ると、本当にそうなのだろうかと気になる。

6エポックと12エポックの結果が出た。ひどいもんだ。train_lossとval_lossだけ見ているほうが、まだまし、と思ってしまう。

epochs | train_loss | val_loss | train_Dice | val_Dice |

  1│ 0.1260│ 0.0678│ 0.5811│ 0.8120│
  2│ 0.0554│ 0.0500│ 0.8127│ 0.8475│ 
  3│ 0.0444│ 0.0867│ 0.8497│ 0.7538│ 
  4│ 0.0438│ 0.0386│ 0.8480│ 0.8574│ 
  5│ 0.0387│ 0.0511│ 0.8690│ 0.7533│ 
  6│ 0.0376│ 0.0347│ 0.8624│ 0.8859│ LB=0.850

..........

12│ 0.0286│ 0.0332│ 0.8988│ 0.8953│ LB=0.855

..........

18│ 0.0272│ 0.0322│ 0.9032│ 0.8987│ LB=0.852

..........

24│ 0.0243│ 0.0322│ 0.9125│ 0.9031│ 

..........

30│ 0.0235│ 0.0398│ 0.9144│ 0.9026│ LB=0.850

(1)Cutout無し、はここまで。(2)Cutout : 0.1、の12エポックと18エポックを計算しておこう。その後で、Dice_coefficientの計算を見直す。

(2)Cutout : 0.1(LB=0.875のモデルとの違いは、1サイクルのエポック数で、LB=0.875のモデルは、3サイクルで20エポック、ここでは、3サイクルで18エポック。

12│ 0.0300│ 0.0363│ 0.8931│ 0.8842│ LB=0.859

..........

18│ 0.0286│ 0.0305│ 0.8987│ 0.8930│ LB=0.859

Cutout : 0.1を追加することによって、train_lossが少し大きくなっている。train_lossは、パラメータの変化に対して最も再現性の良い指標の1つである。これに対して、val_lossは、試料数が少ないことや偏りがあることなどのために、ばらつきが大きい。

LBスコアを上げることはできなかったが、サイクル数の最適化には必要な作業だろうと思う。

 

***Dice coefficientのコードを変更している間に、inferenceのコードもおかしくなってしまったので、LB=0.877(version52/87)のコードまでもどって再スタートする。Dice coefficientのことは、しばらく忘れよう。

 

この2日間で得たLBスコアは、0.799, 0.862, 0.850, 0.847, 0.832, 0.850, 0.855, 0.852, 0.859, 0.859の10件である。

4月3日から4月9日の計画は、lossとDice coefficientとLBスコアの相関を調べて整理することによって、次の進め方を考えることであった。

この2日間でわかったことは、Dice coefficientの計算に失敗しているらしいこと、Dice coefficientを指標にして決めたサイクル数の設定条件(特に、1サイクルあたりのエポック数:良い結果を得ていたのは3サイクル20エポックであり、これは、1サイクルあたりのエポック数が整数にならないので、今回は、3サイクル18エポック(5サイクル30エポック)の検討と、Dice coefficientの計算と、Dice coefficientを指標にして、サイクル数とLBスコアの関係を調べようとした。得られた結果は、

・Dice coefficientの計算ができるようにしたが、それらしい値は示しているものの、LBスコアとの対応は良くない。

・3サイクル20エポックと同等かそれ以上の性能を求めて、3サイクル18エポックすなわち1サイクル6エポックを検討したが、3サイクル20エポックに近い、3サイクル18エポックでのLBスコアは、0.859となり、3サイクル20エポックでのLBスコア0.875より0.016小さい値となった。5サイクル30エポックでのLBスコアが0.862であったことも加味すると、今の諸々の条件下では、1サイクル6エポックは、1サイクル6.67エポック(3サイクル20エポック)よりも小さいLBスコアになることが分かった。1サイクル6エポックはdownのサイクル数が少ないぶんだけ、学習量が少なく、lossの下がりが少なくなったのだろう。

 

明日以降の予定:

1.1サイクル7エポック(up: 2 epochs, down: 5 epochs)と8エポック(up: 2 epochs, down: 6 epochs)を試す。

2.Cutout条件と1サイクルのエポック数の見直し:320pixelの画像を使ったときに、LBスコアが、0.877と0.862になった。両者の違いは2か所ある。0.877は、3サイクル20エポック、かつ、cutoutが0.1x256を10か所。0.862は、3サイクル21エポック(1サイクル7エポック)、かつ、cutoutが0.1x320を10か所。0.1x256と0.1x320とでは、ホール面積は1.56倍異なる。

3.Cutout条件の見直し:多数ホールか単一ホールか、および、ホールのサイズ

これらをふまえつつ、512 pixelにフォーカスして検討してみようと思う。

 

4月5日(月)

HuBMAP: 1,374 teams, 36 days to go

今日は、CyclicLRの1サイクルから始めてみよう。

1サイクル7エポック (up: 2epochs, down: 5 epochs): LB=0.859 (0.0255, 0.0292)

このあたりのLBスコアはもう見たくない。1サイクルのエポック数を増やしてみよう。立ち上がりも2エポックから3エポックに増やしてみよう。立ち上がりを1サイクルの30%にするのがOneCycleLRのデフォルトであることからここでもその値を用いてみよう。そうすると、立ち上がりが3エポック、下りが7エポックとなる。

1サイクル10エポック (up: 3 epochs, down: 7 epochs): LB=0.880 (0.0235, 0.0287)

ようやく、0.877(320 pixel)を超えたが、256 pixelで0.875が得られているので、512 pixelで0.880というのは、まだ何も達成していないのと同じようなものだ。

2サイクル20エポックを試してみよう。

2サイクル20エポック (up: 3 epochs, down: 7 epochs) x 2 : LB=0.870 (0.0203, 0.277)

train_loss=0.0203となっているので、overfitting状態になっている。 

この状態から、Cutout条件を適正に設定することによって、LBスコアを0.89+にもっていくことができる筈だ。と思って取り組もう。カラーやノイズについても検討しよう。

 

4月6日(火)

***また、文字変換の途中でフリーズして、フォーマットがぐちゃぐちゃになった。復元ボタンを使っても、フォーマットがぐちゃぐちゃのままで、全く使えない。無意味な復元ボタン!これで、何回目かな。なにもなくても(何かあったのだろうが気付かなかったのだろう)次に文書を開くとフォーマットが崩れていることもあった。*** 

1時間半くらい、GPUの使用計画を立てていたのだが、消えてしまった。脳の中に少し残っているので書きとめておこう。

改善の対象とするデータ:2サイクル20エポック (up: 3 epochs, down: 7 epochs) x 2 : LB=0.870 (0.0203, 0.277)

検討課題1:Cutout:hole_numとホール面積の調整:ベース:hole_num=10, hole_size=1/10(hole_area=0.01):2サイクル20エポックに適用して、train_lossが0.022~0.023くらいで、LB=0.88+になること。

検討課題2:Cutoutのベース条件で、train_lossが0.018レベルになること。何サイクル何エポックが最適かわからない。512 pixelで条件探索するのは時間がかかる。256 pixelで基本を押さえておこう。

実験:512 pixel

1.3サイクル30エポックで、train_lossが0.02以下になるかどうかを確認する。

2.hole_num=1, hole_area=0.1(hole_size=0.316)で、2サイクル20エポック条件の計算を行う。

予備実験:256 pixel

512 pixel 3サイクル30エポックで所望の結果(train_lossが0.018以下になること)が得られるかどうかを、256 pixelで確認する。

256 pixelでは、1サイクル10エポックはなく、2サイクル20エポックはあるが、3:7ではなく5:5である。3サイクル30エポックは3:7であるが、0.1-2.0ではなく0.1-3.0である。lr=0.1-3.0では、train_lossが、10E:0.0281, 20E:0.0255, 29E:0.0243, 30E:0.0253となっていて、LB=0.857(trfm_1:弱)であった。256 pixeで参考になるデータが少ないので、1から始めることになる。計算時間は512 pixelの1/3くらいですむが、基本データをとって、条件検討をするにはそれなりの時間がかかる。

ということで、256 pixelによる予備実験はとりやめにする。

まずは、512 pixelで、3サイクル30エポックの結果を確認しようか。

2サイクルで30エポックはどうだろうか。upは3エポックのままで、downを7エポックから12エポックに増やし、15エポックで0.21くらいまで下がれば、2サイクル目で0.185くらいまで下がることを期待するか。

ということは、1サイクルで15エポックがどうなるか調べておく必要がある。

1サイクル15エポック(up:3, down:12) : (0.0208, 0.0291) : LB=0.867

train_lossが0.021より少し低い値になったので、2サイクルで0.0185くらいになることを期待してもよさそうだ。

384 pixelの画像に対して、hole_num=1, hole_area=0.1(hole_size=0.316)と、hole_num=10, hole_size=0.1 (hole_area=0.01)を用いた結果を既に出していて、どちらを適用してもLBスコアが変わらなかったので、hole_num=1の条件を強くして適用することにしよう。

論文ではhole_area=0.25で良好な結果が得られると書かれていたように思うので、これを真似てみることにして、1サイクル15エポックに適用してみた。

1サイクル15エポック& hole_num=1, hole_area=0.25(hole_size=0.5) : (0.0293, 0.0347) : LB=0.878

train_lossは、0.0208から0.0293まで大きくなった。これはダメかな、ホールの面積が大きすぎたので、もう少し小さくしようかなと考えていたのだが、LBスコアが出てみると、それほど悪くない。使えそうだ!これで学習量を増やしていけば、LB=0.89+になるかもしれない。

サイクルを繰り返してみた。(CyclicLRというのは同じ学習を繰り返すことだということを、今初めて実感している。)

2サイクル30エポック& hole_num=1, hole_area=0.25(hole_size=0.5) : (0.0271, 0.0406) : 

思っていたほど、15エポックより30エポックのtrain_lossのほうが小さくなっているので、学習を繰り返した効果があったということだが、30エポックのval_lossが大きいので、LBスコアは上がるのだろうかと、かなり心配しながら、commitしているところ。

結果は、LB=0.859でした。ホールサイズが大きすぎたようだ。次はホールサイズを少し小さくしようと思うが、GPUの残り時間が6時間半となったし、1サイクル10エポックシリーズも先に検討しておきたいことがあるので、この続きは今週の土曜日以降になる。

学習経過は、こんなふうになった。

  1│ 0.1490│ 0.1265│ 2.98
  2│ 0.0597│ 0.0598│ 2.93
  3│ 0.0500│ 0.0583│ 2.94
  4│ 0.0465│ 0.0400│ 2.95
  5│ 0.0451│ 0.0450│ 2.94
  6│ 0.0392│ 0.0489│ 2.95
  7│ 0.0383│ 0.0482│ 2.95
  8│ 0.0366│ 0.0410│ 2.94
  9│ 0.0358│ 0.0388│ 2.94
10│ 0.0329│ 0.0441│ 2.96
11│ 0.0336│ 0.0362│ 2.96
12│ 0.0314│ 0.0415│ 2.98
13│ 0.0311│ 0.0444│ 2.97
14│ 0.0308│ 0.0376│ 2.95
15│ 0.0293│ 0.0347│ 3.00 : LB=0.878
16│ 0.0289│ 0.0384│ 2.95
17│ 0.0320│ 0.0620│ 2.97
18│ 0.0354│ 0.0527│ 2.97
19│ 0.0360│ 0.0528│ 2.98
20│ 0.0339│ 0.0427│ 3.00
21│ 0.0319│ 0.0343│ 2.97                           22│ 0.0315│ 0.0400│ 2.96
23│ 0.0332│ 0.0341│ 2.97
24│ 0.0304│ 0.0471│ 2.98
25│ 0.0301│ 0.0453│ 2.97
26│ 0.0299│ 0.0352│ 2.98
27│ 0.0302│ 0.0404│ 2.96
28│ 0.0320│ 0.0360│ 2.98
29│ 0.0283│ 0.0365│ 2.96
30│ 0.0271│ 0.0406│ 2.95 : LB=0.859

この結果から、hole_area=0.25ではなく、0.20や0.15を使えば、15エポックでは、0.880を超えることができそうである。

1サイクル15エポックは、2サイクルに用いる繰り返しの単位としては、長すぎたようだ。最適な単位エポック数はいくらだろうか。6は短すぎたようだ。7は効果的だった。それ以上は検討できていない。間をとばして10と15を試みているというところだが、upとdownの比率は3:7が良好だが、upが2エポックの場合は1サイクルが7エポックくらい、upが3エポックの場合は1サイクルが10エポックとなる。upが4エポックの場合は、1サイクルが13エポックくらいとなる。上記の1サイクル15エポックはupが3サイクルなので、upとdownの比率は2:8となる。3:7は、OneCycleLRのデフォルトということであって、CyclicLRに対してのデフォルトではない。CyclicLRのデフォルトは5:5である。CuclicLRのupとdownの比率を5:5から3:7に変更したのは、CyclicLRのチューニング中にOneCycleLRのデフォルト設定値を思い出してちょっとやってみようと思っただけである。どのような比率がベストなのかはわからない。実験して探すしかない。元に戻ると、upが3エポックでdownが12エポックの1サイクル15エポックは、単独では悪いモデルではないのかもしれない。これをそのまま繰り返して2サイクル30エポックとして動作させた場合、今回は、良いモデルではなかったということ。

schedulerに限らず、気に入らなければ自分で書き換えれば良い。最初は既製品をデフォルトで使う。慣れてくると内部パラメータの適切な組み合わせを探すようになる。さらに進むと、既製品ではできないパラメータの組み合わせを試してみたくなる。このときに、元のコードに戻って、自分で書き換えて、既製品では不可能だった動作をさせることができるようになりたいものだ。

CyclicLRで使っていないパラメータがある。これら {triangular, triangular2, exp_range}の他に、自分で関数を入力することができる。

scale_fn (function) – Custom scaling policy defined by a single argument lambda function, where 0 <= scale_fn(x) <= 1 for all x >= 0. If specified, then ‘mode’ is ignored. Default: None

次のサイトに簡単な関数例が示されている。現状でも、まだできることがある。

https://github.com/keras-team/keras-contrib/blob/master/keras_contrib/callbacks/cyclical_learning_rate.py

clr_fn = lambda x: 0.5*(1+np.sin(x*np.pi/2.))

 

4月7日(水)

HuBMAP: 1,397 teams, 34 days to go : me:0.880 ---> top:0.939

GPU : 6.5 h

2サイクル20エポック(train_loss=0.0203 : LB=0.870)については、3サイクル30エポックに、hole_num=1, hole_area=0.25(hole_size=0.5)、を適用する。

この予定だったが、2サイクル30エポックのLBスコア向上に失敗したので、この1サイクル10エポック系は、2サイクル20エポックで0.880を超えることを目指す。

面積率とホールサイズ:0.1≒0.316x0.316 : 0.15≒0.387x0.387 : 0.2≒0.447x0.447

Base  : 1 cycle 10 epochs : num_hole=10, hole_size=0.1 x 0.1 : LB=0.880 (0.0235, 0.0287)

Run1 : 1 cycle 10 epochs : num_hole=1, hole_size=0.387 x 0.387 : LB=0.864 (0.0271, 0.0312)

この2つは直接比較できないが、Cutoutの全ホール面積が1.5倍になったことによってunderfittingの傾向になったと考える。次に、後者の条件でサイクルを繰り返すことによって、学習が進み、underfittingが解消されることによって、スコアが上がると予想した。train_loss, val_lossともに、小さくなっているので、予想通り、学習は進んだので、LBスコアは上がる筈だと思うのだが、上がるどころか、少し下がった。

Run2 : 2 cycle 20 epochs : num_hole=1, hole_size=0.387 x 0.387 : LB=0.860 (0.0243, 0.0305)

CutoutがLBスコアの向上に寄与しなかった理由として何が考えられるだろうか。使っているCutoutのパターンは、ホールが1個と10個の場合の2種類である。Cutoutのオリジナル論文は、ホールが1個、HuBMAPのコンペで自分が見た使用例は、ホールが10個、であり、そのことについてのコメントや注釈は見当たらなかった。ということから、これは、比較評価することが必要である。気になっていたのだが、1個のホールの方が良いと思い込んでいたかもしれない。ということで、予想通りのスコアが得られなかった原因として、Cutoutのパターンの選択が課題に対して適切でなかったということが考えられる。

 

1サイクル15エポック& hole_num=1, hole_area=0.25(hole_size=0.5) : (0.0293, 0.0347) : LB=0.878

この結果に対して、hole_area=0.20や0.15にすれば、LBスコアは0.880を超える筈だと書いた。これを確かめよう。

1サイクル15エポック& hole_num=1, hole_area=0.20(hole_size=0.447) : (0.0270, 0.0335) : LB=0.880

わずかに改善したが、超えられなかった。

上に書いたように(もっと前にも書いたような気がする)ホールが1個の場合と10個の場合の比較をする必要がある。もっといえば、適切なホールの個数と面積の組み合わせを探す必要がある。実験結果に基づかないことを根拠や指標にしていることがある(多い)ように思うので注意しよう。

 

予測性能を上げるために必要なことが何であるのか、正しく理解できていない。道具の使い方を間違っているのかもしれない。適用範囲を逸脱した領域で使っているのかもしれない。わからないことがたくさんある。何がわかっていないのかもわからない。

 

同一視野の画像の画素数による違い:EfficientNetB2-Unet, CyclicLR(SGD), lr=0.1-2.0, up 3 epochs, down 7 epochs, BCE, trfm_4

512 pixel

  1│ 0.1387│ 0.0823│ 3.16
  2│ 0.0468│ 0.0521│ 3.15
  3│ 0.0382│ 0.0438│ 3.14
  4│ 0.0372│ 0.0404│ 3.11
  5│ 0.0335│ 0.0374│ 3.11
  6│ 0.0297│ 0.0319│ 3.12
  7│ 0.0270│ 0.0352│ 3.12
  8│ 0.0259│ 0.0317│ 3.12
  9│ 0.0248│ 0.0291│ 3.14
10│ 0.0235│ 0.0287│ 3.12 : LB=0.880

256 pixel

  1│ 0.1422│ 0.0888│ 1.14
  2│ 0.0595│ 0.0557│ 1.13
  3│ 0.0519│ 0.1444│ 1.13
  4│ 0.0484│ 0.0510│ 1.12
  5│ 0.0414│ 0.0405│ 1.12
  6│ 0.0396│ 0.0499│ 1.12
  7│ 0.0372│ 0.0422│ 1.12
  8│ 0.0350│ 0.0343│ 1.12
  9│ 0.0315│ 0.0334│ 1.12
10│ 0.0308│ 0.0299│ 1.11 : LB=0.869

OneCycleLRを使っていた頃とはaugmentationの条件がかなり異なっている可能性が高いので、OneCycleLRについても再度計算(性能確認)する必要があるように思う。

 

4月8日(木)

HuBMAP:1,405 teams, 33 days to go

 256 pixel, batch_size=24, CyclicLR(SGD, 0.1-2.0, 3:7, 298:696), trfm_4, v50

  1│ 0.1572│ 0.0902│ 1.10
  2│ 0.0589│ 0.0579│ 1.08
  3│ 0.0475│ 0.0444│ 1.08
  4│ 0.0437│ 0.0397│ 1.08
  5│ 0.0393│ 0.0433│ 1.08
  6│ 0.0368│ 0.0418│ 1.08
  7│ 0.0344│ 0.0319│ 1.08
  8│ 0.0331│ 0.0322│ 1.08
  9│ 0.0302│ 0.0309│ 1.08
10│ 0.0295│ 0.0311│ 1.08                           11│ 0.0290│ 0.0339│ 1.08
12│ 0.0295│ 0.0361│ 1.08
13│ 0.0309│ 0.0383│ 1.07
14│ 0.0327│ 0.0366│ 1.07
15│ 0.0318│ 0.0392│ 1.07
16│ 0.0303│ 0.0320│ 1.08
17│ 0.0298│ 0.0370│ 1.08
18│ 0.0285│ 0.0318│ 1.08
19│ 0.0269│ 0.0344│ 1.08
20│ 0.0255│ 0.0333│ 1.08 : LB=0.867

3サイクル20エポックで、batch_size=16-->24を検討するつもりだったが、ステップサイズを変更するのを忘れたために、当初の実験目的を外れたもの。

上に示した256 pixelの条件とbatch_size以外は全く同じで、1サイクル10エポック、を繰り返したものになっている。

1サイクル10エポックでは、バッチサイズを16から24に増やすことで、学習は少し効率よく進んだようにみえる。LBスコアへの影響を後日調べてみる。

 

EarlyStopping

迷路に迷い込んでいるような気がしている。学習率は低減させていくのが常識だったし、train_lossとval_lossの動きを見ていて、両者が大きく乖離しないことと、val_lossが反転しない事の両方を満たすところで学習を停止するのが常識だったと思うのだが、OneCycleLRやCyclicLRはこれまで見たことが無いような変化をするので、惑わされてしまったように思う。OneCycleLRもCyclicLRも、設定しておいた最終エポックで良好な結果が得られるものだと思い込んでしまっていたように思う。

その流れで、512 pixel、20エポック3サイクル、学習率0.1-2.0で20エポックでのtrain_lossが0.0202になったのを見て、ここから、augmentationの条件を適切に調整することができさえすれば、スコアは上がっていくものと思い込んでしまった。

このことに気がついたからと言って、スコアが上がるわけではない。地道に、トライアンドエラー、系統的な実験、先人に学ぶ、ということを繰り返していく・・・。

EarlyStoppingの考え方に近い/関連性があるのは、サイクル数の繰り返しを減らす、最終エポック数を設定エポック数の少し手前に設定する、トータルエポック数を減らす、小さなモデル(今使っているモデルだと小さなEncoder)を用いる、画像の解像度を下げる、などであるから、結局はそれらの間のバランスをとりながら、LBスコアが最大になる組み合わせを探索し、アンサンブルやTTAを適切に組み合わせるということになるのだろう。言うは易し、行うは難し・・・。

 

LB=0.925のモデル(inferenceはコンペサイトのnotebook(code)、trainはGitHub)が公開されているので、そのあたりのスコアを望むだけなら真似すれば良いだけだが(とはいえ、口で言うほど簡単ではないが)、自分としては、今のやり方でスコアアップを目指すことが自分には必要だと思っている。すでに、その公開コードからエッセンスの一部を拝借しながら進めているので大きなことは言えないが、それでも、直接真似るのと、実験しながら進めるのとでは大きな違いがあると思っている。

 

4月10日(土)から4月15日(木)までの6日間の実験計画を立てよう!

目を閉じると、シナプスが活動しているのが見えるような気がする。そろそろ、良いアイデアが浮かんでもよさそうなものだが、浮かんでこないということは、努力が足りていないということなのだろう。集中力が足りないのか。深く考えるために必要な演算能力と記憶能力が不足しているのだろうか。記憶したものが片っ端から消去されているような気がする。

明日は、4月15日までに、LB=0.880 ---> 0.900、となることを目指して、実験計画を立てよう。0.900 - 0.880 = 0.001 x 20 : 0.001の改善を20回繰り返せば0.900に到達!

*overfitting条件にしてから、augmentationを強めてjust-fittingにもっていこうとしていて条件探しの途中だが、min_lrとmax_lrの組み合わせ、1サイクルのエポック数、サイクル数、up_epochとdown_epochの組み合わせ、encoder、バッチサイズ、画素数なども並行して検討する必要がある。

限られた時間と資源の中で、効果的に実験する方法を考えよう。

教師データの量と質:バッチサイズ:モデル:損失関数:最適化関数:学習率スケジューラー

OneCycleLRから始めて、CyclicLRに移り、サイクル数や最大学習率、upとdownの比率などパラメータが多くて最適化はうまくいかず、最近の512 pixelの画像を使った実験では、CyclicLRを使いながら、11回の計算のうち、3回が2サイクルで8回は1サイクルであった。こうなった理由は、256 pixelの画像を使った場合よりも収束が速く、サイクルを繰り返すことによってlossを小さくする必要がなかったことにある。2回目に試みた1サイクル10エポックでLB=0.880になったが、それ以降はこれを超えることができていない。GPUを使った最後のテストは、512 pixelで0.88を得た1サイクル10エポックを256 pixelに適用することで、LB=0.869となった。1サイクルで十分ならば、OnsCycleLRがある。明日からのGPUマシンタイムは、OneCycleLRに集中するか1サイクルとの併用で進めようと思う。

バッチサイズの影響は、条件設定ミスで調べることができなかったので、優先度を上げて実施する。

Cutoutは、10ホールと1ホールの比較が不十分で、その中間状態(3ホールとか5ホール)については、全く調べることができていない。

256 pixelと512 pixelの間(320, 384, 448 pixelなど)はCutoutの条件設定ミスを含んだ結果があるだけで、0.877となった320 pixelすら、フォローできていない。これら中間的な解像度で良い結果が得られる可能性は高いと思うので、順に結果を出していく。

Encoderは、EfficientNetB2に固定して進めているが、underfittingになればB3やB4を試し、overfittingになればB1やB0を試すなど、最適な組み合わせを探っていく。

Decoderは、2月頃はFPNが良さそうであったが、3月末に調べたときには、FPNは収束が悪く、LinknetとMAnetは使えそうであったが、Unetに優るような感じはしなかった。

損失関数は、BCEから始めてDiceに落ち着きそうだったが、ある条件でDiceLossでは収束が遅くなったときにBCEで良好な結果が得られてからBCE単独で進めている。他の損失関数との組み合わせもどこかのタイミングで試してみたいと思っている。今回のマシンタイムに組み込もう。

 

4月10日(土)の予定

最初に、OneCycleLRの10エポックと20エポックの性能を評価し、CyclicLRで得られている結果と比較する。

Run1&2 : 256 pixelのスコアLB=0.875を、OneCycleLR(Adam)とOneCycleLR(SGD)で再現できるかどうかを調べる。CyclicLR(SGD)は3サイクル20エポックなので、20エポックで試す。

Run3&4 : 256 pixelのスコアLB=0.869を、OneCycleLR(Adam)とOneCycleLR(SGD)で再現できるかどうかを調べる。CyclicLR(SGD)は1サイクル10エポックなので、10エポックで試す。

 

4月11日(日)の予定:

256 pixelの画像を用いて、3サイクル20エポックで、LB=0.875を得ていたモデルとパラメータを用いて、1サイクルnエポックで同等のLBスコアが得られる条件を見出す。

CyclicLRによる1サイクル10エポックでは、LB=0.869が得られているので、エポック数を増やすだけで0.875に近い値が得られるのではないかと思っている。このときに、4月10日に検討したOneCycleLRの方が優れていれば、OneCycleLRを用いて、エポック数の最適な値を探すことになる。

 

4月12日(月)の予定:

Cutoutの1ホールと3ホールと10ホールのトータル面積を等しくしてその効果の比較を行う。面積率は0.10とする。

 

4月13日(火)の予定:

バッチ数の効果を調べる。これまでの16をベースとして、24と32について調べる。

 

4月14日(水)の予定:

素数の効果を調べる。256, 320, 384, 448, 512とし、256 pixelでの最大スコアが得られている条件をベースとして、画素数が増えるごとに、1エポックづつエポック数を減らして計算する。

たとえば、512 pixelでは、10エポックで0.880が得られているので、これをスタートラインとするならば、448 pixelでは11エポック、384 pixelでは12エポック、320 pixelでは13エポック、256 pixelでは14エポック、のように画像解像度が低いほどエポック数を増やす。

ただし、512 pixelありきで考えるよりは、256 pixelの方から積み上げていく方が、最終スコアは上がるのではないかと思っている。 

 

4月15日(木)の予定:

損失関数の検討。upとdownの割合、max_lr、

 

<CPUを用いた予備検討>

CPUを用い、同一条件で、ResNet34と EfficientNetB0の学習結果を比較してみる。

model = smp.Unet('resnet34', encoder_weights='imagenet', classes=1), batch_size=16, CyclicLR(SGD), lr=0.1-2.0, up=3 epochs, down=7 epochs

 

  1│ 0.1392│ 0.1253│ 25.53
  2│ 0.0850│ 0.5311│ 25.57
  3│ 0.1121│ 0.0932│ 25.49
  4│ 0.0781│ 0.0548│ 25.21
  5│ 0.0607│ 0.0632│ 25.28
  6│ 0.0568│ 0.0721│ 25.40
  7│ 0.0501│ 0.0496│ 25.34
  8│ 0.0479│ 0.0408│ 25.31
  9│ 0.0426│ 0.0397│ 25.25
10│ 0.0405│ 0.0372│ 25.37

model = smp.Unet('efficientnet-b0', encoder_weights='imagenet', classes=1), CyclicLR(SGD), lr=0.1-2.0, batch_size=16, up=3 epochs, down=7 epochs
  1│ 0.1533│ 0.1128│ 20.32                                                                                                      2│ 0.0657│ 0.3048│ 20.44
  3│ 0.0649│ 0.0624│ 20.31
  4│ 0.0587│ 0.0810│ 20.21
  5│ 0.0490│ 0.0474│ 20.67
  6│ 0.0444│ 0.0669│ 20.60
  7│ 0.0430│ 0.0385│ 20.67
  8│ 0.0381│ 0.0359│ 20.63
  9│ 0.0358│ 0.0337│ 20.44
10│ 0.0333│ 0.0360│ 20.44

以上の2つの結果は、resnet34とEfficientNetB0との違いを示すものである。これらのモデルで予測した結果がどうなるかまでは調べていないので安易な評価をしてはいけないかもしれないが、train_loss, val\lossともにEfficientNetB0の方が小さくなっているので、後者の方が速く収束しているとみてもよいのだろうと思う。

次に、model = smp.Unet('efficientnet-b0', encoder_weights='imagenet', classes=1)に対して、OneCycleLR(SGD, max_lr=2, epochs=10)を適用してみる。

  1│ 0.1547│ 0.0819│ 21.97
  2│ 0.0648│ 0.6287│ 21.18
  3│ 0.0905│ 0.1146│ 21.12
  4│ 0.0600│ 0.0506│ 21.11
  5│ 0.0493│ 0.0416│ 21.15
  6│ 0.0458│ 0.0681│ 21.23
  7│ 0.0418│ 0.0395│ 21.25
  8│ 0.0388│ 0.0364│ 21.19
  9│ 0.0355│ 0.0330│ 21.24
10│ 0.0334│ 0.0337│ 21.48

CyclicLRの1サイクルとOneCycleLRとで見た目には大差ない結果となっているように見える。同じ最適化関数とmax_lrを用いているが、異なるところは、学習率lrの変化が、CyclicLRが三角波であるのに対して、OneCycleLRはコサイン関数(三角波を選ぶこともできる)となっていることである。さらに、OneCycleLRでは、学習率lrの初期値と最終値を別々に指定することができる。デフォルトではlrの最終値はlrの初期値(デフォルトではmax_lrの1/25)より4桁小さい。

GPUが使えるようになれば、予測結果をsubmitしてLBスコアを比較する予定である。

最後に、model = smp.Unet('efficientnet-b0', encoder_weights='imagenet', classes=1)に対して、AdamWを最適化関数に用いた、OneCycleLR(AdamW, max_lr=1e-3, epochs=10)を試してみよう。これは、CyclicLRを使う前に、1か月以上使っていたものである。

  1│ 0.4280│ 0.1768│ 24.61
  2│ 0.1092│ 0.0692│ 24.71
  3│ 0.0607│ 0.0490│ 25.10
  4│ 0.0479│ 0.0434│ 25.04
  5│ 0.0439│ 0.0369│ 24.94
  6│ 0.0403│ 0.0374│ 24.98
  7│ 0.0371│ 0.0331│ 25.30
  8│ 0.0348│ 0.0341│ 25.21
  9│ 0.0327│ 0.0351│ 25.25
10│ 0.0320│ 0.0341│ 25.23

id predicted
0 aa05346ff 15325 185 46045 185 76764 187 107482 189 13820...
1 2ec3f1bb9 60690327 20 60714307 41 60738287 56 60762271 6...
2 3589adb90 68511797 12 68541223 56 68570651 65 68600081 7...
3 d488c759a 62493304 1 62539963 4 62586623 5 62633282 6 62...
4 57512b7f1 217865569 2 217898801 14 217898952 3 217932039...

 

4月10日(土)~4月16日(金)

GPU : 40 h

目標:LB=0.880 ---> LB=0.900

手段:256 pixelのLB=0.875から、augmentation, batch_size, loss_fn, optimizer, learning_scheduler, pixel_size, encoder-decoder,などの最適化

 

4月10日(土)

GPU : 40 h

HuBMAP:1,421 teams, 31 days to go : top LB score 0.943

今日は、OneCycleLRを用いて、256 pixelの自己記録LB=0.875を超えることを目指す。

CyclicLR(SGD, 0.1-2.0)の、1サイクル10エポックでLB=0.869、3サイクル20エポックでLB=0.875が得られている。

さて、このような状況で、本日最初の一手をどう指せばよいだろうか。これまでの248回のsubmissionデータを完璧な機械学習モデルに入力し学習させたら、どのような出力が出てくるだろうか。「入力データの質が悪すぎて予測不可能」と出力されそう。

5月10日のLBスコアの予測なら、簡単に答えが出てきそうだ。そんなのは、方眼紙にプロットすればすぐ出てくる。

2月22日(月):0.836(EfficientNetB5-Unet,scse, DiceLoss, OneCycleLR(Adam, max_lr=1e-3, 30 epochs)

3月20日(土):0.850(EfficientNetB2-Unet, BCELoss, OneCycleLR(Adam, max_lr=1e-3, 20 epochs, : train_loss= 0.0219, val_loss=0.0356):更新データ8件使用

同日:0.865 更新データ10件使用(データ更新によりtrain_dataは8件から15件に増えたがメモリーの制約により今と同じ10件のみ使用): (train_loss=0.0238, val_loss=0.0311)

3月24日(水):0.875, Cutout

3月28日(日):0.877, 320 pixel

4月5日(月):0.880, 512 pixel

5月10日(月):0.95???:妄想による予測結果

 

OneCycleLR

Adam or SGD

max_lr:Adamなら1e-3, SGDなら2.0

up/(up+down):デフォルトは0.3

エポック数 10CyclicLRの1サイクル10エポック(v12)との比較、及び、baselineとして

その他:encoder-decoder, pixel_size, batch_size, loss_fn, augmentation,

b2-Unet, 256 pixel, batch_size=16, BCELoss, Cutout 10, 10%

 

1サイクル10エポックLB=0.869のlossは、(0.0308, 0.0299)であったのに対し、OneCycleLR(SGD, max_lr=2.0, epochs=10)では、(0.0310, 0.0315)であった。

結果:LB=0.866(-0.003となった):0.869より少し低いが、CyclicLR(SGD)との比較ということでは、これで十分である。

次のターゲットは、3サイクル20エポックで得られたLB=0.875である。これには、エポック数を増やすことが必要だが、どこから始めようか。

それとも、エポック数10で、バッチ数の効果を先に調べておくか。

batch_size=16 : train_loss=0.0310, val_loss=0.0315 : LB=0.866

batch_size=24 : train_loss=0.0283, val_loss=0.0302 : LB=0.868

batch_size=32 : train_loss=0.0311, val_loss=0.0328 : commit, submitせず

batch_sizeを24に増やすと収束が良くなり、LBスコアも少し上がったが、32に増やすと収束が悪くなった。batch_sizeを大きくすると、GPUモリー占有量が増えて、大きな画素数の画像が扱えなくなることも加味して、batch_sizeは、16を継続する。

さて、エポック数をどうしようか。

15エポックと20エポックは、計算しておく必要がありそうだ。

OneCycleLR(SGD)のエポック数を増やすだけではなく、max_lrやupとdownの比率などの調整が必要になるかもしれない。

次の2つの場合について実験する:まずは、LB=0.869を越えないと。

OneCycleLR(SGD, max_lr=2.0, epochs=15) : loss (0.0273, 0.0308) : LB=0.858

予測結果が、これまでと違っていて心配だ。LB=0.858となり、悪い予想が的中した。

10エポックの結果はこうなっていた。

OneCycleLR(SGD, max_lr=2.0, epochs=10, pct_start=0.3) : loss (0.0310, 0.0315) : LB=0.866

train_lossとval_lossの値は、常識的には、15エポックの方が、かなり優れている。にもかかわらず、10エポックよりもLBスコアが悪い。

こうなると、次に20エポックを試してもだめだろう。

15エポックの方が悪くなる原因として考えられるのは、15エポックの中身で、upが3割だから、内部では、upが4.5エポック、downが10.5エポックとなっている。10エポックのときは、upが3エポックで、downが7エポックである。10エポックと15エポックの本質的な違いは、学習率の変化率である、10エポックの場合は、3エポックで最大学習率2.0に達するが、15エポックでは、4.5エポックで最大学習率に達する。この違いが、Super-Convergenceの効果に大きな違いを生じている可能性がある。

とりあえず、15エポックの場合に、3エポックで学習率が最大になるように、pct_startを0.3から0.2に変更してみる。そうするとupが3エポックで、downが12エポックとなる。

OneCycleLRも、CyclicLRも性能を発揮するための条件は、Super-Convergenceなので、それを満たす条件から外れると、通常の学習率の制御方法を用いた場合よりも、非常に悪い結果しか得られないということになるのではないかと思う。

ということは、train_lossとval_lossの組み合わせに対して、従来の常識に従って判断すると、良い結果を見逃し、悪い結果に固執してしまうことにもなりかねない。

自分が落ち込んでいた罠の正体の何割かは、これによるものではないだろうか。

非常に気になったので、"super convergence deep learning" で検索したら、Super-Convergenceを理論的に解明した(らしい)論文があった。

Super-Convergence with an Unstable Learning Rate
Samet Oymak∗, arXiv:2102.10734v1 [cs.LG] 22 Feb 2021
Abstract
Conventional wisdom dictates that learning rate should be in the stable regime so that gradient-based algorithms don’t blow up. This note introduces a simple scenario where an unstable learning rate scheme leads to a super fast convergence, with the convergence rate depending only logarithmically on the condition number of the problem. Our scheme uses a Cyclical Learning Rate where we periodically take one large unstable step and several small stable steps to compensate for the instability. These findings also help explain the empirical observations of [Smith and Topin, 2019] where they claim CLR with a large maximum learning rate leads to “super-convergence”. We prove that our scheme excels in the problems where Hessian exhibits a bimodal spectrum and the eigenvalues can be grouped into two clusters (small and large). The unstable step is the key to enabling fast convergence over the small eigen-spectrum.

 従来の通念では、勾配ベースのアルゴリズムが爆発しないように、学習率は安定した状態にある必要があります。 このノートでは、不安定な学習率スキームが超高速収束につながる単純なシナリオを紹介します。収束率は、問題の条件数に対数的にのみ依存します。 私たちのスキームは、不安定性を補うために、定期的に1つの大きな不安定なステップといくつかの小さな安定したステップを実行する循環学習率を使用します。 これらの調査結果は、[Smith and Topin、2019]の経験的観察を説明するのにも役立ちます。ここでは、最大学習率が高いCLRが「超収束」につながると主張しています。 私たちのスキームは、ヘッセ行列が二峰性スペクトルを示し、固有値を2つのクラスター(小さいものと大きいもの)にグループ化できる問題に優れていることを証明します。 不安定なステップは、小さな固有スペクトルでの高速収束を可能にするための鍵です。(Google翻訳

原理がわかっても、答えはわからない。個々の条件/状況によって実際に適用するときの数値は異なる。 

15エポックの場合に、3エポックで学習率が最大になるように、pct_startを0.3から0.2に変更してみた。(upが3エポックで、downが12エポック)

OneCycleLR(SGD, max_lr=2.0, epochs=15, pct_start=0.2) : loss (0.0272, 0.0320) : LB=0.865

最後に、12エポックを試しておく。pct_start=0.25としておけば、3エポックで学習率が最大になる。

OneCycleLR(SGD, max_lr=2.0, epochs=12, pct_start=0.25) : loss (0.0289, 0.0325) : LB=0.866

 

得られた結果を並べてみよう。3エポックまでは、全く同じであることがわかる。

OneCycleLR(SGD, max_lr=2., epochs=10, pct_start=0.3) : loss (0.0310, 0.0315) : LB=0.866

  1│ 0.1526│ 0.2327│ 1.12
  2│ 0.0623│ 0.0656│ 1.10
  3│ 0.0517│ 0.0459│ 1.11
  4│ 0.0494│ 0.0451│ 1.10
  5│ 0.0426│ 0.0481│ 1.10
  6│ 0.0418│ 0.0406│ 1.11
  7│ 0.0369│ 0.0373│ 1.11
  8│ 0.0359│ 0.0387│ 1.10
  9│ 0.0320│ 0.0339│ 1.11
10│ 0.0310│ 0.0315│ 1.10

OneCycleLR(SGD, max_lr=2., epochs=12, pct_start=0.25) : loss (0.0289, 0.0325) : LB=0.866

  1│ 0.1526│ 0.2327│ 1.14
  2│ 0.0623│ 0.0656│ 1.11
  3│ 0.0517│ 0.0459│ 1.12
  4│ 0.0494│ 0.0452│ 1.12
  5│ 0.0430│ 0.0498│ 1.13
  6│ 0.0417│ 0.0451│ 1.12
  7│ 0.0374│ 0.0395│ 1.12
  8│ 0.0373│ 0.0427│ 1.12
  9│ 0.0334│ 0.0355│ 1.12
10│ 0.0320│ 0.0330│ 1.12
11│ 0.0299│ 0.0332│ 1.11
12│ 0.0289│ 0.0325│ 1.11

OneCycleLR(SGD, max_lr=2., epochs=15, pct_start=0.2) : loss (0.0272, 0.0320) : LB=0.865

  1│ 0.1526│ 0.2327│ 1.13
  2│ 0.0623│ 0.0656│ 1.11
  3│ 0.0517│ 0.0459│ 1.11
  4│ 0.0495│ 0.0452│ 1.11
  5│ 0.0430│ 0.0549│ 1.10
  6│ 0.0426│ 0.0649│ 1.11
  7│ 0.0397│ 0.0424│ 1.12
  8│ 0.0386│ 0.0389│ 1.12
  9│ 0.0345│ 0.0354│ 1.11
10│ 0.0330│ 0.0335│ 1.11
11│ 0.0315│ 0.0330│ 1.12
12│ 0.0301│ 0.0373│ 1.11
13│ 0.0284│ 0.0322│ 1.11
14│ 0.0275│ 0.0352│ 1.11
15│ 0.0272│ 0.0320│ 1.10

 

明日は、3エポックで学習率が最大になる条件で、最大学習率を極限まで上げてみよう。CyclicLRを使っているときに、最大学習率が大きすぎると、lossが一旦大きくなって下がった後、収束しなくなるので、そうならない最大の値に近い値に設定して、LBスコアがどうなるかを調べてみよう。

256 pixelでLB=0.875となった、3サイクル20エポックの場合は、2エポックで最大学習率が2.0となる条件であった。

512 pixelでLB=0.880となった1サイクル10エポックの場合は、3エポックで最大学習率が2.0となる条件であった。

素数によってもSuper-Convergenceの条件は異なるかもしれない。EfficientNetのB番号によってもSC条件は異なるかもしれない。FPNが収束しなかったのもSC条件が合わなかったことによるのかもしれない。

 

4月11日(日)

HuBMAP:1,429 teams, 30 days to go

エポック数10、12、15、は、それぞれ、up=3, down=7、up=3, down=9、up=3, down=12であった。これらの立ち上がりエポック数を2にして、立下りをup=3の場合と同じにすると、それぞれ、up=2, down=7、up=2, down=9、up=2, down=12となる。そうするためには、若干誤差を含むが、pct_startを、それぞれ、0.222、0.182、0.143とすればよい。以下の3通りの条件で計算してみよう。

OneCycleLR(SGD, max_lr=2., epochs=9, pct_start=0.222) : LB=0.861

OneCycleLR(SGD, max_lr=2., epochs=11, pct_start=0.182)

OneCycleLR(SGD, max_lr=2., epochs=14, pct_start=0.143

 

OneCycleLRは、デフォルトのコサインモード(anneal_strategy='cos')にしているが、CyclicLRは、"triangle"すなわち線形モード(anneal_strategy='linear')であった。

OneCycleLRでは、両方のモードが使えるので、その比較をやってみよう。

OneCycleLR(SGD, max_lr=2., epochs=9, pct_start=0.222, anneal_strategy='linear') : LB=0.863

OneCycleLR(SGD, max_lr=3., epochs=9, pct_start=0.222, anneal_strategy='linear') : 

max_lr=3と大きくしたことで、lossは(0.0608, 0.0494)までしか下がらなかったので、エポック数を増やしてみる。CyclicLRなら、downのエポックを増やすか、もう1サイクル追加すればlossはさらに下がるのだが、OneCycleLRでは、downのエポック数を増やすという選択肢だけである。

OneCycleLR(SGD, max_lr=3., epochs=14, pct_start=0.143, anneal_strategy='linear') :

2エポック目まで同じ条件のはずだが、2エポック目でlossが数桁大きくなり、収束しなくなった。

OneCycleLR(SGD, max_lr=3., epochs=14, pct_start=0.214, anneal_strategy='linear')

upを3エポックにしてみた。こういうことをしていると、もう、OneCycleLRを使う意味がないという気がしてきた。LB=0.856だった。コサインアニールに戻そう。

OneCycleLR(SGD, max_lr=3., epochs=14, pct_start=0.214, anneal_strategy='cos')

LB=0.857だった。低空飛行が続いています。

比較のために、1か月前のOneCycleLRのデフォルトを試して、OneCycleLRを終わろう。

OneCycleLR(AdamW, max_lr=1e-3., epochs=14) : LB=0.867

 

0.85+~0.86+をウロウロしていただけ!

OneCycleLRよりもCyclicLRの方が良さそうだ。上にも示した、次の論文に従って、CyclicLRに戻ろうか。 

Super-Convergence with an Unstable Learning Rate
Samet Oymak∗, arXiv:2102.10734v1 [cs.LG] 22 Feb 2021

Our scheme uses a Cyclical Learning Rate where we periodically take one large unstable step and several small stable steps to compensate for the instability.

one large unstable step and several small stable steps

 

256 pixel, 1サイクル10エポック、0.1-2.0, up=3, down=7, trfm_4, B2-Unet

  1│ 0.1422│ 0.0888│ 1.15
  2│ 0.0595│ 0.0557│ 1.13
  3│ 0.0519│ 0.1444│ 1.14
  4│ 0.0484│ 0.0510│ 1.12
  5│ 0.0414│ 0.0405│ 1.13
  6│ 0.0396│ 0.0499│ 1.13
  7│ 0.0372│ 0.0422│ 1.13
  8│ 0.0350│ 0.0343│ 1.13
  9│ 0.0315│ 0.0334│ 1.13
10│ 0.0308│ 0.0299│ 1.12 : LB=0.869

このデータもって、CyclicLRの再出発としよう。256 pixelで、LB=0.875を超えよう。

 

4月12日(月)

HuBMAP:1,435 teams, 29 days to go

SGDはCyclicLRではnesterov=Trueとして用いてきたが、これをFalseにしてみた。結果は、LB=0.867(v1)であった。Trueの場合のLB=0.869と大差なかった。以後もnesterov=Trueにしておこう。

max_lrを0.25に上げてみよう。結果は、LB=0.856(v2)となった。upが3エポック、downが7エポックで、変えていないのだが、lossがいったん下がってから上昇したときの最大値が4エポック目で、5エポック目もlossの下がりが少なく、最後の10エポックでもlossは十分下がらなかった(0.322, 0.314)。

次は、max_lrを1.5に下げてみよう。結果は、LB=0.865(v3)となった(0.0302, 0.0309)。

極大は、max_lr=2.0と1.5の間のようだ。

max_lrをどこまで上げられるかを調べることの方が重要なので、max_lr=3.0にしてみよう。3エポック目にval_lossが0.4を超えたが、10エポック目には(0.0315, 0.0302)となり、LB=0.867(v4)となった。max_lr=2.5でLB=0.856となったので駄目だろうなと思ったのだが、予想外の結果となった。

  1│ 0.1369│ 0.0986│ 0.5470│ 0.7288│ 1.14
  2│ 0.0601│ 0.0554│ 0.7964│ 0.8399│ 1.12
  3│ 0.0509│ 0.4107│ 0.8279│ 0.3687│ 1.13
  4│ 0.0524│ 0.0696│ 0.8217│ 0.7586│ 1.12
  5│ 0.0458│ 0.0572│ 0.8440│ 0.8088│ 1.12
  6│ 0.0449│ 0.0463│ 0.8454│ 0.8219│ 1.12
  7│ 0.0400│ 0.0525│ 0.8623│ 0.8356│ 1.11
  8│ 0.0355│ 0.0347│ 0.8782│ 0.8721│ 1.12
  9│ 0.0330│ 0.0346│ 0.8840│ 0.8820│ 1.12
10│ 0.0315│ 0.0302│ 0.8907│ 0.8912│ 1.12 : LB=0.867(v4)

それではと、max_lyを5.0にしてみとところ、val_lossが1.0を超え、train_lossもval_lossも0.28くらいから下がらなくなった。これはダメのようだ。

max_lr=4.0を試すことにした。val\lossが3エポック目に0.5以上、5エポック目に0.3以上となってどうなることかと思ったが、10エポック目には(0.0316, 0.0311)となり、LB=0.861(v5)となった。

  1│ 0.1368│ 0.0944│ 0.5481│ 0.7352│ 1.14
  2│ 0.0648│ 0.1624│ 0.7842│ 0.6723│ 1.12
  3│ 0.0546│ 0.5056│ 0.8184│ 0.5011│ 1.13
  4│ 0.0689│ 0.0789│ 0.7807│ 0.7328│ 1.12
  5│ 0.0472│ 0.3160│ 0.8415│ 0.2536│ 1.12
  6│ 0.0480│ 0.0460│ 0.8402│ 0.8534│ 1.12
  7│ 0.0388│ 0.0496│ 0.8685│ 0.8269│ 1.12
  8│ 0.0361│ 0.0363│ 0.8769│ 0.8597│ 1.13
  9│ 0.0337│ 0.0353│ 0.8818│ 0.8803│ 1.12
10│ 0.0316│ 0.0311│ 0.8897│ 0.8883│ 1.11 : LB=0.861(v5)

今日はここまで。

 

次はこれらを2サイクルにしてみよう。2サイクルにしてoverfittingするようであれば、downのエポック数を減らしてみよう。(up:3, down:7) x 2 --> (up:3, down:6) x 2 --> (up:3, down:5) x 2  

その前に、1エポックのままで、かつ、upは3エポックのままで、downを増やしてみようと思う。最終エポックの10エポック目で、train_lossよりもval_lossが小さいからであるが、downを1エポック増やしてみたら、val_lossの方が大きくなった。

 

*一向に進まない。次の論文を読もう。

GRADIENT DESCENT ON NEURAL NETWORKS TYPICALLY OCCURS AT THE EDGE OF STABILITY
Jeremy Cohen Simran Kaur Yuanzhi Li J. Zico Kolter1 and Ameet Talwalkar2
Carnegie Mellon University and: 1Bosch AI 2 Determined AI
Correspondence to: jeremycohen@cmu.edu

arXiv:2103.00065v1 [cs.LG] 26 Feb 2021, Published as a conference paper at ICLR 2021

 

4月13日(火)

HubMAP:1,445 teams, 28 days to go

 

上記の論文、難しくて理解が進まないのだが、勾配降下法で最適解に到達しようとするときに、従来の方法で到達する最適解とは違うところに、より汎用性の高い解が存在し、そこに到達するためには、従来のtraining方法とは異なる考え方が必要だということが書かれているように思う。

f:id:AI_ML_DL:20210413092141p:plain

max_lr=2, (up=3, down=7) x 2で、LB=0.875となった。

  1│ 0.1422│ 0.0888│ 0.5264│ 0.7703│ 1.13
  2│ 0.0595│ 0.0557│ 0.8006│ 0.8261│ 1.12
  3│ 0.0519│ 0.1444│ 0.8270│ 0.5969│ 1.13
  4│ 0.0484│ 0.0510│ 0.8348│ 0.8596│ 1.12
  5│ 0.0414│ 0.0405│ 0.8568│ 0.8779│ 1.12
  6│ 0.0396│ 0.0499│ 0.8632│ 0.8479│ 1.12
  7│ 0.0372│ 0.0422│ 0.8725│ 0.8453│ 1.13
  8│ 0.0350│ 0.0343│ 0.8806│ 0.8712│ 1.12
  9│ 0.0315│ 0.0334│ 0.8888│ 0.8898│ 1.12
10│ 0.0308│ 0.0299│ 0.8922│ 0.8917│ 1.12 : LB=0.869
11│ 0.0300│ 0.0350│ 0.8935│ 0.8831│ 1.13
12│ 0.0327│ 0.0377│ 0.8852│ 0.8755│ 1.11
13│ 0.0339│ 0.0381│ 0.8823│ 0.8691│ 1.12
14│ 0.0338│ 0.0362│ 0.8835│ 0.8727│ 1.14
15│ 0.0326│ 0.0337│ 0.8858│ 0.8754│ 1.14
16│ 0.0309│ 0.0336│ 0.8920│ 0.8885│ 1.12
17│ 0.0308│ 0.0480│ 0.8921│ 0.8323│ 1.13
18│ 0.0304│ 0.0320│ 0.8926│ 0.8868│ 1.14
19│ 0.0279│ 0.0399│ 0.9009│ 0.8760│ 1.14
20│ 0.0261│ 0.0325│ 0.9079│ 0.8883│ 1.14 : LB=0.875

 

3サイクル20エポックに加えて、2サイクル20エポックでもLB=0.0875になった。

1サイクル20エポックも試してみるかな。

 

max_lr=2.0を常用しているが、上限を確認するために3.0, 4.0, 5.0と変えてみた。upのエポック数は3である。upが2エポックの場合、max_lr=3ではlossが異常値を示して収束しなくなった。upを3エポックにすると、max_lr=5以外は、収束した。

SGDのnesterovはTrueで使っているが、画素数を多くしたときのoverfittingを抑制する効果があるかもしれないと思い、nesterov=Falseを試し始めた。

・収束は、1サイクル10エポックの場合、1エポックから2エポックくらい遅いようだ。

・384pixelの場合に、max_lr=3で異常値を示して収束しなかったが、nesterov=Falseにすると、異常値を示さず、収束した。

 

明日は、画素数の効果を調べる。

CyclicLRで最も簡単な1サイクル10エポックを用いる。

max_lrは、3または4を用いる。

 

4月14日(水)

HuBMAP:1,453 teams, 27 days to go

CyclicLRで最も簡単な1サイクル10エポックを用い、max_lrは3または4を用いて、画素数320, 384, 448, および512 pixelについてtrain, inference, commit, submitした結果、512 pixelの画像を用いた場合のLB=0.880が最良となった。ということで、またしても記録更新ならず。

SGDのnestrov=Falseは、max_lr=3でlossが異常値を示して収束しなかったものについて適用したところ、lossが順調に収束するということでは良かったのだが、スコアは向上しなかった。

 

4月15日(木)

HuBMAP:1,454 teams, 26 days to go

今日は、EfficientNetB2-UnetでLB=0.869が得られた条件のままで、B2を、B3, B4, B5, B6, B7と変えることにより、LBスコアがどうなるかを調べる。エポック数、サイクル数、Max_lr、upとqornの比率など、とても調べ切れるものではなく、最適値の組み合わせとは程遠いかもしれないが、ともかく、どういう傾向になるか調べてみよう。

256 pixel : 

B2-Unet : LB=0.869

b3-Unet : LB=0.858

B4-Unet : LB=0.873

B5-Unet : LB=0.860

B6-Unet : LB=0.875

B7-Unet : LB=0.868

LBスコアとしては、期待外れ。ちょっと面白いのは、この並びでは、でこぼこしていて傾向が見えにくいが、偶数組と奇数組に分けて並べると、各組の中ではモデルが大きいほどLBスコアが高くなっている、ということがわかる。

B2-Unet : LB=0.869

B4-Unet : LB=0.873

B6-Unet : LB=0.875

b3-Unet : LB=0.858

B5-Unet : LB=0.860

B7-Unet : LB=0.868

trainingの経過データをいくつか掲載しているが、overfittingといっても、train_diceは0.91止まり。これでは、トップレベルのLBスコアを出している人たちのval_diceである、0.92なんておぼつかない。

256 pixel : CyclicLR(SGD, up:down=3:7, lr=0.1-2.0), 2サイクル20エポックのtraining結果

  1│ 0.1489│ 0.0727│ 0.4805│ 0.7530│ 2.03
  2│ 0.0564│ 0.0646│ 0.8073│ 0.8146│ 2.02
  3│ 0.0567│ 0.0586│ 0.8260│ 0.8512│ 2.02
  4│ 0.0455│ 0.0428│ 0.8513│ 0.8492│ 2.02
  5│ 0.0386│ 0.0387│ 0.8714│ 0.8745│ 2.02
  6│ 0.0359│ 0.0394│ 0.8789│ 0.8570│ 2.02
  7│ 0.0333│ 0.0356│ 0.8855│ 0.8809│ 2.02
  8│ 0.0309│ 0.0304│ 0.8932│ 0.9017│ 2.01
  9│ 0.0284│ 0.0304│ 0.9003│ 0.8987│ 2.02
10│ 0.0272│ 0.0304│ 0.9039│ 0.8999│ 2.01 : LB=0.875
11│ 0.0267│ 0.0314│ 0.9051│ 0.8969│ 2.02
12│ 0.0292│ 0.0375│ 0.8988│ 0.8889│ 2.01
13│ 0.0304│ 0.0474│ 0.8982│ 0.8721│ 2.02
14│ 0.0316│ 0.0331│ 0.8939│ 0.8980│ 2.02
15│ 0.0285│ 0.0346│ 0.9011│ 0.8837│ 2.01
16│ 0.0264│ 0.0329│ 0.9074│ 0.8894│ 2.02
17│ 0.0258│ 0.0301│ 0.9091│ 0.9040│ 2.02
18│ 0.0256│ 0.0295│ 0.9104│ 0.9013│ 2.01
19│ 0.0239│ 0.0300│ 0.9146│ 0.9062│ 2.02
20│ 0.0245│ 0.0291│ 0.9149│ 0.9007│ 2.02 : LB=0.872

 
4月16日(金)

HuBMAP: 1,459 teams, 25 days to go

モデルのアンサンブルとTTAによって、LB=0.880の壁を突破したいと思うのだが、それよりも、まだ、CyclicLRで調べたいことがある。

 

4月17日(土)

GPU : 36 h

HuBMAP1,461 teams, 24 days to go

何度も同じ文献を見ているように思うが、都度、観点は異なる。今日は、サイクル数を繰り返すことによって改善される場合と、改善されない場合について整理してみようと思う。そのときに、思い出されるのが次の論文のFigure 2(b)である。1サイクルで最良値が得られ、サイクルを繰り返しても最初の値を超えていないように見える。

EXPLORING LOSS FUNCTION TOPOLOGY WITH CYCLICAL LEARNING RATES

L. N. Smith and N. Topin, arXiv:1702.04283v1 [cs.LG] 14 Feb 2017

f:id:AI_ML_DL:20210417091026p:plain

これまでに得られた結果が示唆するもの

1.1サイクルで良好な結果が得られる条件では、2サイクル、3サイクルと繰り返しても、1サイクルの結果を超えない。

2.3サイクルで良好な結果が得られる条件では、2サイクル、1サイクルでは3サイクルの結果を超えない。

3.画像解像度を上げることは、スコアアップにつながる。

4.Encoderのモデルサイズを大きくすることは、低解像度の画像を用いた場合に、スコアアップにつながる。

 

512 pixel, 1サイクル10エポック、lr=0.1-2.0で、(up=4, down=6)と、(up=down=5)について計算してみよう。

up=5, down=5 : LB=0.868

up=4, down=6 : LB=0.872

up=3, down=7 : LB=0.880

up=2, down=8 : LB=0.872

 

Cutoutの効果を調べる。

num_holes=1, max_h_size=int(0.316 * 512):(0.316 * 0.316 ≒ 0.1)

  1│ 0.1379│ 0.0758│ 0.5463│ 0.7905│ 3.02
  2│ 0.0502│ 0.0560│ 0.8334│ 0.8330│ 2.99
  3│ 0.0394│ 0.0866│ 0.8675│ 0.7177│ 3.01
  4│ 0.0403│ 0.0383│ 0.8618│ 0.8809│ 3.00
  5│ 0.0336│ 0.0388│ 0.8831│ 0.8766│ 3.01
  6│ 0.0320│ 0.0446│ 0.8899│ 0.8725│ 3.00
  7│ 0.0302│ 0.0448│ 0.8958│ 0.8498│ 3.02
  8│ 0.0279│ 0.0311│ 0.9022│ 0.8840│ 3.01
  9│ 0.0261│ 0.0292│ 0.9073│ 0.9017│ 3.00
10│ 0.0247│ 0.0271│ 0.9135│ 0.9030│ 3.02 : LB=0.865

比較データ:num_holes=10, max_h_size=int(0.1 * 512):LB=0.880

 

4月18日(日)

HuBMAP:1,466 teams, 23 days to go

 

Loss functionは、BCE(BCEWithLogitsLoss)で固定したままだった。

今日は、Dice, Jaccard, Lovasz, Focalを試してみよう。

B2-Unet, 512 pixel, CyclicLR(SGD, nesterov, lr=0.1-2.0, up=3, down=7, 10 epochs)

BCE : LB=0.880

Dice : 

  1│ 0.3138│ 0.2053│ 0.6862│ 0.7947│ 3.05
  2│ 0.1600│ 0.8500│ 0.8400│ 0.1500│ 3.02
  3│ 0.1658│ 0.3692│ 0.8342│ 0.6308│ 3.05
  4│ 0.1341│ 0.1762│ 0.8659│ 0.8238│ 3.02
  5│ 0.1147│ 0.1261│ 0.8853│ 0.8739│ 3.01
  6│ 0.1147│ 0.1424│ 0.8853│ 0.8576│ 3.04
  7│ 0.1035│ 0.1919│ 0.8965│ 0.8081│ 3.01
  8│ 0.1214│ 0.1200│ 0.8786│ 0.8800│ 3.02
  9│ 0.0917│ 0.0930│ 0.9083│ 0.9070│ 3.01
10│ 0.0851│ 0.1060│ 0.9149│ 0.8940│ 3.01 : LB=0.812

LBスコアは非常に低い。それでも、次は、downを7から12に増やしてみよう。

Jaccard : 

  1│ 0.5231│ 0.9143│ 0.6076│ 0.1578│ 3.05
  2│ 0.3025│ 0.7233│ 0.8190│ 0.4285│ 3.02
  3│ 0.2833│ 0.8238│ 0.8330│ 0.2968│ 3.05
  4│ 0.2603│ 0.3547│ 0.8485│ 0.7812│ 3.06
  5│ 0.2442│ 0.3780│ 0.8583│ 0.7605│ 3.02
  6│ 0.2029│ 0.3472│ 0.8861│ 0.7875│ 3.04
  7│ 0.1871│ 0.2345│ 0.8960│ 0.8621│ 3.02
  8│ 0.1695│ 0.1764│ 0.9068│ 0.9017│ 3.03
  9│ 0.1618│ 0.1593│ 0.9111│ 0.9126│ 3.04
10│ 0.1510│ 0.1796│ 0.9179│ 0.8991│ 3.03 : LB=0.814

Diceと同様、9エポックで止めた方がスコアは良さそうだ。次は、downを7から12に増やしてみよう。

Lovasz : 

機能せず。原因不明。

Focal : 

収束はするが、非常に遅い。train_Dice_coeff, Val_Dice_coeffともに、0.72を超えず。

Lovasz, Focal, ともに、期待していたのだが、使ってみなければわからないものだ。

SGDとの相性だろうか。

 

Dice_LossとJaccard_Lossは、どちらもスコアアップの可能性がありそうなので、最適条件を探索してみたいが、時間も資源も限られている。そこで、BCE_lossで良い結果を得ている、up=3, down=12の条件にするとともに、Cutoutも強め(num_holes=1, max_h_size=int(0.447 * 512), 0.447^2≒0.2)にして計算してみる。

Dice_Loss :

  1│ 0.3315│ 0.2272│ 0.6685│ 0.7728│ 3.04
  2│ 0.1863│ 0.9131│ 0.8137│ 0.0869│ 3.03
  3│ 0.1695│ 0.9015│ 0.8305│ 0.0985│ 3.00
  4│ 0.1760│ 0.3990│ 0.8240│ 0.6010│ 3.01
  5│ 0.1522│ 0.1426│ 0.8478│ 0.8574│ 3.03
  6│ 0.1270│ 0.1614│ 0.8730│ 0.8386│ 2.99
  7│ 0.1271│ 0.1413│ 0.8729│ 0.8587│ 3.01
  8│ 0.1161│ 0.1543│ 0.8839│ 0.8457│ 3.00
  9│ 0.1142│ 0.1338│ 0.8858│ 0.8662│ 2.99
10│ 0.1064│ 0.1156│ 0.8936│ 0.8844│ 3.02
11│ 0.1093│ 0.1155│ 0.8907│ 0.8845│ 3.00
12│ 0.1068│ 0.1251│ 0.8932│ 0.8749│ 3.03
13│ 0.0989│ 0.1062│ 0.9011│ 0.8938│ 3.03
14│ 0.0918│ 0.1090│ 0.9082│ 0.8910│ 3.00
15│ 0.0882│ 0.0841│ 0.9118│ 0.9159│ 3.05 : LB=0.842

downを5エポック増やして、Cutoutを加えてみたが、だめだった。

JaccardLoss :

  1│ 0.4901│ 0.9936│ 0.6367│ 0.0126│ 3.20
  2│ 0.3292│ 0.9128│ 0.8001│ 0.1601│ 3.17
  3│ 0.2946│ 0.9769│ 0.8245│ 0.0446│ 3.17
  4│ 0.2760│ 0.7785│ 0.8374│ 0.3552│ 3.17
  5│ 0.2781│ 0.7158│ 0.8355│ 0.4395│ 3.17
  6│ 0.2369│ 0.4669│ 0.8641│ 0.6888│ 3.16
  7│ 0.2201│ 0.2245│ 0.8749│ 0.8727│ 3.16
  8│ 0.2094│ 0.1894│ 0.8821│ 0.8944│ 3.15
  9│ 0.2038│ 0.2969│ 0.8852│ 0.8208│ 3.17
10│ 0.2022│ 0.2547│ 0.8866│ 0.8520│ 3.17
11│ 0.1978│ 0.1894│ 0.8893│ 0.8944│ 3.17
12│ 0.1827│ 0.1987│ 0.8986│ 0.8851│ 3.16
13│ 0.1775│ 0.1939│ 0.9019│ 0.8923│ 3.17
14│ 0.1749│ 0.1821│ 0.9035│ 0.8990│ 3.17
15│ 0.1663│ 0.1646│ 0.9087│ 0.9096│ 3.18 : LB=0.812

train_loss > val_loss and train_Dice < val_Diceとなったので、スコアの改善を期待したのだが、まったくだめ。どうなっているのか。

先日、Dice coefficientを計算できるようにしたときに、この値とLBスコアの対応が良くなかったので、計算が間違っているかもしれないと思って、使わなかったのだが、train_lossやval_lossとの対応が比較的良いので、また、使うようにした。

この結果は、受け入れがたい。どうなっているのだろう。

*JaccardLossよりは、DiceLossの方が、少し可能性を残しているように思う。

 

CyclicLRの1サイクル20エポック(up=3, down=17)で、512 pixel, BCE, 1 hole, area=0.25を試した。LB=0.875(v37)となった。up=3, down=12でLB=0.878だったので、これ以上は期待できないという結果となった。

 

4月19日(月)

HuBMAP:1,470 teams, 22 days to go

512 pixel, BCE, 1 hole, area=0.25を、これまでの何通りかのOneCycleLR, CyclicLRで試す。

v38 : OneCycleLR(AdamW), 20エポック : LB=0.870

v39 : OneCycleLR(SGD), 20エポック : LB=0.862

v40 : CyclicLR(SGD, up=2, down=5, 0.1-2.0) : 3サイクル:LB=0.858

v41 : Cyclic_LR(SGD, up=3, down=7, 0.1-2.0) : 3サイクル:収束が悪いのでcommitせず。

ここまでの検討結果:CyclicLRのサイクル数を増やすことによりスコアアップを図ってきたが、CyclicLRの1サイクルによって同等のスコアを得ることが可能であることがわかり、サイクル数を増やしても、1サイクルを超えるスコアは得られなかった。

気になることは、CyclicLRの1サイクルのスコアを、1サイクル専用の、OneCycleLRでは、まだ、同レベルに達していないことである。

ということで、明日は、OneCycleLRでLB=0.880に到達し、超えることを試みる。

 

4月20日(火)

HuBMAP: 1,469 teams, 21 days to go

512 pixel, BCE, 1 hole, area=0.25 ---> 10 hole, area=0.1

OneCycleLR(AdamW, maxlr=1e-3, epochs=10) : LB=0.872

OneCycleLR(SGD, maxlr=2.0, epochs=10) : LB=0.849

Blur, GaussNoiseの割合を増やして、同じ計算を行った。

OneCycleLR(AdamW), Blur 0.25 -> 0.5, GaussNoise 0.25 -> 0.5 : LB=0.881

OneCycleLR(SGD), Blur 0.25 -> 0.5, GaussNoise 0.25 -> 0.5 : LB=0.862

2週間ぶりに、ようやく自己新記録だ。わずか、0.001の改善であったが、augmentationの条件:BlurやGaussianNoiseなどを少し多くすることによってスコアアップしたことは、良かったと思っている。これを、CyclicLRで0.880が得られている条件に適用することで、もう少しスコアアップできる可能性があると思う。

これで、GPU割当時間はほぼ使い切った。

水、木、金は、これまでのデータを整理して、土曜日からのサイクルで行う実験の計画を立てようと思う。

 

4月21日(水)

HuBMAP:1,474 teams, 20 days to go

このコンペで使えるKaggleのGPUはあと90時間+α。1つの計算に90分かかるとすると、commitがあるので、submitまでに3時間かかる。ということは、あと30回チャレンジできる。

300回のトライで、0.83から0.88まで上がったので、このペースだと、0.885までということになる。

1月7日にRiiid!コンペが終了し、その後(その前にも少し)、HuBMAPにとりかかったので、もう3か月以上取り組んでいる。これも、あと20日で終了する。

qubvel/segmentation_models.pytorch

このsegmentation_modelsパッケージを使うことによって、様々なEncoder/Decoderの組み合わせ、いくつかのloss_functionの組み合わせ、を試すことができた。

https://www.kaggle.com/leighplt/pytorch-fcn-resnet50

このコードとその高速版を使わせていただき、そこに、segmentation_modelsを追加するとともに、いくつかのaugmentationのセット、optimizer, learning rate schedulerなどを追加した。TTAも組み込んだが毎回使うと時間のロスになるので、テスト的に使っただけである。KFoldとアンサンブルは、まだ、使っていない。それらは最後まで使わないかもしれない。

 

これまで、augmentationについて、画像をチェックしながら、その効果を検討したことが無かった。パラメータを変更してみて、スコアがどう動くかを見ていただけである。

あと20日間の間に、augmentationの効果について、具体的に、画像をチェックしながら、スコアとの関係を把握したいと思っている。

 

昨日確率を0.25から0.5に変更したBlurとGaussNoise:

GaussNoiseは、パラメータの設定値が非常に小さく、あまり効いていなかったと推測される。少なくとも、自分の目では変化を確認できなかった。パラメータの設定値を10倍にしても目視で変化を確認できなかった。

4月24日追記:GaussNoiseのパラメータは分散と平均で、デフォルトは、var_limit=(10.0, 50.0), mean=0となっている。

今日まで、画像をチェックしないで、得られた結果だけをみてパラメータを機械的に変更してきた。これでは、何も学んだことにならないと思う。

画像は、モデルに入力する段階で規格化されたり、他の演算が行われて、目視した画像とは全く違ったものになって演算が進むので、元画像に対するaugmentationの効果を目視で確認することが、augmentationの効果を理解するうえでどういう意味を持つかはわからないが、とにかくやってみることにする。

Blurは、blur_limit=3に設定している。この条件では、画像の解像度が1/2~1/3程度になっているようにみえる。これで効果があったのだから、(3, 5)とか(5, 5)くらいまでは、GPUが使えるようになれば計算してみるつもりだ。これ以上数字を大きくすると128とか64ピクセルレベルの画像になる。それでも意味が無いとは言い切れない。一度は試してみてもよいかもしれない。

明日は、Brightness, Contrast, Gamma等がtrain画像に対してどの程度の変化を与えているのかを自分の目で見て確認する予定。

 

4月22日(木)

HuBMAP:1,478 teams, 19 days to go

RandomGamma:gamma_limit=(80, 120):これがデフォルトだが、80と120の定義がわからない。

ウィキペディアには次のような記述がある。

入力値と出力値が直線の関係を示す場合、ガンマ値は1 (γ=1) となるが、γ<1の場合は階調が明るい出力に、γ>1の場合は階調が暗い出力になる。例えば、ガンマ値2.2のディスプレイで適正に表示される画像をガンマ値1.8のディスプレイに表示した場合、実際のガンマ値はγ=1.8/2.2≒0.82となり、意図したものよりも明るい画像となる。画像の入力から最終出力までの全体のガンマが1になるよう、適当なガンマ値のカーブに従って画像の階調を補正することをガンマ補正という。

Qiitaには画像を使ってパラメータと見え方の例が示されているが、その記事には、数値の意味は書かれていなかった。gamma_limit=(50, 50)は元画像より明るく、gamma_limit=(150, 150)は元画像より暗い。

デフォルト値、画像例から推測すると、100がノーマルで、数値を下げれば明るく、数値を上げれば暗くなり、2つの数値によって、最大値と最小値を指定する、ということになるのかな。ウィキペディアを英語に切り替えるとluminanceという単語が出てきて、1または100でnormalizeと書かれているので、これでよさそうだ。

 

RandomBrightnessとRandomContrast:いずれもデフォルトはlimit=(-0.2, 0.2)であり、マイナス側は、暗い、コントラストが低い(結果として暗く感じる)、プラス側はその逆となる。

これをどう使うか。

Cutoutもいくつか試したが、まだまだ、探索の途中である。

gamma, brightness, contrastなどがどう効くのか、1つ1つ試してみるしかない。

なんとなく、ものすごく、原始的な作業をしているような気がしてきた。どこが人工知能か。1つ1つが手作業というのは、何かおかしい。

F. Chollet氏のテキストの犬と猫の分類で、500枚づつのデータを使用するとoverfittingによってaccuracyが0.7くらいで頭打ちになり、augmentationでデータ数を増やすことによって、0.85くらいまで上がるということから、augmentationの効果を体感するというのがあった。

今使っているaugmentationは、画像の枚数は変わらずに、部分的に画像を加工しているだけのような気がする。このことがずっと気になっている。

KaggleのコンペのDiasussionで、誰かが、PyTorchよりもKerasの方がスコアが高いように思う。なぜだろう、と問いかけていたことがあるのを思い出した。もしかすると、このことが原因となっていたということではないだろうか。

F. Chollet氏のテキストでKerasを使っていたので、augmenrationは、画像枚数を増やすものだと思っていた。

しかし、今使っているPyTorchのコードでは、train_dataの数は、augmentationを使っても増えない。指定した確率で、加工されているだけである。たとえば、加工がAだけで、その確率を0.25と指定すると、元の画像に、Aという加工がされた画像の25%が追加されるのではなく、元画像が75%で、Aという加工をした画像が25%で、合わせて100%となり、合計枚数は増えないのである。

これまでは、PyTorchでaugmentationを使っていても、augmentationを含む前処理をしながらモデルにデータを投入していたので、augmentationによって画像が増えていたかどうかはわからないが、今回は、augmentationを含む前処理を済ませてからモデルにデータを投入しており、加工した画像の枚数が表示されるので、augmentationによって画像枚数が増えていないことは、確かだと思う。

KerasかPyTorchかということではなく、プログラム作成者の意図によるのだろうと思う。元データ+transformed_dataがよいのか、元データを所定の確率でtransformしたデータがよいのかを選んだ結果なのであろう。プログラミング技術と計算資源の制限によって、どちらかを選ばないといけない状況になることもある。現状をわかったうえで次の手段を考え、準備しているつもりだが、今は、プログラミング技術がボトルネックになっている。

 

明日は、有効なaugmentation方法の探索をやってみよう。使っていない手法が山ほどある。

 

AutoML: A Survey of the State-of-the-Art
Xin He, Kaiyong Zhao, Xiaowen Chu, arXiv:1908.00709v6 [cs.LG] 16 Apr 2021

Abstract
     Deep learning (DL) techniques have obtained remarkable achievements on various tasks, such as image recognition, object detection, and language modeling. However, building a high-quality DL system for a specific task highly relies on human expertise, hindering its wide application. Meanwhile, automated machine learning (AutoML) is a promising solution for building a DL system without human assistance and is being extensively studied. This paper presents a comprehensive and up-to-date review of the state-of-the-art (SOTA) in AutoML. According to the DL pipeline, we introduce AutoML methods –– covering data preparation, feature engineering, hyperparameter optimization, and neural architecture search (NAS) –– with a particular focus on NAS, as it is currently a hot sub-topic of AutoML. We summarize the representative NAS algorithms’ performance on the CIFAR-10 and ImageNet datasets and further discuss the following subjects of NAS methods: one/two-stage NAS, one-shot NAS, joint hyperparameter and architecture optimization, and resource-aware NAS. Finally, we discuss some open problems related to the existing AutoML methods for future research.

Keywords: deep learning, automated machine learning (AutoML), neural architecture search (NAS), hyperparameter optimization (HPO)

概要
ディープラーニング(DL)技術は、画像認識、オブジェクト検出、言語モデリングなどのさまざまなタスクで目覚ましい成果を上げています。ただし、特定のタスクのために高品質のDLシステムを構築することは、人間の専門知識に大きく依存しており、その幅広いアプリケーションを妨げています。一方、自動機械学習(AutoML)は、人間の支援なしでDLシステムを構築するための有望なソリューションであり、広く研究されています。このホワイトペーパーでは、AutoMLの最先端(SOTA)の包括的で最新のレビューを紹介します。 DLパイプラインによると、現在AutoMLのホットなサブトピックであるため、NASに特に焦点を当てて、データ準備、機能エンジニアリング、ハイパーパラメータ最適化、ニューラルアーキテクチャ検索(NAS)をカバーするAutoMLメソッドを紹介します。 CIFAR-10およびImageNetデータセットでの代表的なNASアルゴリズムのパフォーマンスを要約し、NASメソッドの次の主題についてさらに説明します:1/2ステージNAS、ワンショットNAS、共同ハイパーパラメータとアーキテクチャの最適化、およびリソース認識NAS。最後に、将来の研究のために、既存のAutoMLメソッドに関連するいくつかの未解決の問題について説明します。 by Google翻訳

f:id:AI_ML_DL:20210423004754p:plain

f:id:AI_ML_DL:20210423004905p:plain

f:id:AI_ML_DL:20210423005008p:plain

2.3. Data Augmentation
     To some degree, data augmentation (DA) can also be regarded as a tool for data collection, as it can generate new data based on the existing data. However, DA also serves as a regularizer to avoid over-fitting of model training and has received more and more attention. Therefore, we introduce DA as a separate part of data preparation in detail. Figure 3 classifies DA techniques from the perspective of data type (image, audio, and text), and incorporates automatic DA techniques that have recently received much attention. 
     For image data, the affine transformations include rotation, scaling, random cropping, and reflection; the elastic transformations contain the operations like contrast shift,
brightness shift, blurring, and channel shuffle; the advanced transformations involve random erasing, image blending, cutout [89], and mixup [90], etc. These three types of
common transformations are available in some open source libraries, like torchvision 5, ImageAug [91], and Albumentations [92]. In terms of neural-based transformations, it can be divided into three categories: adversarial noise [93], neural style transfer [94], and GAN technique [95].

 

4月23日(金)

HuBMAP:1,492 teams, 18 days to go

少し前に見ていた文献を、昨夜なんとなくながめていて、少し気になる箇所があった。それは、trainingの初期状態が、trainingの結果に大きな影響を及ぼすということである。ImageNetなどによる学習済みモデルを用いる場合には、小さな学習率から始めることによって、モデルの事前学習が生かされて、良いtraining結果が得られると学んだ。しかし、この論文の趣旨は、SGDでtrainingする場合に、trainingの初期に、小さくない学習率でtrainingすると、良いtraining結果が得られるということのようである。

THE BREAK-EVEN POINT ON OPTIMIZATION TRAJECTORIES OF DEEP NEURAL NETWORKS
Stanisław Jastrz˛ebski, Maciej Szymczak, Stanislav Fort, Devansh Arpit, Jacek Tabor
Kyunghyun Cho, Krzysztof Geras,  Published as a conference paper at ICLR 2020

ABSTRACT
The early phase of training of deep neural networks is critical for their final performance.
In this work, we study how the hyperparameters of stochastic gradient descent (SGD) used in the early phase of training affect the rest of the optimization trajectory. We argue for the existence of the “break-even" point on this trajectory, beyond which the curvature of the loss surface and noise in the gradient are implicitly regularized by SGD. In particular, we demonstrate on multiple classification tasks that using a large learning rate in the initial phase of training reduces the variance of the gradient, and improves the conditioning of the covariance of gradients. These effects are beneficial from the optimization perspective and become visible after the break-even point. Complementing prior work, we also show that using a low learning rate results in bad conditioning of the loss surface even for a neural network with batch normalization layers. In short, our work shows that key properties of the loss surface are strongly influenced by SGD in the early
phase of training. We argue that studying the impact of the identified effects on
generalization is a promising future direction. 

概要
ディープニューラルネットワークのトレーニングの初期段階は、最終的なパフォーマンスにとって重要です。この作業では、トレーニングの初期段階で使用される確率的勾配降下法SGD)のハイパーパラメーターが残りの最適化軌道にどのように影響するかを調べます。この軌道上に「損益分岐点」が存在することを主張します。それを超えると、損失面の曲率と勾配のノイズがSGDによって暗黙的に正則化されます。特に、大規模な学習を使用することを複数の分類タスクで示します。レーニングの初期段階でのレートは、勾配の分散を減らし、勾配の共分散の調整を改善します。これらの効果は、最適化の観点から有益であり、損益分岐点の後に見えるようになります。以前の作業を補完して、次のことも示します。低い学習率を使用すると、バッチ正則化層を備えたニューラルネットワークでも損失面のコンディショニングが悪くなります。要するに、私たちの研究は、損失面の主要な特性がトレーニングの初期段階でSGDの影響を強く受けることを示しています。正則化に対する特定された影響の影響を研究することは、有望な将来の方向性です。by Google翻訳

 

この論文の記述と関係がありそうな実験結果を眺めてみよう。

CyClicLRのパラメータ、base_lrを、0.1, 0.001, 0.00001として計算してみた。

v29 : 2 cycles, 20 epochs:0.1-2.0 : LB=0.839 : (train_loss=0.0258, val_loss=0.0301)

v31 : 2 cycles, 20 epochs:0.001-2.0 : LB=0.853 : (train_loss=0.0256, val_loss=0.0311)

v32 : 2 cycles, 20 epochs:0.00001-2.0 : LB=0.847 : (train_loss=0.0255, val_loss=0.0339)

この結果(LBスコア)からは、base_lrとして、0.1よりも0.001や0.00001の方が良いと判断するところだが、別の観点から0.1が良いと判断した。それは、最終エポックでのtrain_lossとval_lossである。base_lrを小さくすると、train_lossはあまり変わらないのに、val_lossが明らかに大きくなっていることから良くないと判断した。今見ると、これだけの結果で、0.1の一択にしてしまったのは早計だったようであり、上記論文の内容を見ていると、lrの最小値における一桁の違いは大きく、base_lr=0.01も調べておく必要がありそうだと思う。ということで、base_lr=0.1, 0.01, 0.001の比較を、512 pixelの場合について、実施してみよう。

 

4月24日(土)~ 4月30日(金)の予定

・base_lr=0.1, 0.01, 0.001の比較

・GaussNoise, Blur, (gamma, contrast, brightness)の効果確認

・Cutoutは、オリジナルの論文に示されている、ホール数1、面積率0.25にこだわってその効果を調べていたが、ホール数10で、面積率0.1, 0.15, 0.2と変えて、その効果を調べてみる。

 

4月24日(土)

HuBMAP:1,503 teams, 17 days to go

GPU:38 h

今日は、「base_lr=0.1, 0.01, 0.001の比較」をやってみよう。

Baseは、LB=0.880(v2/v25)で、512 pixel, CyclicLR(SGD, 0.1-2.0, up=3 epochs, down=7 epochs), BCELoss, augmentationは、flip, rotateの他に、cutout(hole_num=1, total_area=0.1), Blur(limit=(3,3), p=0.5), GaussNoiseはデフォルトでp=0.5, brightness, contrast, gamma等はデフォルトでp=0.5など。

trfm_4 = A.Compose([
A.Resize(512, 512),
A.OneOf([
A.RandomBrightness(limit=(-0.2,0.2), p=1),
A.RandomContrast(limit=(-0.2,0.2), p=1),
A.RandomGamma(gamma_limit=(80,120),p=1)
], p=0.5),
A.OneOf([
A.Blur(blur_limit=(3,3), p=1),
A.MedianBlur(blur_limit=(3,3), p=1)
], p=0.5),
A.GaussNoise(var_limit=(10.0,50.0), mean=0, p=0.5),
A.RandomRotate90(p=.5),
A.HorizontalFlip(p=.5),
A.VerticalFlip(p=.5),
A.Cutout(num_holes=10, max_h_size=int(.1 * 512), max_w_size=int(.1 * 512), p=.25),])

結果:

  1│ 0.1406│ 0.1000│ 0.5418│ 0.7483│ 3.98
  2│ 0.0462│ 0.0695│ 0.8463│ 0.7611│ 3.96
  3│ 0.0389│ 0.0504│ 0.8689│ 0.8558│ 3.97
  4│ 0.0357│ 0.0364│ 0.8777│ 0.8743│ 3.93
  5│ 0.0336│ 0.0363│ 0.8822│ 0.8813│ 3.96
  6│ 0.0318│ 0.0323│ 0.8903│ 0.8976│ 3.94
  7│ 0.0280│ 0.0321│ 0.9020│ 0.8903│ 3.96
  8│ 0.0267│ 0.0353│ 0.9055│ 0.8749│ 3.94
  9│ 0.0256│ 0.0271│ 0.9086│ 0.9073│ 3.98
10│ 0.0241│ 0.0322│ 0.9152│ 0.8960│ 3.96 : LB=0.883

これは、自己新記録だ。0.002だけ上がった。

overfittingになっていると思うので、もう少しaugmentationの強度を上げても良さそうに思うのだが、・・・。

予定していなかったが、損失関数の組み合わせをやってみよう。時間がかかって、効果が出ないことを覚悟で実験する。

最初に、BCEとDiceとJaccardを等分で組み合わせてみる。

上記のLB=0.883の条件を踏襲するが、画素数512 pixelではGPUモリーが15.8 GBとギリギリなので、448 pixelとする。結果は次の通り、LBスコアは、非常に低い。

  1│ 0.3207│ 0.2839│ 0.6660│ 0.7603│ 3.17
  2│ 0.1688│ 0.4044│ 0.8523│ 0.6247│ 3.15
  3│ 0.1431│ 0.1717│ 0.8758│ 0.8555│ 3.14
  4│ 0.2095│ 0.2449│ 0.8339│ 0.7640│ 3.11
  5│ 0.1360│ 0.3452│ 0.8833│ 0.7496│ 3.12
  6│ 0.1273│ 0.1305│ 0.8908│ 0.8872│ 3.14
  7│ 0.1062│ 0.1581│ 0.9089│ 0.8672│ 3.13
  8│ 0.1032│ 0.1118│ 0.9114│ 0.9015│ 3.13
  9│ 0.0983│ 0.0969│ 0.9158│ 0.9168│ 3.14
10│ 0.0901│ 0.0890│ 0.9225│ 0.9232│ 3.12 : LB=0.840

train_dice_coeff=0.9225とval_dice_coeff=0.9232から、新記録を期待したのだが、非常に残念な結果となった。何がおきているのだろうか。同じことは、DiceLossとJaccardLoss単独のときにも生じていた。いずれも、val_dice_coeffは0.90を超えていたが、LB=0.842とかLB=0.812という値になっていた。

あきらめきれないので、downのエポック数を7から11に増やしてみた。

  1│ 0.3207│ 0.2839│ 0.6660│ 0.7603│ 3.19
  2│ 0.1688│ 0.4044│ 0.8523│ 0.6247│ 3.16
  3│ 0.1431│ 0.1717│ 0.8758│ 0.8555│ 3.15
  4│ 0.1337│ 0.1880│ 0.8842│ 0.8265│ 3.14
  5│ 0.1258│ 0.1937│ 0.8908│ 0.8218│ 3.13
  6│ 0.1209│ 0.1141│ 0.8952│ 0.9038│ 3.16
  7│ 0.1083│ 0.1476│ 0.9061│ 0.8691│ 3.14
  8│ 0.0986│ 0.1044│ 0.9151│ 0.9085│ 3.14
  9│ 0.0992│ 0.1300│ 0.9141│ 0.8923│ 3.16
10│ 0.0963│ 0.0974│ 0.9170│ 0.9158│ 3.15
11│ 0.0925│ 0.0941│ 0.9201│ 0.9201│ 3.14
12│ 0.0881│ 0.1002│ 0.9237│ 0.9146│ 3.16
13│ 0.0825│ 0.0915│ 0.9289│ 0.9214│ 3.15
14│ 0.0799│ 0.0912│ 0.9311│ 0.9222│ 3.14 : LB=0.855

 

明日、BCE:Dice:Jaccard=8:1:1を試してみよう。

 

4月25日(日)

HuBMAP:1,517 teams, 16 days to go

train_dataは15件ある。今使っているのは、当初の8件+2件である。未使用のTrain_dataとpublic_LBのpublic_test_dataに大きな違いがあれば、val_dice_coeffとLBスコアの間に大きな乖離があっても不思議ではない。

このことを確認するために、train_dataの追加2件を変えてみる。

以下に示すのが、2月初旬にデータが更新されたときに追加されたtrain_dataである。pub_testと書いてある5件が、データ更新前の、public_LBスコアの計算に用いられていたpublic_test_dataである。これまで使ってきた追加2件のデータは紫色で示した。この後、茶色で示した2件のデータに変えてみる。

'26dc41664': 1.6e9 245 pub_test
'4ef6695ce': 2.0e9 439
'8242609fa': 1.4e9 586
'afa5e8098': 1.6e9 235 pub_test
'b2dc8411c': 4.6e8 138 pub_test
'b9a3865fc': 1.3e9 469 pub test
'c68fe75ea': 1.3e9 118 pub test
8件のtrain_dataはそのままで、追加の2件をどう選ぶかによって、収束の仕方もLBスコアも全く違ったものになった。

紫色の2件追加:LB=0.883

黒色の3件追加:LB=0.850

カーキ色の2件追加:LB=0.846

驚きだ、8件まで同じで、残り7件のうちのどれを追加するかによってLBスコアがここまで違うとは、どういうことなのか。

private_dataに対するLBスコアも、また、違ってくるのだろう。こういうものなのか、それとも、自分のやり方が間違っているのか。こういうものだ、だから、KFoldもモデルのアンサンブルも、TTAも広範囲にやって平均をとらないとダメなんだよ、ということか。

3件のデータを追加した場合は、次に示すように、完全にoverfittingになっている。LBスコアが低いのはoverfittingだから、augmentationを強くしてみるということがLBスコアの向上につながるだろうと思う。

  1│ 0.1138│ 0.0595│ 0.5990│ 0.7940│ 4.24
  2│ 0.0379│ 0.0420│ 0.8618│ 0.8733│ 4.20
  3│ 0.0327│ 0.0420│ 0.8800│ 0.8787│ 4.20
  4│ 0.0282│ 0.0350│ 0.8954│ 0.8733│ 4.16
  5│ 0.0258│ 0.0381│ 0.9029│ 0.8676│ 4.14
  6│ 0.0242│ 0.0329│ 0.9089│ 0.8896│ 4.15
  7│ 0.0221│ 0.0338│ 0.9152│ 0.8974│ 4.14
  8│ 0.0209│ 0.0368│ 0.9199│ 0.8764│ 4.19
  9│ 0.0200│ 0.0304│ 0.9229│ 0.8987│ 4.16
10│ 0.0192│ 0.0305│ 0.9254│ 0.9039│ 4.19 : LB=0.850

次に示す、カーキ色の2件のデータを追加した場合のtrainingの結果は、良さそうに思うのだが、LBスコアは低い。LBスコアが低い原因の1つには、trainingに使ったデータと、public_dataに違いがある、ということが考えられる。

  1│ 0.1592│ 0.0940│ 0.4803│ 0.6938│ 3.42
  2│ 0.0533│ 0.0547│ 0.8175│ 0.8650│ 3.39
  3│ 0.0442│ 0.0510│ 0.8506│ 0.8180│ 3.41
  4│ 0.0441│ 0.0430│ 0.8490│ 0.8539│ 3.38
  5│ 0.0359│ 0.0555│ 0.8762│ 0.7945│ 3.39
  6│ 0.0320│ 0.0404│ 0.8885│ 0.8592│ 3.37
  7│ 0.0299│ 0.0361│ 0.8945│ 0.8478│ 3.34
  8│ 0.0282│ 0.0347│ 0.9003│ 0.8770│ 3.38
  9│ 0.0262│ 0.0311│ 0.9068│ 0.8877│ 3.37
10│ 0.0258│ 0.0273│ 0.9058│ 0.9042│ 3.35 : LB=0.846

追加データを3つに分けた時に、LBスコアに違いが生じたということは、現状の方法では、予測モデルの一般性:generalityが低いということを示唆しているように思う。

最初の8件のデータに、8件のデータと類似したデータを追加すると、overfittingが生じやすく、8件のデータとは違うデータを追加すると、overfittingは生じにくい、ということになっているようである。

今、3種類の予測モデルができた。1つは、overfittingしてLBスコアが悪いもの。1つは、overfittingせず、LBスコアがよいもの、1つは、overfittingせず、LBスコアが悪いもの。train_dataの選び方によって、異なる予測モデルが生じる。

どうすれば、糸球体を正確に補足することができる予測モデルをつくることができるのか、train_dataを眺めながら、考えてみよう。

 

メモ:今日、AtCoderに登録した。プログラミングをゼロから学びなおすために。C++の入門編(APG4b : AtCoder Programming Guide for beginners)から始めよう。

 

4月26日(月)

HuBMAP:1,533 teams, 15 days to go

CyclicLRのbase_lr=0.1を0.01, 0.001とする。使うのは、LB=0.883となったコード。

lbase_lr=0.1 :     LB=0.883

base_lr=0.01 :   LB=0.875

base_lr=0.001 : LB=0.870

こうなると、base_lrを0.1より大きくしてみたくなる。0.2を試してみよう。

base_lr=0.2 :    LB=0.865

 

面白いが、役に立たない!

 

今後のことを考えて、モデルのアンサンブルをやってみよう。

明日は、B0-UnetとB2-Unetのアンサンブルに挑戦してみようと思う。

 

AtCoder: 初心者用のC++コースを学習中。EX1からEX26まである練習問題のうち、EX10まで進んだ。

 

4月27日(火)

HuBMAP1,540 teams,14 days to go

Effb0-UnetとEffb2-Unetの2つの予測モデルを作ってアンサンブルをするには、どうすればよいのか。

公開コードに学ぼう。

 

4月28日(水)

HuBMAP:1,553 teams, 13 days to go

 公開コードのチューニング

 

4月29日(木)

HuBMAP: 1,566 teams, 12 days to go

今日も公開コードのチューニング。

trainコードとinferenceコードの両方が公開されていて、使いやすく、よくできていて、LBスコアも高く、そのチューニングに、はまっている。

 

4月30日(金)

HuBMAP:1,571 teams, 11 days to go

今日も、チューニング。

4月27日に得たLBスコアのまま、進展なし。

Lookaheadが使われているのだが、パラメータの最適化に時間がかかりそうだ。 

 

5月1日(土)

HuBMAP:1,570 teams, 10 days to go

Lookaheadの論文を見よう。

f:id:AI_ML_DL:20210501101146p:plain

arXiv:1907.08610v2 [cs.LG] 3 Dec 2019

Abstract
The vast majority of successful deep neural networks are trained using variants of
stochastic gradient descent (SGD) algorithms. Recent attempts to improve SGD can be broadly categorized into two approaches: (1) adaptive learning rate schemes, such as AdaGrad and Adam, and (2) accelerated schemes, such as heavy-ball and Nesterov momentum. In this paper, we propose a new optimization algorithm, Lookahead, that is orthogonal to these previous approaches and iteratively updates two sets of weights. Intuitively, the algorithm chooses a search direction by looking ahead at the sequence of “fast weights" generated by another optimizer. We show that Lookahead improves the learning stability and lowers the variance of its inner optimizer with negligible computation and memory cost. We empirically demonstrate Lookahead can significantly improve the performance of SGD and Adam, even with their default hyperparameter settings on ImageNet, CIFAR- 10/100, neural machine translation, and Penn Treebank.

成功したディープニューラルネットワークの大部分は、確率的勾配降下法SGDアルゴリズムの変形を使用してトレーニングされています。 SGDを改善する最近の試みは、大きく2つのアプローチに分類できます。(1)AdaGradやAdamなどの適応学習率スキーム、および(2)ヘビーボールやネステロフ運動量などの加速スキームです。この論文では、これらの以前のアプローチに直交し、2セットの重みを繰り返し更新する新しい最適化アルゴリズムであるLookaheadを提案します。直感的に、アルゴリズムは別のオプティマイザーによって生成された「高速重み」のシーケンスを先読みして検索方向を選択します。ルックアヘッドが学習の安定性を向上させ、計算とメモリコストを無視して内部オプティマイザーの分散を低減することを示します。先読みは、ImageNet、CIFAR- 10/100、ニューラル機械翻訳、およびPenn Treebankのデフォルトのハイパーパラメーター設定を使用しても、SGDとAdamのパフォーマンスを大幅に向上させることができます。<Google翻訳

f:id:AI_ML_DL:20210501111813p:plain

こういう図表を見ると、Lookaheadよりも、SGDとADAMの性能の違いの方が気になる。さらに、SGDに対しては、Lookaheadの効果はわずかである。

 

5月2日(日)

HuBMAP:1,570 teams, 9 days to go

Lookaheadのsync_periodを、文献に示されている範囲(5, 10, 20)で変えてみた(6を12にしてみた)が、目に見える違いは、殆ど認められなかった。初期の20~30エポック以内で振動構造が認められる場合に、その周期が変化したように見えなくもないが、そう見えるのは先入観によるものかもしれない。LBスコアはわずかに下がった。

 

5月3日(月)

HuBMAP

learning_schedulerのパラメータを変更しようとしたが、PyTorchと比べると、設定(変更)できるパラメータが少ないと感じた。最近のKerasについての学習不足によるものかもしれない。

 

5月4日(火)

HuBMAP

TPUの30時間を使い果たしたので、GPUに切り替えてtraining条件を検討しようとしているが、計算速度が圧倒的に遅い(1/20-1/30)ことと、Batch_sizeの極端な違いなどのために、TPUでの計算結果をフォローするのが難しい。

Lookaheadのsync_periodの効果を調べたいのだが、GPUでは無理かな。

GPUで試していて思ったのだが、TPUでは、計算速度が速いので100エポックでも200エポックでも試すことができるので、エポック数を増やしすぎていたかもしれない。

GPUだと、12エポックでも5Foldだと6時間くらいかかる。それが、TPUだと、100エポックの5 Foldが2時間半くらいで計算できる 。

学習率の初期値、バッチサイズ、などを変えてみたが、val_dice_coefficientが0.80を超えないので、GPUでのトライは断念する。

あとは、TPUの最後の30時間をどう使うか、よりよいトレーニング条件を探すために、これまでに試みた10件弱のトレーニング結果を振り返って、検討してみよう。

 

5月5日(水)

HuBMAP:1,554 teams, 6 days to go

進展なし。

 

5月6日(木)

HuBMAP:1,564 teams, 5 days to go

 

進展なし。

土曜日の朝にTPUが使えるようになるのを待つのみ。

 

5月7日(金)

HuBMAP1,570 teams, 4 days to go

5件のpublic_test_dataのうちの1件は、糸球体の内部構造がつぶれているように見えるものが多いようで、そのために、正しく囲うのが難しく、結果としてLBスコアが上がらないということになる。このtest_dataに対して、ハンドラベリングして、train_dataに追加すれば、LBスコアは上がる。ハンドラベルのデータは公開されていて、今回のコンペではこのような手段は規則違反でないことも公式に確認されている。

train_dataとの類似性の低いデータに対して、どれだけ予測性能を上げることができるかということで争っているチームのスコアが、0.93+のレベルで、5件のうちの1件に対してハンドラベルを用いると0.934くらいになることは、ハンドラベルデータを公開した方が報告していたように思う。これ以外にも、予測困難な例(試料が薄いために、明るくてコントラストが小さかったように思う)がDiscussionで紹介されていたように思う。

5件のpublic_test_dataに対するハンドラベルや擬ラベルを用いることによって、train_dataが増えるので、public_test_dataに対する予測精度が向上するだけでなく、private_test_dataに対する予測精度も良好であることが期待される。おそらく、0.935+のチームの方々は、そうしているのであろうと思う。

自分(公開コードを借用してチューニング中)もそうだが、5件のpublic_test_dataをtrain_dataに加えることができないチームは、高いpublic_LBスコアも、高いprivate_LBスコアも、難しいのではないかと思う。

とはいえ、せっかく公開していただいた優れたコードなので、なんとか、top 10%以内に留まれるようにしたいのだが、どうしたものか。

 

<雑談>今使っているコードはKerasで書かれていて、callbacksが使えるレベルまでには理解できていないことに気付き、F. Chollet氏のDeep Learningのテキストを読み返すことにした。画像処理分野は、Chapter 5のDeep Learning for computer visionまで勉強していたが、Chapter 7のAdvanced deep-learning best practicesは、殆ど読んでいなかった。7.2に面白い記述がある。大きなデータセットに対してmodel.fit( )またはmodel.fit_generator( )を用いて何十エポックものトレーニングの計算をすることは、紙飛行機を飛ばすようなもので、一旦手を離れると飛行経路も着陸位置も制御できない。これに対して、紙飛行機ではなくドローンを飛ばせば、環境情報を把握することができ、その情報をオペレータに伝えることによって状況に即した飛行をさせることができる。それがcallbacksの機能ということである。keras.callbacksとして、ModelCheckpoint, EarlyStopping, LearningRateScheduler, ReduceLROnPlateau, CSVLoggerなどの説明が続く。

 

TPU:バッチ数を多くすることによる高速化のイメージがある。Flower Classification with TPUsというコンペで、チュートリアルモデルでは、128バッチで計算していて、Adamを用い、学習率は0.00001からスタートし、5エポックくらいで0.0004まで上げ、12エポックで0.00011くらいまで下げるというような感じであった。なんだかCyclicLRに似ている。

これがそのまま適用できるとは思わないが、次の一手が見つからなかったので、明日は、このバッチ数と学習率でどうなるか、試してみようと思う。

 

5月8日(土)

HuBMAP:1,162 teams, 3 days to go:昨日より、400チームくらい少なくなっている。何がおきたのだろうか。データセットを更新する前の状態のまま放置しているチームがいなくなったようだ。自分のsubmit回数も、そのぶん、減っている。ということで、母数が減ったのでメダル圏内に該当するチーム数が40ほど減った。それゆえ、一気にメダル圏外に押し出され、やる気が失せた。残念だな。

とはいうものの、最後までチャレンジしないと、もったいない。

バッチ数を小さくして、少し、検討してみた。ここまでは1024だったのを、512、256、128、および64について検討した。バッチ数が小さくなるほど、収束は早くなるが、train_lossとval_lossの反転が速くなり、この中では1024がベストであった。

それならばと、1024以上(2048、1536、1280など)に設定しようとしたが、エラーとなって停止した。1024が設定可能な最大値のようである。

 

5月9日(日)

HuBMAP1,172 teams, 2 days to go

明日で終わり(明後日の午前9時)だが、もうスコアアップの手立てはなくなった。

ダメもとで計算(主に、TPUを使ったtraining)しているが、テキトーにパラメータを変えても何も(val_diceやLBスコア)改善されなかった。

今から条件を検討するだけの計算資源も時間もないので、今回のコンペは、これでおしまい。

*前処理とsubmitのコードを借用し、trainingu部分をSemantic Segmentationのコードを使って種々検討したが、単独モデルでは、LB=0.883(val_dice_coefficientは0.91程度)までであった。

*最後の2週間は、LB=0.927の公開コードを借用し、最初はLB=0.920だったが、training条件を検討して、0.932になった。といっても、特段の工夫をしたわけではなく、最終的には、公開コードのオリジナルの条件を追い越すことはできなかった。

*今回のコンペは、最初の論文調査の段階で、複雑なモデルを使う必要はなさそうだという感触を得たが、途中段階では、様々なモデルの組み合わせを試し、比較的新しい論文に出てくるモデルをGitHubから借用してみたりもした。スコアへの影響が大きかったのは、augmentation(albumentation)と、KFoldであった。TTAの効果はよくわからなかった。モデルのアンサンブルは、何度かやりかけたが、結局、借用も含め、試すことができなかった。擬ラベルも試すことができなかった。

*実質、3か月以上をこのコンペに費やした。

 

5月10日(月)

HuBMAP:1,172 teams, a day to go

あと2回、seed_numberを変えて丁寧にrainingした結果を用いてinferenceした結果を提出して終わり。

2回trainingし、それを用いてinferenceした結果、いずれも、LB=0.930となった。seedを変えたので、val_dicecoefficientは様々な値となり、OOF lossやOOF dice_coefficientも2つの場合で大きく異なっていたにもかかわらず、LBスコアが同一だったことに驚いた。重要なメッセージが隠れているように思うのだが、わからない。

これまでの結果では、0.932と0.931もあるので、最終的に提出するコードをどれにすれば良いのか、さっぱりわからない 。

 

以上で、今回のコンペは終了である。

明日からは、Bristol-Myers Squibb – Molecular Translationに参加するつもりである。

 

5月11日(火)

HuBMAP:

コンペの暫定結果が出た。public_LBの順位からは、130番下がった。

今回は、大波乱がおきた。

このコンペの目標がメダル狙いだけだったら、ほんとうに、がっかりするところだが、いろいろチャレンジできておもしろかった、ということにしておこう。

最終的に選択した2件のコードは、結果としては、適切ではなかったが、最後に選ぼうかなと迷ったコードでも、200~211位相当であった。

メダル圏に最も近かったのは、126~149位相当のコードであるが、自分の基準では、選ぶ理由はなかった(最終的に選んだコードと比べても、Public_LBスコアが0.015くらい小さかったから)。

最後に、今回のHuBMAPコンペは、最後に大波乱がおきて、とてもひどいことになったが、いろいろ学べて面白かった、ということにしておこう。

 

<雑談>HuBMAPのコンペが終わって、結果を見て、参加者のコメントを見ていると、みんなの努力はなんだったんだろうな、と思ってしまう。public_test_dataとprivate_test_dataとでスコアが大きく違うって、何なんだろうと思う。こんなtest_dataを使ってコンペをするのはどうなんだろう。どちらのtest_dataに対してもハイスコアになるモデルを表彰するならまだしも、偏ったスコアが出てしまうようなtest_dataを使って、片方のteat_data(private_teat_data)のスコアだけで評価するというのはものすごく不公平で、間違ったやり方ではないだろうか。このような偏りが生じた原因は、憶測だが、当初準備したデータセットに不備が見つかったため、急遽、データセットを作り直したためではないだろうか。このような現象が生じることを想定していたのだろうか。結果として、自分への影響は殆どなかったが、Kagglerの事後コメントを読んでいると、特にトップレベルのチームの方々にとっては、ほんとうに残念なコンペだったのではないかと思う。 

 

f:id:AI_ML_DL:20210301083105p:plain

style=164 iterations=500

f:id:AI_ML_DL:20210301083248p:plain

style=164 iterations=50

f:id:AI_ML_DL:20210301083347p:plain

style=164 iterations=5