SJ_Koding

tqdm 라이브러리 사용법 (+ Train loss 실시간 출력 방법) 본문

카테고리 없음

tqdm 라이브러리 사용법 (+ Train loss 실시간 출력 방법)

성지코딩 2024. 4. 17. 23:17

데이터를 만들어내거나, 학습을 돌리는 것 처럼 반복적인 코드 흐름이 진행 될 때, 무작정 기다리는 것 보단 진행도를 알 수 있으면 좋다. 

특히 AI를 공부하고 있을때 거의 필수적으로 사용되는데, 단순 진행률만 보는게 아니라 실시간 loss, smooth loss를 보며 학습이 잘 되고있는지 실시간으로 확인하는 코드도 포함한다.

 

tqdm 이란?

tqdm은 'taqaddum'의 약자이며 아랍어라고 한다. '진행'이라는 의미를 가지며 프로그래머에게 어떠한 프로세스의 진행 상황을 시각적으로 보여주는 라이브러리이다.

 

tqdm 기본적인 사용방법

1. tqdm설치

pip install tqdm # 노트북 상에서 설치하려면 앞에 !를 붙여야함

 

2. tqdm 불러오기 & 사용방법

from tqdm import tqdm

for i in tqdm(반복대상):
	~~~
	~~~

정말 간단한 코드이다. 전체 반복량과 현재 진행상황. 경과 시간 및 남은 시간, 초당 반복횟수등을 알려준다.

아무런 인자를 주지 않을 때는 위와같이 출력된다.

 

3. tqdm.auto

tqdm.auto는 사용자의 코드 실행 환경을 자동으로 탐지하여 tqdm.notebook이나 tqdm.std(기본값)을 알아서 설정한다. 대부분은 auto로 종결한다. 그래도 안되면 tqdm.notebook, tqdm.cli를 실행해보자.

notebook 환경

 

tqdm 인자값

1. iterable: 진행률 표시줄을 적용할 반복 가능한 객체. 리스트, 집합, 범위 등이 될 수 있다. (required)

2. desc: (기본값: None) 진행률 표시줄 앞에 표시할 설명 또는 접두사설정

3. total: (기본값: None) 진행해야 할 전체 항목의 수. None일 경우, iterable의 길이가 사용된다. 파일 다운로드와 같이 길이를 미리 알 수 없는 작업에서 유용하게 사용된다.

4. leave: (기본값: True) 반복 작업이 완료된 후에 진행률 표시줄을 화면에 남겨둘지 여부를 결정한다. False로 두면 진행바가 지워지는데, 쓸 이유가 없는 것 같다.

ncols: (기본값: None) 진행률 표시줄의 총 너비를 문자 단위로 설정한다. None일 경우 자동으로 창의 너비에 맞춰진다.

mininterval: (기본값: 0.1) 진행률 표시줄의 업데이트 최소 간격을 초 단위로 설정한다. 이는 너무 자주 업데이트되는 것을 방지하여 성능을 개선할 수 있다. 예를 들어 pass구문 만 실행한다고 가정하면, 너무 빠른 진행률 출력이 실행속도를 저하시킬 수 있다.

unit_scale: (기본값: False) 진행 중인 항목의 수를 자동으로 스케일링하여 더 큰 단위로 표시할지 여부.

dynamic_ncols: (기본값: False) 진행률 표시줄의 너비를 창 크기에 맞춰 동적으로 조정한다.

smoothing: (기본값: 0.3) 평균 속도를 계산할 때 사용하는 이동 평균의 가중치.

 

진행률 뿐 만 아니라 특정 변수의 값도 실시간으로 출력하는 방법

대부분 Loss를 출력할 때 많이 사용했다. 이는 set_description()를 사용하면 된다.

반복 객체에 tqdm(~~~)자체를 넣는게 아니라. 해당 함수를 하나의 변수에 저장(흔히 bar로 지정)하여 아래와 같이 사용한다.

from tqdm.auto import tqdm

bar = tqdm(range(100000000))
for i in bar:
    bar.set_description(f'현재 숫자: {i}')

값의 변화를 한 줄에 볼 수 있다! 실제로 어떻게 사용했는지 아래 예시 코드를 참고하자.

for cnt, (data, target) in enumerate(bar):
    data = data.to('cuda:1')
    target = target.to('cuda:1')
    if is_training:
        optimizer.zero_grad()

    outputs = model(data)

    if outputs.dim() == 0:
        outputs = outputs.unsqueeze(0)

    if target.dim() == 0:
        target = target.unsqueeze(0)

    if not is_test:
        total_loss = loss_fn(outputs, target.long())
        running_loss += total_loss.item()
        smooth_loss_queue.append(total_loss.item())
        smooth_loss = sum(smooth_loss_queue) / len(smooth_loss_queue)

    predicted = torch.argmax(outputs, dim=-1)

    if not is_test:
        preds.extend(predicted.detach().cpu().tolist())
        targets.extend(target.detach().cpu().tolist())

    else:
        preds.extend(predicted.detach().cpu().tolist())

    if is_training:
        total_loss.backward()
        optimizer.step()


    bar.set_description(f'Loss: {total_loss:.6f} | Smooth Loss: {smooth_loss:.6f}')

각 배치마다의 loss를 출력하고 최근 100개의 로스를 저장해 평균치를 출력하여 loss변화 추이를 확인하는 코드이다.
버그로인해 loss가 줄어들지 않는다거나 underfitting이 일어나는지를 한 눈에 바로 알 수 있어 편하다.

+) tqdm이 너무 많이 출력돼서 에포크가 많을 경우 출력물이 조금 길어질 수 있는데, 이 때 

tqdm(dataloader, desc="어쩌구", leave=False)

와 같이 leave를 False로 설정해주면 tqdm이 종료된 후 그 내용을 감춰버린다.