1. 손글씨 도형
- 그림판으로 그린 손글씨 이미지 총 300개의 이미지를 압축시켜놓은 파일입니다.
사용하던 data 폴더에 압축파일을 옮겨주세요
# Python에서 사용하는 파일 시스템 경로 변경 명령으로
# 현재 디렉토리를 변경해줍니다
%cd /content/drive/MyDrive/(상위폴더 1)/(상위폴더 2)/data
# 압축풀기
# -qq : quickmode 결과를 출력을 하지 않음.
!unzip -qq "/content/drive/MyDrive/(상위폴더 1)/(상위폴더 2)/data/shape.zip"
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader
import matplotlib.pyplot as plt
transform = transforms.Compose([
transforms.Resize((28, 28)),
# Grayscale(1) : 모든 이미지를 Grayscale로 변환, 0.5입력 시 50프로만 랜덤하게
transforms.Grayscale(1),
transforms.ToTensor(),
# RandomInvert(1) : 모든 이미지 색상 반전, 0.5입력 시 50프로만 랜덤하게
transforms.RandomInvert(1),
transforms.Normalize((0.5), (0.5)),
])
train_path = '/content/drive/MyDrive/(상위폴더 1)/(상위폴더 2)/data/shape/train'
test_path = '/content/drive/MyDrive/(상위폴더 1)/(상위폴더 2)/data/shape/test'
trainset = torchvision.datasets.ImageFolder(
root=train_path,
transform=transform
)
testset = torchvision.datasets.ImageFolder(
root=test_path,
transform=transform
)
len(trainset), len(testset)
# 10번째 데이터 가져오기
trainset.__getitem__(10)
# 데이터셋의 class 확인하기
trainset.classes, testset.classes
loader = DataLoader (
dataset = trainset,
batch_size = 64,
shuffle = True
)
imgs, labels = next(iter(loader))
fig, axes = plt.subplots(8, 8, figsize=(16, 16))
for ax, img, label in zip(axes.flatten(), imgs, labels):
ax.imshow(img.reshape(28, 28), cmap='gray')
ax.set_title(class_map[label.item()])
ax.axis('off')
# 장치 확인
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)
2. CNN 모델 만들기
class ConvNeuralNetwork(nn.Module) :
def __init__(self) :
super(ConvNeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.classifier = nn.Sequential(
# Conv layer 1
nn.Conv2d(1, 28, kernel_size=3, padding='same'),
nn.ReLU(),
# Conv layer2
nn.Conv2d(28, 28, kernel_size=3, padding='same'),
nn.ReLU(),
# Max Pooling layer
nn.MaxPool2d(kernel_size=2),
nn.Dropout(0.25),
# Conv layer3
nn.Conv2d(28, 56, kernel_size=3, padding='same'),
nn.ReLU(),
# Conv layer4
nn.Conv2d(56, 56, kernel_size=3, padding='same'),
nn.ReLU(),
# Max Pooling layer
nn.MaxPool2d(kernel_size=2),
nn.Dropout(0.25)
)
self.Linear = nn.Linear(56 * 7 * 7, 3)
def forward(self, x) :
x = self.classifier(x)
x = self.flatten(x)
output = self.Linear(x)
return output
2-1. CNN 모델 학습시키기
model = ConvNeuralNetwork().to(device)
print(model)
loss = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
def train_loop(loader, model, loss_fn, optimizer) :
sum_losses = 0
sum_accs = 0
for x_batch, y_batch in loader :
x_batch = x_batch.to(device)
y_batch = y_batch.to(device)
y_pred = model(x_batch)
loss = loss_fn(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
sum_losses += loss
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 += acc
avg_loss = sum_losses / len(loader)
avg_acc = sum_accs / len(loader)
return avg_loss, avg_acc
epochs = 50
for i in range(epochs) :
print(f"------------------------------------------------")
avg_loss, avg_acc = train_loop(loader, model, loss, optimizer)
print(f'Epoch: {i+1:4d}/{epochs}, Loss: {avg_loss:.6f}, Accuracy: {avg_acc:.2f}%')
print('Done!!')
2-2. CNN 모델 Test하기
test_loader = DataLoader(
dataset=testset,
batch_size=32,
shuffle=False
)
imgs, labels = next(iter(test_loader))
fig, axes = plt.subplots(4, 8, figsize=(16, 8))
for ax, img, label in zip(axes.flatten(), imgs, labels):
ax.imshow(img.reshape(28, 28), cmap='gray')
ax.set_title(class_map[label.item()])
ax.axis('off')
def test(model, test_loader) :
model.eval()
sum_accs = 0
img_list = torch.Tensor().to(device)
y_pred_list = torch.Tensor().to(device)
y_true_list = torch.Tensor().to(device)
for x_batch, y_batch in test_loader :
x_batch = x_batch.to(device)
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)
# torch.cat((y_pred_list, y_pred_index), dim = 0) : 행(0)으로 합치기
y_pred_list = torch.cat((y_pred_list, y_pred_index), dim = 0)
y_true_list = torch.cat((y_true_list, y_batch), dim = 0)
img_list = torch.cat((img_list, x_batch), dim = 0)
acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
sum_accs += acc
avg_acc = sum_accs / len(test_loader)
return y_pred_list, y_true_list, img_list, avg_acc
y_pred_list, y_true_list, img_list, avg_acc = test(model, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')
fig, axes = plt.subplots(4, 8, figsize=(16, 8))
img_list_cpu = img_list.cpu()
y_pred_list_cpu = y_pred_list.cpu()
y_true_list_cpu = y_true_list.cpu()
for ax, img, y_pred, y_true in zip(axes.flatten(), img_list_cpu, y_pred_list_cpu, y_true_list_cpu):
ax.imshow(img.reshape(28, 28), cmap='gray')
ax.set_title(f'pred: {class_map[y_pred.item()]}, true: {class_map[y_true.item()]}')
ax.axis('off')
plt.show()
3. 모델 저장하고 불러오기
# 모델의 가중치(Weight)와 매개변수(Parameters)만 저장
# 모델의 구조가 저장되지 않으므로 모델 클래스 정의가 없으면 복원 할 수 없음
torch.save(model.state_dict(), 'model_weights.pth')
model2 = ConvNeuralNetwork().to(device)
print(model2)
# 모델을 만들고 학습을 하지 않았기 때문에 정확도는 매우 떨어질 것이다
y_pred_list, y_true_list, img_list, avg_acc = test(model2, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')
# 저장된 모델의 파라미터 불러오기
model2.load_state_dict(torch.load('model_weights.pth'))
# 학습을 마치고 난 이후의 파라미터들이 저장되어있는 것을 가져다 사용했기 때문에
# 학습을 마치고 난 이후의 결과가 나올 것이다
y_pred_list, y_true_list, img_list, avg_acc = test(model2, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')
4. 모델 전체 저장하기
- 모델 Class와 Weight가 함께 저장되므로, 복원 시 모델 구조를 별도로 정의할 필요가 없습니다.
torch.save(model, 'model.pth')
model3 = torch.load('model.pth')
# 모델의 구조 자체까지도 저장되었기 때문에 바로 사용이 가능하고
# 정확도도 학습을 마친 정확도가 나오게 된다
y_pred_list, y_true_list, img_list, avg_acc = test(model3, test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다.')