LLM 엔지니어링2장 · 신경망과 딥러닝의 기본 원리13 / 23← 이전목차다음 →MetaContents

2장. 신경망과 딥러닝의 기본 원리

PyTorch 와 GPU

큰 식당의 주방을 떠올려 보세요. 솜씨 좋은 요리사 몇 명은 복잡한 요리를 차례로 해냅니다. 반면 감자 만 개를 깎는 일은 손 빠른 보조 수천 명이 한꺼번에 덤비는 편이 훨씬 빠릅니다. CPU 가 요리사, GPU 가 보조들입니다.

솜씨 좋은 요리사 몇 명에 견준 CPU 와 손 빠른 보조 수천 명에 견준 GPU 를 나란히 놓은 비교 그림

왼쪽 CPU 는 코어가 몇 개에서 수십 개라 복잡한 일을 차례로 잘합니다. 오른쪽 GPU 는 단순한 계산을 하는 코어가 수천 개라, 신경망의 행렬 곱처럼 같은 곱셈을 엄청나게 많이 하는 일에 빠릅니다. 여기서 감자 깎기가 행렬 곱입니다. 비유와 다른 점은, GPU 에 일을 맡기려면 재료(텐서)를 GPU 의 메모리로 옮기는 수고가 든다는 것입니다. 일이 작으면 옮기는 시간이 더 걸려 오히려 느립니다.

텐서와 장치

PyTorch 의 텐서는 NumPy 배열과 거의 같게 쓰고, 어느 장치에 있는지를 하나 더 가집니다.

할 일코드
GPU 를 쓸 수 있나torch.cuda.is_available()
쓸 장치를 고르기device = "cuda" if torch.cuda.is_available() else "cpu"
텐서를 옮기기x = x.to(device)
모델을 옮기기model = model.to(device)
NumPy 로 돌아오기x.cpu().numpy()

모델과 데이터는 같은 장치에 있어야 계산할 수 있습니다. 하나만 옮기면 오류가 납니다.

Python
import torch

device = "cuda" if torch.cuda.is_available() else "cpu"   # GPU 가 없으면 CPU 를 쓴다
x = torch.ones(2, 3)          # 처음에는 CPU 에 만들어진다
x = x.to(device)              # 고른 장치로 옮긴다
print(device, x.device, x.shape)

GPU 가 있는 PC 에서는 cuda cuda:0 torch.Size([2, 3]) 처럼, 없는 PC 에서는 cpu cpu torch.Size([2, 3]) 처럼 나옵니다. 같은 코드가 어느 PC 에서나 돌아가도록 장치를 이렇게 고르는 것이 관례입니다.

코드에 나오는 함수

주방 비유로 정리합니다. 텐서가 재료, CPU 가 요리사의 조리대, GPU 가 보조들의 작업대입니다. 장치(device)는 "재료가 지금 어느 조리대 위에 있나"입니다.

함수하는 일주방으로 치면
torch.cuda.is_available()이 PC 에서 PyTorch 가 NVIDIA GPU 를 쓸 수 있는지 True, False 로 알려 준다보조들의 작업대가 있는지 본다
"cuda" if ... else "cpu"쓸 수 있으면 "cuda"(GPU), 아니면 "cpu" 를 고른다. PyTorch 는 NVIDIA GPU 를 CUDA 라는 기술로 쓰므로 GPU 를 "cuda" 라고 부른다작업대가 있으면 그쪽, 없으면 조리대를 쓰기로 한다
torch.ones(2, 3)모든 칸이 1 인 (2, 3) 텐서를 만든다. 따로 정하지 않으면 CPU 에 만들어진다재료를 조리대 위에 꺼내 놓는다
x.to(device)텐서를 고른 장치로 옮긴 새 텐서를 돌려준다. 그래서 x = x.to(device) 처럼 다시 받아 둔다재료를 작업대로 옮긴다
x.device텐서가 지금 있는 장치. cpu 또는 cuda:0(0번 GPU)이다재료가 어느 조리대 위에 있나
x.shape텐서의 모양("텐서는 무엇인가" 절)재료의 크기
x.cpu().numpy()GPU 에 있는 텐서를 CPU 로 가져온 뒤 NumPy 배열로 바꾼다다 된 요리를 손님 식탁(NumPy)으로 내간다

cuda:0 의 0 은 GPU 번호입니다. GPU 가 여러 장이면 cuda:0, cuda:1 처럼 번호로 가립니다. x.to(device) 는 원래 텐서를 바꾸지 않고 옮긴 복사본을 돌려준다는 점을 기억해 둡니다. x.to(device) 라고만 쓰고 받아 두지 않으면 x 는 그대로 CPU 에 남습니다.

옮기고 되돌리기를 들여다보기

텐서가 장치 사이를 오가는 모습과, 장치가 다를 때 나는 오류를 찍어 봅니다.

Python
import torch

device = "cuda" if torch.cuda.is_available() else "cpu"
x = torch.ones(2, 3)
print("처음 자리:", x.device)              # 처음에는 늘 CPU
x = x.to(device)
print("옮긴 자리:", x.device)              # GPU 가 있으면 cuda:0
back = x.cpu().numpy()                     # CPU 로 되돌려 NumPy 배열로
print(type(back).__name__, back.shape)

if device == "cuda":
    print("GPU 이름:", torch.cuda.get_device_name(0))
    try:
        x + torch.ones(2, 3)               # GPU 의 x 와 CPU 의 텐서를 더하려 한다
    except RuntimeError as e:
        print("오류:", e)
else:
    print("GPU 가 없어 장치가 다른 경우의 오류는 생기지 않습니다.")

이 교안을 만든 PC(GeForce GTX 1650)에서는 이렇게 나왔습니다.

출력
처음 자리: cpu
옮긴 자리: cuda:0
ndarray (2, 3)
GPU 이름: NVIDIA GeForce GTX 1650
오류: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!

GPU 가 없는 PC 에서는 "옮긴 자리"가 cpu 이고 마지막 줄에 안내 문장이 나옵니다. 오류 문장은 "모든 텐서가 같은 장치에 있어야 하는데 cuda:0 과 cpu 두 곳에 있다"는 뜻입니다. 재료가 서로 다른 조리대에 있으면 한 냄비에 넣을 수 없는 것과 같습니다. 이 오류가 나면 모델과 데이터 양쪽에 .to(device) 를 했는지부터 봅니다. torch.cuda.get_device_name(0) 은 0번 GPU 의 이름을 알려 줍니다.

이 교안의 약속

이 장의 실습 4부터 6까지는 일부러 CPU 에서 학습합니다. 모델이 작아 CPU 로도 몇 초면 끝나고, GPU 가 없는 수강생과 결과 숫자를 똑같이 맞추기 위해서입니다. GPU 는 실습 1에서 속도를 견주어 보고, 모델이 커지는 4장부터 본격적으로 씁니다.

주의할 점

  • torch.cuda.is_available() 이 False 로 나오는데 NVIDIA GPU 가 있다면, CPU 판 PyTorch 를 설치한 경우가 많습니다. 환경설정의 GPU 판 설치 명령으로 다시 설치합니다.
  • GPU 에서 학습하면 같은 씨앗이라도 마지막 자리의 숫자가 CPU 와 조금 다를 수 있습니다.
← 이전 절2장 · 학습 루프: 에포크와 배치다음 절 →2장 · 실습 지도