Pytorch를 활용한 CNN 구현하기

: 코드 레벨 설명

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

MNIST 데이터셋 로드 및 전처리

transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)력하세요

간단한 CNN 모델 정의

class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(64 * 28 * 28, 128)
        self.fc2 = nn.Linear(128, 10)
        
    def forward(self, x):
        x = torch.relu(self.conv1(x))  # 첫 번째 Convolution + Activation
        x = torch.relu(self.conv2(x))  # 두 번째 Convolution + Activation
        x = x.view(x.size(0), -1)  # Flatten 연산
        x = torch.relu(self.fc1(x))  # 첫 번째 Fully Connected + Activation
        x = self.fc2(x)  # 두 번째 Fully Connected (출력 레이어)
        return x

모델, 손실 함수, 옵티마이저 설정

: device 설정시 mac 용에 최적화된 mps 사용

device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")
model = SimpleCNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

훈련 함수

def train(model, train_loader, criterion, optimizer, num_epochs=10):
    model.train()
    for epoch in range(num_epochs):
        running_loss = 0.0
        for inputs, labels in train_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
        print(f'Epoch {epoch+1}, Loss: {running_loss/len(train_loader)}')

테스트 함수

def evaluate(model, test_loader, criterion):
    model.eval()
    correct = 0
    total = 0
    loss = 0.0
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            loss += criterion(outputs, labels).item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = 100 * correct / total
    print(f'Loss: {loss/len(test_loader)}, Accuracy: {accuracy}%')
    return loss / len(test_loader), accuracy
train(model, train_loader, criterion, optimizer)
evaluate(model, test_loader, criterion)

Max Pooling, Average Pooling 레이어 추가

Max Pooling이나 Average Pooling은 고정된 커널 크기와 스트라이드를 사용하여 입력 텐서의 크기를 줄이면서 계산

Pooling Test 용 CNN 모델 정의

class CNNWithPooling(nn.Module):
    def __init__(self):
        super(CNNWithPooling, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)  # Max Pooling 레이어 추가
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(64 * 7 * 7, 128)  # Max Pooling으로 인해 크기 변경
        self.fc2 = nn.Linear(128, 10)
        
    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))  # Convolution + Activation + Max Pooling
        x = self.pool(torch.relu(self.conv2(x)))  # Convolution + Activation + Max Pooling
        x = x.view(x.size(0), -1)  # Flatten 연산
        x = torch.relu(self.fc1(x))  # 첫 번째 Fully Connected + Activation
        x = self.fc2(x)  # 두 번째 Fully Connected (출력 레이어)
        return x

모델 초기화, 손실 함수 및 최적화 설정

device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")
model = CNNWithPooling().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

모델 훈련 함수

def train(model, train_loader, criterion, optimizer, num_epochs=10):
    model.train()
    for epoch in range(num_epochs):
        running_loss = 0.0
        for inputs, labels in train_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
        print(f'Epoch {epoch+1}, Loss: {running_loss/len(train_loader)}')

모델 평가 함수

def evaluate(model, test_loader, criterion):
    model.eval()
    correct = 0
    total = 0
    loss = 0.0
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            loss += criterion(outputs, labels).item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = 100 * correct / total
    print(f'Loss: {loss/len(test_loader)}, Accuracy: {accuracy}%')
    return loss / len(test_loader), accuracy

모델 훈련 및 평가

train(model, train_loader, criterion, optimizer)
evaluate(model, test_loader, criterion)

Adaptive Pooling 활용

daptive Pooling은 출력 텐서의 크기를 미리 정의하고, 그 크기에 맞게 입력 텐서를 유동적으로 변환

1.	Adaptive Max Pooling: 각 출력 위치에 대해 입력 텐서의 특정 영역에서 최대값을 선택합니다.
2.	Adaptive Average Pooling: 각 출력 위치에 대해 입력 텐서의 특정 영역에서 평균값을 계산합니다.
class CNNWithAdaptivePooling(nn.Module):
    def __init__(self):
        super(CNNWithAdaptivePooling, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.adaptive_pool = nn.AdaptiveAvgPool2d((7, 7))  # Adaptive Pooling 레이어 추가
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)
        
    def forward(self, x):
        x = torch.relu(self.conv1(x))  # 첫 번째 Convolution + Activation
        x = torch.relu(self.conv2(x))  # 두 번째 Convolution + Activation
        x = self.adaptive_pool(x)  # Adaptive Pooling
        x = x.view(x.size(0), -1)  # Flatten 연산
        x = torch.relu(self.fc1(x))  # 첫 번째 Fully Connected + Activation
        x = self.fc2(x)  # 두 번째 Fully Connected (출력 레이어)
        return x

모델 초기화, 손실 함수 및 최적화 설정은 동일

model = CNNWithAdaptivePooling().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

모델 훈련 함수

def train(model, train_loader, criterion, optimizer, num_epochs=10):
    model.train()
    for epoch in range(num_epochs):
        running_loss = 0.0
        for inputs, labels in train_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
        print(f'Epoch {epoch+1}, Loss: {running_loss/len(train_loader)}')

모델 평가 함수

def evaluate(model, test_loader, criterion):
    model.eval()
    correct = 0
    total = 0
    loss = 0.0
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            loss += criterion(outputs, labels).item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = 100 * correct / total
    print(f'Loss: {loss/len(test_loader)}, Accuracy: {accuracy}%')
    return loss / len(test_loader), accuracy

모델 훈련 및 평가

train(model, train_loader, criterion, optimizer)
evaluate(model, test_loader, criterion)

: accuracy -> 99프로

Only Convolution + Activations

Flattern 연산 및 Fully Connected 레이어 없이 Convolution과 Activation 레이어만 가지고 MNIST 분류기 만들기

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

하이퍼파라미터 설정

batch_size = 64
epochs = 10
learning_rate = 0.001

데이터셋 로드 및 전처리

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform, download=True)

train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

모델 정의

class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.conv3 = nn.Conv2d(64, 10, kernel_size=3, padding=1)
        self.relu = nn.ReLU()
        self.pool = nn.AdaptiveAvgPool2d((1, 1))

    def forward(self, x):
        x = self.relu(self.conv1(x)) # 첫 번째 Conv 레이어 + ReLU
        x = self.pool(x)             # Adaptive Average Pooling
        x = self.relu(self.conv2(x)) # 두 번째 Conv 레이어 + ReLU
        x = self.pool(x)             # Adaptive Average Pooling
        x = self.conv3(x)            # 세 번째 Conv 레이어 (출력: 10채널)
        x = self.pool(x)             # Adaptive Average Pooling
        return x.squeeze()           # 차원 축소 (batch_size, 10)

model = ConvNet()

손실 함수 및 옵티마이저 정의

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

모델 학습

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

for epoch in range(epochs):
    model.train()
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)

        outputs = model(images)
        loss = criterion(outputs, labels)

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

    print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

모델 평가

model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print(f'Test Accuracy: {100 * correct / total:.2f}%')

: 실제 이 모델의 경우 정확도가 50프로 이하로 매우 낮음

profile
인공지능관련 작업중

0개의 댓글