Knowledge Distillation in AutoMM

Open In Colab Open In SageMaker Studio Lab

Pretrained foundation models are becoming increasingly large. However, these models are difficult to deploy due to limited resources available in deployment scenarios. To benefit from large models under this constraint, you transfer the knowledge from the large-scale teacher models to the student model, with knowledge distillation. In this way, the small student model can be practically deployed under real-world scenarios, while the performance will be better than training the student model from scratch thanks to the teacher.

In this tutorial, we introduce how to adopt MultiModalPredictor for knowledge distillation. For the purpose of demonstration, we use the Question-answering NLI dataset, which comprises 104,743 question, answer pairs sampled from question answering datasets. We will demonstrate how to use a large model to guide the learning and improve the performance of a small model in AutoGluon.

Load Dataset

The Question-answering NLI dataset contains sentence pairs in English. In the label column, 0 means that the sentence is not related to the question and 1 means that the sentence is related to the question.

import datasets
from datasets import load_dataset

datasets.logging.disable_progress_bar()

dataset = load_dataset("glue", "qnli")
/home/ci/opt/venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
---------------------------------------------------------------------------
HfUriError                                Traceback (most recent call last)
Cell In[2], line 6
      2 from datasets import load_dataset
      3 
      4 datasets.logging.disable_progress_bar()
      5 
----> 6 dataset = load_dataset("glue", "qnli")

File ~/opt/venv/lib/python3.13/site-packages/datasets/load.py:1696, in load_dataset(path, name, data_dir, data_files, split, cache_dir, features, download_config, download_mode, verification_mode, keep_in_memory, save_infos, revision, token, streaming, num_proc, storage_options, **config_kwargs)
   1691 verification_mode = VerificationMode(
   1692     (verification_mode or VerificationMode.BASIC_CHECKS) if not save_infos else VerificationMode.ALL_CHECKS
   1693 )
   1695 # Create a dataset builder
-> 1696 builder_instance = load_dataset_builder(
   1697     path=path,
   1698     name=name,
   1699     data_dir=data_dir,
   1700     data_files=data_files,
   1701     cache_dir=cache_dir,
   1702     features=features,
   1703     download_config=download_config,
   1704     download_mode=download_mode,
   1705     revision=revision,
   1706     token=token,
   1707     storage_options=storage_options,
   1708     **config_kwargs,
   1709 )
   1711 # Return iterable dataset in case of streaming
   1712 if streaming:

File ~/opt/venv/lib/python3.13/site-packages/datasets/load.py:1323, in load_dataset_builder(path, name, data_dir, data_files, cache_dir, features, download_config, download_mode, revision, token, storage_options, **config_kwargs)
   1321 if features is not None:
   1322     features = _fix_for_backward_compatible_features(features)
-> 1323 dataset_module = dataset_module_factory(
   1324     path,
   1325     revision=revision,
   1326     download_config=download_config,
   1327     download_mode=download_mode,
   1328     data_dir=data_dir,
   1329     data_files=data_files,
   1330     cache_dir=cache_dir,
   1331 )
   1332 # Get dataset builder class
   1333 builder_kwargs = dataset_module.builder_kwargs

File ~/opt/venv/lib/python3.13/site-packages/datasets/load.py:1215, in dataset_module_factory(path, revision, download_config, download_mode, data_dir, data_files, cache_dir, **download_kwargs)
   1210             if isinstance(e1, FileNotFoundError):
   1211                 raise FileNotFoundError(
   1212                     f"Couldn't find any data file at {relative_to_absolute_path(path)}. "
   1213                     f"Couldn't find '{path}' on the Hugging Face Hub either: {type(e1).__name__}: {e1}"
   1214                 ) from None
-> 1215             raise e1 from None
   1216 else:
   1217     raise FileNotFoundError(f"Couldn't find any data file at {relative_to_absolute_path(path)}.")

File ~/opt/venv/lib/python3.13/site-packages/datasets/load.py:1190, in dataset_module_factory(path, revision, download_config, download_mode, data_dir, data_files, cache_dir, **download_kwargs)
   1180     else:
   1181         use_exported_dataset_infos = True
   1182     return HubDatasetModuleFactory(
   1183         path,
   1184         commit_hash=commit_hash,
   1185         data_dir=data_dir,
   1186         data_files=data_files,
   1187         download_config=download_config,
   1188         download_mode=download_mode,
   1189         use_exported_dataset_infos=use_exported_dataset_infos,
-> 1190     ).get_module()
   1191 except GatedRepoError as e:
   1192     message = f"Dataset '{path}' is a gated dataset on the Hub."

File ~/opt/venv/lib/python3.13/site-packages/datasets/load.py:608, in HubDatasetModuleFactory.get_module(self)
    606     download_config.download_desc = "Downloading standalone yaml"
    607 try:
--> 608     standalone_yaml_path = cached_path(
    609         hf_dataset_url(self.name, config.REPOYAML_FILENAME, revision=self.commit_hash),
    610         download_config=download_config,
    611     )
    612     with open(standalone_yaml_path, encoding="utf-8") as f:
    613         standalone_yaml_data = yaml.safe_load(f.read())

File ~/opt/venv/lib/python3.13/site-packages/datasets/utils/file_utils.py:180, in cached_path(url_or_filename, download_config, **download_kwargs)
    174 # Download files from Hugging Face.
    175 # Note: no need to check for https://huggingface.co file URLs since _prepare_path_and_storage_options
    176 # prepares Hugging Face HTTP URLs as hf:// paths already
    177 if url_or_filename.startswith("hf://") and not url_or_filename.startswith("hf://buckets/"):
    178     resolved_path = huggingface_hub.HfFileSystem(
    179         endpoint=config.HF_ENDPOINT, token=download_config.token
--> 180     ).resolve_path(url_or_filename)
    181     try:
    182         output_path = huggingface_hub.HfApi(
    183             endpoint=config.HF_ENDPOINT,
    184             token=download_config.token,
   (...)    194             proxies=download_config.proxies,
    195         )

File ~/opt/venv/lib/python3.13/site-packages/huggingface_hub/hf_file_system.py:305, in HfFileSystem.resolve_path(self, path, revision)
    300 if path.count("/") == 0:
    301     raise ValueError(
    302         f"Repository id must be 'namespace/name', got '{path}'. Single-segment ids (e.g. 'gpt2') are no longer supported."
    303     )
--> 305 parsed = parse_hf_uri(f"{constants.HF_PROTOCOL}{path}")
    307 # --- Buckets ---
    308 if parsed.is_bucket:

File ~/opt/venv/lib/python3.13/site-packages/huggingface_hub/utils/_hf_uris.py:319, in parse_hf_uri(uri, endpoint)
    317 if type_ == "bucket":
    318     return _parse_bucket_body(location, type_, raw=raw)
--> 319 return _parse_repo_body(location, type_, raw=raw)

File ~/opt/venv/lib/python3.13/site-packages/huggingface_hub/utils/_hf_uris.py:617, in _parse_repo_body(location, type_, raw)
    615     raise HfUriError(uri=raw, msg="Missing repository id before '@'.")
    616 if repo_id.count("/") != 1:
--> 617     raise HfUriError(uri=raw, msg=f"Repository id must be 'namespace/name', got '{repo_id}'.")
    618 # Special refs like 'refs/pr/10' contain '/' and must be matched eagerly,
    619 # otherwise we would split them at the first '/' and treat the rest as a path.
    620 match = _SPECIAL_REFS_REVISION_REGEX.match(rev_and_path)

HfUriError: Invalid HF URI 'hf://datasets/glue@bcdcba79d07bc864c1c254ccfcedcce55bcc9a8c/.huggingface.yaml'. Repository id must be 'namespace/name', got 'glue'.
dataset['train']
from sklearn.model_selection import train_test_split

train_valid_df = dataset["train"].to_pandas()[["question", "sentence", "label"]].sample(1000, random_state=123)
train_df, valid_df = train_test_split(train_valid_df, test_size=0.2, random_state=123)
test_df = dataset["validation"].to_pandas()[["question", "sentence", "label"]].sample(1000, random_state=123)

Load the Teacher Model

In our example, we will directly load a teacher model with the google/bert_uncased_L-12_H-768_A-12 backbone that has been trained on QNLI and distill it into a student model with the google/bert_uncased_L-6_H-768_A-12 backbone.

!wget --quiet https://automl-mm-bench.s3.amazonaws.com/unit-tests/distillation_sample_teacher.zip -O distillation_sample_teacher.zip
!unzip -q -o distillation_sample_teacher.zip -d .
from autogluon.multimodal import MultiModalPredictor

teacher_predictor = MultiModalPredictor.load("ag_distillation_sample_teacher/")

Distill to Student

Training the student model is straight forward. You may just add the teacher_predictor argument when calling .fit(). Internally, the student will be trained by matching the prediction/feature map from the teacher. It can perform better than directly finetuning the student.

student_predictor = MultiModalPredictor(label="label")
student_predictor.fit(
    train_df,
    tuning_data=valid_df,
    teacher_predictor=teacher_predictor,
    hyperparameters={
        "model.hf_text.checkpoint_name": "google/bert_uncased_L-6_H-768_A-12",
        "optim.max_epochs": 2,
    }
)
print(student_predictor.evaluate(data=test_df))

More about Knowledge Distillation

To learn how to customize distillation and how it compares with direct finetuning, see the distillation examples and README in AutoMM Distillation Examples. Especially the multilingual distillation example with more details and customization.

Other Examples

You may go to AutoMM Examples to explore other examples about AutoMM.

Customization

To learn how to customize AutoMM, please refer to Customize AutoMM.