CNN 학습

조회수 714회
#!/usr/bin/env python
# coding: utf-8

# In[30]:


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


# In[31]:


import visdom

vis = visdom.Visdom()
vis.close(env="main")


# In[32]:


def value_tracker(value_plot, value, num):
    '''num, loss_value, are Tensor'''
    vis.line(X=num,
             Y=value,
             win = value_plot,
             update='append'
             )


# In[33]:


#device = 'cuda' if torch.cuda.is_available() else 'cpu'
device = 'cpu'
print(device)

torch.manual_seed(2020)
if device =='cuda':
    torch.cuda.manual_seed_all(2020)


# In[34]:


train_data_mean = [0.5, 0.5, 0.5]
train_data_std = [0.5, 0.5, 0.5]


# In[35]:


transform_train = transforms.Compose([
    transforms.RandomCrop(16, padding=4),
    transforms.ToTensor(),
    #transforms.Normalize(train_data_mean, train_data_std)
])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(train_data_mean, train_data_std)
])

trainset = torchvision.datasets.ImageFolder(root='C:/Users/beomseokpark/Desktop/CNN/train_data_processed',
                                            transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,
                                          shuffle=True, num_workers=0)

testset = torchvision.datasets.ImageFolder(root='C:/Users/beomseokpark/Desktop/CNN/test_data_processed',
                                            transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=128,
                                         shuffle=False, num_workers=0)


# In[36]:


import torchvision.models.resnet as resnet


# In[37]:


conv1x1=resnet.conv1x1
Bottleneck = resnet.Bottleneck
BasicBlock= resnet.BasicBlock


# In[38]:


class ResNet(nn.Module):

    def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):
        super(ResNet, self).__init__()
        self.inplanes = 16
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1,
                               bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.relu = nn.ReLU(inplace=True)
        #self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        self.layer1 = self._make_layer(block, 16, layers[0], stride=1)
        self.layer2 = self._make_layer(block, 32, layers[1], stride=1)
        self.layer3 = self._make_layer(block, 64, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 128, layers[3], stride=2)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(128 * block.expansion, num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

        if zero_init_residual:
            for m in self.modules():
                if isinstance(m, Bottleneck):
                    nn.init.constant_(m.bn3.weight, 0)
                elif isinstance(m, BasicBlock):
                    nn.init.constant_(m.bn2.weight, 0)

    def _make_layer(self, block, planes, blocks, stride=1):
        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                conv1x1(self.inplanes, planes * block.expansion, stride),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample))
        self.inplanes = planes * block.expansion
        for _ in range(1, blocks):
            layers.append(block(self.inplanes, planes))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        #x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = self.avgpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x


# In[39]:


resnet18 = ResNet(resnet.BasicBlock, [2, 2, 2, 2], 4, True).to(device)
resnet50 = ResNet(resnet.Bottleneck, [3, 4, 6, 3], 4, True).to(device) 


# In[40]:


criterion = nn.CrossEntropyLoss().to(device)
#optimizer = torch.optim.SGD(resnet50.parameters(), lr = 0.005, momentum = 0.9, weight_decay=5e-4)#default = lr 0.1
optimizer = torch.optim.Adam(resnet50.parameters(), lr=0.05)
lr_sche = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=1)#default = gamma 0.5


# In[41]:


loss_plt = vis.line(Y=torch.Tensor(1).zero_(),opts=dict(title='loss_tracker', legend=['loss'], showlegend=True))
acc_plt = vis.line(Y=torch.Tensor(1).zero_(),opts=dict(title='Accuracy', legend=['Acc'], showlegend=True))


# In[42]:


def acc_check(net, test_set, epoch, save=1):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_set:
            images, labels = data
            images = images.to(device)
            labels = labels.to(device)
            outputs = net(images)

            _, predicted = torch.max(outputs.data, 1)

            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    acc = (100 * correct / total)
    print('Accuracy of the network on test images: %d %%' % acc)
    if save:
        torch.save(net.state_dict(), "C:/Users/beomseokpark/Desktop/CNN/model/ResNet_epoch_{}_acc_{}.pth".format(epoch, int(acc)))
    return acc


# In[43]:


print(len(trainloader))
epochs = 50

for epoch in range(epochs):

    running_loss = 0.0
    lr_sche.step()
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        outputs = resnet50(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 30 == 29:
            value_tracker(loss_plt, torch.Tensor([running_loss/30]), torch.Tensor([i + epoch*len(trainloader) ]))
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 30))
            running_loss = 0.0

    acc = acc_check(resnet50, testloader, epoch, save=1)
    value_tracker(acc_plt, torch.Tensor([acc]), torch.Tensor([epoch]))


print('Finished Training')


# In[ ]:


correct = 0
total = 0

with torch.no_grad():
    for data in testloader:
        images, labels = data
        images = images.to(device)
        labels = labels.to(device)
        outputs = resnet50(images)

        _, predicted = torch.max(outputs.data, 1)

        total += labels.size(0)

        correct += (predicted == labels).sum().item()

print('Accuracy of the network on test images: %d %%' % (
    100 * correct / total))

이미지

다음과 같은 암석 박편 데이터셋을 수집하여 4개의 클래스로 구별하는 코드를 ResNet으로 구현해보았는데, 정확도가 너무 나오지 않네요.

학습이 잘 되지 않는데 데이터셋이 학습시키기 어려운 것인가요?

이 정도 난이도의 데이터셋 같은 경우 어느정도의 정확도가 나와야 정상인지 궁금합니다.

1 답변

  • 좋아요

    1

    싫어요
    채택 취소하기

    좀 실망스러운 답변이 되겠습니다만 그냥 주어진 코드와 캡쳐만 가만히 보자면 말씀이지요, Garbage In Garbage Out이라는 격언만이 강렬하게 떠오릅니다.

    "쓰레기가 들어가면 쓰레기가 나온다"는 뜻으로 (...) 결함이 있는, 심지어는 터무니없는 입력 데이터라도 의심을 품지 않고 처리하며, 생각하지도 않던 터무니없는 출력을 만들어낸다는 사실을 가리킨다. (...) 이 표현은 (...) 강력한 컴퓨터들이 많은 양의 오류성 데이터나 정보를 빠른 시간 내에 출력할 수 있다는 점에서 오늘날에 훨씬 더 잘 적용된다.

    누가 알겠습니까요? 지금 극히 일부만 올려 주신 저 분류 결과가 사실은 매우 올바른 것일지. 작성하신 코드는 그저 아주 성실하게 Garbage In Garbage Out을 수행한 것뿐은 아닐는지. 그리고 그 코드의 주인이신 질문자님은 그 코드의 업무 수행 결과를 평가할 객관적인 도리가 있긴 한지. 그래서 저 코드를 지우고 다시 짜려고 할 때, 저 코드가 "배운 대로 일했더니 리팩토링 왠말이냐!"를 외치고 파업을 한대면 그땐 어떻게 교섭하실 수 있을는지.* 무슨 일이 일어났어야 하고, 그러려면 뭐가 준비되었어야 하고, 뭘 어디에 넣어서 어떤 비교 작업을 거쳐 어떤 것이 어떻게 출력되었어야 했단 말인지.

    그런 걸 잘 모르겠단 말씀이지요. 좀 알려 주시면 감사하겠습니다.


    * 웃기려고만 쓴 표현은 아닙니다. 실제 리팩토링 과정에서 왕왕 일어나는 일입니다. 분명 필요 없는 코드/데이터인데, 그것만 지워놓고 고쳐놨을 뿐인데 그때부터 완전히 다른 동작을 하며 파행을 하는 경우들이 있습니다. 코드 전체 또는 서비스 전체의 구조(ex. 일관된 쓰레기가 입력됨)와 맞물려 있어서 빼고 싶어도 뺄 수 없는 고임목이 되어 있는 것이지요. 이런 차원에서는 오히려, "이 데이터 입력들이 혹시 쓰레기는 아닌가?"를 지금 단계에서 의심하지 않으면, 지금 단계의 저 데이터들 기준으로 "잘돌아가는" 코드를 만들어 놓으면, 나중에 그대로 그만큼의 고생문이 열리는 겁니다.

    • 제가 잘못 설명한것 같네요. 죄송합니다. 네 종류의 암석의 구분은 이미 되어 있는 상태이고 구분 되어있는 암석 박편에 라벨링을 하여 학습시킨 것입니다. 헷갈리게 작성한부분 죄송합니다. 박범석 2020.3.20 16:49

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)