지저분한 데이터셋 코드를 더 나은 가독성과 모듈성을 위해 모델 학습 코드로부터 분리하는것이 이상적이다. PyTorch는 torch.utils.data.DataLoader와 torch.utils.data.Dataset의 두 가지 데이터 기본 요소를 제공하여 미리 준비해둔 pre-loaded 데이터셋 뿐만 아니라 가지고 있는 데이터를 사용할 수 있도록 한다.
torch.utils.data.DataLoadertorch.utils.data.DataSetimport torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor
PyTorch는 TorchText,TorchVision, TorchAudio와 같이 도메인 특화 라이브러리를 데이터셋에 함께 제공하고 있다.
torchvision.datasets 모듈은 실제 비전 데이터에 대한 Dataset을 포함하고 있다. 이번 튜토리얼에서는 FashionMNIST를 사용해보자
-root : 학습/테스트 데이터가 저장되는 경로
-train : 학습용 또는 테스트용 데이터셋 여부 지정
-download=True: root에 데이터가 없는 경우 인터넷에서 다운로드
-transform 과 target_transform : 특징과 정답변형을 지정
# 공개 데이터셋에서 학습 데이터를 내려받는다
training_data = datasets.FashionMNIST(
root ="data",
train=True,
download=True,
transform=ToTensor(),
)
# 공개 데이터셋에서 테스트 데이터를 내려받는다
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transfrom=ToTensor(),
)
Dataset은 데이터셋의 특징을 가져오고 하나의 샘플에 정답을 지정하는 일을 한번에 한다. 모델을 학습할때, 일반적으로 샘플들을 미니배치로 전달하고, 매 에폭마다 데이터를 다시 섞어서 과적합을 막고, python의 멀티프로세싱을 사용해서 검색 속도를 높인다
DataLoader 는 데이터셋의 순회 가능한 객체로 감싸고, 자동화된 batch, sampling, shuffle 및 다중 프로세스로 데이터 불러오기를 지원한다.
batch_size를 64로 지정했는데 데이터로더 객체의 각 요소를 64개의 특징과 정답을 묶음으로 반환한다.
batch_size = 64 # 배치크기를 64로 정의
#데이터로더를 생성
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)
# 데이터셋의 구조와 형태를 확인
for X,y in test_dataloader:
print(f"Shape of X[N, C, H, W]: {X.shape}")
print(f"Shape of y: {y.shape} {y.dtype}")
break

PyTorch에서 신경망 모델은 nn.Module을 상속받는 클래스를 생성하여 정의한다.
가능한 gpu or mps와 같은 하드웨어 가속기에서 모델을 학습하려고 한다. torch.cuda 또는 torch.backends.mps가 사용 가능한지 확인해보고 그렇지 않으면 cpu
#학습에 사용할 cpu나 gpu,mps 장치를 얻는다
device = (
"cuda"
if torch.cuda.is_available()
else 'mps'
if torch.backends.mps.is_available()
else "cpu"
)
print(f"Using {device} device")

신경망 모델을 nn.Module 의 하위클래스로 정의하고, __init__ 에서 신경망 계층들을 초기화한다. nn.Module 을 상속받은 모든 클래스는 forward 메소드에 입력 데이터에 대한 연산들을 구현한다
# 28x28 크기의 이미지 데이터를 입력으로 받아 10개의 클래스로 분류하는 인공신경망을 정의
class NeuralNetwork(nn.Module):
def __init__(self): # 신경망 계층 정의
super().__init__()
self.flatten = nn.Flatten() # 입력 이미지를 일렬로 펴는 역할
self.linear_relu_stack = nn.Sequential( # 여러 계층을 순차적으로 쌓은 신경망 정의
nn.Linear(28*28, 512), # 28*28차원을 512차원으로
nn.ReLU(), # 활성화 함수
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10)
)
def forward(self,x): # 신경망 데이터 전달 방법
x = self.flatten(x) # x를 일렬로 편다
logits = self.linear_relu_stack(x) #연산 수행
return logits # 모델의 출력값 반환.(각 클래스에 대한 점수)
1이 되는 차원을 나타냄. !! NeuralNetwork 의 인스턴스(instance)를 생성하고 이를 device 로 이동한 뒤, 구조(structure)를 출력
model = NeuralNetwork().to(device)
print(model)

모델과 데이터가 준비되었으니 데이터에 매개변수를 최적화하여 모델을 학습하고, 검증하고 테스트할 차례!!
모델을 학습하는 과정은 반복적인 과정을 거친다. 각 반복단계에서 모델은 출력을 추측하고, 추측과 정답 사이의 손실을 계산하고 매개변수에 대한 오류의 도함수를 수집한뒤, 경사하강법을 사용하여 파라미터들을 최적화한다.
모델 최적화 과정을 제어할 수 있는 조절 가능한 매개변수이다
학습용 데이터를 제공하면, 학습되지 않은 신경망은 정답을 제공하지 않을 확률이 높다. 손실함수는 획득한 결과와 실제값 사이의 틀린정도를 측정하며 학습 중에 이 값을 최소화하려고 한다.. (똑똑하군). 주어진 데이터 샘플을 입력으로 계산한 예측과 정답을 비교하여 손실을 계산한다
최적화는 각 학습 단계에서 오류를 줄이기 위해 매개변수를 조정하는 과정이다.
#모델 매개변수 최적화하기
loss_fn = nn.CrossEntropyLoss() # 손실함수 정의
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
# 옵티마이저 정의
nn.CrossEntropyLoss()
torch.optim.SGD
model.parameters()
lr=1e-3
각 학습단계에서 모델은 학습 데이터셋에 대한 예측을 수행하고, 예측 오류를 역전파(?)하여 모델의 매개변수를 조정한다
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset) # 데이터셋의 전체 크기를 가져옴
for batch, (X, y) in enumerate(dataloader): # 데이터 로더에서 배치 단위로 데이터를 반복
X, y = X.to(device), y.to(device) # 입력 데이터(X)와 레이블(y)을 mps로 이동
#예측오류계산
pred = model(X) # 모델을 사용하여 예측값을 계산
loss = loss_fn(pred, y) # 예측값과 실제값 사이의 손실(loss)을 계산
#역전파
loss.backward() # 역전파를 통해 모델의 가중치를 조정
optimizer.step() # 옵티마이저를 사용하여 가중치를 업데이트
optimizer.zero_grad() # 옵티마이저의 기울기(gradient)를 초기화
if batch % 100 == 0: # 100번째 배치마다 현재 손실과 학습 상태를 출력
loss, current = loss.item(), (batch + 1) * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
모델이 학습하고 있는지를 확인하기 위해 테스트 데이터셋으로 모델의 성능을 확인하자
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset) # 데이터셋의 전체 크기를 가져옴
num_batches = len(dataloader) # 데이터 로더의 배치 수를 가져옴
model.eval() # 모델을 평가 모드로 설정
test_loss, correct = 0, 0 # 테스트 손실과 정확도 계산을 위한 변수 초기화
with torch.no_grad(): # 기울기 계산을 비활성화(평가시 필요 없음)
for X, y in dataloader: # 데이터 로더에서 배치 단위로 데이터를 반복
X, y = X.to(device), y.to(device) # 입력 데이터(X)와 레이블(y)을 mps로 이동
pred = model(X) # 모델을 사용하여 예측값을 계산
test_loss += loss_fn(pred, y).item() # 배치 손실을 계산하고 누적
correct += (pred.argmax(1) == y).type(torch.float).sum().item() # 예측값과 실제값을 비교하여 정확도를 계산하고 누적
test_loss = test_loss/num_batches # 평균 테스트 손실을 계산
correct = correct/size # 정확도를 계산
print(f"Test Error: \n Accuracy: {(100 * correct):>0.1f}%, Avg loss: {test_loss:>8f}\n")
학습 단계는 여러번의 반복단계 (epochs)을 거쳐서 수행된다! 각 에포크마다 모델의 정확도와 손실을 출력한다
epochs = 5
for t in range(epochs):
print(f"Epoch {t+1}\n-----------")
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model, loss_fn)
print(f"Done!")

PyTorch 모델은 학습한 매개변수를 state_dict라고 불리는 내부 상태 사전에 저장한다. 이 상태 값들은 torch.save 메소드를 사용하여 저장(persist)할 수 있다
상태 사전이란?
- PyTorch에서
torch.nn.Module모델의 학습 가능한 매개변수(예. 가중치와 편향)들은 모델의 매개변수에 포함되어 있습니다. (model.parameters()로 접근합니다) state_dict 는 간단히 말해 각 계층을 매개변수 텐서로 매핑되는 Python 사전(dict) 객체입니다.
torch.save(model.state_dict(), "model.pth")
print("Saved PyTorch Model State to model.pth")

이제 이 모델을 사용해서 예측을 할 수 있다!!!
classes = [
"T-shirt/top",
"Trouser",
"Pullover",
"Dress",
"Coat",
"Sandal",
"Shirt",
"Sneaker",
"Bag",
"Ankle boot",
]
model.eval()
x, y = test_data[0][0], test_data[0][1]
with torch.no_grad():
x = x.to(device)
pred = model(x)
predicted, actual = classes[pred[0].argmax(0)], classes[y]
print(f'Predicted: "{predicted}, Actaul: {actual}"')
