Transfer Learning(전이 학습)

: 이미 학습된 Neural Networks의 파라미터를 새로운 Task에 맞게 다시 미세조정(Fine-Tuning)하는 것

 

몇몇 사전 학습 모델들은 큰 사이즈의 합성곱 신경망(CNN) 구조를 가지고 있다.

CNN은 크게 두 가지 파트로 나뉜다.

이미지 출처 https://muzukphysics.tistory.com/entry/DL-8-Convolutional-Neural-Network-CNN-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D

1. Convolutional base

: Convolutional 층과 Pooling 층이 여러겹 쌓여있는 부분. 

  이 부분의 목표는 이미지로부터 특징을 효과적으로 추출하는 것이다.

 

2. Classifier

: 주로 완전 연결 계층(모든 계층의 뉴런이 이전 층의 출력노드와 하나도 빠짐없이 모두 연결되어 있는 층)으로 이루어짐.

  이 부분의 목표는 추출된 측징을 잘 학습해서 이미지를 알맞는 카테고리로 분류하는 것이다.

 

→ Convolutional base부분에서 낮은 레벨의 계층(input에 가까운 계층)일수록 일반적인 특징을 추출할 것이고, 높은 레벨의 계층(output과 가까운 계층)과 Classifier 부분은 보다 구체적이고 특유한 특징들을 추출한다.

 


<Fine-Tuning의 세 가지 전략>

이미지 출처 https://adioshun.gitbook.io/deep-learning/transfer-learning/transfer-learning

① 전체 모델을 새로 학습

-  사전학습 모델의 구조만 사용, 나머지는 전부 새로 학습시키기

   따라서 큰 사이즈의 데이터셋이 필요

 

② Convolutional base의 일부분은 고정시킨 상태로, 나머지 계층과 classifier를 새로 학습

-   Convolutional의 낮은 레벨의 계층 혹은 높은 레벨의 계층은 각각 특징이 달랐다.

    따라서 이런 파라미터 중 어느 정도까지 재학습시킬지를 정해야 한다. 

 

ex) 데이터셋이 작고 모델의 파라미터가 많다면, 오버피팅이 될 위험이 있으므로 더 많은 계층을 건들지 않고 그대로 둠

     데이터셋이 크고 모델의 파라미터가 적다면 더 많은 계층을 학습시켜서 진행하고자 하는 프로젝트에 맞게 학습시킴

 

③ Convolutional base 전체는 고정, classifier만 새로 학습

-    컴퓨팅 연산 능력이 부족하거나 데이터셋이 너무 작을때,

     진행하고자 하는 문제가 사전학습모델이 이미 학습한 데이서셋과 매우 비슷할 때 고려할 수 있는 방법

'Study > Deep Learning' 카테고리의 다른 글

순환신경망(RNN)  (0) 2021.10.19
Pre-Trained CNN 모델을 이용한 Image Classification  (0) 2021.10.18
TensorBoard 이용하기  (0) 2021.10.18
tf.train.CheckpointManager API  (0) 2021.10.18
드롭아웃(Dropout)  (0) 2021.10.17

터미널 로그 등을 이용해서 학습 과정을 모니터링 할 경우, 한눈에 학습 과정의 문제점을 파악하기 쉽지 않다.

따라서 TensorFlow 에서는 학습과정 시각화를 위해 TensorBoard 기능을 제공한다.

 

 

 

TensorBoard는 크게 3가지의 API를 제공한다.

 

① tf.summary.scalar : scalar값 형태의 로그 저장

② tf.summary.histogram : histogram 형태의 로그 저장

③ tf.summary.image : 이미지 형태의 로그 저장

 

 

 

[텐서보드 로그 저장법]

① 인자값으로 텐서보드 로그 파일을 저장할 경로르 지정해서 File Writer 생성

summary_writer = tf.summary.create_file_writer('./tensorboard_log')

 

 

② 요약 정보를 남기고 싶은 값을 Writer scope 내에서 tf.summary.* API로 추가

with summary_writer.as_default():

      tf.summary.scalar('loss function', loss, step=optimizer.iterations)

="summary_writer를 as_default()로 열어준 다음, 현재 손실함수 값을 loss function이라는 이름으로 저장하고, 현재 iterations 횟수를 step 인자에 넣겠다"

 

--> epoch 별 손실함수 값이 파일에 저장되며, 이를 불러와 시각화 된 형태로 볼 수 있게 된다.

 

 

[텐서보드 실행방법]

터미널에서 명령어 실행

tensorboard --logdir=로그가 저장된 경로

 

그 후 웹 브라우저에서 실행

URL 창에 localhost:6006 을 입력

 


이전에 진행했던 'CNN을 이용한 MNIST 숫자 분류기 구현'에 TensorBoard 기능을 추가한 예제이다.

https://these-dayss.tistory.com/139

 

TensorFlow 2.0을 이용한 MNIST 숫자분류를 위한 CNN 구현

# -*- coding: utf-8 -*- # Convolutional Neural Networks(CNN)을 이용한 MNIST 분류기(Classifier) - Keras API를 이용한 구현 import tensorflow as tf # MNIST 데이터를 다운로드 (x_train, y_train), (x_test..

these-dayss.tistory.com

 

위의 예제에서 추가 및 수정된 부분만 따로 정리해두었다.

# 텐서보드 summary 정보들을 저장할 폴더 경로를 설정하고 FileWriter를 선언
# trainig 과정에서 발생되는 log를 저장하기 위한 것과 test 과정에서의 log를 저장하기 위한 것을 따로 지정
train_summary_writer = tf.summary.create_file_writer('./tensorboard_log/train')
test_summary_writer = tf.summary.create_file_writer('./tensorboard_log/test')

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):
  with tf.GradientTape() as tape:
    y_pred, logits = model(x)
    loss = cross_entropy_loss(logits, y)
  # 매 step마다 tf.summary.scalar, tf.summary.image 텐서보드 로그를 기록
  with train_summary_writer.as_default():
    tf.summary.scalar('loss', loss, step=optimizer.iterations) # loss라는 이름으로 현재 iteration time step의 loss값 저장
    x_image = tf.reshape(x, [-1, 28, 28, 1]) # flattening된 상태 (784차원)를 다시 28x28 gray scale 이미지로 저장
    tf.summary.image('training image', x_image, max_outputs=10, step=optimizer.iterations) # training image라는 이름으로 그 시점에서 뽑힌 랜덤 배치의 mnist 트레이닝 이미지 저장. 저장 크기는 최대 10개로 지정

  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# 모델의 정확도를 출력하는 함수를 정의
@tf.function
def compute_accuracy(y_pred, y, summary_writer): # 정확도 측정함수 인자값에 summary_writer 추가
  correct_prediction = tf.equal(tf.argmax(y_pred,1), tf.argmax(y,1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

  with summary_writer.as_default():
    tf.summary.scalar('accuracy', accuracy, step=optimizer.iterations)

  return accuracy

# Convolutional Neural Networks(CNN) 모델을 선언
CNN_model = CNN()

# 10000 Step만큼 최적화를 수행
for i in range(10000):
  # 50개씩 MNIST 데이터를 불러옴
  batch_x, batch_y = next(train_data_iter)
  # 100 Step마다 training 데이터셋에 대한 정확도를 출력
  if i % 100 == 0: # 100번의 학습마다 정확도 측정
    train_accuracy = compute_accuracy(CNN_model(batch_x)[0], batch_y, train_summary_writer)
    print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f" % (i, train_accuracy))
  # 옵티마이저를 실행해 파라미터를 한스텝 업데이트
  train_step(CNN_model, batch_x, batch_y)

# 학습이 끝나면 학습된 모델의 정확도를 출력. test 데이터셋에 대한 정확도
print("정확도(Accuracy): %f" % compute_accuracy(CNN_model(x_test)[0], y_test, test_summary_writer))

 

결과
파일 생성
각각의 데이터셋(training, test)의 Log를 저장하기 위한 파일 생성
training 이미지셋에 대한 log를 시각화
test 이미지셋에 대한 log를 시각화

실제 문제를 해결하기 위해서는 많은 횟수를 반복하여 학습하는 것이 필요하다.

따라서 중간중간 학습시킨 파라미터를 저장하고, 이를 불러와 이어서 학습을 진행하는 것이 필수적이다.

TensorFlow 2.0에선 tf.train.CheckpointManager를 제공해주어 이 과정을 따를 수 있다.

 

 

[파라미터를 저장하는 법]

① tf.train.Checkpoint 클래스의 인자값으로 저장하고자 하는 tf.keras.Model 인스턴스와 전역 반복횟수를 지정해서 선언

 

ckpt = tf.train.Checkpoint(step=tf.Variable(0), model=CNN_model)

="전역 반복횟수는 0부터 시작, tf.keras.Model 인스턴스에 있는 CNN_model을 사용하겠다"

 

 

 

② tf.train.CheckpointManager에 인자값으로 선언한 tf.train.Checkpoint 인스턴스와 중간 파라미터를 저장할 경로 설정

 

ckpt_manager = tf.train.CheckpointManager(ckpt, directory=SAVER_DIR, max_to_keep=5)

="ckpt 인스턴스를 사용할 것이며, 저장할 경로는 SAVER_DIR이고 최근 5개의 학습 결과를 저장할 것이다."

 

 

 

③ 파라미터를 저장하고자 하는 시점에 해당 시점의 전역 반복횟수를 인자값으로 선언한 tf.train.CheckpointManager의 save 메소드를 호출

 

ckpt_manager.save(checkpoint_number=ckpt.step)

="전역 반복횟수를 저장"

 

 

 

④ tf.train.Checkpoint의 전역 반복 횟수 값(ckpt.step)을 매 반복마다 1씩 증가시킴

 

ckpt.step.assign_add(1)

="전역 반복횟수 1씩 증가"

 

 

[파라미터를 불러오는 법]

① tf.train.latest_checkpoint의 인자값으로 파라미터가 저장된 폴더 경로를 지정해서 가장 최근의 체크포인트 파일의 경로(full path)를 가져옴

 

latest_ckpt = tf.train.latest_checkpoint(SAVER_DIR)

 

 

 

② 선언한 tf.train.CheckpointManager의 restore 함수의 인자값으로 불러올 체크포인트 파일의 경로를 지정해서 파라미터 값을 복원

 

ckpt.restore(latest_ckpt)

 

 


# -*- coding: utf-8 -*-
# CNN을 이용한 MNIST 분류기 - tf.train.CheckpointManager API 예제 - Keras API를 이용한 구현

import tensorflow as tf

# 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)

# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옴
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.repeat().shuffle(60000).batch(50)
train_data_iter = iter(train_data)

# tf.keras.Model을 이용해서 CNN 모델을 정의
class CNN(tf.keras.Model):
  def __init__(self):
    super(CNN, self).__init__()
    # 첫번째 Convolution Layer
    # 5x5 Kernel Size를 가진 32개의 Filter를 적용
    self.conv_layer_1 = tf.keras.layers.Conv2D(filters=32, kernel_size=5, strides=1, padding='same', activation='relu')
    self.pool_layer_1 = tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=2)

    # 두번째 Convolutional Layer
    # 5x5 Kernel Size를 가진 64개의 Filter를 적용
    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=(2, 2), strides=2)

    # Fully Connected Layer
    # 7x7 크기를 가진 64개의 activation map을 1024개의 특징들로 변환
    self.flatten_layer = tf.keras.layers.Flatten()
    self.fc_layer_1 = tf.keras.layers.Dense(1024, activation='relu')

    # Output Layer
    # 1024개의 특징들(feature)을 10개의 클래스-one-hot encoding으로 표현된 숫자 0~9-로 변환
    self.output_layer = tf.keras.layers.Dense(10, activation=None)

  def call(self, x):
    # MNIST 데이터를 3차원 형태로 reshape함. MNIST 데이터는 grayscale 이미지기 때문에 3번째차원(컬러채널)의 값은 1임
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    # 28x28x1 -> 28x28x32
    h_conv1 = self.conv_layer_1(x_image)
    # 28x28x32 -> 14x14x32
    h_pool1 = self.pool_layer_1(h_conv1)
    # 14x14x32 -> 14x14x64
    h_conv2 = self.conv_layer_2(h_pool1)
    # 14x14x64 -> 7x7x64
    h_pool2 = self.pool_layer_2(h_conv2)
    # 7x7x64(3136) -> 1024
    h_pool2_flat = self.flatten_layer(h_pool2)
    h_fc1 = self.fc_layer_1(h_pool2_flat)
    # 1024 -> 10
    logits = self.output_layer(h_fc1)
    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.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))

# 최적화를 위한 Adam 옵티마이저를 정의
optimizer = tf.optimizers.Adam(1e-4)

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):
  with tf.GradientTape() as tape:
    y_pred, logits = model(x)
    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):
  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

# Convolutional Neural Networks(CNN) 모델을 선언
CNN_model = CNN()

# tf.train.CheckpointManager를 이용해서 파라미터를 저장
SAVER_DIR = "./model"
ckpt = tf.train.Checkpoint(step=tf.Variable(0), model=CNN_model)
ckpt_manager = tf.train.CheckpointManager(
      ckpt, directory=SAVER_DIR, max_to_keep=5)
latest_ckpt = tf.train.latest_checkpoint(SAVER_DIR)

# 만약 저장된 모델과 파라미터가 있으면 이를 불러오고 (Restore)
# Restored 모델을 이용해서 테스트 데이터에 대한 정확도를 출력하고 프로그램을 종료
if latest_ckpt:
  ckpt.restore(latest_ckpt)
  print("테스트 데이터 정확도 (Restored) : %f" % compute_accuracy(CNN_model(x_test)[0], y_test))
  exit()

# 10000 Step만큼 최적화를 수행
while int(ckpt.step) < (10000 + 1):
  # 50개씩 MNIST 데이터를 불러옴
  batch_x, batch_y = next(train_data_iter)
  # 100 Step마다 training 데이터셋에 대한 정확도를 출력하고 tf.train.CheckpointManager를 이용해서 파라미터를 저장
  if ckpt.step % 100 == 0:
    ckpt_manager.save(checkpoint_number=ckpt.step)
    train_accuracy = compute_accuracy(CNN_model(batch_x)[0], batch_y)
    print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f" % (ckpt.step, train_accuracy))
  # 옵티마이저를 실행해 파라미터를 한스텝 업데이트
  train_step(CNN_model, batch_x, batch_y)

  ckpt.step.assign_add(1)

# 학습이 끝나면 테스트 데이터에 대한 정확도를 출력
print("정확도(Accuracy): %f" % compute_accuracy(CNN_model(x_test)[0], y_test))

 

위 코드는 앞서 진행했던 MNIST 숫자분류를 위한 CNN 모델 실습 코드와 거의 유사하며 여기에 checkpoint 기능만 추가한 것이다.

 

https://these-dayss.tistory.com/139

 

TensorFlow 2.0을 이용한 MNIST 숫자분류를 위한 CNN 구현

# -*- coding: utf-8 -*- # Convolutional Neural Networks(CNN)을 이용한 MNIST 분류기(Classifier) - Keras API를 이용한 구현 import tensorflow as tf # MNIST 데이터를 다운로드 (x_train, y_train), (x_test..

these-dayss.tistory.com

 

 

1900번까지 학습한 결과

 

실행 결과, model 폴더가 생성되며, 100번의 학습마다 파라미터 저장 파일들이 생성되었음을 알 수 있고, 

1900번 정도의 학습까지만 시키고 끝냈기 때문에 1500부터 1900까지의 학습 파라미터가 저장된 것을 확인할 수 있다.

(최근 5번의 학습결과)

 

checkpoint라는 이름의 text 파일도 생성되는데, 여기에는 반복횟수에 대한 메타정보 등이 key-value 형태로 들어가 있다.

 

 

두 번째 결과

# 만약 저장된 모델과 파라미터가 있으면 이를 불러오고 (Restore)
# Restored 모델을 이용해서 테스트 데이터에 대한 정확도를 출력하고 프로그램을 종료
if latest_ckpt:
  ckpt.restore(latest_ckpt)
  print("테스트 데이터 정확도 (Restored) : %f" % compute_accuracy(CNN_model(x_test)[0], y_test))
  exit()

실행코드 중 이 if문의 마지막 exit() 함수로 인해서 다시 실행하면 이전 학습했던 상태까지의 정확도를 측정한 후 프로그램이 종료됨을 알 수 있다.

Regularization 기법

: 오버피팅(OverFitting)을 방지할 수 있도록 만들어주는 기법을 총칭하는 말

 

오버피팅? 이전 글 참고

2021.10.14 - [Study/Deep Learning] - 머신러닝 Data 종류

 

머신러닝 Data 종류

머신러닝 모델은 크게 트레이닝 과정과 테스트 과정으로 나뉜다. ▷ 트레이닝 과정 - 대량의 데이터와 충분한 시간을 들여 모델의 최적 파라미터를 찾음 ▷ 테스트 과정 - 트레이닝 과정에서 구

these-dayss.tistory.com

 

 

Dropout이 대표적인 Regularization 기법 중 하나

= 학습 과정에서 일부 노드를 사용하지 않는 형태로 만들어서 오버피팅을 방지할 수 있도록 만들어주는 기법

 

드롭아웃 기법 / 출처 https://sacko.tistory.com/45

%를 정해서 일부 노드를 배제.

적은 노드 개수를 이용해 전체 특징을 학습시켜야 하기 때문에 모델의 압축적인 표현으로 효율성이 높아지게 된다.

 

Training Data에 대해서는 오버피팅을 방지하기 위해서  Dropout을 수행하지만 Test Data에 대해서는 Dropout을 수행하지 않는다.

 

 

 

<Tensorflow에서 제공하는 드롭아웃 API>

 

tf.nn.dropout

 

https://www.tensorflow.org/api_docs/python/tf/nn/dropout

 

tf.nn.dropout  |  TensorFlow Core v2.6.0

Computes dropout: randomly sets elements to zero to prevent overfitting.

www.tensorflow.org

 

'Study > Deep Learning' 카테고리의 다른 글

TensorBoard 이용하기  (0) 2021.10.18
tf.train.CheckpointManager API  (0) 2021.10.18
TensorFlow 2.0을 이용한 MNIST 숫자분류를 위한 CNN 구현  (0) 2021.10.17
CNN(Convolutional Neural Networks)  (0) 2021.10.17
CNN 의 등장  (0) 2021.10.16
# -*- coding: utf-8 -*-
# Convolutional Neural Networks(CNN)을 이용한 MNIST 분류기(Classifier) - Keras API를 이용한 구현

import tensorflow as tf

# 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)

# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옴
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.repeat().shuffle(60000).batch(50)
train_data_iter = iter(train_data)

여기까지는 앞서 실습했던 코드와 같다.

 

2021.10.15 - [Study/Deep Learning] - TensorFlow 2.0과 Softmax Regression을 이용한 MNIST 숫자분류기 구현

 

TensorFlow 2.0과 Softmax Regression을 이용한 MNIST 숫자분류기 구현

<가설 정의> 1. MNIST 데이터를 불러와 학습하기 적합한 형태로 변형 # -*- coding: utf-8 -*- import tensorflow as tf # MNIST 데이터를 다운로드 (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mn..

these-dayss.tistory.com

 

 

# tf.keras.Model을 이용해서 CNN 모델을 정의
class CNN(tf.keras.Model):
  def __init__(self):
    super(CNN, self).__init__()
    # 첫번째 Convolution Layer
    # 5x5 Kernel Size를 가진 32개의 Filter를 적용
    self.conv_layer_1 = tf.keras.layers.Conv2D(filters=32, kernel_size=5, strides=1, padding='same', activation='relu')
    self.pool_layer_1 = tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=2)

    # 두번째 Convolutional Layer
    # 5x5 Kernel Size를 가진 64개의 Filter를 적용
    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=(2, 2), strides=2)

    # Fully Connected Layer
    # 7x7 크기를 가진 64개의 activation map을 1024개의 특징들로 변환
    self.flatten_layer = tf.keras.layers.Flatten() #Flattening 수행하는 함수
    self.fc_layer_1 = tf.keras.layers.Dense(1024, activation='relu')

    # Output Layer
    # 1024개의 특징들(feature)을 10개의 클래스-one-hot encoding으로 표현된 숫자 0~9-로 변환
    self.output_layer = tf.keras.layers.Dense(10, activation=None)

  def call(self, x):
    # CNN 같은 경우 인풋 데이터를 Flattening 된 1차원 데이터로 받을 필요가 없음! 따라서 이미지 Dimesion 자체를 인풋으로 받음
    # MNIST 데이터를 3차원 형태로 reshape. MNIST 데이터는 grayscale 이미지기 때문에 3번째차원(컬러채널)의 값은 1.
    x_image = tf.reshape(x, [-1, 28, 28, 1])
    # 28x28x1 -> 28x28x32
    h_conv1 = self.conv_layer_1(x_image)
    # 28x28x32 -> 14x14x32
    h_pool1 = self.pool_layer_1(h_conv1)
    # 14x14x32 -> 14x14x64
    h_conv2 = self.conv_layer_2(h_pool1)
    # 14x14x64 -> 7x7x64
    h_pool2 = self.pool_layer_2(h_conv2)
    # 7x7x64(3136) -> 1024
    h_pool2_flat = self.flatten_layer(h_pool2)
    h_fc1 = self.fc_layer_1(h_pool2_flat)
    # 1024 -> 10
    logits = self.output_layer(h_fc1)
    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.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))

# 최적화를 위한 Adam 옵티마이저를 정의
optimizer = tf.optimizers.Adam(1e-4)  #0.0001

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):  #실제 Gradient Descent 한번 수행
  with tf.GradientTape() as tape:
    y_pred, logits = model(x)
    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):
  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
  
# Convolutional Neural Networks(CNN) 모델을 선언
CNN_model = CNN()

# 10000 Step만큼 최적화를 수행
for i in range(10000):
  # 50개씩 MNIST 데이터를 불러옴
  batch_x, batch_y = next(train_data_iter)
  # 100 Step마다 training 데이터셋에 대한 정확도를 출력
  if i % 100 == 0:
    train_accuracy = compute_accuracy(CNN_model(batch_x)[0], batch_y)
    print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f" % (i, train_accuracy))
  # 옵티마이저를 실행해 파라미터를 한스텝 업데이트
  train_step(CNN_model, batch_x, batch_y)

# 학습이 끝나면 학습된 모델의 정확도를 출력
print("정확도(Accuracy): %f" % compute_accuracy(CNN_model(x_test)[0], y_test))

 

결과

10000번의 epoch 결과, 정확도는 99% 정도가 나왔다. 앞서 실습했던 모델들과 비교하면 CNN 구조의 정확도가 월등히 높은 것을 확인할 수 있다.

'Study > Deep Learning' 카테고리의 다른 글

tf.train.CheckpointManager API  (0) 2021.10.18
드롭아웃(Dropout)  (0) 2021.10.17
CNN(Convolutional Neural Networks)  (0) 2021.10.17
CNN 의 등장  (0) 2021.10.16
오토인코더 Autoencoder  (0) 2021.10.15

컨볼루션 신경망, CNN

:: 이미지 분야를 다루는데 최적화 된 인공신경망 구조

: 크게 컨볼루션층(Convolution Layer), 풀링층(Pooling, 서브샘플링 Subsampling Layer) 으로 구성되어 있다.

CNN 구조/ 출처 http://taewan.kim/post/cnn/

 


1. 컨볼루션 Convolution 층

: 컨볼루션 연산(합성곱 연산)을 통해서 이미지의 특징을 추출해내는 역할

 

- 컨볼루션

: 커널(Kernel) 또는 필터(Filter)라고 불리는 윈도우 크기만큼의 X×X 크기의 행렬을 Y×Y 크기의 이미지 행렬의 X×X 크기

부분과 곱해서 모두 더하는 수학 연산자

 

출처 https://halfundecided.medium.com/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-cnn-convolutional-neural-networks-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-836869f88375

 

이렇게 원본 이미지에 커널을 이용해서 컨볼루션을 수행하면 커널의 종류에 따라 원본 이미지의 특징들(Features)이 활성화 맵(Activation Map) 으로 추출되게 된다.

 

이때, 어떤 커널을 사용하느냐에 따라 원본 이미지에서 다양한 특징을 추출할 수 있다.

커널 종류에 따른 다양한 추출 이미지 / 출처 https://en.wikipedia.org/wiki/Kernel_(image_processing)

 

이렇게 추출한 활성화 맵은 원본 이미지에서 명확히 드러나지 않던 특징들을 보여준다.

자동차와 사람을 분류하고자 할 때, 원본 이미지 자체를 사용하는 것보다 모서리만 추출된 특징 이미지를 사용하는 것이 더 효율적일 것이다.

 


2. 풀링 Pooling 층

: 차원을 축소하는 연산 수행

 

종류?

- 최대값 풀링 Max Pooling , 평균값 풀링 Average Pooling, 최소값 풀링 Min Pooling 등

 

Max Pooling / 출처 https://developers.google.com/machine-learning/practica/image-classification/convolutional-neural-networks

최대값 풀링은 이미지의 X×X 크기 부분에서 가장 큰 값 하나를 추출해서 원본 이미지의  X×X개의 값을 1개의 값으로 축소한다.

 

동일한 원리로 평균값 풀링, 최소값 풀링은 평균값, 최소값으로 축소한다.

풀링층 역시 이미지의 좌측 상단에서 우측하단으로 순차적으로 전체 이미지에 대해 풀링을 수행한다.

 

 

이러한 이미지 차원 축소를 통해 필요한 연산량을 감소시킬 수 있고, 이미지의 가장 강한 특징만을 추출하는 특징 선별 효과가 있다. 

 

 


→ 컨볼루션층을 거치면 인풋 이미지의 가로, 세로 차원이 축소된다. 

출력 이미지 가로 길이 계산법

(W in = 인풋 이미지의 가로 길이, W out = 출력 이미지의 가로 길이, F = 필터의 크기, S = 스트라이드)

 

+) 스트라이드? 컨볼루션 연산시 건너뛰는 정도

스트라이드가 크면 컨볼루션 수행 시 차원이 많이 축소되고 스트라이드가 작으면 차원이 조금 축소된다.

 

또한 모든 차원이 정수로 나누어 떨어지지 않을 수 있으므로 인풋 이미지의 상하좌우 모서리에 P만큼 0을 채워주는 제로패딩(Zero-Padding)을 P만큼 적용해준다.

제로 패딩 / 출처 https://excelsior-cjh.tistory.com/180 

 

 

출력 이미지 세로 길이 계산법

출력 이미지 세로 길이 계산법은 가로 길이 계산법과 같다.

 

 

 

마지막으로, 컨볼루션층의 출력결과와 3번째 차원은 컨볼루션 필터 개수 K가 된다.

따라서 컨볼루션층의 결과로 출력되는 차원은 [W out, H out, K] 이다.

 

예를 들어, [28x28x1] MNIST 이미지에 4x4 크기의 필터(F=4)에 스트라이드가 2(S=2)이고, 제로 패딩을 1만큼 적용한(P=1) 64개의 필터개수(K=64)를 가진 컨볼루션층을 적용하면 출력 결과로 [14,14,64]

즉, 14x14 크기의 64개의 활성화맵이 추출된다.

 

 

이런 과정을 거친 활성화 맵들은 마지막에 Flattening으로 펼친 다음 완전연결층(ANN)의 인풋으로 들어가서 Softmax 분류를 수행하게 된다.

사람은 고양이를 판단할 때,

엎드려 있던지, 귀가 가려져 있던지, 꼬리만 보이던지, 줄무늬가 있던지/없던지, 배경과 비슷한 색이던지 

대부분 고양이라고 판단할 수 있다.

 

하지만 컴퓨터는 모든 픽셀을 0-255 단위의 숫자로 보고 판단하기 때문에 판단하는데 어려움을 겪을 수 있다.

 

따라서 사람이 사물의 특징을 기술한 Handcrafted Feature 로 기계를 학습시키면 해결이 될 것이라 판단했지만 이는 한계가 있었다. 

 

이 후 End-To-End Learning(종단간 기계학습) 방법이 등장하게 되는데, 이는 입력 및 출력을 직접 고려하여 네트워크 가중치를 최적화 하는 학습을 말한다. 

즉, 데이터만 사람이 넣어주고 중간 부분을 딥러닝 모델에 위임한다는 것이다. 따라서 사람이 많은 개수의 데이터(input, output) 만 모아준다면 자동으로 학습하도록 한다. 

 

 

여기서 CNN의 개념이 등장한다.

CNN은 Convolutional Nerual Networks의 약자로 전처리 작업이 들어가는 딥러닝 모델이다. 

이는 Raw Image들로부터 특징들(Features)을 자동으로 학습하도록 하는 것이 목표이다.

 

CNN의 중요 포인트는 이미지 전체보다는 부분을 보는 것, 이미지의 한 픽셀과 주변 픽셀들의 연관성을 살리는 것이다.

 

오토인코더?

:: 대표적인 비지도 학습(Unsupervised Learning, 데이터의 숨겨진 특징을 찾는 것이 목표)을 위한 인공신경망 구조 중 하나

출처 https://excelsior-cjh.tistory.com/187

출력층 Output Layer의 노드 개수와 입력층 Input Layer의 노드 개수가 동일한 구조의 인공신경망이라고 생각하면 된다.

 

따라서, 오토인코더의 출력은 원본 데이터 Raw Data를 재구축 Reconstruction한 결과가 된다.

 

똑같은 결과가 나오는데 이를 왜 사용할까?

오토인코더의 핵심은 은닉층의 출력값이다.

 

오토인코더의 구조를 보면 입력층과 출력층의 노드 개수보다 은닉층의 노드 개수가 적다는 것을 알 수 있다.

더 작은 표현력으로 원본 데이터의 모든 특징들을 학습해야 하기 때문에 이 출력값은 원본 데이터에서 불필요한 특징들을 제거한 압축된 특징들을 학습하게 된다.

따라서 은닉층의 출력값을 원본 데이터 대신에 분류기 Classifier의 입력으로 사용한다면 더욱 좋은 분류 성능을 기대할 수 있다.

 

 

 

 

 

2021.10.15 - [Study/Deep Learning] - TensorFlow 2.0과 Softmax Regression을 이용한 MNIST 숫자분류기 구현

 

TensorFlow 2.0과 Softmax Regression을 이용한 MNIST 숫자분류기 구현

<가설 정의> 1. MNIST 데이터를 불러와 학습하기 적합한 형태로 변형 # -*- coding: utf-8 -*- import tensorflow as tf # MNIST 데이터를 다운로드 (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mn..

these-dayss.tistory.com

이전 글을 참고하면 이해가 잘 갈 것이다.

 

 

# -*- coding: utf-8 -*-
# 텐서플로우를 이용한 ANN(Artificial Neural Networks) 구현 - Keras API를 이용한 구현

import tensorflow as tf

# 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)

# 학습을 위한 설정값들을 정의
learning_rate = 0.001
num_epochs = 30     # 학습횟수
batch_size = 256    # 배치개수
display_step = 1    # 손실함수 출력 주기
input_size = 784    # 28 * 28
hidden1_size = 256
hidden2_size = 256
output_size = 10

# tf.data API를 이용해서 데이터를 섞고 batch 형태로 가져옴
train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.shuffle(60000).batch(batch_size) # 한번 epoch가 끝날 때마다 셔플(섞어줌)

# 초기 W값과 b 값을 초기화
def random_normal_intializer_with_stddev_1():
  return tf.keras.initializers.RandomNormal(mean=0.0, stddev=1.0, seed=None)

대충 그리자면 이런 구조다

# tf.keras.Model을 이용해서 ANN 모델을 정의
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

 

 

# cross-entropy 손실 함수를 정의
@tf.function
def cross_entropy_loss(logits, y):
  return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))

# 최적화를 위한 Adam 옵티마이저를 정의, 기본 옵티마이저를 업그레이드 한 것. 미분값이 0인 곳도 넘어감
optimizer = tf.optimizers.Adam(learning_rate)

# 최적화를 위한 function을 정의
@tf.function
def train_step(model, x, y):  #한 번의 Gradient Descent 수행
  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))

# 모델의 정확도를 출력하는 함수를 정의
@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 모델을 선언
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), cross_entropy_loss(ANN_model(batch_x), batch_y)
    # 평균 손실을 측정
    average_loss += current_loss / total_batch
  # 지정된 epoch마다 학습결과를 출력
  if epoch % display_step == 0:
    print("반복(Epoch): %d, 손실 함수(Loss): %f" % ((epoch+1), average_loss))

# 테스트 데이터를 이용해서 학습된 모델이 얼마나 정확한지 정확도를 출력
print("정확도(Accuracy): %f" % compute_accuracy(ANN_model(x_test), y_test)) # 정확도: 약 94%

 

 

결과

epoch 횟수 별로 손실함수 값이 어떻게 변화하는지 알 수 있다.

(한 번의 epoch는 인공 신경망에서 전체 데이터 셋에 대해 forward pass/backward pass 과정을 거친 것을 말함. 즉, 전체 데이터 셋에 대해 한 번 학습을 완료한 상태)

 

epoch가 반복되면서 손실함수 값이 점진적으로 감소하는 것을 알 수 있고

30번 모두 끝난 후의 모델의 학습 정확도를 계산해보면 약 94%임을 알 수 있다.

인공신경망(ANN, Artificial Neural Network)

 

인간의 뇌 : 엄청난 양의 병렬 처리 연산기(by 뉴런의 생물학적 구조)

컴퓨터 : 순차 처리 연산기 -> 단순한 기초 연산은 인간보다 훨씬 뛰어남

 

"컴퓨터도 인간의 뇌처럼 대량의 병렬 처리 연산을 수행하도록 한다면 컴퓨터도 인간이 쉽게 할 수 있는 인지행동을 할 수 있지 않을까?"

 

 

<퍼셉트론 Perceptron>

생물학적 뉴런을 공학적인 구조로 변형한 그림, 가중치에 기반한 의사결정모델

 

- 가중치 : Input의 중요도를 나타냄

출처 https://liveyourit.tistory.com/63

- 입력층과 출력층을 가지고 있다.

 

퍼셉트론 동작과정

- 입력층에서 인풋 데이터 x 를 받고, 이를 가중치 W와 곱한 후, 편향 b를 더한다.

이 값을 활성함수의 입력값으로 대입하여 출력층은 최종적으로 0 또는 1의 값을 출력한다.

 

계단 함수

 

퍼셉트론은 활성 함수로 계단 함수(Step Function)을 사용해서 값이 0보다 크면 1, 0보다 작으면 0을 출력한다.

 

이진분류

즉, 퍼셉트론은 입력값을 받으면 2개의 출력값 중 하나를 출력해내는 선형 이진분류기(Linear Binary Classifier)이다.

 

선형분류로 처리할 수 없는 XOR 문제(3번째)                                        출처 https://steemit.com/kr-steemit/@beseeyong/xor

--> 단순한 선형분류기에 불과하며, 간단한 XOR 문제도 해결할 수 없다는 사실이 증명되며 인기가 사그라들었음

 


<다층 퍼셉트론 MLP (Multi-Layer Perceptron) = 인공신경망 ANN>

선형 분리가 불가능한 문제도 해결할 수 있다는 사실이 밝혀지며 다시 인공신경망 연구를 계속하게 된다.

 

- 다층 퍼셉트론은 입력층과 은닉층, 출력층으로 구성

은닉층은 입출력 과정에서 직접적으로 보이진 않지만 숨겨진 특징을 학습하는 역할

출처 https://m.blog.naver.com/samsjang/221030487369

 

이는 활성함수로 비선형 함수(시그모이드 Sigmoid와 쌍곡 탄젠트 Tangent Hyperbolic, ReLU)를 사용한다.

 

앞 두 함수(시그모이드, 쌍곡탄젠트)는 Input data가 특정 값을 넘어가면 미분값이 0이 되는 현상 "Vanishing Gradient Problem(기울기값이 사라지는 문제)" 이 발생한다. 

ReLU에선 양수 데이터에 대해선 미분값이 살아있어 이 문제가 줄어들기 때문에 최근엔 이 함수를 많이 사용한다.

 

 

이렇게 입력층과 출력층 사이에 여러개의 은닉층이 있는 인공신경망ANN을 심층 신경망(Deep Neural Network)라고 부르며, 심층 신경망을 학습하기 위해 고안된 알고리즘을 딥러닝(Deep Learning)이라고 부르는 것이다.

+ Recent posts