import torch
import torch.nn as nn
import torch.optim as optim
# 배치크기 * 채널 * 너비 * 높이
inputs = torch.Tensor(1,1,28,28)
print(inputs.shape)
첫번째 Conv2D
# Conv2d(입력 데이터가 1개, 출력되는 피쳐의 수 32개, 마스크가 3*3짜리(기울기), padding='same': 테두리를 채워서 크기를 유지)
conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding='same')
out = conv1(inputs)
print(out.shape)
# 우선 CNN 레이어를 설정하는 과정에서 컨볼루션 연산을 수행합니다.
# 여기서 kernel_size는 필터의 크기를 의미합니다.
# padding='same'은 입력 이미지의 크기를 유지하기 위한 설정입니다.
# 따라서 이 설정으로 인해 출력 채널 수가 이전보다 32개 증가한 것을 확인할 수 있습니다.
첫번째 MaxPool2D
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape)
# 다음으로는 풀링 작업을 수행하여 이미지의 중요하지 않은 부분을 제거합니다.
# 이를 위해 이전에 설정한 28의 너비와 높이를 줄이기 위해 kernel_size를 조절합니다.
# 결과적으로 높이와 너비가 절반으로 줄어든 것을 확인할 수 있습니다.
두번째 Conv2D
conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding='same')
out = conv2(out)
print(out.shape)
두번째 MaxPool2D
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape)
flatten = nn.Flatten()
out = flatten(out)
print(out.shape) # 64 * 7 * 7 = 3136
fc = nn.Linear(3136, 10)
out = fc(out)
print(out.shape)
CNN으로 MNIST 분류하기
- MNIST는 손으로 쓴 숫자(0부터 9까지)로 이루어진 대표적인 데이터셋
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)
# 학습 데이터셋과 테스트 데이터셋 불러오기
train_data = datasets.MNIST(
root = 'data', # 데이터셋이 저장될 디렉토리 경로
train=True, # 훈련 데이터를 가져옴
transform=transforms.ToTensor(), # 이미지 데이터를 텐서로 변환하는 변환
download=True # 데이터셋이 로컬에 없을 경우 인터넷에서 데이터를 자동으로 다운로드
)
test_data = datasets.MNIST(
root = 'data',
train=False, # test용으로 설정하여 로드
transform=transforms.ToTensor(), # 이미지 데이터를 텐서로 변환하는 변환
download=True
)
print(train_data)
print(test_data)
# 데이터로더를 사용하여 학습 데이터셋을 배치 단위로 로드하기
loader = DataLoader(
dataset = train_data, # 훈련 데이터셋을 배치단위로 로드
batch_size=64, # 배치 크기 64
shuffle=True # 데이터를 섞어줌
)
# 학습 데이터셋 시각화
imgs, labels = next(iter(loader)) # 데이터로더에서 배치를 가져옴
fig, axes = plt.subplots(8, 8, figsize=(16, 16)) # 16*16픽셀의 이미지데이터가 8행 8열로 만드는 subplot
for ax, img, label in zip(axes.flatten(), imgs, labels):
ax.imshow(img.reshape((28,28)), cmap='gray') # 이미지를 28x28 크기로 변형, 흑백으로 표시
ax.set_title(label.item()) # lable값을 제목으로 설정
ax.axis('off') # 축 숨김
# 이제 구성된 CNN 모델을 활용하여 MNIST 데이터셋을 학습시키려 합니다.
# 필요한 파이썬 모듈을 불러옵니다.
# MNIST 데이터셋을 train과 test로 분할합니다.
# 이후 train 데이터셋을 DataLoader로 변환합니다.
# 여기서 batch_size는 한 번에 처리할 데이터의 개수를 의미합니다.
# CNN 모델 만들기
model = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=3, padding='same'),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(32, 64, kernel_size=3, padding='same'),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
# Flatten
nn.Flatten(),
nn.Linear(7 * 7 * 64, 10)
).to(device) # gpu로 보냄
print(model)
# 모델을 정의합니다.
# 입력 채널 1개를 받아서 출력 채널을 32개로 만드는 첫 번째 Convolutional 레이어를 정의합니다.
# 각 필터의 크기는 3x3으로 설정됩니다.
# 출력 텐서의 높이와 너비를 반으로 줄이기 위해 stride를 2로 설정하여 MaxPooling을 두 번 반복합니다.
# 최종적으로 입력 이미지 구조는 (1, 64, 7, 7)이 되며, 총 64개의 채널로 나누어지고, 높이와 너비는 각각 7이 됩니다.
# 학습 데이터로 모델 학습시키기
optimizer = optim.Adam(model.parameters(), lr=0.001)
epochs = 10
for epoch in range(epochs+1):
sum_losses = 0
sum_accs = 0
for x_batch, y_batch in loader:
x_batch = x_batch.to(device) # gpu 연산을 위해 보냄
y_batch = y_batch.to(device)
y_pred = model(x_batch)
loss = nn.CrossEntropyLoss()(y_pred, y_batch) # 3개 이상 클래스로 분류 -> CrossEntropyLoss
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 배치 단위 loss 저장
sum_losse = sum_losses + loss.item()
# 배치 단위 정확도 저장
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
sum_accs = sum_accs + acc
avg_loss = sum_losses / len(loader)
avg_acc = sum_accs / len(loader)
print(f'Epoch: {epoch:4d}/{epochs} Loss: {avg_loss:6f} Accuracy: {avg_acc:2f}%')
# 데이터로더를 사용하여 test 데이터셋을 배치 단위로 로드하기
test_loader = DataLoader(
dataset = test_data,
batch_size=64,
shuffle=True
)
# test 데이터셋 시각화
imgs, labels = next(iter(test_loader))
fig, axes = plt.subplots(8, 8, figsize=(16, 16)) # 16*16픽셀의 이미지데이터가 8행 8열로 만드는 subplot
for ax, img, label in zip(axes.flatten(), imgs, labels):
ax.imshow(img.reshape((28,28)), cmap='gray')
ax.set_title(label.item())
ax.axis('off')
평가모드로 전환하는 이유
테스트나 추론 과정에서 일관된 결과를 얻기 위해, 드롭아웃이나 배치 정규화와 같은 학습 중에만 적용되는 기법들을 비활성화하여, 모델의 성능을 평가합니다.
# 모델을 테스트 모드로 전환하여 모델 성능 평가하기
model.eval() # 모델을 테스트 모드로 전환
sum_accs = 0
for x_batch, y_batch in test_loader:
x_batch = x_batch.to(device) # gpu 연산을 위해 보냄
y_batch = y_batch.to(device)
y_pred = model(x_batch)
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
sum_accs = sum_accs + acc
avg_acc = sum_accs / len(test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다')
'머신러닝 & 딥러닝' 카테고리의 다른 글
22. 포켓몬 분류 (2) | 2024.06.17 |
---|---|
21. 전이 학습 (0) | 2024.06.17 |
19. CNN 기초 (0) | 2024.06.13 |
18. 비선형 활성화 함수 (0) | 2024.06.13 |
17. 딥러닝 (0) | 2024.06.13 |