[D&A 운영진 딥러닝 스터디] 3주차 1차시

권유진·2022년 1월 17일
0

D&A 운영진 스터디

목록 보기
5/17

컴퓨터 비전

  • 컴퓨터 비전 문제는 이미지를 구성하는 픽셀 값을 input으로 사용
  • 일반적으로 input의 변수가 독립적이지만 이미지의 픽셀 값은 서로 독립이 아니다.
  • MLP의 경우 이미지의 2차원 픽셀 값을 vector로 flatten하여 input으로 사용
    • flatten 시, 가까운 위치에 있는 픽셀의 정보 반영 불가
      • 이러한 지역 정보(Region Feature) 학습할 수 있는 CNN 등장
  • 자신의 image data 불러오고 싶을 시 torchvision.datasets.ImageFolder() 메소드 이용

CNN(Convolutional Neural Network)

  • Region Feature(Graphical Feature)을 학습시키기 위한 신경망 모형
    • 1998년에 Yann LeCun 교수가 제안하였지만 학습 시간이 너무 길고 신경망 모형의 단점으로 인해 주목받지 못함
    • 2010년대에 들어 알고리즘과 하드웨어의 발전으로 CNN 급부상
  • Region Feature을 뽑아내는 Convolution Layer와 Feature Dimension을 위한 Pooling Layer, 최종적인 분류를 위한 일반적인 MLP 구조의 Fully Connected Layer로 구성
    • Receptive Field의 크기, Stride, Pooling의 종류, Layer를 쌓는 횟수 등은 하이퍼파라미터

Convolution Layer

  • 이미지 뒤에서 stride 값 만큼 filter(kernel)을 이동시키면서 겹쳐지는 부분의 각 원소의 값을 곱해서 모두 더한 값으로 출력하는 연산
  • Receptive Field를 정의해 입력층의 이미지의 feature 추출
    • 이미지가 input으로 들어왔을 때, 사각형 모양의 Receptive Field가 이미지를 스캔하며 Region Feature 추출
    • 이미지 픽셀 값과 Receptive Field의 weight의 선형 결합으로 1개의 값이 나옴
      • 여기서의 weight는 신경망 모형을 이용해 학습
  • 여기서 추출한 feature를 Feature Map이라고 부름
stride

Receptive Field가 이미지를 돌면서 feature를 뽑을 때 이동하는 칸 수

Padding
  • 일반적인 Convolution을 적용하면 다음 feature의 size가 감소
  • 가장 자리에 있는 픽셀 값은 안쪽에 있는 픽셀 값보다 적게 convolution 적용
  • 이 현상들을 방지하기 위해 이미지의 테두리에 0값을 넣어 이미지의 사이즈 유지
  1. Full Padding: 모든 요소들이 같은 비율로 convolution 연산에 참여하도록 padding
  2. Same Padding: 합성곱 연산의 output 크기를 input의 크기와 동일하게 padding
  3. Valid Padding: 패딩을 하지 않는 것
Weight Sharing
  • Receptive Field를 움직여가며 다음 Feature Map을 추출할 때 다른 weight로 feature를 추출하려면 매우 많은 파라미터 학습
  • 그렇기 때문에 Receptive Field를 옮길 때마다 같은 weight를 사용
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, dilation, groups, bias)
  1. in_channels: input의 filter 채널 수
  2. out_channels: filter의 개수
  3. kernel_size: kernel(filter)의 size
  • 3*1 생성 시 (3,1) 입력
  • 3입력 시 자동으로 (3*\3)
  1. stride
  2. padding
  • input은 (batch_size, channel, height, width) 형태
  • convolution의 output 크기(소수점은 버림)
    output_size=input_sizefilter_size+(2padding)stride+1output\_size = \cfrac{input\_size - filter\_size + (2*padding)}{stride} + 1
conv = torch.nn.Conv2d(1,1,11,stride=4, padding=0)
inputs = torch.Tensor(1,1,227,227)
out = conv(inputs)
print(f'Input Size: {inputs.shape}, Output Size: {out.shape}')

Pooling Layer

  • feature size를 줄여주는 것
  • 학습 속도 향상을 위해 feature의 dimension 줄이는 개념
    • dimension이 감소하므로 정보손실 발생
    • 학습 속도 높일 수 있는 알고리즘이 많이 개발돼 상황에 따라 Pooling Layer 안 씀
  • 사각형 안의 픽셀 중 최대값을 추출하면 Max Pooling, 평균을 계산하여 추출하면 Average Pooling, Channel 수를 줄여서 차원의 수를 줄이면 Cross Channel Pooling
torch.nn.MaxPool2d(kernel_size, stride, padding, dilation, return_indices, ceil_mode)

Fully-Connected Layer

  • Pooling Layer에서 나온 Feature를 flatten하여 MLP의 input으로 두고 학습 진행

Data Augmentation

  • Graphical Feature을 학습시키는 것은 복잡한 모델이 대부분
  • 복잡한 모델을 만들기 위해서는 다량의 데이터 필요
  • 하지만 보유하고 있는 데이터는 한정적이기 때문에 이를 보완하기 위해 데이터 변형
  1. Random Flip: 이미지를 랜덤으로 좌우/상하 반전
  2. Rotation: 이미지 회전
  3. Crop: 이미지의 일정 부분을 잘라 사용
  4. Scaling: 이미지 확대 또는 축소
  5. Cutout: 이미지의 일부를 사각형 모양으로 검은색을 칠하는 방법(0를 채움 - dropout효과)
  6. Cutmix: 여러 이미지를 합쳐 놓고 이미지의 label을 각각의 이미지가 차지하는 비율로 설정
  7. ColorShifting: 색 변환
  8. Mixup: 두 데이터를 적절하게 섞은 후 모델의 학습에 활용
  • label이 부정확한 상황에서도 좋은 성능을 얻을 수 있음

CIFAR-10 예제 (파이썬 딥러닝 파이토치)

import numpy as np
import matplotlib.pyplot as plt
import torch
from torchvision import transforms, datasets

if torch.cuda.is_available():
    device = torch.device('cuda')
else:
    device = torch.device('cpu')
    
batch_size = 32
epochs = 10

# transforms 함수 이용 시 augmentation 쉽게 적용
train_dataset = datasets.CIFAR10(root='data/CIFAR_10', 
                                 train=True, download=True,
                                 transform = transforms.Compose([ # torch.Compose() 는 불러오는 이미지 데이터에 전처리 및 Augmentation을 다양하게 적용할 때 이용 
                                     transforms.RandomHorizontalFlip(), # 해당 이미지를 50% 확률로 좌우 반전
                                     transforms.ToTensor(), # 데이터를 tensor 형태로 변환 및 0~1로 정규화
                                     transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)) # red, green, blue 순으로 평균, 표준편차를 0.5씩 적용
                                 ]))
test_dataset = datasets.CIFAR10(root='data/CIFAR_10',
                                train=False, download=True,
                                transform = transforms.Compose([
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))
                                 ]))

train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                          batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                         batch_size=batch_size, shuffle=False)
                                         
class CNN(torch.nn.Module): # torch.nn.Module: PyTorch Module 내에 딥러닝 모델 관련 함수 포함하는 클래스 상속(그대로 사용)
    def __init__(self): # 인스턴스 생성 시 지니는 성질 정의
        super(CNN, self).__init__() # torch.nn.Module 내에 있는 메소드 상속받아 이용
        self.conv1 = nn.Conv2d( # Convolution 연산 수행하는 filter 정의
            in_channels = 3, # 채널 수를 이미지의 채널 수와 동일하게 맞춰야 함(red, green, blue)
            out_channels = 8, # filter 개수 설정
            kernel_size = 3, # filter size 설정
            padding = 1 # padding size 결정
            )
        self.conv2 = nn.Conv2d(
            in_channels = 8, # 이전 feature map의 채널 수와 동일하게 채널 수 맞춤
            out_channels = 16,
            kernel_size = 3, padding = 1
            )
        self.pool = nn.MaxPool2d( # Max Pooling 사용
            kernel_size = 2, # pooling filter size 결정
            stride = 2
            )
        self.fc1 = nn.Linear(8*8*16, 64) # 8*8의 2차원 데이터 16개가 겹쳐 있는 형태로 존재하는 데이터를 input으로
        self.fc2 = nn.Linear(64, 32)
        self.fc3 = nn.Linear(32, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = torch.nn.functional.relu(x)
        x = self.pool(x)
        
        x = self.conv2(x)
        x = torch.nn.functional.relu(x)
        x = self.pool(x)
        
        x = x.view(-1, 8*8*16) # fully-connected layer에 input으로 넣기 위해 shape 변환
        x = self.fc1(x)
        x = torch.nn.functional.relu(x)
        x = self.fc2(x)
        x = torch.nn.functional.relu(x)
        x = self.fc3(x)
        return x
        
model = CNN().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = torch.nn.CrossEntrophyLoss() # softmax 내포

def train(model, train_loader, optimizer, log_interval):
    model.train()
    for batch_idx, (image, label) in  enumerate(train_loader):
        image = image.to(device)
        label = label.to(device)
        
        optimizer.zero_grad()
        output = model(image)
        loss = criterion(output, label)
        loss.backward()
        optimizer.step()
        
        if batch_idx % log_interval == 0:
            print(f'Train Epoch: {epoch}\tTrain Loss: {loss.item()}')
            
def evaluate(model, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    
    with torch.no_grad():
        for image, label in test_loader:
            image = image.to(device)
            label = label.to(device)
            output = model(image)
            test_loss += criterion(output, label).item()
            prediction = output.max(1, keepdim=True)[1]
            correct += prediction.eq(label.view_as(prediction)).sum().item()
            
    test_loss /= len(test_loader.dataset)
    test_accuracy = 100 * correct/len(test_loader.dataset)
    return test_loss, test_accuracy
    
for epoch in range(1, epochs+1):
    train(model, train_loader, optimizer, log_interval=200)
    test_loss, test_accuracy = evaluate(model, test_loader)
    print(f'\nEpoch: {epoch},\t Test Loss: {test_loss},\t Test Accuracy: {test_accuracy}')

MNIST 예제 (모두를 위한 딥러닝)

# 라이브러리 가져오기
import torch
import torch.nn as nn
from torchvision import datasets, transforms

# GPU 설정 및 random seed 고정
device = 'cuda' if torch.cuda.is_available() else 'cpu'

torch.manual_seed(777)
if device == 'cuda':
    torch.cuda.manual_seed_all(777)
    
# parameters
learning_rate = 0.001
traning_epochs = 15
batch_size = 100

# MNIST dataset
mnist_train = datasets.MNIST(root='MNIST_data/',
                             train=True, download=True,
                             transform=transforms.ToTensor())
# root: 데이터 저장 위치, train data 여부, transform=변환
mnist_test = datasets.MNIST(root='MNIST_data/',
                            train=False, download=True,
                            transform=transforms.ToTensor())
data_loader = torch.utils.DataLoader(DataLoader=mnist_train, batch_size=batch_size,
                                    shuffle=True, drop_last=True) # 불러오기
                                    
# Modeling
class CNN(nn.module):
    def __init__(self):
        super(CNN, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(1, 32, 3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.layer2 = nn.Sequential(
            nn.Conv2d(32, 64, 3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.fc = nn.Linear(7*7*64, 10, bias=True)
        torch.nn.init.xavier_uniform_(self.fc.weight)
    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(x)
        
        out = out.view(out.size(0), -1) # batch_size만 고정하고 나머지는 flatten
        out = self.fc(x)
        return out
        
model = CNN.to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate)

# Training
total_batch = len(data_loader)

for epoch in range(training_epochs):
    avg_cost = 0
    
    for X,Y in data_loader:
        X = X.to(device)
        Y = Y.to(device)
        
        optimizer.zero_grad()
        hypothesis = model(X)
        
        cost = criterion(hypothesis, Y)
        cost.backward()
        optimizer.step()
        
        avg_cost += cost/total_batch
        
        print(f'Epoch: {epoch+1}, Cost = {avg_cost}')
        
# Evaluate
with torch.no_grad():
    X_test = mnist_test.test_data.view(len(mnist_test), 1, 28, 28).float().to(device)
    # 한번에 집어넣기 위해 펼침(flatten)
    Y_test = mnist_test.test_labels.to(device)
    
    prediction = model(X_test)
    correct_prediction = torch.argmax(prediction, 1) == Y_test
    accuracy = correct_prediction.float().mean()
    print(f'Accuracy: {accuracy.item()}')

Visdom

  1. visdom 설치
!pip install visdom
  1. visdom 서버 켜기
python -m visdom.server
  1. import visdom
import visdom
vis = visdom.Visdom()
  1. visdom 서버에 띄우기
# text
vis.text('Hello, world!', env='main')
# image
vis.image(torch.randn(3,200,200), env='main')
# images
vis.images(torch.Tensor(3,3,28,28), env='main')
# Line Plot
X_data = torch.randn(5)
Y_data = torch.randn(5)
plt = vis.line(Y=Y_data, X=X_data,
              opts = dict(title='Test', showlegend=True, legend=['1번']))
# Update Line Plot
Y_append = torch.randn(1)
X_append = torch.Tensor([6])
vis.line(Y=Y_append, X=X_append, win=plt, update='append')
  1. close the window
vis.close(env='main')

참고
파이썬 딥러닝 파이토치 (이경택, 방성수, 안상준)
모두를 위한 딥러닝 시즌 2 Lab 10-1, 10-2, 10-3, 10-4-1

profile
데이터사이언스를 공부하는 권유진입니다.

0개의 댓글