Kaggle competition: Plant Pathology 2020 - FGVC7
<SOS> metric: MCAUC Kaggle data source: plant-pathology-2020-fgvc7<install_modules>
!pip install timm
!pip install efficientnet 
<import_modules>
warnings.filterwarnings('ignore')
import json
import math
import os
import cv2
import tensorflow as tf
from PIL import Image
import numpy as np
import keras
from keras import layers
from keras.applications import MobileNetV2
from keras.callbacks import Callback, ModelCheckpoint
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Model, load_model
from keras.layers import GlobalAveragePooling2D, Dense, Dropout, BatchNormalization, concatenate, Input, add
from keras.optimizers import Adam
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import cohen_kappa_score, accuracy_score
import scipy
from tqdm import tqdm<load_from_disk>
DIR_INPUT = '/kaggle/input/plant-pathology-2020-fgvc7'
IMAGE_INPUT = '/kaggle/input/plant-pathology-2020-resized-images'
SEED = 42
N_FOLDS = 5
N_EPOCHS = 20
BATCH_SIZE = 8
IMAGE_SIZE =(409,273)
 device = 'cuda' if torch.cuda.is_available() else 'cpu'
device = torch.device(device)
 device
!mkdir.. /tmp
!unzip -q.. /input/recursion-2019-load-resize-and-save-images/train.zip -d.. /tmp
!unzip -q.. /input/recursion-2019-load-resize-and-save-images/test.zip -d.. /tmp<load_from_csv>
class PlantDataset(Dataset):
def __init__(self, df, transforms=None, test_set=False):
self.df = df
self.transforms = transforms
self.test_set = test_set
if not self.transforms:
self.transforms = A.Compose([ToTensorV2(p=1.0)])
 def __len__(self):
return self.df.shape[0]
def __getitem__(self, idx):
image_src = IMAGE_INPUT + '/images_409_273/' + self.df.loc[idx, 'image_id'] + '.jpg'
image = cv2.imread(image_src, cv2.IMREAD_COLOR)
 image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
 transformed = self.transforms(image=image)
 image = transformed['image']
if not self.test_set:
labels = self.df.loc[idx, ['healthy', 'multiple_diseases', 'rust', 'scab']].values
labels = torch.from_numpy(labels.astype(np.int8))
labels = labels.unsqueeze(-1)
 return image, labels
else:
return image
train_df = pd.read_csv('.. /input/recursion-cellular-image-classification/train.csv')
 test_df = pd.read_csv('.. /input/recursion-cellular-image-classification/test.csv')
 train_df['category'] = train_df['experiment'].apply(lambda x: x.split('-')[0])
 test_df['category'] = test_df['experiment'].apply(lambda x: x.split('-')[0])
 train_target_df = pd.get_dummies(train_df['sirna'])
 print(train_df.shape)
 print(test_df.shape)
 print(train_target_df.shape)
 train_df.head()<split>
def trim_network_at_index(network,index=-1):
assert index <0, f'Param index must be negative.Received {index}'
return nn.Sequential(*list(network.children())[:index] )
train_idx, val_idx = train_test_split(
 train_df.index, test_size=0.15, random_state=2019 
)
 print(train_idx.shape)
 print(val_idx.shape )<train_model>
class PlantModel(nn.Module):
def __init__(self, num_classes=4):
super().__init__()
self.backbone = timm.create_model('resnest269e',pretrained=True)
 in_features = self.backbone.fc.in_features
self.backbone = trim_network_at_index(self.backbone,-1)
 self.logit = nn.Linear(in_features, num_classes)
 def forward(self, x):
x = self.backbone(x ).flatten(start_dim=1)
 x = self.logit(x)
 return x
class DataGenerator(keras.utils.Sequence):
'Generates data for Keras'
def __init__(self, list_IDs, df, target_df=None, mode='fit',
base_path = '.. /input/recursion-cellular-image-classification-224-jpg/train/train',
batch_size=32, dim=(224, 224), n_channels=3, ext='png',
rotation_range=0, fill_mode='nearest', swap=False,
vertical_flip=False, horizontal_flip=False, rescale=1/255.,
n_classes=5, random_state=2019, shuffle=True):
self.dim = dim
self.batch_size = batch_size
self.df = df
self.mode = mode
self.base_path = base_path
self.rotation_range=rotation_range
self.target_df = target_df
self.list_IDs = list_IDs
self.n_channels = n_channels
self.n_classes = n_classes
self.shuffle = shuffle
self.ext = ext
self.rescale = rescale
self.vertical_flip = vertical_flip
self.horizontal_flip = horizontal_flip
self.random_state = random_state
self.swap = swap
self.fill_mode = self.__compute_fill_mode(fill_mode)
 np.random.seed(self.random_state)
 self.on_epoch_end()
def __len__(self):
'Denotes the number of batches per epoch'
return int(np.floor(len(self.list_IDs)/ self.batch_size))
def __getitem__(self, index):
'Generate one batch of data'
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
list_IDs_batch = [self.list_IDs[k] for k in indexes]
X = self.__generate_X(list_IDs_batch)
 if self.mode == 'fit':
y = self.__generate_y(list_IDs_batch)
 return X, y
elif self.mode == 'predict':
return X
else:
raise AttributeError('The parameter mode should be set to "fit" or "predict".')
 def on_epoch_end(self):
'Updates indexes after each epoch'
self.indexes = np.arange(len(self.list_IDs))
if self.shuffle == True:
np.random.shuffle(self.indexes)
 def __generate_X(self, list_IDs_batch):
'Generates data containing batch_size samples'
X_1 = np.empty(( self.batch_size, *self.dim, self.n_channels))
X_2 = np.empty(( self.batch_size, *self.dim, self.n_channels))
for i, ID in enumerate(list_IDs_batch):
code = self.df['id_code'].iloc[ID]
img_path_1 = f"{self.base_path}/{code}_s1.{self.ext}"
img_path_2 = f"{self.base_path}/{code}_s2.{self.ext}"
img1 = self.__load_image(img_path_1)
 img2 = self.__load_image(img_path_2)
 if self.swap and np.random.rand() > 0.5:
img1, img2 = img2, img1
X_1[i,] = img1
X_2[i,] = img2
return [X_1, X_2]
def __generate_y(self, list_IDs_batch):
y = np.empty(( self.batch_size, self.n_classes), dtype=int)
 for i, ID in enumerate(list_IDs_batch):
sirna = self.target_df.iloc[ID]
y[i, ] = sirna
return y
def __load_image(self, img_path):
img = cv2.imread(img_path)
 img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
 img = self.rescale * img.astype(np.float32)
 return img
def __compute_fill_mode(self, fill_mode):
convert_cv2 = {
'nearest': cv2.BORDER_REPLICATE,
'reflect': cv2.BORDER_REFLECT,
'wrap': cv2.BORDER_WRAP,
'constant': cv2.BORDER_CONSTANT
}
return convert_cv2[fill_mode]
def __random_transform(img):
if np.random.rand() > 0.5 and self.vertical_flip:
img = cv2.flip(img, 0)
 if np.random.rand() > 0.5 and self.horizontal_flip:
img = cv2.flip(img, 1)
 rotation = self.rotation_range * np.random.rand()
rows,cols = img.shape[:2]
M = cv2.getRotationMatrix2D(( cols/2,rows/2),rotation,1)
 img = cv2.warpAffine(img,M,(cols,rows), borderMode=self.fill_mode)
 return img<define_variables>
transforms_valid = A.Compose([
A.Normalize(p=1.0),
ToTensorV2(p=1.0)
 ])
 test_df = pd.read_csv(DIR_INPUT + '/test.csv')
 dataset_test = PlantDataset(df=test_df,test_set = True, transforms=transforms_valid)
 testloader = DataLoader(dataset_test, batch_size=BATCH_SIZE,
shuffle=False, num_workers=4 )
BATCH_SIZE = 32
train_generator = DataGenerator(
 train_idx,
df=train_df,
target_df=train_target_df,
batch_size=BATCH_SIZE,
vertical_flip=True,
horizontal_flip=True,
swap=True,
dim=(300, 300),
base_path='.. /tmp/train_resized',
rotation_range=15,
n_classes=train_target_df.shape[1] 
)
 val_generator = DataGenerator(
 val_idx,
df=train_df,
target_df=train_target_df,
batch_size=BATCH_SIZE,
vertical_flip=True,
horizontal_flip=True,
swap=True,
dim=(300, 300),
base_path='.. /tmp/train_resized',
rotation_range=15,
n_classes=train_target_df.shape[1] 
)
 test_generator = DataGenerator(
 test_df.index,
df=test_df,
batch_size=1,
shuffle=False,
mode='predict',
n_classes=train_target_df.shape[1],
dim=(300, 300),
base_path='.. /tmp/test_resized'
)<choose_model_class>
def test_model(model_name,testloader):
model_path = f'/kaggle/input/plant-pathology-2020-training/{model_name}.pth'
model = PlantModel()
model.load_state_dict(torch.load(model_path,map_location=device))
model = model.to(device)
 test_probs = []
model.eval()
test_iter = iter(testloader)
 with torch.no_grad() :
for i in tqdm(range(len(testloader))):
image = next(test_iter)
 probs = F.softmax(model(image.to(device)))
 test_probs.append(probs.cpu().numpy())
 test_probs = np.concatenate(test_probs, axis=0)
 return test_probs
def build_model(n_classes, input_shape=(224, 224, 3)) :
backbone = efficientnet.EfficientNetB3(
 weights='imagenet',
include_top=False,
input_shape=input_shape 
)
 im_inp_1 = Input(shape=input_shape)
 im_inp_2 = Input(shape=input_shape)
 x1 = backbone(im_inp_1)
 x2 = backbone(im_inp_2)
 x1 = GlobalAveragePooling2D()(x1)
 x2 = GlobalAveragePooling2D()(x2)
 out = add([x1, x2])
 out = Dropout(0.5 )(out)
 out = Dense(n_classes, activation='softmax' )(out)
 model = Model(inputs=[im_inp_1, im_inp_2], outputs=out)
 model.compile(Adam(0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
 return model<train_model>
test_probs = []
start = time.perf_counter()
for i_fold in range(N_FOLDS):
test_probs_fold = test_model(f'modelF{i_fold}',testloader)
 test_probs.append(test_probs_fold)
 print(f'Finished Inference in {(time.perf_counter() - start):.2f} seconds' )
checkpoint = ModelCheckpoint(
 'model.h5',
monitor='val_loss',
verbose=0,
save_best_only=True,
save_weights_only=False,
mode='auto' 
)
 history = model.fit_generator(
 train_generator,
validation_data=val_generator,
callbacks=[checkpoint],
use_multiprocessing=False,
workers=1,
verbose=2,
epochs=20
)<split>
test_probs_mean = [np.mean(k,axis=0)for k in zip(*test_probs)]
<save_to_csv><EOS> 
submission_df = pd.read_csv(DIR_INPUT + '/sample_submission.csv')
 submission_df.iloc[:, 1:] = 0
submission_df[['healthy', 'multiple_diseases', 'rust', 'scab']] = test_probs_mean
submission_df.to_csv('submission.csv', index=False )