https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection
552nd / 1249
# criterion = nn.BCEWithLogitsLoss()
criterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([.35], device=DEVICE))
data_set = []
data_set.append(
{
"train_img": [img1, img2],
"train_label": [img1_label, img2_label],
"valid_img": [img3],
"valid_label": [img3_label],
"valid_mask": [img3_mask],
}
)
data_set.append(
{
"train_img": [img1, img3],
"train_label": [img1_label, img3_label],
"valid_img": [img2],
"valid_label": [img2_label],
"valid_mask": [img2_mask],
}
)
data_set.append(
{
"train_img": [img2, img3],
"train_label": [img2_label, img3_label],
"valid_img": [img1],
"valid_label": [img1_label],
"valid_mask": [img1_mask],
}
)
src/2-5d-segmentaion-baseline-training
のEXP1とEXP2の結果比較:
[EXP 1]
-------- exp_info -----------------
best_th: 0.5, fbeta: 0.5089870965985367
Epoch 15 - avg_train_loss: 0.3655 avg_val_loss: 0.4501 time: 72s
Epoch 15 - avgScore: 0.5090
-------- exp_info -----------------
Epoch 15 - avg_train_loss: 0.3254 avg_val_loss: 0.6164 time: 62s
Epoch 15 - avgScore: 0.3941
best_th: 0.1, fbeta: 0.4037166002688188
-------- exp_info -----------------
Epoch 15 - avg_train_loss: 0.3638 avg_val_loss: 0.3804 time: 74s
Epoch 15 - avgScore: 0.4535
best_th: 0.5, fbeta: 0.5139692463216654
[EXP 2]
-------- exp_info -----------------
Epoch 45 - avg_train_loss: 0.1141 avg_val_loss: 0.5670 time: 70s
Epoch 45 - avgScore: 0.4945
best_th: 0.5, fbeta: 0.5518652693152954
-------- exp_info -----------------
Epoch 45 - avg_train_loss: 0.1026 avg_val_loss: 0.8426 time: 64s
Epoch 45 - avgScore: 0.3234
best_th: 0.15, fbeta: 0.40391082025522707
-------- exp_info -----------------
Epoch 45 - avg_train_loss: 0.1221 avg_val_loss: 0.4019 time: 70s
Epoch 45 - avgScore: 0.5515
best_th: 0.5, fbeta: 0.5769704817882036
segmentation_models_pytorch
のお陰でモデル実装は簡便だが, データオーグメンテーションはalbumentations
に習熟する必要があり難しい[1] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417496
[2] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417255
[3] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417536
[4] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417779
[5] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417642
[6] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417274
[7] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417430
[8] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417383
[9] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417361
[10] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417363
[11] https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/discussion/417281
You can expect two fragments in the hidden test set, which together are roughly the same size as a single training fragment. The sample slices available to download in the test folders are simply copied from training fragment one, but when you submit your notebook they will be substituted with the real test data.
https://www.kaggle.com/competitions/vesuvius-challenge-ink-detection/data
segmentation_models_pytorch
で使われるエンコーダはpre-trainedなのか?: A)引数encoder_weights
にimagenet
を渡すとpre-trainedになるが, None
を渡すとrandom initialized (no pre-trained)となる https://segmentation-modelspytorch.readthedocs.io/en/latest/#segmentation_models_pytorch.Unet なお上位解法ではどちらも採用されていた模様https://wandb.ai/riow1983/vesuvius-challenge-ink-detection/table?workspace=user-riow1983
[Ensemble models w/ TTA on parallel processing]
# Credit to https://www.kaggle.com/code/riow1983/2-5d-segmentaion-model-with-rotate-tta/notebook?scriptVersionId=133522169
class CustomModel(nn.Module):
def __init__(self, cfg, weight=None):
super().__init__()
self.cfg = cfg
self.encoder = smp.Unet(
encoder_name=cfg.backbone,
encoder_weights=weight,
in_channels=cfg.in_chans,
classes=cfg.target_size,
activation=None,
)
def forward(self, image):
output = self.encoder(image)
output = output.squeeze(-1)
return output
def build_model(cfg, weight="imagenet"):
print('model_name', cfg.model_name)
print('backbone', cfg.backbone)
model = CustomModel(cfg, weight)
return model
class EnsembleModel(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.ModuleList()
for fold in [1, 2, 3]:
_model = build_model(CFG, weight=None)
model_path = f'/kaggle/input/{CFG.exp_name}/vesuvius-challenge-ink-detection-models/Unet_fold{fold}_best.pth'
state = torch.load(model_path)['model']
state = torch.load(model_path)['model']
_model.load_state_dict(state)
_model.eval()
self.model.append(_model)
def forward(self,x):
output=[]
for m in self.model:
output.append(m(x))
output=torch.stack(output, dim=0).mean(0)
return output
model = EnsembleModel()
model = nn.DataParallel(model, device_ids=[0, 1])
model = model.cuda()
def TTA(x:tc.Tensor, model:nn.Module):
#x.shape=(batch,c,h,w)
if CFG.TTA:
shape=x.shape
x=[x,*[tc.rot90(x,k=i,dims=(-2,-1)) for i in range(1,4)]]
x=tc.cat(x,dim=0)
x=model(x)
x=torch.sigmoid(x)
x=x.reshape(4,shape[0],*shape[2:])
x=[tc.rot90(x[i],k=-i,dims=(-2,-1)) for i in range(4)]
x=tc.stack(x,dim=0)
return x.mean(0)
else :
x=model(x)
x=torch.sigmoid(x)
return x
# Under an inference iteration:
for step, (images) in tqdm(enumerate(test_loader), total=len(test_loader)):
images = images.cuda()
batch_size = images.size(0)
with torch.no_grad():
y_preds = TTA(images,model).cpu().numpy()
[Early stopping]
# Credit to: https://www.kaggle.com/code/yururoi/pytorch-unet-baseline-with-train-code?scriptVersionId=122620610&cellId=20
class EarlyStopping:
def __init__(self, patience=7, verbose=False, delta=0, fold=""):
self.patience = patience
self.verbose = verbose
self.counter = 0
self.best_score = None
self.early_stop = False
self.val_loss_min = np.Inf
self.delta = delta
def __call__(self, val_loss, model):
score = -val_loss
if self.best_score is None:
self.best_score = score
self.save_checkpoint(val_loss, model)
elif score < self.best_score + self.delta:
self.counter += 1
logger.info(f"EarlyStopping counter: {self.counter} out of {self.patience}")
if self.counter >= self.patience:
self.early_stop = True
else:
self.best_score = score
self.save_checkpoint(val_loss, model)
self.counter = 0
def save_checkpoint(self, val_loss, model):
"""Saves model when validation loss decrease."""
if self.verbose:
logger.info(
f"Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}). Saving model ..."
)
torch.save(model.state_dict(), CP_DIR / f"{HOST}_{NB}_checkpoint_{fold}.pt")
self.val_loss_min = val_loss