Kaggle competition: Prostate cANcer graDe Assessment (PANDA) Challenge
<SOS> metric: QuadraticWeightedKappa Kaggle data source: prostate-cancer-grade-assessment-(panda)-challenge<merge>
TEST_IMAGES_PATH = '.. /input/prostate-cancer-grade-assessment/test_images'
TEST_CSV_PATH = '.. /input/prostate-cancer-grade-assessment/test.csv'
TRAIN_IMAGES_PATH = '.. /input/prostate-cancer-grade-assessment/train_images'
TRAIN_CSV_PATH = '.. /input/prostate-cancer-grade-assessment/train.csv'
SAMPLE_CSV_PATH = '.. /input/prostate-cancer-grade-assessment/sample_submission.csv'
MODEL_WEIGHTS_PATH = '.. /input/pandamodelweights/cv-{fold}-model-29.pth.tar'
stores = pd.merge(stores, tmp, how='left', on=['air_store_id','dow'] )<merge>
if os.path.exists(TEST_IMAGES_PATH):
TEST_DF = pd.read_csv(TEST_CSV_PATH)
 else:
TEST_DF = pd.read_csv(TRAIN_CSV_PATH)[:32]
TEST_DF['opt_angle'] = -1
stores = pd.merge(stores, data['as'], how='left', on=['air_store_id'] )<categorify>
def load_model_state(model, checkpoint_path):
device = torch.device('cpu')
 state_dict = torch.load(checkpoint_path, map_location=device)
 model.load_state_dict(non_parallel_state_dict(state_dict['model_state_dict']))
def non_parallel_state_dict(state_dict):
new_state_dict = OrderedDict()
for k, v in state_dict.items() :
k = k[k.startswith('module.')and len('module.'):]
new_state_dict[k] = v
return new_state_dict
lbl = preprocessing.LabelEncoder()
stores['air_genre_name'] = lbl.fit_transform(stores['air_genre_name'])
 stores['air_area_name'] = lbl.fit_transform(stores['air_area_name'] )<data_type_conversions>
class L_Conv2d(nn.Conv2d):
def __init__(self, in_channels, out_channels, kernel_size, stride=1,
padding=0, dilation=1, groups=1, bias=True):
super(L_Conv2d, self ).__init__(in_channels, out_channels, kernel_size, stride,
padding, dilation, groups, bias)
 def forward(self, x):
weight = self.weight
weight_mean = weight.mean(dim=1, keepdim=True ).mean(dim=2,
keepdim=True ).mean(dim=3, keepdim=True)
 weight = weight - weight_mean
std = weight.view(weight.size(0), -1 ).std(dim=1 ).view(-1, 1, 1, 1)+ 1e-5
weight = weight / std.expand_as(weight)
 return F.conv2d(x, weight, self.bias, self.stride,
self.padding, self.dilation, self.groups)
 def L_BatchNorm2d(num_features):
return nn.GroupNorm(num_channels=num_features, num_groups=32 )
data['hol']['visit_date'] = pd.to_datetime(data['hol']['visit_date'])
 data['hol']['day_of_week'] = lbl.fit_transform(data['hol']['day_of_week'])
 data['hol']['visit_date'] = data['hol']['visit_date'].dt.date<merge>

 class Bottleneck(nn.Module):

 expansion = 4
def __init__(self, inplanes, planes, baseWidth, cardinality, stride=1, downsample=None):

 super(Bottleneck, self ).__init__()
D = int(math.floor(planes *(baseWidth / 64)))
 C = cardinality
self.conv1 = L_Conv2d(inplanes, D * C, kernel_size=1, stride=1, padding=0, bias=False)
 self.bn1 = L_BatchNorm2d(D * C)
 self.conv2 = L_Conv2d(D * C, D * C, kernel_size=3, stride=stride, padding=1, groups=C, bias=False)
 self.bn2 = L_BatchNorm2d(D * C)
 self.conv3 = L_Conv2d(D * C, planes * 4, kernel_size=1, stride=1, padding=0, bias=False)
 self.bn3 = L_BatchNorm2d(planes * 4)
 self.relu = nn.ReLU(inplace=True)
 self.downsample = downsample
def forward(self, x):
residual = x
out = self.conv1(x)
 out = self.bn1(out)
 out = self.relu(out)
 out = self.conv2(out)
 out = self.bn2(out)
 out = self.relu(out)
 out = self.conv3(out)
 out = self.bn3(out)
 if self.downsample is not None:
residual = self.downsample(x)
 out += residual
out = self.relu(out)
 return out
class ResNeXt(nn.Module):

 def __init__(self, baseWidth, cardinality, layers, num_classes):

 super(ResNeXt, self ).__init__()
block = Bottleneck
self.cardinality = cardinality
self.baseWidth = baseWidth
self.num_classes = num_classes
self.inplanes = 64
self.output_size = 64
self.conv1 = L_Conv2d(3, 64, 7, 2, 3, bias=False)
 self.bn1 = L_BatchNorm2d(64)
 self.relu = nn.ReLU(inplace=True)
 self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
 self.layer1 = self._make_layer(block, 64, layers[0])
 self.layer2 = self._make_layer(block, 128, layers[1], 2)
 self.layer3 = self._make_layer(block, 256, layers[2], 2)
 self.layer4 = self._make_layer(block, 512, layers[3], 2)
 self.avgpool = nn.AdaptiveAvgPool2d(1)
 self.fc = nn.Linear(512 * block.expansion, num_classes)
 self.multiple_devices = False
for m in self.modules() :
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2./ n))
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
 m.bias.data.zero_()
def place_on_multiple_devices(self, device_0, device_1):
self.conv1.to(device_0)
 self.bn1.to(device_0)
 self.relu.to(device_0)
 self.maxpool1.to(device_0)
 self.layer1.to(device_0)
 self.layer2.to(device_1)
 self.layer3.to(device_1)
 self.layer4.to(device_1)
 self.avgpool.to(device_1)
 self.fc.to(device_1)
 self.multiple_devices = True
def load_pretrained_weights(self, state_dict):
state_dict.pop('fc.weight')
 state_dict.pop('fc.bias')
 res = self.load_state_dict(state_dict, strict=False)
 assert set(res.missing_keys)== set(['fc.weight', 'fc.bias']), 'issue loading pretrained weights'
print('Loaded pretrained weights for ResNeXt')
 def _make_layer(self, block, planes, blocks, stride=1):

 downsample = None
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
 L_Conv2d(self.inplanes, planes * block.expansion,
kernel_size=1, stride=stride, bias=False),
L_BatchNorm2d(planes * block.expansion), 
)
 layers = []
layers.append(block(self.inplanes, planes, self.baseWidth, self.cardinality, stride, downsample))
self.inplanes = planes * block.expansion
for i in range(1, blocks):
layers.append(block(self.inplanes, planes, self.baseWidth, self.cardinality))
return nn.Sequential(*layers)
 def forward(self, x):
x = self.conv1(x)
 x = self.bn1(x)
 x = self.relu(x)
 x = self.maxpool1(x)
 x = self.layer1(x)
 if self.multiple_devices:
x = x.to(next(self.layer2.parameters() ).device)
 x = self.layer2(x)
 x = self.layer3(x)
 x = self.layer4(x)
 x = self.avgpool(x)
 x = x.view(x.size(0), -1)
 x = self.fc(x)
 if self.multiple_devices:
x = x.to(next(self.conv1.parameters() ).device)
 return x
def l_resnext50(baseWidth=4, cardinality=32):

 model = ResNeXt(baseWidth, cardinality, [3, 4, 6, 3], 5)
 return model
def l_resnext101(baseWidth=4, cardinality=32):

 model = ResNeXt(baseWidth, cardinality, [3, 4, 23, 3], 5)
 return model
train = pd.merge(data['tra'], data['hol'], how='left', on=['visit_date'])
 test = pd.merge(data['tes'], data['hol'], how='left', on=['visit_date'] )<merge>
model = l_resnext50()
train = pd.merge(train, stores, how='left', on=['air_store_id','dow'])
 test = pd.merge(test, stores, how='left', on=['air_store_id','dow'] )<merge>
def quadratic_weighted_kappa(y_pred, y_true):
max_rat = 5
y_true = np.asarray(y_true, dtype=int)
 y_pred = np.asarray(y_pred, dtype=int)
 hist1 = np.zeros(( max_rat + 1,))
hist2 = np.zeros(( max_rat + 1,))
o = 0
for k in range(len(y_true)) :
i, j = y_true[k], y_pred[k]
hist1[i] += 1
hist2[j] += 1
o +=(i - j)*(i - j)
 e = 0
for i in range(max_rat + 1):
for j in range(max_rat + 1):
e += hist1[i] * hist2[j] *(i - j)*(i - j)
 e = e / y_true.shape[0]
return 1 - o / e
for df in ['ar','hr']:
train = pd.merge(train, data[df], how='left', on=['air_store_id','visit_date'])
 test = pd.merge(test, data[df], how='left', on=['air_store_id','visit_date'] )<feature_engineering>
def rotate_bound(image, angle): 
(h, w)= image.shape[:2] 
(cX, cY)=(w / 2, h / 2)
 M = cv2.getRotationMatrix2D(( cX, cY), -angle, 1.0)
 cos = np.abs(M[0, 0])
 sin = np.abs(M[0, 1])
 nW = int(( h * sin)+(w * cos))
nH = int(( h * cos)+(w * sin))
M[0, 2] +=(nW / 2)- cX
M[1, 2] +=(nH / 2)- cY
try:
return cv2.warpAffine(image, M,(nW, nH), borderValue=(255, 255, 255))
except Exception:
return image
def find_best_rounding(image):
volume = image.shape[0] * image.shape[1]
opt_angle = 0
for angle in range(-45, 45, 10):
rot_image = rotate_bound(image, angle + 2)
 rot_image = cut_image(rot_image)
 rot_volume = rot_image.shape[0] * rot_image.shape[1]
if rot_volume < volume:
volume = rot_volume
opt_angle = angle
del rot_image
gc.collect()
return opt_angle
def get_corners(image):
image_mask =(( 255 - image ).sum(axis=-1)!= 0 ).astype(np.uint8)
 rows = np.any(image_mask, axis=1)
 cols = np.any(image_mask, axis=0)
 x_min, x_max = np.where(rows)[0][[0, -1]]
y_min, y_max = np.where(cols)[0][[0, -1]]
return x_min, x_max, y_min, y_max
def cut_image(image):
x_min, x_max, y_min, y_max = get_corners(image)
 image = image[x_min:x_max, y_min:y_max]
h, w, c = image.shape
if w > h:
image = np.transpose(image, [1, 0, 2])
 return image
def get_image_mask(image):
image_mask =(( 255 - image ).sum(axis=-1)!= 0 ).astype(np.uint8)
 return image_mask
train['id'] = train.apply(lambda r: '_'.join([str(r['air_store_id']), str(r['visit_date'])]), axis=1 )<feature_engineering>
def extract_tiles_from_image(img, tile_size=256, n_tiles=36, mode=0):
h, w, c = img.shape
pad_h =(tile_size - h % tile_size)% tile_size +(( tile_size * mode)// 2)
 pad_w =(tile_size - w % tile_size)% tile_size +(( tile_size * mode)// 2)
 img2 = np.pad(img, [[pad_h // 2, pad_h - pad_h // 2], [pad_w // 2, pad_w - pad_w // 2], [0, 0]],
constant_values=255)
 img3 = img2.reshape(
 img2.shape[0] // tile_size,
tile_size,
img2.shape[1] // tile_size,
tile_size,
3 
)
 img3 = img3.transpose(0, 2, 1, 3, 4 ).reshape(-1, tile_size, tile_size, 3)
 n_relevant_tiles =(img3.reshape(img3.shape[0], -1 ).sum(1)< tile_size ** 2 * 3 * 255 ).sum()
idxs = np.argsort(img3.reshape(img3.shape[0], -1 ).sum(-1)) [:int(np.minimum(n_relevant_tiles, n_tiles)) ]
img3 = img3[idxs]
del img2
del img
gc.collect()
return img3
def get_tiles(image_folder, image_id, tile_size=256, n_tiles=49):
tiff_file = os.path.join(image_folder, f'{image_id}.tiff')
 image_base = skimage.io.MultiImage(tiff_file)
 image = image_base[1]
if TEST_DF[TEST_DF.image_id == image_id].opt_angle.iloc[0] == -1:
low_res_image = image_base[-1]
low_res_image = cv2.resize(low_res_image,(low_res_image.shape[1] // 8,
low_res_image.shape[0] // 8))
opt_angle = find_best_rounding(low_res_image)
 else:
opt_angle = TEST_DF[TEST_DF.image_id == image_id].opt_angle.iloc[0]
image = cut_image(image)
 image = rotate_bound(image, opt_angle)
 image = cut_image(image)
 tiles = extract_tiles_from_image(image, tile_size, n_tiles)
 del image
gc.collect()
return tiles
def assemble_image(image_tiles, image_processing_func, tile_processing_func, num_tiles, tile_size):
num_row_tiles = int(np.sqrt(num_tiles))
image_tiles = [tile_processing_func(image=tile)['image'] for tile in image_tiles]
image_tiles = np.array(image_tiles)
 if len(image_tiles)== 0:
image_tiles =(255 * np.ones(( 1, tile_size, tile_size, 3)) ).astype(np.int8)
 if len(image_tiles)< num_tiles:
image_tiles = np.pad(image_tiles,
[[0, num_tiles - len(image_tiles)], [0, 0], [0, 0], [0, 0]], constant_values=255)
 joined_image = np.zeros(( tile_size * num_row_tiles, tile_size * num_row_tiles, 3))
for h in range(num_row_tiles):
for w in range(num_row_tiles):
i = h * num_row_tiles + w
h1 = h * tile_size
w1 = w * tile_size
tile_in_place = image_tiles[i]
joined_image[h1:h1 + tile_size, w1:w1 + tile_size] = tile_in_place
del image_tiles
gc.collect()
return image_processing_func(image=joined_image.astype(np.uint8)) ['image']
train['total_reserv_sum'] = train['rv1_x'] + train['rv1_y']
train['total_reserv_mean'] =(train['rv2_x'] + train['rv2_y'])/ 2
test['total_reserv_sum'] = test['rv1_x'] + test['rv1_y']
test['total_reserv_mean'] =(test['rv2_x'] + test['rv2_y'])/ 2<categorify>
def identity_processing() :
def identity(image):
return {'image': image}
return identity
def simple_image_processing() :
transformations = albu.Compose([
albu.Transpose(p=0.5),
albu.VerticalFlip(p=0.5),
albu.HorizontalFlip(p=0.5),
])
 return albu.Compose(transformations)
 def spacial_image_processing() :
transformations = [albu.Flip() ,
albu.Transpose() ,
albu.ShiftScaleRotate(rotate_limit=360, shift_limit=0, scale_limit=0.125),
albu.GridDistortion() ]
return albu.Compose(transformations)
 def distributional_image_processing() :
transformations = [
albu.OneOf([albu.RandomSnow() ,
albu.RandomBrightnessContrast() ,
albu.CLAHE() ]),
albu.OneOf([albu.CoarseDropout(max_holes=36 * 16, fill_value=0, max_height=32, max_width=32),
albu.CoarseDropout(max_holes=36 * 8, fill_value=0, max_height=32, max_width=32),
albu.GridDropout() ])
 ]
return albu.Compose(transformations)
 def scaling_image_processing() :
return albu.Normalize(mean=[0.90949707, 0.8188697, 0.87795304],
std=[0.36357649, 0.49984502, 0.40477625] )
train = train.fillna(-1)
 test = test.fillna(-1 )<compute_test_metric>
class PandaDataset(Dataset):
def __init__(self, images_df, num_tta,
tile_augmentation=None,
image_augmentation=None,
num_tiles=49,
tile_size=256):
super().__init__()
self.images_df = images_df
self.tile_augmentation = identity_processing() if tile_augmentation is None else tile_augmentation
self.image_augmentation = identity_processing() if image_augmentation is None else image_augmentation
self.image_scaling = scaling_image_processing()
self.num_tiles = num_tiles
self.tile_size = tile_size
self.num_tta = num_tta
def __getitem__(self, item):
df_item = self.images_df.iloc[item]
image = self._get_image(df_item.image_id)
 image = np.transpose(image, [0, 3, 1, 2])
 return torch.tensor(image)
 def __len__(self):
return len(self.images_df)
 def _get_image(self, image_id):
if os.path.exists(TEST_IMAGES_PATH):
images_folder = TEST_IMAGES_PATH
else:
images_folder = TRAIN_IMAGES_PATH
image_tile_list = get_tiles(images_folder, image_id)
 images = []
for j in range(self.num_tta):
image = assemble_image(image_tile_list, self.image_augmentation, self.tile_augmentation,
num_tiles=self.num_tiles, tile_size=self.tile_size)
 image = self.image_scaling(image=image)['image']
images.append(image)
 return np.array(images )
def RMSLE(y, pred):
return metrics.mean_squared_error(y, pred)**0.5<split>
def get_preds(loader, model, device):
model.eval()
y_pred = []
for image_tta_batch in tqdm(loader, total=len(loader)) :
image_tta_batch = torch.transpose(image_tta_batch, 0, 1)
 tta_preds = []
for image in image_tta_batch:
image = image.to(device)
 with torch.no_grad() :
predictions = model(image ).sigmoid().detach().cpu().numpy()
del image
gc.collect()
isup_pred = predictions.sum(axis=1)
 tta_preds.append(isup_pred)
 tta_preds = np.array(tta_preds)
 isup_pred = tta_preds.mean(axis=0)
 y_pred.extend(isup_pred)
 return np.array(y_pred )
X_train, X_val, y_train, y_val = train_test_split(train[col], train['visitors'], test_size=0.33, random_state=42)
 X_train.head()<choose_model_class>
gc.collect()
sub_df = pd.read_csv(SAMPLE_CSV_PATH)
 device = torch.device('cuda')
 model.to(device)
 num_folds = 5
if os.path.exists(TEST_IMAGES_PATH):
num_tta = 1
test_dataset = PandaDataset(images_df=TEST_DF, num_tta=num_tta,
tile_augmentation=simple_image_processing())
 test_loader = DataLoader(dataset=test_dataset, batch_size=5,
sampler=SequentialSampler(test_dataset), num_workers=2)
 y_pred_per_fold = []
for fold in range(num_folds):
load_model_state(model, MODEL_WEIGHTS_PATH.format(fold=fold))
y_pred = get_preds(test_loader, model, device)
 y_pred_per_fold.append(y_pred)
 y_pred = np.mean(y_pred_per_fold, axis=0)
 rounder = OptimizedRounder()
y_pred = rounder.predict(y_pred, coefficients=[0.5, 1.5, 2.5, 3.5, 4.5])
 sub_df = pd.DataFrame({'image_id': TEST_DF.image_id,
'isup_grade': y_pred})
 else:
num_tta = 1
y_pred_per_fold = []
for fold in range(num_folds):
test_dataset = PandaDataset(images_df=TEST_DF, num_tta=num_tta,
tile_augmentation=simple_image_processing())
 test_loader = DataLoader(dataset=test_dataset, batch_size=5,
sampler=SequentialSampler(test_dataset), num_workers=2)
 load_model_state(model, MODEL_WEIGHTS_PATH.format(fold=fold))
y_pred = get_preds(test_loader, model, device)
 y_pred_per_fold.append(y_pred)
 del test_dataset
del test_loader
gc.collect()
y_pred = np.mean(y_pred_per_fold, axis=0)
 rounder = OptimizedRounder()
y_pred = rounder.predict(y_pred, coefficients=[0.5, 1.5, 2.5, 3.5, 4.5])
 sub_df = pd.DataFrame({'image_id': TEST_DF.image_id,
'isup_grade': y_pred})
<train_on_grid><EOS> 
sub_df.to_csv('submission.csv', index=False )