Kaggle competition: SIIM-ACR Pneumothorax Segmentation
<SOS> metric: SIIMDice Kaggle data source: siim-acr-pneumothorax-segmentation<prepare_x_and_y>
warnings.filterwarnings("ignore" )
y=Train_data['label'].values
Train=Train_data.drop('label',axis=1 ).values
Test=Test_data.values<sort_values>
!pip install git+https://github.com/qubvel/segmentation_models.pytorch > /dev/null 2>&1
def sort_dic(dic):
index=sorted(dic)
 sort_list=[]
label=[]
for i in index:
sort_list.append(dic[i])
 label.append(i)
 return sort_list,label<sort_values>
import segmentation_models_pytorch as smp
train_count=collections.Counter(y)
 train_count,labels=sort_dic(train_count )<prepare_x_and_y>
def run_length_decode(rle, height=1024, width=1024, fill_value=1):
component = np.zeros(( height, width), np.float32)
 component = component.reshape(-1)
 rle = np.array([int(s)for s in rle.strip().split(' ')])
 rle = rle.reshape(-1, 2)
 start = 0
for index, length in rle:
start = start+index
end = start+length
component[start: end] = fill_value
start = end
component = component.reshape(width, height ).T
return component
def run_length_encode(component):
component = component.T.flatten()
start = np.where(component[1:] > component[:-1])[0]+1
end = np.where(component[:-1] > component[1:])[0]+1
length = end-start
rle = []
for i in range(len(length)) :
if i == 0:
rle.extend([start[0], length[0]])
 else:
rle.extend([start[i]-end[i-1], length[i]])
 rle = ' '.join([str(r)for r in rle])
 return rle
X=Train.reshape(Train.shape[0],28,28,1)
 X_test=Test.reshape(Test.shape[0],28,28,1 )<split>
class SIIMDataset(Dataset):
def __init__(self, df, fnames, data_folder, size, mean, std, phase):
self.df = df
self.root = data_folder
self.size = size
self.mean = mean
self.std = std
self.phase = phase
self.transforms = get_transforms(phase, size, mean, std)
 self.gb = self.df.groupby('ImageId')
 self.fnames = fnames
def __getitem__(self, idx):
image_id = self.fnames[idx]
df = self.gb.get_group(image_id)
 annotations = df[' EncodedPixels'].tolist()
image_path = os.path.join(self.root, image_id + ".png")
 image = cv2.imread(image_path)
 mask = np.zeros([1024, 1024])
 if annotations[0] != ' -1':
for rle in annotations:
mask += run_length_decode(rle)
 mask =(mask >= 1 ).astype('float32')
 augmented = self.transforms(image=image, mask=mask)
 image = augmented['image']
mask = augmented['mask']
return image, mask
def __len__(self):
return len(self.fnames)
 def get_transforms(phase, size, mean, std):
list_transforms = []
if phase == "train":
list_transforms.extend(
 [
ShiftScaleRotate(
 shift_limit=0,
scale_limit=0.1,
rotate_limit=10,
p=0.5,
border_mode=cv2.BORDER_CONSTANT 
),
] 
)
 list_transforms.extend(
 [
Resize(size, size),
Normalize(mean=mean, std=std, p=1),
ToTensor() ,
] 
)
 list_trfms = Compose(list_transforms)
 return list_trfms
def provider(
 fold,
total_folds,
data_folder,
df_path,
phase,
size,
mean=None,
std=None,
batch_size=8,
num_workers=4, 
):
df_all = pd.read_csv(df_path)
 df = df_all.drop_duplicates('ImageId')
 df_with_mask = df[df[" EncodedPixels"] != " -1"]
df_with_mask['has_mask'] = 1
df_without_mask = df[df[" EncodedPixels"] == " -1"]
df_without_mask['has_mask'] = 0
df_without_mask_sampled = df_without_mask.sample(len(df_with_mask), random_state=69)
 df = pd.concat([df_with_mask, df_without_mask_sampled])
 kfold = StratifiedKFold(total_folds, shuffle=True, random_state=69)
 train_idx, val_idx = list(kfold.split(df["ImageId"], df["has_mask"])) [fold]
train_df, val_df = df.iloc[train_idx], df.iloc[val_idx]
df = train_df if phase == "train" else val_df
fnames = df['ImageId'].values
image_dataset = SIIMDataset(df_all, fnames, data_folder, size, mean, std, phase)
 dataloader = DataLoader(
 image_dataset,
batch_size=batch_size,
num_workers=num_workers,
pin_memory=True,
shuffle=True, 
)
 return dataloader
X_t, X_v, y_t, y_v = train_test_split(X, y, test_size=0.1, random_state=42 )<categorify>
sample_submission_path = '.. /input/siim-stage1/sample_submission.csv'
train_rle_path = '.. /input/siim-dicom-images/train-rle.csv'
data_folder = ".. /input/siim-png-images/input/train_png"
test_data_folder = ".. /input/siim-png-images/input/test_png"
y_train=to_categorical(y_t,10)
 y_val=to_categorical(y_v,10)
 X_train=X_t/255
X_val=X_v/255<choose_model_class>
dataloader = provider(
 fold=0,
total_folds=5,
data_folder=data_folder,
df_path=train_rle_path,
phase="train",
size=512,
mean =(0.485, 0.456, 0.406),
std =(0.229, 0.224, 0.225),
batch_size=16,
num_workers=4,
)
classifier=Sequential()
classifier.add(Conv2D(64,(3,3),input_shape=(28,28,1),activation='relu'))
classifier.add(MaxPooling2D(pool_size=(2,2),strides=(2,2)))
 classifier.add(Dropout(0.2))
classifier.add(Conv2D(64,(3,3),activation='relu'))
classifier.add(MaxPooling2D(pool_size=(2,2),strides=(2,2)))
 classifier.add(Dropout(0.2))
classifier.add(Flatten())
 classifier.add(Dense(units=256,activation='relu'))
classifier.add(Dropout(0.5))
classifier.add(Dense(units=10,activation='softmax'))<choose_model_class>
batch = next(iter(dataloader))
images, masks = batch
classifier.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['accuracy'] )<train_model>
def dice_loss(input, target):
input = torch.sigmoid(input)
 smooth = 1.0
iflat = input.view(-1)
 tflat = target.view(-1)
 intersection =(iflat * tflat ).sum()
return(( 2.0 * intersection + smooth)/(iflat.sum() + tflat.sum() + smooth))
class FocalLoss(nn.Module):
def __init__(self, gamma):
super().__init__()
self.gamma = gamma
def forward(self, input, target):
if not(target.size() == input.size()):
raise ValueError("Target size({})must be the same as input size({})" 
.format(target.size() , input.size()))
max_val =(-input ).clamp(min=0)
 loss = input - input * target + max_val + \ 
(( -max_val ).exp() +(-input - max_val ).exp() ).log()
invprobs = F.logsigmoid(-input *(target * 2.0 - 1.0))
loss =(invprobs * self.gamma ).exp() * loss
return loss.mean()
class MixedLoss(nn.Module):
def __init__(self, alpha, gamma):
super().__init__()
self.alpha = alpha
self.focal = FocalLoss(gamma)
 def forward(self, input, target):
loss = self.alpha*self.focal(input, target)- torch.log(dice_loss(input, target))
return loss.mean()
T=25
datagen = ImageDataGenerator(
 rotation_range=10,
zoom_range = 0.1,
width_shift_range=0.1,
height_shift_range=0.1, 
)
 datagen.fit(X_train)
 history = classifier.fit_generator(datagen.flow(X_train,y_train, batch_size=128),
epochs = T, validation_data =(X_val,y_val),
verbose = 2, steps_per_epoch=X_train.shape[0] // 128 
)
<predict_on_test>
def predict(X, threshold):
X_p = np.copy(X)
 preds =(X_p > threshold ).astype('uint8')
 return preds
def metric(probability, truth, threshold=0.5, reduction='none'):


batch_size = len(truth)
 with torch.no_grad() :
probability = probability.view(batch_size, -1)
 truth = truth.view(batch_size, -1)
 assert(probability.shape == truth.shape)
 p =(probability > threshold ).float()
t =(truth > 0.5 ).float()
t_sum = t.sum(-1)
 p_sum = p.sum(-1)
 neg_index = torch.nonzero(t_sum == 0)
 pos_index = torch.nonzero(t_sum >= 1)
 dice_neg =(p_sum == 0 ).float()
dice_pos = 2 *(p*t ).sum(-1)/(( p+t ).sum(-1))
dice_neg = dice_neg[neg_index]
dice_pos = dice_pos[pos_index]
dice = torch.cat([dice_pos, dice_neg])
 num_neg = len(neg_index)
 num_pos = len(pos_index)
 return dice, dice_neg, dice_pos, num_neg, num_pos
class Meter:

 def __init__(self, phase, epoch):
self.base_threshold = 0.5
self.base_dice_scores = []
self.dice_neg_scores = []
self.dice_pos_scores = []
self.iou_scores = []
def update(self, targets, outputs):
probs = torch.sigmoid(outputs)
 dice, dice_neg, dice_pos, _, _ = metric(probs, targets, self.base_threshold)
 self.base_dice_scores.extend(dice)
 self.dice_pos_scores.extend(dice_pos)
 self.dice_neg_scores.extend(dice_neg)
 preds = predict(probs, self.base_threshold)
 iou = compute_iou_batch(preds, targets, classes=[1])
 self.iou_scores.append(iou)
 def get_metrics(self):
dice = np.nanmean(self.base_dice_scores)
 dice_neg = np.nanmean(self.dice_neg_scores)
 dice_pos = np.nanmean(self.dice_pos_scores)
 dices = [dice, dice_neg, dice_pos]
iou = np.nanmean(self.iou_scores)
 return dices, iou
def epoch_log(phase, epoch, epoch_loss, meter, start):

 dices, iou = meter.get_metrics()
dice, dice_neg, dice_pos = dices
print("Loss: %0.4f | dice: %0.4f | dice_neg: %0.4f | dice_pos: %0.4f | IoU: %0.4f" %(epoch_loss, dice, dice_neg, dice_pos, iou))
return dice, iou
def compute_ious(pred, label, classes, ignore_index=255, only_present=True):

 pred[label == ignore_index] = 0
ious = []
for c in classes:
label_c = label == c
if only_present and np.sum(label_c)== 0:
ious.append(np.nan)
 continue
pred_c = pred == c
intersection = np.logical_and(pred_c, label_c ).sum()
union = np.logical_or(pred_c, label_c ).sum()
if union != 0:
ious.append(intersection / union)
 return ious if ious else [1]
def compute_iou_batch(outputs, labels, classes=None):

 ious = []
preds = np.copy(outputs)
 labels = np.array(labels)
 for pred, label in zip(preds, labels):
ious.append(np.nanmean(compute_ious(pred, label, classes)))
 iou = np.nanmean(ious)
 return iou
y_pred=classifier.predict(X_val)
 pred_label=np.argmax(y_pred,axis=1)
 actual_label=np.argmax(y_val,axis=1 )<define_variables>
model = smp.Unet("resnet34", encoder_weights="imagenet", activation=None )
error=pred_label!=y_v
missclassified_images=X_v[error]
missclassified_label=pred_label[error]
correct_label=y_v[error]<count_values>
class Trainer(object):

 def __init__(self, model):
self.fold = 1
self.total_folds = 5
self.num_workers = 6
self.batch_size = {"train": 4, "val": 4}
self.accumulation_steps = 32 // self.batch_size['train']
self.lr = 5e-4
self.num_epochs = 40
self.best_loss = float("inf")
 self.phases = ["train", "val"]
self.device = torch.device("cuda:0")
 torch.set_default_tensor_type("torch.cuda.FloatTensor")
 self.net = model
self.criterion = MixedLoss(10.0, 2.0)
 self.optimizer = optim.Adam(self.net.parameters() , lr=self.lr)
 self.scheduler = ReduceLROnPlateau(self.optimizer, mode="min", patience=3, verbose=True)
 self.net = self.net.to(self.device)
 cudnn.benchmark = True
self.dataloaders = {
phase: provider(
 fold=1,
total_folds=5,
data_folder=data_folder,
df_path=train_rle_path,
phase=phase,
size=512,
mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225),
batch_size=self.batch_size[phase],
num_workers=self.num_workers, 
)
 for phase in self.phases
}
self.losses = {phase: [] for phase in self.phases}
self.iou_scores = {phase: [] for phase in self.phases}
self.dice_scores = {phase: [] for phase in self.phases}
def forward(self, images, targets):
images = images.to(self.device)
 masks = targets.to(self.device)
 outputs = self.net(images)
 loss = self.criterion(outputs, masks)
 return loss, outputs
def iterate(self, epoch, phase):
meter = Meter(phase, epoch)
 start = time.strftime("%H:%M:%S")
 print(f"Starting epoch: {epoch} | phase: {phase} | ⏰: {start}")
 batch_size = self.batch_size[phase]
self.net.train(phase == "train")
 dataloader = self.dataloaders[phase]
running_loss = 0.0
total_batches = len(dataloader)
 self.optimizer.zero_grad()
for itr, batch in enumerate(dataloader):
images, targets = batch
loss, outputs = self.forward(images, targets)
 loss = loss / self.accumulation_steps
if phase == "train":
loss.backward()
if(itr + 1)% self.accumulation_steps == 0:
self.optimizer.step()
self.optimizer.zero_grad()
running_loss += loss.item()
outputs = outputs.detach().cpu()
meter.update(targets, outputs)
 epoch_loss =(running_loss * self.accumulation_steps)/ total_batches
dice, iou = epoch_log(phase, epoch, epoch_loss, meter, start)
 self.losses[phase].append(epoch_loss)
 self.dice_scores[phase].append(dice)
 self.iou_scores[phase].append(iou)
 torch.cuda.empty_cache()
return epoch_loss
def start(self):
for epoch in range(self.num_epochs):
self.iterate(epoch, "train")
 state = {
"epoch": epoch,
"best_loss": self.best_loss,
"state_dict": self.net.state_dict() ,
"optimizer": self.optimizer.state_dict() ,
}
val_loss = self.iterate(epoch, "val")
 self.scheduler.step(val_loss)
 if val_loss < self.best_loss:
print("******** New optimal found, saving state ********")
 state["best_loss"] = self.best_loss = val_loss
torch.save(state, "./model.pth")
 print()
pred_counts=collections.Counter(pred_label)
 labels_count=collections.Counter(y_v)
 prediction_count,labels=sort_dic(pred_counts)
 actual_count,labels=sort_dic(labels_count )<save_to_csv>
model_trainer = Trainer(model)
 model_trainer.start()
submission=pd.read_csv("/kaggle/input/submission-mnist/submission.csv")
 submission.to_csv('submission.csv', index=False )<set_options>
class TestDataset(Dataset):
def __init__(self, root, df, size, mean, std, tta=4):
self.root = root
self.size = size
self.fnames = list(df["ImageId"])
 self.num_samples = len(self.fnames)
 self.transform = Compose(
 [
Normalize(mean=mean, std=std, p=1),
Resize(size, size),
ToTensor() ,
] 
)
 def __getitem__(self, idx):
fname = self.fnames[idx]
path = os.path.join(self.root, fname + ".png")
 image = cv2.imread(path)
 images = self.transform(image=image)["image"]
return images
def __len__(self):
return self.num_samples
def post_process(probability, threshold, min_size):
mask = cv2.threshold(probability, threshold, 1, cv2.THRESH_BINARY)[1]
num_component, component = cv2.connectedComponents(mask.astype(np.uint8))
predictions = np.zeros(( 1024, 1024), np.float32)
 num = 0
for c in range(1, num_component):
p =(component == c)
 if p.sum() > min_size:
predictions[p] = 1
num += 1
return predictions, num
<load_from_csv><EOS> 
size = 512
mean =(0.485, 0.456, 0.406)
 std =(0.229, 0.224, 0.225)
 num_workers = 8
batch_size = 16
best_threshold = 0.5
min_size = 3500
device = torch.device("cuda:0")
 df = pd.read_csv(sample_submission_path)
 testset = DataLoader(
 TestDataset(test_data_folder, df, size, mean, std),
batch_size=batch_size,
shuffle=False,
num_workers=num_workers,
pin_memory=True, 
)
 model = model_trainer.net
model.eval()
state = torch.load('./model.pth', map_location=lambda storage, loc: storage)
 model.load_state_dict(state["state_dict"])
 encoded_pixels = []
for i, batch in enumerate(tqdm(testset)) :
preds = torch.sigmoid(model(batch.to(device)))
 preds = preds.detach().cpu().numpy() [:, 0, :, :]
for probability in preds:
if probability.shape !=(1024, 1024):
probability = cv2.resize(probability, dsize=(1024, 1024), interpolation=cv2.INTER_LINEAR)
 predict, num_predict = post_process(probability, best_threshold, min_size)
 if num_predict == 0:
encoded_pixels.append('-1')
 else:
r = run_length_encode(predict)
 encoded_pixels.append(r)
 df['EncodedPixels'] = encoded_pixels
df.to_csv('submission.csv', columns=['ImageId', 'EncodedPixels'], index=False )