Model training anatomy

<aside>

Model training anatomy

To understand performance optimization techniques that one can apply to improve efficiency of model training speed and memory utilization, it’s helpful to get familiar with how GPU is utilized during training, and how compute intensity varies depending on an operation performed.

Let’s start by exploring a motivating example of GPU utilization and the training run of a model. For the demonstration, we’ll need to install a few libraries:

📌 GPU utilization (GPU 활용도)

→ 훈련 과정에서 GPU가 얼마나 효과적으로 사용되는지를 나타내는 지표.

📌 Compute intensity (계산 강도)

→ 수행하는 연산이 얼마나 많은 계산을 필요로 하는지를 의미.

pip install transformers datasets accelerate nvidia-ml-py3

<aside>

📌 Transformers (라이브러리)

→ Hugging Face에서 제공하는 자연어 처리(NLP) 모델을 위한 라이브러리.

📌 Datasets (라이브러리)

→ Hugging Face에서 제공하는 대용량 데이터셋을 쉽게 로딩하고 처리할 수 있도록 도와주는 라이브러리.

📌 Accelerate (라이브러리)

→ 다양한 하드웨어(GPU, TPU)에서 효율적으로 학습을 실행할 수 있도록 지원하는 라이브러리.

📌 nvidia-ml-py3 (라이브러리)

→ NVIDIA의 GPU 활용도를 모니터링하는 라이브러리.

<aside>

GPU 메모리 모니터링

The nvidia-ml-py3 library allows us to monitor the memory usage of the models from within Python. You might be familiar with the nvidia-smi command in the terminal - this library allows to access the same information in Python directly.

더미 데이터 생성

Then, we create some dummy data: random token IDs between 100 and 30000 and binary labels for a classifier. In total, we get 512 sequences each with length 512 and store them in a Dataset with PyTorch format.

</aside>

import numpy as np
from datasets import Dataset

seq_len, dataset_size = 512, 512
dummy_data = {
    "input_ids": np.random.randint(100, 30000, (dataset_size, seq_len)),
    "labels": np.random.randint(0, 2, (dataset_size)),
}
ds = Dataset.from_dict(dummy_data)
# 딕셔너리 형태의 데이터를 Hugging Face의 Dataset 형식으로 변환
ds.set_format("pt")
# PyTorch 형식으로 변환
# 참고) "pt" → PyTorch, "tf" → TensorFlow, "numpy" → NumPy

<aside>

📌 Hugging Face datasets 라이브러리

<aside>

GPU 활용도 및 훈련 요약 정보 출력 함수 정의

To print summary statistics for the GPU utilization and the training run with the Trainer we define two helper functions:

from pynvml import *

def print_gpu_utilization():
    nvmlInit()
    # nvmlInit(): NVIDIA GPU 관리를 초기화
    handle = nvmlDeviceGetHandleByIndex(0)
    # nvmlDeviceGetHandleByIndex(0): 첫 번째 GPU(인덱스 0)의 핸들을 가져옴
    info = nvmlDeviceGetMemoryInfo(handle)
    # nvmlDeviceGetMemoryInfo(handle): 해당 GPU의 메모리 정보를 가져옴
    print(f"GPU memory occupied: {info.used//1024**2} MB.")
    # info.used // 1024**2: MB 단위로 변환된 GPU 사용량을 출력

def print_summary(result):
    print(f"Time: {result.metrics['train_runtime']:.2f}")
    print(f"Samples/second: {result.metrics['train_samples_per_second']:.2f}")
    print_gpu_utilization()

<aside>

GPU 메모리 사용량 확인

Let’s verify that we start with a free GPU memory:

</aside>

print_gpu_utilization()

<aside>

That looks good: the GPU memory is not occupied as we would expect before we load any models. If that’s not the case on your machine make sure to

However, not all free GPU memory can be used by the user. When a model is loaded to the GPU the kernels are also loaded, which can take up 1-2GB of memory.

한마디로, 모델을 로드하기 전에도 GPU 메모리가 일부 점유됨

</aside>

import torch

torch.ones((1, 1)).to("cuda")
print_gpu_utilization()

<aside>

BERT 모델 로드 (GPU 메모리 사용량 확인)

First, we load the google-bert/bert-large-uncased model.

We load the model weights directly to the GPU so that we can check how much space just the weights use.

</aside>

from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-large-uncased").to("cuda")
print_gpu_utilization()

<aside>

We can see that the model weights alone take up 1.3 GB of GPU memory. The exact number depends on the specific GPU you are using.

Note that on newer GPUs a model can sometimes take up more space since the weights are loaded in an optimized fashion that speeds up the usage of the model.

Now we can also quickly check if we get the same result as with nvidia-smi CLI:

</aside>

nvidia-smi

<aside>

We get the same number as before and you can also see that we are using a V100 GPU with 16GB of memory.

So now we can start training the model and see how the GPU memory consumption changes. First, we set up a few standard training arguments:

</aside>

default_args = {
    "output_dir": "tmp",
    "eval_strategy": "steps",
    "num_train_epochs": 1,
    "log_level": "error",
    "report_to": "none",
}

<aside>

If you plan to run multiple experiments, in order to properly clear the memory between experiments, restart the Python kernel between experiments.

</aside>