Kaggle competition: Bengali.AI Handwritten Grapheme Classification
<SOS> metric: WeightedCategorizationAccuracy Kaggle data source: bengali.ai-handwritten-grapheme-classification<install_modules>
DEBUG = False
!pip install -q efficientnet<set_options>
import os
import gc
import cv2
import math
import glob
import numpy as np
import pandas as pd
from tqdm import tqdm_notebook as tqdm
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
%matplotlib inline
print("Tensorflow version " + tf.__version__ )<define_variables>
data_dir = '.. /input/bengaliai-cv19'
device = torch.device('cuda')
 HEIGHT = 137
WIDTH = 236
img_size = 128
seen_th = 0.825
c0_dim = 1295
c1_dim = 168
c2_dim = 11
c3_dim = 7
out_dim = c0_dim + c1_dim + c2_dim + c3_dim
num_workers = 4
batch_size = 32
files_test = [f'test_image_data_{fid}.parquet' for fid in range(4)]
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'))
id2grapheme = {i: grapheme for i, grapheme in enumerate(df_train.grapheme.unique())}
grapheme2id = {grapheme: i for i, grapheme in enumerate(df_train.grapheme.unique())}
df_train['grapheme_id'] = df_train['grapheme'].map(grapheme2id)
 df_label_map = []
for i, df in tqdm(df_train.groupby('grapheme_id')) :
df_label_map.append(df.iloc[:, 1:6].drop_duplicates())
 df_label_map = pd.concat(df_label_map ).reset_index(drop=True)
 if DEBUG:
files_test = [f'train_image_data_{fid}.parquet' for fid in range(4)]
df_test = pd.read_csv(os.path.join(data_dir, 'train.csv'))
seen_th = 0.94
GCS_DS_PATH = KaggleDatasets().get_gcs_path("tpu-getting-started" )<define_variables>
def read_data(f):
f = os.path.join(data_dir, f)
 data = pd.read_parquet(f)
 data = data.iloc[:, 1:].values
return data
IMAGE_SIZE = [512, 512]
BATCH_SIZE = 16 * strategy.num_replicas_in_sync
GCS_PATH_SELECT = {
192: GCS_DS_PATH + '/tfrecords-jpeg-192x192',
224: GCS_DS_PATH + '/tfrecords-jpeg-224x224',
331: GCS_DS_PATH + '/tfrecords-jpeg-331x331',
512: GCS_DS_PATH + '/tfrecords-jpeg-512x512'
}
GCS_PATH = GCS_PATH_SELECT[IMAGE_SIZE[0]]
VALIDATION_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')
 TEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')
 SEED = 2020<data_type_conversions>
class BengaliDataset(Dataset):
def __init__(self, data, image_size=128):
self.data = data
self.image_size = image_size
def __len__(self):
return self.data.shape[0]
def __getitem__(self, index):
image = 255 - self.data[index].reshape(HEIGHT, WIDTH)
 image = cv2.resize(image,(self.image_size, self.image_size))
image = image.astype(np.float32)/ 255
image = image[np.newaxis, :, :]
image = np.repeat(image, 3, 0)
 return torch.tensor(image )
def decode_image(image_data):

 image = tf.image.decode_jpeg(image_data, channels=3)
 image = tf.cast(image, tf.float32)/ 255.0
image = tf.reshape(image, [*IMAGE_SIZE, 3])
 return image
def read_labeled_tfrecord(example):
LABELED_TFREC_FORMAT = {
"image": tf.io.FixedLenFeature([], tf.string),
"class": tf.io.FixedLenFeature([], tf.int64),
}
example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)
 image = decode_image(example['image'])
 label = tf.cast(example['class'], tf.int32)
 return image, label
def read_unlabeled_tfrecord(example):
UNLABELED_TFREC_FORMAT = {
"image": tf.io.FixedLenFeature([], tf.string),
"id": tf.io.FixedLenFeature([], tf.string),
}
example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)
 image = decode_image(example['image'])
 idnum = example['id']
return image, idnum
def load_dataset(filenames, labeled=True, ordered=False):

 ignore_order = tf.data.Options()
if not ordered:
ignore_order.experimental_deterministic = False
dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)
 dataset = dataset.with_options(ignore_order)
 dataset = dataset.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord, num_parallel_calls=AUTO)
 return dataset
def get_validation_dataset() :
dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=True)
 dataset = dataset.batch(BATCH_SIZE)
 dataset = dataset.cache()
dataset = dataset.prefetch(AUTO)
 return dataset
def get_test_dataset(ordered=False):
dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)
 dataset = dataset.batch(BATCH_SIZE)
 dataset = dataset.prefetch(AUTO)
 return dataset
def count_data_items(filenames):
n = [int(re.compile(r"-([0-9]*)\." ).search(filename ).group(1)) for filename in filenames]
return np.sum(n)
 NUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)
 NUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)
 print('Dataset: {} validation images, {} unlabeled test images'.format(NUM_VALIDATION_IMAGES, NUM_TEST_IMAGES))<choose_model_class>
sigmoid = torch.nn.Sigmoid()
class Swish(torch.autograd.Function):
@staticmethod
def forward(ctx, i):
result = i * sigmoid(i)
 ctx.save_for_backward(i)
 return result
@staticmethod
def backward(ctx, grad_output):
i = ctx.saved_variables[0]
sigmoid_i = sigmoid(i)
 return grad_output *(sigmoid_i *(1 + i *(1 - sigmoid_i)))
 swish = Swish.apply
class Swish_module(nn.Module):
def forward(self, x):
return swish(x)
 swish_layer = Swish_module()
def relu_fn(x):

 return swish_layer(x)
 class DenseCrossEntropy(nn.Module):
def forward(self, x, target, reduction='mean'):
x = x.float()
target = target.float()
logprobs = torch.nn.functional.log_softmax(x, dim=-1)
 loss = -logprobs * target
loss = loss.sum(-1)
 if reduction == 'mean':
return loss.mean()
elif reduction == 'sum':
return loss.sum()
elif reduction == 'none':
return loss
class ArcFaceLoss(nn.modules.Module):
def __init__(self, s=30.0, m=0.5, reduction='mean'):
super().__init__()
self.reduction = reduction
self.s = s
self.cos_m = math.cos(m)
 self.sin_m = math.sin(m)
 self.th = math.cos(math.pi - m)
 self.mm = math.sin(math.pi - m)* m
def forward(self, logits, labels):
logits = logits.float()
cosine = logits
sine = torch.sqrt(1.0 - torch.pow(cosine, 2))
phi = cosine * self.cos_m - sine * self.sin_m
phi = torch.where(cosine > self.th, phi, cosine - self.mm)
 output =(labels * phi)+(( 1.0 - labels)* cosine)
 output *= self.s
loss = DenseCrossEntropy()(output, labels, self.reduction)
 return loss / 2
class ArcMarginProduct(nn.Module):
def __init__(self, in_features, out_features):
super().__init__()
self.weight = nn.Parameter(torch.FloatTensor(out_features, in_features))
self.reset_parameters()
def reset_parameters(self):
stdv = 1./ math.sqrt(self.weight.size(1))
self.weight.data.uniform_(-stdv, stdv)
 def forward(self, features):
cosine = F.linear(F.normalize(features), F.normalize(self.weight))
return cosine
class enet_3cg(nn.Module):
def __init__(self, backbone, out_dim_1, out_dim_2):
super(enet_3cg, self ).__init__()
self.enet = enet.EfficientNet.from_name(backbone)
 self.dropouts = nn.ModuleList([
nn.Dropout(0.5)for _ in range(5)
 ])
 self.myfc_1 = nn.Linear(self.enet._fc.in_features, out_dim_2)
 self.activate = Swish_module()
self.myfc_2 = nn.Linear(out_dim_2, out_dim_1)
 self.enet._fc = nn.Identity()
def extract(self, x):
return self.enet(x)
 def forward(self, x):
x = self.extract(x)
 for i, dropout in enumerate(self.dropouts):
if i == 0:
out_2 = self.myfc_1(dropout(x))
else:
out_2 += self.myfc_1(dropout(x))
out_2 /= len(self.dropouts)
 out_1 = self.myfc_2(self.activate(out_2))
return out_1, out_2
class enet_arcface_v2(nn.Module):
def __init__(self, backbone, out_dim_1, out_dim_2):
super(enet_arcface_v2, self ).__init__()
self.enet = enet.EfficientNet.from_name(backbone)
 self.dropouts = nn.ModuleList([
nn.Dropout(0.5)for _ in range(5)
 ])
 self.gfc = nn.Linear(self.enet._fc.in_features, 4096)
 self.metric_classify = ArcMarginProduct(4096, out_dim_1)
 self.myfc_1 = nn.Linear(4096, out_dim_1)
 self.myfc_2_1 = nn.Linear(4096, 512)
 self.myfc_2_2 = nn.Linear(512, out_dim_2)
 self.enet._fc = nn.Identity()
def extract(self, x):
return self.enet(x)
 def forward(self, x):
x = self.extract(x)
 x = Swish_module()(self.gfc(x))
for i, dropout in enumerate(self.dropouts):
if i == 0:
out_1 = self.myfc_1(dropout(x))
out_2 = self.myfc_2_1(dropout(x))
else:
out_1 += self.myfc_1(dropout(x))
out_2 += self.myfc_2_1(dropout(x))
out_1 /= len(self.dropouts)
 out_2 /= len(self.dropouts)
 out_2 = self.myfc_2_2(Swish_module()(out_2))
metric_output = self.metric_classify(x)
 return out_1, out_2, metric_output
def get_model(use_model):
base_model = use_model(weights='imagenet',
include_top=False, pooling='avg',
input_shape=(*IMAGE_SIZE, 3))
x = base_model.output
predictions = Dense(104, activation='softmax' )(x)
 model = Model(inputs=base_model.input, outputs=predictions)
 model.compile(
 optimizer='nadam',
loss = 'sparse_categorical_crossentropy',
metrics=['sparse_categorical_accuracy'] 
)
 return model
with strategy.scope() :
model1 = get_model(DenseNet201)
 model1.load_weights("/kaggle/input/start-with-pre-train-densenet201/my_densenet201.h5" )<choose_model_class>
def get_models(model_files, model_class):
enet_type = 'efficientnet-b1'
models = []
for model_f in model_files:
model = model_class(enet_type, out_dim_1=c0_dim, out_dim_2=c1_dim+c2_dim+c3_dim)
 model = model.to(device)
 model.load_state_dict(torch.load(model_f, map_location=lambda storage, loc: storage), strict=True)
 model.eval()
models.append(model)
 print(model_f, 'loaded!')
 return models
def get_model(use_model):
base_model = use_model(weights='imagenet',
include_top=False, pooling='avg',
input_shape=(*IMAGE_SIZE, 3))
x = base_model.output
predictions = Dense(104, activation='softmax' )(x)
 model = Model(inputs=base_model.input, outputs=predictions)
 model.compile(
 optimizer='nadam',
loss = 'sparse_categorical_crossentropy',
metrics=['sparse_categorical_accuracy'] 
)
 return model
with strategy.scope() :
model2 = get_model(Xception)
 model2.load_weights("/kaggle/input/start-with-pre-train-xception/my_xception.h5" )<predict_on_test>
model_files_unseen = [
'.. /input/bengali-train-unseen-model/effnet-b1-unseen_model_fold0.pth',
]
model_files_arcface = [
'.. /input/bengali-train-seen-model/effnet-b1-seen_model_fold0.pth',
]
val_dataset = get_validation_dataset()
images_ds = val_dataset.map(lambda image, label: image)
 labels_ds = val_dataset.map(lambda image, label: label ).unbatch()
val_labels = next(iter(labels_ds.batch(NUM_VALIDATION_IMAGES)) ).numpy()
m1 = model1.predict(images_ds)
 m2 = model2.predict(images_ds)
 scores = []
for alpha in np.linspace(0,1,100):
val_probabilities = alpha*m1+(1-alpha)*m2
val_predictions = np.argmax(val_probabilities, axis=-1)
 scores.append(f1_score(val_labels, val_predictions, labels=range(104), average='macro'))
best_alpha = np.argmax(scores)/100
print('Best alpha: ' + str(best_alpha))<predict_on_test>
print('loading unseen models...')
 models_unseen = get_models(model_files_unseen, enet_3cg)
 print('loading arcface models...')
 models_arcface = get_models(model_files_arcface, enet_arcface_v2)
 print(len(models_unseen), len(models_arcface))
test_ds = get_test_dataset(ordered=True)
 print('Вычисляем предсказания...')
 test_images_ds = test_ds.map(lambda image, idnum: image)
 probabilities1 = model1.predict(test_images_ds)
 probabilities2 = model2.predict(test_images_ds)
 probabilities = best_alpha * probabilities1 +(1 - best_alpha)* probabilities2
predictions = np.argmax(probabilities, axis=-1)
 print(predictions)
 print('Создание файла submission.csv...')
 test_ids_ds = test_ds.map(lambda image, idnum: idnum ).unbatch()
test_ids = next(iter(test_ids_ds.batch(NUM_TEST_IMAGES)) ).numpy().astype('U')
 np.savetxt('submission.csv', np.rec.fromarrays([test_ids, predictions]), fmt=['%s', '%d'], delimiter=',', header='id,label', comments='' )<install_modules>
FINAL_P = []
with torch.no_grad() :
for file in tqdm(files_test):
data = read_data(file)
 dataset_test = BengaliDataset(data, img_size)
 test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
 for(image)in tqdm(test_loader):
image = image.to(device)
 logits_1 = torch.zeros(image.shape[0], c0_dim ).to(device)
 logits_metric = torch.zeros(image.shape[0], c0_dim ).to(device)
 for mid, model in enumerate(models_arcface):
l1, l2, l3 = model(image)
 logits_1 += l1.softmax(1)
 logits_metric += l3
logits_metric /= len(models_arcface)
 pred = df_label_map.iloc[logits_metric.detach().cpu().numpy().argmax(1), :3].values
max_p = logits_metric.cpu().numpy().max(1)
 unseen_idx = np.where(max_p <= seen_th)[0]
if unseen_idx.shape[0] > 0:
logits_2_unseen = torch.zeros(unseen_idx.shape[0], c1_dim+c2_dim+c3_dim ).to(device)
 for mid, model in enumerate(models_unseen):
_, l2 = model(image[unseen_idx])
 logits_2_unseen[:, :c1_dim] += l2[:, :c1_dim].softmax(1)
 logits_2_unseen[:, c1_dim:c1_dim+c2_dim] += l2[:, c1_dim:c1_dim+c2_dim].softmax(1)
 logits_2_unseen[:, c1_dim+c2_dim:] += l2[:, c1_dim+c2_dim:].softmax(1)
 pred[unseen_idx, 0] = logits_2_unseen[:, :c1_dim].detach().cpu().numpy().argmax(1)
 pred[unseen_idx, 1] = logits_2_unseen[:, c1_dim:c1_dim+c2_dim].detach().cpu().numpy().argmax(1)
 pred[unseen_idx, 2] = logits_2_unseen[:, c1_dim+c2_dim:].detach().cpu().numpy().argmax(1)
 FINAL_P += pred.reshape(-1 ).tolist()
del data
gc.collect()
<import_modules><EOS> 
df_sub = pd.DataFrame({
'row_id': [f'Test_{i}_{p}' for i in range(len(FINAL_P)// 3)for p in ['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']],
'target': FINAL_P
})
 df_sub.to_csv('submission.csv',index=False )