안녕하세요, 미래를 만드는 AI 개발자 여러분! ✨
복잡한 수학 공식과 이론에 머리가 지끈거렸던 경험, 한 번쯤은 있으시죠? 머신러닝과 딥러닝은 분명 흥미롭지만, 막상 실전에 뛰어들려고 하면 어디서부터 시작해야 할지 막막하게 느껴질 때가 많습니다. 하지만 걱정 마세요! 오늘 이 글에서는 텐서플로우(TensorFlow)라는 강력한 도구를 활용하여, 머신러닝의 핵심 개념들을 직접 코드로 구현해보는 실전 예제들을 통해 여러분의 아이디어를 현실로 만드는 첫걸음을 함께 내디뎌 볼 겁니다. 🚀
이 글의 목표는 단순히 코드를 따라 치는 것을 넘어, 각 예제가 어떤 문제를 해결하고 어떤 원리로 작동하는지 명확하게 이해하는 것입니다. 준비되셨나요? 그럼 지금부터 텐서플로우와 함께 머신러닝의 세계로 깊이 빠져들어가 봅시다! 💪
1. 텐서플로우(TensorFlow), 간단히 알아볼까요? 🧠🛠️
텐서플로우는 구글이 개발한 오픈소스 머신러닝 라이브러리입니다. 이름 그대로 ‘텐서(Tensor)’라는 다차원 배열을 ‘흐르게(Flow)’ 하여 다양한 계산 그래프를 구축하고, 이를 통해 복잡한 뉴럴 네트워크 모델을 만들고 학습시킬 수 있게 해줍니다.
왜 텐서플로우일까요?
- 유연성: 다양한 종류의 모델을 구축할 수 있습니다.
- 확장성: 작은 프로젝트부터 대규모 상업용 시스템까지 확장 가능합니다.
- 강력한 생태계: Keras와 같은 고수준 API를 통해 쉽고 빠르게 모델을 만들 수 있으며, 풍부한 문서와 커뮤니티 지원을 받을 수 있습니다.
- 배포 용이성: 학습된 모델을 다양한 플랫폼(모바일, 웹, 엣지 디바이스 등)에 쉽게 배포할 수 있습니다.
이 글에서는 주로 tf.keras를 사용하여 예제를 진행할 예정입니다. Keras는 텐서플로우 위에 구축된 고수준 API로, 복잡한 딥러닝 모델도 몇 줄의 코드로 구현할 수 있게 해주는 매우 편리한 도구입니다.
2. 왜 실전 예제가 중요할까요? 💪💡
이론은 중요합니다. 하지만 이론만으로는 부족합니다. 머신러닝 프로젝트는 데이터를 다루고, 모델을 구축하고, 훈련시키고, 평가하는 일련의 과정 속에서 수많은 문제에 직면하게 됩니다. 이때 실전 예제를 통해 얻는 경험은 다음과 같은 이점을 제공합니다:
- 개념의 구체화: 추상적인 이론이 실제 코드와 어떻게 연결되는지 명확히 이해하게 됩니다.
- 문제 해결 능력 향상: 에러 메시지를 만나고, 디버깅하며 문제를 해결하는 과정에서 실력이 크게 향상됩니다.
- 자신감 고취: 내 손으로 직접 AI 모델을 만들어 작동시키는 경험은 큰 성취감을 선사합니다.
- 실무 능력 향상: 실제 프로젝트에 필요한 데이터 전처리, 모델 선택, 하이퍼파라미터 튜닝 등의 감각을 익힐 수 있습니다.
자, 이제 이론을 잠시 내려놓고 직접 코드를 만져보며 머신러닝을 체득해 봅시다!
3. 본격 실전 예제 속으로! 🚀
각 예제는 기본적인 머신러닝 작업 흐름(데이터 준비, 모델 구축, 학습, 평가, 예측)을 따릅니다. 간결하면서도 핵심을 담은 코드로 설명하며, 전체적인 흐름을 이해하는 데 집중하겠습니다.
예제 1: 선형 회귀 (Linear Regression) – 가장 기본적인 예측 모델 📈🏠
선형 회귀는 가장 간단하면서도 강력한 예측 모델입니다. 주어진 입력(독립 변수)에 따라 연속적인 출력(종속 변수)을 예측할 때 사용됩니다. 예를 들어, 집의 크기에 따라 가격을 예측하는 시나리오를 생각해 볼 수 있습니다.
문제 정의: 집의 크기(평방미터)에 따라 집 가격을 예측하는 모델을 만들어봅시다.
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
print(f"텐서플로우 버전: {tf.__version__}")
# 1. 데이터 준비 (가상의 데이터 생성)
# 실제 데이터는 더 복잡하지만, 개념 설명을 위해 단순화합니다.
house_size = np.array([50, 60, 70, 80, 90, 100, 110, 120, 130, 140], dtype=float) # 평방미터
house_price = np.array([150, 180, 210, 240, 270, 300, 330, 360, 390, 420], dtype=float) # 억 원 (예시)
# 데이터에 약간의 노이즈 추가하여 실제와 유사하게 만듭니다.
house_price_noisy = house_price + np.random.randn(10) * 10
print(f"집 크기 데이터: {house_size}")
print(f"집 가격 데이터: {house_price_noisy}")
# 시각화하여 데이터의 선형 관계 확인
plt.figure(figsize=(8, 6))
plt.scatter(house_size, house_price_noisy, label='실제 집 가격')
plt.xlabel('집 크기 (평방미터)')
plt.ylabel('집 가격 (억 원)')
plt.title('집 크기와 가격 관계 (가상 데이터)')
plt.legend()
plt.grid(True)
plt.show()
# 2. 모델 구축 (Keras Sequential API 사용)
# 선형 회귀는 입력층 1개, 출력층 1개로 구성된 가장 단순한 신경망입니다.
# units=1은 출력이 하나임을 의미합니다.
model = tf.keras.Sequential([
tf.keras.layers.Dense(units=1, input_shape=[1])
])
# 3. 모델 컴파일
# optimizer: 'adam'은 학습률을 자동으로 조절하여 효율적인 학습을 돕습니다.
# loss: 'mse' (Mean Squared Error)는 예측값과 실제값의 차이(오차)를 제곱하여 평균 낸 값입니다.
# 회귀 문제에서 가장 많이 사용되는 손실 함수입니다.
model.compile(optimizer='adam', loss='mse')
# 모델 요약 정보 출력
model.summary()
# 4. 모델 학습 (훈련)
# epochs: 전체 데이터셋을 몇 번 반복하여 학습할 것인지 지정합니다.
print("\n모델 학습 시작...")
history = model.fit(house_size, house_price_noisy, epochs=500, verbose=0) # verbose=0 학습 과정 출력 안 함
print("모델 학습 완료!")
# 학습 과정 시각화 (Loss 변화)
plt.figure(figsize=(8, 6))
plt.plot(history.history['loss'])
plt.title('모델 학습 손실 (Loss) 변화')
plt.xlabel('에포크 (Epoch)')
plt.ylabel('손실 (Loss)')
plt.grid(True)
plt.show()
# 5. 모델 평가 및 예측
# 학습된 모델을 사용하여 새로운 집 크기에 대한 가격을 예측합니다.
new_house_size = np.array([95.0, 150.0])
predicted_price = model.predict(new_house_size)
print(f"\n새로운 집 크기 {new_house_size[0]} 평방미터의 예측 가격: {predicted_price[0][0]:.2f} 억 원")
print(f"새로운 집 크기 {new_house_size[1]} 평방미터의 예측 가격: {predicted_price[1][0]:.2f} 억 원")
# 학습된 선형 모델 시각화
plt.figure(figsize=(8, 6))
plt.scatter(house_size, house_price_noisy, label='실제 집 가격')
plt.plot(house_size, model.predict(house_size), color='red', label='예측 선형 모델')
plt.scatter(new_house_size, predicted_price, color='green', marker='X', s=100, label='새로운 예측 지점')
plt.xlabel('집 크기 (평방미터)')
plt.ylabel('집 가격 (억 원)')
plt.title('집 크기 및 가격 예측 (선형 회귀)')
plt.legend()
plt.grid(True)
plt.show()
# 모델의 가중치(weight)와 편향(bias) 확인
print(f"\n모델의 가중치 (기울기): {model.get_weights()[0][0][0]:.2f}")
print(f"모델의 편향 (y절편): {model.get_weights()[1][0]:.2f}")
설명:
- 데이터 준비:
numpy
를 사용하여 가상의 집 크기와 가격 데이터를 만들었습니다. 실제 데이터를 사용할 때는pandas
라이브러리 등으로 CSV 파일을 읽어오는 경우가 많습니다. - 모델 구축:
tf.keras.Sequential
을 사용하여 가장 간단한 신경망인Dense
레이어를 추가했습니다.Dense
레이어는 각 입력 노드가 모든 출력 노드에 연결된 완전 연결(fully connected) 레이어를 의미합니다.input_shape=[1]
은 입력이 하나의 특성(집 크기)임을 나타냅니다. - 모델 컴파일:
model.compile()
단계에서 옵티마이저(‘adam’)와 손실 함수(‘mse’)를 설정합니다. 옵티마이저는 모델이 학습하는 방식을, 손실 함수는 모델의 예측이 얼마나 틀렸는지를 계산하는 방식을 정의합니다. - 모델 학습:
model.fit()
을 사용하여 데이터를 모델에 ‘학습’시킵니다.epochs
는 전체 데이터셋을 몇 번 반복하여 모델을 업데이트할지 지정합니다. 에포크를 늘릴수록 모델은 데이터를 더 많이 보며 학습하지만, 과적합(overfitting)의 위험도 있습니다. - 예측:
model.predict()
를 사용하여 학습된 모델로 새로운 데이터에 대한 예측을 수행합니다.
이 예제를 통해 텐서플로우로 선형 회귀 모델을 구축하고 학습시키는 기본적인 파이프라인을 이해하셨기를 바랍니다.
예제 2: MNIST 숫자 이미지 분류 (Classification) – AI의 Hello World! ✍️🔢🎯
MNIST 데이터셋은 손으로 쓴 0부터 9까지의 숫자 이미지로 구성되어 있으며, 딥러닝 모델의 성능을 테스트하는 데 가장 흔히 사용되는 데이터셋입니다. 이미지 분류는 머신러닝의 대표적인 분류(Classification) 문제 중 하나입니다.
문제 정의: 손으로 쓴 숫자 이미지(28×28 픽셀)를 보고 어떤 숫자인지(0~9) 정확하게 분류하는 모델을 만들어봅시다.
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 1. 데이터 로드 및 전처리
# MNIST 데이터셋은 텐서플로우에 내장되어 있어 쉽게 로드할 수 있습니다.
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 이미지 픽셀 값을 0-255 범위에서 0-1 범위로 정규화합니다.
# 이는 신경망 학습 시 안정성과 성능 향상에 도움을 줍니다.
x_train, x_test = x_train / 255.0, x_test / 255.0
print(f"훈련 이미지 개수: {len(x_train)}")
print(f"테스트 이미지 개수: {len(x_test)}")
print(f"훈련 이미지 형태: {x_train.shape}") # (60000, 28, 28)
print(f"훈련 라벨 형태: {y_train.shape}") # (60000,)
# 몇 가지 이미지 시각화하여 데이터 확인
plt.figure(figsize=(10, 4))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(x_train[i], cmap='gray')
plt.title(f"라벨: {y_train[i]}")
plt.axis('off')
plt.suptitle('MNIST 훈련 데이터 샘플', fontsize=16)
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
# 2. 모델 구축
# Sequential API를 사용하여 모델을 쌓아 올립니다.
model = tf.keras.Sequential([
# Flatten: 28x28 2차원 이미지를 784(28*28)개의 1차원 벡터로 평탄화합니다.
tf.keras.layers.Flatten(input_shape=(28, 28)),
# Dense: 완전 연결 레이어. 128개의 뉴런을 가집니다. 활성화 함수로 ReLU 사용.
tf.keras.layers.Dense(128, activation='relu'),
# Dropout: 과적합 방지를 위해 훈련 중 무작위로 일부 뉴런을 비활성화합니다.
tf.keras.layers.Dropout(0.2),
# Dense: 출력 레이어. 10개의 뉴런 (0~9 숫자). 활성화 함수로 softmax 사용.
# softmax는 각 클래스에 속할 확률을 출력하여, 총합이 1이 되도록 만듭니다.
tf.keras.layers.Dense(10, activation='softmax')
])
# 모델 요약 정보 출력
model.summary()
# 3. 모델 컴파일
# optimizer: 'adam'
# loss: 'sparse_categorical_crossentropy'는 라벨이 정수형일 때 사용됩니다.
# (예: 0, 1, 2... 와 같이 One-hot 인코딩 되지 않은 경우)
# metrics: 'accuracy'는 모델의 성능을 정확도로 측정합니다.
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 4. 모델 학습
print("\n모델 학습 시작...")
history = model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))
print("모델 학습 완료!")
# 학습 과정 시각화 (정확도 및 손실 변화)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='훈련 정확도')
plt.plot(history.history['val_accuracy'], label='검증 정확도')
plt.title('훈련 및 검증 정확도')
plt.xlabel('에포크')
plt.ylabel('정확도')
plt.legend()
plt.grid(True)
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='훈련 손실')
plt.plot(history.history['val_loss'], label='검증 손실')
plt.title('훈련 및 검증 손실')
plt.xlabel('에포크')
plt.ylabel('손실')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# 5. 모델 평가
# 테스트 데이터셋으로 모델의 성능을 최종 평가합니다.
print("\n모델 평가 시작...")
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2) # verbose=2는 진행 막대와 결과만 출력
print(f"테스트 정확도: {test_acc*100:.2f}%")
print(f"테스트 손실: {test_loss:.4f}")
# 6. 예측 및 결과 시각화
# 몇 가지 테스트 이미지에 대해 예측을 수행합니다.
predictions = model.predict(x_test)
plt.figure(figsize=(12, 6))
for i in range(10): # 10개의 샘플만 시각화
plt.subplot(2, 5, i + 1)
plt.imshow(x_test[i], cmap='gray')
predicted_label = np.argmax(predictions[i]) # 가장 높은 확률을 가진 클래스 선택
true_label = y_test[i]
color = 'green' if predicted_label == true_label else 'red'
plt.title(f"예측: {predicted_label}\n실제: {true_label}", color=color)
plt.axis('off')
plt.suptitle('MNIST 예측 결과 (초록: 정답, 빨강: 오답)', fontsize=16)
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
# 잘못 예측한 이미지 하나 예시
misclassified_idx = np.where(np.argmax(predictions, axis=1) != y_test)[0]
if len(misclassified_idx) > 0:
sample_idx = misclassified_idx[0]
plt.figure(figsize=(4, 4))
plt.imshow(x_test[sample_idx], cmap='gray')
predicted_label = np.argmax(predictions[sample_idx])
true_label = y_test[sample_idx]
plt.title(f"잘못된 예측: {predicted_label}, 실제: {true_label}", color='red')
plt.axis('off')
plt.show()
설명:
- 데이터 로드 및 전처리: MNIST 데이터는
tf.keras.datasets
에 내장되어 있어 쉽게 불러올 수 있습니다. 이미지 픽셀 값을 0-255에서 0-1 사이로 정규화하는 것은 딥러닝 학습에서 매우 중요합니다. - 모델 구축:
Flatten
레이어는 2차원 이미지 데이터를 1차원 벡터로 변환하여Dense
레이어에 전달할 수 있도록 합니다.Dense
레이어는 일반적인 신경망 레이어입니다.activation='relu'
는 비선형성을 추가하여 모델이 복잡한 패턴을 학습할 수 있게 합니다.Dropout
은 과적합을 방지하는 기법입니다. 훈련 시 무작위로 일부 뉴런을 비활성화하여 모델이 특정 뉴런에만 의존하지 않도록 합니다.- 마지막
Dense
레이어는 분류할 클래스의 수(여기서는 10개)와 동일한units
를 가집니다.activation='softmax'
는 각 클래스에 대한 확률 분포를 출력하여 가장 높은 확률을 가진 클래스를 최종 예측으로 선택할 수 있게 합니다.
- 모델 컴파일: 분류 문제에서는
loss='sparse_categorical_crossentropy'
(라벨이 정수일 경우) 또는categorical_crossentropy
(라벨이 One-hot 인코딩일 경우)를 사용합니다.metrics=['accuracy']
를 통해 훈련 중 정확도를 모니터링합니다. - 모델 학습:
validation_data
인자를 사용하여 훈련 중에 모델의 검증 성능을 함께 확인할 수 있습니다. 이는 과적합 여부를 판단하는 데 도움이 됩니다. - 모델 평가 및 예측:
model.evaluate()
는 모델의 최종 성능을 측정하고,model.predict()
는 새로운 입력에 대한 예측 값을 반환합니다.
이 예제를 통해 이미지 분류라는 대표적인 머신러닝 문제를 텐서플로우로 어떻게 해결하는지 이해하셨을 겁니다.
예제 3: 전이 학습(Transfer Learning)을 이용한 이미지 분류 – 빠르고 똑똑하게! 🖼️🐾💡
전이 학습은 이미 대규모 데이터셋(예: ImageNet)으로 훈련된 강력한 신경망 모델(예: ResNet, VGG, MobileNet 등)의 지식을 새로운, 작은 데이터셋에 전이하여 사용하는 기법입니다. 처음부터 모델을 학습시키는 것보다 훨씬 적은 데이터와 시간으로도 높은 성능을 얻을 수 있다는 장점이 있습니다.
문제 정의: 직접 데이터를 모아 모델을 훈련시키기에는 데이터가 부족할 때, 고양이와 강아지 이미지를 분류하는 모델을 만들어봅시다. (실제 데이터셋 대신 가상의 시나리오로 설명합니다)
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
print(f"텐서플로우 버전: {tf.__version__}")
# 1. 데이터 준비 (가상의 고양이 vs 강아지 데이터)
# 실제 전이 학습에서는 ImageNet과 같은 큰 데이터셋으로 훈련된 모델을 가져옵니다.
# 여기서는 개념 이해를 위해 작은 이미지 데이터셋을 가정합니다.
# tf.keras.utils.get_file 등으로 실제 데이터를 다운로드할 수 있습니다.
# 예: Cats vs Dogs Dataset - https://www.kaggle.com/c/dogs-vs-cats/data
# 편의상 tf.keras.applications의 예시 이미지 전처리 함수를 사용합니다.
# MobileNetV2는 224x224 크기의 이미지를 기대합니다.
IMG_HEIGHT = 224
IMG_WIDTH = 224
IMG_SHAPE = (IMG_HEIGHT, IMG_WIDTH, 3) # 컬러 이미지 (RGB)
# 가상의 이미지 데이터 (실제로는 이미지 파일을 로드하고 전처리해야 함)
# 예를 들어, 100장의 고양이 이미지와 100장의 강아지 이미지가 있다고 가정
num_cats = 100
num_dogs = 100
total_images = num_cats + num_dogs
# 훈련 데이터셋 (랜덤 노이즈 이미지로 대체)
# 실제 데이터셋이라면 ImageDataGenerator.flow_from_directory()를 사용합니다.
x_train = np.random.rand(total_images, IMG_HEIGHT, IMG_WIDTH, 3).astype(np.float32)
y_train = np.array([0] * num_cats + [1] * num_dogs) # 0: 고양이, 1: 강아지
np.random.shuffle(y_train) # 라벨 섞기
# 데이터를 전처리 (MobileNetV2에 맞는 전처리 함수 사용)
x_train = tf.keras.applications.mobilenet_v2.preprocess_input(x_train * 255.0) # 0-255 스케일로 변환 후 전처리
print(f"훈련 데이터 형태: {x_train.shape}")
print(f"훈련 라벨 형태: {y_train.shape}")
# 2. 사전 훈련된 모델 불러오기 (특성 추출기 역할)
# include_top=False: 모델의 상단 분류 레이어를 포함하지 않고, 합성곱(Convolution) 부분만 가져옵니다.
# weights='imagenet': ImageNet 데이터셋으로 훈련된 가중치를 사용합니다.
# input_shape: 입력 이미지의 크기입니다.
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
include_top=False,
weights='imagenet')
# 베이스 모델의 가중치를 동결(freeze)합니다.
# 이렇게 하면 학습 시 베이스 모델의 가중치가 업데이트되지 않고, 오직 새로 추가된 레이어만 학습됩니다.
base_model.trainable = False
# 베이스 모델 요약
base_model.summary()
# 3. 새로운 분류 레이어 추가
# 베이스 모델의 출력 위에 우리의 특정 작업에 맞는 레이어를 추가합니다.
# GlobalAveragePooling2D: 합성곱 레이어의 특징 맵을 단일 벡터로 평균 풀링하여 차원을 줄입니다.
# Dense: 최종 분류를 위한 완전 연결 레이어. 클래스 수(고양이/강아지 = 2개)만큼 뉴런을 가집니다.
# 이진 분류이므로 sigmoid 활성화 함수를 사용합니다.
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid') # 0 또는 1 (고양이 또는 강아지)
# 베이스 모델과 새로운 레이어를 연결하여 최종 모델 구축
model = tf.keras.Sequential([
base_model,
global_average_layer,
prediction_layer
])
# 모델 요약
model.summary()
# 4. 모델 컴파일
# 이진 분류이므로 'binary_crossentropy' 손실 함수를 사용합니다.
# metrics는 'accuracy'로 설정합니다.
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(),
metrics=['accuracy'])
# 5. 모델 학습 (훈련)
# 실제 데이터를 사용할 경우, ImageDataGenerator를 사용하여 데이터를 증강하고 효율적으로 로드합니다.
# 여기서는 가상 데이터이므로 직접 fit()을 사용합니다.
initial_epochs = 10
print("\n모델 학습 시작 (전이 학습)...")
history = model.fit(x_train, y_train, epochs=initial_epochs)
print("모델 학습 완료!")
# 6. 모델 평가 및 예측
# 테스트 데이터셋으로 모델 평가 (가상의 테스트 데이터 생성)
x_test = np.random.rand(20, IMG_HEIGHT, IMG_WIDTH, 3).astype(np.float32)
x_test = tf.keras.applications.mobilenet_v2.preprocess_input(x_test * 255.0)
y_test = np.array([0]*10 + [1]*10) # 10개 고양이, 10개 강아지
np.random.shuffle(y_test)
loss, accuracy = model.evaluate(x_test, y_test)
print(f"\n테스트 정확도: {accuracy*100:.2f}%")
print(f"테스트 손실: {loss:.4f}")
# 예측 예시
sample_image = np.random.rand(1, IMG_HEIGHT, IMG_WIDTH, 3).astype(np.float32)
sample_image = tf.keras.applications.mobilenet_v2.preprocess_input(sample_image * 255.0)
prediction = model.predict(sample_image)[0][0]
if prediction > 0.5:
print(f"이 이미지는 강아지일 확률이 높습니다. (확률: {prediction*100:.2f}%)")
else:
print(f"이 이미지는 고양이일 확률이 높습니다. (확률: {(1-prediction)*100:.2f}%)")
# 추가: 미세 조정 (Fine-tuning)
# 베이스 모델의 상위 레이어 일부를 동결 해제하고 더 낮은 학습률로 다시 학습시켜 성능을 더 높일 수 있습니다.
# base_model.trainable = True
# for layer in base_model.layers[:-some_number_of_layers]: # 예를 들어 마지막 100개 레이어는 풀지 않고 나머지 풀기
# layer.trainable = False
# model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate/10),
# loss=tf.keras.losses.BinaryCrossentropy(),
# metrics=['accuracy'])
# fine_tune_epochs = 10
# total_epochs = initial_epochs + fine_tune_epochs
# history_fine = model.fit(x_train, y_train, epochs=total_epochs, initial_epoch=history.epoch[-1])
설명:
- 사전 훈련된 모델 불러오기:
tf.keras.applications.MobileNetV2
를 사용하여 ImageNet 데이터셋으로 훈련된 MobileNetV2 모델을 가져옵니다.include_top=False
는 분류를 담당하는 최상위 레이어를 제외하고 특징 추출기 부분만 가져오겠다는 의미입니다. - 베이스 모델 동결:
base_model.trainable = False
를 통해 불러온 베이스 모델의 가중치를 동결합니다. 이는 학습 시 기존 가중치가 변경되지 않고, 새로 추가된 레이어의 가중치만 학습되도록 합니다. - 새로운 분류 레이어 추가: 베이스 모델의 출력 위에
GlobalAveragePooling2D
레이어를 추가하여 특징 맵의 차원을 줄이고, 마지막으로Dense
레이어를 추가하여 우리가 분류하고자 하는 클래스 수(여기서는 2개: 고양이, 강아지)에 맞게 최종 예측을 수행합니다.sigmoid
는 이진 분류에서 0과 1 사이의 확률을 출력하는 데 사용됩니다. - 모델 컴파일 및 학습: 이진 분류이므로
loss='binary_crossentropy'
를 사용하고,Adam
옵티마이저와 낮은 학습률을 사용합니다. - 미세 조정 (Fine-tuning) (선택 사항): 더 나은 성능을 위해 베이스 모델의 일부 상위 레이어의 동결을 해제하고, 더 낮은 학습률로 다시 훈련시키는 ‘미세 조정’을 수행할 수도 있습니다. 이는 모델이 새로운 데이터셋의 특정 특징에 더 잘 적응하도록 돕습니다.
전이 학습은 특히 데이터가 부족하거나, 모델 학습에 많은 시간이 소요될 때 매우 유용하게 활용될 수 있는 강력한 기법입니다.
예제 4: 텍스트 생성 (Text Generation) 또는 감성 분석 (Sentiment Analysis) – NLP의 맛보기! 🗣️💬✨
텐서플로우는 이미지뿐만 아니라 자연어 처리(Natural Language Processing, NLP) 분야에서도 강력한 성능을 발휘합니다. 텍스트 생성이나 감성 분석은 그 중 대표적인 예시입니다. 여기서는 감성 분석의 간략한 개념을 짚어봅니다.
문제 정의 (감성 분석): 특정 문장이 긍정적인지, 부정적인지 또는 중립적인 감성을 담고 있는지 분류하는 모델을 만들어봅시다. (IMDB 영화 리뷰 데이터셋 예시)
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# 1. 데이터 로드 및 전처리 (IMDB 영화 리뷰 데이터셋)
# 텐서플로우에 내장된 IMDB 데이터셋은 이미 정수 인코딩되어 있습니다.
# num_words: 가장 자주 나타나는 단어 10000개만 사용합니다.
imdb = tf.keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
print(f"훈련 리뷰 개수: {len(train_data)}")
print(f"훈련 라벨 개수: {len(train_labels)}")
print(f"첫 번째 훈련 리뷰: {train_data[0]}") # 숫자로 인코딩된 단어들
print(f"첫 번째 훈련 라벨: {train_labels[0]}") # 0: 부정, 1: 긍정
# 리뷰 길이 확인 (리뷰마다 길이가 다름)
print(f"최대 리뷰 길이: {max(len(review) for review in train_data)}")
print(f"최소 리뷰 길이: {min(len(review) for review in train_data)}")
# 모든 리뷰의 길이를 동일하게 맞춥니다 (패딩).
# maxlen: 모든 리뷰를 256 길이로 맞춥니다.
# padding='post': 리뷰 뒤에 0을 채웁니다.
train_data = tf.keras.preprocessing.sequence.pad_sequences(train_data,
value=0, # 0은 패딩 값으로 사용 (사전에 없는 단어)
padding='post',
maxlen=256)
test_data = tf.keras.preprocessing.sequence.pad_sequences(test_data,
value=0,
padding='post',
maxlen=256)
print(f"패딩 후 첫 번째 훈련 리뷰: {train_data[0]}") # 길이가 256으로 맞춰짐
print(f"패딩 후 첫 번째 훈련 리뷰 길이: {len(train_data[0])}")
# 2. 모델 구축 (텍스트 분류를 위한 임베딩 + LSTM/GRU)
# Vocab size: 사용된 단어의 총 개수
vocab_size = 10000
model = tf.keras.Sequential([
# Embedding: 단어들을 밀집 벡터로 변환합니다.
# input_dim: 사전의 크기 (사용할 단어의 개수)
# output_dim: 임베딩 벡터의 차원 (각 단어를 표현할 벡터의 길이)
# input_length: 입력 시퀀스의 길이 (패딩 후 256)
tf.keras.layers.Embedding(vocab_size, 16, input_length=256),
# GlobalAveragePooling1D: 각 시퀀스(리뷰)의 임베딩된 단어들을 평균하여 단일 벡터로 만듭니다.
# RNN 계열 (LSTM, GRU) 대신 간단하게 평균하여 사용해봅니다.
tf.keras.layers.GlobalAveragePooling1D(),
# Dense: 분류를 위한 완전 연결 레이어 (ReLU 활성화 함수)
tf.keras.layers.Dense(16, activation='relu'),
# Dense: 출력 레이어 (이진 분류이므로 1개의 뉴런, sigmoid 활성화 함수)
tf.keras.layers.Dense(1, activation='sigmoid')
])
# 모델 요약
model.summary()
# 3. 모델 컴파일
# 이진 분류이므로 'binary_crossentropy' 손실 함수 사용
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
# 4. 모델 학습
print("\n모델 학습 시작 (감성 분석)...")
history = model.fit(train_data,
train_labels,
epochs=10,
batch_size=512, # 배치 사이즈 설정
validation_data=(test_data, test_labels),
verbose=1)
print("모델 학습 완료!")
# 5. 모델 평가
loss, accuracy = model.evaluate(test_data, test_labels, verbose=2)
print(f"\n테스트 정확도: {accuracy*100:.2f}%")
# 6. 예측
# 새로운 리뷰를 입력하여 감성 예측
sample_review = "This movie was absolutely fantastic! I loved every single moment of it."
# 단어를 숫자로 변환 (실제로는 단어 사전을 사용해야 함)
# 여기서는 간단히 문자열을 정수로 매핑하는 가상 로직을 사용합니다.
# 실제 단어 사전을 불러와서 리뷰를 정수 시퀀스로 변환하는 과정이 필요합니다.
# word_index = imdb.get_word_index()
# index_word = dict([(value, key) for (key, value) in word_index.items()])
# encoded_review = [word_index.get(word.lower(), 2) + 3 for word in sample_review.split()] # +3은 스페셜 토큰 때문
encoded_review = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] # 가상으로 인코딩된 리뷰
# 패딩 적용
padded_review = tf.keras.preprocessing.sequence.pad_sequences([encoded_review],
value=0,
padding='post',
maxlen=256)
prediction = model.predict(padded_review)[0][0]
if prediction > 0.5:
sentiment = "긍정적"
else:
sentiment = "부정적"
print(f"\n리뷰 '{sample_review}' 에 대한 예측 감성: {sentiment} (확률: {prediction*100:.2f}%)")
# 다른 예시
sample_review_neg = "What a terrible movie. I regret watching this."
encoded_review_neg = [13, 14, 15, 16, 17, 18, 19] # 가상 인코딩
padded_review_neg = tf.keras.preprocessing.sequence.pad_sequences([encoded_review_neg],
value=0,
padding='post',
maxlen=256)
prediction_neg = model.predict(padded_review_neg)[0][0]
sentiment_neg = "긍정적" if prediction_neg > 0.5 else "부정적"
print(f"리뷰 '{sample_review_neg}' 에 대한 예측 감성: {sentiment_neg} (확률: {prediction_neg*100:.2f}%)")
설명:
- 데이터 로드 및 전처리: IMDB 데이터셋은 텐서플로우에 내장되어 있으며, 이미 각 단어가 숫자로 인코딩되어 있습니다.
tf.keras.preprocessing.sequence.pad_sequences
를 사용하여 모든 리뷰의 길이를 동일하게 맞춥니다. - 모델 구축:
Embedding
레이어: 텍스트 데이터를 처리할 때 가장 먼저 사용되는 레이어입니다. 각 단어를 고차원의 밀집 벡터(dense vector)로 변환하여 단어 간의 의미론적 관계를 포착할 수 있게 합니다.GlobalAveragePooling1D
: 임베딩된 단어 벡터 시퀀스를 하나의 고정된 길이의 벡터로 압축합니다. (더 복잡한 모델에서는 RNN 계열인LSTM
이나GRU
레이어를 사용하여 시퀀스의 시간적 의존성을 모델링하기도 합니다.)Dense
레이어: 마지막으로 완전 연결 레이어를 통해 긍정/부정 감성을 분류합니다.
- 모델 컴파일 및 학습: 이진 분류이므로
loss='binary_crossentropy'
를 사용하고Adam
옵티마이저를 사용합니다.
이 예제는 텍스트 데이터를 텐서플로우로 처리하는 가장 기본적인 파이프라인을 보여줍니다. 텍스트 생성이나 번역과 같은 더 복잡한 NLP 작업에는 RNN (Recurrent Neural Network)
, LSTM (Long Short-Term Memory)
, GRU (Gated Recurrent Unit)
, 그리고 최근 각광받는 Transformer
아키텍처 등이 활용됩니다.
4. 예제 속 핵심 개념 다시보기 ⚙️📊
위 예제들을 통해 여러분은 텐서플로우/Keras로 머신러닝 모델을 구축하는 데 필요한 몇 가지 핵심 개념들을 직접 사용해 보셨습니다. 다시 한번 정리해 볼까요?
- 데이터 전처리 (Data Preprocessing):
- 모델이 이해할 수 있는 형태로 데이터를 가공하는 과정 (예: 이미지 픽셀 값 정규화, 텍스트 패딩).
- 데이터의 품질과 전처리 방식은 모델 성능에 지대한 영향을 미칩니다.
- 모델 구축 (Model Building):
tf.keras.Sequential
: 레이어를 순차적으로 쌓아 올리는 가장 간단한 방법입니다.tf.keras.layers.Dense
: 완전 연결(Fully Connected) 레이어로, 신경망의 기본 구성 요소입니다.tf.keras.layers.Flatten
: 다차원 데이터를 1차원 벡터로 평탄화합니다.tf.keras.layers.Dropout
: 과적합을 방지하는 기법입니다.tf.keras.layers.Embedding
: 텍스트 데이터를 벡터 공간으로 매핑합니다.activation function (활성화 함수)
: 뉴런의 출력을 비선형적으로 변환하여 모델이 복잡한 관계를 학습할 수 있게 합니다. (예:relu
,softmax
,sigmoid
)
- 모델 컴파일 (Model Compile):
optimizer (옵티마이저)
: 모델의 가중치를 업데이트하여 손실을 최소화하는 알고리즘 (예:Adam
).loss function (손실 함수)
: 모델의 예측이 실제 값과 얼마나 차이가 나는지 측정하는 함수 (예:mse
for 회귀,sparse_categorical_crossentropy
/binary_crossentropy
for 분류).metrics (평가 지표)
: 모델의 성능을 측정하는 기준 (예:accuracy
for 분류).
- 모델 학습 (Model Training –
.fit()
):epochs
: 전체 훈련 데이터셋을 몇 번 반복하여 학습할 것인지 지정합니다.batch_size
: 한 번의 가중치 업데이트에 사용될 데이터 샘플의 개수입니다.validation_data
: 훈련 중 모델의 성능을 모니터링하기 위한 별도의 데이터셋입니다. 과적합 여부를 판단하는 데 중요합니다.
- 모델 평가 (Model Evaluation –
.evaluate()
):- 훈련되지 않은 새로운 데이터(테스트 데이터셋)에 대한 모델의 최종 성능을 측정합니다.
- 예측 (Prediction –
.predict()
):- 학습된 모델을 사용하여 새로운 입력 데이터에 대한 출력을 생성합니다.
5. 더 깊이 파고들기 위한 팁 💡
이 예제들은 텐서플로우로 머신러닝을 시작하는 좋은 발판이 될 것입니다. 여기서 멈추지 않고 더 깊이 탐구하고 싶다면 다음 팁들을 활용해 보세요!
- 공식 문서와 튜토리얼 활용: 텐서플로우 공식 문서는 방대하고 잘 정리되어 있습니다. 특히
tf.keras
문서를 집중적으로 살펴보세요. (TensorFlow 공식 문서) - 하이퍼파라미터 튜닝:
epochs
,batch_size
,learning_rate
등 모델의 성능에 영향을 미치는 값들을 바꿔가며 실험해 보세요. 이 값들을 하이퍼파라미터라고 부릅니다. - 다양한 모델 아키텍처 탐색: Convolutional Neural Network (CNN)와 Recurrent Neural Network (RNN), Transformer 등 다양한 딥러닝 아키텍처와 그 활용법을 배워보세요.
- 더 큰 데이터셋 도전: Kaggle 등에서 실제 데이터를 찾아 직접 전처리하고 모델을 구축해 보세요.
- 커뮤니티 참여: Stack Overflow, GitHub, 텐서플로우 포럼 등에서 질문하고 다른 사람들의 코드를 살펴보세요.
- 디버깅 연습: 코드를 실행하며 발생하는 오류를 해결하는 과정은 실력 향상에 필수적입니다.
- 이론 공부 병행: 실습과 함께 머신러닝 및 딥러닝의 기본적인 수학적 원리와 알고리즘을 이해하면 모델을 더 효과적으로 구축하고 개선할 수 있습니다.
마무리하며 🌟
오늘 우리는 텐서플로우를 활용하여 선형 회귀, 이미지 분류, 그리고 감성 분석이라는 다양한 머신러닝 실전 예제들을 직접 구현해 보았습니다. 이론적인 개념이 실제 코드에서 어떻게 동작하는지 체험하며, 머신러닝 모델을 처음부터 끝까지 만들어보는 값진 경험을 하셨기를 바랍니다.
머신러닝과 딥러닝은 끝없이 발전하는 분야입니다. 이 글이 여러분의 AI 여정에 작은 불꽃이 되기를 바라며, 앞으로도 끊임없이 배우고, 실험하고, 여러분만의 멋진 아이디어를 현실로 만들어나가시길 응원합니다! 🚀💖
궁금한 점이나 더 배우고 싶은 내용이 있다면 언제든지 질문해주세요! D