PyTorch 학습 목차
- PyTorch 기본 개념
- 1.1 Tensor
- 1.2 Autograd
- 1.3 Dataset and DataLoader
- 신경망 모델 구축 및 훈련
- 2.1 신경망 모델 정의 (nn.Module)
- 2.2 손실 함수 (Loss Function)
- 2.3 옵티마이저 (Optimizer)
- 2.4 훈련 루프 (Training Loop)
- 이미지 분류 모델 훈련 실습
- 3.1 CIFAR-10 데이터셋 로드
- 3.2 CNN 모델 정의
- 3.3 모델 훈련
- 3.4 모델 평가
이제 위 목차에 따라 "1. PyTorch 기본 개념"부터 진행하겠습니다.
1. PyTorch 기본 개념
1.1 Tensor
- 정의: Tensor는 PyTorch의 가장 핵심적인 데이터 구조로, 다차원 배열을 나타냅니다. NumPy의 ndarray와 유사하지만, GPU를 사용하여 연산을 가속할 수 있다는 장점이 있습니다.
- 생성:실행 결과:
-
tensor([[0.0000e+00, 0.0000e+00, 0.0000e+00], [0.0000e+00, 0.0000e+00, 0.0000e+00], [0.0000e+00, 0.0000e+00, 0.0000e+00], [0.0000e+00, 0.0000e+00, 0.0000e+00], [0.0000e+00, 0.0000e+00, 0.0000e+00]]) tensor([[0.7399, 0.0384, 0.7724], [0.2793, 0.3475, 0.9867], [0.1476, 0.5694, 0.2669], [0.2467, 0.7483, 0.1399], [0.3536, 0.7453, 0.3473]]) tensor([[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]) tensor([5.5000, 3.0000]) tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]], dtype=torch.float64) tensor([[-0.4941, -0.9492, -1.4278], [ 0.8486, -0.5345, 0.8756], [-1.2466, -0.4889, 0.0953], [ 1.1298, -0.2803, 0.5022], [ 0.6089, 0.2535, -0.5662]]) torch.Size([5, 3])
-
Python
import torch # 빈 5x3 텐서 생성 (초기화되지 않음) x = torch.empty(5, 3) print(x) # 무작위로 초기화된 5x3 텐서 생성 x = torch.rand(5, 3) print(x) # 0으로 채워진 long 타입의 5x3 텐서 생성 x = torch.zeros(5, 3, dtype=torch.long) print(x) # 데이터로부터 직접 텐서 생성 x = torch.tensor([5.5, 3]) print(x) # 기존 텐서의 속성(크기, 자료형)을 사용하여 새로운 텐서 생성 x = x.new_ones(5, 3, dtype=torch.double) # x와 같은 크기, double 타입, 1로 채워진 텐서 print(x) x = torch.randn_like(x, dtype=torch.float) # x와 같은 크기, float 타입, 무작위 값으로 채워진 텐서 print(x) # 텐서 크기 확인 print(x.size())
- 연산:실행 결과:
-
tensor([[ 0.1988, -0.2748, -1.1798], [ 1.6027, -0.3240, 1.8295], [-0.7497, 0.3719, 0.2704], [ 1.3412, 0.6433, 0.7886], [ 0.6956, 0.5735, -0.4309]]) tensor([[ 0.1988, -0.2748, -1.1798], [ 1.6027, -0.3240, 1.8295], [-0.7497, 0.3719, 0.2704], [ 1.3412, 0.6433, 0.7886], [ 0.6956, 0.5735, -0.4309]]) tensor([[ 0.1988, -0.2748, -1.1798], [ 1.6027, -0.3240, 1.8295], [-0.7497, 0.3719, 0.2704], [ 1.3412, 0.6433, 0.7886], [ 0.6956, 0.5735, -0.4309]]) tensor([[ 0.1988, -0.2748, -1.1798], [ 1.6027, -0.3240, 1.8295], [-0.7497, 0.3719, 0.2704], [ 1.3412, 0.6433, 0.7886], [ 0.6956, 0.5735, -0.4309]]) tensor([-0.9492, -0.5345, -0.4889, -0.2803, 0.2535]) torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
-
Python
# 덧셈 y = torch.rand(5, 3) print(x + y) print(torch.add(x, y)) # 덧셈 결과를 저장할 텐서를 미리 지정 result = torch.empty(5, 3) torch.add(x, y, out=result) print(result) # inplace 덧셈 (y에 x를 더한 후 y에 저장) y.add_(x) print(y) # 인덱싱 (NumPy와 유사) print(x[:, 1]) # 두 번째 열 # 크기 변경 x = torch.randn(4, 4) y = x.view(16) z = x.view(-1, 8) # -1은 다른 차원으로부터 유추 print(x.size(), y.size(), z.size())
- NumPy 변환:실행 결과:
-
tensor([1., 1., 1., 1., 1.]) [1. 1. 1. 1. 1.] [1. 1. 1. 1. 1.] tensor([1., 1., 1., 1., 1.], dtype=torch.float64)
-
Python
# Tensor -> NumPy a = torch.ones(5) b = a.numpy() print(a, b) # NumPy -> Tensor import numpy as np a = np.ones(5) b = torch.from_numpy(a) print(a, b)
- CUDA Tensors:실행 결과:
tensor([[...]], device='cuda:0') # GPU 텐서임을 나타내는 device='cuda:0' tensor([[...]], dtype=torch.float64) # CPU로 이동하고 자료형이 double로 변경됨
- (GPU 사용 환경에 따라 결과가 다를 수 있습니다. GPU를 사용할 수 없는 환경이라면 이 코드는 에러를 발생시킵니다.)
-
Python
# GPU 사용 가능 여부 확인 if torch.cuda.is_available(): device = torch.device("cuda") # CUDA device 객체 y = torch.ones_like(x, device=device) # GPU에 직접 텐서 생성 x = x.to(device) # 텐서를 GPU로 이동 z = x + y print(z) print(z.to("cpu", torch.double)) # 텐서를 CPU로 이동 (자료형 변경)
1.2 Autograd
- 정의: Autograd는 PyTorch의 자동 미분 엔진으로, 텐서 연산에 대한 *그래디언트(gradient)*를 자동으로 계산해줍니다. 이를 통해 복잡한 딥러닝 모델의 학습을 쉽게 구현할 수 있습니다.
- requires_grad: requires_grad=True로 설정된 텐서에 대한 연산은 계산 그래프에 기록됩니다. 이를 통해 나중에 .backward()를 호출하여 해당 텐서까지의 모든 연산에 대한 그래디언트를 자동으로 계산할 수 있습니다.
- grad_fn: 연산의 결과로 생성된 텐서는 grad_fn 속성을 갖게 됩니다. grad_fn은 해당 텐서를 생성한 함수를 참조합니다. 이를 통해 연산 그래프를 역추적하며 그래디언트를 계산할 수 있습니다.
- backward(): backward() 함수는 스칼라 텐서 (즉, 하나의 요소만 가진 텐서)에서만 호출 가능합니다. y.backward()가 호출되면, y의 grad 속성에 y까지의 모든 연산에 대한 그래디언트가 누적됩니다.실행 결과:
-
tensor([[1., 1.], [1., 1.]], requires_grad=True) tensor([[3., 3.], [3., 3.]], grad_fn=<AddBackward0>) <AddBackward0 object at 0x...> tensor([[27., 27.], [27., 27.]], grad_fn=<MulBackward0>) tensor(13.5000, grad_fn=<MeanBackward0>) tensor([[4.5000, 4.5000], [4.5000, 4.5000]])
-
Python
import torch # requires_grad=True로 텐서 생성 x = torch.ones(2, 2, requires_grad=True) print(x) # 연산 수행 y = x + 2 print(y) print(y.grad_fn) # y는 연산의 결과로 생성되었으므로 grad_fn을 가짐 z = y * y * 3 out = z.mean() print(z, out) # 그래디언트 계산 out.backward() print(x.grad) # d(out)/dx
- 더 복잡한 예제:실행 결과:
-
tensor([-749.7367, 915.7348, 83.5167], grad_fn=<MulBackward0>) tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
-
Python
x = torch.randn(3, requires_grad=True) y = x * 2 while y.data.norm() < 1000: y = y * 2 print(y) v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float) # Jacobian-vector product 계산을 위한 벡터 y.backward(v) print(x.grad)
- requires_grad_() 와 detach():
- requires_grad_()는 기존 텐서의 requires_grad 속성을 in-place로 변경합니다.
- detach()는 텐서를 계산 그래프에서 분리하여 requires_grad=False인 새로운 텐서를 생성합니다.
Pythonprint(x.requires_grad) print((x ** 2).requires_grad) with torch.no_grad(): print((x ** 2).requires_grad) # detach() 예제 print(x.requires_grad) y = x.detach() print(y.requires_grad) print(x.eq(y).all())
-
True True False True False tensor(True)
1.3 Dataset and DataLoader
- 정의: PyTorch는 데이터를 효율적으로 로드하고 관리하기 위해 torch.utils.data.Dataset과 torch.utils.data.DataLoader 클래스를 제공합니다.
- Dataset:
- Dataset은 추상 클래스로, 사용자 정의 데이터셋을 만들 때 상속해야 합니다.
- __len__과 __getitem__ 메소드를 구현해야 합니다.
- __len__: 데이터셋의 총 샘플 개수를 반환합니다.
- __getitem__: 주어진 인덱스에 해당하는 샘플을 반환합니다.
- DataLoader:
- DataLoader는 Dataset을 감싸서 미니 배치 생성, 데이터 셔플링, 멀티프로세싱을 통한 데이터 로딩 가속화 등의 기능을 제공합니다.
- 주요 인자:
- dataset: 사용할 Dataset 객체
- batch_size: 미니 배치 크기
- shuffle: 에폭마다 데이터를 섞을지 여부
- num_workers: 데이터를 로드하는 데 사용할 서브 프로세스 개수
Pythonimport torch from torch.utils.data import Dataset, DataLoader # 예제 Dataset 클래스 class MyDataset(Dataset): def __init__(self, data, targets, transform=None): self.data = data self.targets = targets self.transform = transform def __getitem__(self, index): x = self.data[index] y = self.targets[index] if self.transform: x = self.transform(x) return x, y def __len__(self): return len(self.data) # 데이터와 타겟 생성 (예시) data = torch.randn(100, 3, 32, 32) targets = torch.randint(0, 10, (100,)) # Dataset 객체 생성 dataset = MyDataset(data, targets) # DataLoader 객체 생성 dataloader = DataLoader(dataset, batch_size=4, shuffle=True, num_workers=2) # DataLoader를 이용한 데이터 순회 for batch_idx, (data, targets) in enumerate(dataloader): print(f"Batch {batch_idx}: Data shape={data.size()}, Targets shape={targets.size()}")
-
Batch 0: Data shape=torch.Size([4, 3, 32, 32]), Targets shape=torch.Size([4]) Batch 1: Data shape=torch.Size([4, 3, 32, 32]), Targets shape=torch.Size([4]) ... Batch 24: Data shape=torch.Size([4, 3, 32, 32]), Targets shape=torch.Size([4])
2. 신경망 모델 구축 및 훈련
이 섹션에서는 PyTorch를 사용하여 신경망 모델을 정의하고, 손실 함수와 옵티마이저를 설정하여 모델을 훈련하는 과정을 살펴보겠습니다.
2.1 신경망 모델 정의 (nn.Module)
- nn.Module: PyTorch에서 신경망 모델은 nn.Module을 상속하여 정의합니다.
- __init__: 신경망의 레이어들을 정의하고 초기화합니다.
- forward: 입력 데이터가 신경망을 통과하는 순전파(forward pass) 과정을 정의합니다.실행 결과:
-
Net( (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1)) (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) )
-
Python
import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() # 1개의 입력 이미지 채널, 6개의 출력 채널, 5x5 합성곱 커널 self.conv1 = nn.Conv2d(1, 6, 5) self.conv2 = nn.Conv2d(6, 16, 5) # 아핀(affine) 연산: y = Wx + b self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5*5는 이미지 차원에 해당 self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): # (2, 2) 크기 윈도우에 대해 맥스 풀링(max pooling) x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) # 크기가 제곱수라면 하나의 숫자만을 특정 x = F.max_pool2d(F.relu(self.conv2(x)), 2) x = x.view(-1, self.num_flat_features(x)) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x def num_flat_features(self, x): size = x.size()[1:] # 배치 차원을 제외한 모든 차원 num_features = 1 for s in size: num_features *= s return num_features net = Net() print(net)
2 1 .2 손실 함수 (Loss Function)
- 정의: 손실 함수는 모델의 예측값과 실제값 사이의 차이를 측정하는 함수입니다. 모델 훈련의 목표는 손실 함수의 값을 최소화하는 것입니다.
- 종류: PyTorch는 nn 패키지에서 다양한 손실 함수를 제공합니다.
- nn.MSELoss: 평균 제곱 오차 (Mean Squared Error)
- nn.CrossEntropyLoss: 크로스 엔트로피 손실
- nn.L1Loss: L1 손실
- ...
- 예제:실행 결과:
-
tensor(0.5559, grad_fn=<MseLossBackward>)
-
Python
output = net(torch.randn(1, 1, 32, 32)) # 임의의 입력값 target = torch.randn(10) # 임의의 타겟 target = target.view(1, -1) # 출력과 같은 shape로 만듦 criterion = nn.MSELoss() loss = criterion(output, target) print(loss)
2.3 옵티마이저 (Optimizer)
- 정의: 옵티마이저는 손실 함수의 값을 최소화하기 위해 모델의 가중치를 업데이트하는 알고리즘을 구현합니다.
- 종류: PyTorch는 torch.optim 패키지에서 다양한 옵티마이저를 제공합니다.
- optim.SGD: 확률적 경사 하강법 (Stochastic Gradient Descent)
- optim.Adam: Adam 옵티마이저
- optim.RMSprop: RMSprop 옵티마이저
- ...
- 예제:
-
Python
import torch.optim as optim # 옵티마이저 생성 (SGD 사용) optimizer = optim.SGD(net.parameters(), lr=0.01)
2.4 훈련 루프 (Training Loop)
- 훈련 루프: 모델 훈련은 일반적으로 다음과 같은 단계를 반복적으로 수행합니다.
- 입력 데이터를 모델에 전달하여 예측값을 계산합니다. (순전파)
- 손실 함수를 사용하여 예측값과 실제값 사이의 손실을 계산합니다.
- 역전파를 수행하여 손실에 대한 각 가중치의 그래디언트를 계산합니다.
- 옵티마이저를 사용하여 가중치를 업데이트합니다.
- 예제:실행 결과: (이 코드는 가중치를 업데이트하지만, 그 결과를 직접 출력하지는 않습니다.)
-
Python
# 훈련 루프 optimizer.zero_grad() # 모든 변화도(gradient) 버퍼를 0으로 만듦 output = net(torch.randn(1, 1, 32, 32)) loss = criterion(output, target) loss.backward() optimizer.step() # 업데이트 진행
'Dev > Deep Learning' 카테고리의 다른 글
CUDA cuDNN 개발환경 (0) | 2024.12.22 |
---|---|
컴퓨터 비전 프로젝트를 위한 8개의 오픈소스 의료 이미지 라이브러리 (1) | 2024.12.13 |
전처리 툴 v 0.1 (0) | 2024.11.07 |
Anomalib (1) | 2024.08.18 |
이상 탐지 관련 오픈소스 프로젝트들 (0) | 2024.08.18 |
댓글