GPU 가 달린 서버에 프레임워크를 깔았는데 학습이 CPU 로 도는 것 같다. nvidia-smi 는 카드를 보여 주는데 프레임워크가 못 잡는 경우가 많다.
드라이버 → 프레임워크 인식 → 실제 연산 배치 순으로 아래에서 위로 올라간다. 한 단계라도 건너뛰면 엉뚱한 곳을 고치게 된다.
먼저 드라이버부터 본다.
nvidia-smi
여기서 카드가 안 보이면 프레임워크 문제가 아니다. 드라이버·커널 모듈·컨테이너 런타임 순으로 확인한다.
import torch
print(torch.__version__)
print(torch.version.cuda) # None 이면 CPU 전용 빌드
print(torch.cuda.is_available())
print(torch.cuda.device_count())
if torch.cuda.is_available():
print(torch.cuda.get_device_name(0))
torch.version.cuda 가 None 이면 CPU 전용 휠을 깐 것이다. 이 경우 설정을 아무리 바꿔도 GPU 를 못 쓴다. CUDA 빌드를 다시 받아야 한다.
실제로 텐서가 GPU 에 올라가는지 확인한다.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
x = torch.randn(4096, 4096, device=device)
y = x @ x
print(y.device, y.sum().item())
모델과 데이터를 모두 옮겨야 한다. 한쪽만 옮기면 Expected all tensors to be on the same device 가 난다.
model = model.to(device)
inputs = inputs.to(device)
labels = labels.to(device)
import tensorflow as tf
print(tf.__version__)
print(tf.config.list_physical_devices('GPU'))
print(tf.test.is_built_with_cuda())
빈 리스트가 나오면 못 잡은 것이다. 어느 장치에 연산이 배치되는지 보려면 로깅을 켠다.
tf.debugging.set_log_device_placement(True)
a = tf.random.normal([4096, 4096])
b = tf.matmul(a, a)
print(b.device)
TensorFlow 는 기본적으로 GPU 메모리를 통째로 선점한다. 한 장비에서 여러 작업을 돌린다면 점진 할당을 켠다.
for gpu in tf.config.list_physical_devices('GPU'):
tf.config.experimental.set_memory_growth(gpu, True)
프레임워크가 "사용 가능"이라고 말해도 실제 부하가 걸리는지는 별개다. 학습을 돌리면서 옆 터미널에서 본다.
nvidia-smi --query-gpu=index,utilization.gpu,memory.used,memory.total --format=csv -l 2
메모리는 잡혔는데 utilization.gpu 가 0 근처를 맴돈다면 데이터 로딩이 병목이다. 배치 크기와 DataLoader 의 num_workers 를 올린다.
CPU 전용 패키지를 설치한 경우가 가장 흔하다. pip install tensorflow-cpu 나 기본 인덱스의 CPU 휠이 걸린 경우다.
드라이버와 프레임워크가 요구하는 CUDA 계열이 어긋난 경우다. PyTorch 는 CUDA 런타임을 휠에 포함해 배포하므로 드라이버 버전만 충족하면 되지만, TensorFlow 는 요구하는 cuDNN·CUDA 조합이 버전마다 정해져 있다.
컨테이너에서 GPU 를 넘기지 않은 경우다. --gpus all 이나 쿠버네티스의 nvidia.com/gpu 자원 요청이 빠지면 컨테이너 안에서는 카드가 보이지 않는다.
CUDA_VISIBLE_DEVICES 가 빈 문자열로 설정된 경우다. 스케줄러나 이미지 기본값이 이 값을 넣어 두는 일이 있다.
echo "[$CUDA_VISIBLE_DEVICES]"
torch.cuda.is_available() 이 True 라고 해서 학습 코드가 GPU 를 쓰는 것은 아니다. .to(device) 를 빠뜨리면 그대로 CPU 에서 돈다.
여러 장을 쓰려면 프레임워크의 분산 학습 기능을 따로 켜야 한다. 카드가 여러 장 보인다고 자동으로 나뉘지 않는다.