파이토치
[파이토치] CNN
알 수 없는 사용자
2022. 2. 14. 13:05
Convolutional Neural Networks

CNN모델과 10개의 클래스를 가진 이미지 데이터인 CIFAR-10데이터를 이용하여 실습을 진행하였습니다.
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=8, shuffle=True)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=8, shuffle=False)
# CPU/GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f'{device} is available.')
1. 모델 선언
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5) # 합성곱 연산 (입력 채널수 3, 출력 채널수 6, 필터크기 5x5 , stride=1(defualt))
self.pool1 = nn.MaxPool2d(2, 2) # 합성곱 연산 (필터크기 2, stride=2)
self.conv2 = nn.Conv2d(6, 16, 5) # 합성곱 연산 (입력 채널수 6, 출력 채널수 16, 필터크기 5x5 , stride=1(defualt))
self.pool2 = nn.MaxPool2d(2, 2) # 합성곱 연산 (필터크기 2, stride=2)
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5x5 피쳐맵 16개를 일렬로 피면 16*5*5개의 노드가 생성
self.fc2 = nn.Linear(120, 10) # 120개 노드에서 클래스의 개수인 10개의 노드로 연산
def forward(self, x): # 실제 계산이 이루어진다.
x = self.pool1(F.relu(self.conv1(x))) # conv1 -> ReLU -> pool1
x = self.pool2(F.relu(self.conv2(x))) # conv2 -> ReLU -> pool2
x = x.view(-1, 16 * 5 * 5) # 5x5 피쳐맵 16개를 일렬로 만든다. # -1은 배치수 대신에 사용하였다.
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
2. 모델 선언 (Preferable)
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# sequential로 feature extraction layers를 하나로 묶을 수 있다.
self.feature_extraction = nn.Sequential(nn.Conv2d(3, 6, 5),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(2, 2))
self.classifier = nn.Sequential(nn.Linear(16 * 5 * 5, 120),
nn.ReLU(),
nn.Linear(120, 10))
def forward(self, x):
x = self.feature_extraction(x)
x = x.view(-1, 16 * 5 * 5) # 5x5 피쳐맵 16개를 일렬로 만든다.
x = self.classifier(x)
return x
net = Net().to(device) # 모델이 GPU연산을 가능하도록 한다.
criterion = nn.CrossEntropyLoss() # CrossEntropyLoss는 softmax 계산까지 포함되어 있으므로 모델의 마지막 output node에 별도의 활성화 함수를 사용하지 않아도 된다.
optimizer = optim.SGD(net.parameters(), lr=1e-3, momentum=0.9)
# 모델의 학습 과정인 4강에서 배운 인공 신경망과 동일하다.
loss_ = [] # 그래프를 그리기 위한 loss 저장용 리스트
n = len(trainloader) # 배치 개수
for epoch in range(10): # 10번 학습을 진행한다.
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data[0].to(device), data[1].to(device) # GPU연산이 가능한 배치 데이터
optimizer.zero_grad()
outputs = net(inputs) # 예측값 산출
loss = criterion(outputs, labels) # 손실함수 계산
loss.backward() # 손실함수 기준으로 역전파 선언
optimizer.step() # 가중치 최적화
# print statistics
running_loss += loss.item()
loss_.append(running_loss / n)
print('[%d] loss: %.3f' %(epoch + 1, running_loss / len(trainloader)))
print('Finished Training')
plt.plot(loss_)
plt.title("Training Loss")
plt.xlabel("epoch")
plt.show()

PATH = './models/cifar_net.pth' # 모델 저장 경로
torch.save(net.state_dict(), PATH) # 모델 저장
# 모델 불러오기는 엄밀히 말하자면 모델의 파라메타를 불러오는 것이다. 따라서 모델의 뼈대를 먼저 선언하고
# 모델의 파라메타를 불러와 pretrained model을 만든다.
net = Net().to(device) # 모델을 먼저 불러오고 그 위에 파라메타를 덮어씌운다. 이때, GPU 모델로 저장을 했기 때문에, 불러올때도 GPU로 받아야한다.
net.load_state_dict(torch.load(PATH)) # 모델 파라메타 불러오기
# 평가 데이터를 이용해 정확도 구하기.
# output은 미니배치의 결과가 산출되기 때문에 for문을 통해서 test 전체의 예측값을 구한다.
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data[0].to(device), data[1].to(device)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1) # 벡터값에서 argmax를 하기위해 max()를 취한다. 즉, 최댓값의 위치
total += labels.size(0) # 개수 누적(총 개수)
correct += (predicted == labels).sum().item() # 누적(맞으면 1, 틀리면 0으로 합산)
print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))