알고리즘

TensorFlow 2.0을 이용한 ANN MNIST 숫자분류기 구현

WDmil 2024. 6. 16. 21:38
728x90
import tensorflow as tf

# MNIST 데이터를 다운로드
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train, x_test = x_train.astype('float32'), x_test.astype('float32')
x_train, x_test = x_train.reshape([-1, 784]), x_test.reshape([-1, 784])
x_train, x_test = x_train / 255., x_test / 255.
y_train, y_test = tf.one_hot(y_train, depth=10), tf.one_hot(y_test, depth=10)

learning_rate = 0.001
num_epochs = 30
batch_size = 256
display_step = 1
input_size = 784
hidden1_size = 256
hidden2_size = 256
output_size = 10

train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.shuffle(60000).batch(batch_size)

def random_normal_intializer_with_stddev_1():
    return tf.keras.initializers.RandomNormal(mean=0.0, stddev=1.0, seed=None)

class ANN(tf.keras.Model):
    def __init__(self):
        super(ANN, self).__init__()
        self.hidden_layer_1 = tf.keras.layers.Dense(hidden1_size,
                                                    activation='relu',
                                                    kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                    bias_initializer=random_normal_intializer_with_stddev_1())
        self.hidden_layer_2 = tf.keras.layers.Dense(hidden2_size,
                                                    activation='relu',
                                                    kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                    bias_initializer=random_normal_intializer_with_stddev_1())
        self.output_layer = tf.keras.layers.Dense(output_size,
                                                  activation=None,
                                                  kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                  bias_initializer=random_normal_intializer_with_stddev_1())
        
    def call(self, x):
        H1_output = self.hidden_layer_1(x)
        H2_output = self.hidden_layer_2(H1_output)
        logits = self.output_layer(H2_output)

        return logits
        
@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.Adam(learning_rate)

@tf.function
def train_step(model, x, y):
    with tf.GradientTape() as tape:
        y_pred = model(x)
        loss = cross_entropy_loss(y_pred, y)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    return loss

@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

ANN_model = ANN()

for epoch in range(num_epochs):
    average_loss = 0.
    total_batch = int(x_train.shape[0] / batch_size)
    for batch_x, batch_y in train_data:
        current_loss = train_step(ANN_model, batch_x, batch_y)
        average_loss += current_loss / total_batch
    if epoch % display_step == 0:
        print("반복(Epoch): %d, 손실함수(Loss): %f" % ((epoch+1), average_loss))

accuracy = compute_accuracy(ANN_model(x_test), y_test)
print("정확도(Accuracy): %f" % accuracy)

 


코드 부분을 한부분씩 잘라서 이해해보자.

데이터 전처리

# MNIST 데이터를 다운로드
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
# 이미지들을 float32 데이터 타입으로 변경
x_train, x_test = x_train.astype('float32'), x_test.astype('float32')
# 28*28 형태의 이미지를 784차원으로 flattening 한다.
x_train, x_test = x_train.reshape([-1, 784]), x_test.reshape([-1, 784])
# [0, 255] 사이의 값을 [0, 1] 사이의 값으로 Normalize한다.
x_train, x_test = x_train / 255., x_test / 255.
# 레이블 데이터에 one-hot encoding을 적용한다.
y_train, y_test = tf.one_hot(y_train, depth=10), tf.one_hot(y_test, depth=10)

https://inradestrt.tistory.com/664

 

MNIST 데이터셋

머신러닝 모델을 학습시키기 위해서는 데이터가 필요하다.하지만 데이터 학습에 적합한 형태로 정제하는 것은 많은 시간과 노력이 필요한 일이다. 따라서 많은 연구자가 학습하기 쉬운 형태로

inradestrt.tistory.com

위 MNIST데이터셋을 다운로드하여 사용한다.

 

데이터타입을 float32형식으로 변환한다.

  • 이미지데이터는 기본적으로 uint8형식으로 되어있으나, 신경망 모델은 float32 타입의 데이터를 다루기 때문.

이미지 평탄화(Flattening)

  • MNIST이미지는 28x28픽셀 크기의 2D배열 형태이다. 이 이미지를 1D형태로 변환하여 신경만의 입력으로 사용하기 위해 평탄화(flatten)한다.

정규화(Normalize)

  • 이미지 픽셀값은 [0, 255](흑백) 값을 가지는데, 모델이 학습할 떄 더 빠르고 안정적으로 학습할 수 있도록 [0, 1] 사이로 정규화한다.

One-Hot 인코딩

  • 레이블 데이터는 0에서 9까지의 정수값이다. 이를 신경망 출력과 비교하기 쉽게 One-hot인코딩을 통해 이진벡터로 변환하여 키값을 레이블 해준다.

데이터셋 준비

learning_rate = 0.001
num_epochs = 30
batch_size = 256
display_step = 1
input_size = 784
hidden1_size = 256
hidden2_size = 256
output_size = 10

학습률(learning_rate)

  • 모델의 학습 속도를 결정한다. 내려가는 경사도라고 이해하면됨

에포크 수(num_epochs)

  • 전체 데이터셋 학습을 몇 번 반복할지 설정한다.

배치 크기(batch_size)

  • 한번에 학습할 데이터의 크기를 설정한다. 256개씩 묶음
    한번에 모든 데이터를 학습하게 되면 너무 느리기 때문에, 부분부분씩 잘라서 학습한다고 생각하자.

레이어 크기(hidden, output_size)

  • 노드의 크기를 설정한다.

모델 정의

class ANN(tf.keras.Model):
    def __init__(self):
        super(ANN, self).__init__()
        self.hidden_layer_1 = tf.keras.layers.Dense(hidden1_size,
                                                    activation='relu',
                                                    kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                    bias_initializer=random_normal_intializer_with_stddev_1())
        self.hidden_layer_2 = tf.keras.layers.Dense(hidden2_size,
                                                    activation='relu',
                                                    kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                    bias_initializer=random_normal_intializer_with_stddev_1())
        self.output_layer = tf.keras.layers.Dense(output_size,
                                                  activation=None,
                                                  kernel_initializer=random_normal_intializer_with_stddev_1(),
                                                  bias_initializer=random_normal_intializer_with_stddev_1())
        
    def call(self, x):
        H1_output = self.hidden_layer_1(x)
        H2_output = self.hidden_layer_2(H1_output)
        logits = self.output_layer(H2_output)

        return logits

모델 클래스 정의

  • 딥러닝 모델 정의

첫 번째 은닉층

  • 256개의 뉴런을 가진 첫 번째 은닉층, ReLU활성화 함수 사용

두 번째 은닉층

    • 256개의 뉴런을 가진 첫 번째 은닉층, ReLU활성화 함수 사용

출력층

  • 10개의 뉴런을 가진 출력층, 나중에 소프트맥스 적용을 위해 활성화 함수가 없다.

손실 함수 정의

@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.Adam(learning_rate)

Adam최적화기

  • 모델 가중치 학습을 위한 최적화 알고리즘

학습 단계 정의

@tf.function
def train_step(model, x, y):
    with tf.GradientTape() as tape:
        y_pred = model(x)
        loss = cross_entropy_loss(y_pred, y)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    return loss

학습 단계

  • 모델의 가중치를 업데이트하는 단계 정의, 손실을 계산하고 그에따른 그래디언트를 구해 가중치를 조정한다.

모델 학습 및 평가

ANN_model = ANN()

for epoch in range(num_epochs):
    average_loss = 0.
    total_batch = int(x_train.shape[0] / batch_size)
    for batch_x, batch_y in train_data:
        current_loss = train_step(ANN_model, batch_x, batch_y)
        average_loss += current_loss / total_batch
    if epoch % display_step == 0:
        print("반복(Epoch): %d, 손실함수(Loss): %f" % ((epoch+1), average_loss))

accuracy = compute_accuracy(ANN_model(x_test), y_test)
print("정확도(Accuracy): %f" % accuracy)

모델 인스턴스화

  • ANN모델 인스턴스 화

모델 학습

  • 설정한 에포크 수 만큼 학습한다. 각 배치마다 손실을 계산하고 가중치 업데이트.

모델 평가

  • 테스트 데이터로 모델의 정확도를 계산하고 출력.

 

728x90