Kaggle competition: Recursion Cellular Image Classification
<SOS> metric: CategorizationAccuracy Kaggle data source: recursion-cellular-image-classification<categorify>
!pip install efficientnet
class QuoraFeatureExtractor:
def __init__(self, num_words=120000, max_len=70, want_capstate=False, want_wide=False, preprocessor=None):
self.max_len = max_len
self.num_words = num_words
self.num_aux = 1
self.preprocessor = preprocessor
self.want_capstate = want_capstate
self.want_wide = want_wide
def fit_transform(self, texts, fit_mask=None):
if fit_mask is None:
fit_mask = np.full(len(texts), True, dtype='bool')
 if self.preprocessor:
print('Preprocessing', flush=True)
 texts = self.preprocessor.transform(texts)
 fit_mask = np.array(fit_mask, dtype='bool')
 print('Tokenizing', flush=True)
 self.tokenizer = Tokenizer(num_words=self.num_words, filters='', split=' ', oov_token='__')
 self.tokenizer.fit_on_texts(texts[fit_mask])
 self.word_index = self.tokenizer.word_index
tokens = self.tokenizer.texts_to_sequences(texts)
 tokens = pad_sequences(tokens, maxlen=self.max_len)
 res = {'tokens': tokens}
if self.want_capstate:
print('Generating capstate', flush=True)
 with multiprocessing.Pool(multiprocessing.cpu_count())as pool:
capstate = np.stack(pool.imap(self.map_capstate, texts, chunksize=10000))
res['capstate'] = capstate
if self.want_wide:
print('Generating wide features', flush=True)
 wide = self.gen_wide_features(texts)
 self.ss = sklearn.preprocessing.RobustScaler()
self.ss.fit(wide[fit_mask].values)
 wide = self.ss.transform(wide.values)
 wide[wide > 3] = 3
res['wide'] = wide
return res
def map_capstate(self, text):
toks = text.split(' ')
 res = np.zeros(self.max_len, dtype=np.int8)
 for i in range(min(len(res), len(toks))):
tok = toks[len(toks)- 1 - i]
x = 0
if tok[0].isdigit() :
x = 6
elif unicodedata.category(tok[0] ).startswith('P'):
x = 5
elif tok == tok.lower() :
x = 1
elif tok == tok.capitalize() :
x = 2
elif tok == tok.upper() :
x = 3
else:
x = 4
res[-i-1] = x
return res
def gen_wide_features(self, texts):
texts = pd.Series(texts)
 wide = pd.DataFrame()
wide['num_chars'] = texts.str.len()
wide['num_caps'] = texts.str.count('[A-Z]')
 wide['frac_caps'] =(wide.num_caps / wide.num_chars ).fillna(0)
 wide['num_dots'] = texts.str.count('[.]')
 return wide
def embedding_weights(self, emb, verbose=1):

 W = np.random.normal(emb.mean() , emb.std() ,(self.num_words, emb.dim))
miss = 0
for word, idx in self.word_index.items() :
if idx >= self.num_words: continue
vec = emb.lookup(word, lower=True)
 if vec is not None:
W[idx] = vec
else:
miss += 1
if verbose > 0:
print(f'{miss} words from tokenizer missing({miss/self.num_words:.2f}%)in {emb.name}')
 return W<set_options>
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
def torch_seed(seed):
os.environ['PYTHONHASHSEED'] = str(seed)
 random.seed(seed)
 np.random.seed(seed)
 torch.manual_seed(seed)
 torch.cuda.manual_seed(seed)
 torch.backends.cudnn.deterministic = True<set_options>
!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
def torch_gc() :
gc.collect()
torch.cuda.empty_cache()<normalization>
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()
def keras_rnn_init(m):
for name, param in m.named_parameters() :
if 'weight_ih' in name: torch.nn.init.xavier_uniform_(param)
 if 'weight_hh' in name: torch.nn.init.orthogonal_(param)
 if 'bias_' in name: torch.nn.init.constant_(param, 0 )<categorify>
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 )
class Fold:
def __init__(self, data, X, seed=42, valid_frac=0.1, holdout_frac=0.0432, holdout_seed=42):
self.data = data
self.X = X
self.y = data.target.values.astype(np.float32)
 self.seed = seed
tmask = data.target.notnull().values
self.all_idx = data.index.values.astype(np.int32)
 self.test_idx = self.all_idx[~tmask]
trainval_idx, self.holdout_idx = train_test_split(
 self.all_idx[tmask],
stratify=self.y[tmask],
test_size=holdout_frac,
random_state=holdout_seed,
shuffle=True)
 self.train_idx, self.valid_idx = train_test_split(
 trainval_idx,
stratify=self.y[trainval_idx],
test_size=valid_frac,
random_state=seed,
shuffle=True)
 self.train_idx.sort()
self.valid_idx.sort()
self.holdout_idx.sort()
self.oob_idx = self.all_idx[~np.isin(self.all_idx, self.train_idx, assume_unique=True)]<feature_engineering>
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
%%time
torch_seed(42)
 qd = QuoraData()
qd.convert_start(['glove', 'wiki'])
 prep = QuoraPreprocessor()
input_df = qd.read_input()
input_df['question_text'] = prep.transform(input_df.question_text )<categorify>
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'
)
%%time
qfe = QuoraFeatureExtractor(num_words=95000, max_len=70)
 input_X = qfe.fit_transform(input_df.question_text, fit_mask=input_df.target.notnull().values)
 print({ k: v.shape for(k, v)in input_X.items() } )<load_pretrained>
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
%%time
qd.convert_wait()
torch_seed(42)
 emb_glove = qfe.embedding_weights(qd.glove())
 torch_seed(43)
 emb_wiki = qfe.embedding_weights(qd.wiki())
 emb_glovewiki = np.concatenate([emb_glove, emb_wiki], axis=1)
 gc.collect()
os.system('rm -f glove.npy glove.vocab wiki.npy wiki.vocab' )<choose_model_class>
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
)
class Arch1b_LstmGru(torch.nn.Module):
def __init__(self, emb_weights):
super().__init__()
self.embedding = nn.Embedding(emb_weights.shape[0], emb_weights.shape[1])
 self.embedding.weight = nn.Parameter(torch.Tensor(emb_weights), requires_grad=False)
 self.lstm = nn.LSTM(self.embedding.embedding_dim, 128, bidirectional=True, batch_first=True)
 self.gru = nn.GRU(self.lstm.hidden_size*2, 64, bidirectional=True, batch_first=True)
 self.fc = nn.Linear(128, 1)
 def dropout1d(self, x, p):
x = x.permute(0, 2, 1)
 x = F.dropout2d(x, p, training=self.training)
 x = x.permute(0, 2, 1)
 return x
def forward(self, x_tok):
x = self.embedding(x_tok)
 x = self.dropout1d(x, 0.2)
 x = F.dropout2d(x, 0.05, training=self.training)
 x, _ = self.lstm(x)
 x, c = self.gru(x)
 x = x.max(1)[0]
x = F.dropout(x, 0.1, training=self.training)
 x = self.fc(x)
 return x<prepare_x_and_y>
categories = train_df['category'].unique()
output_df = []
for category in categories:
category_df = train_df[train_df['category'] == category]
cat_test_df = test_df[test_df['category'] == category].copy()
print('
' + '=' * 40)
 print("CURRENT CATEGORY:", category)
 print('-' * 40)
 train_idx, val_idx = train_test_split(
 category_df.index,
random_state=2019,
test_size=0.15 
)
 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,
rotation_range=15,
dim=(300, 300),
base_path='.. /tmp/train_resized',
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,
rotation_range=15,
dim=(300, 300),
base_path='.. /tmp/train_resized',
n_classes=train_target_df.shape[1] 
)
 test_generator = DataGenerator(
 cat_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' 
)
 model.load_weights('model.h5')
 model.compile(
 Adam(0.0001),
loss='categorical_crossentropy',
metrics=['accuracy'] 
)
 checkpoint = ModelCheckpoint(
 f'model_{category}.h5',
monitor='val_loss',
verbose=0,
save_best_only=True,
save_weights_only=False,
mode='auto' 
)
 history_category = model.fit_generator(
 train_generator,
validation_data=val_generator,
callbacks=[checkpoint],
use_multiprocessing=False,
workers=1,
verbose=2,
epochs=10 
)
 y_pred = model.predict_generator(
 test_generator,
workers=2,
use_multiprocessing=True,
verbose=1 
)
 cat_test_df['sirna'] = y_pred.argmax(axis=1)
 output_df.append(cat_test_df[['id_code', 'sirna']])
 with open(f'history_{category}.json', 'w')as f:
json.dump(history_category.history, f )
<choose_model_class><EOS> 
output_df = pd.concat(output_df)
 output_df.to_csv('submission.csv', index=False )