본문 바로가기

[머신러닝 야학/2기] Tensorflow 이미지 분류 - CNN (python) 수업 정리

Tensorflow 이미지 분류 - CNN (python)

 

다음은 머신러닝 야학에서 텐서플로우 심화 강의인 <Tensorflow 이미지 분류 - CNN (python)>을 학습하고 요약정리한 내용입니다. 강의 내용을 기반으로 요약하되, 보충 설명이 필요한 경우 <밑바닥부터 시작하는 딥러닝>이라는 책과 Stanford 대학의 <CS231n 2017> 강의를 참고했습니다.

오리엔테이션


이전 기초 수업에서는 "표" 형태의 데이터를 다뤘다면

이번 심화 수업에서는 "이미지" 형태의 데이터를 다룰 예정입니다.

데이터와 차원


데이터에 대해 차원은 두 가지 의미를 가집니다.

• "차원 수"의 의미

- 데이터 공간의 맥락: 변수의 개수

- 데이터 형태의 맥락: 배열의 깊이

이미지 데이터 이해


이미지 데이터를 이해하기 위해 MNIST와 CIFAR10 데이터를 가지고 실습을 진행해봅니다.

 

MNIST 이미지 데이터 샘플

MNIST 이미지 데이터 하나의 "차원 수"

- 데이터 공간의 맥락: 변수의 개수 = 28 * 28 = 784개

- 데이터 형태의 맥락: 배열의 깊이 = (28, 28)로 2차원 형태

 

# MNIST
(독립, 종속) = tf.keras.datasets.mnist.load_data()
print(독립.shape, 종속.shape) # (60000, 28, 28) (60000,)

 

CIFAR10 이미지 데이터 샘플

CIFAR10 이미지 데이터 하나의 "차원 수"

- 데이터 공간의 맥락: 변수의 개수 = 32 * 32 * 3 = 3072개

- 데이터 형태의 맥락: 배열의 깊이 = (32, 32, 3)로 3차원 형태

 

# CIFAR10
(독립, 종속) = tf.keras.datasets.cifar10.load_data()
print(독립.shpae, 종속.shape) # (50000, 32, 32, 3) (50000, 1)

다섯 번째 딥러닝 1 - Flatten


"이미지" 데이터를 "표" 형태의 데이터로 변형해서 학습하는 방법과 딥러닝 모델의 별명이 "특징 자동 추출기"인 이유를 배웁니다.

• 이미지 데이터를 표 형태의 데이터로 변형해서 학습하는 방법

1. 독립변수에 대해 reshape 해주거나

28 x 28을 1 x 784로 reshape

① 과거의 데이터를 준비합니다.

(독립, 종속), _ = tf.keras.datasets.mnist.load_data()

독립 = 독립.reshape(60000, 784)

종속 = pd.get_dummies(종속)

print(독립.shape, 종속.shape)

 

② 모델의 구조를 만듭니다.

X = tf.keras.layers.Input(shape=[784])

H = tf.keras.layers.Dense(84, activation='swish')(X)

Y = tf.keras.layers.Dense(10, activation='softmax')(H)

model = tf.keras.models.Model(X, Y)

model.compile(loss='categorical_crossentropy', metrics='accuracy')

 

③ 데이터로 모델을 학습(fit)합니다.

model.fit(독립, 종속, epochs=10)

 

④ 모델을 이용합니다.

print("Predictions: ", model.predict(독립[0:5]))

 

2. 아예 flatten layer 쌓기

① 과거의 데이터를 준비합니다.

(독립, 종속), _ = tf.keras.datasets.mnist.load_data()

종속 = pd.get_dummies(종속)

print(독립.shape, 종속.shape)

 

② 모델의 구조를 만듭니다.

X = tf.keras.layers.Input(shape=[28, 28])

H = tf.keras.layers.Flatten()(X)

H = tf.keras.layers.Dense(84, activation='swish')(H)

Y = tf.keras.layers.Dense(10, activation='softmax')(H)

model = tf.keras.models.Model(X, Y)

model.compile(loss='categorical_crossentropy', metrics='accuracy')

 

③ 데이터로 모델을 학습(fit)합니다.

model.fit(독립, 종속, epochs=10)

 

④ 모델을 이용합니다.

print("Predictions: ", model.predict(독립[0:5]))

• 딥러닝 모델의 별명이 "특징 자동 추출기"인 이유

위 슬라이드에서 입력층은 (60000, 784), 은닉층은 (60000, 84), 출력층은 (60000, 10)의 형태입니다. 이때 컴퓨터에게 출력층에서 0~9, 즉 10개의 카테고리로 이미지를 분류하기 위해서 입력층에서 784개의 픽셀 중 가장 좋은 특징을 84개만 찾아달라는 것과 같습니다. 이로 인해 딥러닝이 "특징 자동 추출기"라는 별명을 갖게 되었습니다.

다섯 번째 딥러닝 2 - Conv2D


컨볼루션을 이해하고, 필터를 이해하고, 컨볼루션 연산을 직접 해봅니다.

 

• 컨볼루션의 이해

1. convolution(합성곱)

특정한 패턴의 특징이 어디서 나타나는지 확인하는 도구

 

2. feature map(특징맵)

대상 이미지로부터 필터를 통해 특징을 잡아낸 컨볼루션의 결과

특징에 대한 위치 정보가 포함된 지도

 

★ 컨볼루션 필터 하나가 특징맵 이미지 하나를 만든다!

3. 코드

① 과거의 데이터를 준비합니다.

(독립, 종속), _ = tf.keras.datasets.mnist.load_data()

독립 = 독립.reshape(60000, 28, 28, 1)

종속 = pd.get_dummies(종속)

print(독립.shape, 종속.shape)

 

② 모델의 구조를 만듭니다.

X = tf.keras.layers.Input(shape=[28, 28, 1])

H = tf.keras.layers.Conv2D(3, kernel_size=5, activation='swish')(X) # 사이즈가 5인 필터 셋 3개 사용

H = tf.keras.layers.Conv2D(6, kernel_size=5, activation='swish')(H) # 사이즈가 5인 필터셋 6개 사용

H = tf.keras.layers.Flatten()(H)

H = tf.keras.layers.Dense(84, activation='swish')(H)

Y = tf.keras.layers.Dense(10, activation='softmax')(H)

model = tf.keras.models.Model(X, Y)

model.compile(loss='categorical_crossentropy', metrics='accuracy')

 

- 컨볼루션 필터 3개 → 3개의 특징맵 생성 = 3 채널의 특징맵

- 컨볼루션 필터 6개 → 6개의 특징맵 생성 = 6 채널의 특징맵

Tip) "채널"이라는 표현 기억해두기!

 

③~④ 이하 동일

• 필터의 이해

1. 필터셋은 3차원 형태로 된 가중치의 모음

2. 필터셋 하나는 앞선 레이어의 결과인 "특징맵" 전체를 본다.

3. 필터셋 개수만큼 특징맵을 만든다.

• 컨볼루션 연산

참고로 컨볼루션 연산 시 output size를 결정하는 식은 $\frac{N + 2*P - F}{stride} + 1$

N - input size, P - padding size, F - filter size

여기서는 padding에 대한 정보가 없으니 padding=0, stride=1이므로 output size가 $\frac{8 + 2*0 - 3}{1} + 1 = 6$이다.

다섯 번째 딥러닝 3 - MaxPool2D


pooling의 목적과 pooling 중에서도 max pooling의 연산 방식을 살펴보고 코드로 실습해봅니다.

• pooling

1. 목적

flatten layer 이후에 사용되는 가중치의 수를 작게 유지하기 위해 입력으로 사용할 column 수를 조정하는 것

즉, 학습해야 할 파라미터 수를 줄이기 위해 풀링 사용!

 

2. 연산 방식(max pooling)

위 슬라이드에서 2x2 필터가 6x6 특징맵을 돌면서 가장 큰 값(max)만 가져옴

Tip) 특징맵에서 값이 클수록 특징이 많이 나타나므로 average pooling보다 max pooling을 사용

 

3. 코드

① 과거의 데이터를 준비합니다.

(독립, 종속), _ = tf.keras.datasets.mnist.load_data()

독립 = 독립.reshape(60000, 28, 28, 1)

종속 = pd.get_dummies(종속)

print(독립.shape, 종속.shape)

 

② 모델의 구조를 만듭니다.

X = tf.keras.layers.Input(shape=[28, 28, 1])

H = tf.keras.layers.Conv2D(3, kernel_size=5, activation='swish')(X)

H = tf.keras.layers.MaxPool2D()(H)

H = tf.keras.layers.Conv2D(6, kernel_size=5, activation='swish')(H)

H = tf.keras.layers.MaxPool2D()(H)

H = tf.keras.layers.Flatten()(H)

H = tf.keras.layers.Dense(84, activation='swish')(H)

Y = tf.keras.layers.Dense(10, activation='softmax')(H)

model = tf.keras.models.Model(X, Y)

model.compile(loss='categorical_crossentropy', metrics='accuracy')

 

③~④ 이하 동일

다섯 번째 딥러닝 완성 - LeNet


지금까지 배운 내용을 바탕으로 MNIST와 Cifar10 dataset에 대해 LeNet을 만들어봅니다.

• MNIST

1. 과거의 데이터를 준비합니다.

(독립, 종속), _ = tf.keras.datasets.mnist.load_data()
독립 = 독립.reshape(60000, 28, 28, 1)
종속 = pd.get_dummies(종속)
print(독립.shape, 종속.shape)

 

2. 모델의 구조를 만듭니다.

# INPUT 32x32
X = tf.keras.layers.Input(shape=[28, 28, 1])

# C1: feature maps 6@28x28
# MNIST 데이터의 경우 input 자체가 28x28이므로 이를 유지해주기 위해 padding='same'
H = tf.keras.layers.Conv2D(6, kernel_size=5, padding='same', activation='swish')(X)

# S2: f. maps 6@14x14
H = tf.keras.layers.MaxPool2D()(H)

# C2: f. maps 16@10x10
# output size = (input size + 2*padding - filter)/stride + 1 = (14 + 2*0 - 5)/1 + 1 = 10
H = tf.keras.layers.Conv2D(16, kernel_size=5, activation='swish')(H)

# S4: f.maps 16@5x5
H = tf.keras.layers.MaxPool2D()(H)

H = tf.keras.layers.Flatten()(H)

# C5: layer 120
H = tf.keras.layers.Dense(120, activation='swish')(H)

# F6: layer 84
H = tf.keras.layers.Dense(84, activation='swish')(H)

# OUTPUT 10
Y = tf.keras.layers.Dense(10, activation='softmax')(H)

model = tf.keras.models.Model(X, Y)
model.compile(loss='categorical_crossentropy', metrics='accuracy')

 

3~4. 이하 동일

• Cifar10

1. 과거의 데이터를 준비합니다.

(독립, 종속), _ = tf.keras.datasets.cifar10.load_data()
종속 = pd.get_dummies(종속.reshape(50000))
print(독립.shape, 종속.shape)

 

2. 모델의 구조를 만듭니다.

# INPUT 32x32
X = tf.keras.layers.Input(shape=[32, 32, 3])

# C1: feature maps 6@28x28
# output size = (input size + 2*padding - filter)/stride + 1 = (32 + 2*0 - 5)/1 + 1 = 28
H = tf.keras.layers.Conv2D(6, kernel_size=5, activation='swish')(X)

# S2: f. maps 6@14x14
H = tf.keras.layers.MaxPool2D()(H)

# C2: f. maps 16@10x10
# output size = (input size + 2*padding - filter)/stride + 1 = (14 + 2*0 - 5)/1 + 1 = 10
H = tf.keras.layers.Conv2D(16, kernel_size=5, activation='swish')(H)

# S4: f.maps 16@5x5
H = tf.keras.layers.MaxPool2D()(H)
 
H = tf.keras.layers.Flatten()(H)

# C5: layer 120
H = tf.keras.layers.Dense(120, activation='swish')(H)

# F6: layer 84
H = tf.keras.layers.Dense(84, activation='swish')(H)

# OUTPUT 10
Y = tf.keras.layers.Dense(10, activation='softmax')(H)
 
model = tf.keras.models.Model(X, Y)
model.compile(loss='categorical_crossentropy', metrics='accuracy')

 

3~4. 이하 동일

내 이미지 사용하기


내 이미지를 사용하여 LeNet을 구성해봅니다.

• 이미지 데이터를 구성하는 방법

1. 이미지 데이터셋 준비
2. 앞서 이미지 데이터셋을 분류한대로 폴더 생성

• 이미지 데이터를 읽어 들이는 코드의 사용법

1. 과거의 데이터를 내 로컬로부터 이미지를 읽어 들여 준비합니다.

# 라이브러리 로딩
import glob
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf 

# 이미지 읽어서 데이터 준비하기
paths = glob.glob('./notMNIST_small/*/*.png')
paths = np.random.permutation(paths)
독립 = np.array([plt.imread(paths[i]) for i in range(len(paths))])
종속 = np.array([paths[i].split('/')[-2] for i in range(len(paths))])
print(독립.shape, 종속.shape)

독립 = 독립.reshape(18724, 28, 28, 1)
종속 = pd.get_dummies(종속)
print(독립.shape, 종속.shape)

 

2~4. 이하 동일

수업을 마치며


수업을 마치며 차원, 특징 자동 추출기, LeNet에 대해 정리해봅니다.