<jupyter_start><jupyter_text>This notebook is distributed under MIT License<jupyter_code>import os
import sys
sys.path = [
    '../input/efficientnet-pytorch/EfficientNet-PyTorch/EfficientNet-PyTorch-master',
] + sys.path
import numpy as np
import pandas as pd

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Dataset
from efficientnet_pytorch import model as enet

from tqdm import tqdm_notebook as tqdm
import skimage.io
data_dir = '../input/prostate-cancer-grade-assessment'
df_train = pd.read_csv(os.path.join(data_dir, 'train.csv'))
df_test = pd.read_csv(os.path.join(data_dir, 'test.csv'))
df_sub = pd.read_csv(os.path.join(data_dir, 'sample_submission.csv'))

model_dir = '../input/panda-base-model'
image_folder = os.path.join(data_dir, 'test_images')
is_test = os.path.exists(image_folder) 
image_folder = image_folder if is_test else os.path.join(data_dir, 'train_images')

df = df_test if is_test else df_train.loc[:3]
print(df.shape)

tile_size = 256
image_size = 256
n_tiles = 36
batch_size = 4
num_workers = 4

device = torch.device('cuda')

print(image_folder)<jupyter_output><empty_output><jupyter_text>Model<jupyter_code>class enetv2(nn.Module):
    def __init__(self, backbone, out_dim):
        super(enetv2, self).__init__()
        self.enet = enet.EfficientNet.from_name(backbone)
        self.myfc = nn.Linear(self.enet._fc.in_features, out_dim)
        self.enet._fc = nn.Identity()

    def extract(self, x):
        return self.enet(x)

    def forward(self, x):
        x = self.extract(x)
        x = self.myfc(x)
        return x
    
    
def load_models(model_files):
    models = []
    for model_dict in model_files:
        model_f = model_dict['name']
        model_f = os.path.join(model_dir, model_f)
        backbone = 'efficientnet-b0'
        model = enetv2(backbone, out_dim=model_dict['out_dim'])
        if model_dict['parallel']:
            model = nn.DataParallel(model)
        model.load_state_dict(torch.load(model_f, map_location=lambda storage, loc: storage), strict=True)
        model.eval()
        model.to(device)
        models.append(model)
        print(f'{model_f} loaded!')
    return models


model_files = [
    {
        'name': 'model_2_bce_0.881.pth',
#         Trained on several GPUs in parallel
        'parallel': True,
#         Misconfigured while training, it should have predicted 5 bins
        'out_dim': 6,
        'n_tiles': 36
    },
    {
        'name': 'model_0_bce_4x4_0.884.pth',
        'parallel': False,
        'out_dim': 5,
        'n_tiles': 16
    },
    {
        'name': 'model_2_bce_5x5_total_0.883.pth',
        'parallel': False,
        'out_dim': 5,
        'n_tiles': 25
    },
    {
        'name': 'model_3_bce_6x6_total_0.883.pth',
        'parallel': False,
        'out_dim': 5,
        'n_tiles': 36
    }
]

models = load_models(model_files)<jupyter_output><empty_output><jupyter_text>Dataset<jupyter_code>def get_tiles(img, n_tiles, mode=0):
    result = []
    h, w, c = img.shape
    pad_h = (tile_size - h % tile_size) % tile_size
    pad_w = (tile_size - w % tile_size) % tile_size
    mode_pad_h = (tile_size * mode) // 4
    mode_pad_w = (tile_size * mode) // 4
    img2 = np.pad(img,[[pad_h // 2 + mode_pad_h, pad_h - pad_h // 2 + tile_size - mode_pad_h], 
                       [pad_w // 2 + mode_pad_w, pad_w - pad_w // 2 + tile_size - mode_pad_w], [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_tiles_with_info = (img3.reshape(img3.shape[0],-1).sum(1) < tile_size ** 2 * 3 * 255).sum()
    if len(img3) < n_tiles:
        img3 = np.pad(img3, [[0, n_tiles - len(img3)], [0, 0] , [0, 0] , [0, 0]], constant_values=255)
    idxs = np.argsort(img3.reshape(img3.shape[0],-1).sum(-1))[:n_tiles]    
    img3 = img3[idxs]
    for i in range(len(img3)):
        result.append({'img':img3[i], 'idx':i})
    return result, n_tiles_with_info >= n_tiles


class PANDADataset(Dataset):
    def __init__(self,
                 df,
                 image_size,
                 n_tiles=n_tiles,
                 tile_mode=0,
                 rotate=False,
                 rand=False,
                 sub_imgs=False
                ):

        self.df = df.reset_index(drop=True)
        self.image_size = image_size
        self.n_tiles = n_tiles
        self.tile_mode = tile_mode
        self.rotate = rotate
        self.rand = rand
        self.sub_imgs = sub_imgs

    def __len__(self):
        return self.df.shape[0]

    def __getitem__(self, index):
        row = self.df.iloc[index]
        img_id = row.image_id
        
        tiff_file = os.path.join(image_folder, f'{img_id}.tiff')
        image = skimage.io.MultiImage(tiff_file)[1]
        tiles, OK = get_tiles(image, n_tiles=self.n_tiles, mode=self.tile_mode)

        if self.rand:
            idxes = np.random.choice(list(range(self.n_tiles)), self.n_tiles, replace=False)
        else:
            idxes = list(range(self.n_tiles))
        idxes = np.asarray(idxes) + self.n_tiles if self.sub_imgs else idxes

        n_row_tiles = int(np.sqrt(self.n_tiles))
        images = np.zeros((image_size * n_row_tiles, image_size * n_row_tiles, 3))
        for h in range(n_row_tiles):
            for w in range(n_row_tiles):
                i = h * n_row_tiles + w
    
                if len(tiles) > idxes[i]:
                    this_img = tiles[idxes[i]]['img']
                else:
                    this_img = np.ones((self.image_size, self.image_size, 3)).astype(np.uint8) * 255
                this_img = 255 - this_img
                h1 = h * image_size
                w1 = w * image_size
                
                if self.rotate:
                    # 90 degrees counter-clockwise rotation
                    this_img = this_img.transpose(1, 0, 2)[::-1]
                    
                images[h1:h1+image_size, w1:w1+image_size] = this_img

        images = images.astype(np.float32)
        images /= 255
        images = images.transpose(2, 0, 1)

        return torch.tensor(images)<jupyter_output><empty_output><jupyter_text>Prediction<jupyter_code>model_loaders = []

for model_dict, model in zip(model_files, models):
    loaders = []
    for mode in [0, 1, 2, 3]:
        for rotate in [False, True]:
            dataset = PANDADataset(df, image_size, n_tiles=model_dict['n_tiles'], tile_mode=mode, rotate=rotate)
            loader = DataLoader(dataset, batch_size=batch_size, num_workers=num_workers, shuffle=False)
            loaders.append(loader)
    
    model_loaders.append((model, loaders))
print(f'{len(model_loaders) * len(model_loaders[0][1])} predictions in total')
LOGITSS = []
with torch.no_grad():
    for model, loaders in model_loaders:
        for loader in loaders:
            LOGITS = []
            for data in tqdm(loader):
                data = data.to(device)
                logits = model(data)
                LOGITS.append(logits)
            LOGITS = torch.cat(LOGITS).sigmoid().cpu().numpy().sum(1)
            LOGITSS.append(LOGITS)

LOGITS = np.array(LOGITSS).mean(0)
PREDS = LOGITS.round()

df['isup_grade'] = PREDS.astype(int)
df[['image_id', 'isup_grade']].to_csv('submission.csv', index=False)
print(df.head())
print()
print(df.isup_grade.value_counts())<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup