Kaggle competition: RANZCR CLiP - Catheter and Line Position Challenge
<SOS> metric: MCAUC Kaggle data source: ranzcr-clip-catheter-and-line-position-challenge<choose_model_class>
batch_size = 1
image_size = 512
tta = True
submit = True
enet_type = ['resnet200d'] * 5
model_path = ['.. /input/resnet200d-baseline-benchmark-public/resnet200d_fold0_cv953.pth',
'.. /input/resnet200d-baseline-benchmark-public/resnet200d_fold1_cv955.pth',
'.. /input/resnet200d-baseline-benchmark-public/resnet200d_fold2_cv955.pth',
'.. /input/resnet200d-baseline-benchmark-public/resnet200d_fold3_cv957.pth',
'.. /input/resnet200d-baseline-benchmark-public/resnet200d_fold4_cv954.pth']
model.compile(
 loss="categorical_crossentropy",
optimizer=Adam(lr=0.001),
metrics=['accuracy'] )<train_model>
sys.path.append('.. /input/pytorch-image-models/pytorch-image-models-master')
 sys.path.append('.. /input/timm-pytorch-image-models/pytorch-image-models-master')
 DEBUG = False
%matplotlib inline
device = torch.device('cuda')if not DEBUG else torch.device('cpu' )
model.fit(images_train,label_train, validation_data=(images_validation,label_validation),batch_size=64, epochs=50,callbacks=callbacks )<predict_on_test>
class RANZCRResNet200D(nn.Module):
def __init__(self, model_name='resnet200d', out_dim=11, pretrained=False):
super().__init__()
self.model = timm.create_model(model_name, pretrained=False)
 n_features = self.model.fc.in_features
self.model.global_pool = nn.Identity()
self.model.fc = nn.Identity()
self.pooling = nn.AdaptiveAvgPool2d(1)
 self.fc = nn.Linear(n_features, out_dim)
 def forward(self, x):
bs = x.size(0)
 features = self.model(x)
 pooled_features = self.pooling(features ).view(bs, -1)
 output = self.fc(pooled_features)
 return output
test_prediction = model.predict(images_test )<define_variables>
transforms_test = albumentations.Compose([
Resize(image_size, image_size),
Normalize(
 mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225], 
),
ToTensorV2()
] )
test_labels = []
for i in range(len(test_prediction)) :
test_labels.append(np.argmax(test_prediction[i]))<prepare_output>
test = pd.read_csv('.. /input/ranzcr-clip-catheter-line-classification/sample_submission.csv')
 test['file_path'] = test.StudyInstanceUID.apply(lambda x: os.path.join('.. /input/ranzcr-clip-catheter-line-classification/test', f'{x}.jpg'))
target_cols = test.iloc[:, 1:12].columns.tolist()
test_dataset = RANZCRDataset(test, 'test', transform=transforms_test)
 test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=24 )
for i in range(len(sample_submission)) :
sample_submission.iloc[i,1]=test_labels[i]<predict_on_test>
if submit:
test_preds_1 = []
for i in range(len(enet_type)) :
if enet_type[i] == 'resnet200d':
print('resnet200d loaded')
 model = RANZCRResNet200D(enet_type[i], out_dim=len(target_cols))
model = model.to(device)
 model.load_state_dict(torch.load(model_path[i], map_location='cuda:0'))
if tta:
test_preds_1 += [tta_inference_func(test_loader)]
else:
test_preds_1 += [inference_func(test_loader)]
np.argmax(model.predict(images_test[2].reshape(1,28,28,1)) )<save_to_csv>
submission = pd.read_csv('.. /input/ranzcr-clip-catheter-line-classification/sample_submission.csv')
 submission[target_cols] = np.mean(test_preds_1, axis=0 )
sample_submission.to_csv("submission.csv",index=False )<set_options>
from pathlib import Path
import random
from scipy.sparse import coo_matrix
import gc
from joblib import Parallel, delayed
import typing as tp
from torch.utils import data
%matplotlib inline 
<import_modules>
ROOT = Path.cwd().parent
INPUT = ROOT / "input"
OUTPUT = ROOT / "output"
DATA = INPUT / "ranzcr-clip-catheter-line-classification"
TRAIN = DATA / "train"
TEST = DATA / "test"
TRAINED_MODEL = INPUT / "ranzcr-clip-weights-for-multi-head-model-v2"
TMP = ROOT / "tmp"
TMP.mkdir(exist_ok=True)
 RANDAM_SEED = 1086
N_CLASSES = 11
FOLDS = [0, 1, 2, 3, 4]
N_FOLD = len(FOLDS)
 IMAGE_SIZE =(512, 512)
 CONVERT_TO_RANK = False
FAST_COMMIT = False
CLASSES = [
'ETT - Abnormal',
'ETT - Borderline',
'ETT - Normal',
'NGT - Abnormal',
'NGT - Borderline',
'NGT - Incompletely Imaged',
'NGT - Normal',
'CVC - Abnormal',
'CVC - Borderline',
'CVC - Normal',
'Swan Ganz Catheter Present'
]
<load_from_csv>
for p in DATA.iterdir() :
print(p.name)
 train = pd.read_csv(DATA / "train.csv")
 smpl_sub = pd.read_csv(DATA / "sample_submission.csv" )
train = pd.read_csv('.. /input/Kannada-MNIST/train.csv')
 test = pd.read_csv('.. /input/Kannada-MNIST/test.csv' )<count_values>
if FAST_COMMIT and len(smpl_sub)== 3582:
smpl_sub = smpl_sub.iloc[:64 * 3].reset_index(drop=True )
train.label.value_counts()<prepare_x_and_y>
def multi_label_stratified_group_k_fold(label_arr: np.array, gid_arr: np.array, n_fold: int, seed: int=42):

 np.random.seed(seed)
 random.seed(seed)
 start_time = time.time()
n_train, n_class = label_arr.shape
gid_unique = sorted(set(gid_arr))
n_group = len(gid_unique)
 gid2aid = dict(zip(gid_unique, range(n_group)))
 aid_arr = np.vectorize(lambda x: gid2aid[x] )(gid_arr)
 cnts_by_class = label_arr.sum(axis=0)
 col, row = np.array(sorted(enumerate(aid_arr), key=lambda x: x[1])).T
cnts_by_group = coo_matrix(
(np.ones(len(label_arr)) ,(row, col))
).dot(coo_matrix(label_arr)).toarray().astype(int)
 del col
del row
cnts_by_fold = np.zeros(( n_fold, n_class), int)
 groups_by_fold = [[] for fid in range(n_fold)]
group_and_cnts = list(enumerate(cnts_by_group))
np.random.shuffle(group_and_cnts)
 print("finished preparation", time.time() - start_time)
 for aid, cnt_by_g in sorted(group_and_cnts, key=lambda x: -np.std(x[1])) :
best_fold = None
min_eval = None
for fid in range(n_fold):
cnts_by_fold[fid] += cnt_by_g
fold_eval =(cnts_by_fold / cnts_by_class ).std(axis=0 ).mean()
cnts_by_fold[fid] -= cnt_by_g
if min_eval is None or fold_eval < min_eval:
min_eval = fold_eval
best_fold = fid
cnts_by_fold[best_fold] += cnt_by_g
groups_by_fold[best_fold].append(aid)
 print("finished assignment.", time.time() - start_time)
 gc.collect()
idx_arr = np.arange(n_train)
 for fid in range(n_fold):
val_groups = groups_by_fold[fid]
val_indexs_bool = np.isin(aid_arr, val_groups)
 train_indexs = idx_arr[~val_indexs_bool]
val_indexs = idx_arr[val_indexs_bool]
print("[fold {}]".format(fid), end=" ")
 print("n_group:(train, val)=({}, {})".format(n_group - len(val_groups), len(val_groups)) , end=" ")
 print("n_sample:(train, val)=({}, {})".format(len(train_indexs), len(val_indexs)))
 yield train_indexs, val_indexs
X_train = train.drop(['label'],axis=1)
 Y_train = train['label']<feature_engineering>
label_arr = train[CLASSES].values
group_id = train.PatientID.values
train_val_indexs = list(
 multi_label_stratified_group_k_fold(label_arr, group_id, N_FOLD, RANDAM_SEED))
X_train = X_train/255.0<define_variables>
train["fold"] = -1
for fold_id,(trn_idx, val_idx)in enumerate(train_val_indexs):
train.loc[val_idx, "fold"] = fold_id
train.groupby("fold")[CLASSES].sum()
testids = test['id']<drop_column>
def resize_images(img_id, input_dir, output_dir, resize_to=(512, 512), ext="png"):
img_path = input_dir / f"{img_id}.jpg"
save_path = output_dir / f"{img_id}.{ext}"
img = cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE)
 img = cv2.resize(img, resize_to)
 cv2.imwrite(str(save_path), img,)
 TEST_RESIZED = TMP / "test_{0}x{1}".format(*IMAGE_SIZE)
 TEST_RESIZED.mkdir(exist_ok=True)
 TEST_RESIZED
_ = Parallel(n_jobs=2, verbose=5 )([
delayed(resize_images )(img_id, TEST, TEST_RESIZED, IMAGE_SIZE, "png")
 for img_id in smpl_sub.StudyInstanceUID.values
] )
test.drop(['id'],axis=1,inplace=True )<feature_engineering>
def get_activation(activ_name: str="relu"):

 act_dict = {
"relu": nn.ReLU(inplace=True),
"tanh": nn.Tanh() ,
"sigmoid": nn.Sigmoid() ,
"identity": nn.Identity() }
if activ_name in act_dict:
return act_dict[activ_name]
else:
raise NotImplementedError
class Conv2dBNActiv(nn.Module):

 def __init__(
 self, in_channels: int, out_channels: int,
kernel_size: int, stride: int=1, padding: int=0,
bias: bool=False, use_bn: bool=True, activ: str="relu" 
):

 super(Conv2dBNActiv, self ).__init__()
layers = []
layers.append(nn.Conv2d(
 in_channels, out_channels,
kernel_size, stride, padding, bias=bias))
if use_bn:
layers.append(nn.BatchNorm2d(out_channels))
layers.append(get_activation(activ))
self.layers = nn.Sequential(*layers)
 def forward(self, x):

 return self.layers(x)
 class SSEBlock(nn.Module):

 def __init__(self, in_channels: int):

 super(SSEBlock, self ).__init__()
self.channel_squeeze = nn.Conv2d(
 in_channels=in_channels, out_channels=1,
kernel_size=1, stride=1, padding=0, bias=False)
 self.sigmoid = nn.Sigmoid()
def forward(self, x):

 h = self.sigmoid(self.channel_squeeze(x))
return x * h
class SpatialAttentionBlock(nn.Module):

 def __init__(
 self, in_channels: int,
out_channels_list: tp.List[int], 
):

 super(SpatialAttentionBlock, self ).__init__()
self.n_layers = len(out_channels_list)
 channels_list = [in_channels] + out_channels_list
assert self.n_layers > 0
assert channels_list[-1] == 1
for i in range(self.n_layers - 1):
in_chs, out_chs = channels_list[i: i + 2]
layer = Conv2dBNActiv(in_chs, out_chs, 3, 1, 1, activ="relu")
 setattr(self, f"conv{i + 1}", layer)
 in_chs, out_chs = channels_list[-2:]
layer = Conv2dBNActiv(in_chs, out_chs, 3, 1, 1, activ="sigmoid")
 setattr(self, f"conv{self.n_layers}", layer)
 def forward(self, x):

 h = x
for i in range(self.n_layers):
h = getattr(self, f"conv{i + 1}" )(h)
 h = h * x
return h
test = test/255.0<train_model>
class MultiHeadResNet200D(nn.Module):
def __init__(
 self, out_dims_head: tp.List[int]=[3, 4, 3, 1], pretrained=False 
):

 self.base_name = "resnet200d_320"
self.n_heads = len(out_dims_head)
 super(MultiHeadResNet200D, self ).__init__()
base_model = timm.create_model(
 self.base_name, num_classes=sum(out_dims_head), pretrained=False)
 in_features = base_model.num_features
if pretrained:
pretrained_model_path = '.. /input/startingpointschestx/resnet200d_320_chestx.pth'
state_dict = dict()
for k, v in torch.load(pretrained_model_path, map_location='cpu')["model"].items() :
if k[:6] == "model.":
k = k.replace("model.", "")
 state_dict[k] = v
base_model.load_state_dict(state_dict)
 base_model.reset_classifier(0, '')
 self.backbone = base_model
for i, out_dim in enumerate(out_dims_head):
layer_name = f"head_{i}"
layer = nn.Sequential(
 SpatialAttentionBlock(in_features, [64, 32, 16, 1]),
nn.AdaptiveAvgPool2d(output_size=1),
nn.Flatten(start_dim=1),
nn.Linear(in_features, in_features),
nn.ReLU(inplace=True),
nn.Dropout(0.5),
nn.Linear(in_features, out_dim))
setattr(self, layer_name, layer)
 def forward(self, x):

 h = self.backbone(x)
 hs = [
getattr(self, f"head_{i}" )(h)for i in range(self.n_heads)]
y = torch.cat(hs, axis=1)
 return y
m = MultiHeadResNet200D([3, 4, 3, 1], False)
 m = m.eval()
x = torch.rand(1, 3, 256, 256)
 with torch.no_grad() :
y = m(x)
 print("[forward test]")
 print("input:\t{}
output:\t{}".format(x.shape, y.shape))
del m; del x; del y
gc.collect()
X_train = X_train.values.reshape(-1,28,28,1)
 test = test.values.reshape(-1,28,28,1 )<categorify>
class LabeledImageDataset(data.Dataset):

 def __init__(
 self,
file_list: tp.List[
tp.Tuple[tp.Union[str, Path], tp.Union[int, float, np.ndarray]]],
transform_list: tp.List[tp.Dict], 
):

 self.file_list = file_list
self.transform = ImageTransformForCls(transform_list)
 def __len__(self):

 return len(self.file_list)
 def __getitem__(self, index):

 img_path, label = self.file_list[index]
img = self._read_image_as_array(img_path)
 img, label = self.transform(( img, label))
return img, label
def _read_image_as_array(self, path: str):

 img_arr = cv2.imread(str(path))
img_arr = cv2.cvtColor(img_arr, cv2.COLOR_BGR2RGB)
 return img_arr
Y_train = to_categorical(Y_train,num_classes=10 )<split>
def get_dataloaders_for_inference(
 file_list: tp.List[tp.List], batch_size=64, 
):

 dataset = LabeledImageDataset(
 file_list,
transform_list=[
["Normalize", {
"always_apply": True, "max_pixel_value": 255.0,
"mean": ["0.4887381077884414"], "std": ["0.23064819430546407"]}],
["ToTensorV2", {"always_apply": True}],
])
 loader = data.DataLoader(
 dataset,
batch_size=batch_size, shuffle=False,
num_workers=2, pin_memory=True,
drop_last=False)
 return loader
X_train,X_test,Y_train,Y_test = train_test_split(X_train,Y_train,random_state=2,test_size=0.1 )<choose_model_class>
class ImageTransformBase:

 def __init__(self, data_augmentations: tp.List[tp.Tuple[str, tp.Dict]]):

 augmentations_list = [
self._get_augmentation(aug_name )(**params)
 for aug_name, params in data_augmentations]
self.data_aug = albumentations.Compose(augmentations_list)
 def __call__(self, pair: tp.Tuple[np.ndarray])-> tp.Tuple[np.ndarray]:

 raise NotImplementedError
def _get_augmentation(self, aug_name: str)-> tp.Tuple[ImageOnlyTransform, DualTransform]:

 if hasattr(albumentations, aug_name):
return getattr(albumentations, aug_name)
 else:
return eval(aug_name)
 class ImageTransformForCls(ImageTransformBase):

 def __init__(self, data_augmentations: tp.List[tp.Tuple[str, tp.Dict]]):

 super(ImageTransformForCls, self ).__init__(data_augmentations)
 def __call__(self, in_arrs: tp.Tuple[np.ndarray])-> tp.Tuple[np.ndarray]:

 img, label = in_arrs
augmented = self.data_aug(image=img)
 img = augmented["image"]
return img, label
model = Sequential()
model.add(Conv2D(filters=32,kernel_size=(5,5),padding='Same',activation='relu',input_shape=(28,28,1)))
 model.add(Conv2D(filters=32,kernel_size=(5,5),padding='Same',activation='relu'))
model.add(BatchNormalization(momentum=.15))
model.add(MaxPool2D(pool_size=(2,2)))
 model.add(Dropout(0.25))
model.add(Conv2D(filters=64,kernel_size=(3,3),padding='Same',activation='relu',))
model.add(Conv2D(filters=64,kernel_size=(3,3),padding='Same',activation='relu',))
model.add(BatchNormalization(momentum=.15))
model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
 model.add(Dropout(0.5))
model.add(Flatten())
 model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10,activation='softmax'))<choose_model_class>
def load_setting_file(path: str):

 with open(path)as f:
settings = yaml.safe_load(f)
 return settings
def set_random_seed(seed: int = 42, deterministic: bool = False):

 random.seed(seed)
 np.random.seed(seed)
 os.environ["PYTHONHASHSEED"] = str(seed)
 torch.manual_seed(seed)
 torch.cuda.manual_seed(seed)
 torch.backends.cudnn.deterministic = deterministic
def run_inference_loop(stgs, model, loader, device):
model.to(device)
 model.eval()
pred_list = []
with torch.no_grad() :
for x, t in tqdm(loader):
y = model(x.to(device))
pred_list.append(y.sigmoid().detach().cpu().numpy())
 pred_arr = np.concatenate(pred_list)
 del pred_list
return pred_arr
optimizer = RMSprop(lr=0.001,rho=0.9,epsilon=1e-08,decay=0.0 )<choose_model_class>
if not torch.cuda.is_available() :
device = torch.device("cpu")
 else:
device = torch.device("cuda")
 print(device )
model.compile(optimizer=optimizer,loss='categorical_crossentropy',metrics=['accuracy'] )<choose_model_class>
model_dir = TRAINED_MODEL
test_dir = TEST_RESIZED
test_file_list = [ 
(test_dir / f"{img_id}.png", [-1] * 11)
 for img_id in smpl_sub["StudyInstanceUID"].values]
test_loader = get_dataloaders_for_inference(test_file_list, batch_size=64)
 test_preds_arr = np.zeros(( N_FOLD, len(smpl_sub), N_CLASSES))
for fold_id in FOLDS:
print(f"[fold {fold_id}]")
 stgs = load_setting_file(model_dir / f"fold{fold_id}" / "settings.yml")
 stgs["model"]["params"]["pretrained"] = False
model = MultiHeadResNet200D(**stgs["model"]["params"])
 model_path = model_dir / f"best_model_fold{fold_id}.pth"
model.load_state_dict(torch.load(model_path, map_location=device))
test_pred = run_inference_loop(stgs, model, test_loader, device)
 test_preds_arr[fold_id] = test_pred
del model
torch.cuda.empty_cache()
gc.collect()
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',patience=3,verbose=1,factor=0.5,min_lir=0.00001 )<define_variables>
sub = smpl_sub.copy()
sub[CLASSES] = test_preds_arr.mean(axis=0 )
epochs=30
batch_size=86<choose_model_class>
Final_Submission = smpl_sub.copy()
Final_Submission[CLASSES] =.50 * sub[CLASSES] +.50 * submission[CLASSES]
<train_model><EOS> 
Final_Submission.to_csv("submission.csv", index=False )