알고리즘
TensorFlow 2.0을 이용한 CIFAR-10 이미지 분류 CNN구현
WDmil
2024. 6. 18. 03:01
728x90
CIFAR-10 데이터셋은 10개의 클래스, 50,000개의 traing images, 10,000 개의 testing images로 구성되어있다.
이미지크기 : 32x32x3 pixels(Color Image)
레이블 : airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck.
import tensorflow as tf
# CIFAR-10 데이터를 다운로드하고 데이터를 불러옵니다.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
# 이미지들을 float32 데이터 타입으로 변경합니다.
x_train, x_test = x_train.astype('float32'), x_test.astype('float32')
# [0, 255] 사이의 값을 [0, 1]사이의 값으로 Normalize합니다.
x_train, x_test = x_train / 255., x_test / 255.
# scalar 형태의 레이블(0~9)을 One-hot Encoding 형태로 변환합니다.
y_train_one_hot = tf.squeeze(tf.one_hot(y_train, 10), axis=1)
y_test_one_hot = tf.squeeze(tf.one_hot(y_test, 10), axis=1)
# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옵니다.
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train_one_hot))
train_data = train_data.repeat().shuffle(50000).batch(128)
train_data_iter = iter(train_data)
test_data = tf.data.Dataset.from_tensor_slices((x_test, y_test_one_hot))
test_data = test_data.batch(1000)
test_data_iter = iter(test_data)
# tf.keras.Model을 이용해서 CNN 모델을 정의합니다.
class CNN(tf.keras.Model):
def __init__(self):
super(CNN, self).__init__()
# 첫번째 convolutional layer - 하나의 RGB 이미지를 64개의 특징들(feature)으로 맵핑(mapping)합니다.
self.conv_layer_1 = tf.keras.layers.Conv2D(filters=64, kernel_size=5, strides=1, padding='same', activation='relu')
self.pool_layer_1 = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)
# 두번째 convolutional layer - 64개의 특징들(feature)을 64개의 특징들(feature)로 맵핑(mapping)합니다.
self.conv_layer_2 = tf.keras.layers.Conv2D(filters=64, kernel_size=5, strides=1, padding='same', activation='relu')
self.pool_layer_2 = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)
# 세번째 convolutional layer
self.conv_layer_3 = tf.keras.layers.Conv2D(filters=128, kernel_size=3, strides=1, padding='same', activation='relu')
# 네번째 convolutional layer
self.conv_layer_4 = tf.keras.layers.Conv2D(filters=128, kernel_size=3, strides=1, padding='same', activation='relu')
# 다섯번째 convolutional layer
self.conv_layer_5 = tf.keras.layers.Conv2D(filters=128, kernel_size=3, strides=1, padding='same', activation='relu')
# Fully Connected Layer 1 - 2번의 downsampling 이후에, 우리의 32x32 이미지는 8x8x128 특징맵(feature map)이 됩니다.
# 이를 384개의 특징들로 맵핑(mapping)합니다.
self.flatten_layer = tf.keras.layers.Flatten()
self.fc_layer_1 = tf.keras.layers.Dense(384, activation='relu')
self.dropout = tf.keras.layers.Dropout(0.2)
# Fully Connected Layer 2 - 384개의 특징들(feature)을 10개의 클래스-airplane, automobile, bird...-로 맵핑(mapsping)합니다.
self.output_layer = tf.keras.layers.Dense(10, activation=None)
def call(self, x, training=False):
# 입력 이미지
h_conv1 = self.conv_layer_1(x)
h_pool1 = self.pool_layer_1(h_conv1)
h_conv2 = self.conv_layer_2(h_pool1)
h_pool2 = self.pool_layer_2(h_conv2)
h_conv3 = self.conv_layer_3(h_pool2)
h_conv4 = self.conv_layer_4(h_conv3)
h_conv5 = self.conv_layer_5(h_conv4)
h_conv5_flat = self.flatten_layer(h_conv5)
h_fc1 = self.fc_layer_1(h_conv5_flat)
# Dropout - 모델의 복잡도를 컨트롤합니다. 특징들의 co-adaptation을 방지합니다.
h_fc1_drop = self.dropout(h_fc1, training=training)
logits = self.output_layer(h_fc1_drop)
y_pred = tf.nn.softmax(logits)
return y_pred, logits
# cross-entropy 손실 함수를 정의합니다.
@tf.function
def cross_entropy_loss(logits, y):
return tf.reduce_mean(tf.keras.losses.categorical_crossentropy(y, logits, from_logits=True))
# 최적화를 위한 RMSprop 옵티마이저를 정의합니다.
optimizer = tf.optimizers.RMSprop(1e-3)
# 최적화를 위한 function을 정의합니다.
@tf.function
def train_step(model, x, y, training=True):
with tf.GradientTape() as tape:
y_pred, logits = model(x, training=training)
loss = cross_entropy_loss(logits, y)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
# 모델의 정확도를 출력하는 함수를 정의합니다.
@tf.function
def compute_accuracy(y_pred, y):
accuracy = tf.reduce_mean(tf.keras.metrics.categorical_accuracy(y, y_pred))
return accuracy
# Convolutional Neural Networks(CNN) 모델을 선언합니다.
CNN_model = CNN()
# 10000 Step만큼 최적화를 수행합니다.
for i in range(10000):
batch_x, batch_y = next(train_data_iter)
# 100 Step마다 training 데이터셋에 대한 정확도와 loss를 출력합니다.
if i % 100 == 0:
y_pred, logits = CNN_model(batch_x, training=False)
train_accuracy = compute_accuracy(y_pred, batch_y)
loss_print = cross_entropy_loss(logits, batch_y)
print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f, 손실 함수(loss): %f" % (i, train_accuracy, loss_print))
# 20% 확률의 Dropout을 이용해서 학습을 진행합니다.
train_step(CNN_model, batch_x, batch_y)
# 학습이 끝나면 테스트 데이터(10000개)에 대한 정확도를 출력합니다.
test_accuracy = 0.0
for i in range(10):
test_batch_x, test_batch_y = next(test_data_iter)
y_pred, _ = CNN_model(test_batch_x, training=False)
test_accuracy += compute_accuracy(y_pred, test_batch_y)
test_accuracy /= 10
print("테스트 데이터 정확도: %f" % test_accuracy)
유의! 학습과정이 엄청 오래걸린다!
코드를 분리해서 해석해보자.
1. 데이터 준비 및 전처리
# CIFAR-10 데이터를 다운로드하고 데이터를 불러옵니다.
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
# 이미지들을 float32 데이터 타입으로 변경합니다.
x_train, x_test = x_train.astype('float32'), x_test.astype('float32')
# [0, 255] 사이의 값을 [0, 1]사이의 값으로 Normalize합니다.
x_train, x_test = x_train / 255., x_test / 255.
# scalar 형태의 레이블(0~9)을 One-hot Encoding 형태로 변환합니다.
y_train_one_hot = tf.squeeze(tf.one_hot(y_train, 10), axis=1)
y_test_one_hot = tf.squeeze(tf.one_hot(y_test, 10), axis=1)
- tf.keras.datasets.cifar10.load_data(): CIFAR-10 데이터셋을 다운로드.
이는 32x32 크기의 3채널(RGB) 이미지와 해당 레이블로 구성된다. - astype('float32'): 이미지를 32비트 부동 소수점 형식으로 변환한다.
- / 255.: 이미지 픽셀 값을 [0, 255] 범위에서 [0, 1] 범위로 정규화.
- tf.squeeze(tf.one_hot(y_train, 10), axis=1): 레이블을 원-핫 인코딩.
이는 각 레이블을 10차원 벡터로 변환하여 클래스별로 이진 값을 가지게 된다.
2. 데이터셋을 섞고 배치로 묶기
# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옵니다.
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train_one_hot))
train_data = train_data.repeat().shuffle(50000).batch(128)
train_data_iter = iter(train_data)
test_data = tf.data.Dataset.from_tensor_slices((x_test, y_test_one_hot))
test_data = test_data.batch(1000)
test_data_iter = iter(test_data)
- tf.data.Dataset.from_tensor_slices((x_train, y_train_one_hot)): 훈련 데이터를 텐서로 변환.
- repeat(): 데이터셋을 무한 반복한다.
- shuffle(50000): 50000개의 데이터를 무작위로 섞는다.
- batch(128): 데이터를 128개의 배치로 나눈다.
- iter(train_data): 데이터셋을 반복할 수 있는 이터레이터로 변환한다.
3. CNN모델 정의
class CNN(tf.keras.Model):
def __init__(self):
super(CNN, self).__init__()
self.conv_layer_1 = tf.keras.layers.Conv2D(filters=64, kernel_size=5, strides=1, padding='same', activation='relu')
self.pool_layer_1 = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)
self.conv_layer_2 = tf.keras.layers.Conv2D(filters=64, kernel_size=5, strides=1, padding='same', activation='relu')
self.pool_layer_2 = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)
self.conv_layer_3 = tf.keras.layers.Conv2D(filters=128, kernel_size=3, strides=1, padding='same', activation='relu')
self.conv_layer_4 = tf.keras.layers.Conv2D(filters=128, kernel_size=3, strides=1, padding='same', activation='relu')
self.conv_layer_5 = tf.keras.layers.Conv2D(filters=128, kernel_size=3, strides=1, padding='same', activation='relu')
self.flatten_layer = tf.keras.layers.Flatten()
self.fc_layer_1 = tf.keras.layers.Dense(384, activation='relu')
self.dropout = tf.keras.layers.Dropout(0.2)
self.output_layer = tf.keras.layers.Dense(10, activation=None)
def call(self, x, is_training):
h_conv1 = self.conv_layer_1(x)
h_pool1 = self.pool_layer_1(h_conv1)
h_conv2 = self.conv_layer_2(h_pool1)
h_pool2 = self.pool_layer_2(h_conv2)
h_conv3 = self.conv_layer_3(h_pool2)
h_conv4 = self.conv_layer_4(h_conv3)
h_conv5 = self.conv_layer_5(h_conv4)
h_conv5_flat = self.flatten_layer(h_conv5)
h_fc1 = self.fc_layer_1(h_conv5_flat)
h_fc1_drop = self.dropout(h_fc1, training=is_training)
logits = self.output_layer(h_fc1_drop)
y_pred = tf.nn.softmax(logits)
return y_pred, logits
- Conv2D: 2D 합성곱 층. 필터 수, 커널 크기, 스트라이드, 패딩 방식, 활성화 함수를 지정한다.
- filters=64: 첫 번째 합성곱 층은 64개의 필터를 사용.
- kernel_size=5: 각 필터의 크기는 5x5
- strides=1: 필터가 한 번에 이동하는 픽셀 수는 1
- padding='same': 출력 크기를 입력 크기와 동일하게 유지
- activation='relu': 활성화 함수로 ReLU를 사용
- MaxPool2D: 최대 풀링 층. 풀링 크기와 스트라이드를 지정
- pool_size=(3, 3): 풀링 윈도우 크기는 3x3.
- strides=2: 풀링 윈도우가 한 번에 이동하는 픽셀 수는 2.
- Flatten(): 입력을 1차원으로 변환
- Dense: 완전 연결 층.
- units=384: 첫 번째 완전 연결 층은 384개의 뉴런을 가진다.
- activation='relu': 활성화 함수로 ReLU를 사용.
- units=10: 출력 층은 10개의 뉴런을 가지며, 클래스 수와 동일하다.
- Dropout(0.2): 20%의 드롭아웃 확률을 사용하여 오버피팅을 방지한다.
4. 손실 함수와 옵티마이저 정의
@tf.function
def cross_entropy_loss(logits, y):
return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))
optimizer = tf.optimizers.RMSprop(1e-3)
- cross_entropy_loss(logits, y): 크로스 엔트로피 손실 함수를 정의.
이는 모델의 출력값과 실제 레이블 사이의 차이를 계산하는것 과 같다.- logits: 모델의 출력값.
- y: 실제 레이블.
- tf.optimizers.RMSprop(1e-3): RMSprop 옵티마이저를 정의한다. 학습률은 0.001로 설정.
5. 학습 단계 정의
@tf.function
def train_step(model, x, y, is_training):
with tf.GradientTape() as tape:
y_pred, logits = model(x, is_training)
loss = cross_entropy_loss(logits, y)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
- train_step(model, x, y, is_training): 한 단계 학습을 수행하는 함수.
- model(x, is_training): 모델에 입력값 x를 넣어 예측값 y_pred와 로짓 logits를 얻는다.
- cross_entropy_loss(logits, y): 손실을 계산.
- tape.gradient(loss, model.trainable_variables): 손실에 대한 그라디언트를 계산.
- optimizer.apply_gradients(zip(gradients, model.trainable_variables)): 그라디언트를 적용하여 모델의 파라미터를 업데이트.
6. 정확도 계산 함수
@tf.function
def compute_accuracy(y_pred, y):
correct_prediction = tf.equal(tf.argmax(y_pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
return accuracy
- compute_accuracy(y_pred, y): 모델의 예측 정확도를 계산하는 함수.
- tf.argmax(y_pred, 1): 예측값에서 가장 높은 확률을 가진 클래스의 인덱스를 찾는다.
- tf.equal(...): 예측값과 실제값이 같은지 비교한다.
- tf.reduce_mean(...): 정확도의 평균을 계산.
7. 모델 학습 및 평가
# Convolutional Neural Networks(CNN) 모델을 선언합니다.
CNN_model = CNN()
# 10000 Step만큼 최적화를 수행합니다.
for i in range(10000):
batch_x, batch_y = next(train_data_iter)
# 100 Step마다 training 데이터셋에 대한 정확도와 loss를 출력합니다.
if i % 100 == 0:
train_accuracy = compute_accuracy(CNN_model(batch_x, False)[0], batch_y)
loss_print = cross_entropy_loss(CNN_model(batch_x, False)[1], batch_y)
print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f, 손실 함수(loss): %f" % (i, train_accuracy, loss_print))
# 20% 확률의 Dropout을 이용해서 학습을 진행합니다.
train_step(CNN_model, batch_x, batch_y, True)
# 학습이 끝나면 테스트 데이터(10000개)에 대한 정확도를 출력합니다.
test_accuracy = 0.0
for i in range(10):
test_batch_x, test_batch_y = next(test_data_iter)
test_accuracy = test_accuracy + compute_accuracy(CNN_model(test_batch_x, False)[0], test_batch_y).numpy()
test_accuracy = test_accuracy / 10
print("테스트 데이터 정확도: %f" % test_accuracy)
- CNN_model = CNN(): CNN 모델을 선언.
- for i in range(10000): 10000번 반복하여 학습을 수행.
- batch_x, batch_y = next(train_data_iter): 학습 데이터를 배치 단위로 가져온다.
- if i % 100 == 0: 100번 반복마다 훈련 데이터의 정확도와 손실을 출력.
- compute_accuracy(...): 정확도를 계산.
- cross_entropy_loss(...): 손실을 계산.
- train_step(...): 학습을 진행.
- test_accuracy = 0.0: 테스트 데이터 정확도를 초기화.
- for i in range(10): 테스트 데이터 10개 배치에 대해 정확도를 계산.
- test_accuracy = test_accuracy + compute_accuracy(...): 각 배치의 정확도를 누적.
- test_accuracy = test_accuracy / 10: 평균 정확도를 계산.
- print("테스트 데이터 정확도: %f" % test_accuracy): 최종 테스트 데이터 정확도를 출력한다.
728x90