케라스 창시자에게 배우는 딥러닝_9장

코넬·2023년 2월 2일
0

DeepLearning_Keras

목록 보기
9/13
post-thumbnail

컴퓨터 비전을 위한 고급 딥러닝

⚡️ 세가지 주요 컴퓨터 비전 작업

이미지 분류 : 이미지에 하나 이상의 레이블을 할당 하는 것이 목표 !
단일 레이블 분류이거나 다중 레이블 분류일 수 있다.
이미지 분할 : 이미지를 다른 영역으로 나누거나 분할하는 것이 목표 !
각 영역은 일반적으로 하나의 범주를 나타낸다. 이미지 분할 모델을 사용하여 픽셀 수준에서 배경과 목표물을 분리한다.
객체 탐지 : 이미지에 있는 관심 객체 주변에 바운딩 박스(bounding box)라는 사각형을 그리는 것이 목표 ! 각 사각형은 하나의 클래스와 연관된다.

이 외에도 이미지 유사도 평가, 키포인트 감지, 포즈 추정, 3D 메시 추정 등이 있다.

💫 이미지 분할 예제

이미지 분할에는 두가지 종류가 있다.

  • 시맨틱 분할 : 각 픽셀이 독립적으로 고양이와 같은 하나의 의미를 가진 범주로 분류된다. 이미지에 2개의 고양이가 있다면 이에 해당되는 모든 픽셀은 동일한 고양이 범주로 매핑된다.
  • 인스턴스 분할 : 이미지 픽셀을 범주로 분류하는 것뿐만 아니라 개별 객체의 인스턴스를 구분한다. 이미지에 2개의 고양이가 있다면 인스턴스 분할은 고양이1,2 별개의 클래스로 구분한다.

시맨틱 분할의 코드 예제를 살펴보자.

import os

input_dir = "images/"
target_dir = "annotations/trimaps/"

input_img_paths = sorted(
    [os.path.join(input_dir, fname)
     for fname in os.listdir(input_dir)
     if fname.endswith(".jpg")])
target_paths = sorted(
    [os.path.join(target_dir, fname)
     for fname in os.listdir(target_dir)
     if fname.endswith(".png") and not fname.startswith(".")])

입력 파일 경로와 분할 마스크 파일 경로를 각각 리스트로 구성한다.
여기서 해당하는 타깃(분할 마스크)는 다음과 같다.

def display_target(target_array):
    normalized_array = (target_array.astype("uint8") - 1) * 127
    plt.axis("off")
    plt.imshow(normalized_array[:, :, 0])

img = img_to_array(load_img(target_paths[9], color_mode="grayscale"))
display_target(img)



그다음, 입력과 타깃을 2개의 넘파이 배열로 로드하고 이 배열을 훈련과 검증 세트로 나눈다.(데이터 전처리 단계)

import numpy as np
import random

img_size = (200, 200) #입력과 타깃 모두 200*200크기로 변경
num_imgs = len(input_img_paths) #데이터에 있는 전제 샘플의 개수

random.Random(1337).shuffle(input_img_paths)
random.Random(1337).shuffle(target_paths)

def path_to_input_image(path):
    return img_to_array(load_img(path, target_size=img_size))

def path_to_target(path):
    img = img_to_array(
        load_img(path, target_size=img_size, color_mode="grayscale"))
    img = img.astype("uint8") - 1 #레이블이 0,1,2가 되도록 1을 뺀다.
    return img

input_imgs = np.zeros((num_imgs,) + img_size + (3,), dtype="float32")
targets = np.zeros((num_imgs,) + img_size + (1,), dtype="uint8")
for i in range(num_imgs):
    input_imgs[i] = path_to_input_image(input_img_paths[i])
    targets[i] = path_to_target(target_paths[i])

num_val_samples = 1000
train_input_imgs = input_imgs[:-num_val_samples] #데이터를 훈련과 검증 세트로 나눈다.
train_targets = targets[:-num_val_samples]
val_input_imgs = input_imgs[-num_val_samples:]
val_targets = targets[-num_val_samples:]

다음으로 훈련을 위한 모델을 정의해보자.

from tensorflow import keras
from tensorflow.keras import layers

def get_model(img_size, num_classes):
    inputs = keras.Input(shape=img_size + (3,))
    x = layers.Rescaling(1./255)(inputs)

    x = layers.Conv2D(64, 3, strides=2, activation="relu", padding="same")(x)
    x = layers.Conv2D(64, 3, activation="relu", padding="same")(x)
    x = layers.Conv2D(128, 3, strides=2, activation="relu", padding="same")(x)
    x = layers.Conv2D(128, 3, activation="relu", padding="same")(x)
    x = layers.Conv2D(256, 3, strides=2, padding="same", activation="relu")(x)
    x = layers.Conv2D(256, 3, activation="relu", padding="same")(x)

    x = layers.Conv2DTranspose(256, 3, activation="relu", padding="same")(x)
    x = layers.Conv2DTranspose(256, 3, activation="relu", padding="same", strides=2)(x)
    x = layers.Conv2DTranspose(128, 3, activation="relu", padding="same")(x)
    x = layers.Conv2DTranspose(128, 3, activation="relu", padding="same", strides=2)(x)
    x = layers.Conv2DTranspose(64, 3, activation="relu", padding="same")(x)
    x = layers.Conv2DTranspose(64, 3, activation="relu", padding="same", strides=2)(x)

    outputs = layers.Conv2D(num_classes, 3, activation="softmax", padding="same")(x)
    #각 출력 픽셀을 3개의 범주 중 하나로 분류하기 위해 3개의 유닛과 소프트맥스 활성화 함수를 가진 밀집층으로 모델 종료하기

    model = keras.Model(inputs, outputs)
    return model

model = get_model(img_size=img_size, num_classes=3)

현재 구현한 모델과 이전 분류 모델간의 큰 차이점은 다운샘플링 방식이다. 기존 분류 모델의 경우 Maxpooling을 사용하여 특성 맵을 다운샘플링 하였으나, 현 모델은 합성곱 층마다 stride를 추가하여 다운샘플링을 한다. 이미지 분할의 경우 모델의 출력으로 픽셀별 타깃 마스크를 생성해야하므로 정보의 공간상 위치에 많은 관심을 두기 때문에 이러한 방식을 적용한다. 스트라이드 합성곱 은 위치 정보를 유지하면서 특성 맵을 다운샘플링하는 작업에 더 잘 맞는다.
다음 문제로 ! 나머지 모델 절반에 Conv2DTranspose 층을 쌓았는데, 모델의 처음 절반은 (25,25,256)크기의 특성 맵을 출력하지만, 최종 출력은 타깃 마스크의 크기인 (200,200,3)과 동일해야하므로 적용한 변환을 거꾸로, 즉 업샘플링(upsampling) 과정을 거쳐야한다. 이 층이 업샘플링 역할을 하는 것이다.

✨ 최신 컨브넷 아키텍처 패턴

모델의 아키텍처 는 모델을 만드는데 사용된 일련의 선택이다. 사용할 층, 층의 설정, 층을 연결하는 방법 등 가설공간 을 정의한다. 특성 공학과 마찬가지로 좋은 가설 공간은 현재 문제와 솔루션에 대한 사전 지식 을 인코딩한다. 이와 같이 데이터에서 효율적으로 학습하기 위해서는 찾고 있는 것에 대한 가정을 해야한다.
지금부터 핵심적인 컨브넷 아키텍처의 모범 사례를 살펴보자.

  • 잔차 연결
  • 배치 정규화
  • 분리 합성곱

모듈화, 계층과 그리고 재사용에 대해 먼저 알아보자.

형태를 알아보기 힘든 복잡한 구조를 모듈화 하고, 모듈을 계층화 하며, 같은 모듈을 적절하게 여러곳에서 재사용 한다. 모든 딥러닝 모델 아키텍처는 모듈화, 계층화, 재사용을 영리하게 활용한다. 모든 컨브넷 아키텍처는 층으로만 구성되어있지 않고 반복되는 층 그룹(블록(block) 또는 모듈(module) ) 로 구성되어있다.
또한 컨브넷은 피라미드와 같은 구조를 가지는 경우가 많다. 계층 구조가 깊으면 특성 재사용과 이로 인한 추상화를 장려하기 떄문에 본질적으로 좋다. 하지만 그라디언트 소실(gradient vanishing) 문제 때문에 층을 쌓을 수 있는 정도에 한계가 있다. 이러한 문제 덕분에 잔차 연결 이 탄생하게 되었다.

1️⃣ 잔차 연결

그레이디언트 소실 문제를 해결하기 위해 탄생, 연결된 각 함수를 비파괴적으로 만드는 방법인데, 이전 입력에 담긴 잡음 없는 정보를 유지시키는 방법이라 할 수 있다.
잔차 연결 의 방법은 간단하다. 층이나 블록의 입력을 출력에 더하기만 하면 된다. 즉, 파괴적이거나 잡음이 있는 블록을 돌아가는 정보의 지름길이라고 할 수 있다.

2️⃣ 배치 정규화

3️⃣ 깊이별 분리 합성곱

  • 모델은 반복되는 층 블록으로 구성된

컨브네싱 학습한 것 해석하기

출처 : 케라스 창시자에게 배우는 딥러닝(개정판)

profile
어서오세요.

0개의 댓글