파이썬 이것저것/파이썬 딥러닝 관련

[Python] 딥러닝 학습 속도 문제와 최적화

agingcurve 2022. 7. 14. 23:42
반응형

딥러닝 학습의 문제점


데이터가 많아지면서 실질적인 한계에서 학습 시간이 기하급수적으로 증가하였고, 데이터 또한 수가 많아지면서 복잡해지는 문제가 생김

 

 

학습 속도 문제

데이터의 개수가 폭발적으로 증가하여 딥러닝 모델 학습 시 소요되는 시간도 함께 증가

 

기울기 소실 문제

 

더 깊고 더 넓은 망을 학습시키는 과정에서 출력값과 멀어질 수록 학습이 잘 안되는 현상 발생

역전파는 미분을 하면서 기울기가 소실된다는 것은0에 가까운 역전파가 예를 들어 0.1 * 0.1 이면 점점 0으로 수렴하게되고, 이에 가까운 파라미터 들은 잘 학습을 하지 못하게 되는 문제가 있음 실제로 찾아야 되는 weight 값을 찾지 못하는 문제가 있음

 

초기값 설정 문제

 

학습시 파라미터가 많지만 무언가의 초깃값을 설정해 줘야하는데, 가장 적합한 값을 줘야 하는데, 이것을 적절히 줘야 최적의 값을 찾을 수 있음 

초기값 설정 방식에 따른 성능 차이가 매우 크게 발생

 

 

과적합 문제

 

머신러닝과 마찬가지로 딥러닝의 경우, 파라미터가 많고 모델이 복잡하여 복잡한 모델에서 자신이 갖고있는 학습데이터를 외우기 때문에 새로운 모델에 잘 적응 못하는 문제가 있음

학습 데이터(training data)에 모델이 과하게 최적화되어 테스트 데이터(test data)에 대한 모델 성능이 저하

학습 속도 문제의 발생 원인

 

전체 학습 데이터 셋을 사용하여 손실 함수를 계산하기 때문에 계산량이 너무 많아짐

전체 데이터가 아닌 부분 데이터만 활용하여 손실 함수를 계산할 필요가 있음

 

SGD(Stochastic Gradient Descent)

 

- 전체 데이터를 학습하는게 아니기 때문에 정확도가 떨어지지만, 나눠서 실시하지만 나중에 전체 데이터로 진행하는것과 유사한 값에 도달할것이다. 라는게 증명이 되었음. 나눠서 진행하면 최적점에 도달 할 수 있음

전체 데이터(batch) 대신 일부 조그마한 데이터의 모음인 미니 배치(mini-batch)에 대해서만 손실 함수를 계산

 

 

GD vs SGD

빠른 시간에 더 많이 학습하는 SGD 알고리즘

다소 부정확할 수 있지만, 훨씬 계산 속도가 빠르기 때문에 같은 시간에 더 많은step을갈수있음

SGD의 한계 : Gradient 방향성 문제

SGD는 gradient 값 계산 시, mini-batch에 따라 gradient 방향의 변화가 큼

 

 

SGD의 한계 : Learning Rate 설정 문제

Learning Rate 문제


a * Grad 하게 되는데, gradinent에 a 값을 곱해서 방향으로 가게되는데 a 값이 크면 loss를 나타내는 그래프에서 왔다 갔다 많이 하게됨 그런데 반대로 작다면, 방향으로 가지만 조금조금 가게 됨 적합한 learning rate를 찾는게 중요

 

 

 

다양한 최적화 알고리즘의 등장

 

Momentum

 

과거에 이동했던 방식을 기억하면서 그 방향으로 일정 정도를 추가적으로 이동하는 방식

예를 들어 산을 올라 갔다고 보면, 계속 올라가려는 성질이 있다. 즉, 과거의 이동했던 방식을 기억해서 그 방향으로 가려고 하는 방식이다.

 

AdaGrad(Adaptive Gradient)

 

Learning rate가 많이 변화했다면 작게 해주고, 그게 아니라면 더욱 크게해주는 대로 과거의 값들을 제곱을 해서 이를 변형시키는 것임 a 값을 fix하는 것보단 잘됨

과거의 기울기를 제곱해서 계속 더하기 때문에 학습이 진행될수록 갱신 강도가 약해짐

 

RMSProp

 

무한히 학습하다보면 순간 갱신량이 0에 가까워 학습이 되지 않는 Adagrad의 단점을 해결 과거의 기울기는 잊고 새로운 기울기 정보를 크게 반영

Adgrad는 모든 값을 기억한다면, RMSProp는 moving average를 사용하여 최근에 가중값을 사용함

 

Adam

 

Momentum + RMSProp 가장 발전된 최적화 알고리즘

 

 

 

GD vs SGD(Stochastic Gradient Descent)

GD(Gradient Descent) 는 시작 지점에서 기울기의 반대 방향으로 하강하면서 손실 함수(loss function)를 최소화하는 지점을 찾기 위한 가장 직관적인 방법

이처럼 전체 데이터 셋을 가지고 학습하게 되면 안정적이긴 하지만, 계산량과 학습 비용이 많아지게 됨

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

데이터셋은 IMDB 영화 리뷰 데이터 셋을 사용

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.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. 두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가

'''

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) 기법은 관성의 개념을 이용해 최적값에 좀 더 빠르게 도달할 수 있도록 도와줌

 

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. 두 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가

'''
    
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) 알고리즘

Adagrad

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

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

 

RMSprop

RMSprop 최적화 알고리즘은 학습이 진행될수록 가중치 업데이트 강도가 약해지는 Adagrad의 단점을 보완하고자 제안된 방법 RMSProp은 과거의 gradient 값은 잊고 새로운 gradient 값을 크게 반영해서 가중치를 업데이트

 

Adam

Adam은 최적화 알고리즘 중 가장 발전된 기법 RMSProp과 모멘텀(momentum)을 함께 사용함으로써, 진행 방향과 learning rate 모두를 적절하게 유지하면서 학습할 수 있도록 고안

 

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. 세 모델을 불러온 후 학습시키고 테스트 데이터에 대해 평가

'''

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, epsilon = 0.00001, decay = 0.4)
    rmsprop_model.compile(loss = "binary_crossentropy", optimizer = rmsprop_opt, metrics=["accuracy","binary_crossentropy"])
    
    adam_opt = tf.keras.optimizers.Adam(lr=0.001, epsilon = 0.00001, decay = 0.4)
    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, verbose=0)
    scores_rmsprop = rmsprop_model.evaluate(test_data, test_labels, verbose=0)
    scores_adam = adam_model.evaluate(test_data, test_labels, verbose=0)
    
    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)의 발생 원인

예를들어 Final Output이 0.1 나왔는데, 1.0이 실제값이라면, output이 차이가 나는가 라는 것을 볼때, 미분을 한다는 것인데, 갈수록  Chain Rule 적용하여 미분을 실시하면 점점 0으로 수렴하여 학습을 하기 어려워진다.

기울기가 0인 값을 전달하며 중간 전달값이 사라지는 문제

 

기울기 소실 문제 해결 방법

활성화 함수(Activation Function) 방식 변화 activation function을 어떻게 적용하는지에 따라 달라진다.

기존에 사용하던 sigmoid 함수 대신 ReLU 함수를 사용하여 해결한다. 내부 Hidden Layer에는 ReLU를 적용하고 Output Layer에서만 Tanh를 적용

기울기 소실 확인해보기

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(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, activation="relu"),
        tf.keras.layers.Dense(32, 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(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(32, activation="sigmoid"),
        tf.keras.layers.Dense(10, activation='softmax')
    ])
    
    return model_sig

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

'''

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

 

활성화 함수 다르게 적용해보기

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(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(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(10, activation='softmax')
    ])
    
    return model_tanh

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

'''

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(optimizer="adam",loss="sparse_categorical_crossentropy", metrics = ["accuracy"])
    model_relu.compile(optimizer="adam",loss="sparse_categorical_crossentropy", metrics = ["accuracy"])
    model_tanh.compile(optimizer="adam",loss="sparse_categorical_crossentropy", 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()