전이 학습(Transfer Learning)에 대해 깊이 있게 알아보겠습니다. 특히 이미지 인식 분야에서 강력한 성능을 자랑하는 CNN(Convolution Neural Network)을 기반으로 전이 학습의 다양한 전략을 코드와 함께 분석해 보겠습니다.
전이 학습이란?
전이 학습은 사전에 훈련된 모델이 갖고 있는 지식(가중치)을 그대로 가져와 새로운 문제에 적용함으로써 학습 시간을 단축하고 성능을 향상 시키는 머신러닝 기법입니다.
- 사용 이유 : 대규모 데이터셋(ImageNet)으로 학습된 모델의 광범위한 특징 추출 능력을 활용하여, 더 적은 데이터와 시간으로도 새로운 문제에 대해 높은 성능을 달성하기 위함입니다.
- CNN과의 관계 : CNN은 여러 층을 거치며 저수준(예지, 텍스처)부터 고수준(형태, 객체) 특징까지 계층적으로 학습하기 때문에, 이 사전 학습된 특징 추출기를 다른 시각 과제(이미지 분류, 객체 검출 등)에 재활용하기가 매우 용이합니다.
전이 학습의 종류
Fine-tuning(미세 조정)
Fine-tuning은 사전 훈련된 모델의 전체 또는 일부 계층을 새로운 데이터셋에 맞게 재훈련하여 최적화하는 방식입니다. 이미 학습된 가중치와 패턴을 활용하면서도, 새로운 문제의 특성을 반영하기 위해 미세한 조정 과정을 거치는 것입니다.
이 기법은 학습 자원이 많이 들 수 있지만, 데이터셋 특유의 패턴을 보다 깊이 있게 반영하므로 더 높은 성능을 얻을 가능성이 큽니다.
import numpy as np
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.applications import VGG16
# 가상 데이터셋 생성
num_classes = 10
input_shape = (224, 224, 3)
# 가상 이미지 데이터 생성
x_train = np.random.random((1000, 224, 224, 3))
# size=(1000,)은 1000개의 샘플이 있고, 각 샘플에 대해 하나의 클래스 레이블을 가집니다.
# (1000,)의 형태는 1000개의 요소를 가지는 1차원 배열입니다.
y_train = np.random.randint(num_classes, size=(1000,))
x_test = np.random.random((200, 224, 224, 3))
# size=(200,)은 200개의 샘플이 있고, 각 샘플에 대해 하나의 클래스 레이블을 가집니다.
# (200,)의 형태는 200개의 요소를 가지는 1차원 배열입니다.
y_test = np.random.randint(num_classes, size=(200,))
# 원-핫 인코딩
y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)
# 사전 학습된 모델 불러오기 (include_top=True로 로드하여 최상위 분류 레이어 포함)
base_model = VGG16(weights='imagenet', include_top=True, input_shape=input_shape)
# VGG16 모델 구조 출력
base_model.summary()
- Full Fine Tunning :모델이 가진 잠재 역량을 최대한 활용할 수 있지만, 학습 비용이 크고, 과적합에 주의해야합니다.
- Partial Fine Tuning : 사전 훈련된 모델의 일부 파라미터(예:특정 레이어나 모듈)만 업데이트 하는 방식입니다. 모델 전체를 재학습하지 않으므로 계산량을 절약할 수 있으며 필요한 범위에 집중하여 빠른 성능 개선이 가능합니다.
- 단계적 파인튜닝 : 학습 과정에서 모델의 파라미터를 한번에 전부 업데이트하지 않고, 특정 구간이나 레이어를 순차적으로 미세조정하는 접근법, 학습 안정성을 높이고 과적합 위험을 줄이는 데 도움이 됩니다.
- 하이브리드 방법 : Full Fine Tuning과 Partial Fine Tuning, 또는 여러 전략을 조합하여 적용하는 방식 , 프로젝트 규모, 데이터 양, 성능 요구사항 등 따라 유연하게 구성할 수 있어 효율성과 성능 사이에서 균형을 찾기 좋습니다.
Full Fine Tuning
모델의 모든 레이어를 동결 해제하고 재학습하는 방식입니다.
- 장점 : 모델이 가진 잠재 역량을 최대한 활용하여 새로운 데이터셋의 특징을 가장 깊이 있게 반영할 수 있습니다.
- 단점 : 학습 비용이 크고, 데이터셋이 작으면 과적합(Overfitting)에 매우 취약할 수 있습니다.
| 단계 | 설명 |
| 1. 모델 불러오기 | include_top=False로 VGG16 등 기본 모델의 컨볼루션 베이스만 불러옵니다. |
| 2. 구조 변경 | 새로운 분류 문제에 맞게 새로운 분류 레이어(Dense Layer)를 추가합니다. |
| 3. 레이어 동결 해제 | for layer in model.layers : layer.trainable = True 를 통해 모든 레이어를 학습 가능 상태로 설정합니다. |
| 4. 컴파일 & 학습 | 전체 모델을 새로운 데이터로 학습합니다. |
import numpy as np
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Flatten, Dense, Input
from tensorflow.keras.applications import VGG16
from tensorflow.keras.optimizers import Adam
# 가상 데이터셋 생성
num_classes = 10
input_shape = (224, 224, 3)
x_train = np.random.random((1000, 224, 224, 3))
y_train = np.random.randint(num_classes, size=(1000,))
x_test = np.random.random((200, 224, 224, 3))
y_test = np.random.randint(num_classes, size=(200,))
# 원-핫 인코딩
y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)
# 사전 학습된 모델 불러오기
# weights='imagenet' , ImageNet 데이터셋으로 사전 학습되었음을 의미
# ImageNet은 매우 큰 이미지 데이터셋으로, 다양한 객체 분류 작업에 사용됩니다. 이 데이터셋에는 수백만 개의 이미지가 포함되어 있으며, 1000개의 클래스(예: 개, 고양이, 자동차 등)로 라벨링되어 있습니다.
base_model = VGG16(weights='imagenet', include_top=False, input_shape=input_shape)
# 모델의 구조 변경 (새로운 분류 레이어 추가)
x = Flatten()(base_model.output)# 레이어는 입력을 1차원 벡터로 변환
x = Dense(256, activation='relu')(x) # Dense 레이어는 완전 연결(fully connected) 층을 의미합니다. 여기서는 256개의 뉴런(neuron)을 가진 Dense 레이어를 추가합니다.
x = Dense(num_classes, activation='softmax')(x) # (x)는 생성된 Dense 레이어 객체를 호출하여 이전 레이어(x)의 출력을 입력으로 전달합니다
model = Model(inputs=base_model.input, outputs=x)
# 모든 레이어를 동결 해제
for layer in model.layers:
layer.trainable = True
# 모델 컴파일
model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
# 전체 모델 학습
model.fit(x_train, y_train, epochs=10, batch_size=32, validation_split=0.2)
# 모델 평가
loss, accuracy = model.evaluate(x_test, y_test)
print(f'Test Loss: {loss}')
print(f'Test Accuracy: {accuracy}')
Partial Fine-Tuning(부분 미세 조정)
사전 훈련된 모델의 일부 파라미터(주로 상위 레이어)만 업데이트하는 방식입니다.
- 배경 : 하위 레이어는 일반적인 저수준 특징(에지, 텍스처)을 학습하므로 고정하고, 새로운 문제와 관련된 고수준 특징을 담당하는 상위 레이어만 재학습합니다.
- 장점 : 모델 전체를 재학습하지 않아 계산량을 절약할 수 있으며, 필요한 범위에 집중하여 빠른 성능 개선이 가능합니다.
| 레이어 종류 | 위치 | 역할 | 특징 |
| 하위 레이어 | 입력층 근처 | 기본적인 저수준 특징 추출 | 대부분 동결하여 기존 지식을 보존합니다. |
| 상위 레이어 | 출력층 근처 | 복잡한 고수준 특징 추출 및 분류 | 새로운 과제에 맞게 동결 해제하고 학습합니다. |
| 단계 | 설명 |
| 1. 사전 학습된 모델 불러오기 | weights='imagent' , include_top=False 옵션을 사용하여 대규모 데이터셋으로 학습된 기본 모델의 특징 추출 부분(Convolutional Base)만 불러옵니다. 이렇게 하면 IPmageNet의 1000개 클래스를 분류하는 기존 최상위 분류 레이어는 제외됩니다. |
| 2. 모델의 구조 변경 | 새로운 분류 문제의 클래스 수에 맞게 Flatten Layer 및 새로운 분류 레이어(Dense Layer)를 기존 모델의 출력층(base_model.output) 위에 연결하여 모델을 구성합니다. |
| 3. 하위 레이어 동결 및 상위 레이어 동결 해제 | 하위 레이어(base_model.layers)는 일반적인 특징을 담고 있으므로 layer.trainable = False 로 설정하여 동결합니다. 새롭게 추가된 상위 분류 레이어와 원본 모델의 마지막 몇 개 컨볼루션 볼록만 layer.trainable = True로 설정하여 학습 가능하게 만듭니다. |
| 4. 모델 컴파일 | 동결이 적용된 모델을 Adam과 같은 최적화 도구와 새로운 문제에 맞는 손실 함수(categorial_crossentropy 등) 및 평가지표로 컴파일합니다. 이때, 동결된 레이어의 가중치는 업데이트되지 않습니다. |
| 5. 일부 레이어 학습 | 준비된 새로운 데이터셋으로 모델을 학습(model.fit())시킵니다. 이 과정에서 동결 해제된(trainable=True) 레이어의 가중치만 업데이트되며, 동결된 레이어는 기존 가중치를 유지합니다. |
| 6. 모델 평가 | 테스트 데이터셋을 사용하여 학습된 모델의 손실(Loss)과 정확도(Accuracy)를 평가하여 새로운 문제에 대한 모델의 성능을 최종적으로 확인합니다. |
import numpy as np
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Flatten, Dense, Input
from tensorflow.keras.applications import VGG16
from tensorflow.keras.optimizers import Adam
# 가상 데이터셋 생성
num_classes = 10
input_shape = (224, 224, 3)
x_train = np.random.random((1000, 224, 224, 3))
y_train = np.random.randint(num_classes, size=(1000,))
x_test = np.random.random((200, 224, 224, 3))
y_test = np.random.randint(num_classes, size=(200,))
# 원-핫 인코딩
y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)
# 사전 학습된 모델 불러오기
base_model = VGG16(weights='imagenet', include_top=False, input_shape=input_shape)
# 모델의 구조 변경 (새로운 분류 레이어 추가)
x = Flatten()(base_model.output)
x = Dense(256, activation='relu')(x)
x = Dense(num_classes, activation='softmax')(x)
model = Model(inputs=base_model.input, outputs=x)
# 하위 레이어를 동결하고, 상위 레이어만 동결 해제
for layer in base_model.layers:
layer.trainable = False
for layer in model.layers[-4:]: # 이 리스트의 마지막 네 개의 요소를 선택
layer.trainable = True
# 모델 컴파일
model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
# 일부 레이어 학습
model.fit(x_train, y_train, epochs=10, batch_size=32, validation_split=0.2)
# 모델 평가
loss, accuracy = model.evaluate(x_test, y_test)
print(f'Test Loss: {loss}')
print(f'Test Accuracy: {accuracy}')
단계적 파인튜닝
| 단계 | 설명 | 학습 대상 레이어 |
| 1. 사전 학습된 모델 불러오기 | include_top=False 로 특징 추출기(Convolutional Base)만 불러옵니다. | N/A |
| 2. 모델의 구조 변경 | 새로운 분류 문제의 클래스 수에 맞춰 새로운 분류 레이어(Dense Layer)를 추가하여 전체 모델 구조를 만듭니다. | N/A |
| 3. 기본 레이어를 동결(1단계 시작) | 사전 학습된 기본 레이어(base_model.layer) 전체를 trainable = False로 설정하여 가중치가 업데이트되지 않도록 동결합니다. | 새로운 분류 레이어만 |
| 4. 모델 컴파일(1차) | 동결된 상태로 모델을 컴파일합니다. 일반적으로 상대적으로 높은 학습률(Learning Rate)을 사용합니다. | N/A |
| 5. 상위 레이어학습(1단계 학습) | 새로운 데이터로 모델을 학습시킵니다. 이 단계에서는 새로 추가된 분류 레이어만 빠르게 학습시켜 새로운 문제에 대한 초기 대응 능력을 확보합니다. | 새로운 분류 레이어만 |
| 6. 하위 레이어 동결 히제 및 일부 동결 유지(2단계 준비) | 학습 안정화를 위해 모델의 매우 깊은 하위 레이어는 동결 상태로 유지하고, 나머지 중간 및 상위 레이어를 trainable = True 로 설정하여 동결 해제합니다. | 동결 해제된 중간/상위 레이어 |
| 7. 모델 재컴파일 및 전체 모델 학습(2단계 학습) | 모델을 매우 낮은 학습률로 재컴파일합니다. 낮은 학습률은 기존 사전 학습된 가중치의 급격한 변화를 막고 미세 조정을 가능하게 합니다. 전체 모델을 다시 학습시켜 최종 성능을 끌어올립니다. |
동결 해제된 모든 레이어 |
| 8. 모델 평가 | 최종 테스트 데이터셋으로 학습된 모델의 성능을 평가합니다. | N/A |
import numpy as np
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Flatten, Dense, Input
from tensorflow.keras.applications import VGG16
from tensorflow.keras.optimizers import Adam
# 가상 데이터셋 생성
num_classes = 10
input_shape = (224, 224, 3)
x_train = np.random.random((1000, 224, 224, 3))
y_train = np.random.randint(num_classes, size=(1000,))
x_test = np.random.random((200, 224, 224, 3))
y_test = np.random.randint(num_classes, size=(200,))
# 원-핫 인코딩
y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)
# 사전 학습된 모델 불러오기
base_model = VGG16(weights='imagenet', include_top=False, input_shape=input_shape)
# 모델의 구조 변경 (새로운 분류 레이어 추가)
x = Flatten()(base_model.output)
x = Dense(256, activation='relu')(x)
x = Dense(num_classes, activation='softmax')(x)
model = Model(inputs=base_model.input, outputs=x)
# 기본 레이어를 동결
for layer in base_model.layers:
layer.trainable = False
# 모델 컴파일
model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
# 상위 레이어 학습
model.fit(x_train, y_train, epochs=10, batch_size=32, validation_split=0.2)
# 하위 레이어 동결 해제 및 상위 레이어 일부 동결 유지
for layer in model.layers[:15]: # 모델의 레이어 중 첫 번째 15개의 레이어를 선택
layer.trainable = True
# 모델 재컴파일 및 전체 모델 학습
model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=10, batch_size=32, validation_split=0.2)
# 모델 평가
loss, accuracy = model.evaluate(x_test, y_test)
print(f'Test Loss: {loss}')
print(f'Test Accuracy: {accuracy}')
하이브리드 방법
이 방법은 여러 파인튜닝 방법을 결합하여 사용합니다.
예를 들어, 일부 레이어 파인튜닝과 단계적 파인튜닝을 결합하여 모델을 최적화합니다.
| 단계 | 설명 | 적용된 Fine-tuning 전략 |
| 1. 사전 학습된 모델 불러오기 | 특징 추출기(Convolutional Base)만 불러옵니다 | N/A |
| 2. 모델의 구조 변경 | 새로운 분류 문제에 맞게 새로운 분류 레이어(Dense Layer)를 추가하여 전체 모델을 구성합니다. | N/A |
| 3. 하위 레이어 동결 및 상위 레이어 동결 해제(1단계 준비) | 사전 학습된 하위 레이어를 동결하여 기존 지식을 보호하고, 상위 레이어와 새로운 분류 레이어만 학습 가능하도록 설정합니다. | Partial Fine-tuning |
| 4. 모델 컴파일(1차) | 동결된 상태로 모델을 컴파일합니다. 이때는 비교적 높은 학습률을 사용하여 상위 레이어를 빠르게 학습 준비합니다. | N/A |
| 5. 상위 레이어 학습(1단계 학습) | 새로운 데이터로 모델을 학습시킵니다. 이 단계는 새로운 분류 레이어가 문제에 적합한 예측을 할 수 있도록 가장 빠르게 가중치를 업데이트합니다. | 단계적 Fine-tuning(1단계) |
| 6. 일부 하위 레이어 동결 해제(2단계 준비) | 학습 안정화 후 모델의 가장 깊은 하위 레이어는 유지하고, 중간 레이어 일부의 동결을 풀어 재학습 대상에 포함합니다. | Partial Fine-tuning(1단계) |
| 7. 모델 재컴파일 및 전체 모델 학습(2단계 학습) | 모델을 매우 낮은 학습률로 재컴파일합니다. 이 낮은 학습률은 미세한 가중치 조정(미세 조정)을 유도하며, 동결 해제된 모든 레이어를 학습시켜 최종 성능을 극대화합니다. | 단계적 Fine-tuning(2단계) + Full Fine-tuning(제한적) |
| 8. 모델 평가 | 테스트 데이터셋으로 최종 모델의 성능을 확인합니다. | N/A |
전체 코드
import tensorflow as tf
from tensorflow.keras import layers, models, optimizers
import numpy as np
# 가상 데이터 생성 (CIFAR-10 유사, 32x32 크기의 컬러 이미지, 10개의 클래스)
num_samples = 10000 # 데이터셋 크기
input_channels = 3
input_height = 32
input_width = 32
num_classes = 10
# 가상 이미지 데이터 생성
X = np.random.rand(num_samples, input_height, input_width, input_channels).astype(np.float32)
# 가상 레이블 데이터 생성
y = np.random.randint(num_classes, size=num_samples)
# 데이터셋을 훈련/검증 데이터로 나누기
train_size = int(0.8 * len(X))
val_size = len(X) - train_size
X_train, X_val = X[:train_size], X[train_size:]
y_train, y_val = y[:train_size], y[train_size:]
# 데이터셋을 TensorFlow 데이터셋으로 변환
train_dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train)).shuffle(train_size).batch(32)
val_dataset = tf.data.Dataset.from_tensor_slices((X_val, y_val)).batch(32)
# 사전 훈련된 VGG16 모델 로드
base_model = tf.keras.applications.VGG16(weights='imagenet', include_top=False, input_shape=(32, 32, 3))
# 상위 층 고정
for layer in base_model.layers:
layer.trainable = False
# 출력 층 수정
model = models.Sequential([
base_model,
layers.Flatten(),
layers.Dense(512, activation='relu'),
layers.Dense(num_classes, activation='softmax')
])
# 손실 함수 및 옵티마이저 설정
model.compile(
optimizer=optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# 모델 훈련
model.fit(
train_dataset,
epochs=25,
validation_data=val_dataset
)
# 모델 평가
val_loss, val_accuracy = model.evaluate(val_dataset)
print(f'Validation Accuracy: {val_accuracy:.4f}')
라이브러리 임포트
- tensorflow as tf : TensorFlow의 기본 패키지로, 딥러닝 모델을 구성하고 훈련하는 데 사용됩니다.
- layers : 다양한 신경망 레이어를 포함합니다.
- models : 모델을 구성하는 데 사용됩니다.
- optimizers : 다양한 최적화 알고리즘을 제공합니다.
- numpy : 수치 연산을 위한 파이썬 라이브러리로, 다차원 배열 객체와 다양한 수학 함수를 제공합니다.
import tensorflow as tf
from tensorflow.keras import layers, models, optimizers
import numpy as np
데이터셋 및 데이터로더 준비
- num_samples : 생성할 데이터의 수 입니다.
- input_channels , input_height, input_width : 각각 입력 데이터의 채널 수, 높이, 너비를 정의합니다.
- num_classes : 분류할 클래스의 수입니다.
- X : 랜덤으로 생성된 이미지 데이터입니다.
- y : 랜덤으로 생성된 레이블 데이터입니다.
- tf.data.Dataset : TensorFlow의 데이터셋 API를 사용하여 데이터를 배치 단위로 불러옵니다.
# 가상 데이터 생성 (CIFAR-10 유사, 32x32 크기의 컬러 이미지, 10개의 클래스)
num_samples = 10000 # 데이터셋 크기
input_channels = 3
input_height = 32
input_width = 32
num_classes = 10
# 가상 이미지 데이터 생성
X = np.random.rand(num_samples, input_height, input_width, input_channels).astype(np.float32)
# 가상 레이블 데이터 생성
y = np.random.randint(num_classes, size=num_samples)
# 데이터셋을 훈련/검증 데이터로 나누기
train_size = int(0.8 * len(X))
val_size = len(X) - train_size
X_train, X_val = X[:train_size], X[train_size:]
y_train, y_val = y[:train_size], y[train_size:]
# 데이터셋을 TensorFlow 데이터셋으로 변환
train_dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train)).shuffle(train_size).batch(32)
val_dataset = tf.data.Dataset.from_tensor_slices((X_val, y_val)).batch(32)
사전 훈련된 모델 로드 및 상위 층 고정
- tf.keras.applications.VGG16 : 사전 훈련된 VGG16 모델을 로드합니다.
- weights='imagenet' : ImageNet 데이터셋으로 사전 훈련된 가중치를 사용합니다.
- include_top=False : 마지막 완전 연결 레이어를 제외합니다.
- input_shape : 입력 데이터의 형태를 지정합니다.
- layer.trainable = False : 레이어의 가중치를 고정합니다.
# 사전 훈련된 VGG16 모델 로드
base_model = tf.keras.applications.VGG16(weights='imagenet', include_top=False, input_shape=(32, 32, 3))
# 상위 층 고정
for layer in base_model.layers:
layer.trainable = False
새로운 출력 층 추가(파인 튜닝하는 코드)
- layers.Flatten : 입력 데이터를 1차원으로 펼칩니다.
- layer.Dense : 완전 연결 레이어를 정의합니다.
- activation='relu' : ReLU 활성화 함수를 사용합니다.
- activation='softmax' : 소프트맥스 활성화 함수를 사용하여 클래스 확률을 출력합니다.
# 출력 층 수정
model = models.Sequential([
base_model,
layers.Flatten(),
layers.Dense(512, activation='relu'),
layers.Dense(num_classes, activation='softmax')
])
모델 컴파일
- optimizers.Adam : Adam 옵티마이저를 사용합니다.
- learning_rate : 학습률(learning rate) 입니다.
- loss = 'sparse_categorical_crossentropy' : 다중 클래스 분류 문제에 사용하는 손실 함수입니다.
- metrics=['accuracy'] : 모델 성능 평가 지표로 정확도를 사용합니다.
# 손실 함수 및 옵티마이저 설정
model.compile(
optimizer=optimizers.Adam(learning_rate=0.001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
모델 훈련
- model.fit : 모델을 훈련하는 함수
- train_dataset : 훈련 데이터셋입니다.
- epochs : 훈련할 에포크 수입니다.
- validation_data : 검증 데이터셋입니다.
# 모델 훈련
model.fit(
train_dataset,
epochs=25,
validation_data=val_dataset
)
모델 성능 검사
모델 성능 검사는 훈련이 완료된 모델을 평가하기 위해 중요합니다. 검증 데이터셋을 사용하여 모델의 성능을 측정합니다.
- model.evaluate : 검증 데이터셋에 대한 모델의 손실 및 정확도를 평가합니다.
- val_loss 및 val_accuracy : 검증 데이터셋에 대한 손실과 정확도를 저장합니다.
# 모델 평가
val_loss, val_accuracy = model.evaluate(val_dataset)
print(f'Validation Accuracy: {val_accuracy:.4f}')
Feature Extraction(특징 추출)
- 정의 : 사전 훈련된 모델의 중간 계층 가중치를 그대로 고정(Freeze) 하고, 이 동결된 계층들을 새로운 작업의 특징 추출기(Feature Extractor)로 활용하는 방식입니다.
- 작동 방식
- 중간 레이어 역할 : 모델의 중간 레이어는 이미 대규모 데이터셋(예 : ImageNet)에서 에지, 텍스처, 일반적인 형태 등 데이터의 일반적인 특징을 학습한 상태입니다.
- 특징 벡터 생성 : 새로운 데이터가 이 동결된 모델을 통과하면, 모델은 데이터를 고차원의 고수준 특징 벡터(Feature Vector)로 변환합니다.
- 분류기 학습 : 이 특징 벡터를 입력으로 받아, 새로운 과제에 맞게 설계된 마지막 분류 레이어(Classifier)만 새로 학습시킵니다.
| 구분 | 설명 |
| 리소스 효율성 | 재학습 대상이 마지막 분류 레이어에 한정되므로, 필요한 연산량(Computational Resource)과 시간을 크게 절약할 수 있습니다. |
| 데이터 효율성 | 사전 학습된 모델이 가진 광범위하고 일반화된 특징을 재활용하기 때문에, 데이터셋 규모가 크지 않아도 빠르게 괜찮은 성능을 얻을 수 있습니다. |
| 빠른 성능 확보 | 이미 강력하게 훈련된 특징 추출 능력을 활용하여, 빠른 시간 내에 높은 수준의 특징 벡터를 얻고 최종 예측 성능을 끌어올릴 수 있습니다. |
코드
라이브러리 및 모델 로드
- import tensorflow as tf : TensorFlow 라이브러리 임포트
- import numpy as np : 수치 연산을 위한 NumPy 라이브러리 임포트
- from tensorflow.keras import layers, models : Keras의 레이어 및 모델 관련 모듈 임포트
- base_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, pooling='avg') : ResNet50 모델을 Feature Extractor(특징 추출기)로 로드
- weights='imagenet' : ImageNet 사전 학습 가중치 사용
- include_top=False : 기존 완전(FC) 분류 레이어 제거
- pooling='avg' : Global Average Pooling 적용(출력 자원 축소)
- base_model.trainable = False : 사전 학습된 모델의 가중치를 고정하여 학습되지 않도록 설정
import tensorflow as tf
import numpy as np
from tensorflow.keras import layers, models
# ResNet50 모델 로드 (Feature Extractor)
base_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, pooling='avg')
base_model.trainable = False # 가중치 고정
새로운 Fully Connected Layer 추가
- model = models.Sequential([...]) : Sequential API를 사용하여 새로운 모델 정의
- layers.Input(shape=(224,224,3)) : 입력 레이어
- shape=(224,224, 3) : 224X224 RGB 이미지 입력(3채널)
- base_model : ResNet50 모델을 Feature Extractor로 추가
- layers.Dense(128, activation='relu') : 새로운 Fully Connected Layer 추가
- 128 : 뉴런수
- activation = 'relu' : ReLU 활성화 함수 사용(비선형성 추가 및 학습 안정화)
- layers.Dense(10, activation='softmax') : 출력층 (10개 클래스 분류)
- 10 : 클래스 개수
- activation='softmax' : 다중 클래스 분류를 위한 소프트 맥스 활성화 적용
- model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) : 모델 컴파일 설정
- optimizer='adam' : Adam 옵티마이저 사용(가중치 최적화)
- loss='sparese_categorical_crossentropy' : 정수형 라벨을 사용하는 다중 클래스 분류 손실 함수 적용
- metrics=['accuracy'] : 모델 평가 지표로 정확도(Accuracy)사용
# 새로운 Fully Connected Layer 추가
model = models.Sequential([
layers.Input(shape=(224, 224, 3)), # 입력층 명시
base_model,
layers.Dense(128, activation='relu'), # 새로운 Fully Connected Layer
layers.Dense(10, activation='softmax') # 10개 클래스 분류
])
# 모델 컴파일
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
가상 데이터셋 생성
- num_samples = 100 : 생성할 가상 데이터 샘플 수 설정(100개)
- input_shape = (224,224,3) : 입력 데이터 형태 설정(224x224 RGB 이미지)
- num_classes = 10 : 클래스 개수 설정(10개 클래스 분류 문제)
- X_train = np.random.rand(num_samples, *input_shape).astype(np.float32) : 랜덤한 이미지 생성 (0~1 범위의 실수값을 가지는 배열)
- np.random.rand(num_samples, *input_shape):(100,224,224,3) 크기의 난수 배열 생성
- .astype(np.float32) : 데이터 타입을 float32로 변환(TensorFlow 호환성 유지)
- y_train = np.random.randint(0, num_classes, num_samples) : 랜덤한 정수 레이블 생성(0~9 범위의 정수값)
- np.random.randint(0, num_classes, num_samples) : (100,) 크기의 정수형 레이블 배열 생성
# 가상 데이터셋 생성 (입력 이미지: 224x224 RGB, 클래스: 10개)
num_samples = 100
input_shape = (224, 224, 3)
num_classes = 10
X_train = np.random.rand(num_samples, *input_shape).astype(np.float32) # 난수 이미지 생성
y_train = np.random.randint(0, num_classes, num_samples) # 난수 레이블 생성
모델 학습
- model.fit(X_train, y_train, epochs=5, batch_size=10) : 모델 학습 수행
- X_train : 훈련 데이터 (가상 이미지 데이터)
- y_train : 훈련 데이터의 정답 레이블
- epoch=5 : 총 5 에포크 동안 학습 진행(작은 데이터셋이므로 간단히 진행)
- batch_size=10 : 배치 크기 10으로 설정(한번에 10개 샘플씩 학습)
# 모델 학습 (Epochs 5, 작은 데이터셋이라 간단하게 진행)
model.fit(X_train, y_train, epochs=5, batch_size=10)
모델 평가
- X_test = np.random.rand(20, *input_shape).astype(np.float32) : 랜덤한 테스트 데이터 생성
- (20,224,224,3) : 20개의 224x224 RGB 이미지 생성
- .astype(np.float32) : 데이터 타입을 float32로 변환(Tensorflow 호환성 유지)
- y_test = np.random.randint(0, num_classes, 20) : 랜덤한 테스트 레이블 생성
- np.random.randint(0, num_classes, 20) : 0~9 범위의 정수형 레이블 20개 생성
- loss, accuracy = model.evaluate(X_test, y_test) : 테스트 데이터로 모델 평가 수행
- loss : 모델의 테스트 손실 값 반환
- accuracy : 모델의 테스트 정확도 반환
- print(f"Test Accuracy : {accuracy:.4f}") : 테스트 데이터의 정확도 출력
- print(f"Test Loss : {loss:.4f}") : 테스트 데이터의 손실 값 출력
# 가상의 테스트 데이터 생성 및 평가
X_test = np.random.rand(20, *input_shape).astype(np.float32)
y_test = np.random.randint(0, num_classes, 20)
loss, accuracy = model.evaluate(X_test, y_test)
print(f"Test Accuracy: {accuracy:.4f}")
print(f"Test Loss: {loss:.4f}")
Few-Shot Learning(소수 학습)
| 구분 | 설명 |
| 정의 | 극히 적은 양의 학습 데이터만을 가지고도 모델이 새로운 클래스를 정확하게 인식하고 일반화 능력을 획득하도록 하는 기법입니다. |
| 사용 이유 | 일반적인 딥러닝 모델은 대량의 데이터를 요구하지만, 현실 세계에선ㄴ 희귀 질병 이미지, 특정 환경의 데이터 등 충분한 데이터 확보가 어려운 경우가 많습니다. |
| 핵심 목표 | 소량의 데이터로드 학습을 진행하여, 새로운 클래스를 인식하거나 이전에 접하지 않은 상황에 모델이 잘 대응하도록 만드는 것입니다. |
| 주요 활용 | 의료 영상 진단, 로봇 공학, 희귀 품종 분류 등 데이터 수집 비용이 높거나 데이터가 부족한 분야 |
Zero-Shot Learning(제로샷 학습)
| 구분 | 설명 |
| 정의 | 학습에 한 번도 등장하지 않은 클래스(레이블)에 대해서도 모델이 추가 학습 없이 즉시 예측(분류, 인식)을 수행할 수 있도록 하는 기법입니다. |
| 사용 원리 | 모델이 사전에 개념 정보나 멀티 모달(텍스트, 이미지) 표현을 학습해둡니다. 새로운 클래스가 등장했을 때 모델은 이 클래스의 속성이나 개념적 유사성을 기존 지식과 연결하여 식별합니다. |
| 예시 | 얼룩말 이미지를 직접 학습한 적이 없더라도, 말이라는 기본 지식과 줄무늬라는 속성 정보를 조합하여 얼룩말을 추론할 수 있게 됩니다. |
| 장점 | 데이터가 ㅇ벗는 클래스에 대해서도 추론할 수 있어 확장성이 매우 높으며, 긴급 상황 분류 등에 활용될 수 있습니다. |
| 대표 모델 | DALL-E , GPT-4V 등 자연어의 의미적 표현을 바탕으로 새로운 시각적 개념을 생성하거나 이해하는 멀티 모달 AI에서 핵심적으로 활용됩니다. |
코드
랜덤 데이터 생성 함수
먼저 훈련 데이터(A, B, C 클래스)와 Zero-Shot 데이터(D 클래스)를 만들기 위한 함수를 정의합니다.
- import matplotlib.pylot as plt : 데이터 시각화를 위한 Matplotlib 라이브러리 임포트
- from tensorflow.keras.models import Sequential : Sequential API를 사용하여 모델 정의
- from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Input : CNN 모델 레이어 임포트
- def generage_random_images(num_samples, img_size, num_classes) : 랜덤한 이미지 데이터를 생성하는 함수 정의
- num_samples : 생성할 이미지 개수
- img_size : 이미지 크기(가로, 세로 동일)
- num_classes : 클래스 개수
- x_data = np.random.rand(num_samples, img_size, img_size, 1) :
- 랜덤한 픽셀 값을 가지는 흑백 이미지 데이터 생성(0~1 범위의 실수 값)
- (num_samples, img_size, img_size, 1) : (샘플 개수, 가로, 세로 채널(1) 형태의 데이터 생성
- y_data = np.random.randint(0, num_classes, size=(num_samples,)) :
- 각 이미지에 대해 0부터 num_classes-1 사이의 랜덤한 정수형 클래스 할당
- y_data = tf.keras.utils.to_categorical(y_data, num_classes) :
- 클래스 레이블을 원-핫 인코딩하여 변환
- num_classes : 클래스 개수만큼의 원-핫 벡터로 변환
- return x_data, y_data : 생성된 이미지 데이터(x_data)와 레이블(y_data) 반환
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Input
# 랜덤 데이터 생성 함수
def generate_random_images(num_samples, img_size, num_classes):
x_data = np.random.rand(num_samples, img_size, img_size, 1) # 랜덤 픽셀 값 (0~1)
y_data = np.random.randint(0, num_classes, size=(num_samples,)) # 랜덤 클래스 할당
y_data = tf.keras.utils.to_categorical(y_data, num_classes) # 원-핫 인코딩
return x_data, y_data
훈련 데이터 및 Zero-Shot 데이터 생성
간단한 CNN 모델을 정의하고, 기존 3개 클래스(A, B, C)를 학습시킵니다.
- model = Sequentail([...]) : Sequential API를 사용하여 CNN 모델 정의
- Input(shape=(img_size, img_size, 1)) : 입력 레이어
- (img_size, img_size, 1) : 28x28 크기의 흑백 이미지 입력
- Conv2D(32, (3, 3), activation='relu') : 첫번째 합성곱(Convolution) 레이어
- 32 : 32개의 필터 사용
- (3,3) : 3x3 필터 크기 적용
- activation='relu' : ReLU 활성화 함수 적용
- MaxPooling2D((2,2)) : 2x2 크기의 맥스 풀링(Max Pooling) 적용
- Flatten() : 2D 특징 맵을 1D 벡터로 변환
- Dense(64, activation='relu' : 완전 연결(Dense) 레이어 추가(64개의 뉴런 사용 , ReLU 활성화 함수 적용)
- Dense(num_classes, activation='softmax') : 출력층(Dense) 설정(3개의 클래스 분류 , 다중 클래스 분류를 위한 Softmax 활성화 함수 적용)
- model.compile(optimizer='adam', loss='categorical_crossentropy' , metrics=['accuracy']) :
- optimizer='adam' : Adam 옵티마이저 사용(가중치 최적화)
- loss='categorical_crossentropy' : 다중 클래스 분류 손실 함수 사용(원-핫 인코딩 레이블 대응)
- metrics=['accuracy'] : 모델 평가 지표로 정확도(Accuracy) 사용
- model.fit(x_train, y_train, epochs=10, batch_size=32, verbose=1):
- x_train, y_train : 훈련 데이터 및 레이블 입력
- epochs=10 : 총 10 에포크 동안 학습 진행
- batch_size=32 : 미니배치 크기 32로 설정
- verbose=1 : 학습 과정 출력(훈련 진행 상황 표시)
- Input(shape=(img_size, img_size, 1)) : 입력 레이어
# CNN 모델 정의
model = Sequential([
Input(shape=(img_size, img_size, 1)),
Conv2D(32, (3, 3), activation='relu'),
MaxPooling2D((2, 2)),
Flatten(),
Dense(64, activation='relu'),
Dense(num_classes, activation='softmax') # 기존 3개 클래스(A, B, C) 학습
])
# 모델 컴파일 및 학습
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, epochs=10, batch_size=32, verbose=1)
Zero-Shot 데이터 예측 및 결과 확인
학습되지 않은 새로운 클래스(D)에 대해 Zero-shot 방식으로 예측을 수행합니다.
- predictions = model.predict(x_test) : 모델이 학습되지 않은 새로운 클래스(D)에 대한 예측 수행
- x_test : Zero-Shot 테스트 데이터(클래스 D)
- 출력 : 각 클래스 (A, B, C)에 대한 확률 값이 담긴 배열
- predicted_classes = np.argmax(predictions, axis =1) : 가장 높은 확률을 가진 클래스 인덱스 선택
- np.argmax(predictions, axis=1) : 각 샘플에서 가장 높은 확률을 가지는 인덱스 반환
- print(f"Zero-Shot 데이터 예측 결과(기존 클래스 중 가장 가까운 것 선택) : {predickted_classes}") : Zero-Shot 데이터(클래스D)에 대해 모델이 예측한 결과 출력
- 학습되지 않은 D 클래스를 기존 A, B, C 중 가장 가까운 클래스로 분류
- 결과적으로 모델이 D를 학습하지 않았음에도 유사한 클래스를 선택하여 예측하는 방식으로 Zero-Shot 학습을 테스트함
# Zero-Shot 데이터 예측 (클래스 D)
predictions = model.predict(x_test)
# 예측된 클래스 출력
predicted_classes = np.argmax(predictions, axis=1) # 가장 높은 확률의 클래스 선택
print(f"Zero-Shot 데이터 예측 결과(기존 클래스 중 가장 가까운 것 선택): {predicted_classes}")
Zero-Shot 데이터 샘플 시각화 및 확률 값 확인
Zero-Shot 예측 결과를 시각적으로 확인하고, 각 샘플의 softmax 확률값도 출력하여 모델이 얼마나 확신을 가지고 예측하는지 확인합니다.
- fig, axes = plt.subplots(1, 5, figsize=(12, 3)) : 1 행 5열의 서브 플롯을 생성(5개 샘플 시각화)
- figsize=(12,3) : 그래프 크기 설정
- for i, ax in enumerate(axes): 각 샘플에 대한 반복
- ax.imshow(x_test[i].reshape(img_size, img_size), cmap='gray') : Zero-Shot 테스트 데이터(D 클래스) 시각화
- reshape(img_size, img_size) : (28x28) 크기로 변환(흑백 이미지)
- cmap='gray' : 흑백 컬러맵 적용
- ax.set_title(f"Pred: {predicted_classes[i]}") : 예측된 클래스(A, B, C 중 하나) 제목 추가
- ax.axis('off') : 축 제거
- plt.show() : 그래프 출력
- for i in range(len(x_test)): Zero-Shot 데이터에 대해 확률 값 출력 반복
- print(f"샘플 {i+1}: 예측 확률 (A, B, C): {predictions[i]}") : 각 샘플별 기존 클래스(A, B, C) 중 예측 확률 출력
- predictions[i] : 해당 샘플의 Softmax 확률 값(A, B, C 클래스 중 어느 확률이 높은지 확인 가능)
- print(f"샘플 {i+1}: 예측 확률 (A, B, C): {predictions[i]}") : 각 샘플별 기존 클래스(A, B, C) 중 예측 확률 출력
# 몇 개 샘플 시각화 및 확률 확인
fig, axes = plt.subplots(1, 5, figsize=(12, 3))
for i, ax in enumerate(axes):
ax.imshow(x_test[i].reshape(img_size, img_size), cmap='gray')
ax.set_title(f"Pred: {predicted_classes[i]}")
ax.axis('off')
plt.show()
# Softmax 확률 출력 (각 샘플별 기존 클래스 A, B, C 중 어느 확률이 높은지)
for i in range(len(x_test)):
print(f"샘플 {i+1}: 예측 확률 (A, B, C): {predictions[i]}")'AI > 인공지능(CNN)' 카테고리의 다른 글
| [인공지능] 머신 러닝의 핵심 열쇠 : 베이즈 정리(Bayes' Theorem) (0) | 2025.11.01 |
|---|---|
| [인공지능] 잔차 신경망(ResNet)으로 기울기 소실 문제 해결 (0) | 2025.10.30 |
| [인공지능] 딥러닝 객체 검출의 혁신, Yolo(You Only Look Once) (0) | 2025.10.29 |
| [인공지능] 딥러닝 핵심 : CNN(Convolution Neural Network) (0) | 2025.10.28 |
| [인공지능] 인공 신경망(Artificial Neural Network, ANN) (0) | 2025.10.28 |