안녕하세요 Oneclick AI 입니다!!
오늘은, CNN 모델에 대해서 깊게 알아보는 시간을 가져볼까 합니다.
딥러닝에 이미지 데이터를 사용할 수 있게 된 이유가 바로 CNN의 합성곱 신경망 (Convolutional Neural Network, CNN) 덕분인데요, 오늘은 이 신경망이 어떻게 작동하는지, CNN은 어떻게 사진 속의 숫자를 구분할 수 있는지 알아봅시다.
목차
- CNN 핵심 원리 파악하기
- 왜 이미지에 CNN을 사용할까?
- CNN의 핵심 : 지역 수용 영역과 파라미터 공유
- CNN의 주요 구성 요소
- 아키텍처를 통한 내부 코드 들여다 보기
- keras로 구현한 CNN 모델 아키텍쳐
- model.summary()로 구조 확인하기
- 직접 CNN 구현해 보기
- 1단계 : 데이터 로드 및 전처리
- 2단계 : 모델 컴파일
- 3단계 : 모델 학습 및 평가
- 4단계 : 학습된 모델 저장하기
- 5단계 : 모델 사용하기
- 나만의 CNN 모델 만들어보기
- 하이퍼파라미터 튜닝
- 모델 구조 변경하기
- 전이학습으로 성능 극대화 하기
- 결론
1. CNN 핵심원리 파악하기
들어가기 앞서, CNN 이 어떤 원리로 이미지를 이해하는지 먼저 살펴보겠습니다.
왜 이미지에 CNN을 사용할까??
단순한 신경망(Fully Connected Layer)에 이미지를 입력하려면, 2차원인 이미지를 1차원의 긴 데이터로 펼치는 데이터 전처리 과정이 꼭 필요합니다.
이 과정에서 픽셀 간의 공간적인 정보가 전부 파괴됩니다.
이는 어떤 픽셀이 서로 이웃해 있는지 알 수 없어져서 눈이 코 옆에 있다는 위치정보 같은 내용이 가라저 버린다는 의미 입니다.
CNN은 이런 문제를 해결하기 위해 인간의 시신경 구조를 모방하여 설계되었습니다.
CNN의 핵심 아이디어
이것이 바로 지역적 수용영역과 파라미터 공유 입니다.
지역적 수용 영역(Local Receptive Fields)
신경망의 각 뉴런이 이미지 전체가 아닌, 작은 일부에만 연결됩니다.
이는 전체 픽셀에 대해서가 아닌 예시를 들면 3 * 3 픽셀에만 적용되는 방식인데요, 이를 통해 모델은 이미지의 전체 맥락보다 선, 모서리, 질감과 같은 지역적인 패턴을 먼저 학습하게 됩니다.파라미터 공유(Parameter Sharing) CNN은 이미지 전체를 필터를 통해서 스캔하는 느낌으로 학습합니다.
따라서, 한번 이미지의 특징을 학습하면, 이미지의 모든 위치에서 해당 특징을 감지할 수 있습니다.
이를 통해서 학습할 파라미터 수를 많이 줄일 수 있습니다.
CNN의 주요 구성 요소 앞선 아이디어를 바탕으로, CNN은 다음 3가지의 계층을 조합해서 만듭니다.
합성곱 계층 (Convolutional Layer) 학습 가능한 필터를 사용해서 이미지의 특징을 추출해 냅니다.
edge, corner 등을 추출하여 얻는 결과물을 특징 맵(Feature Map) 이라고 합니다.풀링 계층 (Pooling Layer) 앞서 만든 맵의 크기를 줄이는 요약단계 입니다.
최대 풀링(Max Pooling)은 특정 영역헤서 가장 중요한 특징(가장 큰 값)만 남겨 계산량을 줄이고, 모델이 특징의 미세한 위치 변화에 덜 민감해 하도록 만듭니다.완전 연결 계층 (Dense Layer) 추출된 특징들을 종합하여 최종적으로 이미지가 어떤 클래스에 속하는지 분류하는 역할을 합니다.
2. 아키텍처를 통한 내부 코드 들여다 보기
이제, 앞서 설명한 내용을 바탕으로, 실제 TensorFlow Keras 코드를 통해서 어떻게 작동하는지 실감해 봅시다.
다음은, Keras로 구현한 CNN 모델 아키텍쳐 입니다.
import tensorflow as tf
from tensorflow import keras
# 모델 아키텍처 정의
model = keras.Sequential([
# Input: (28, 28, 1) 이미지
# 첫 번째 Conv-Pool 블록
keras.layers.Conv2D(32, kernel_size=(3, 3), activation="relu", input_shape=(28, 28, 1)),
keras.layers.MaxPooling2D(pool_size=(2, 2)),
# 두 번째 Conv-Pool 블록
keras.layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
keras.layers.MaxPooling2D(pool_size=(2, 2)),
# 분류기(Classifier)
keras.layers.Flatten(),
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation="softmax"),
])
이제, 앞서 설명했던 이론이 이 코드에 어떻게 녹아있는지 알아봅시다.
- 합성곱 계층(Conv2D)
keras.layers.Conv2D(32, kernel_size=(3, 3), activation="relu", input_shape=(28, 28, 1))
이 코드를 통해서, 합성곱 계층을 형성, 다음 아이디어를 구현합니다.
- 지역 수용영역
kernel_size(3, 3)
을 통해서 이미지 전체가 아닌 3 * 3 크기의 작은 영역만 보도록 만듭니다.
이렇게, 지역적 수용영역을 만듭니다
- 파라미터 공유
Conv2D
계층은 32개의 필터를 생성합니다.
3 * 3필터는 고유한 파라미터(가중치)세트를 가지며, 이 필터 하나가 이미지 전체를 스캔합니다.
만약, 파라미터 공유가 없다면, 각 3 * 3 위치마다 별도의 파라미터를 사용해야 하므로 크기가 엄청 커집니다.
하지만, 파라미터 공유 덕분에, 이 경우에서 (3 * 3 * 1 +1) * 32 = 320
개의 파라미터 만으로 이미지 전체의 특징을 추출할 수 있습니다.
- 풀링 계층(MaxPooling2D)
keras.layers.MaxPooling2D(pool_size=(2, 2))
앞선 합성곱 계층이 생성한 특징맵을 2* 2 크기의 영역으로 나누고, 각 영역에서 가장 큰 값만 남기라는 의미입니다.
이를 통해 맵 크기가 절반으로 줄어드는 다운 샘플링이 일어나고, 계산 효율성이 높아져 모델 학습이 더 가벼워 집니다.
- 완전 연결 계층(Dense Layer)
keras.layers.Flatten()
keras.layers.Dense(10, activation="softmax")
최종 분류기 이며, 완전연결계층 입니다.
keras.layers.Flatten()
완전연결계층은 1차원 백터를 입력으로 받기 때문에, Flastten 계층이 먼저 들어와 2차원 형태의 특징 맵을 한 줄로 펼쳐줍니다.
keras.layers.Dense(10, activation="softmax")
이 코드가 완전연결계층이며, 보통 Dense Layer 라고 부릅니다.
특징백터를 입력받아 10개의 클래스 중 어느 클래스에 할당할지 최종적으로 결정합니다.activation="softmax"
는 각 클래스에 대한 예측값을 0 과 1 사이의 확률값으로 하게 하여 모든 확률의 합이 1이 되도록 만들어 줍니다.
3. 직접 CNN 구현해 보기
이제, 직접 CNN 학습 코드를 단계별로 구현해 봅시다.
1단계. 데이터 로드 및 전처리
모델이 학습할 데이터를 가져와 줍니다.
이번엔, 쉽게 불러올 수 있는 MNIST 손글씨 숫자 데이터셋을 가져와 적절한 형태로 전처리 하겠습니다.
import numpy as np
import tensorflow as tf
from tensorflow import keras
from keras import layers
# Keras 라이브러리를 통해 MNIST 데이터셋을 손쉽게 불러옵니다.
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
# 정규화: 픽셀 값의 범위를 0~255에서 0~1 사이로 조정하여 학습 안정성 및 속도를 높입니다.
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0
# 채널 차원 추가: 흑백 이미지(채널 1)의 차원을 명시적으로 추가합니다.
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
# 레이블 원-핫 인코딩: 숫자 '5'를 [0,0,0,0,0,1,0,0,0,0] 형태의 벡터로 변환합니다.
num_classes = 10
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
2단계. 모델 컴파일
모델 아키텍쳐를 정의하고 모델을 어떻게 학습시킬지에 대해 정합니다.
model = keras.Sequential([
keras.Input(shape=(28, 28, 1)), # 입력 레이어
layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(0.5),
layers.Dense(num_classes, activation="softmax")
])
model.compile(
# 손실 함수(Loss Function): 모델의 예측이 정답과 얼마나 다른지 측정합니다.
loss="categorical_crossentropy",
# 옵티마이저(Optimizer): 손실을 최소화하기 위해 모델의 가중치를 업데이트하는 방법입니다.
optimizer="adam",
# 평가지표(Metrics): 훈련 과정을 모니터링할 지표로, 정확도를 사용합니다.
metrics=["accuracy"]
)
3단계. 모델 학습 및 평가model.fit()
함수를 통해서 학습을 시작하고, 학습이 끝난 후 model.evaluate()
로 최종 성능을 확인합니다.
batch_size = 128
epochs = 15
# 모델 학습 실행
history = model.fit(
x_train, y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(x_test, y_test)
)
# 학습 완료 후 최종 성능 평가
score = model.evaluate(x_test, y_test, verbose=0)
print(f"\nTest loss: {score[0]:.4f}")
print(f"Test accuracy: {score[1]:.4f}")
4단계. 학습된 모델 저장하기 모델을 저장하고, 불러올 수 있도록 합니다.
# 모델의 구조, 가중치, 학습 설정을 모두 '.keras' 파일 하나에 저장합니다.
model.save("my_keras_model.keras")
print("\nModel saved to my_keras_model.keras")
위 단계를 전부 수행한 모델이 지금 이 허깅페이스 라이브러리에 들어있는 모델입니다. 이어서, 모델을 사용해 볼 떄, 위 코드를은 직접 실행하지 말고, 다음 코드를 실행시켜 주세요!
만일, 위 코드를 통해 직접 딥러닝을 시켭고 싶으시다면, Files의
train.py를 실행시켜 주세요!
5단계. 모델 사용해 보기
앞선 단계들을 거쳐 완성한 모델이 이 허깅페이스 페이지에 올라가 있습니다.
이제, 이 허깅페이스 페이지의 모델을 불러와서, 직접 사용해 보겠습니다.
Files의 test.py를 실행시켜 보세요!!
직접 준비한 숫자 손글씨를 모델에 입력으로 넣으면, 그 숫자가 어떤 숫자인지 맞춰줄 겁니다!!
코드 실행을 위해서, 이 종속성을 설치해야 합니다.
cmd 창을 열고, 이 코드를 넣고 실행하여 먼저 종속성을 받아주세요
pip install tensorflow huggingface_hub Pillow numpy
4. 나만의 CNN 모델 만들기
이제, 성능을 더 끌어올리기 위해, 또 원하는 목적에 맞게 모델을 수정할 차례입니다.
- 하이퍼파라미터 튜닝
모델의 성능에 큰 영향을 미치는 batch_size, epochs, 옵티나이저의 leaarning_rate등을 조합하여 최적화 합니다.
다음 코드를 건드려서 수정합니다.
# 예: Adam 옵티마이저의 학습률(learning_rate)을 직접 설정
optimizer = keras.optimizers.Adam(learning_rate=0.001)
model.compile(loss="categorical_crossentropy", optimizer=optimizer, metrics=["accuracy"])
- 모델 구조 변경
모델의 성능을 위해서,
Conv2D-MaxPooling2D
블록을 더 깊게,Conv2D
계층의 필터 수를 늘려 더 풍부하게 할 수 있습니다.
model = keras.Sequential([
keras.layers.Conv2D(32, kernel_size=(3, 3), activation="relu", input_shape=(28, 28, 1)),
keras.layers.MaxPooling2D(pool_size=(2, 2)),
# 블록 추가!
keras.layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
keras.layers.MaxPooling2D(pool_size=(2, 2)),
# 필터 수를 늘린 블록 추가!
keras.layers.Conv2D(128, kernel_size=(3, 3), activation="relu"),
keras.layers.MaxPooling2D(pool_size=(2, 2)),
keras.layers.Flatten(),
keras.layers.Dense(128, activation='relu'), # Dense 층 추가
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation="softmax"),
])
- 전이학습
아주 큰 데이터셋으로 학습된 이미 강력한 모델을 가져와, 그 모델이 학습한 이미지 특징추출능력만 가져와 사용하는 방법 입니다.
적은 데이터로도 높은 성능을 낼 수 있습니다.
# Keras에서 제공하는 사전 학습된 VGG16 모델 불러오기 (분류기는 제외)
base_model = keras.applications.VGG16(
weights='imagenet', # ImageNet으로 사전 학습된 가중치 사용
include_top=False, # 분류기(Dense 층)는 제외
input_shape=(32, 32, 3) # VGG16은 최소 32x32 컬러 이미지를 입력으로 받음
)
# 불러온 모델의 가중치는 고정(freeze)
base_model.trainable = False
# 새로운 분류기 추가
model = keras.Sequential([
base_model,
keras.layers.Flatten(),
keras.layers.Dense(256, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(10, activation='softmax') # 나의 문제에 맞는 출력층
])
# 새로 추가한 분류기 부분만 학습
model.compile(...)
model.fit(...)
5. 결론
오늘은, 이렇게 CNN 모델에 대해 간단하게 알아봤습니다.
이미지 처리에 특화된 CNN은 나중에 트랜스포머 모델이 나오면서 Vit라는 모델이 만들어지는데 큰 기여를 하게 됩니다.
이렇듯 근본 넘치는 CNN 많이 사랑해 주세요!!
다음에는 RNN모델로 돌아오겠습니다
오늘도 좋은하루 보내세용!!
- Downloads last month
- 51