Target 199 is out of bounds.

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:00.034314Z","iopub.execute_input":"2024-03-05T02:16:00.034655Z","iopub.status.idle":"2024-03-05T02:16:00.061793Z","shell.execute_reply.started":"2024-03-05T02:16:00.034630Z","shell.execute_reply":"2024-03-05T02:16:00.060855Z"}}
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:03.863517Z","iopub.execute_input":"2024-03-05T02:16:03.863900Z","iopub.status.idle":"2024-03-05T02:16:23.791368Z","shell.execute_reply.started":"2024-03-05T02:16:03.863872Z","shell.execute_reply":"2024-03-05T02:16:23.789944Z"}}
import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms as T
import torchvision
import torch.nn.functional as F
from torch.autograd import Variable

from PIL import Image
import cv2
import albumentations as A

import time
import os
from tqdm.notebook import tqdm

!pip install -q segmentation-models-pytorch
!pip install -q torchsummary

from torchsummary import summary
import segmentation_models_pytorch as smp

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
import gc

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:39.325698Z","iopub.execute_input":"2024-03-05T02:16:39.326038Z","iopub.status.idle":"2024-03-05T02:16:39.332411Z","shell.execute_reply.started":"2024-03-05T02:16:39.326010Z","shell.execute_reply":"2024-03-05T02:16:39.331009Z"}}
import torch
if torch.cuda.is_available():
    torch.device('cuda')
    print(torch.cuda.get_device_name(0))
else:
    torch.device('cpu')
    print('cpu')

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:42.071346Z","iopub.execute_input":"2024-03-05T02:16:42.071713Z","iopub.status.idle":"2024-03-05T02:16:42.075948Z","shell.execute_reply.started":"2024-03-05T02:16:42.071686Z","shell.execute_reply":"2024-03-05T02:16:42.075072Z"}}
IMAGE_PATH = '/kaggle/input/datadanosa2/datadanosa/img/'
MASK_PATH  = '/kaggle/input/datadanosa2/datadanosa/mask/'

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:11:01.748808Z","iopub.execute_input":"2024-03-05T02:11:01.749147Z","iopub.status.idle":"2024-03-05T02:11:01.759062Z","shell.execute_reply.started":"2024-03-05T02:11:01.749124Z","shell.execute_reply":"2024-03-05T02:11:01.758209Z"}}
# !tree --filelimit=10

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:45.593234Z","iopub.execute_input":"2024-03-05T02:16:45.593586Z","iopub.status.idle":"2024-03-05T02:16:45.602535Z","shell.execute_reply.started":"2024-03-05T02:16:45.593563Z","shell.execute_reply":"2024-03-05T02:16:45.601679Z"}}
n_classes = 3

def create_df():
    name = []
    for dirname, _, filenames in os.walk(IMAGE_PATH):
        for filename in filenames:
            name.append(filename.split('.')[0])
    
    return pd.DataFrame({'id': name}, index = np.arange(0, len(name)))

df = create_df()
print('Total Images: ', len(df))

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:48.287418Z","iopub.execute_input":"2024-03-05T02:16:48.287784Z","iopub.status.idle":"2024-03-05T02:16:48.296032Z","shell.execute_reply.started":"2024-03-05T02:16:48.287760Z","shell.execute_reply":"2024-03-05T02:16:48.295019Z"}}
X_trainval, X_test = train_test_split(df['id'].values, test_size=0.1, random_state=19)
X_train, X_val = train_test_split(X_trainval, test_size=0.15, random_state=19)

print('Train Size   : ', len(X_train))
print('Val Size     : ', len(X_val))
print('Test Size    : ', len(X_test))

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:16:50.475418Z","iopub.execute_input":"2024-03-05T02:16:50.475762Z","iopub.status.idle":"2024-03-05T02:16:55.387012Z","shell.execute_reply.started":"2024-03-05T02:16:50.475738Z","shell.execute_reply":"2024-03-05T02:16:55.386336Z"}}
img = Image.open(IMAGE_PATH + df['id'][1] + '.jpg')
mask = Image.open(MASK_PATH + df['id'][1] + '.png')
print('Image Size', np.asarray(img).shape)
print('Mask Size', np.asarray(mask).shape)


plt.imshow(img)
plt.imshow(mask, alpha=0.6)
plt.title('Picture with Mask Appplied')
plt.show()

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:16.494939Z","iopub.execute_input":"2024-03-05T02:27:16.495336Z","iopub.status.idle":"2024-03-05T02:27:16.509410Z","shell.execute_reply.started":"2024-03-05T02:27:16.495311Z","shell.execute_reply":"2024-03-05T02:27:16.507935Z"}}
class WeedDataset(Dataset):
    
    def __init__(self, img_path, mask_path, X, mean, std, transform=None, patch=False):
        self.img_path = img_path
        self.mask_path = mask_path
        self.X = X
        self.transform = transform
        self.patches = patch
        self.mean = mean
        self.std = std
        
    def __len__(self):
        return len(self.X)
    
    def __getitem__(self, idx):
        img = cv2.imread(self.img_path + self.X[idx] + '.jpg')
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(self.mask_path + self.X[idx] + '.png', cv2.IMREAD_GRAYSCALE)
       
        # Fix for out-of-bounds error
        mask[mask == 255] = n_classes - 1

        if self.transform is not None:
            aug = self.transform(image=img, mask=mask)
            img = Image.fromarray(aug['image'])
            mask = aug['mask']

        if self.transform is None:
            img = Image.fromarray(img)

        t = T.Compose([T.ToTensor(), T.Normalize(self.mean, self.std)])
        img = t(img)
        mask = torch.from_numpy(mask).long()

        if self.patches:
            img, mask = self.tiles(img, mask)

        return img, mask

    
    def tiles(self, img, mask):

        img_patches = img.unfold(1, 512, 512).unfold(2, 768, 768) 
        img_patches  = img_patches.contiguous().view(3,-1, 512, 768) 
        img_patches = img_patches.permute(1,0,2,3)
        
        mask_patches = mask.unfold(0, 512, 512).unfold(1, 768, 768)
        mask_patches = mask_patches.contiguous().view(-1, 512, 768)
        
        return img_patches, mask_patches

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:19.661631Z","iopub.execute_input":"2024-03-05T02:27:19.661942Z","iopub.status.idle":"2024-03-05T02:27:19.671245Z","shell.execute_reply.started":"2024-03-05T02:27:19.661921Z","shell.execute_reply":"2024-03-05T02:27:19.669841Z"}}
mean=[0.485, 0.456, 0.406]
std=[0.229, 0.224, 0.225]

t_train = A.Compose([A.Resize(704, 1056, interpolation=cv2.INTER_NEAREST), A.HorizontalFlip(), A.VerticalFlip(), 
                     A.GridDistortion(p=0.2), A.RandomBrightnessContrast((0,0.5),(0,0.5)),
                     A.GaussNoise()])

t_val = A.Compose([A.Resize(704, 1056, interpolation=cv2.INTER_NEAREST), A.HorizontalFlip(),
                   A.GridDistortion(p=0.2)])

#datasets
train_set = WeedDataset(IMAGE_PATH, MASK_PATH, X_train, mean, std, t_train, patch=False)
val_set = WeedDataset(IMAGE_PATH, MASK_PATH, X_val, mean, std, t_val, patch=False)

#dataloader
batch_size= 3 

train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_set, batch_size=batch_size, shuffle=True)

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:22.447425Z","iopub.execute_input":"2024-03-05T02:27:22.447862Z","iopub.status.idle":"2024-03-05T02:27:22.452502Z","shell.execute_reply.started":"2024-03-05T02:27:22.447817Z","shell.execute_reply":"2024-03-05T02:27:22.451389Z"}}
decoder = smp.FPN
encoder = 'efficientnet-b0'

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:24.053185Z","iopub.execute_input":"2024-03-05T02:27:24.053550Z","iopub.status.idle":"2024-03-05T02:27:24.181866Z","shell.execute_reply.started":"2024-03-05T02:27:24.053522Z","shell.execute_reply":"2024-03-05T02:27:24.180992Z"}}
model = decoder(encoder, encoder_weights='imagenet', classes=n_classes, activation=None, encoder_depth=5, 
#                 decoder_channels=[256, 128, 64, 32, 16], 
                in_channels=3
               )

# %% [markdown]
# 

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:26.341994Z","iopub.execute_input":"2024-03-05T02:27:26.342325Z","iopub.status.idle":"2024-03-05T02:27:26.353123Z","shell.execute_reply.started":"2024-03-05T02:27:26.342301Z","shell.execute_reply":"2024-03-05T02:27:26.351746Z"}}
model

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:31.447837Z","iopub.execute_input":"2024-03-05T02:27:31.448457Z","iopub.status.idle":"2024-03-05T02:27:31.455782Z","shell.execute_reply.started":"2024-03-05T02:27:31.448424Z","shell.execute_reply":"2024-03-05T02:27:31.454320Z"}}
def pixel_accuracy(output, mask):
    with torch.no_grad():
        output = torch.argmax(F.softmax(output, dim=1), dim=1)
        correct = torch.eq(output, mask).int()
        accuracy = float(correct.sum()) / float(correct.numel())
    return accuracy

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:33.750355Z","iopub.execute_input":"2024-03-05T02:27:33.751403Z","iopub.status.idle":"2024-03-05T02:27:33.759878Z","shell.execute_reply.started":"2024-03-05T02:27:33.751375Z","shell.execute_reply":"2024-03-05T02:27:33.758563Z"}}
def mIoU(pred_mask, mask, smooth=1e-10, n_classes=n_classes):
    with torch.no_grad():
        pred_mask = F.softmax(pred_mask, dim=1)
        pred_mask = torch.argmax(pred_mask, dim=1)
        pred_mask = pred_mask.contiguous().view(-1)
        mask = mask.contiguous().view(-1)

        iou_per_class = []
        for clas in range(0, n_classes): #loop per pixel class
            true_class = pred_mask == clas
            true_label = mask == clas

            if true_label.long().sum().item() == 0: #no exist label in this loop
                iou_per_class.append(np.nan)
            else:
                intersect = torch.logical_and(true_class, true_label).sum().float().item()
                union = torch.logical_or(true_class, true_label).sum().float().item()

                iou = (intersect + smooth) / (union +smooth)
                iou_per_class.append(iou)
        return np.nanmean(iou_per_class)

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:36.391079Z","iopub.execute_input":"2024-03-05T02:27:36.391397Z","iopub.status.idle":"2024-03-05T02:27:36.411165Z","shell.execute_reply.started":"2024-03-05T02:27:36.391373Z","shell.execute_reply":"2024-03-05T02:27:36.409520Z"}}
def get_lr(optimizer):
    for param_group in optimizer.param_groups:
        return param_group['lr']

def fit(epochs, model, train_loader, val_loader, criterion, optimizer, scheduler, patch=False):
    torch.cuda.empty_cache()
    train_losses = []
    test_losses = []
    val_iou = []; val_acc = []
    train_iou = []; train_acc = []
    lrs = []
    min_loss = np.inf
    decrease = 1 ; not_improve=0

    model.to(device)
    fit_time = time.time()
    for e in range(epochs):
        since = time.time()
        running_loss = 0
        iou_score = 0
        accuracy = 0
        #training loop
        model.train()
        for i, data in enumerate(tqdm(train_loader)):
            #training phase
            image_tiles, mask_tiles = data
            if patch:
                bs, n_tiles, c, h, w = image_tiles.size()

                image_tiles = image_tiles.view(-1,c, h, w)
                mask_tiles = mask_tiles.view(-1, h, w)
            
            image = image_tiles.to(device); mask = mask_tiles.to(device);
            #forward
            output = model(image)
            loss = criterion(output, mask)
            #evaluation metrics
            iou_score += mIoU(output, mask)
            accuracy += pixel_accuracy(output, mask)
            #backward
            loss.backward()
            optimizer.step() #update weight          
            optimizer.zero_grad() #reset gradient
            
            #step the learning rate
            lrs.append(get_lr(optimizer))
            scheduler.step() 
            
            running_loss += loss.item()
            
        else:
            model.eval()
            test_loss = 0
            test_accuracy = 0
            val_iou_score = 0
            #validation loop
            with torch.no_grad():
                for i, data in enumerate(tqdm(val_loader)):
                    #reshape to 9 patches from single image, delete batch size
                    image_tiles, mask_tiles = data

                    if patch:
                        bs, n_tiles, c, h, w = image_tiles.size()

                        image_tiles = image_tiles.view(-1,c, h, w)
                        mask_tiles = mask_tiles.view(-1, h, w)
                    
                    image = image_tiles.to(device); mask = mask_tiles.to(device);
                    output = model(image)
                    #evaluation metrics
                    val_iou_score +=  mIoU(output, mask)
                    test_accuracy += pixel_accuracy(output, mask)
                    #loss
                    loss = criterion(output, mask)                                  
                    test_loss += loss.item()
            
            #calculatio mean for each batch
            train_losses.append(running_loss/len(train_loader))
            test_losses.append(test_loss/len(val_loader))


            if min_loss > (test_loss/len(val_loader)):
                print('Loss Decreasing.. {:.3f} >> {:.3f} '.format(min_loss, (test_loss/len(val_loader))))
                min_loss = (test_loss/len(val_loader))
                decrease += 1
                if decrease % 5 == 0:
                    print('saving model...')
                    torch.save(model, '/kaggle/working/model_mIoU-{:.3f}.pth'.format(val_iou_score/len(val_loader)))
                    

            if (test_loss/len(val_loader)) > min_loss:
                not_improve += 1
                min_loss = (test_loss/len(val_loader))
                print(f'Loss Not Decrease for {not_improve} time')
                if not_improve == 7:
                    print('Loss not decrease for 7 times, Stop Training')
                    break
            
            #iou
            val_iou.append(val_iou_score/len(val_loader))
            train_iou.append(iou_score/len(train_loader))
            train_acc.append(accuracy/len(train_loader))
            val_acc.append(test_accuracy/ len(val_loader))
            print("Epoch:{}/{}..".format(e+1, epochs),
                  "Train Loss: {:.3f}..".format(running_loss/len(train_loader)),
                  "Val Loss: {:.3f}..".format(test_loss/len(val_loader)),
                  "Train mIoU:{:.3f}..".format(iou_score/len(train_loader)),
                  "Val mIoU: {:.3f}..".format(val_iou_score/len(val_loader)),
                  "Train Acc:{:.3f}..".format(accuracy/len(train_loader)),
                  "Val Acc:{:.3f}..".format(test_accuracy/len(val_loader)),
                  "Time: {:.2f}m".format((time.time()-since)/60))
        
    history = {'train_loss' : train_losses, 'val_loss': test_losses,
               'train_miou' :train_iou, 'val_miou':val_iou,
               'train_acc' :train_acc, 'val_acc':val_acc,
               'lrs': lrs}
    print('Total time: {:.2f} m' .format((time.time()- fit_time)/60))
    return history

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:40.485440Z","iopub.execute_input":"2024-03-05T02:27:40.485780Z","iopub.status.idle":"2024-03-05T02:27:40.914137Z","shell.execute_reply.started":"2024-03-05T02:27:40.485757Z","shell.execute_reply":"2024-03-05T02:27:40.913251Z"}}
gc.collect()

# %% [code] {"jupyter":{"outputs_hidden":false}}


# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-03-05T02:27:42.527525Z","iopub.execute_input":"2024-03-05T02:27:42.527861Z","iopub.status.idle":"2024-03-05T02:27:46.777056Z","shell.execute_reply.started":"2024-03-05T02:27:42.527838Z","shell.execute_reply":"2024-03-05T02:27:46.775501Z"}}
max_lr = 1e-3
epoch = 20
weight_decay = 1e-4

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=max_lr, weight_decay=weight_decay)
sched = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr, epochs=epoch,
                                            steps_per_epoch=len(train_loader))

history = fit(epoch, model, train_loader, val_loader, criterion, optimizer, sched)


i tried: mask[mask == 255] = n_classes - 1