--- 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모델로 돌아오겠습니다 오늘도 좋은하루 보내세용!!