본문 바로가기
데이터 분석

CNN (Pytorch)

by JoinInNoetic 2024. 6. 30.

● 임포트 및 데이터 준비

import torch

from torchvision import datasets

from torchvision.transforms import ToTensor

train_data = datasets.MNIST(root = 'data', train = True, transform = ToTensor(), download = True)

test_data = datasets.MNIST(root = 'data', train = False, transform = ToTensor())

 

● 데이터 시각화

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.imshow(train_data.data[0], cmap='gray')        # imshow : image show, 이미지 데이터를 화면에 출력

for i in range(train_data.data[0].shape[0]):

    for j in range(train_data.data[0].shape[1]):

        c = 1 if train_data.data[0][i, j].item() < 128 else 0

        ax.text(j, i, str(train_data.data[0][i, j].item()), color=(c,c,c), ha='center', va='center', fontsize=5)

plt.title('%i' % train_data.targets[0])    # 포맷팅

plt.show()

figure = plt.figure(figsize=(10,8))

cols, rows = 5, 5

for i in range(1, cols * rows +1):

    sample_idx = torch.randint(len(train_data), size=(1,)).item()    # 랜덤숫자 선택 , = np.random.randint()와 동일한 효과

    img, label = train_data[sample_idx]

    figure.add_subplot(rows, cols, i)    # rows, cols 그리드에 i번째 subplot 추가

    plt.title(label)

    plt.axis('off')

    plt.imshow(img.squeeze(), cmap='gray')

plt.show()

 

◆ CNN Model

● 데이터 로더

from torch.utils.data import DataLoader

loaders = {

    'train' : DataLoader(train_data, batch_size=100, shuffle=True, num_workers=1),

    'test' : DataLoader(test_data, batch_size=100, shuffle=True, num_workers=1)

}

# batch_size : 지정된 크기의 배치로 나누어 로드

# shuffle : 데이터를 무작위로 섞음

# num_workers : n개의 수의 병렬 작업자를 사용하여 병렬처리 -> 로딩 속도 높임

 

● CNN Model class 선언( 2 conv layers)

class CNN(torch.nn.Module):

    def __init__(self):    # 모듈로 쓸 layer를 선언

        super(CNN, self).__init__()

        self.layer1 = torch.nn.Sequential(        # padding : 이미지크기를 n픽셀만큼 늘림 -> 특성맵 크기 조절

            torch.nn.Conv2d(1, 16, kernel_size=5, stride=1, padding=2),    # 28 X 28 -> 32 X 32 -> 28 X 28

            torch.nn.ReLU(),        # -> 1차원 데이터를 받아 16개 특성맵으로 나누겠다 / 1차원(흑백) : 28 X 28 X 1 이미지

            torch.nn.MaxPool2d(kernel_size=2, stride=2))                           # 28 X 28 -> 14 X 14

        self.layer2 = torch.nn.Sequential(

            torch.nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=2),    # 14 X 14 -> 18 X 18 -> 14 X 14

            torch.nn.ReLU(),

            torch.nn.MaxPool2d(kernel_size=2, stride=2))                             # 14 X 14 -> 7 X 7

        self.fc = torch.nn.Linear(32 * 7 * 7, 10, bias=True)               # 32개 특성맵, 7 X 7 이미지, 10개 분류로 output

        torch.nn.init.xavier_uniform_(self.fc.weight)

   

    def forward(self, x):              # init에서 정의된 모듈을 실질적으로 실행하는 부분; 순전파

        out = self.layer1(x)

        out = self.layer2(out)

        out = out.view(out.size(0), -1)            # 평탄화

        out = self.fc(out)                   # 완전 연결층(fully-connected layer)

        return out

 

model = CNN()    # 인스턴스 생성

 

● 학습

learning_rate = 0.01    # 학습률은 모델의 가중치 업데이트할 때 사용하는 비율, 너무 크면 불안정해지고 너무 작으면 학습 속도가 느려짐 

loss_func = torch.nn.CrossEntropyLoss()    # 손실함수, CrossEntropyLoss는 분류문제에서 자주 사용되는 손실함수, 예측된 클래스 확률과 실제 클래스 간의 차이 측정

optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)    # 모델의 가중치를 업데이트하는 방법 정의

                                                                                                                # 신경망의 훈련과정에서 발생할 수 있는 문제들을 가급적 안발생하게 만듬

 

training_epochs = 10

total_batch = len(loaders['train'])

for epoch in range(training_epochs):

    avg_cost = 0

    for X, Y in loaders['train']):    # X : 이미지 데이터, Y : 정답

        optimizer.zero_grad()       # '.grad' 속성 초기화, 훈련할때마다 초기화 필요

        pred = model(X)               # 순전파

        cost = loss_func(pred, Y) # 손실 계산

        cost.backward()                # 손실에 대한 기울기 계산 및 '.grad' 속성에 저장, 역전파

        optimizer.step()                # '.grad' 속성에 저장된 기울기를 사용하여 가중치 업데이트

        avg_cost += cost / total_batch

    print('[Epoch: {:>4}] cost = {:>.9}'.format(epoch + 1, avg_cost))      # cost가 줄어드는 것을 확인해야 학습이 잘 되고있는지 알 수 있음

print('Learning Finished')

 

● 모델 평가

model.eval()

figrue = plt.figure(figsize=(10,8))

cols, rows = 5, 5

for i in range(1, cols * rows +1):

    sample_idx = torch.randint(len(test_data), size=(1, )).item()

    test_output = model(torch.unsqueeze(loaders['test'].dataset[sample_idx][0], dim=0))

    label = torch.argmax(test_output)

    figure.add_subplot(rows, cols, i)

    plt.title(label.numpy())

    plt.axis('off')

    plt.imshow(loaders['test'].dataset[sample_idx][0].squeeze().detach().numpy(), cmap='gray')

plt.show()