Using transformers (to perform NLP tasks) in computer vision

Instead of tokens, subsets of an image (patches) are embedded (linearly embedded into a vector) → add positional embeddings → fed into a transformer

Added a Class token (similar to [CLS] token) = the first token (0) which is meant to represent a representation of the entire image (using image classification)

Biggest difference? Instead of word embeddings, there are patch embeddings

vision

Source: https://arxiv.org/pdf/2010.11929.pdf

Model is pretrained on ImageNet-21k (14 million images sorted into 21843 classes)

from transformer import ViTModel 
vit_model = ViTModel.from_pretrained('google/vit-base-patch16-224-in21k') #load model

16 by 16 patches, Size 224 by 224 pixels, 21k dataset

feature


Fine tune - Image captioning with multiple transformers

Using 2 transformers: Images loaded based on ViT model (encoder) → cross attention → DistilGPT2 model (decoder) → images can be captioned

Loading up encoder and decoder models:

model = VisionEncoderDecoderModel.from_encoder_decoder_pretrained('google/vit-base-patch16-224-in21k', 'distilgpt2')
#encoder = vit #decoder = distilGPT2

Since ViT model does not have cross attention, it does have not those weights, which makes the main goal of fine tuning to adjust the weights.

Feature extractor → compose object (take in raw image, resize image to 224 size, convert to a tensor, normalize it to mean and std dev):

compose = transforms.Compose([
        transforms.Resize((224,224)),
        transforms.ToTensor(),
        transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
    ])

Now if we wanted to write a function that allows Gpt2 tokenizes captions:

padtok

Keep in mind ViT expects image pixels, not token_ids

When you create a custom model and combine transformers, must set the model’s pad token to be same as GPT2’s pad token, same with start token.

sametoken

Because we’re training it’ll take some time. To make this quicker, we can freeze the last two layers in the Transformer model

# freeze all but the last three layers in the ViT
for name, param in model.encoder.named_parameters():
    if 'encoder.layer.10' in name:
        break
    param.requires_grad = False

Now time to train:

trained

Always run evaluate first to see the loss #loss should decrease the next time

trainer.evaluate()
#loss < 1 indicates the model is predicting correct tokens/pixels accurately

losstrain

Done training → inference

A singular random photo should not be going to inference, this will add bias. A new composition needs to be made for inference.

newtrans

Setting number of beams to 3 (higher than 1) = tells model to predict next 3 words

Now let’s say you give it an image and ask it to caption this. It’s doing alright and able to make a caption about a brown dog running in the woods…but for a photo of a women on a cliff doing yoga:

notaman

The clear issue is that all 5 sentences say man. This is because the top common words are all about men, dogs, and water.

whynotaman

Most of the data it’s seen does not have women, which is why fine-tuning it is important to provide accurate data. Models are efficient, but need to make accurate statements to be useful.

This model needs to be trained more, but once it’s at a low loss function, we can push the model to huggingface.


Pushing to Huggingface

If you want to push a model to huggingface, make sure to push the model and tokenizer. You need a huggingface account and api key (in huggingface settings).

trainer.model.push_to_hub(
 repo_path_or_name=MODEL_IDENTIFIER, use_auth_token=api_key
)
trainer.model.push_to_hub(
 repo_path_or_name=MODEL_IDENTIFIER, use_auth_token=api_key
)

When you push to hub, you’re actually creating a commit on git. On huggingface you can create a README.md.

Now after we use it and train it more. To update it on huggingface:

auto_tokenizer = AutoTokenizer.from_pretrained(MODEL_IDENTIFIER)
auto_model  = AutoModelForSequenceClassification.from_pretrained(MODEL_IDENTIFIER)
#Load up our tokenizer and model 
trainer.model.push_to_hub(
 repo_path_or_name=MODEL_IDENTIFIER, use_auth_token=api_key
)
trainer.model.push_to_hub(
 repo_path_or_name=MODEL_IDENTIFIER, use_auth_token=api_key
)

A quicker way…

toxic_clf=pipeline(
 ‘text-classification’, #type of NLP task
 MODEL_IDENTIFIER, #handle of new model
 use_fast=True, #use fast tokenizer
 return_all_scores=True #return probabilities for all classes
)

We’ve seen multiple chatboxes, but transformers architecture can be applied to many use cases. Weights and parameters specific to a model impact the accuracy and loss function. A low loss function indicates the model is ready to be pushed to huggingface. The more we share our models, the more we can learn or be creative when building. This is just the start of the future for transformers.

All code is referenced from Sinan Ozdemir’s Transformer course.

https://github.com/sinanuozdemir/oreilly-transformers-video-series/tree/main/notebooks/deploy