딥러닝 학습의 문제점

이재경·2023년 1월 12일
0

인공지능

목록 보기
12/14

GD vs SGD(Stochastic Gradient Descent)

출처:엘리스

GD(Gradient Descent) 는 시작 지점에서 기울기의 반대 방향으로 하강하면서 손실 함수(loss function)를 최소화하는 지점을 찾기 위한 가장 직관적인 방법입니다. 이처럼 전체 데이터 셋을 가지고 학습하게 되면 안정적이긴 하지만, 계산량과 학습 비용이 많아지게 됩니다.

이때 전체 데이터 셋이 아닌, 무작위로 뽑은 데이터들에 대한 Gradient Descent를 진행하고, 이를 반복하며 정확도를 찾아 나가는 것을 SGD(Stochastic Gradient Descent)라고 합니다.

이번 실습에서는 동일한 모델 생성 및 학습을 통하여 두 최적화 기법을 비교해보도록 하겠습니다.

데이터셋은 IMDB 영화 리뷰 데이터 셋을 사용합니다. 해당 데이터셋은 훈련용 데이터 25,000개와 테스트용 데이터 25,000개로 이루어져 있으며, 레이블은 긍정/부정으로 두 가지입니다. 이때 긍정은 1, 부정은 0으로 표시되어 있습니다. 우리의 목표는 전처리된 영화 리뷰 데이터를 가지고 그 리뷰가 긍정적인지 혹은 부정적인지를 예측하는 것입니다.

모델 구조 확인과 학습을 위한 함수/라이브러리

model.summary()
: 생성한 모델의 구조를 확인합니다.

history = model.fit(train_data, train_labels, epochs = 20, batch_size = 500, validation_data = (test_data, test_labels), verbose = 0)
: 모델을 학습시킬 때 검증용 데이터를 설정하는 방법입니다. 이전 장의 실습과는 다르게, 이번에는 모델을 학습시킬 때 검증용 데이터(validation_data)를 설정합니다.

실습
GD를 적용할 모델을 자유롭게 생성합니다.
모델을 생성할 때 다음 사항을 참고합니다.

입력층의 input_shape 인자를 어떻게 설정해야 할지 생각해보세요.
출력층의 노드 수는 1, 활성화 함수는 sigmoid로 설정하세요.
히든층은 자유롭게 설계하세요.
모델 생성 방법은 이전 장의 실습을 참고하세요.

SGD를 적용할 모델을 GD를 적용할 모델과 똑같이 생성합니다. 우리는 두 모델을 똑같이 만들되, 학습 시 batch_size를 다르게 해서 각각 GD와 SGD를 적용할 것입니다.

두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

두 모델에 대해 손실 함수, 최적화(optimize) 알고리즘, 평가 방법(metrics)은 다음과 같이 설정합니다.

손실 함수(loss) : ‘binary_crossentropy’

최적화 알고리즘(optimizer) : ‘sgd’

평가 방법(metrics): [‘accuracy’, ‘binary_crossentropy’]

손실 함수로는 ‘binary crossentropy‘를 사용합니다. binary crossentropy는 두 확률 분포 간의 차이를 측정하는 손실 함수로써,값이 작을수록 모델의 예측 결과가 좋다는 뜻입니다.

batch_size 설정은 스켈레톤 코드의 주석 설명을 참고해주세요.

Tips!
모델 학습(fit) 과정에서 verbose 변수를 활용하면 모델의 학습 과정 기록을 간단하게 표현할 수 있습니다. 이후 실습에서도 이를 활용해서 학습 과정 기록을 확인하세요.

verbose = 0: 기록을 나타내지 않음.
verbose = 1: 진행 바 형태의 학습 과정 기록을 나타냄.
verbose = 2: epoch 당 1줄의 학습 과정 기록을 나타냄.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

# 데이터를 전처리하는 함수

def sequences_shaping(sequences, dimension):
    
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0 
        
    return results

'''
1. GD를 적용할 모델을 자유롭게 생성합니다.
'''

def GD_model(word_num):
    
    model = tf.keras.Sequential([tf.keras.layers.Dense(32,input_shape=(word_num,),activation='relu'),tf.keras.layers.Dense(32,activation='relu'),tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return model
    
'''
2. SGD를 적용할 모델을 GD를 적용할 모델과 똑같이 생성합니다.
'''

def SGD_model(word_num):
    
    model = tf.keras.Sequential([tf.keras.layers.Dense(32,input_shape=(word_num,),activation='relu'),tf.keras.layers.Dense(32,activation='relu'),tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return model

'''
3. 두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. GD 함수와 SGD 함수를 이용해 
           두 모델을 불러옵니다.
   
   Step02. 두 모델의 손실 함수, 최적화 알고리즘, 
           평가 방법을 설정합니다.
   
   Step03. 두 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 두 모델을 각각 학습시킵니다. 
           검증용 데이터도 설정해주세요.
           'epochs'는 20으로 설정합니다.
   
           GD를 적용할 경우 학습 시 
           전체 데이터 셋(full-batch)을
           사용하므로 'batch_size'를 
           전체 데이터 개수로 설정합니다. 
           
           SGD를 적용할 경우 학습 시 
           미니 배치(mini-batch)를 사용하므로
           'batch_size'를 전체 데이터 개수보다 
           작은 수로 설정합니다. 
           
           여기선 500으로 설정하겠습니다.
   
   Step05. 학습된 두 모델을 테스트하고 
           binary crossentropy 값을 출력합니다. 
           둘 중 어느 모델의 성능이 더 좋은지 확인해보세요.
'''

def main():
    
    word_num = 100
    data_num = 25000
    
    # Keras에 내장되어 있는 imdb 데이터 세트를 불러오고 전처리합니다.
    
    (train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words = word_num)
    
    train_data = sequences_shaping(train_data, dimension = word_num)
    test_data = sequences_shaping(test_data, dimension = word_num)
    
    gd_model = GD_model(word_num)   # GD를 사용할 모델입니다.
    sgd_model = SGD_model(word_num)  # SGD를 사용할 모델입니다.
    
    gd_model.compile(loss='binary_crossentropy',optimizer='sgd',metrics=['accuracy', 'binary_crossentropy'])
    sgd_model.compile(loss='binary_crossentropy',optimizer='sgd',metrics=['accuracy', 'binary_crossentropy'])
    
    gd_model.summary()
    sgd_model.summary()
    
    gd_history = gd_model.fit(train_data,train_labels,epochs=20,batch_size=data_num,validation_data = (test_data, test_labels), verbose = 0)
    print('\n')
    sgd_history = sgd_model.fit(train_data,train_labels,epochs=20,batch_size=500,validation_data = (test_data, test_labels), verbose = 0)
    
    scores_gd = gd_history.history['val_binary_crossentropy'][-1]
    scores_sgd = sgd_history.history['val_binary_crossentropy'][-1]
    
    print('\nscores_gd: ', scores_gd)
    print('scores_sgd: ', scores_sgd)
    
    Visulaize([('GD', gd_history),('SGD', sgd_history)])
    
    return gd_history, sgd_history

if __name__ == "__main__":
    main()

모멘텀(Momentum)

출처:엘리스

SGD는 손실 함수(loss function)의 최솟값에 도달하는 동안 Gradient가 진동하여 최적값에 도달하기까지의 시간이 오래 걸리는 단점을 가지고 있습니다. 이를 보완하기 위해 사용되는 모멘텀(momentum) 기법은 관성의 개념을 이용해 최적값에 좀 더 빠르게 도달할 수 있도록 도와줍니다.

SGD에서 momentum을 사용하기 위한 함수/라이브러리

tf.keras.optimizers.SGD(lr, momentum):
lr : 학습률 (learning rate) (lr >= 0), 기본값 0.1
momentum : 진동을 막아주고 SGD를 가속하는 파라미터 (momentum >= 0), 기본값 0.9
모델을 테스트하기 위한 함수/라이브러리

score = model.evaluate(test_data, test_labels)
: 테스트 데이터와 테스트 label에 대한 모델의 성능을 나타냅니다. compile 과정에서 어떤 평가 방법(metrics)을 쓰느냐에 따라 다양한 성능 지표가 나옵니다.

test_data: 테스트 데이터

test_labels: 테스트 데이터의 label

실습을 통해서 모멘텀 기법을 활용해 봅시다. 이번 실습에서는 이전 실습과 동일한 IMDB 영화 리뷰 데이터셋을 활용합니다.

실습
모멘텀(momentum)을 적용/비적용 할 하나의 모델을 자유롭게 생성합니다.
입력층, 출력층과 관련된 모델 설계 방법은 [실습1]과 동일합니다.

모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.
두 모델에 대해 손실 함수(loss function), 평가 방법(metrics)은 다음과 같이 동일하게 설정합니다.

손실 함수(loss) : ‘binary_crossentropy’
평가 방법(metrics): [‘accuracy’, ‘binary_crossentropy’]
단, 최적화(optimize) 알고리즘은 각각의 모델에 대해 sgd_opt, msgd_opt를 위 설명과 같이 정의하고, 변수는 아래와 똑같이 설정하여 사용합니다.

모멘텀을 적용하지 않은 모델: lr = 0.01, momentum = 0
모멘텀을 적용한 모델: lr = 0.01, momentum = 0.9
또한, 이번 실습부터는 evaluate 메소드를 이용해 두 모델을 테스트한 후, 손실 함수 값과 그래프를 출력합니다. 코드는 위 설명을 참고하세요.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

# 데이터를 전처리하는 함수

def sequences_shaping(sequences, dimension):
    
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0 
    
    return results
    
'''
1. 모멘텀(momentum)을 적용/비적용 할 하나의 모델을 자유롭게 생성합니다.
'''
    
def Momentum_model(word_num):
    
    model = tf.keras.Sequential([tf.keras.layers.Dense(32,input_shape=(word_num,),activation='relu'),tf.keras.layers.Dense(32,activation='relu'),tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return model

'''
2. 두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. Momentum_model 함수를 이용해 
           두 모델을 불러옵니다. 모두 동일한 모델입니다.
   
   Step02. 두 모델의 손실 함수, 최적화 알고리즘, 
           평가 방법을 설정합니다.
   
   Step03. 두 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 두 모델을 각각 학습시킵니다. 
           검증용 데이터도 설정해주세요.
           두 모델 모두 'epochs'는 20, 'batch_size'는
           500으로 설정합니다.
   
   Step05. 학습된 두 모델을 테스트하고 
           binary crossentropy 값을 출력합니다. 
           둘 중 어느 모델의 성능이 더 좋은지 확인해보세요.
'''
    
def main():
    
    word_num = 100
    data_num = 25000
    
    # Keras에 내장되어 있는 imdb 데이터 세트를 불러오고 전처리합니다.
    
    (train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words = word_num)
    
    train_data = sequences_shaping(train_data, dimension = word_num)
    test_data = sequences_shaping(test_data, dimension = word_num)
    
    sgd_model = Momentum_model(word_num)   # 모멘텀을 사용하지 않을 모델입니다.
    msgd_model =Momentum_model(word_num)  # 모멘텀을 사용할 모델입니다.
    
    sgd_opt = tf.keras.optimizers.SGD(lr=0.01, momentum=0)
    sgd_model.compile(loss='binary_crossentropy',optimizer=sgd_opt,metrics=['accuracy', 'binary_crossentropy'])
    
    msgd_opt =tf.keras.optimizers.SGD(lr=0.01, momentum=0.9)
    msgd_model.compile(loss='binary_crossentropy',optimizer=msgd_opt,metrics=['accuracy', 'binary_crossentropy'])
    
    sgd_model.summary()
    msgd_model.summary()
    
    sgd_history = sgd_model.fit(train_data,train_labels,epochs=20,batch_size=500,validation_data = (test_data, test_labels), verbose = 0)
    print('\n')
    msgd_history = msgd_model.fit(train_data,train_labels,epochs=20,batch_size=500,validation_data = (test_data, test_labels), verbose = 0)
    
    scores_sgd = sgd_model.evaluate(test_data, test_labels)
    scores_msgd = msgd_model.evaluate(test_data, test_labels)
    
    print('\nscores_sgd: ', scores_sgd[-1])
    print('scores_msgd: ', scores_msgd[-1])
    
    Visulaize([('SGD', sgd_history),('mSGD', msgd_history)])
    
    return sgd_history, msgd_history

if __name__ == "__main__":
    main()

Adagrad, RMSprop, Adam 최적화(optimize) 알고리즘

adam

출처:엘리스

Adagrad
Adagrad(Adaptive Gradient) 최적화 알고리즘은 손실 함수(loss function)의 값을 최소로 만드는 최적의 가중치를 찾아내기 위해 learning rate를 조절해 하강하는 방법 중 하나입니다.

기존 방식이 가중치들의 업데이트를 같은 속도로 한꺼번에 하는 방법이었다면, Adagrad는 가중치 각각의 업데이트 속도를 데이터에 맞추어(adaptively) 계산해 적절한 learning rate로 하강하도록 합니다.

Adagrad

tf.keras.optimizers.Adagrad(lr, epsilon, decay) :
lr : 학습률 (learning rate) (lr >= 0), 기본값 0.1
epsilon : 연산 시 분모가 0이 되는 것을 막는, 0에 가까운 상수 (epsilon >= 0), 기본값 0.000001
decay : 업데이트마다 학습률을 비율만큼 줄여주는 파라미터 (decay >= 0), 기본값 0.0
RMSprop
RMSprop 최적화 알고리즘은 학습이 진행될수록 가중치 업데이트 강도가 약해지는 Adagrad의 단점을 보완하고자 제안된 방법입니다.

RMSProp은 과거의 gradient 값은 잊고 새로운 gradient 값을 크게 반영해서 가중치를 업데이트합니다.

RMSprop

tf.keras.optimizers.RMSprop(lr) :
lr : 학습률, 기본값 0.1
Adam
Adam은 최적화 알고리즘 중 가장 발전된 기법입니다. RMSProp과 모멘텀(momentum)을 함께 사용함으로써, 진행 방향과 learning rate 모두를 적절하게 유지하면서 학습할 수 있도록 고안되었습니다.

Adam

tf.keras.optimizers.Adam(lr, beta_1, beta_2) :
lr : 학습률, 기본값 0.01
beta_1 : 모멘텀을 결정하기 위해 사용하는 파라미터 (beta_1 >= 0 ), 기본값 0.9
beta_2 : step size를 결정하기 위해 사용하는 파라미터 (beta_2 >= 0), 기본값 0.999
실습을 통해서 각각의 최적화 알고리즘을 활용해 봅시다. 이번 실습에서는 이전 실습과 동일한 IMDB 영화 리뷰 데이터셋을 활용합니다.

실습
Adagrad, RMSprop, Adam 최적화 알고리즘을 적용할 하나의 모델을 자유롭게 생성합니다.
입력층, 출력층과 관련된 모델 설계 방법은 [실습1]과 동일합니다.

세 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.
세 모델에 대해 손실 함수(loss function), 평가 방법(metrics)은 다음과 같이 설정합니다.

손실 함수(loss) : ‘binary_crossentropy’
평가 방법(metrics): [‘accuracy’, ‘binary_crossentropy’]
단, 최적화 알고리즘은 각각의 모델에 대해 adagrad_opt, rmsprop_opt, adam_opt를 위 설명과 같이 정의하고, 변수는 아래와 똑같이 설정하여 사용합니다.

Adagrad: lr = 0.01, epsilon = 0.00001, decay = 0.4
RMSProp: lr = 0.001
Adam: lr = 0.01, beta_1 = 0.9, beta_2 = 0.999
예시 코드는 [실습2]를 참고해주세요.

Tips!
일반적으로 Adam의 성능이 제일 좋고, 그 다음 RMSProp, Adagrad 순으로 성능이 좋다고 알려져 있습니다.

그러나 데이터셋의 종류, epochs나 batch_size 같은 하이퍼 파라미터의 값 등등에 따라 최적화 알고리즘의 성능은 이론과 완전히 같지 않을 수 있습니다.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

# 데이터를 전처리하는 함수

def sequences_shaping(sequences, dimension):
    
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0 
    
    return results

'''
1. Adagrad, RMSprop, Adam 최적화 알고리즘을 적용할 하나의 모델을 자유롭게 생성합니다.
'''

def OPT_model(word_num):
    
    model = tf.keras.Sequential([tf.keras.layers.Dense(32,input_shape=(word_num,),activation='relu'),tf.keras.layers.Dense(32,activation='relu'),tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return model

'''
2. 세 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. OPT_model 함수를 이용해 세 모델을 불러옵니다. 
           모두 동일한 모델입니다.
   
   Step02. 세 모델의 손실 함수, 최적화 방법, 
           평가 방법을 설정합니다.
   
   Step03. 세 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 세 모델을 각각 학습시킵니다. 
           세 모델 모두 'epochs'는 20, 'batch_size'는
           500으로 설정합니다.
   
   Step05. 세 모델을 테스트하고 
           binary crossentropy 점수를 출력합니다. 
           셋 중 어느 모델의 성능이 가장 좋은지 확인해보세요.
'''

def main():
    
    word_num = 100
    data_num = 25000
    
    # Keras에 내장되어 있는 imdb 데이터 세트를 불러오고 전처리합니다.
    
    (train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words = word_num)
    
    train_data = sequences_shaping(train_data, dimension = word_num)
    test_data = sequences_shaping(test_data, dimension = word_num)
    
    adagrad_model = OPT_model(word_num)  # Adagrad를 사용할 모델입니다.
    rmsprop_model = OPT_model(word_num)  # RMSProp을 사용할 모델입니다.
    adam_model = OPT_model(word_num)    # Adam을 사용할 모델입니다.
    
    adagrad_opt = tf.keras.optimizers.Adagrad(lr=0.01, epsilon=0.00001, decay=0.4)
    adagrad_model.compile(loss='binary_crossentropy',optimizer=adagrad_opt,metrics=['accuracy', 'binary_crossentropy'])
    
    rmsprop_opt = tf.keras.optimizers.RMSprop(lr=0.001)
    rmsprop_model.compile(loss='binary_crossentropy',optimizer=rmsprop_opt,metrics=['accuracy', 'binary_crossentropy'])
    
    adam_opt =tf.keras.optimizers.Adam(lr=0.01,beta_1=0.9,beta_2=0.999)
    adam_model.compile(loss='binary_crossentropy',optimizer= adam_opt ,metrics=['accuracy', 'binary_crossentropy'])
    
    adagrad_model.summary()
    rmsprop_model.summary()
    adam_model.summary()
   
    
    adagrad_history =adagrad_model.fit(train_data,train_labels,epochs=20,batch_size=500,validation_data = (test_data, test_labels), verbose = 0)
    print('\n')
    rmsprop_history =rmsprop_model.fit(train_data,train_labels,epochs=20,batch_size=500,validation_data = (test_data, test_labels), verbose = 0)
    print('\n')
    adam_history = adam_model.fit(train_data,train_labels,epochs=20,batch_size=500,validation_data = (test_data, test_labels), verbose = 0)
    
    scores_adagrad = adagrad_model.evaluate(test_data, test_labels)
    scores_rmsprop = rmsprop_model.evaluate(test_data, test_labels)
    scores_adam = adam_model.evaluate(test_data, test_labels)
    
    print('\nscores_adagrad: ', scores_adagrad[-1])
    print('scores_rmsprop: ', scores_rmsprop[-1])
    print('scores_adam: ', scores_adam[-1])
    
    Visulaize([('Adagrad', adagrad_history),('RMSprop', rmsprop_history),('Adam', adam_history)])
    
    return adagrad_history, rmsprop_history, adam_history
    
if __name__ == "__main__":
    main()

기울기 소실 문제(Vanishing Gradient) 확인하기

역전파(back propagation) 알고리즘이란 우리의 목푯값과 실제 모델이 예측한 예측값이 얼마나 차이 나는지 구한 후, 오차값을 다시 뒤로 전파해가며 가중치(weight)들을 업데이트하는 과정이라고 배웠습니다.

그러나 깊은 층의 모델에선 역전파시에 전달되는 손실 함수(loss function)의 gradient 값에 활성화 함수인 sigmoid 함수의 0에 가까운 기울기 값이 계속해서 곱해지면서 결국 가중치 업데이트가 잘 안되는 문제가 생기는데, 이것이 바로 기울기 소실 문제(Vanishing Gradient)입니다.

이번 실습에서는 모델의 층이 깊은 경우 히든층의 활성화 함수가 ‘relu’일 때와 ‘sigmoid’일 때의 모델 정확도를 확인해보고, 왜 최근에는 활성화 함수로 sigmoid를 잘 쓰지 않는지 직접 확인해봅시다.

MNIST Dataset
실습에서 활용할 데이터셋은 28x28의 크기를 가진 MNIST 손글씨 데이터셋입니다. MNIST 데이터는 0 ~ 9까지의 손글씨 이미지로 구성되어 있으며 데이터의 label은 0 ~ 9의 정수로 이루어져 있습니다.

이번 실습에선 MNIST 데이터를 모델의 입력층에서 1차원 데이터로 변환해 사용합니다.

실습
make_model_relu 함수에 레이어를 10층 이상으로 깊게 쌓아 모델을 만들어 보세요.

make_model_sigmoid 함수에 1번과 똑같은 모델을 만드세요.

두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

두 모델에 대해 손실 함수(loss function), 최적화(optimize) 알고리즘, 평가 방법(metrics)을 다음과 같이 설정합니다.

손실 함수(loss) : ‘sparse_categorical_crossentropy’
최적화 알고리즘(optimizer) : ‘adam’
평가 방법(metrics): [‘accuracy’]

import tensorflow as tf

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

'''
1. 활성화 함수는 출력층만 그대로 두고 
   나머지 히든층들은 `relu`로 설정하세요.
'''

def make_model_relu():
    
    model_relu = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return model_relu
    
'''
2. 활성화 함수는 출력층만 그대로 두고
   나머지 히든층들은 `sigmoid`로 설정하세요.
'''
    
def make_model_sig():
    
    model_sig = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),

        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return model_sig

'''
3. 두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. model_relu와 model_sig 불러옵니다.
   
   Step02. 두 모델의 최적화 방법과 손실 함수를 
           똑같이 설정합니다.
   
   Step03. 두 모델의 구조를 확인하는 코드를 작성합니다.
           우리가 만든 모델이 얼마나 깊은지 확인해보세요.
   
   Step04. 두 모델을 학습시킵니다. 
           'epochs'는 5로 설정합니다.
           검증용 데이터는 설정하지 않습니다. 
           'verbose'는 0으로 설정합니다.
   
   Step05. 두 모델을 테스트하고 점수를 출력합니다. 
           둘 중 어느 모델의 성능이 더 좋은지 확인해보세요.
'''

def main():
    
    # MNIST 데이터를 불러오고 전처리합니다.
    mnist = tf.keras.datasets.mnist
    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0
    
    model_relu = make_model_relu() # 히든층들의 활성화 함수로 relu를 쓰는 모델입니다.
    model_sig = make_model_sig()  # 히든층들의 활성화 함수로 sigmoid를 쓰는 모델입니다.
    
    model_relu.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    model_sig.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    
    model_relu.summary()
    model_sig.summary()
    
    model_relu_history = model_relu.fit(x_train,y_train,epochs=5,batch_size=500,verbose=0)
    print('\n')
    model_sig_history = model_sig.fit(x_train,y_train,epochs=5,batch_size=500,verbose=0)
    
    scores_relu = model_relu.evaluate(x_test,y_test,verbose=0)
    scores_sig = model_sig.evaluate(x_test,y_test,verbose=0)
    
    print('\naccuracy_relu: ', scores_relu[-1])
    print('accuracy_sig: ', scores_sig[-1])
    
    return model_relu_history, model_sig_history

if __name__ == "__main__":
    main()
    

활성화 함수 다르게 적용하기

이번 실습에서는 sigmoid의 기울기 소실 문제를 해결하기 위해 등장한 활성화 함수인 relu와 tanh를 활용해봅시다. 동일한 모델에서 히든층의 활성화 함수가 각각 ‘sigmoid’, ‘relu’, ‘tanh’일 때의 모델 정확도를 확인해봅시다.

이전 실습과 동일하게, 활용할 데이터셋은 28x28의 크기를 가진 MNIST 손글씨 데이터셋으로, 모델의 입력층에서 1차원 데이터로 변환해 사용합니다.

실습
make_model_sig 함수에 레이어를 10층 이상으로 깊게 쌓아 모델을 만들어 보세요.

make_model_relu 함수에 1번과 똑같은 모델을 만드세요.

make_model_tanh 함수에 1번과 똑같은 모델을 만드세요.

세 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

세 모델에 대해 손실 함수(loss function), 최적화(optimize) 알고리즘, 평가 방법(metrics)을 다음과 같이 설정합니다.

손실 함수(loss) : ‘sparse_categorical_crossentropy’
최적화 알고리즘(optimizer) : ‘adam’
평가 방법(metrics): [‘accuracy’]
Tips!
1, 2, 3번 모델은 서로 히든층들의 활성화 함수를 제외하곤 완전히 똑같은 모델이어야 합니다.

import tensorflow as tf

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 
    
'''
1. 활성화 함수는 출력층만 그대로 두고 
   나머지 히든층들은 'sigmoid'로 설정하세요.
'''
    
def make_model_sig():
    
    model_sig = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(64, activation='sigmoid'),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return model_sig
    
'''
2. 활성화 함수는 출력층만 그대로 두고 
   나머지 히든층들은 'relu'로 설정하세요.
'''

def make_model_relu():
    
    model_relu = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
     
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return model_relu

    
'''
3. 활성화 함수는 출력층만 그대로 두고 
   나머지 히든층들은 'tanh'로 설정하세요.
'''
    
def make_model_tanh():
    
    model_tanh = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(64, activation='tanh'),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return model_tanh

'''
4. 세 개의 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. make_model_sig, make_model_relu, make_model_tanh 함수를 이용해 세 모델을 불러옵니다.
   
   Step02. 세 모델의 손실 함수, 최적화 알고리즘, 
          평가 방법을 설정합니다.
   
   Step03. 세 모델의 구조를 확인하는 코드를 작성합니다.
           우리가 만든 모델이 얼마나 깊은지 확인해보세요.
   
   Step04. 세 모델을 학습시킵니다. 
           'epochs'는 5로 설정합니다.
           검증용 데이터는 설정하지 않습니다.
   
   Step05. 세 모델을 테스트하고 accuracy 값을 출력합니다. 
           셋 중 어느 모델의 성능이 가장 좋은지 확인해보세요.
'''

def main():
    
    # MNIST 데이터를 불러오고 전처리합니다.
    mnist = tf.keras.datasets.mnist
    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0
    
    model_sig = make_model_sig()   # 히든층들의 활성화 함수로 sigmoid를 쓰는 모델입니다.
    model_relu = make_model_relu() # 히든층들의 활성화 함수로 relu를 쓰는 모델입니다.
    model_tanh = make_model_tanh()  # 히든층들의 활성화 함수로 tanh를 쓰는 모델입니다.
    
    model_sig.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    model_relu.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    model_tanh.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    
    model_sig.summary()
    model_relu.summary()
    model_tanh.summary()
    
    model_sig_history =model_sig.fit(x_train,y_train,epochs=5,batch_size=500,verbose=0)
    print('\n')
    model_relu_history = model_relu.fit(x_train,y_train,epochs=5,batch_size=500,verbose=0)
    print('\n')
    model_tanh_history = model_tanh.fit(x_train,y_train,epochs=5,batch_size=500,verbose=0)
    
    scores_sig = model_sig.evaluate(x_test,y_test,verbose=0)
    scores_relu = model_relu.evaluate(x_test,y_test,verbose=0)
    scores_tanh = model_tanh.evaluate(x_test,y_test,verbose=0)
    
    print('\naccuracy_sig: ', scores_sig[-1])
    print('accuracy_relu: ', scores_relu[-1])
    print('accuracy_tanh: ', scores_tanh[-1])
    
    return model_sig_history, model_relu_history, model_tanh_history

if __name__ == "__main__":
    main()
    

Naive한 가중치 초기화 방법

가중치 초기화 문제는 활성화 함수의 입력값이 너무 커지거나 작아지지 않게 만들어주려는 것이 핵심입니다.

초기화 설정 문제 해결을 위한 Naive한 방법으론 평균이 0, 표준 편차가 1인 표준 정규 분포를 이용해 초기화하는 방법과 평균이 0, 표준 편차가 0.01인 정규분포로 초기화하는 방법이 있습니다.

이번 실습에서는 각 방법으로 가중치를 초기화 해보고, 각 경우에 활성화 결괏값들의 분포가 어떻게 변화하는지 확인해봅시다. 실습을 마치고 나면 왜 Naive한 방식으로 가중치를 초기화 해서는 안되는지 알 수 있습니다.

표준 정규 분포(평균이 0, 표준 편차가 1인 정규 분포)를 따르는 무작위 데이터를 만들기 위한 함수/라이브러리

x = np.random.randn(50,20)
: 표준 정규 분포를 따르는 무작위 데이터는 위와 같이 만들 수 있습니다. 예시는 20개의 노드를 가진 모델에 들어갈 50개의 데이터입니다. 데이터의 shape은 (50,20)으로, 배열(array) 형태입니다.

실습
모델의 층은 5개가 있으며, 각 층의 노드는 100개씩입니다.

100개의 노드를 가진 모델에 들어갈 1,000개의 입력 데이터 x_1, x_2를 표준 정규 분포를 따르도록 무작위로 생성하세요. 둘은 같은 값입니다.
활성화 함수는 sigmoid 함수이고, 각 층의 활성화 결괏값을 activations_1과 activations_2에 저장합니다.

가중치 w_1은 표준 정규 분포를 따르도록, 가중치 w_2는 평균이 0, 표준 편차가 0.01인 정규 분포를 따르도록 무작위로 생성하세요. 각각은 100개의 노드를 가진 모델에 들어갈 100개의 가중치입니다.
만약 20개의 노드를 가진 모델에 들어갈 50개의 ‘표준편차가 0.3, 평균이 5인 표준 정규 분포를 따르는 무작위 데이터’를 생성하고 싶다면 다음과 같은 코드를 사용할 수 있습니다.

x = np.random.randn(50, 20) * 0.3 + 5
Copy
sigmoid를 통과할 값인 x_1과 w_1의 곱, x_2와 w_2의 곱을 Numpy 메소드를 이용해 각각 a_1, a_2에 정의하세요. x_1과 w_1, x_2와 w_2은 각각 배열이라는 것을 기억하세요.
Tips!
시각화된 활성화 결괏값들의 분포를 확인해보세요. 값들이 골고루 퍼져있나요? 아니면 어느 한 쪽으로 몰려있나요? 한 쪽으로 몰려있다면 학습 시 어떤 문제가 발생할까요?

import numpy as np
from visual import *

np.random.seed(100)

def sigmoid(x):
    result = 1 / (1 + np.exp(-x))
    return result

'''
1. 입력 데이터를 정의하세요.

2. 가중치를 정의하세요.

3. sigmoid를 통과할 값인 'a_1', 'a_2'를 정의하세요.
'''

def main():
    
    x_1 = np.random.randn(1000,100)
    x_2 = np.random.randn(1000,100)
    
    node_num = 100
    hidden_layer_size = 5
    
    activations_1 = {}
    activations_2 = {}
    
    for i in range(hidden_layer_size):
        if i != 0:
            x_1 = activations_1[i-1]
            x_2 = activations_2[i-1]
        
        w_1 = np.random.randn(100,100)
        w_2 = np.random.randn(100, 100) * 0.01 + 0
        
        a_1 = np.dot(x_1,w_1)
        a_2 = np.dot(x_2,w_2)
        
        z_1 = sigmoid(a_1)
        z_2 = sigmoid(a_2)
        
        activations_1[i] = z_1
        activations_2[i] = z_2
        
    Visual(activations_1,activations_2)
    
    return activations_1, activations_2

if __name__ == "__main__":
    main()

Xavier 초기화 방법

가중치 초기화의 문제를 해결하기 위해 나온 방법 중 하나인 Xavier 초기화 방법은 현재 일반적인 딥러닝 프레임워크들이 표준적으로 이용하고 있습니다.

Xavier 초기화 방법은 앞 레이어의 노드가 n개일 때 표준 편차가 1/n1 / \sqrt{n}1/
n

인 분포를 사용하는 것입니다. 즉 표준 정규 분포를 입력 개수의 제곱근으로 나누어주면 됩니다.

따라서 Xavier 초기화 방법을 사용하면 앞 레이어의 노드가 많을수록 다음 레이어의 노드의 초깃값으로 사용하는 가중치가 좁게 퍼집니다.

실습
모델의 층은 5개가 있으며, 각 층의 노드는 100개씩입니다.

이전 실습과 마찬가지로 100개의 노드를 가진 모델에 들어갈 1,000개의 입력 데이터 x_sig, x_relu를 표준 정규 분포를 따르도록 무작위로 생성하세요. 둘은 같은 값입니다.

이전 실습에서 정의한 가중치 초깃값 설정 부분을 참고하되, 이번엔 표준 편차가 1/n1 / \sqrt{n}1/
n

이 되도록 만드세요. w_sig와 w_relu는 같은 값입니다.

sigmoid를 통과할 값인 x_sig와 w_sig의 곱, x_relu와 w_relu의 곱을 Numpy 메소드를 이용해 각각 a_sig, a_relu에 정의하세요.

Tips!
활성화 함수가 ‘sigmoid’일 때와 ‘relu’일 때의 활성화 결괏값 분포 그림을 확인해보세요. ‘relu’일 때는 어떤 문제가 발생하나요?

import numpy as np
from visual import *

np.random.seed(100)

def sigmoid(x):
    result = 1 / (1 + np.exp(-x))
    return result

def relu(x):
    result = np.maximum(0,x)
    return result

'''
1. 입력 데이터를 정의하세요.

2. 가중치 초깃값 설정 부분을 왼쪽 설명에 맞게 바꿔보세요.
   Numpy의 연산 메서드를 사용할 수 있습니다.
   
3. sigmoid와 relu를 통과할 값인 'a_sig', 'a_relu'를 정의하세요.
'''

def main():
    # np.random.randn(50, 20) * 0.3 + 5
    x_sig = np.random.randn(1000,100)
    x_relu = np.random.randn(1000,100)
    
    node_num = 100
    hidden_layer_size = 5
    
    activations_sig = {}
    activations_relu = {}
    
    for i in range(hidden_layer_size):
        if i != 0:
            x_sig = activations_sig[i-1]
            x_relu = activations_relu[i-1]
        
        w_sig = np.random.randn(100,100)*(1/np.sqrt(node_num))
        w_relu = np.random.randn(100,100)*(1/np.sqrt(node_num))
        
        a_sig = np.dot(x_sig,w_sig)
        a_relu =  np.dot(x_relu,w_relu)
        
        z_sig = sigmoid(a_sig)
        z_relu = relu(a_relu)
        
        activations_sig[i] = z_sig
        activations_relu[i] = z_relu
        
    Visual(activations_sig, activations_relu)
    
    return activations_sig, activations_relu

if __name__ == "__main__":
    main()
    
    

He 초기화 방법
He 초기화 방법은 활성화 함수로 ReLU를 쓸 때 활성화 결괏값들이 한쪽으로 치우치는 문제를 해결하기 위해 나온 방법입니다.

He 초기화 방법은 앞 레이어의 노드가 n개일 때 표준 편차가 2/n\sqrt{2} / \sqrt{n}
2

/
n

인 분포를 사용하는 것입니다. 즉 표준 정규 분포를 입력 개수 절반의 제곱근으로 나누어주면 됩니다.

Xavier 초기화 방법은 표준 편차가 1/n1 / \sqrt{n}1/
n

이라고 하였습니다. ReLU는 음의 영역에 대한 함숫값이 0이라서 더 넓게 분포시키기 위해 2\sqrt{2}
2

배의 계수가 필요하다고 이해할 수 있습니다.

실습
모델의 층은 5개가 있으며, 각 층의 노드는 100개씩입니다.

이전 실습과 마찬가지로 100개의 노드를 가진 모델에 들어갈 1,000개의 입력 데이터 x_relu를 표준 정규 분포를 따르도록 무작위로 생성하세요.

이전 실습에서 정의한 가중치 초깃값 설정 부분을 참고하되, 이번엔 표준 편차가 2/n\sqrt{2} / \sqrt{n}
2

/
n

이 되도록 만드세요.

relu를 통과할 값인 x_relu와 w_relu의 곱을 Numpy 메소드를 이용해 a_relu에 정의하세요.

Tips!
활성화 함수가 ‘relu’인 상황에서, Xavier 초기화 방법과 He 초기화 방법을 적용했을 때 어떤 차이가 발생하나요? 이전 실습과 비교해서 활성화 결괏값 분포 그림을 확인해보세요.

import numpy as np
from visual import *

np.random.seed(100)
    
def relu(x):
    result = np.maximum(0,x)
    return result

'''
1. 입력 데이터를 정의하세요.

2. 가중치 초깃값 설정 부분을 왼쪽 설명에 맞게 바꿔보세요.
   Numpy의 연산 메서드를 사용할 수 있습니다.
   
3. relu를 통과할 값인 'a_relu'를 정의하세요.
'''

def main():
    
    x_relu = np.random.randn(1000,100)
    
    node_num = 100
    hidden_layer_size = 5
    
    activations_relu = {}
    
    for i in range(hidden_layer_size):
        if i != 0:
            x_relu = activations_relu[i-1]
            
        w_relu = np.random.randn(100,100)*(np.sqrt(2/node_num))
        
        a_relu = np.dot(x_relu,w_relu)
        
        z_relu = relu(a_relu)
        
        activations_relu[i] = z_relu
        
    Visual(activations_relu)
    
    return activations_relu    

if __name__ == "__main__":
    main()

과적합(Overfitting)

과적합(Overfitting)은 모델이 학습 데이터에만 너무 치중되어 학습 데이터에 대한 예측 성능은 좋으나 테스트 데이터에 대한 예측 성능이 떨어지는 경우를 말합니다.

모델이 과적합되면 일반화되지 않은 모델이라고도 합니다. 과적합이 발생하는 원인은 아래와 같습니다.

데이터의 퍼진 정도, 즉 분산(variance)이 높은 경우

너무 많이 학습 데이터를 학습시킨 경우 (epochs가 매우 큰 경우)

학습에 사용된 파라미터가 너무 많은 경우

데이터에 비해 모델이 너무 복잡한 경우

데이터에 노이즈 & 이상치(outlier)가 너무 많은 경우

이번 실습에서는 일부러 과적합된 모델을 만들어 보고 손실값(loss) 그래프를 통해 기존 모델과 어떻게 성능 차이가 나는지 확인해보겠습니다.

[실습1]과 마찬가지로 활용할 데이터셋은 IMDB 영화 리뷰 데이터셋입니다.

실습
과적합 될 모델과 비교하기 위해 아래와 같이 기본 모델을 생성합니다.
basic_model = tf.keras.Sequential([ tf.keras.layers.Dense(256, activation = 'relu', input_shape=(word_num,)), tf.keras.layers.Dense(128, activation = 'relu'),
tf.keras.layers.Dense(1, activation= 'sigmoid')])
Copy
기본 모델을 응용해 과적합 될 모델을 생성합니다.

모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

두 모델에 대해 손실 함수(loss function), 최적화(optimize) 알고리즘, 평가 방법(metrics)은 다음과 같이 설정합니다.

손실 함수(loss) : ‘binary_crossentropy’
최적화 알고리즘(optimizer) : ‘adam’
평가 방법(metrics): [‘accuracy’, ‘binary_crossentropy’]
Tips!
과적합 된 모델 특성상 학습 시간이 매우 오래 걸릴 수 있습니다.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

# 데이터를 전처리하는 함수

def sequences_shaping(sequences, dimension):
    
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0 
        
    return results
    
'''
1. 과적합 될 모델과 비교하기 위해 기본 모델을 
   마크다운 설명과 동일하게 생성합니다.
'''

def Basic(word_num):
    
    basic_model = tf.keras.Sequential([ tf.keras.layers.Dense(256, activation = 'relu', input_shape=(word_num,)), 
    tf.keras.layers.Dense(128, activation = 'relu'),
    tf.keras.layers.Dense(1, activation= 'sigmoid')])
    
    return basic_model

'''
2. 기본 모델의 레이어 수와 노드 수를 자유롭게 늘려서
   과적합 될 모델을 생성합니다.
'''

def Overfitting(word_num):
    
    overfit_model = tf.keras.Sequential([ tf.keras.layers.Dense(1024, activation = 'relu', input_shape=(word_num,)), 
    tf.keras.layers.Dense(512, activation = 'relu'),
    tf.keras.layers.Dense(512, activation = 'relu'),
    tf.keras.layers.Dense(512, activation = 'relu'),
    tf.keras.layers.Dense(1, activation= 'sigmoid')])
    
    return overfit_model

'''
3. 두 개의 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. basic_model와 overfit_model 함수를 이용해 
           두 모델을 불러옵니다.
   
   Step02. 두 모델의 손실 함수, 최적화 알고리즘, 
           평가 방법을 설정합니다.
   
   Step03. 두 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 두 모델을 학습시킵니다. 
           검증용 데이터도 설정해주세요.
           
           기본 모델은 'epochs'를 20, 
           과적합 모델은 'epochs'를 300이상으로 설정합니다.
           'batch_size'는 두 모델 모두 500으로 설정합니다.
   
   Step05. 두 모델을 테스트하고 
           binary crossentropy 값을 출력합니다. 
'''

def main():
    
    word_num = 100
    data_num = 25000
    
    # Keras에 내장되어 있는 imdb 데이터 세트를 불러오고 전처리합니다.
    
    (train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words = word_num)
    
    train_data = sequences_shaping(train_data, dimension = word_num)
    test_data = sequences_shaping(test_data, dimension = word_num)
    
    basic_model = Basic(word_num)   # 기본 모델입니다.
    overfit_model = Overfitting(word_num)  # 과적합시킬 모델입니다.
    
    basic_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    overfit_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    
    basic_model.summary()
    overfit_model.summary()
    
    basic_history = basic_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),
    verbose=0)
    print('\n')
    overfit_history =overfit_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),
    verbose=0)
    
    scores_basic = basic_model.evaluate(test_data, test_labels,verbose=0)
    scores_overfit = overfit_model.evaluate(test_data, test_labels,verbose=0)
    
    print('\nscores_basic: ', scores_basic[-1])
    print('scores_overfit: ', scores_overfit[-1])
    
    Visualize([('Basic', basic_history),('Overfitting', overfit_history)])
    
    return basic_history, overfit_history

if __name__ == "__main__":
    main()
    

L1, L2 정규화(Regularization) 기법

L1 정규화
L1 정규화는 가중치(weight)의 절댓값에 비례하는 손실(loss)이 기존 손실 함수(loss function)에 추가되는 형태입니다.

TotalLoss=Loss+λ∑w∣W∣Total Loss = Loss + \lambda\sum_w|W|

L1 정규화는 모델 내의 일부 가중치를 0으로 만들어 의미있는 가중치만 남도록 만들어줍니다. 이를 통해 모델을 일반화시킬 수 있습니다. 다른 말로 Sparse Model을 만든다라고도 합니다.

L1 정규화를 적용하기 위해 사용하는 함수/라이브러리

tf.keras.layers.Dense(kernel_regularizer = tf.keras.regularizers.l1(ratio))
ratio : 가중치에 L1 정규화를 적용하는 비율 (0.001 ~0.005)
L2 정규화
L2 정규화는 가중치의 제곱에 비례하는 손실이 기존 손실 함수에 추가되는 형태입니다.

TotalLoss=Loss+λ∑wW2Total Loss = Loss + \lambda\sum_wW^2

L2 정규화는 학습이 진행될 때 가중치의 값이 0에 가까워지도록 만들어줍니다. 가중치를 0으로 만들어주는 L1 정규화와는 차이가 있습니다.

이를 통해 특정 가중치에 치중되지 않도록 가중치 값을 조율하게 되며 가중치 감쇠 (Weight Decay)라고도 부릅니다.

L2 정규화를 적용하기 위해 사용하는 함수/라이브러리

tf.keras.layers.Dense(kernel_regularizer = tf.keras.regularizers.l2(ratio))
ratio : 가중치에 L2 정규화를 적용하는 비율 (0.001 ~0.005)
[실습1]과 마찬가지로 활용할 데이터셋은 IMDB 영화 리뷰 데이터셋입니다.

실습
L1, L2 정규화를 적용한 모델과 비교하기 위한 하나의 기본 모델을 자유롭게 생성합니다.
입력층, 출력층과 관련된 모델 설계 방법은 [실습1]과 동일합니다.

위 설명을 참고해서 기본 모델에 L1 정규화를 적용합니다.

위 설명을 참고해서 기본 모델에 L2 정규화를 적용합니다.

세 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

세 모델에 대해 손실 함수, 최적화(optimize) 알고리즘, 평가 방법(metrics)은 다음과 같이 설정합니다.

  • 손실 함수(loss) : ‘binary_crossentropy’
  • 최적화 알고리즘(optimizer) : ‘adam’
  • 평가 방법(metrics): [‘accuracy’, ‘binary_crossentropy’]

실제 모델링 시에는 validation 데이터로 test 데이터를 사용할 시 overfitting의 문제가 발생할 수 있습니다.
Tips!
L1 정규화 또는 L2 정규화를 적용한 모델의 손실 함수인 binary crossentropy 값, 즉 scores_l1과 scores_l2가 기본 모델의 성능(scores_basic)보다 작은 걸 확인하셨나요? 이로써 L1 정규화 또는 L2 정규화를 적용하면 모델의 성능이 훨씬 좋아진다는 것을 확인할 수 있습니다.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

# 데이터를 전처리하는 함수

def sequences_shaping(sequences, dimension):
    
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0 
    
    return results

'''
1. L1, L2 정규화를 적용한 모델과 비교하기 위한
   하나의 기본 모델을 자유롭게 생성합니다.
'''

def Basic(word_num):
    
    basic_model = tf.keras.Sequential([tf.keras.layers.Dense(128,input_shape=(word_num,),activation='relu'),tf.keras.layers.Dense(
    128,activation='relu'),tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return basic_model

'''
2. 기본 모델에 L1 정규화를 적용합니다.
   입력층과 히든층에만 적용하세요.
'''

def L1(word_num):
    
    l1_model = tf.keras.Sequential([tf.keras.layers.Dense(128,input_shape=(word_num,),activation='relu',kernel_regularizer = 
    tf.keras.regularizers.l1(0.001)),tf.keras.layers.Dense(128,activation='relu',kernel_regularizer = tf.keras.regularizers.l1(0.001))
    , tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return l1_model

'''
3. 기본 모델에 L2 정규화를 적용합니다.
   입력층과 히든층에만 적용하세요.
'''

def L2(word_num):
    
    l2_model = tf.keras.Sequential([tf.keras.layers.Dense(128,input_shape=(word_num,),activation='relu',kernel_regularizer = 
    tf.keras.regularizers.l2(0.001)),tf.keras.layers.Dense(128,activation='relu',kernel_regularizer = tf.keras.regularizers.l2(0.001))
    , tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return l2_model


'''
4. 세 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. Basic, L1, L2 함수를 이용해 세 모델을 불러옵니다.
   
   Step02. 세 모델의 손실 함수, 최적화 알고리즘, 
           평가 방법을 설정합니다.
   
   Step03. 세 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 세 모델을 학습시킵니다. 
           세 모델 모두 'epochs'는 20,
           'batch_size'는 500으로 설정합니다. 
           검증용 데이터도 설정해주세요.
   
   Step05. 세 모델을 테스트하고 
           binary crossentropy 값을 출력합니다. 
           셋 중 어느 모델의 성능이 가장 좋은지 확인해보세요.
'''

def main():
    
    word_num = 100
    data_num = 25000
    
    # Keras에 내장되어 있는 imdb 데이터 세트를 불러오고 전처리합니다.
    
    (train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words = word_num)
    
    train_data = sequences_shaping(train_data, dimension = word_num)
    test_data = sequences_shaping(test_data, dimension = word_num)
    
    basic_model = Basic(word_num)  # 기본 모델입니다.
    l1_model = L1(word_num)    # L1 정규화를 적용할 모델입니다.
    l2_model = L2(word_num)    # L2 정규화를 적용할 모델입니다.
    
    basic_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    l1_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    l2_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    
    basic_model.summary()
    l1_model.summary()
    l2_model.summary()
    
    basic_history = basic_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),     
    verbose=0)
    print('\n')
    l1_history = l1_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),     
    verbose=0)
    print('\n')
    l2_history = l2_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),     
    verbose=0)
    
    scores_basic = basic_model.evaluate(test_data, test_labels,verbose=0)
    scores_l1 = l1_model.evaluate(test_data, test_labels,verbose=0)
    scores_l2 = l2_model.evaluate(test_data, test_labels,verbose=0)
    
    print('\nscores_basic: ', scores_basic[-1])
    print('scores_l1: ', scores_l1[-1])
    print('scores_l2: ', scores_l2[-1])
    
    Visulaize([('Basic', basic_history),('L1 Regularization', l1_history), ('L2 Regularization', l2_history)])
    
    return basic_history, l1_history, l2_history

if __name__ == "__main__":
    main()                       

드롭 아웃(Drop out) 기법

드롭 아웃(Drop Out)은 모델이 과적합되는 것을 막기 위한 가장 보편적인 정규화(Regularization) 기법 중 하나입니다.

출처:엘리스

드롭 아웃은 데이터를 학습할 때, 일부 퍼셉트론(뉴런)을 랜덤하게 0으로 만들어 모델 내부의 특정 가중치(Weight)에 치중되는 것을 막습니다.

이를 통해 모델이 일부 데이터에 가중되는 것을 막고 일반화된 모델을 만들 수 있습니다.

드롭 아웃을 사용하는데 있어 주의할 점은 학습이 끝난 후 테스트 과정에서는 드롭 아웃을 사용하면 안된다는 점입니다.

이번 실습에선 드롭 아웃을 적용한 모델과 적용하지 않은 모델의 차이를 보겠습니다.

드롭 아웃을 사용하기 위한 함수/라이브러리

tf.keras.layers.Dropout(prob)
prob : 드롭 아웃을 적용할 확률 (0.1 ~ 0.5)
[실습1]과 마찬가지로 활용할 데이터셋은 IMDB 영화 리뷰 데이터셋입니다.

실습
드롭 아웃을 적용할 모델과 비교하기 위한 하나의 기본 모델을 자유롭게 생성합니다.
입력층, 출력층과 관련된 모델 설계 방법은 [실습1]과 동일합니다.

기본 모델에 드롭 아웃 레이어를 추가합니다.

두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

두 모델에 대해 손실 함수(loss function), 최적화(optimize) 알고리즘, 평가 방법(metrics)은 다음과 같이 설정합니다.

손실 함수(loss) : ‘binary_crossentropy’
최적화 알고리즘(optimizer) : ‘adam’
평가 방법(metrics): [‘accuracy’, ‘binary_crossentropy’]
Tips!
드롭 아웃을 적용한 모델의 손실 함수인 binary crossentropy 값, 즉 scores_dropout이 기본 모델의 성능(scores_basic)보다 작은 걸 확인하셨나요? 이로써 드롭 아웃을 적용하면 모델의 성능이 훨씬 좋아진다는 것을 확인할 수 있습니다.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

# 데이터를 전처리하는 함수

def sequences_shaping(sequences, dimension):
    
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0 
        
    return results
    
'''
1. 드롭 아웃을 적용할 모델과 비교하기 위한
   하나의 기본 모델을 자유롭게 생성합니다.
'''

def Basic(word_num):
    
    basic_model = tf.keras.Sequential([tf.keras.layers.Dense(128,input_shape=(word_num,),activation='relu'),tf.keras.layers.Dense(
    128,activation='relu'),tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return basic_model
    
'''
2. 기본 모델에 드롭 아웃 레이어를 추가합니다.
   일반적으로 마지막 히든층과 출력층 사이에 하나만 추가합니다.
   드롭 아웃 적용 확률은 자유롭게 설정하세요.
'''

def Dropout(word_num):
    
    dropout_model = tf.keras.Sequential([tf.keras.layers.Dense(128,input_shape=(word_num,),activation='relu'),
    tf.keras.layers.Dropout(0.3),
    tf.keras.layers.Dense(
    128,activation='relu'),
    tf.keras.layers.Dropout(0.3),
    tf.keras.layers.Dense(1,activation='sigmoid')])
    
    return dropout_model

'''
3. 두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. Basic, Dropout 함수를 이용해 두 모델을 불러옵니다.
   
   Step02. 두 모델의 손실 함수, 최적화 알고리즘, 
           평가 방법을 설정합니다.
   
   Step03. 두 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 두 모델을 학습시킵니다. 
           두 모델 모두 'epochs'는 20,
           'batch_size'는 500으로 설정합니다. 
           검증용 데이터도 설정해주세요.
   
   Step05. 두 모델을 테스트하고 
           binary crossentropy 점수를 출력합니다. 
           둘 중 어느 모델의 성능이 더 좋은지 확인해보세요.
'''


def main():
    
    word_num = 100
    data_num = 25000
    
    # Keras에 내장되어 있는 imdb 데이터 세트를 불러오고 전처리합니다.
    
    (train_data, train_labels), (test_data, test_labels) = tf.keras.datasets.imdb.load_data(num_words = word_num)
    
    train_data = sequences_shaping(train_data, dimension = word_num)
    test_data = sequences_shaping(test_data, dimension = word_num)
    
    basic_model =Basic(word_num)   # 기본 모델입니다.
    dropout_model = Dropout(word_num)  # 드롭 아웃을 적용할 모델입니다.
    
    basic_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    dropout_model.compile(loss='binary_crossentropy',optimizer='adam',metrics= ['accuracy', 'binary_crossentropy'])
    
    basic_model.summary()
    dropout_model.summary()
    
    basic_history = basic_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),     
    verbose=0)
    print('\n')
    dropout_history = dropout_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),     
    verbose=0)
    
    scores_basic = basic_model.evaluate(test_data, test_labels,verbose=0)
    scores_dropout = dropout_model.evaluate(test_data, test_labels,verbose=0)
    
    print('\nscores_basic: ', scores_basic[-1])
    print('scores_dropout: ', scores_dropout[-1])
    
    Visulaize([('Basic', basic_history),('Dropout', dropout_history)])
    
    return basic_history, dropout_history

if __name__ == "__main__":
    main()
    

배치 정규화(Batch Normalization)

배치 정규화(Batch Normalization)는 정규화를 모델에 들어가는 입력 데이터뿐만 아니라 모델 내부 히든층의 입력 노드에도 적용하는 것입니다.

배치 정규화를 적용하면 매 층마다 정규화를 진행하므로 가중치 초기값에 크게 의존하지 않습니다. 즉, 가중치 초기화의 중요도가 감소합니다.

또한 과적합을 억제합니다. 즉, 드롭 아웃(Drop out)과 L1, L2 정규화의 필요성이 감소합니다.

가장 큰 장점은 학습 속도도 빨라진다는 것입니다.

배치 정규화를 사용하기 위한 함수/라이브러리

배치 정규화는 하나의 레이어로써 Dense 레이어와 활성화 함수 사이에서 작용합니다.

따라서 이번 실습에서는 기본 모델을 생성할 때 활성화 함수와 똑같은 역할을 하는 Activation 레이어를 따로 활용해야 합니다.

tf.keras.layers.BatchNormalization()

배치 정규화를 하는 레이어입니다.
tf.keras.layers.Activation()

활성화 함수를 추가하는 레이어입니다.

활성화 함수 레이어와 Dense 레이어를 활용하는 예시는 아래와 같습니다. 즉, 활성화 함수 레이어를 사용하면 Dense 레이어에서 activation을 설정하지 않아도 됩니다.

...
tf.keras.layers.Dense(256),
tf.keras.layers.Activation('relu')
...
Copy
실습을 통해 배치 정규화를 활용해봅시다. 이번 실습에 사용할 데이터셋은 [실습4]와 마찬가지로 MNIST 손글씨 데이터셋입니다.

실습
기본 모델의 Dense 레이어와 Activation 레이어 사이에 배치 정규화 레이어를 넣어서 새로운 모델을 생성합니다.

두 개의 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

두 모델에 대해 손실 함수(loss function), 최적화(optimize) 알고리즘, 평가 방법(metrics)은 다음과 같이 설정합니다.

손실 함수(loss) : ‘sparse_categorical_crossentropy’
최적화 알고리즘(optimizer) : ‘adam’
평가 방법(metrics): [‘accuracy’]
Tips!
이번 실습에서 사용한 데이터는 비교적 간단한 MNIST 데이터이고, 모델도 복잡하지 않기 때문에 배치 정규화를 적용한 결과와 그렇지 않은 결과의 차이가 크지 않을 수 있습니다.

그러나 MNIST보다 훨씬 고차원의 데이터를 더욱 복잡한 딥러닝 모델에 사용할 경우, 배치 정규화가 모델의 성능 향상에 큰 도움이 됩니다.

import numpy as np
import tensorflow as tf
from visual import *

import logging, os
logging.disable(logging.WARNING)
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 

np.random.seed(200)
tf.random.set_seed(200)

# 배치 정규화를 적용할 모델과 비교하기 위한 기본 모델입니다.

def Basic():
    
    basic_model = tf.keras.Sequential([
                  tf.keras.layers.Flatten(input_shape=(28, 28)),
                  tf.keras.layers.Dense(256),
                  tf.keras.layers.Activation('relu'),
                  tf.keras.layers.Dense(128),
                  tf.keras.layers.Activation('relu'),
                  tf.keras.layers.Dense(512),
                  tf.keras.layers.Activation('relu'),
                  tf.keras.layers.Dense(64),
                  tf.keras.layers.Activation('relu'),
                  tf.keras.layers.Dense(128),
                  tf.keras.layers.Activation('relu'),
                  tf.keras.layers.Dense(256),
                  tf.keras.layers.Activation('relu'),
                  tf.keras.layers.Dense(10, activation='softmax')
                  ])
    
    return basic_model

'''
1. 기본 모델에 배치 정규화 레이어를 적용한 
   모델을 생성합니다. 입력층과 출력층은 그대로 사용합니다.
'''

def BN():
    
    bn_model = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(256),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Activation('relu'),
        tf.keras.layers.Dense(128),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Activation('relu'),
        tf.keras.layers.Dense(512),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Activation('relu'),
        tf.keras.layers.Dense(64),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Activation('relu'),
        tf.keras.layers.Dense(128),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Activation('relu'),
        tf.keras.layers.Dense(256),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Activation('relu'),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return bn_model

'''
2. 두 개의 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가합니다.

   Step01. Basic, BN 함수를 이용해 두 모델을 불러옵니다.
   
   Step02. 두 모델의 손실 함수, 최적화 알고리즘, 
           평가 방법을 설정합니다.
   
   Step03. 두 모델의 구조를 확인하는 코드를 작성합니다.
   
   Step04. 두 모델을 학습시킵니다. 
           두 모델 모두 'epochs'는 20,
           'batch_size'는 500으로 설정합니다. 
           검증용 데이터도 설정해주세요.
   
   Step05. 두 모델을 테스트하고 accuracy 값을 출력합니다. 
           둘 중 어느 모델의 성능이 더 좋은지 확인해보세요.
'''

def main():
    
    # MNIST 데이터를 불러오고 전처리합니다.
    mnist = tf.keras.datasets.mnist
    (train_data, train_labels), (test_data, test_labels) = mnist.load_data()
    train_data, test_data = train_data / 255.0, test_data / 255.0
    
    basic_model =Basic()  # 기본 모델입니다.
    bn_model = BN()     # 배치 정규화를 적용할 모델입니다.
    
    basic_model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    bn_model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
    
    basic_model.summary()
    bn_model.summary()
    
    basic_history = basic_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),
    verbose=0)
    print('\n')
    bn_history = bn_model.fit(train_data, train_labels,epochs=20,batch_size=500,validation_data=(test_data, test_labels),
    verbose=0)
    
    scores_basic = basic_model.evaluate(test_data, test_labels,verbose=0)
    scores_bn = bn_model.evaluate(test_data, test_labels,verbose=0)
    
    print('\naccuracy_basic: ', scores_basic[-1])
    print('accuracy_bn: ', scores_bn[-1])
    
    Visulaize([('Basic', basic_history),('Batch Normalization', bn_history)])
    
    return basic_history, bn_history

if __name__ == "__main__":
    main()
    
profile
코딩으로 빛나게

0개의 댓글