본문 바로가기

Study/딥러닝 텐서플로 교과서 - 길벗

[Book]6. 합성곱 신경망 2

개요

  • 책을 보고 공부한 내용을 정리합니다.

딥러닝 텐서플로 교과서

  • 저자 서지영님 
  • 길벗 출판사

코드 출처

https://github.com/gilbutITbook/080263

 

GitHub - gilbutITbook/080263

Contribute to gilbutITbook/080263 development by creating an account on GitHub.

github.com

 


6. 합성곱 신경망 2

 

6.1.1 LeNet-5

  • 합성곱과 다운 샘플링을 반복적으로 거치면서 마지막에 완전 연결층에서 분류 수행
  1. C1에서 5 *5 합성곱 연산 후 28 * 28 크기의 특성 맵 여섯 개 생성
  2. S2에서 다운 샘플링하여 특성 맵 크기를 14 *14로 줄임
  3. C3에서 5 * 5 합성곱 연산하여 10 * 10 크기의 특성 맵 16개 새성
  4. S4다운 샘플링 하여 특성 맵 크기를 5 * 5로 줄임
  5. C5에서 5 * 5 합성곱 연산하여 1 *1 크기의 특성 맵 120개 생성
  6. 마지막으로 F6에서 완전 연결층으로 C5의 결과를 84개 노드에 연결
  7. C로 시작하는 것은 합성곱층을 의미, S로 시작하는 것은 풀링층을 의미

num_classes = 2
class LeNet(Sequential):
  def __init__(self, input_shape, nb_classes):
    super().__init__()

    # 6 : 필터 개수
    # kernel_size : 커널의 행과 열
    # strides : 필터 적용하는 간격
    self.add(Conv2D(6, kernel_size = (5, 5), strides = (1, 1), activation = 'relu',
                    input_shape = input_shape, padding ='same'))
    
    # pool_size : 연산범위, 필터크기
    self.add(AveragePooling2D(pool_size= (2, 2), strides = (2, 2), padding = 'valid'))
    self.add(Conv2D(16, kernel_size = (5, 5), strides = (1, 1), activation = 'relu'))
    self.add(AveragePooling2D(pool_size = (2, 2), strides =(2, 2), padding = 'valid'))
    self.add(Flatten())
    self.add(Dense(120, activation = 'relu'))
    self.add(Dense(84, activation = 'relu'))
    self.add(Dense(nb_classes, activation = 'softmax'))

    self.compile(optimizer = 'adam',
                 loss = 'categorical_crossentropy',
                 metrics = ['acc'])

 


 

6.1.2 AlexNet

  • 이미지 크기를 나타내는 너비와 높이뿐만 아니라 깊이를 갖는다
  • 합성 곱층 5개와 완전 연결층 3 개로 구성
  • 맨 마지막 완전연결층은 카테고리 1000개로 분류하기 위해 소프트 맥스 사용

AlexNet

num_classes = 2
class AlexNet(Sequential):
    def __init__(self, input_shape, num_classes):
        super().__init__()

        # kernel_initializer : 가중치 초기화 방법 
        self.add(Conv2D(96, kernel_size=(11,11), strides= 4,
                        padding='valid', activation='relu',
                        input_shape=input_shape,
                        kernel_initializer='he_normal'))
        
        # data_format : 입력에 대한 형식 지정
        # 1. channels_last : 입력 데이터 텐서의 형식이 (배치크기, 높이, 너비, 채널)
        # 2. channels_first : (배치크기, 채널개수 , 높이 , 너비) 

        self.add(MaxPooling2D(pool_size=(3,3), strides=(2,2),
                              padding='valid', data_format='channels_last'))

        self.add(Conv2D(256, kernel_size=(5,5), strides=1,
                        padding='same', activation='relu',
                        kernel_initializer='he_normal'))
        self.add(MaxPooling2D(pool_size=(3,3), strides=(2,2),
                              padding='valid', data_format='channels_last'))

        self.add(Conv2D(384, kernel_size=(3,3), strides=1,
                        padding='same', activation='relu',
                        kernel_initializer='he_normal'))

        self.add(Conv2D(384, kernel_size=(3,3), strides=1,
                        padding='same', activation='relu',
                        kernel_initializer='he_normal'))

        self.add(Conv2D(256, kernel_size=(3,3), strides=1,
                        padding='same', activation='relu',
                        kernel_initializer='he_normal'))

        self.add(MaxPooling2D(pool_size=(3,3), strides=(2,2),
                              padding='valid', data_format='channels_last'))

        self.add(Flatten())
        self.add(Dense(4096, activation='relu'))
        self.add(Dense(4096, activation='relu'))
        self.add(Dense(1000, activation='relu'))
        self.add(Dense(num_classes, activation='softmax'))

        self.compile(optimizer=tf.keras.optimizers.Adam(0.001),
                     loss='categorical_crossentropy',
                     metrics=['accuracy'])


6.1.3 VGGNet

  • 합성곱층의 파라미터 개수를 줄이고 훈련 시간을 개선하려고 탄생
  • VGG16에는 파라미터가 총 1억 3300만 개가 있음
  • 모든 합성곱 커널의 크기는 3*3, 최대 풀링 커널의 크기는 2*2 임
class VGG19(Sequential):
    def __init__(self, input_shape):
        super().__init__()

        self.add(Conv2D(64, kernel_size=(3,3), padding='same',
                        activation='relu', input_shape=input_shape))
        self.add(Conv2D(64, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))

        self.add(Conv2D(128, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(128, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(MaxPooling2D(pool_size=(2,2), strides= (2,2)))

        self.add(Conv2D(256, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(256, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(256, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(256, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))

        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))

        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(Conv2D(512, kernel_size=(3,3), padding='same',
                        activation='relu'))
        self.add(MaxPooling2D(pool_size=(2,2), strides=(2,2)))

        self.add(Flatten())
        self.add(Dense(4096, activation='relu'))
        self.add(Dropout(0.5))
        self.add(Dense(4096, activation='relu'))
        self.add(Dropout(0.5))
        self.add(Dense(1000, activation='softmax'))

        self.compile(optimizer=tf.keras.optimizers.Adam(0.003),
                     loss='categorical_crossentropy',
                     metrics=['accuracy'])


6.1.4 GoogLeNet

  • 인셉션 모듈 추가
  • 인셉션 모듈에서는 특징을 효율적으로 추출하기 위해 1 * 1, 3 *3, 5 *5의 합성곱 연산 수행
  • GoogLeNet에 적용된 해결 방법은 희소 연결(sparse connectivity)
희소 연결
CNN은 합성곱, 풀링, 완전연결층들이 서로 밀집하게 연결되어 있음 뺵뺵하게 연결된 신경망 대신 관련성이 높은 노드끼리만 연결하는 방법을 말함 과적합도 해결

6.1.5 ResNet

  • 깊어진 신경망을 효과적으로 학습하기 위한 방법으로 레지듀얼 개념을 고안한 것
  • 특정 논문에 따르면, 신경망은 깊이가 깊어질수록 성능이 좋아지다가 일정한 단계에 다다르면 오히려 성능이 나빠진다고 함
  • 이러한 문제를 해결하기 위해 ResNet은 레지듀얼 블록(residual block)을 도입
  • residual block는 기울기가 잘 전파될 수 있도록 일종의 숏컷(shortcut, skip connection)를 만듦
  • ResNet은 기본적으로 VGG19 구조를 뼈대로 함

BatchNormalization : e데이터의 평균을 0, 표준편차를 1로 분포

6.2 객체 인식을 위한 신경망

 

  • 객체 인식(object detection)이란 이미지나 영상 내에 있는 여러 객체에 대해 각 객체가 무엇인지를 분류하는 문제와 그 객체의 위치가 어디인지 박스(bounding box)로 나타내는 위치 검출(localization) 문제를 다루는 것
  • object detection = classification + localization
  • 객체 인식 알고리즘은 크게 1 단계 객체인식(1-stage detector) , 2단계 객체 인식(2-stage detector)으로 나눔

 

1-stage detector
분류와 위치 검출을 동시에  행하는 방법

2-stage detector
분류와 위치 검출을 순차적으로 행하는 방법

1-stage detector이 비교적 빠르지만 정확도가 낮고,
2-stage detector 은 비교적 느리지만 정확도가 높음

6.2.1 R-CNN

 

  • 2단계 객체 인식
  • 예쩐의 객체 인식 알고리즘들은 슬라이딩 윈도(sliding window) 방식, 즉 일정한 크기를 가지는 윈도를 가지고 이미지의 모든 영역을 탐색하면서 객체를 검출함
  • 하지만 비효율성 떄문에 많이 사용하지 않았으며, 현재는 선택적 탐색(selective search) 알고리즘을 적용한 후보 영역(region proposal)을 많이 이용

절차

  1. 이미지를 입력 받음
  2. 2000개으 Bounding Box를 선택적 탐색 알고리즘으로 추출한 후 잘라 내고 (cropping),  CNN모델에 넣기 위해 같은 크기로 통일함(Wrapping)
  3. 크기가 동일한 이미지 2000개에 각각 CNN 모델 적용
💡 선택적 탐색 
가능한 후보 영역을 알아내는 방법
분할 방식을 이용하여 시드를 서정하고 그 시드에 대한 완전 탐색을 적용

아래와 같은 세 단계 과정을 거침
1. 각각의 객체가 영역 한 개에 할당될 수 있도록 많은 초기 영역 생성
- 즉 입력된 이미지를 영역 다수 개로 분할

2. 작은 영역의 통합
- 1단계에서 영역 여러 개로 나눈 것들을 비슷한 영역으로 통합, 이때 탐욕(greedy) 알고리즘을 사용하여 비슷한 영역이 하나로 통합될 때까지 반복

3. 후보 영역 생성
- 통합된 영역을 기반으로 후보 영역(bounding box) 추출

RNN 단점

  1. 복잡한 학습과정
  2. 긴 학습 시간과 대용량 저장공간
  3. 객체 검출 속도 문제

6.2.2 공간 피라미드 풀링

  • 기존 CNN은 완전 연결층을 위해 입력 이미지를 고정해야 했음
  • 그래서 이미지를 고정된 크기로 자르거나 (CROP),  비율을 조정(Wrop) 해야 했음
  • 이렇게 하자 본래의 생김새와 달라지는 문제점 발생
  • 이러한 문제를 해결하고자 SPP(spatial pyramid pooling, 공간 피라미드 풀링) 도입

💡 SPP-block에서는 피쳐맵을 입력으로 받음
- 그리고 이를 미리 정해져 있는 영역(피라미드)로 나누어 준다.
- 피라미드의 한 칸을 bin이라하고 각 bin에서 큰 값만 추출하는 max pooling을 수행한다.
- 그 후 그 결과를 쭉 이어 붙여주는 것이다. 이 이어 붙인 형태는 fully-connected 형태이다.
- 따라서 입력 이미지의 크기와는 상관없이 미리 설정한 피라미드로 인해 출력이 결정되므로 CNN 고질문제를 해결하였다. 


6.2.3 Fast R-CNN

 

  • R-CNN의 속도 문제를 개선하려고 ROI 풀링 도입
  • 선택적 탐색에서 찾은 바운딩 박스 정보가 CNN을 통과하면서 유지되도록 하고 최종 CNN 특성 맵은 풀링을 적용하여 완전 연결층을 통과하도록 크기 조정
  • ROI 풀링 : 크기가 다른 특성 맵의 영역마다 스트라이드를 다르게 최대 풀링을 적용하여 결괏값 크기를 동일하게 맞추는 방법

6.3 이미지 분할을 위한 신경망

  • 이미지 분할(image segmentation)은 신경망을 훈련시켜 이미지를 픽셀 단위로 분할하는 것
  • 즉, 이미지를 픽셀 단위로 분할하여 이미지에 포함된 객체 추출
  • 대표적으로 완전 합성곱 네트워크, 합성곱 & 역합성곱 네트워크 , U-Net, PSPNet, DeepLabv3/DeepLabv3+

 


6.3.1 완전 합성곱 네트워크(Fully Convolution Network, FCN)

  • 완전연결층의 한계는 고정된 크기의 입력만 받아들임
  • 이러한 문제를 해결하기 위해 완전 연결층을 1*1 합성곱으로 대체하는 것이 완전 합성곱 네트워크
단점
여러 단계의 합성곱층과 풀링층을 거치면서 해상도 낮아짐 낮아진 해상도를 복원하기 위해 업 샘플링 방식을 사용하기 때문에 이미지의 세부 정보들을 잃어버리는 경우 발생

6.3.2 합성곱 & 역합성곱 네트워크

 

  • FCN의 단점을 해결하고자 탄생
  • 역 합성곱은 CNN의 최종 출력 결과를 원래의 입력 이미지와 같은 크기를 만들고 싶을 때 사용
  • 역 합성곱은 업 샘플링(upsampling)등으로도 부름

역 합성곱 방식

  1. 픽셀 주위에 제로 패딩 추가
  2. 헤딩된 것에 합성곱 연산

6.3.3 U-Net

  • 바이오 메디컬 이미지 분할을 위한 합성곱 신경망

 


특징

  1. 속도가 빠름 : 검증이 끝난 패치는 건너뜀
  2. 트레이드오프(trad-off)에 빠지지 않음

구조

1. FCN 기반, 수축 경로(contracting path)와 확장 경로(expansive path)로 구성
- 수축 경로는 콘텍스트 포착, 확장 경로는 특성 맵을 업 샘플링하고 수축 경로에서 포착한 특성 맵의 콘텍스트와 결합하여 정확한 지역화 수행

2. 3 *3 합성곱이 주를 이루는데 각 합성곱 블록은 3*3 합성곱 두개로 구성되며 그 사이에 드롭아웃이 있음. 그리고 각 블록은 맥스 풀링 하며 크기 줄이고 다음 블록으로 넘어감

3. 확장 경로에서 up-conv라는 것을 붙임
- 수축 과정에서 줄어든 크기를 다시 키워가면서 합성곱 블록을 이용


6.3.4 PSPNeT (pyramid Scene Parsing Network)

  • 완전 연결층의한계를 극복하기 위해 피라미드 풀링 모듈 추가

과정
1. 이미지 출력이 서로 다른 크기가 되도록 여러 차례 풀링
2. 1*1 합성곱을 사용하여 채널 개수 조정
3. 모듈의 입력 크기에 맞게 특성 맵을 업 샘플링

  • 이 과정에서 양선형 보관법(bilinear interpolation) 이 사용
💡 양선형 보간법?
보간법
보간법이란 화소 값을 할당받지 못한 영상의 품질은 안 좋을 수 밖에 없음 이때 빈 화소에 값을 할당하여 좋은 품질의 영상을 만드는 방법 보간법에는 선형 보간법(linear interpolation), 양선형 보간법(bilinear interpolation)

선형 보간법
원시 영상의 화소 값 두 개를 사용하여 원하는 좌표에서 새로운 화소 값을 계산하는 방법

양선형 보간법
화소당 선형 보간을 세 번 수행, 새롭게 생성된 화소는 가장 가까운 화소 네 개에 가중치를 곱한 값을 합해서 얻음

'Study > 딥러닝 텐서플로 교과서 - 길벗' 카테고리의 다른 글

[Book]8. 성능 최적화  (0) 2022.01.06
[Book]7. 시계열 분석  (0) 2022.01.05
[Book]5. 합성곱 신경망 1  (0) 2022.01.02
[Book]4. 딥러닝 시작  (0) 2021.12.29
[Book]3. 머신러닝 핵심 알고리즘  (0) 2021.12.26