Semantic segmentation using LoRA
Semantic segmentation using LoRA
This guide demonstrates how to use LoRA, a low-rank approximation technique, to finetune a SegFormer model variant for semantic segmentation. By using LoRA from 🌍 PEFT, we can reduce the number of trainable parameters in the SegFormer model to only 14% of the original trainable parameters.
LoRA achieves this reduction by adding low-rank “update matrices” to specific blocks of the model, such as the attention blocks. During fine-tuning, only these matrices are trained, while the original model parameters are left unchanged. At inference time, the update matrices are merged with the original model parameters to produce the final classification result.
For more information on LoRA, please refer to the original LoRA paper.
Install dependencies
Install the libraries required for model training:
Copied
Authenticate to share your model
To share the finetuned model with the community at the end of the training, authenticate using your 🌍 token. You can obtain your token from your account settings.
Copied
Load a dataset
To ensure that this example runs within a reasonable time frame, here we are limiting the number of instances from the training set of the SceneParse150 dataset to 150.
Copied
Next, split the dataset into train and test sets.
Copied
Prepare label maps
Create a dictionary that maps a label id to a label class, which will be useful when setting up the model later:
label2id
: maps the semantic classes of the dataset to integer ids.id2label
: maps integer ids back to the semantic classes.
Copied
Prepare datasets for training and evaluation
Next, load the SegFormer image processor to prepare the images and annotations for the model. This dataset uses the zero-index as the background class, so make sure to set do_reduce_labels=True
to subtract one from all labels since the background class is not among the 150 classes.
Copied
Add a function to apply data augmentation to the images, so that the model is more robust against overfitting. Here we use the ColorJitter function from torchvision to randomly change the color properties of an image.
Copied
Add a function to handle grayscale images and ensure that each input image has three color channels, regardless of whether it was originally grayscale or RGB. The function converts RGB images to array as is, and for grayscale images that have only one color channel, the function replicates the same channel three times using np.tile()
before converting the image into an array.
Copied
Finally, combine everything in two functions that you’ll use to transform training and validation data. The two functions are similar except data augmentation is applied only to the training data.
Copied
To apply the preprocessing functions over the entire dataset, use the 🌍 Datasets set_transform
function:
Copied
Create evaluation function
Including a metric during training is helpful for evaluating your model’s performance. You can load an evaluation method with the 🌍 Evaluate library. For this task, use the mean Intersection over Union (IoU) metric (see the 🌍 Evaluate quick tour to learn more about how to load and compute a metric):
Copied
Load a base model
Before loading a base model, let’s define a helper function to check the total number of parameters a model has, as well as how many of them are trainable.
Copied
Choose a base model checkpoint. For this example, we use the SegFormer B0 variant. In addition to the checkpoint, pass the label2id
and id2label
dictionaries to let the AutoModelForSemanticSegmentation
class know that we’re interested in a custom base model where the decoder head should be randomly initialized using the classes from the custom dataset.
Copied
At this point you can check with the print_trainable_parameters
helper function that all 100% parameters in the base model (aka model
) are trainable.
Wrap the base model as a PeftModel for LoRA training
To leverage the LoRa method, you need to wrap the base model as a PeftModel
. This involves two steps:
Defining LoRa configuration with
LoraConfig
Wrapping the original
model
withget_peft_model()
using the config defined in the step above.
Copied
Let’s review the LoraConfig
. To enable LoRA technique, we must define the target modules within LoraConfig
so that PeftModel
can update the necessary matrices. Specifically, we want to target the query
and value
matrices in the attention blocks of the base model. These matrices are identified by their respective names, “query” and “value”. Therefore, we should specify these names in the target_modules
argument of LoraConfig
.
After we wrap our base model model
with PeftModel
along with the config, we get a new model where only the LoRA parameters are trainable (so-called “update matrices”) while the pre-trained parameters are kept frozen. These include the parameters of the randomly initialized classifier parameters too. This is NOT we want when fine-tuning the base model on our custom dataset. To ensure that the classifier parameters are also trained, we specify modules_to_save
. This also ensures that these modules are serialized alongside the LoRA trainable parameters when using utilities like save_pretrained()
and push_to_hub()
.
In addition to specifying the target_modules
within LoraConfig
, we also need to specify the modules_to_save
. When we wrap our base model with PeftModel
and pass the configuration, we obtain a new model in which only the LoRA parameters are trainable, while the pre-trained parameters and the randomly initialized classifier parameters are kept frozen. However, we do want to train the classifier parameters. By specifying the modules_to_save
argument, we ensure that the classifier parameters are also trainable, and they will be serialized alongside the LoRA trainable parameters when we use utility functions like save_pretrained()
and push_to_hub()
.
Let’s review the rest of the parameters:
r
: The dimension used by the LoRA update matrices.alpha
: Scaling factor.bias
: Specifies if thebias
parameters should be trained.None
denotes none of thebias
parameters will be trained.
When all is configured, and the base model is wrapped, the print_trainable_parameters
helper function lets us explore the number of trainable parameters. Since we’re interested in performing parameter-efficient fine-tuning, we should expect to see a lower number of trainable parameters from the lora_model
in comparison to the original model
which is indeed the case here.
You can also manually verify what modules are trainable in the lora_model
.
Copied
This confirms that only the LoRA parameters appended to the attention blocks and the decode_head
parameters are trainable.
Train the model
Start by defining your training hyperparameters in TrainingArguments
. You can change the values of most parameters however you prefer. Make sure to set remove_unused_columns=False
, otherwise the image column will be dropped, and it’s required here. The only other required parameter is output_dir
which specifies where to save your model. At the end of each epoch, the Trainer
will evaluate the IoU metric and save the training checkpoint.
Note that this example is meant to walk you through the workflow when using PEFT for semantic segmentation. We didn’t perform extensive hyperparameter tuning to achieve optimal results.
Copied
Pass the training arguments to Trainer
along with the model, dataset, and compute_metrics
function. Call train()
to finetune your model.
Copied
Save the model and run inference
Use the save_pretrained()
method of the lora_model
to save the LoRA-only parameters locally. Alternatively, use the push_to_hub()
method to upload these parameters directly to the BOINC AI Hub (as shown in the Image classification using LoRA task guide).
Copied
We can see that the LoRA-only parameters are just 2.2 MB in size! This greatly improves the portability when using very large models.
Copied
Let’s now prepare an inference_model
and run inference.
Copied
Get an image:
Copied
Preprocess the image to prepare for inference.
Copied
Run inference with the encoded image.
Copied
Next, visualize the results. We need a color palette for this. Here, we use ade_palette(). As it is a long array, so we don’t include it in this guide, please copy it from the TensorFlow Model Garden repository.
Copied
As you can see, the results are far from perfect, however, this example is designed to illustrate the end-to-end workflow of fine-tuning a semantic segmentation model with LoRa technique, and is not aiming to achieve state-of-the-art results. The results you see here are the same as you would get if you performed full fine-tuning on the same setup (same model variant, same dataset, same training schedule, etc.), except LoRA allows to achieve them with a fraction of total trainable parameters and in less time.
If you wish to use this example and improve the results, here are some things that you can try:
Increase the number of training samples.
Try a larger SegFormer model variant (explore available model variants on the BOINC AI Hub).
Try different values for the arguments available in
LoraConfig
.Tune the learning rate and batch size.
Last updated