본문 바로가기

pyTorch

PyTorch/tutorials/0.Quickstart - 1

PyTorch 에는 데이터로 작업하기 위한 두가지 요소가 있는데, 이는 torch.utils.data.DataLoader와 torch.utils.data.Dataset이다. 이중 Dataset의 경우, 상응하는 데이터의 샘플과 그 라벨을 저장할 수 있게 해준다.

일반적인 모델 훈련에 있어서 데이터는 미니배치로 데이터 샘플로 나눠져 전달되고 섞는 과정을 통하여 과적합을 방지하기도 한다. DataLoader은 Dataset에 대한 반복적인 접근을 도와주는 것으로서 Dataset을 DataLoader을 통해 불러와서 각종 반복적인 과정을 수행할 수 있다.

pyTorch에는 그런 Dataset에 대해서 vision, text, audio 분야의 여러 샘플 데이터셋을 제공한다.

https://pytorch.org/vision/stable/datasets.html

https://pytorch.org/text/stable/datasets.html

https://pytorch.org/audio/stable/datasets.html

정도가 있다. 저 데이터셋들은 각각 torch.utils.data.Dataset을 상속받는다고 보면 된다. 각각의 데이터셋에는 필요한 인자 값들이 다르기 때문에 help함수를 사용하거나 공식 문서를 참고하면 인자 값을 확정할 수 있다.

Dataset에서 각각의 데이터 값들은 list처럼 list[index]로 데이터에 접근할 수 있다. FashionMNIST dataset을 예시로 불러와서 출력하여 보면

>>> training_data
Dataset FashionMNIST
    Number of datapoints: 60000
    Root location: data
    Split: Train
    StandardTransform
Transform: ToTensor()

으로 데이터의 갯수는 6만개이고

각각의 training_data 에는 실제 데이터와 라벨이 붙어있음을 알 수 있다. 위 예시에서의 9번 라벨은 "Ankle Boot" 였다.

>>> training_data[0][0].shape
torch.Size([1, 28, 28])

위의 결과가 나오는 걸로 보아 28x28의 grayscale image라고 확인할 수 있다.

 

지금까지는 미리 만들어진 데이터셋을 불러왔던 것이고 만약 개인이 개인의 데이터를 이용하여 데이터셋을 만들고자 한다면 Dataset을 상속받는 클래스를 만들어 주면 된다. 이때 주의사항으로 새로 만들게 된 클래스는 __init__, __len__, __getitem__ method가 반드시 포함되어야 한다. 

#img_dir : FashionMNIST image's stored Directory
#annotations_file : csv file that stores image's label
import os
import pandas as pd
from torchvision.io import read_image

class CustomImageDataset(Dataset):
    def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
    	#pd.read_csv 인자중 "names"값에 원하는 각 columns들의 이름을 list형태로 넣을 수 있다.
        self.img_labels = pd.read_csv(annotations_file)
        self.img_dir = img_dir
        self.transform = transform
        self.target_transform = target_transform

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, idx):
    	#img_path에다 이미지 디렉토리의 경로와 csv에서 읽은 idx번째 데이터의 0번째값을 합쳐라
        #지금의 예시에서는 index 0이 파일 이름 index 1이 파일의 label을 나타내고 있다.
        img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
        image = read_image(img_path)
        label = self.img_labels.iloc[idx, 1]
        if self.transform:
            image = self.transform(image)
        if self.target_transform:
            label = self.target_transform(label)
        return image, label

위 코드는 PyTorch tutorial에서 제공하는 FashionMNIST 데이터를 이용한 커스텀 데이터셋 예시이다.

 

DataLoader은 DataLoader 객체 생성시 인자로 dataset, batch_size, shuffle, samplet, batch_samplet, num_workers, collate_fn, pin_memory, drop_last, timeout, worker_init_fn, generator, prefetch_factor, persistent_worker을 입력받는데 상황에 따라 다르겠지만 주로 dataset=[우리가 준비한 Dataset 객체], batch_size=[integer batch size, default=1], shuffle=[섞고싶으면 True, 아니면 False]값을 이용한다. 이렇게 DataLoader으로 불러온 데이터를 iter, next를 이용하여 데이터들을 순차적으로 조회할 수 있다.

#DataLoader 객체 생성 예시
from torch.utils.data import DataLoader
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)

#using iter, next to lookup datas in DataLoader Object
iter_var = iter(train_dataloader)
for i in range(number_to_loop):
	train_features, train_labels = next(iter_var)

 

모델 생성

모델 생성에 관련해서는 PyTorch는

https://pytorch.org/docs/stable/nn.html

 

torch.nn — PyTorch 1.10.1 documentation

Shortcuts

pytorch.org

안의 nn.Module을 사용한다. 무듈 클래스를 정의할떄 nn.Module을 상속하여 만든다.

device = 'cuda' if torch.cuda.is_available() else 'cpu'

위 코드는 나중에 모델을 훈련하는데 사용할 장치를 정하는 내용이다. cuda가 활성화 되어 있으면 학습에 GPU를 이용할 수 있다. nn.Module의 docs를 확인해 보면

위와 같이 __init__함수를 overriding하여 모델을 정의하여 사용할 수 있다. 아래는 tutorials 페이지에 있는 예시이다.

class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(#모듈의 순서를 입력받은 순서대로 전달해줌
            nn.Linear(28*28, 512),#선형 모델
            nn.ReLU(),#Relu 함수
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

 

위 모델에 대한 해석으로는 Nerualnetwork 클래스 안에 flatten, linear_relu_stack 이라는 layer들이 사용 가능하게 있고,

이 모델의 순전파로는 입력값 x를 받은 다음에 x를 flatten을 거쳐 tensor 으로 만든 다음 linear_relu_stack을 통과한다.

linear_relu_stack에는 3개의 layer들이 있는데 처음 입력으로 28x28=784개의 값들이 512개로 변환되고 Relu 를 거친후 2번째 Linear(512,512)를 통하여 변환 후 Relu를 거치고 마지막으로 Linear(512,10)으로 바꾼 후 10개의 값들로 출력이 진행된다. 여기서 Linear은 선형 모델로, 입력받은 값을 출력값에 맞게 선형 변환 해 준다.

y=wx+b의 값으로 w는 가중치, b는 기저이다. 참고로 가중치인 W의 값은 처음에는 random으로 설정되어 있는 듯하다. 이를 사용자가 의도해서 변경하기 위해서는

conv1 = torch.nn.Conv2d(...)
conv1.weight.data.fill_(0.01)
conv1.bias.data.fill_(0.01)#가중치가 아니라 기저값 변경

의 방법을 이용하여 원하는 가중치를 지정하여 줄 수 있다.

 

모델 가중치 최적화(Optimizing the Model Parameters)

우리가 정의하게된 모델에 존재하는 기본 가중치를 경사하강법으로 최적화 해 나가는 과정이다. 그 과정에 있어서 큰 영향을 미치는 요소를 꼽아 보자면, 크게 최적화 함수의 종류, 학습하는 데이터 배치 크기, 데이터세트를 몇 번 반복학습할 것인지에 대한 epoch, 한번의 학습으로 어떤 크기만큼의 가중치를 변경할 것인지에 대한 학습률이 존재한다. 그 과정에서 모델이 얼마나 훈련되었는지에 대한 지표로 손실 함수를 사용하게 된다. (물론 손실 함수의 경우 지도학습에서 사용 가능한 방법이다.) 지도학습에서 크게 분류되는 회귀, 분류 방법에선 각각 보통 평균 제곱 오차와 교차 엔트로피 오차를 사용한다. PyTorch에서 사용 가능한 다른 손실 함수의 종류는 

https://pytorch.org/docs/stable/nn.html#loss-functions

에서 확인할 수 있다. 예시로는 아래처럼 사용할 수 있다.

loss_fn = nn.CrossEntropyLoss() #단순 손실함수만 할등
#temp 변수에는 각각의 dataloader에서 iter, next로 읽어온 값들이 들어가있다고 가정)
#손실 함수의 첫번째 변수로는 학습 모델을 통과한 데이터, 두번째 인자로는 그에대한 정답 라벨
loss_fn = nn.CrossEntropyLoss(model(temp[0]), temp[1])

학습을 진행할 알고리즘(경사하강법 중에서도 Adam, Sgd, 등 과 같은 알고리즘)은 torch.optim 에서 불러와서 사용할 수 있으며, 종류는

https://pytorch.org/docs/stable/optim.html

에서 확인할 수 있다. 예시로는 아래처럼 사용할 수 있다. 아래는 Sgd 알고리즘을 이용한 예시이다.

optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

오차역전파로 인하여 pyTorch에서 학습이 이루어지는데에는 3가지 과정이 필요하다.

#optimizer에는 등록하였던 torch.optim.알고리즘 객체가 저장되어 있다고 가정한다.
#loss 변수에는 사용할 손실함수 객체가 담겨있다고 가정한다.(데이터와 결과값까지 함께)

#모델 파라미터의 경사를 초기화하기위해 사용한다.(이전에 발생한 오차값지운다는느낌?)
optimizer.zero_grad()
#손실 값에 대한 오차값을 역전파시킨다.
loss.backward() #
#역전파된 값을 통하여 가중치 변경을 적용한다.
optimizer.step() #

pyTorch 공식 홈페이지에 등록된 구현 예시로는

def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        # Compute prediction and loss
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


def test_loop(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    with torch.no_grad():
        for X, y in dataloader:
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
    
#실행 부분    
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(train_dataloader, model, loss_fn, optimizer)
    test_loop(test_dataloader, model, loss_fn)
print("Done!")

이 있다. 이 내용대로 실행시켜보면

위 사진처럼 각 Epoch별 훈련 진행 상황 및 손실 정도를 확인할 수 있다. 또한 학습시킨 모델의 파라미터는 model.parameters()안에 저장된다. 훈련된 모델의 경우 아래와 같은 방법으로 저장할 수 있다.

model.parameters() #이 변수 안에 학습된 파라미터가 존재한다.
#PATH에는 데이터를 저장할 경로를 입력한다.
#PATH example(Win10) -> PATH = 'C:\\Users\\user\\Desktop\\temp_save'
torch.save(model, PATH)
#torch.save(model.state_dict(), PATH) 모델 없이 가중치 만 딕셔너리 형태로 저장하고 싶을 경우

#저장된 데이터는torch.laod으로 불러올 수 있다. 단 이때 모델 클라스가 python 환경에 사전 정의되
#있어야 한다.
#load_model = torch.load(PATH) 단순히 모델만 로딩할 때 사용한다.
model = NeuralNetwork()#NeuralNetwork는 사전에 정의한 모델
model.load_state_dict(torch.load("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():
    pred = model(x)
    predicted, actual = classes[pred[0].argmax(0)], classes[y]
    print(f'Predicted: "{predicted}", Actual: "{actual}"')

'pyTorch' 카테고리의 다른 글

PyTorch/tutorials/1.Tensor  (0) 2022.01.25
PyTorch start  (0) 2022.01.21