|
--- |
|
license: apache-2.0 |
|
--- |
|
|
|
안녕하세요 Oneclick AI 입니다!! |
|
오늘은, CNN 모델에 대해서 깊게 알아보는 시간을 가져볼까 합니다. |
|
|
|
딥러닝에 이미지 데이터를 사용할 수 있게 된 이유가 바로 CNN의 합성곱 신경망 (Convolutional Neural Network, CNN) 덕분인데요, 오늘은 이 신경망이 어떻게 작동하는지, CNN은 어떻게 사진 속의 숫자를 구분할 수 있는지 알아봅시다. |
|
|
|
--- |
|
|
|
## 목차 |
|
1. CNN 핵심 원리 파악하기 |
|
- 왜 이미지에 CNN을 사용할까? |
|
- CNN의 핵심 : 지역 수용 영역과 파라미터 공유 |
|
- CNN의 주요 구성 요소 |
|
2. 아키텍처를 통한 내부 코드 들여다 보기 |
|
- keras로 구현한 CNN 모델 아키텍쳐 |
|
- model.summary()로 구조 확인하기 |
|
3. 직접 CNN 구현해 보기 |
|
- 1단계 : 데이터 로드 및 전처리 |
|
- 2단계 : 모델 컴파일 |
|
- 3단계 : 모델 학습 및 평가 |
|
- 4단계 : 학습된 모델 저장하기 |
|
- 5단계 : 모델 사용하기 |
|
4. 나만의 CNN 모델 만들어보기 |
|
- 하이퍼파라미터 튜닝 |
|
- 모델 구조 변경하기 |
|
- 전이학습으로 성능 극대화 하기 |
|
5. 결론 |
|
--- |
|
|
|
## 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 모델 아키텍쳐 입니다. |
|
```python |
|
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)** |
|
```python |
|
keras.layers.Conv2D(32, kernel_size=(3, 3), activation="relu", input_shape=(28, 28, 1)) |
|
``` |
|
이 코드를 통해서, 합성곱 계층을 형성, 다음 아이디어를 구현합니다. |
|
1. 지역 수용영역 |
|
```kernel_size(3, 3)```을 통해서 이미지 전체가 아닌 3 * 3 크기의 작은 영역만 보도록 만듭니다. |
|
이렇게, 지역적 수용영역을 만듭니다 |
|
|
|
2. 파라미터 공유 |
|
```Conv2D``` 계층은 32개의 필터를 생성합니다. |
|
3 * 3필터는 고유한 파라미터(가중치)세트를 가지며, 이 필터 하나가 이미지 전체를 스캔합니다. |
|
만약, 파라미터 공유가 없다면, 각 3 * 3 위치마다 별도의 파라미터를 사용해야 하므로 크기가 엄청 커집니다. |
|
하지만, 파라미터 공유 덕분에, 이 경우에서 ```(3 * 3 * 1 +1) * 32 = 320``` 개의 파라미터 만으로 이미지 전체의 특징을 추출할 수 있습니다. |
|
|
|
- **풀링 계층(MaxPooling2D)** |
|
```python |
|
keras.layers.MaxPooling2D(pool_size=(2, 2)) |
|
``` |
|
앞선 합성곱 계층이 생성한 특징맵을 2* 2 크기의 영역으로 나누고, 각 영역에서 가장 큰 값만 남기라는 의미입니다. |
|
이를 통해 맵 크기가 절반으로 줄어드는 **다운 샘플링**이 일어나고, 계산 효율성이 높아져 모델 학습이 더 가벼워 집니다. |
|
|
|
- **완전 연결 계층(Dense Layer)** |
|
```python |
|
keras.layers.Flatten() |
|
keras.layers.Dense(10, activation="softmax") |
|
``` |
|
최종 분류기 이며, 완전연결계층 입니다. |
|
1. ```keras.layers.Flatten()``` |
|
완전연결계층은 1차원 백터를 입력으로 받기 때문에, Flastten 계층이 먼저 들어와 2차원 형태의 특징 맵을 한 줄로 펼쳐줍니다. |
|
|
|
2. ```keras.layers.Dense(10, activation="softmax")``` |
|
이 코드가 완전연결계층이며, 보통 Dense Layer 라고 부릅니다. |
|
특징백터를 입력받아 10개의 클래스 중 어느 클래스에 할당할지 최종적으로 결정합니다. |
|
```activation="softmax"```는 각 클래스에 대한 예측값을 0 과 1 사이의 확률값으로 하게 하여 모든 확률의 합이 1이 되도록 만들어 줍니다. |
|
|
|
--- |
|
|
|
## 3. 직접 CNN 구현해 보기 |
|
이제, 직접 CNN 학습 코드를 단계별로 구현해 봅시다. |
|
|
|
**1단계. 데이터 로드 및 전처리** |
|
모델이 학습할 데이터를 가져와 줍니다. |
|
이번엔, 쉽게 불러올 수 있는 MNIST 손글씨 숫자 데이터셋을 가져와 적절한 형태로 전처리 하겠습니다. |
|
|
|
```python |
|
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단계. 모델 컴파일** |
|
모델 아키텍쳐를 정의하고 모델을 어떻게 학습시킬지에 대해 정합니다. |
|
```python |
|
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()```로 최종 성능을 확인합니다. |
|
```python |
|
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단계. 학습된 모델 저장하기** |
|
모델을 저장하고, 불러올 수 있도록 합니다. |
|
```python |
|
# 모델의 구조, 가중치, 학습 설정을 모두 '.keras' 파일 하나에 저장합니다. |
|
model.save("my_keras_model.keras") |
|
print("\nModel saved to my_keras_model.keras") |
|
``` |
|
|
|
위 단계를 전부 수행한 모델이 지금 이 허깅페이스 라이브러리에 들어있는 모델입니다. 이어서, 모델을 사용해 볼 떄, 위 코드를은 직접 실행하지 말고, 다음 코드를 실행시켜 주세요! |
|
만일, 위 코드를 통해 직접 딥러닝을 시켭고 싶으시다면, Files의 |
|
train.py를 실행시켜 주세요! |
|
|
|
**5단계. 모델 사용해 보기** |
|
앞선 단계들을 거쳐 완성한 모델이 이 허깅페이스 페이지에 올라가 있습니다. |
|
이제, 이 허깅페이스 페이지의 모델을 불러와서, 직접 사용해 보겠습니다. |
|
Files의 test.py를 실행시켜 보세요!! |
|
직접 준비한 숫자 손글씨를 모델에 입력으로 넣으면, 그 숫자가 어떤 숫자인지 맞춰줄 겁니다!! |
|
코드 실행을 위해서, 이 종속성을 설치해야 합니다. |
|
cmd 창을 열고, 이 코드를 넣고 실행하여 먼저 종속성을 받아주세요 |
|
```bash |
|
pip install tensorflow huggingface_hub Pillow numpy |
|
``` |
|
## 4. 나만의 CNN 모델 만들기 |
|
이제, 성능을 더 끌어올리기 위해, 또 원하는 목적에 맞게 모델을 수정할 차례입니다. |
|
|
|
- **하이퍼파라미터 튜닝** |
|
모델의 성능에 큰 영향을 미치는 batch_size, epochs, 옵티나이저의 leaarning_rate등을 조합하여 최적화 합니다. |
|
다음 코드를 건드려서 수정합니다. |
|
```python |
|
# 예: Adam 옵티마이저의 학습률(learning_rate)을 직접 설정 |
|
optimizer = keras.optimizers.Adam(learning_rate=0.001) |
|
model.compile(loss="categorical_crossentropy", optimizer=optimizer, metrics=["accuracy"]) |
|
``` |
|
|
|
- **모델 구조 변경** |
|
모델의 성능을 위해서, ```Conv2D-MaxPooling2D``` 블록을 더 깊게, ```Conv2D``` 계층의 필터 수를 늘려 더 풍부하게 할 수 있습니다. |
|
```python |
|
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"), |
|
]) |
|
``` |
|
|
|
- **전이학습** |
|
아주 큰 데이터셋으로 학습된 이미 강력한 모델을 가져와, 그 모델이 학습한 이미지 특징추출능력만 가져와 사용하는 방법 입니다. |
|
적은 데이터로도 높은 성능을 낼 수 있습니다. |
|
```python |
|
# 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모델로 돌아오겠습니다 |
|
오늘도 좋은하루 보내세용!! |