Tensorflow 2.0 Tutorial ch6.4 - 모형의 성능 높이기

Page content

공지

  • 본 Tutorial은 교재 시작하세요 텐서플로 2.0 프로그래밍의 강사에게 국비교육 강의를 듣는 사람들에게 자료 제공을 목적으로 제작하였습니다.

  • 강사의 주관적인 판단으로 압축해서 자료를 정리하였기 때문에, 자세하게 공부를 하고 싶은 반드시 교재를 구매하실 것을 권해드립니다.

  • 본 교재 외에 강사가 추가한 내용에 대한 Reference를 확인하셔서, 추가적으로 학습하시는 것을 권유드립니다.

Tutorial

이전 강의가 궁금하신 분들은 아래에서 선택하여 추가 학습 하시기를 바랍니다.

I. 개요

성능을 높이는 데는 그 중 대표적이면서 쉬운 두 가지 방법은 더 많은 레이어 쌓기이미지 보강(Image Augmentation) 기법입니다.

II. 더 많은 레이어 쌓기

딥러닝의 역사는 더 깊은 신경망을 쌓기 위한 노력이라고 해도 과언이 아닙니다. 컨볼루션 레이어가 중첩된 더 깊은 구조가 계속해서 나타났고, 그럴 때마다 이전 구조의 성능을 개선 시켰습니다. (Teerapittayanon, et. all., 2017)1

이번 예제에서는 VGGNet의 스타일로 구성한 컨볼루션 신경망을 응용 및 사용합니다.

III. VGGNet 모형 정의

데이터를 불러온 뒤, 정규화를 진행합니다. 그리고 곧바로 모형을 만들도록 합니다.

(1) 데이터 불러오기 및 정규화

import tensorflow as tf
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_X, train_Y), (test_X, test_Y) = fashion_mnist.load_data()

train_X = train_X / 255.0
test_X = test_X / 255.0

# reshape 이전
print(train_X.shape, test_X.shape)

train_X = train_X.reshape(-1, 28, 28, 1)
test_X = test_X.reshape(-1, 28, 28, 1)

# reshape 이후
print(train_X.shape, test_X.shape)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step
(60000, 28, 28) (10000, 28, 28)
(60000, 28, 28, 1) (10000, 28, 28, 1)

(2) 모형 정의

이번에는 모형 정의를 진행합니다. 코드가 길어질 수 있으니 주의하기 바랍니다.

import tensorflow as tf

model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(input_shape=(28,28,1), kernel_size=(3,3), filters=32, padding='same', activation='relu'), 
  tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64, padding='same', activation='relu'),
  tf.keras.layers.MaxPool2D(pool_size=(2,2)), 
  tf.keras.layers.Dropout(rate=0.5), 
  tf.keras.layers.Conv2D(kernel_size=(3,3), filters=128, padding='same', activation='relu'),    
  tf.keras.layers.Conv2D(kernel_size=(3,3), filters=256, padding='valid', activation='relu'),  
  tf.keras.layers.MaxPool2D(pool_size=(2,2)),
  tf.keras.layers.Dropout(rate=0.5),
  tf.keras.layers.Flatten(), 
  tf.keras.layers.Dense(units=512, activation='relu'), 
  tf.keras.layers.Dropout(rate=0.5),
  tf.keras.layers.Dense(units=256, activation='relu'),
  tf.keras.layers.Dropout(rate=0.5),
  tf.keras.layers.Dense(units=10, activation='softmax')                           
])

model.compile(optimizer=tf.keras.optimizers.Adam(), 
              loss='sparse_categorical_crossentropy', 
              metrics=['accuracy'])

model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_4 (Conv2D)            (None, 28, 28, 32)        320       
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 28, 28, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 14, 14, 64)        0         
_________________________________________________________________
dropout_4 (Dropout)          (None, 14, 14, 64)        0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 14, 14, 128)       73856     
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 12, 12, 256)       295168    
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 6, 6, 256)         0         
_________________________________________________________________
dropout_5 (Dropout)          (None, 6, 6, 256)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 9216)              0         
_________________________________________________________________
dense_3 (Dense)              (None, 512)               4719104   
_________________________________________________________________
dropout_6 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 256)               131328    
_________________________________________________________________
dropout_7 (Dropout)          (None, 256)               0         
_________________________________________________________________
dense_5 (Dense)              (None, 10)                2570      
=================================================================
Total params: 5,240,842
Trainable params: 5,240,842
Non-trainable params: 0
_________________________________________________________________

VGGNet은 여러 개의 구조로 실험했는데 그 중 19개의 레이어가 겹쳐진 VGG-19가 제일 깊은 구조입니다. VGG-19는 특징 추출기의 초반에 컨볼루션 레이어를 2개 겹친 뒤 풀링 레이어 1개를 사용하는 패턴을 2차례, 그 후 컨볼루션 레이어를 4개 겹친 뒤 풀링 레이어 1개를 사용하는 패턴을 3차례 반복합니다.

그러나, 이 역시 대상 이미지의 크기 등에 따라 달라집니다.

여기에서는 컨볼루션 레이어를 2개 겹치고 풀링 레이어를 1개 사용하는 패턴을 2차례 반복합니다. 풀링 레이어 다음에 드롭아웃 레이러를 위치시켜서 과적합을 방지합니다. 또한, Flatten 레이어 다음에 이어지는 3개의 Dense 레이어 사이에도 드롭아웃 레이어를 배치합니다. 컨볼루션 레이어와 Dense 레이어의 개수만 세면 VGG-7레이어가 됩니다.

오리지널 VGG-19보다는 깊이가 얕지만 총 파라미터 개수는 약 520만개로 적지 않습니다. (ch6_3. 24만개)

(3) 모형 성능 확인

이 모델의 성능을 확인해봅니다. (이 때, 런타임 유형을 GPU로 변경 하는 것을 잊으면 안됩니다.)

history = model.fit(train_X, train_Y, epochs=25, validation_split=0.25)
Epoch 1/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.5858 - accuracy: 0.7878 - val_loss: 0.3329 - val_accuracy: 0.8759
Epoch 2/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.3676 - accuracy: 0.8678 - val_loss: 0.2709 - val_accuracy: 0.9017
Epoch 3/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.3306 - accuracy: 0.8815 - val_loss: 0.2607 - val_accuracy: 0.8999
Epoch 4/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.3043 - accuracy: 0.8901 - val_loss: 0.2482 - val_accuracy: 0.9171
Epoch 5/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2878 - accuracy: 0.8965 - val_loss: 0.2296 - val_accuracy: 0.9163
Epoch 6/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2759 - accuracy: 0.9005 - val_loss: 0.2454 - val_accuracy: 0.9056
Epoch 7/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2649 - accuracy: 0.9049 - val_loss: 0.2153 - val_accuracy: 0.9227
Epoch 8/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2626 - accuracy: 0.9041 - val_loss: 0.2187 - val_accuracy: 0.9204
Epoch 9/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2546 - accuracy: 0.9093 - val_loss: 0.2130 - val_accuracy: 0.9227
Epoch 10/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2534 - accuracy: 0.9085 - val_loss: 0.2186 - val_accuracy: 0.9227
Epoch 11/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2517 - accuracy: 0.9110 - val_loss: 0.2101 - val_accuracy: 0.9272
Epoch 12/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2427 - accuracy: 0.9122 - val_loss: 0.2111 - val_accuracy: 0.9221
Epoch 13/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2397 - accuracy: 0.9150 - val_loss: 0.2025 - val_accuracy: 0.9294
Epoch 14/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2396 - accuracy: 0.9150 - val_loss: 0.2115 - val_accuracy: 0.9220
Epoch 15/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2338 - accuracy: 0.9166 - val_loss: 0.2102 - val_accuracy: 0.9247
Epoch 16/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2347 - accuracy: 0.9157 - val_loss: 0.2063 - val_accuracy: 0.9285
Epoch 17/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2352 - accuracy: 0.9172 - val_loss: 0.2064 - val_accuracy: 0.9237
Epoch 18/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2290 - accuracy: 0.9180 - val_loss: 0.2118 - val_accuracy: 0.9237
Epoch 19/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2331 - accuracy: 0.9171 - val_loss: 0.2027 - val_accuracy: 0.9286
Epoch 20/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2254 - accuracy: 0.9200 - val_loss: 0.2003 - val_accuracy: 0.9299
Epoch 21/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2312 - accuracy: 0.9199 - val_loss: 0.2064 - val_accuracy: 0.9250
Epoch 22/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2277 - accuracy: 0.9194 - val_loss: 0.1962 - val_accuracy: 0.9290
Epoch 23/25
1407/1407 [==============================] - 8s 6ms/step - loss: 0.2278 - accuracy: 0.9188 - val_loss: 0.1952 - val_accuracy: 0.9341
Epoch 24/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2258 - accuracy: 0.9208 - val_loss: 0.1999 - val_accuracy: 0.9290
Epoch 25/25
1407/1407 [==============================] - 9s 6ms/step - loss: 0.2196 - accuracy: 0.9217 - val_loss: 0.2072 - val_accuracy: 0.9261

학습한 모형을 시각화로 다시 확인합니다.

import matplotlib.pyplot as plt
plt.figure(figsize=(12, 4))

plt.subplot(1,2,1)
plt.plot(history.history['loss'], 'b-', label='loss')
plt.plot(history.history['val_loss'], 'r--', label='val_loss')
plt.xlabel('Epoch')
plt.legend()

plt.subplot(1,2,2)
plt.plot(history.history['accuracy'], 'g-', label='accuracy')
plt.plot(history.history['val_accuracy'], 'r--', label='val_accuracy')
plt.xlabel('Epoch')
plt.ylim(0.7, 1)
plt.legend()

plt.show()

model.evaluate(test_X, test_Y, verbose=0)

png

[0.23273345828056335, 0.9169999957084656]

var_loss가 잘 증가하지 않는 드디어 괜찮은 모형을 얻었습니다. 또한 테스트 데이터에 대한 분류 성적도 91.69%로 비교적 높은 성과를 거뒀습니다. 이렇게 네트워크 구조 변경만으로도 성능이 향상 된 것을 확인할 수 있습니다.

IV. 이미지 보강

이미지 보강은 훈련데이터에 없는 이미지를 새롭게 만들어내서 훈련데이터를 보강하는 기법을 말합니다. Tensorflow에는 이미지 보강 작업을 쉽게 해주는 ImageDataGenerator가 있습니다. 이를 활용해서 훈련 데이터의 첫 번째 이미지를 변형시킵니다.

그리고 100개의 변형된 이미지를 그래프로 작성합니다.

(1) 이미지 보강의 예

from tensorflow.keras.preprocessing.image import ImageDataGenerator
import numpy as np

image_generator = ImageDataGenerator(
    rotation_range=10, 
    zoom_range=0.10, 
    shear_range=0.5, 
    width_shift_range=0.10, 
    height_shift_range=0.10, 
    horizontal_flip=True,
    vertical_flip=False)

augment_size = 100

x_augmented = image_generator.flow(np.tile(train_X[0].reshape(28*28), 100).reshape(-1, 28, 28, 1), 
                                   np.zeros(augment_size), batch_size=augment_size, shuffle=False).next()[0]

# 새롭게 생성된 이미지 표시
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 10))
for c in range(100):
  plt.subplot(10, 10, c+1)
  plt.axis('off')
  plt.imshow(x_augmented[c].reshape(28, 28), cmap='gray')

plt.show()

png

ImageDataGenerator의 주요 인수들에 구체적인 설명은 교재 169페이지를 참고하시기를 바랍니다. 또는 한글 번역문서가 있으니 해당 문서에서 추가적인 공부를 진행하기를 바랍니다. (ImageDataGenerator 클래스)

(2) 3만개 이미지 보강

그러면 실제로 이미지를 보강하고 모형 학습에 추가하는 예제를 진행합니다. 전체적인 모형 학습 코드는 크게 달라지지 않으니, 기 진행했던 내용을 그대로 참조합니다.

import tensorflow as tf
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_X, train_Y), (test_X, test_Y) = fashion_mnist.load_data()

train_X = train_X / 255.0
test_X = test_X / 255.0

# reshape 이전
print(train_X.shape, test_X.shape)

train_X = train_X.reshape(-1, 28, 28, 1)
test_X = test_X.reshape(-1, 28, 28, 1)

# reshape 이후
print(train_X.shape, test_X.shape)

image_generator = ImageDataGenerator(
    rotation_range=10, 
    zoom_range=0.10, 
    shear_range=0.5, 
    width_shift_range=0.10, 
    height_shift_range=0.10, 
    horizontal_flip=True,
    vertical_flip=False)

augment_size = 30000

randidx = np.random.randint(train_X.shape[0], size=augment_size)
x_augmented = train_X[randidx].copy()
y_augmented = train_Y[randidx].copy()
x_augmented = image_generator.flow(x_augmented, np.zeros(augment_size), batch_size=augment_size, shuffle=False).next()[0]

# 원래 데이터인 x_train에 이미지 보강된 x_augmented를 추가합니다. 
train_X = np.concatenate((train_X, x_augmented))
train_Y = np.concatenate((train_Y, y_augmented))

print(train_X.shape)
(60000, 28, 28) (10000, 28, 28)
(60000, 28, 28, 1) (10000, 28, 28, 1)
(90000, 28, 28, 1)
  • 훈련 데이터의 50%인 30,000개의 이미지를 추가하기 위해 augment_size=30000으로 설정합니다.
  • 이미지를 변형할 원본 이미지를 찾기 위해 np.random.randint()함수를 활용하여 0~59,999 범위의 정수 중에서 30,000개의 정수를 뽑았습니다. 이 때 정수는 중복 가능합니다.
    • 만약, 중복을 원치 않으면, np.random.ranint() 대신에 np.random.choice() 함수를 사용하고 replace 인수를 False로 설정합니다.
  • copy() 함수를 사용하여 원본은 보전합니다.
  • image_generator.flow() 함수를 사용하여 30,000개의 새로운 이미지를 생성했습니다.
  • np.concatenate() 함수를 사용하여 훈련 데이터에 보강 이미지를 추가합니다. 그러면, 이미지 보강이 완료가 된 것입니다.

(3) 모형 정의

이제 보강된 훈련데이터를 VGGNet 스타일의 네트워크에 추가하도록 합니다.

import tensorflow as tf

model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(input_shape=(28,28,1), kernel_size=(3,3), filters=32, padding='same', activation='relu'), 
  tf.keras.layers.Conv2D(kernel_size=(3,3), filters=64, padding='same', activation='relu'),
  tf.keras.layers.MaxPool2D(pool_size=(2,2)), 
  tf.keras.layers.Dropout(rate=0.5), 
  tf.keras.layers.Conv2D(kernel_size=(3,3), filters=128, padding='same', activation='relu'),    
  tf.keras.layers.Conv2D(kernel_size=(3,3), filters=256, padding='valid', activation='relu'),  
  tf.keras.layers.MaxPool2D(pool_size=(2,2)),
  tf.keras.layers.Dropout(rate=0.5),
  tf.keras.layers.Flatten(), 
  tf.keras.layers.Dense(units=512, activation='relu'), 
  tf.keras.layers.Dropout(rate=0.5),
  tf.keras.layers.Dense(units=256, activation='relu'),
  tf.keras.layers.Dropout(rate=0.5),
  tf.keras.layers.Dense(units=10, activation='softmax')                           
])

model.compile(optimizer=tf.keras.optimizers.Adam(), 
              loss='sparse_categorical_crossentropy', 
              metrics=['accuracy'])

# model.summary()
history = model.fit(train_X, train_Y, epochs=25, validation_split=0.25)

import matplotlib.pyplot as plt
plt.figure(figsize=(12, 4))

plt.subplot(1,2,1)
plt.plot(history.history['loss'], 'b-', label='loss')
plt.plot(history.history['val_loss'], 'r--', label='val_loss')
plt.xlabel('Epoch')
plt.legend()

plt.subplot(1,2,2)
plt.plot(history.history['accuracy'], 'g-', label='accuracy')
plt.plot(history.history['val_accuracy'], 'r--', label='val_accuracy')
plt.xlabel('Epoch')
plt.ylim(0.7, 1)
plt.legend()

plt.show()

model.evaluate(test_X, test_Y, verbose=0)
Epoch 1/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.5830 - accuracy: 0.7865 - val_loss: 0.5803 - val_accuracy: 0.7819
Epoch 2/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.3876 - accuracy: 0.8607 - val_loss: 0.5290 - val_accuracy: 0.8055
Epoch 3/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.3417 - accuracy: 0.8757 - val_loss: 0.4409 - val_accuracy: 0.8329
Epoch 4/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.3224 - accuracy: 0.8836 - val_loss: 0.4393 - val_accuracy: 0.8370
Epoch 5/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.3097 - accuracy: 0.8900 - val_loss: 0.4315 - val_accuracy: 0.8379
Epoch 6/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2997 - accuracy: 0.8927 - val_loss: 0.4283 - val_accuracy: 0.8452
Epoch 7/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2944 - accuracy: 0.8938 - val_loss: 0.4000 - val_accuracy: 0.8514
Epoch 8/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2907 - accuracy: 0.8967 - val_loss: 0.4203 - val_accuracy: 0.8428
Epoch 9/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2797 - accuracy: 0.9010 - val_loss: 0.3741 - val_accuracy: 0.8618
Epoch 10/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2835 - accuracy: 0.8986 - val_loss: 0.4227 - val_accuracy: 0.8424
Epoch 11/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2748 - accuracy: 0.9006 - val_loss: 0.3786 - val_accuracy: 0.8586
Epoch 12/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2764 - accuracy: 0.9018 - val_loss: 0.3736 - val_accuracy: 0.8668
Epoch 13/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2726 - accuracy: 0.9027 - val_loss: 0.3757 - val_accuracy: 0.8656
Epoch 14/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2737 - accuracy: 0.9023 - val_loss: 0.3790 - val_accuracy: 0.8589
Epoch 15/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2717 - accuracy: 0.9037 - val_loss: 0.3779 - val_accuracy: 0.8610
Epoch 16/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2689 - accuracy: 0.9049 - val_loss: 0.3768 - val_accuracy: 0.8643
Epoch 17/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2636 - accuracy: 0.9051 - val_loss: 0.3831 - val_accuracy: 0.8601
Epoch 18/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2686 - accuracy: 0.9045 - val_loss: 0.4098 - val_accuracy: 0.8484
Epoch 19/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2646 - accuracy: 0.9059 - val_loss: 0.3696 - val_accuracy: 0.8647
Epoch 20/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2630 - accuracy: 0.9078 - val_loss: 0.3510 - val_accuracy: 0.8728
Epoch 21/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2599 - accuracy: 0.9087 - val_loss: 0.3880 - val_accuracy: 0.8584
Epoch 22/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2626 - accuracy: 0.9072 - val_loss: 0.3749 - val_accuracy: 0.8673
Epoch 23/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2604 - accuracy: 0.9078 - val_loss: 0.3811 - val_accuracy: 0.8642
Epoch 24/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2619 - accuracy: 0.9088 - val_loss: 0.3790 - val_accuracy: 0.8585
Epoch 25/25
2110/2110 [==============================] - 13s 6ms/step - loss: 0.2629 - accuracy: 0.9075 - val_loss: 0.3693 - val_accuracy: 0.8688

png

[0.2110530138015747, 0.9251000285148621]

소스코드는 기존에 했던 코드와 차이가 없기 때문에, 코드에 대한 설명은 생략합니다. 그래프 역시, 안정적인 것으로 봐서는 과적합은 일어나지 않았습니다. 그리고, 결과 역시 기존 분류 성적 91.69%보다는 성능이 향상된 92.51%값을 얻었습니다.

이렇게 해서 더 많은 레이어를 쌓는 것과 이미지 보강 기법을 통해서 모형을 성능시키는 것까지 배웠습니다.

V. 결론

이번장에서는 가장 중요한 개념은 컨볼루션 레이어의 개념, 풀링 레이어의 개념, 그리고 드롭아웃 레이어의 개념입니다. 각각의 역할은 조금씩 상이하다는 것을 꼭 기억하기를 바랍니다.

VI. 연습 파일

VII. Reference

김환희. (2020). 시작하세요! 텐서플로 2.0 프로그래밍: 기초 이론부터 실전 예제까지 한번에 끝내는 머신러닝, 딥러닝 핵심 가이드. 서울: 위키북스.


  1. Teerapittayanon, S., McDanel, B., & Kung, H.T. (2017). Distributed Deep Neural Networks Over the Cloud, the Edge and End Devices. 2017 IEEE 37th International Conference on Distributed Computing Systems (ICDCS), 328-339. ↩︎