From 6317b363c84a2a996fa64f785caba7781abea383 Mon Sep 17 00:00:00 2001 From: pinb Date: Thu, 27 Apr 2023 01:08:35 +0000 Subject: [PATCH] =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20'1?= =?UTF-8?q?=EC=B0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=5F=EC=9B=90=EB=B3=B8=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=95=99=EC=8A=B5'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...B0%EC%9D%B4%ED%84%B0-%ED%95%99%EC%8A%B5.md | 214 ++---------------- 1 file changed, 22 insertions(+), 192 deletions(-) diff --git a/1%EC%B0%A8-%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%9B%90%EB%B3%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%95%99%EC%8A%B5.md b/1%EC%B0%A8-%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%9B%90%EB%B3%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%95%99%EC%8A%B5.md index 45f73b8..307af98 100644 --- a/1%EC%B0%A8-%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%9B%90%EB%B3%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%95%99%EC%8A%B5.md +++ b/1%EC%B0%A8-%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%9B%90%EB%B3%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%95%99%EC%8A%B5.md @@ -1,71 +1,26 @@ -# Model +# 테스트 방법 +* WDI 원본 데이터셋을 그대로 사용한다. -```python -import torch -import torch.nn as nn -import torch.nn.functional as F - -class CNN_WDI(nn.Module): - def __init__(self, class_num=9): - super(CNN_WDI, self).__init__() - - self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=0) - self.bn1 = nn.BatchNorm2d(16) - self.pool1 = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(16, 16, kernel_size=3, padding=1) - self.bn2 = nn.BatchNorm2d(16) - - self.conv3 = nn.Conv2d(16, 32, kernel_size=3, padding=1) - self.bn3 = nn.BatchNorm2d(32) - self.pool2 = nn.MaxPool2d(2, 2) - self.conv4 = nn.Conv2d(32, 32, kernel_size=3, padding=1) - self.bn4 = nn.BatchNorm2d(32) - - self.conv5 = nn.Conv2d(32, 64, kernel_size=3, padding=1) - self.bn5 = nn.BatchNorm2d(64) - self.pool3 = nn.MaxPool2d(2, 2) - self.conv6 = nn.Conv2d(64, 64, kernel_size=3, padding=1) - self.bn6 = nn.BatchNorm2d(64) - - self.conv7 = nn.Conv2d(64, 128, kernel_size=3, padding=1) - self.bn7 = nn.BatchNorm2d(128) - self.pool4 = nn.MaxPool2d(2, 2) - self.conv8 = nn.Conv2d(128, 128, kernel_size=3, padding=1) - self.bn8 = nn.BatchNorm2d(128) - - self.spatial_dropout = nn.Dropout2d(0.2) - self.pool5 = nn.MaxPool2d(2, 2) - - self.fc1 = nn.Linear(4608, 512) - self.fc2 = nn.Linear(512, class_num) - - def forward(self, x): - x = F.relu(self.bn1(self.conv1(x))) - x = self.pool1(F.relu(self.bn2(self.conv2(x)))) - - x = F.relu(self.bn3(self.conv3(x))) - x = self.pool2(F.relu(self.bn4(self.conv4(x)))) - - x = F.relu(self.bn5(self.conv5(x))) - x = self.pool3(F.relu(self.bn6(self.conv6(x)))) - - x = F.relu(self.bn7(self.conv7(x))) - x = self.pool4(F.relu(self.bn8(self.conv8(x)))) - - x = self.spatial_dropout(x) - x = self.pool5(x) - - x = x.view(x.size(0), -1) - x = F.relu(self.fc1(x)) - x = self.fc2(x) - - return F.softmax(x, dim=1) +* near-full 클레스의 데이터 수가 가장 적으므로 최대치인 이미지 개수를 조정한다. + * train_max_images = 95 + *val_max_images = 25 + +* 이 경우, 1 epoch 당 120개의 224 x 224 x 3 의 데이터가 들어가므로 배치 사이즈를 맞춰준다. + * batch_size = 18063360 + +* 매우 적은 데이터 단위로 학습하므로 1 epoch 당 데이터셋 원본 폴더에서 랜덤하게 데이터를 선택하여 학습한다. -cnn_wdi = CNN_WDI(class_num=9) +* 랜덤하게 선택한 데이터들은 Argumentation을 적용한다. + * 대략 20 px ~ 300 px 사이즈의 이미지들이 존재하므로 224 x 224에 맞게 0으로 Padding 해준다. + 1. 먼저 상하좌우 224 px 사이즈만큼 0을 Padding 한다. + 2. 그리고 중심에서 224 x 224 사이즈만큼 Crop 한다. + 3. 그 외의 Argumentation 종류는 H-Flip, V-Flip, Rotation (0 ~ 360도를 15도 단위로 회전) 한다. + +* 위와 같은 방법으로 학습 시, 적은 VRAM을 사용하여 다양한 데이터셋을 학습할 수 있을 것으로 예상된다. -``` +--------------------------------------------------------------------------- # Load Data @@ -106,11 +61,10 @@ criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(cnn_wdi.parameters(), lr=0.001) # 배치사이즈 -batch_size = 18063360 #112 +batch_size = 18063360 # 학습 및 평가 실행 -num_epochs = 100 #* 192 -# num_epochs = 50 +num_epochs = 100 # Random sample size train_max_images = 95 @@ -118,133 +72,8 @@ val_max_images = 25 ``` - - -# Train Function - - -```python -# 학습 함수 정의 -def train(model, dataloader, criterion, optimizer, device): - model.train() - running_loss = 0.0 - running_corrects = 0 - - for inputs, labels in dataloader: - inputs = inputs.to(device) - labels = labels.to(device) - - optimizer.zero_grad() - - outputs = model(inputs) - _, preds = torch.max(outputs, 1) - loss = criterion(outputs, labels) - - loss.backward() - optimizer.step() - - running_loss += loss.item() * inputs.size(0) - running_corrects += torch.sum(preds == labels.data) - - epoch_loss = running_loss / len(dataloader.dataset) - epoch_acc = running_corrects.double() / len(dataloader.dataset) - - return epoch_loss, epoch_acc -``` - -# Evaluate Function - - -```python -# 평가 함수 정의 -def evaluate(model, dataloader, criterion, device): - model.eval() - running_loss = 0.0 - running_corrects = 0 - - with torch.no_grad(): - for inputs, labels in dataloader: - inputs = inputs.to(device) - labels = labels.to(device) - - outputs = model(inputs) - _, preds = torch.max(outputs, 1) - loss = criterion(outputs, labels) - - running_loss += loss.item() * inputs.size(0) - running_corrects += torch.sum(preds == labels.data) - - epoch_loss = running_loss / len(dataloader.dataset) - epoch_acc = running_corrects.double() / len(dataloader.dataset) - - return epoch_loss, epoch_acc -``` - -# Train - - -```python -# Train & Validation의 Loss, Acc 기록 파일 -s_title = 'Epoch,\tTrain Loss,\tTrain Acc,\tVal Loss,\tVal Acc\n' -with open('output.txt', 'a') as file: - file.write(s_title) -print(s_title) - -for epoch in range(num_epochs + 1): - # 무작위 샘플 추출 - train_indices = torch.randperm(len(train_dataset))[:train_max_images] - train_random_subset = torch.utils.data.Subset(train_dataset, train_indices) - train_loader = torch.utils.data.DataLoader(train_random_subset, batch_size=batch_size, shuffle=True, num_workers=4) - - val_indices = torch.randperm(len(val_dataset))[:val_max_images] - val_random_subset = torch.utils.data.Subset(train_dataset, val_indices) - val_loader = torch.utils.data.DataLoader(val_random_subset, batch_size=batch_size, shuffle=False, num_workers=4) - - # 학습 및 Validation 평가 - train_loss, train_acc = train(cnn_wdi, train_loader, criterion, optimizer, device) - val_loss, val_acc = evaluate(cnn_wdi, val_loader, criterion, device) - - # 로그 기록 - s_output = f'{epoch + 1}/{num_epochs},\t{train_loss:.4f},\t{train_acc:.4f},\t{val_loss:.4f},\t{val_acc:.4f}\n' - with open('output.txt', 'a') as file: - file.write(s_output) - print(s_output) - - if epoch % 10 == 0: - # 모델 저장 - torch.save(cnn_wdi.state_dict(), 'CNN_WDI_' + str(epoch) + 'epoch.pth') -``` - - Epoch, Train Loss, Train Acc, Val Loss, Val Acc - - 1/100, 1.5930, 0.7789, 1.8920, 0.4800 - - 2/100, 1.5193, 0.8526, 1.8520, 0.5200 - - 3/100, 1.4562, 0.9158, 1.9320, 0.4400 - - 4/100, 1.5088, 0.8632, 1.7720, 0.6000 - - 5/100, 1.5088, 0.8632, 1.8920, 0.4800 - - 6/100, 1.4983, 0.8737, 1.8520, 0.5200 - - 7/100, 1.4983, 0.8737, 1.9720, 0.4000 - - 8/100, 1.5720, 0.8000, 2.0120, 0.3600 - - 9/100, 1.5298, 0.8421, 2.0120, 0.3600 - - 10/100, 1.5088, 0.8632, 2.0520, 0.3200 - - 11/100, 1.4983, 0.8737, 2.0120, 0.3600 - - ... - - - --------------------------------------------------------------------------- - +--------------------------------------------------------------------------- # 원본 데이터셋 학습 결과 @@ -282,5 +111,6 @@ Epoch, Train Loss, Train Acc, Val Loss, Val Acc ``` : 위 설정으로 학습 진행 시, 데이터 부족으로 학습이 제대로 이루어지지 않음. +배치 사이즈를 크게 잡았지만, 결국 학습 이미지셋의 개수가 95 + 25로 정해져 있으므로 한계가 있음. > **-> 데이터 증량 필요 !**