latr-vqa / modeling.py
iakarshu's picture
Upload modeling.py
1828dbd
import torch.nn as nn
import torch
from transformers import T5ForConditionalGeneration, ViTModel
import pytorch_lightning as pl
# Defining the pytorch model
class LaTr_for_pretraining(nn.Module):
def __init__(self, config, classify=False):
super(LaTr_for_pretraining, self).__init__()
self.vocab_size = config['vocab_size']
model = T5ForConditionalGeneration.from_pretrained(config['t5_model'])
# Removing the Embedding layer
dummy_encoder = list(nn.Sequential(
*list(model.encoder.children())[1:]).children())
# Removing the Embedding Layer
dummy_decoder = list(nn.Sequential(
*list(model.decoder.children())[1:]).children())
# Using the T5 Encoder
self.list_encoder = nn.Sequential(*list(dummy_encoder[0]))
self.residue_encoder = nn.Sequential(*list(dummy_encoder[1:]))
self.list_decoder = nn.Sequential(*list(dummy_decoder[0]))
self.residue_decoder = nn.Sequential(*list(dummy_decoder[1:]))
# We use the embeddings of T5 for encoding the tokenized words
self.language_emb = nn.Embedding.from_pretrained(model.shared.weight)
self.top_left_x = nn.Embedding(
config['max_2d_position_embeddings'], config['hidden_state'])
self.bottom_right_x = nn.Embedding(
config['max_2d_position_embeddings'], config['hidden_state'])
self.top_left_y = nn.Embedding(
config['max_2d_position_embeddings'], config['hidden_state'])
self.bottom_right_y = nn.Embedding(
config['max_2d_position_embeddings'], config['hidden_state'])
self.width_emb = nn.Embedding(
config['max_2d_position_embeddings'], config['hidden_state'])
self.height_emb = nn.Embedding(
config['max_2d_position_embeddings'], config['hidden_state'])
self.classify = classify
self.classification_layer = nn.Linear(
config['hidden_state'], config['classes'])
def forward(self, tokens, coordinates, predict_proba=False, predict_class=False):
batch_size = len(tokens)
embeded_feature = self.language_emb(tokens)
top_left_x_feat = self.top_left_x(coordinates[:, :, 0])
top_left_y_feat = self.top_left_y(coordinates[:, :, 1])
bottom_right_x_feat = self.bottom_right_x(coordinates[:, :, 2])
bottom_right_y_feat = self.bottom_right_y(coordinates[:, :, 3])
width_feat = self.width_emb(coordinates[:, :, 4])
height_feat = self.height_emb(coordinates[:, :, 5])
total_feat = embeded_feature + top_left_x_feat + top_left_y_feat + \
bottom_right_x_feat + bottom_right_y_feat + width_feat + height_feat
# Extracting the feature
for layer in self.list_encoder:
total_feat = layer(total_feat)[0]
total_feat = self.residue_encoder(total_feat)
for layer in self.list_decoder:
total_feat = layer(total_feat)[0]
total_feat = self.residue_decoder(total_feat)
if self.classify:
total_feat = self.classification_layer(total_feat)
if predict_proba:
return total_feat.softmax(axis=-1)
if predict_class:
return total_feat.argmax(axis=-1)
return total_feat
class LaTr_for_finetuning(nn.Module):
def __init__(self, config, address_to_pre_trained_weights=None):
super(LaTr_for_finetuning, self).__init__()
self.config = config
self.vocab_size = config['vocab_size']
self.pre_training_model = LaTr_for_pretraining(config)
if address_to_pre_trained_weights is not None:
self.pre_training_model.load_state_dict(
torch.load(address_to_pre_trained_weights))
self.vit = ViTModel.from_pretrained(
"google/vit-base-patch16-224-in21k")
# In the fine-tuning stage of vit, except the last layer, all the layers were freezed
self.classification_head = nn.Linear(
config['hidden_state'], config['classes'])
def forward(self, lang_vect, spatial_vect, quest_vect, img_vect):
# The below block of code calculates the language and spatial featuer
embeded_feature = self.pre_training_model.language_emb(lang_vect)
top_left_x_feat = self.pre_training_model.top_left_x(
spatial_vect[:, :, 0])
top_left_y_feat = self.pre_training_model.top_left_y(
spatial_vect[:, :, 1])
bottom_right_x_feat = self.pre_training_model.bottom_right_x(
spatial_vect[:, :, 2])
bottom_right_y_feat = self.pre_training_model.bottom_right_y(
spatial_vect[:, :, 3])
width_feat = self.pre_training_model.width_emb(spatial_vect[:, :, 4])
height_feat = self.pre_training_model.height_emb(spatial_vect[:, :, 5])
spatial_lang_feat = embeded_feature + top_left_x_feat + top_left_y_feat + \
bottom_right_x_feat + bottom_right_y_feat + width_feat + height_feat
# Extracting the image feature, using the Vision Transformer
img_feat = self.vit(img_vect).last_hidden_state
# Extracting the question vector
quest_feat = self.pre_training_model.language_emb(quest_vect)
# Concating the three features, and then passing it through the T5 Transformer
final_feat = torch.cat(
[img_feat, spatial_lang_feat, quest_feat], axis=-2)
# Passing through the T5 Transformer
for layer in self.pre_training_model.list_encoder:
final_feat = layer(final_feat)[0]
final_feat = self.pre_training_model.residue_encoder(final_feat)
for layer in self.pre_training_model.list_decoder:
final_feat = layer(final_feat)[0]
final_feat = self.pre_training_model.residue_decoder(final_feat)
answer_vector = self.classification_head(
final_feat)[:, :self.config['seq_len'], :]
return answer_vector
def polynomial(base_lr, iter, max_iter=1e5, power=1):
return base_lr * ((1 - float(iter) / max_iter) ** power)
class LaTrForVQA(pl.LightningModule):
def __init__(self, config, learning_rate=1e-4, max_steps=100000//2):
super(LaTrForVQA, self).__init__()
self.config = config
self.save_hyperparameters()
self.latr = LaTr_for_finetuning(config)
self.training_losses = []
self.validation_losses = []
self.max_steps = max_steps
def configure_optimizers(self):
return torch.optim.AdamW(self.parameters(), lr=self.hparams['learning_rate'])
def forward(self, batch_dict):
boxes = batch_dict['boxes']
img = batch_dict['img']
question = batch_dict['question']
words = batch_dict['tokenized_words']
answer_vector = self.latr(lang_vect=words,
spatial_vect=boxes,
img_vect=img,
quest_vect=question
)
return answer_vector
def calculate_metrics(self, prediction, labels):
# Calculate the accuracy score between the prediction and ground label for a batch, with considering the pad sequence
batch_size = len(prediction)
ac_score = 0
for (pred, gt) in zip(prediction, labels):
ac_score += calculate_acc_score(pred.detach().cpu(),
gt.detach().cpu())
ac_score = ac_score/batch_size
return ac_score
def training_step(self, batch, batch_idx):
answer_vector = self.forward(batch)
# https://discuss.huggingface.co/t/bertformaskedlm-s-loss-and-scores-how-the-loss-is-computed/607/2
loss = nn.CrossEntropyLoss(ignore_index=0)(
answer_vector.reshape(-1, self.config['classes']), batch['answer'].reshape(-1))
_, preds = torch.max(answer_vector, dim=-1)
# Calculating the accuracy score
train_acc = self.calculate_metrics(preds, batch['answer'])
train_acc = torch.tensor(train_acc)
# Logging
self.log('train_ce_loss', loss, prog_bar=True)
self.log('train_acc', train_acc, prog_bar=True)
self.training_losses.append(loss.item())
return loss
def validation_step(self, batch, batch_idx):
logits = self.forward(batch)
loss = nn.CrossEntropyLoss(ignore_index=0)(
logits.reshape(-1, self.config['classes']), batch['answer'].reshape(-1))
_, preds = torch.max(logits, dim=-1)
# Validation Accuracy
val_acc = self.calculate_metrics(preds.cpu(), batch['answer'].cpu())
val_acc = torch.tensor(val_acc)
# Logging
self.log('val_ce_loss', loss, prog_bar=True)
self.log('val_acc', val_acc, prog_bar=True)
self.validation_losses.append(loss.item())
return {'val_loss': loss, 'val_acc': val_acc}
def optimizer_step(self, epoch_nb, batch_nb, optimizer, optimizer_i, opt_closure=None, on_tpu=False,
using_native_amp=False, using_lbfgs=False):
# Warmup for 1000 steps
if self.trainer.global_step < 1000:
lr_scale = min(1., float(self.trainer.global_step + 1) / 1000.)
for pg in optimizer.param_groups:
pg['lr'] = lr_scale * self.hparams.learning_rate
# Linear Decay
else:
for pg in optimizer.param_groups:
pg['lr'] = polynomial(
self.hparams.learning_rate, self.trainer.global_step, max_iter=self.max_steps)
optimizer.step(opt_closure)
optimizer.zero_grad()
def validation_epoch_end(self, outputs):
val_loss = torch.stack([x['val_loss'] for x in outputs]).mean()
val_acc = torch.stack([x['val_acc'] for x in outputs]).mean()
self.log('val_loss_epoch_end', val_loss, on_epoch=True, sync_dist=True)
self.log('val_acc_epoch_end', val_acc, on_epoch=True, sync_dist=True)