Lec 10-4: Custom Dataset - ImageFolder
Deep_Learning study Lec10-4
Boostcourse의 ‘파이토치로 시작하는 딥러닝 기초’를 통한 공부와 정리 Post
Goal of Study
- 사용자 데이터를 사용하는 방법에 대해 알아본다.
Keyword
- Image Folder
- CNN
1. 강의 요약
ImageFolder
이제 사용자가 학습하고자 하는 데이터를 직접 가져와 학습시켜보는 과정이다.
필자는 kaggle이라는 사이트에서 고양이와 개의 사진을 각각 12500장씩 가져와 학습시킬 예정이다.
다운받아오거나 학습시키고 싶은 데이터를 가져왔다면 다음과 같이 폴더를 구성해줄것이다.
위처럼, 파일을 구성해주었다.
이제, 이 데이터들의 사이즈를 같은 사이즈로 맞춰줄 것이다.
(사이즈가 너무 크거나, 동일하지 않기 때문에)
import torchvision
from torchvision import transforms
from torch.utils.data import DataLoader
from matplotlib.pyplot import imshow
%matplotlib inline
trans = transforms.Compose([
transforms.Resize((64,128))
])
train_data = torchvision.datasets.ImageFolder(root='E:/DeepLearningStudy/custom_data/origin_data', transform=trans)
다음과 같이 train_data를 만들어 줄 것이다.
torchvision.datasets.MNIST
등을 사용했었는데 여기서는 커스텀 데이터를 위해 .ImageFolder
를 사용해 줄 것이다.
경로를 지정해주고, transform을 위에서 지정한 tras를 통해 64 by 128 사이즈로 Resize
해준다.
for num, value in enumerate(train_data):
data, label = value
print(num, data, label)
if(label == 0):
data.save('E:/DeepLearningStudy/custom_data/train_data/cat/%d_%d.jpeg'%(num, label))
else:
data.save('E:/DeepLearningStudy/custom_data/train_data/dog/%d_%d.jpeg'%(num, label))
이를 매번 Resize할 수는 없기에 data.save
를 통해 train_data/cat(or dog) 경로에 resize한 사진들을 저장해준다.
(실행전, 저 경로에 동일한 빈 폴더가 이미 존재해야한다.)
그러면 25000장의 사진이 모두 변환되어 train_data/ 경로에 저장된다.
실제 학습
이전처럼, 다음과 같은 과정을 통해 학습을 진행할 것이다.
각 레이어의 구성은 위와 같다.
RGB이기 때문에 input_channel은 3채널이고, 커널 사이즈는 5, padding은 없다.
이를 6채널로 그리고 16채널로 출력함으로써, 위의 두 Layer를 통해 64 by 128의 이미지는
16채널의 13 by 29의 이미지가 된다.
계산과정은 이전 포스팅에서 다루었었다.
위와 같이 계산해준후, Max Pooling(2)를 진행해주면 13 by 29가 나오는 것을 확인해보면 좋을 것같다.
이를 .view
를 통해 한 차원으로 펼쳐주면, 6032로 나온다.
이제 Fully connected layer를 통해 6032에서 120으로 120에서 2로 출력해낼 것이다.
Code
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
import torchvision
import torchvision.transforms as transforms
from matplotlib.pyplot import imshow
%matplotlib inline
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print("다음 기기로 학습합니다:", device)
torch.manual_seed(777)
if device =='cuda':
torch.cuda.manual_seed_all(777)
trans = transforms.Compose([
transforms.ToTensor()
])
train_data = torchvision.datasets.ImageFolder(root='E:/DeepLearningStudy/custom_data/train_data', transform=trans)
위의 코드와 달라진 점은 이제 input shape을 Tensor로 넣어줘야하기 때문에
.ToTensor()
를 사용해주었다. 그리고 바로 아래의 코드를 통해 구성해였던 Train_data로 경로를 지정해주었다.
for num, value in enumerate(train_data):
data, label = value
print(num, data, label)
if(label == 0):
data.save('E:/DeepLearningStudy/custom_data/train_data/cat/%d_%d.jpeg'%(num, label))
else:
data.save('E:/DeepLearningStudy/custom_data/train_data/dog/%d_%d.jpeg'%(num, label))
이 과정을 이미 수행하였다면 데이터가 폴더에 저장되었기 때문에 굳이 또 실행시키지 않아도 된다.
data_loader = DataLoader(dataset = train_data, batch_size = 8, shuffle = True, num_workers=2)
이를, dataloader로 불러서 사용할 것이다.
num_workers
는 처음보는 것같아 Pytorch_Documents에서 DataLoader에 관한 부분을 찾아보았다.
num_workers (int, optional) – how many subprocesses to use for data loading. 0 means that the data will be loaded in the main process. (default: 0)
이 매개변수에 대한 고찰을 다른 분이 잘 포스팅을 해놓으셔서 참고하였다.
DataLoader_num_workers에 대한 고찰
간단히 이해한바로는 CPU와 GPU간의 task분담과 CPU에서 N개의 코어로 Data Load를 처리할 수 있도록 지정해주는 parameter가 num_workers 이며,
이 param의 튜닝을 위해 고려해야 하는 부분이 많아서(CPU, GPU, 메모리, I/O속도) 이 값 튜닝에 대한 이슈들 또한 많이 토론한다고 한다.
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.layer1 = nn.Sequential(
#input channel = 3, output channel = 6, Kernel size = 5, stride = 1, padding = 0
nn.Conv2d(3,6,5),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.layer2 = nn.Sequential(
nn.Conv2d(6,16,5),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.layer3 = nn.Sequential(
nn.Linear(16*13*29, 120),
nn.ReLU(),
nn.Linear(120,2)
)
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = out.view(out.shape[0], -1)
out = self.layer3(out)
return out
그리고 위의 Layer 구성 설명에 따라 다음과 같이 CNN()을 구성해준다.
net = CNN().to(device)
test_input = (torch.Tensor(3,3,64,128)).to(device)
test_out = net(test_input)
torch.Size([3, 6, 30, 62])
torch.Size([3, 16, 13, 29])
torch.Size([3, 6032])
그리고 model을 만들어준다.(net)
optimizer = optim.Adam(net.parameters(), lr=0.00005)
loss_func = nn.CrossEntropyLoss().to(device)
total_batch = len(data_loader)
epochs = 5
for epoch in range(epochs):
avg_cost = 0.0
for num, data in enumerate(data_loader):
imgs, labels = data
imgs = imgs.to(device)
labels = labels.to(device)
optimizer.zero_grad()
out = net(imgs)
loss = loss_func(out, labels)
loss.backward()
optimizer.step()
avg_cost += loss / total_batch
print('[Epoch:{}] cost = {}'.format(epoch+1, avg_cost))
print('Learning Finished!')
그리고 학습을 시켜준다.
[Epoch:5] cost = 0.5111982822418213
Learning Finished!
model save
torch.save(net.state_dict(), "E:/DeepLearningStudy/custom_data/model/model.pth")
new_net = CNN().to(device)
new_net.load_state_dict(torch.load('E:/DeepLearningStudy/custom_data/model/model.pth'))
# 확인 과정
print(net.layer1[0])
print(new_net.layer1[0])
print(net.layer1[0].weight[0][0][0])
print(new_net.layer1[0].weight[0][0][0])
net.layer1[0].weight[0] == new_net.layer1[0].weight[0]
Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
tensor([ 0.0225, 0.1049, 0.0722, -0.0684, -0.0265], grad_fn=) tensor([ 0.0225, 0.1049, 0.0722, -0.0684, -0.0265], grad_fn= ) Out : tensor([[[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True]],
[[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True]],
[[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True],
[True, True, True, True, True]]])
위의 과정은 학습시킨 모델을 save해서 불러오는 과정이다.
불러오기 위해서는 모델의 모양이 같아야한다.
Test
trans=torchvision.transforms.Compose([
transforms.Resize((64,128)),
transforms.ToTensor()
])
test_data = torchvision.datasets.ImageFolder(root='E:/DeepLearningStudy/custom_data/test_data/', transform=trans)
test_set = DataLoader(dataset = test_data, batch_size = len(test_data))
kaggle에서 데이터를 다운받을때 Test set도 제공을 해줘서 위와 같이 경로를 설정해주고 Test를 진행하였다.
주의점은 test_data/ 안의 test폴더(예시) 안에 사진이 있어야한다.
그냥 test_data/ 안에 바로 사진들이 들어있다면, 그 경로안에 어떠한 class도 존재하지 않는다는 에러가 뜬다.
with torch.no_grad():
for num, data in enumerate(test_set):
imgs, label = data
imgs = imgs.to(device)
label = label.to(device)
prediction = net(imgs)
correct_prediction = torch.argmax(prediction, 1) == label
accuracy = correct_prediction.float().mean()
print('Accuracy:', accuracy.item())
torch.Size([12500, 6, 30, 62])
torch.Size([12500, 16, 13, 29])
torch.Size([12500, 6032])
Accuracy: 0.5333600044250488
학습결과 Accuracy가 낮아서 epoch(5->10)을 늘려서 다시 학습시켜봤다. Learning rate도 0.0001로 실행해보았다.
(epoch 10, lr = 0.00005)
[Epoch:10] cost = 0.37749120593070984
Learning Finished!
Accuracy: 0.5279200077056885
(epoch 10, lr = 0.0001)
[Epoch:10] cost = 0.13914641737937927
Learning Finished!
Accuracy: 0.5212799906730652
Cost가 감소하는 것에 비해 정확도가 잘 나오지 않는다.
마무리
본 포스팅에서는 사용자의 Data를 가지고, Data-set를 구성하고,
이를 적절한 사이즈로 변환시킨 후, 동일하게 CNN 학습을 진행하였다.
위에 대한 전체 코드는 Github_custom data에 올려놓았다.
📣
본 포스팅의 학습 환경 : Anaconda
, CPU
, Pytorch
, Jupyter Notebook
포스팅에서 오류나 궁금한 점은 Comments를 작성해주시면, 많은 도움이 됩니다.💡
Leave a comment