Kaggle competition: Bengali.AI Handwritten Grapheme Classification
def data_augment(image):
p_spatial = tf.random.uniform([], 0, 1.0, dtype=tf.float32)
 p_rotate = tf.random.uniform([], 0, 1.0, dtype=tf.float32)
 if p_rotate >.8:
image = tf.image.rot90(image, k=3)
 elif p_rotate >.6:
image = tf.image.rot90(image, k=2)
 elif p_rotate >.4:
image = tf.image.rot90(image, k=1)
 image = tf.image.random_flip_left_right(image)
 image = tf.image.random_flip_up_down(image)
 if p_spatial >.75:
image = tf.image.transpose(image)
 image = tf.image.random_crop(image, size=[HEIGHT_RESIZE, WIDTH_RESIZE, CHANNELS])
 return image<categorify>
inputs = ["test_image_data_0.parquet","test_image_data_1.parquet","test_image_data_2.parquet","test_image_data_3.parquet"]
def normalize_img(img):
img = tf.cast(img, dtype=tf.float32)
 return(img / 127.5)- 1.0
def decode_image(image):
image = tf.image.decode_jpeg(image, channels=CHANNELS)
 image = tf.reshape(image, [HEIGHT, WIDTH, CHANNELS])
 return image
def read_tfrecord(example):
tfrecord_format = {
'image': tf.io.FixedLenFeature([], tf.string)
 }
example = tf.io.parse_single_example(example, tfrecord_format)
 image = decode_image(example['image'])
 return image
def load_dataset(filenames):
dataset = tf.data.TFRecordDataset(filenames)
 dataset = dataset.map(read_tfrecord, num_parallel_calls=AUTO)
 return dataset
def get_dataset(filenames, augment=None, repeat=True, shuffle=True, batch_size=1):
dataset = load_dataset(filenames)
 if augment:
dataset = dataset.map(augment, num_parallel_calls=AUTO)
 dataset = dataset.map(normalize_img, num_parallel_calls=AUTO)
 if repeat:
dataset = dataset.repeat()
if shuffle:
dataset = dataset.shuffle(512)
 dataset = dataset.batch(batch_size)
 dataset = dataset.cache()
dataset = dataset.prefetch(AUTO)
 return dataset
def display_samples(ds, n_samples):
ds_iter = iter(ds)
 for n_sample in range(n_samples):
example_sample = next(ds_iter)
 plt.subplot(121)
 plt.imshow(example_sample[0] * 0.5 + 0.5)
 plt.axis('off')
 plt.show()
def display_generated_samples(ds, model, n_samples):
ds_iter = iter(ds)
 for n_sample in range(n_samples):
example_sample = next(ds_iter)
 generated_sample = model.predict(example_sample)
 f = plt.figure(figsize=(12, 12))
plt.subplot(121)
 plt.title('Input image')
 plt.imshow(example_sample[0] * 0.5 + 0.5)
 plt.axis('off')
 plt.subplot(122)
 plt.title('Generated image')
 plt.imshow(generated_sample[0] * 0.5 + 0.5)
 plt.axis('off')
 plt.show()
def evaluate_cycle(ds, generator_a, generator_b, n_samples=1):
fig, axes = plt.subplots(n_samples, 3, figsize=(22,(n_samples*6)))
 axes = axes.flatten()
ds_iter = iter(ds)
 for n_sample in range(n_samples):
idx = n_sample*3
example_sample = next(ds_iter)
 generated_a_sample = generator_a.predict(example_sample)
 generated_b_sample = generator_b.predict(generated_a_sample)
 axes[idx].set_title('Input image', fontsize=18)
 axes[idx].imshow(example_sample[0] * 0.5 + 0.5)
 axes[idx].axis('off')
 axes[idx+1].set_title('Generated image', fontsize=18)
 axes[idx+1].imshow(generated_a_sample[0] * 0.5 + 0.5)
 axes[idx+1].axis('off')
 axes[idx+2].set_title('Cycled image', fontsize=18)
 axes[idx+2].imshow(generated_b_sample[0] * 0.5 + 0.5)
 axes[idx+2].axis('off')
 plt.show()
def create_gif(images_path, gif_path):
images = []
filenames = glob.glob(images_path)
 filenames.sort(key=lambda x: int(''.join(filter(str.isdigit, x))))
for epoch, filename in enumerate(filenames):
img = PIL.ImageDraw.Image.open(filename)
 ImageDraw.Draw(img ).text(( 0, 0),
f'Epoch {epoch+1}')
 images.append(img)
 imageio.mimsave(gif_path, images, fps=2)
 def predict_and_save(input_ds, generator_model, output_path):
i = 1
for img in input_ds:
prediction = generator_model(img, training=False)[0].numpy()
prediction =(prediction * 127.5 + 127.5 ).astype(np.uint8)
 im = PIL.Image.fromarray(prediction)
 im.save(f'{output_path}{str(i)}.jpg')
 i += 1<categorify>
DIR = ".. /input/bengaliai-cv19/"
conv_initializer = tf.random_normal_initializer(mean=0.0, stddev=0.02)
 gamma_initializer = tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.02)
 def encoder_block(input_layer, filters, size=3, strides=2, apply_instancenorm=True, activation=L.ReLU() , name='block_x'):
block = L.Conv2D(filters, size,
strides=strides,
padding='same',
use_bias=False,
kernel_initializer=conv_initializer,
name=f'encoder_{name}' )(input_layer)
 if apply_instancenorm:
block = tfa.layers.InstanceNormalization(gamma_initializer=gamma_initializer )(block)
 block = activation(block)
 return block
def transformer_block(input_layer, size=3, strides=1, name='block_x'):
filters = input_layer.shape[-1]
block = L.Conv2D(filters, size, strides=strides, padding='same', use_bias=False,
kernel_initializer=conv_initializer, name=f'transformer_{name}_1' )(input_layer)
 block = L.ReLU()(block)
 block = L.Conv2D(filters, size, strides=strides, padding='same', use_bias=False,
kernel_initializer=conv_initializer, name=f'transformer_{name}_2' )(block)
 block = L.Add()([block, input_layer])
 return block
def decoder_block(input_layer, filters, size=3, strides=2, apply_instancenorm=True, name='block_x'):
block = L.Conv2DTranspose(filters, size,
strides=strides,
padding='same',
use_bias=False,
kernel_initializer=conv_initializer,
name=f'decoder_{name}' )(input_layer)
 if apply_instancenorm:
block = tfa.layers.InstanceNormalization(gamma_initializer=gamma_initializer )(block)
 block = L.ReLU()(block)
 return block
def decoder_rc_block(input_layer, filters, size=3, strides=1, apply_instancenorm=True, name='block_x'):
block = tf.image.resize(images=input_layer, method='bilinear',
size=(input_layer.shape[1]*2, input_layer.shape[2]*2))
block = L.Conv2D(filters, size,
strides=strides,
padding='same',
use_bias=False,
kernel_initializer=conv_initializer,
name=f'decoder_{name}' )(block)
 if apply_instancenorm:
block = tfa.layers.InstanceNormalization(gamma_initializer=gamma_initializer )(block)
 block = L.ReLU()(block)
 return block<categorify>
class EvalDataset(Dataset):
def __init__(self,df,transform=None,aug=None,norm=None):
self.norm = norm
self.df = df
self.data = 255 - self.df.iloc[:, 1:].values.reshape(-1, 137, 236 ).astype(np.uint8)
 self.transform = transform
self.aug = aug
def __getitem__(self, idx):
name = self.df.iloc[idx,0]
image_arr = cv2.resize(self.data[idx],(128,128))
image_arr = image_arr/255.0
if self.norm != None:
mean = self.norm['mean']
std = self.norm['std']
image_arr =(image_arr - mean)/std
image_arr = torch.from_numpy(image_arr.astype(np.float32))
return image_arr.unsqueeze(0 ).repeat(3,1,1),name
def __len__(self):
return len(self.data )
def generator_fn(height=HEIGHT, width=WIDTH, channels=CHANNELS, transformer_blocks=TRANSFORMER_BLOCKS):
OUTPUT_CHANNELS = 3
inputs = L.Input(shape=[height, width, channels], name='input_image')
 enc_1 = encoder_block(inputs, 64, 7, 1, apply_instancenorm=False, activation=L.ReLU() , name='block_1')
 enc_2 = encoder_block(enc_1, 128, 3, 2, apply_instancenorm=True, activation=L.ReLU() , name='block_2')
 enc_3 = encoder_block(enc_2, 256, 3, 2, apply_instancenorm=True, activation=L.ReLU() , name='block_3')
 x = enc_3
for n in range(transformer_blocks):
x = transformer_block(x, 3, 1, name=f'block_{n+1}')
 x_skip = L.Concatenate(name='enc_dec_skip_1' )([x, enc_3])
 dec_1 = decoder_block(x_skip, 128, 3, 2, apply_instancenorm=True, name='block_1')
 x_skip = L.Concatenate(name='enc_dec_skip_2' )([dec_1, enc_2])
 dec_2 = decoder_block(x_skip, 64, 3, 2, apply_instancenorm=True, name='block_2')
 x_skip = L.Concatenate(name='enc_dec_skip_3' )([dec_2, enc_1])
 outputs = last = L.Conv2D(OUTPUT_CHANNELS, 7,
strides=1, padding='same',
kernel_initializer=conv_initializer,
use_bias=False,
activation='tanh',
name='decoder_output_block' )(x_skip)
 generator = Model(inputs, outputs)
 return generator
sample_generator = generator_fn()
sample_generator.summary()<categorify>
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
def discriminator_fn(height=HEIGHT, width=WIDTH, channels=CHANNELS):
inputs = L.Input(shape=[height, width, channels], name='input_image')
 x = encoder_block(inputs, 64, 4, 2, apply_instancenorm=False, activation=L.LeakyReLU(0.2), name='block_1')
 x = encoder_block(x, 128, 4, 2, apply_instancenorm=True, activation=L.LeakyReLU(0.2), name='block_2')
 x = encoder_block(x, 256, 4, 2, apply_instancenorm=True, activation=L.LeakyReLU(0.2), name='block_3')
 x = encoder_block(x, 512, 4, 1, apply_instancenorm=True, activation=L.LeakyReLU(0.2), name='block_4')
 outputs = L.Conv2D(1, 4, strides=1, padding='valid', kernel_initializer=conv_initializer )(x)
 discriminator = Model(inputs, outputs)
 return discriminator
sample_discriminator = discriminator_fn()
sample_discriminator.summary()<define_search_model>
class MyModel(nn.Module):
def __init__(self):
super().__init__()
model = models.resnet34()
self.model = nn.Sequential(*list(model.children())[:-1])
 self.fc_g = nn.Sequential(nn.Linear(512,200), nn.ReLU() , nn.Dropout(p=0.2), nn.Linear(200,168))
self.fc_v = nn.Sequential(nn.Linear(512,200), nn.ReLU() , nn.Dropout(p=0.2), nn.Linear(200,11))
self.fc_c = nn.Sequential(nn.Linear(512,200), nn.ReLU() , nn.Dropout(p=0.2), nn.Linear(200,7))
def forward(self,x):
x = self.model(x)
 x = torch.flatten(x,1)
 g = self.fc_g(x)
 v = self.fc_v(x)
 c = self.fc_c(x)
 return g,v,c
with strategy.scope() :
monet_generator = generator_fn(height=HEIGHT_RESIZE, width=WIDTH_RESIZE, transformer_blocks=TRANSFORMER_BLOCKS)
 photo_generator = generator_fn(height=HEIGHT_RESIZE, width=WIDTH_RESIZE, transformer_blocks=TRANSFORMER_BLOCKS)
 monet_discriminator = discriminator_fn(height=HEIGHT_RESIZE, width=WIDTH_RESIZE)
 photo_discriminator = discriminator_fn(height=HEIGHT_RESIZE, width=WIDTH_RESIZE)
 class CycleGan(Model):
def __init__(
 self,
monet_generator,
photo_generator,
monet_discriminator,
photo_discriminator,
lambda_cycle=10, 
):
super(CycleGan, self ).__init__()
self.m_gen = monet_generator
self.p_gen = photo_generator
self.m_disc = monet_discriminator
self.p_disc = photo_discriminator
self.lambda_cycle = lambda_cycle
def compile(
 self,
m_gen_optimizer,
p_gen_optimizer,
m_disc_optimizer,
p_disc_optimizer,
gen_loss_fn,
disc_loss_fn,
cycle_loss_fn,
identity_loss_fn 
):
super(CycleGan, self ).compile()
self.m_gen_optimizer = m_gen_optimizer
self.p_gen_optimizer = p_gen_optimizer
self.m_disc_optimizer = m_disc_optimizer
self.p_disc_optimizer = p_disc_optimizer
self.gen_loss_fn = gen_loss_fn
self.disc_loss_fn = disc_loss_fn
self.cycle_loss_fn = cycle_loss_fn
self.identity_loss_fn = identity_loss_fn
def train_step(self, batch_data):
real_monet, real_photo = batch_data
with tf.GradientTape(persistent=True)as tape:
fake_monet = self.m_gen(real_photo, training=True)
 cycled_photo = self.p_gen(fake_monet, training=True)
 fake_photo = self.p_gen(real_monet, training=True)
 cycled_monet = self.m_gen(fake_photo, training=True)
 same_monet = self.m_gen(real_monet, training=True)
 same_photo = self.p_gen(real_photo, training=True)
 disc_real_monet = self.m_disc(real_monet, training=True)
 disc_real_photo = self.p_disc(real_photo, training=True)
 disc_fake_monet = self.m_disc(fake_monet, training=True)
 disc_fake_photo = self.p_disc(fake_photo, training=True)
 monet_gen_loss = self.gen_loss_fn(disc_fake_monet)
 photo_gen_loss = self.gen_loss_fn(disc_fake_photo)
 total_cycle_loss = self.cycle_loss_fn(real_monet, cycled_monet, self.lambda_cycle)+ self.cycle_loss_fn(real_photo, cycled_photo, self.lambda_cycle)
 total_monet_gen_loss = monet_gen_loss + total_cycle_loss + self.identity_loss_fn(real_monet, same_monet, self.lambda_cycle)
 total_photo_gen_loss = photo_gen_loss + total_cycle_loss + self.identity_loss_fn(real_photo, same_photo, self.lambda_cycle)
 monet_disc_loss = self.disc_loss_fn(disc_real_monet, disc_fake_monet)
 photo_disc_loss = self.disc_loss_fn(disc_real_photo, disc_fake_photo)
 monet_generator_gradients = tape.gradient(total_monet_gen_loss,
self.m_gen.trainable_variables)
 photo_generator_gradients = tape.gradient(total_photo_gen_loss,
self.p_gen.trainable_variables)
 monet_discriminator_gradients = tape.gradient(monet_disc_loss,
self.m_disc.trainable_variables)
 photo_discriminator_gradients = tape.gradient(photo_disc_loss,
self.p_disc.trainable_variables)
 self.m_gen_optimizer.apply_gradients(zip(monet_generator_gradients,
self.m_gen.trainable_variables))
self.p_gen_optimizer.apply_gradients(zip(photo_generator_gradients,
self.p_gen.trainable_variables))
self.m_disc_optimizer.apply_gradients(zip(monet_discriminator_gradients,
self.m_disc.trainable_variables))
self.p_disc_optimizer.apply_gradients(zip(photo_discriminator_gradients,
self.p_disc.trainable_variables))
return {'monet_gen_loss': total_monet_gen_loss,
'photo_gen_loss': total_photo_gen_loss,
'monet_disc_loss': monet_disc_loss,
'photo_disc_loss': photo_disc_loss
}<compute_train_metric>
def kaggle_out(model1,model2,model3,dataloader,device):
model1.eval()
model2.eval()
model3.eval()
grapheme_output = []
vowel_output = []
consonant_output = []
img_id_ls = []
for data in dataloader:
inputs = data[0]
image_id = data[1]
inputs = inputs.to(device)
 with torch.no_grad() :
g1,v1,c1 = model1(inputs)
 g2,v2,c2 = model2(inputs)
 g3,v3,c3 = model3(inputs)
 g = g1+g2+g3
v = v1+v2+v3
c = c1+c2+c3
grapheme_preds = g.argmax(dim=1)
 vowel_preds = v.argmax(dim=1)
 consonant_preds = c.argmax(dim=1)
 for i in range(len(image_id)) :
img_id_ls.append(image_id[i])
 grapheme_output.append(grapheme_preds[i].cpu().detach().item())
 vowel_output.append(vowel_preds[i].cpu().detach().item())
 consonant_output.append(consonant_preds[i].cpu().detach().item())
 return img_id_ls,grapheme_output,vowel_output,consonant_output
with strategy.scope() :
def discriminator_loss(real, generated):
real_loss = losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE )(tf.ones_like(real), real)
 generated_loss = losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE )(tf.zeros_like(generated), generated)
 total_disc_loss = real_loss + generated_loss
return total_disc_loss * 0.5
def generator_loss(generated):
return losses.BinaryCrossentropy(from_logits=True, reduction=losses.Reduction.NONE )(tf.ones_like(generated), generated)
 with strategy.scope() :
def calc_cycle_loss(real_image, cycled_image, LAMBDA):
loss1 = tf.reduce_mean(tf.abs(real_image - cycled_image))
return LAMBDA * loss1
with strategy.scope() :
def identity_loss(real_image, same_image, LAMBDA):
loss = tf.reduce_mean(tf.abs(real_image - same_image))
return LAMBDA * 0.5 * loss<choose_model_class>
results = []
mean = 13.4/255
std = 40.8/255
model1 = MyModel()
model1.load_state_dict(torch.load('.. /input/cv-resnets/model_epoch34.pt'))
model1.to(device)
 model2 = MyModel()
model2.load_state_dict(torch.load('.. /input/cv-resnets/model_epoch69.pt'))
model2.to(device)
 model3 = MyModel()
model3.load_state_dict(torch.load('.. /input/cv-resnets/model_epoch104.pt'))
model3.to(device)
 for inp in inputs:
df = pd.read_parquet(DIR+inp)
 kaggle_dataset = EvalDataset(df,norm={'mean':mean,'std':std})
 dl = DataLoader(kaggle_dataset,batch_size=128,num_workers=4)
 img,gra,vow,con = kaggle_out(model1,model2,model3,dl,device)
 for i in range(len(img)) :
results.append(( f"{img[i]}_grapheme_root",gra[i]))
results.append(( f"{img[i]}_vowel_diacritic",vow[i]))
results.append(( f"{img[i]}_consonant_diacritic",con[i]))
df2 = pd.DataFrame(results, columns=["row_id", "target"] )
with strategy.scope() :
lr_monet_gen = lambda: linear_schedule_with_warmup(tf.cast(monet_generator_optimizer.iterations, tf.float32))
lr_photo_gen = lambda: linear_schedule_with_warmup(tf.cast(photo_generator_optimizer.iterations, tf.float32))
monet_generator_optimizer = optimizers.Adam(learning_rate=lr_monet_gen, beta_1=0.5)
 photo_generator_optimizer = optimizers.Adam(learning_rate=lr_photo_gen, beta_1=0.5)
 lr_monet_disc = lambda: linear_schedule_with_warmup(tf.cast(monet_discriminator_optimizer.iterations, tf.float32))
lr_photo_disc = lambda: linear_schedule_with_warmup(tf.cast(photo_discriminator_optimizer.iterations, tf.float32))
monet_discriminator_optimizer = optimizers.Adam(learning_rate=lr_monet_disc, beta_1=0.5)
 photo_discriminator_optimizer = optimizers.Adam(learning_rate=lr_photo_disc, beta_1=0.5)
 gan_model = CycleGan(monet_generator, photo_generator,
monet_discriminator, photo_discriminator)
 gan_model.compile(m_gen_optimizer=monet_generator_optimizer,
p_gen_optimizer=photo_generator_optimizer,
m_disc_optimizer=monet_discriminator_optimizer,
p_disc_optimizer=photo_discriminator_optimizer,
gen_loss_fn=generator_loss,
disc_loss_fn=discriminator_loss,
cycle_loss_fn=calc_cycle_loss,
identity_loss_fn=identity_loss )<create_dataframe>
sub = df2
<train_model><EOS> 
sub.to_csv("submission.csv", index=False )