16장 이미지 인식의 꽃, 컨볼루션 신경망(CNN)¶
이미지를 인식하는 원리¶
In [1]:
Copied!
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import sys
# MNIST 데이터셋을 불러와 학습셋과 테스트셋으로 저장합니다.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 학습셋과 테스트셋이 각각 몇 개의 이미지로 되어 있는지 확인합니다.
print("학습셋 이미지 수 : %d 개" % (X_train.shape[0]))
print("테스트셋 이미지 수 : %d 개" % (X_test.shape[0]))
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import sys
# MNIST 데이터셋을 불러와 학습셋과 테스트셋으로 저장합니다.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 학습셋과 테스트셋이 각각 몇 개의 이미지로 되어 있는지 확인합니다.
print("학습셋 이미지 수 : %d 개" % (X_train.shape[0]))
print("테스트셋 이미지 수 : %d 개" % (X_test.shape[0]))
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11493376/11490434 [==============================] - 1s 0us/step 11501568/11490434 [==============================] - 1s 0us/step 학습셋 이미지 수 : 60000 개 테스트셋 이미지 수 : 10000 개
In [2]:
Copied!
# 첫 번째 이미지를 확인해 봅시다.
plt.imshow(X_train[0], cmap='Greys')
plt.show()
# 첫 번째 이미지를 확인해 봅시다.
plt.imshow(X_train[0], cmap='Greys')
plt.show()
In [3]:
Copied!
# 이미지가 인식되는 원리를 알아봅시다.
for x in X_train[0]:
for i in x:
sys.stdout.write("%-3s" % i)
sys.stdout.write('\n')
# 이미지가 인식되는 원리를 알아봅시다.
for x in X_train[0]:
for i in x:
sys.stdout.write("%-3s" % i)
sys.stdout.write('\n')
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 18 18 18 12613617526 1662552471270 0 0 0 0 0 0 0 0 0 0 0 30 36 94 15417025325325325325322517225324219564 0 0 0 0 0 0 0 0 0 0 0 49 23825325325325325325325325325193 82 82 56 39 0 0 0 0 0 0 0 0 0 0 0 0 18 2192532532532532531981822472410 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 80 15610725325320511 0 43 1540 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 1 15425390 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1392531902 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 19025370 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 35 2412251601081 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 81 24025325311925 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 45 18625325315027 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16 93 2522531870 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 24925324964 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 46 1301832532532072 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 39 1482292532532532501820 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 24 11422125325325325320178 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 66 21325325325325319881 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 17121925325325325319580 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 17222625325325325324413311 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 13625325325321213513216 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
In [4]:
Copied!
# 차원 변환 과정을 실습해 봅니다.
X_train = X_train.reshape(X_train.shape[0], 784)
X_train = X_train.astype('float64')
X_train = X_train / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float64') / 255
# 클래스 값을 확인해 봅니다.
print("class : %d " % (y_train[0]))
# 바이너리화 과정을 실습해 봅니다.
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
print(y_train[0])
# 차원 변환 과정을 실습해 봅니다.
X_train = X_train.reshape(X_train.shape[0], 784)
X_train = X_train.astype('float64')
X_train = X_train / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float64') / 255
# 클래스 값을 확인해 봅니다.
print("class : %d " % (y_train[0]))
# 바이너리화 과정을 실습해 봅니다.
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
print(y_train[0])
class : 5 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
2. 딥러닝 기본 프레임 만들기¶
In [5]:
Copied!
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
import os
# MNIST 데이터를 불러옵니다.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 차원 변환 후, 테스트셋과 학습셋으로 나누어 줍니다.
X_train = X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
# 모델 구조를 설정합니다.
model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.summary()
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
import os
# MNIST 데이터를 불러옵니다.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 차원 변환 후, 테스트셋과 학습셋으로 나누어 줍니다.
X_train = X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float32') / 255
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
# 모델 구조를 설정합니다.
model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense (Dense) (None, 512) 401920 _________________________________________________________________ dense_1 (Dense) (None, 10) 5130 ================================================================= Total params: 407,050 Trainable params: 407,050 Non-trainable params: 0 _________________________________________________________________
In [6]:
Copied!
# 모델 실행 환경을 설정합니다.
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# 모델 최적화를 위한 설정구간입니다.
MODEL_DIR = './data/model/'
if not os.path.exists(MODEL_DIR):
os.mkdir(MODEL_DIR)
modelpath="./data/model/MNIST_MLP.keras"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)
# 모델을 실행합니다.
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback,checkpointer])
# 테스트 정확도를 출력합니다.
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))
# 모델 실행 환경을 설정합니다.
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# 모델 최적화를 위한 설정구간입니다.
MODEL_DIR = './data/model/'
if not os.path.exists(MODEL_DIR):
os.mkdir(MODEL_DIR)
modelpath="./data/model/MNIST_MLP.keras"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)
# 모델을 실행합니다.
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback,checkpointer])
# 테스트 정확도를 출력합니다.
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))
Epoch 00001: val_loss improved from inf to 0.18529, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00002: val_loss improved from 0.18529 to 0.13521, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00003: val_loss improved from 0.13521 to 0.11078, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00004: val_loss improved from 0.11078 to 0.10850, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00005: val_loss improved from 0.10850 to 0.09555, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00006: val_loss improved from 0.09555 to 0.09161, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00007: val_loss improved from 0.09161 to 0.08949, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00008: val_loss improved from 0.08949 to 0.08235, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00009: val_loss did not improve from 0.08235 Epoch 00010: val_loss did not improve from 0.08235 Epoch 00011: val_loss did not improve from 0.08235 Epoch 00012: val_loss did not improve from 0.08235 Epoch 00013: val_loss improved from 0.08235 to 0.08088, saving model to ../data/model\MNIST_MLP.hdf5 Epoch 00014: val_loss did not improve from 0.08088 Epoch 00015: val_loss did not improve from 0.08088 Epoch 00016: val_loss did not improve from 0.08088 Epoch 00017: val_loss did not improve from 0.08088 Epoch 00018: val_loss did not improve from 0.08088 Epoch 00019: val_loss did not improve from 0.08088 Epoch 00020: val_loss did not improve from 0.08088 Epoch 00021: val_loss did not improve from 0.08088 Epoch 00022: val_loss did not improve from 0.08088 Epoch 00023: val_loss did not improve from 0.08088 313/313 [==============================] - 1s 1ms/step - loss: 0.0711 - accuracy: 0.9816 Test Accuracy: 0.9816
In [7]:
Copied!
# 검증셋과 학습셋의 오차를 저장합니다.
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프로 표현해 봅니다.
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
# 그래프에 그리드를 주고 레이블을 표시해 보겠습니다.
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
# 검증셋과 학습셋의 오차를 저장합니다.
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프로 표현해 봅니다.
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
# 그래프에 그리드를 주고 레이블을 표시해 보겠습니다.
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
3. 컨볼루션 신경망(CNN)¶
In [2]:
Copied!
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
import os
# 데이터를 불러옵니다.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32') / 255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
# 컨볼루션 신경망의 설정
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
# 모델의 실행 옵션을 설정합니다.
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# 모델 최적화를 위한 설정 구간입니다.
MODEL_DIR = './data/model/'
if not os.path.exists(MODEL_DIR):
os.mkdir(MODEL_DIR)
modelpath="./data/model/MNIST_CNN.keras"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)
# 모델을 실행합니다.
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback,checkpointer])
# 테스트 정확도를 출력합니다.
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
import numpy as np
import os
# 데이터를 불러옵니다.
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32') / 255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
# 컨볼루션 신경망의 설정
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
# 모델의 실행 옵션을 설정합니다.
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# 모델 최적화를 위한 설정 구간입니다.
MODEL_DIR = './data/model/'
if not os.path.exists(MODEL_DIR):
os.mkdir(MODEL_DIR)
modelpath="./data/model/MNIST_CNN.keras"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)
# 모델을 실행합니다.
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback,checkpointer])
# 테스트 정확도를 출력합니다.
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))
Epoch 00001: val_loss improved from inf to 0.07661, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00002: val_loss improved from 0.07661 to 0.05954, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00003: val_loss improved from 0.05954 to 0.04875, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00004: val_loss improved from 0.04875 to 0.04668, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00005: val_loss improved from 0.04668 to 0.04307, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00006: val_loss did not improve from 0.04307 Epoch 00007: val_loss improved from 0.04307 to 0.03940, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00008: val_loss did not improve from 0.03940 Epoch 00009: val_loss did not improve from 0.03940 Epoch 00010: val_loss did not improve from 0.03940 Epoch 00011: val_loss improved from 0.03940 to 0.03780, saving model to ./data/model\MNIST_CNN.hdf5 Epoch 00012: val_loss did not improve from 0.03780 Epoch 00013: val_loss did not improve from 0.03780 Epoch 00014: val_loss did not improve from 0.03780 Epoch 00015: val_loss did not improve from 0.03780 Epoch 00016: val_loss did not improve from 0.03780 Epoch 00017: val_loss did not improve from 0.03780 Epoch 00018: val_loss did not improve from 0.03780 Epoch 00019: val_loss did not improve from 0.03780 Epoch 00020: val_loss did not improve from 0.03780 Epoch 00021: val_loss did not improve from 0.03780 313/313 [==============================] - 1s 3ms/step - loss: 0.0345 - accuracy: 0.9919: 0s - loss: 0 Test Accuracy: 0.9919
In [9]:
Copied!
# 검증셋과 학습셋의 오차를 저장합니다.
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프로 표현해 봅니다.
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
# 그래프에 그리드를 주고 레이블을 표시하겠습니다.
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
# 검증셋과 학습셋의 오차를 저장합니다.
y_vloss = history.history['val_loss']
y_loss = history.history['loss']
# 그래프로 표현해 봅니다.
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')
# 그래프에 그리드를 주고 레이블을 표시하겠습니다.
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
CNN의 필터(kernel) 학습¶
• CNN의 필터(kernel) 는 전체 이미지나 시퀀스를 훑으면서 특징을 추출합니다.
• 학습 시에는 각 필터가 “어떤 패턴에 강하게 반응해야 하는가”를 배우죠.
• 손실이 역전파될 때,
• 각 필터의 값(예: 3×3×채널 수)이
• 손실의 변화율(∂L/∂filter)에 따라 조금씩 수정됩니다.
💡 예: “얼굴의 눈 부분”을 잘 잡는 필터가 되도록 학습됨 → 눈 모양 패턴에 손실 기여가 크면, 해당 필터 방향으로 가중치 조정
🧠 1️⃣ CNN 필터의 초기값은 랜덤하게 정해집니다
네, CNN의 컨볼루션 필터(kernel) 는 처음엔 완전히 랜덤이에요.
예를 들어, Conv2D(32, kernel_size=(3,3)) 이렇게 선언하면 CNN은 3×3짜리 필터 32개를 만들어 놓습니다.
이 필터들의 초기값은 일반적으로 다음 중 하나의 방법으로 무작위 초기화(random initialization) 됩니다.
🧩 2️⃣ 그런데 학습이 진행되면서?
손실(loss)을 줄이기 위해 역전파(backpropagation) 가 필터까지 흘러갑니다. → 각 필터의 값이 “이런 패턴일 때 예측이 잘 되더라”는 방향으로 조정됩니다.
즉, CNN의 각 필터는 자동으로 특징 탐지기(feature detector) 로 바뀝니다. • 1층 필터: 간단한 edge (경계선, 방향 등) • 2~3층 필터: 눈, 코, 손 같은 구체적 모양 • 더 깊은 층: 얼굴, 고양이, 자동차 등 추상적 개념