‘Deep Learning with Python’ 세미나 7, 5장. Deep learning for computer vision 1/2

Chater 5. Deep learning for computer vision

이번 장에서는 컴퓨터 비전에 딥러닝을 적용하려면 꼭 알아야 하는 합성곱 신경망을 다룹니다. 한 번의 세미나로 다루기에는 학습 분량이 많습니다. 5.2까지 첫 번째 세미나로, 5.3부터 두 번째 세미나로 나누어 진행하겠습니다.

저는 이번 장부터는 코랩을 사용해서 실습을 했습니다. 실행 환경은 GPU로 설정했습니다. 참고자료 [1]에서 코랩에 대한 좀 더 자세한 설명을 보실 수 있습니다.

5.1 Introduction to convnets

코드 5.1, 5.2, 5.3을 참조해서 MNIST 숫자 이미지 분류에 합성곱 신경망을 적용해 봅니다.

모델을 구성해 봅시다. 먼저 입력층에 해당하는 input_shape과 출력층을 작성합니다.

합성곱 신경망은 (image_height, image_width, image_channels) 크기의 텐서를 입력으로 사용합니다. MNIST 이미지 포맷이 (28, 28)이고, 채널은 1(흑백) 임으로 input_shape은 (28, 28, 1)로 설정됩니다. 출력층은 0~9에 해당하는 10개의 숫자로 분류하는 문제이기 때문에 units을 10으로, 활성화 함수로 softmax로 하는 완전연결층으로 작성합니다.

model.add(layers.Conv2D(32, (3, 3), activation=’relu’, input_shape=(28, 28, 1)))

model.add(layers.Dense(10, activation=’softmax’))

중간층들은 아직 배운 내용이 없으니 책 내용대로 작성합니다.

모델을 훈련시키고 평가해 봅니다. MNIST의 입력 텐서는 채널을 포함하고 있지 않기 때문에 채널을 포함하도록 shape을 재구성해야 합니다. reshape 메소드를 사용합니다. 로드된 이미지 데이터 값은 unit로 0~255 사이의 값을 갖습니다. 255로 나누어 0~1 사이의 값이 되도록 합니다. 255로 나누어 0~1 사이의 값을 갖도록 해야 하기 때문에 float 타입으로 설정합니다. 레이블을 원핫인코딩 합니다. to_categorical 함수를 사용합니다.

케라스에서는 model.summary 메소드를 사용해서 모델 구조를 출력합니다. 별다른 설명없이 model.summary의 결과만 보여주기 때문에 궁금증만 일어나고 답답합니다. Layer (type)은 그냥 보면 알 수 있지만, Output Shape과 Param #은 그냥 봐서는 알 수가 없습니다. Output Shape에 대해서는 뒤에 설명들이 이어지니까 넘어가고, Param #(개수)에 대해서 잠깐 설명하고 넘어가겠습니다.

parameter에는 가중치(weight)와 편향(bias)이 있습니다.

완전 연결 레이어에서 가중치는 이전 레이어의 units들과 해당 레이어의 units들 사이에 만들어지는 모든 연결들로, 가중치 개수는 이전 레이어의 units 개수에 해당 레이어의 units 개수를 곱해서 구할 수 있습니다. 편향은 해당 레이어의 units의 unit별로 하나씩 작성되기 때문에 편향의 개수는 units 개수가 됩니다. 해당 레이어의 전체 parameter 개수는 가중치 개수와 편향 개수를 합해서 구합니다.

layers.Dense(32, input_shape=(784,))

위와 같은 경우라면 레이어의 parameter 개수를 구해보면 다음과 같습니다.

  • 입력의 units이 784이기 때문에 w는 784 x 32가 됩니다.
  • 편향의 32가 됩니다.
  • parameter 개수는 784 x 32 + 32가 됩니다.

model.add(layers.Dense(32, input_shape=(784,)))
model.add(layers.Dense(10))

위와 같은 경우 두 번째 레이어의 parameter 개수를 구해보면 다음과 같습니다.

  • 이전 레이어의 units의 개수는 32, 해당 레이어의 units의 개수는 10이기 때문에, 가중치는 32 x 10이 됩니다.
  • 편향은 10이 됩니다.
  • parameter 개수는 32 x 10 + 10이 됩니다.

Conv2D 클래스의 첫 번재 인자는 필터의 개수이고, 두 번째 인자는 필터의 윈도우 크기입니다. 윈도우 크기는 높이와 너비로 작성됩니다. 높이를 행위 수로 보고, 너비를 열의 수로 보면 높이와 너비를 곱한 수 만큼 셀이 만들어 집니다. 합성곱에서는 하나의 셀을 타일이라고 합니다.

합성곱 레이어에서는 하나의 타일마다 가중치가 작성됩니다. 따라서 하나의 필터가 가지는 가중치 개수는 필터의 높이에 너비를 곱한 값이 됩니다. 합성곱 레이어에서는 필터를 여러 개 가질 수 있기 때문에 레이어가 가질 수 있는 가중치 개수는 필터의 가중치 개수에 레이어의 필터 개수를 곱한 값이 됩니다. 편향은 필터마다 하나씩 작성됩니다.

여기에서 주의할 점이 하나 있는데 합성곱 레이어에서 사용하는 텐서는 (height, width, channels)의 3차원 텐서라는 것입니다. 필터는 높이와 너비만 있는 것이 아니라 채널에 따라 깊이도 있다는 것입니다. 필터의 깊이는 입력 텐서의 채널 크기와 같아야 합니다. 따라서 필터가 갖는 타일의 개수는 ‘높이  x 너비 x 깊이(채널수)’가 됩니다.

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation=’relu’, input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation=’relu’))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation=’relu’))

위와 같은 모델에서 각 레이어의 Param #를 구해보면 다음과 같습니다.

  • 첫 번째 레이어는 필터 개수는 32개, 필터 윈도우 크기는 3, 3입니다. 입력 채널은 1입니다.
    • 필터 윈도우 크기가 3, 3이고 입력 채널이 1임으로, 필터 하나가 갖는 가중치 개수는 3 x 3 x 1입니다.
    • 필터 개수가 32개 임으로, 레이어가 갖는 가중치 개수는 32 x 3 x 3 x 1입니다.
    • 필터 개수가 32개 임으로, 편향의 개수는 32입니다.
    • parameter 개수는 (32 x 3 x 3 x 1) + 32가 됩니다.
  • MaxPooling2D 레이어는 parameter를 갖지 않습니다.
  • 세 번째 레이어의 필터 개수는 64개, 필터 윈도우 크기는 3 x 3입니다. 입력 채널은 이전 레이어의 Output Shape을 보면 32입니다.
    • 필터 하나가 갖는 가중치 개수는 3 x 3 x 32입니다.
    • 레이어가 갖는 가중치 개수는 64 x 3 x 3 x 32입니다.
    • 편향의 개수는 64입니다.
    • parameter의 개수는 (64 x 3 x 3 x 32) + 64입니다.
  • 마지막 레이어의 필터 개수는 64개, 필터 윈도우 크기는 3 x 3입니다. 입력 채널은 64입니다.
    • 필터 하나가 갖는 가중치 개수는 3 x 3 x 64입니다.
    • 레이어가 갖는 가중치 개수는 64 x 3 x 3 x 64입니다.
    • 편향의 개수는 64입니다.
    • parameter의 개수는 (64 x 3 x 3 x 64) + 64입니다.
The convolution operation

2장의 완전연결 네트워크는 97% 정도의 정확도를 얻은 반면, 합성곱 네트워크는 99% 정도의 정확도를 얻었습니다. 왜 합성곱이 더 잘 작동하는지 설명해 봅니다.

The fundamental difference between a densely connected layer and a convolution layer is this: Dense layers learn global patterns in their input feature space, whereas convolution layers learn local patterns: in the case of images, patterns found in small 2D windows
of the inputs.

완전연결층은 전역 패턴을 학습합니다. 합성곱층은 지역 패턴을 학습합니다.

This key characteristic gives convnets two interesting properties:

  • The patterns they learn are translation invariant. A densely connected network would have to learn the pattern anew if it appeared at a new location. This makes convnets data efficient when processing images (because the visual world is fundamentally translation invariant). They need fewer training samples to learn representations that have generalization power.
  • They can learn spatial hierarchies of patterns. A first convolution layer will learn small local patterns such as edges, a second convolution layer will learn larger patterns made of the features of the first layers, and so on. This allows convnets to efficiently learn increasingly complex and abstract visual concepts (because the visual world is fundamentally spatially hierarchical).

합성곱이 완전연결 보다 더 높은 정확도를 얻는 이유는 간단합니다. 합성곱이 완전연결 보다 ‘우리가 세상을 보는 방법’과 더 유사하게 동작하기 때문입니다.

고양이와 배경만 있는 사진 예를 가지고 합성곱의 첫 번째 성질에 대해서 설명해 봅니다. 고양이의 위치를 변경해 봅니다.

그림 5.2가 의미하는 바를 합성곱의 두 번째 성질과 연결해서 설명해 봅니다.

Convolutions operate over 3D tensors, called feature maps, with two spatial axes (height and width) as well as a depth axis (also called the channels axis). The convolution operation extracts patches from its input feature map and applies the same transformation to all of these patches, producing an output feature map.

합성곱 연산은 특성맵에 적용됩니다. 특성맵은 이름 그대로 특성들이 이 공간에 매핑되어 있다는 것입니다. 겉으로 보기엔 숨겨져 있는 특성들을 학습을 통해 추출해 내는 것입니다.

특성맵은 3차원 텐서로, 두 개의 공간축(높이와 너비)과 깊이축(채널)을 갖습니다. 합성곱 연산은 특성맵을 입력으로 받아 특성맵을 출력합니다. 입력으로 받는 특성맵을 입력 특성맵이라고 하고, 출력으로 내놓는 특성맵을 출력 특성맵이라고 합니다. 그림 5.4에서 첫 번째 그림이 입력 특성맵이고, 마지막 그림이 출력 특성맵입니다.

The convolution operation extracts patches from its input feature map and applies the same transformation to all of these patches, producing an output feature map.

합성곱은 전역 패턴이 아니라 지역 패턴을 학습하므로, 합성곱 연산 또한 지역 패턴을 학습하기 위해 입력 특징맵에서 일부분을 떼어내 학습해야 합니다. 그림 5.4의 두 번째 그림은 3 x 3 크기로 떼어낸 패치들을 일부를 보여주고 있습니다. 그림 5.4의 마지막 그림과 같이 출력 특성 맵을 작성하기 위해서는 패치가 9개 있어야 합니다.

그림 5.4의 세 번째 그림은 각각의 패치에 합성곱 연산을 해 준 결과입니다.

This output feature map is still a 3D tensor: it has a width and a height. Its depth can be arbitrary, because the output depth is a parameter of the layer, and the different channels in that depth axis no longer stand for specific colors as in RGB input.

첫 번째 층의 입력으로 사용되는 특성맵에서 채널은 이름 그대로 이미지 채널입니다. 하지만 이후의 특성맵에서는 이미지 채널을 의미하지 않습니다. 3차원에서 깊이를 나타내는 축이라고 생각하면 됩니다.

Convolutions are defined by two key parameters:

  • Size of the patches extracted from the inputs—These are typically 3 × 3 or 5 × 5. In the example, they were 3 × 3, which is a common choice.
  • Depth of the output feature map—The number of filters computed by the convolution. The example started with a depth of 32 and ended with a depth of 64.

합성곱 층은 두 개의 핵심 매개변수를 정의합니다. 하나는 패치의 크기이고, 다른 하나는 출력 특성맵의 깊이 입니다. 패치의 크기는 윈도우 크기라고도 합니다.

Each such 3D patch is then transformed (via a tensor product with the same learned weight matrix, called the convolution kernel) into a 1D vector of shape (output_depth,).

합성곱 연산이란 입력 특성맵을 특정 크기의 패치들로 분리해서 가중치 매트릭스에 텐서곱하는 것입니다. 가중치 매트릭스는 합성곱 커널이라고 부릅니다. 가중치 매트릭스는 여러 개 사용할 수 있기 때문에 합성곱 커널은 가중치 매트릭스 컬렉션이 됩니다.

입력 특성맵의 패치들은 가중치 매트릭스에 반응한 결과를 출력으로 내 놓습니다. 가중치 매트릭스가 특성을 추출해 내는 필터 역할을 하는 것입니다. 적절한 특성이 추출될 때까지 가중치 매트릭스의 가중치 값들을 변경하면서 학습합니다.

가중치 매트릭스를 필터라고 생각하면, 합성곱 커널은 필터 컬렉션이 됩니다. 이렇게 보면, 합성곱 연산은 입력 특성맵의 패치들에 필터를 텐서곱하는 것이라고 할 수 있습니다. 입력 특성맵은 3차원 텐서이기 때문에 패치 또한 3차원 텐서이고, 여기에 텐서곱할 필터도 같은 크기의 3차원 텐서여야 합니다. 예를 들어 패치의 크기가 (3, 3, 1)이라면 필터의 크기도 (3, 3, 1)이 되어야 합니다. 합성곱 커널은 필터 컬렉션이므로 필터의 개수를 몇 개로 하느냐에 따라 4차원의 크기를 갖습니다. 필터를 32개 사용하기로 했다면 위의 예에서 합성곱 커널은 (3, 3, 1, 32)가 됩니다.

입력 특성맵의 패치에 필터를 합성곱 연산해서 하나의 값을 구합니다. 전체 패치에 대해 합성곱 연산이 끝나면 (높이, 너비) 크기를 갖는 텐서를 구할 수 있습니다. 필터가 여러 개 있음으로 (높이, 너비) 크기를 갖는 텐서는 필터 개수 만큼 깊이가 쌓이게 되고, 이것은 출력 특성맵이 됩니다. 결국 합성곱층에 설정한 출력 깊이는 필터 개수를 설정한 것이라고 볼 수 있습니다.

그림 5.4를 보고 합성곱의 작동 방식을 설명해 봅니다.

다음 문장을 직관적으로 와 닿을 때까지 반복해서 읽습니다.

Filters encode specific aspects of the input data: at a high level, a single filter could encode the concept “presence of a face in the input,” for instance.

Understanding Border Effects and Padding

A convolution works by sliding these windows of size 3 × 3 or 5 × 5 over the 3D input feature map, stopping at every possible location, and extracting the 3D patch of surrounding features.

합성곱은 입력 특성맵 전체를 돌아다니면서 윈도우 크기의 패치를 구합니다.

If you want to get an output feature map with the same spatial dimensions as the input, you can use padding. In Conv2D layers, padding is configurable via the padding argument, which takes two values: “valid”, which means no padding (only valid window locations will be used); and “same”, which means “pad in such a way as to have an output with the same width and height as the input.” The padding argument defaults to “valid”.

입력 특성맵과 같은 공간 차원을 갖도록 출력 특성맵을 맞추고 싶을 때가 있습니다. 이런 경우 패딩을 사용해서 공간 차원을 크게할 필요가 있습니다. Conv2D 층은 padding 인자 값으로 패딩을 설정할 수 있습니다. padding 값이 ‘valid’로 설정되면 패딩을 하지 않겠다는 것이고, ‘same’으로 하면 출력 특성맵이 입력 특성맵과 같은 공간 차원이 되도록 패딩합니다. 기본 값은 ‘valid’입니다.

Understanding Convolution Strides 

But the distance between two successive windows is a parameter of the convolution, called its stride, which defaults to 1. It’s possible to have strided convolutions: convolutions with a stride higher than 1.

두 개의 연속된 윈도우들 사이의 거리를 스트라이드라고 합니다. 기본 값은 1입니다.

Using stride 2 means the width and height of the feature map are downsampled by a factor of 2. Strided convolutions are rarely used in practice, although they can come in handy for some types of models; it’s good to be familiar with the concept.

스트라이드를 2로 하면, 2배 만큼 패치수가 줄어듭니다. 샘플 수가 줄어든 겁니다. 다운 샘플링된 겁니다.

The max-pooling operation

That’s the role of max pooling: to aggressively downsample feature maps, much like strided convolutions.

스트라이드 대신 특성맵을 다운 샘플링하는 방법으로 최대 풀링 연산을 사용할 수 있습니다.

Max pooling consists of extracting windows from the input feature maps and outputting the max value of each channel. It’s conceptually similar to convolution, except that instead of transforming local patches via a learned linear transformation (the convolution kernel), they’re transformed via a hardcoded max tensor operation.

최대 풀링은 입력 특성 맵에서 윈도우 크기로 패치를 추출하고 각 채널별로 최대 값을 출력합니다. 개념적으로 합성곱과 비슷합니다. 합성곱에서는 합성곱 연산을 하지만 최대 풀링에서는 최대 값을 구하는 연산을 합니다.

A big difference from convolution is that max pooling is usually done with 2 × 2 windows and stride 2, in order to downsample the feature maps by a factor of 2. On the other hand, convolution is typically done with 3 × 3 windows and no stride (stride 1).

합성곱은 일반적으로 3 x 3 윈도우에 stride 1을 사용하지만, 최대 풀링은 일반적으로 2 x 2 윈도우에 stride 2를 사용합니다.

In short, the reason to use downsampling is to reduce the number of feature-map coefficients to process, as well as to induce spatial-filter hierarchies by making successive convolution layers look at increasingly large windows (in terms of the fraction of the original input they cover).

해결할 문제의 복잡성보다 가중치가 많을 경우 과적합이 일어날 수 있습니다. 다운 샘플링은 가중치 개수를 줄여주는 효과를 낼 수 있습니다. 샘플링의 개수가 줄어든다는 것은 대상을 좀 더 멀리서 본 것으로 비유할 수 있습니다. 층이 늘어날 때마다 조금 더 먼 곳에서 본 것으로 생각할 수 있다는 것입니다. 이렇게 함으로 공간 계층을 구성하는 효과를 낼 수 있습니다.

5.2 Training a convnet from scratch on a small dataset

5.2.1 The relevance of deep learning for small-data problems
5.2.2 Downloading the data

코랩에서 캐글과 연동해서 데이터를 받을 수 있습니다. 다음 링크를 참조해서 ‘강아지 vs 고양이’ 데이터셋을 다운로드 받습니다. ‘강아지 vs 고양이’ 데이터셋을 다운로드 받기 위해서는 ‘!kaggle competitions download -c dogs-vs-cats’ 명령을 실행합니다. 이 명령을 실행했을 때 403 에러가 날 수 있습니다. 이것은 규칙에 동의하지 않아서 입니다. 다음 링크에서 규칙에 동의합니다. 규칙에 동의하려면 핸드폰을 등록해야 합니다. 다운로드 받은 파일을 !unzip 명령을 실행해서 압축을 풀어줍니다.

코드 5.4를 참조해서 훈련, 검증, 테스트 데이터를 준비합니다.

5.2.3 Building your network

입력 이미지는 가로 세로 150인 컬러 이미지 이기 때문에 input_shape은 (150, 150, 3)이 됩니다.

출력은 강아지인지 고양이인지 구분하는 2진 분류이기 때문에 units은 1로 활성화 함수는 ‘sigmoid’로 설정합니다.

중간층에는 합성곱 층들을 두어 특성을 추출하도록 하고, 출력층 앞에는 추출한 특성으로 원하는 출력을 낼 수 있도록 완전연결 층을 둡니다. 합성곱 층과 완전연결 층을 연결하기 위해 Flatten 층을 둡니다. 특성 맵의 깊이는 네트워크에서 점진적으로 증가되도록, 특성 맵의 크기는 감소하도록 작성합니다.

코드 5.5를 참조해서 모델 구성 코드를 작성합니다.

model.summary()를 실행하고, 그 결과를 해석합니다. 특히 Param #를 맞춰봅니다.

이진 분류이기 때문에 손실 함수는 binary_crossentropy를 사용하고, 최적화 함수는 rmsprop을 사용합니다. 학습률은 1e-4로 설정합니다. 측정 지표는 acc로 설정합니다. 코드 5.6을 참조합니다.

5.2.4 Data preprocessing

Keras has a module with image-processing helper tools, located at keras.preprocessing.image. In particular, it contains the class ImageDataGenerator, which lets you quickly set up Python generators that can automatically turn image files on disk into batches of preprocessed tensors.

이미지 전처리에 ImageDataGenerator를 사용합니다. 코드 5.7을 참조해서 이미지 전처리를 합니다.

ImageDataGenerator는 파이썬 제너레이터입니다. 제너레이터는 모든 항목을 한 번에 전달하는 것이 아니라 일부분씩 전달합니다. 따라서 모델 훈련에도 이런 성질이 반영되어야 합니다. 케라스는 이를 위해 모델의 fit_generator 메소드를 제공합니다. 코드 5.8을 참조하여 모델 훈련 코드를 작성합니다. 모델의 save 메소드를 사용해 모델을 저장할 수 있습니다.

코드 5.10을 참조하여 훈련 결과를 그래프로 표현하는 코드를 작성해 봅니다.

그래프에서, 훈련 손실을 꾸준히 줄고 있지만 검증 손실은 5 에포크 정도 후에는 증가되는 것을 볼 수 있습니다. 정확도도 마찬가지 입니다. 훈련 정확도는 지속적으로 늘어나는데 검증 정확도는 5 에포크 정도 후 부터는 늘지 않습니다. 과적합된 것입니다.

컴퓨터 비전에서 과적합을 막는 일반적인 방법은 데이터 증식입니다.

5.2.5 Using data augmentation

In Keras, this can be done by configuring a number of random transformations to be performed on the images read by the ImageDataGenerator instance.

케라스에서는 ImageDataGenerator를 사용해서 이미지 데이터를 증식할 수 있습니다. 코드 5.11과 5.12를 참조하여 이미지 데이터를 증식시켜보고 그래프로 출력해 봅니다.

To further fight overfitting, you’ll also add a Dropout layer to your model, right before the densely connected classifier

완전연결 층 앞에 Dropout 층을 두어 과적합을 줄여봅니다. 코드 5.13을 참조합니다.

이미지 데이터 증식과 드롭아웃을 사용해서 네트워크를 훈련합니다. 코드 5.14를 참조합니다.

ImageDataGenerator와 fit_generator에 대한 좀

더 자세한 내용은 다음 링크를 참고합니다.

About the Author
(주)뉴테크프라임 대표 김현남입니다. 저에 대해 좀 더 알기를 원하시는 분은 아래 링크를 참조하세요. http://www.umlcert.com/kimhn/

Leave a Reply

*