본문 바로가기
Study/Deep learning

Deep Learning(3) - Optimizer 와 activation함수 알아보기

by 왕방개 2024. 3. 20.

1.subclassing

1)개요

 

-Sequential API 나 Functional API는 선언적 방식인데 사용할 층과 연결 방식을 정의한 후 모델에 데이터를 주입해서 훈련이나 추론을 하는 방식

-선언적 방식은 장점이 많은데 모델을 저장하거나 복사 또는 공유하기 쉬우며 모델의 구조를 출력하거나 분석하기도 좋고 프레임워크가 크기를 짐작하고 타입을 확인해서 에러를 일찍 발견할 수 있고 디버깅하기도 쉬움

정적이라는 단점이 존재하는데 수정하지 못함

- subclassing 은 기존의 클래스를 상속받아서 수정해서 사용하는 것

-Models 클래스를 상속받고 _init_메서드에서 필요한 층을 만들 call 메서드 안에서 수행하려는 연산을 기술하고 출력층을 리턴하도록 작성

-subclassing을 하고자 하면 원 클래스에 메서드들의 기능을 확인할 필요가 있습니다

필요한 메서드를 오버라이딩해서 사용

 

 

=>샘플

class WideandDeepModel(keras.models.Model):

def _init(self,필요한 매개변수, **kwargs):

   super().__init__(**kwargs)

   self.hidden1 = keras.layers.Dense(units,activation)

   self.hidden 2 = keras.layers.Dense(units,activation)

   self.hidden 3 = keras.layers.Dense(units,activation)

   self.output = keras.layers.Dense(units)



def call(self.inputs):

  hidden1 = self.hidden1(input_B)

  ...

  return self.output



model = WideAndDeepModel(매개변수)

파이썬에서 API 클래스를 상속할 때 _init_ 에서 상위 클래스의 _init_ 을 호출해야 하는 이유와 API를 읽어봐야 하는 이유

=>상속을 받는 것은 상위 클래스의 기능에 필요한 기능을 추가하기 위해서 입니다.

 

heritance(상속) => 하위 클래스가 상위 클래스에 모든 것을 물려받는 것

원래 상속을 하려 했던 이유는 중복되는 것을 없애기 위해서입니다. method 1을 공통적으로 class 가 가지고 있을 때 method1을 수정할때 한번만 수정할 수 있게 해주기 위해서 

 

framework은 사람들이 공통적으로 자주 사용하는 것을 따로 만들어놓는 것을 말합니다. Framework(프로그램),library(API),solution(목적) 또 다른 목적은 상속을 받고 기능을 추가할 수 있게 해줍니다. 이게 __init__입니다

__Init__ 할 떄 모델의 function을 사용하기 위해서 super().__init__ 이 생깁니다.

대신에 아주 특별한 경우가 있는데 어떤 class 에 빈 메서드(내용이 없는 애를 만듬) Abstract 을 만듭니다. 빈 메서드를만드는 이유는 이름을 똑같이 하게 하기 위해서입니다.

스타크래프트에서 종족이 3개 있는데 공격 기능의 이름을 다 다르게 하면 이해하기도 어렵기 때문에 attack 이라는 빈 메서드를 만들고 나서 종족 3개를 통일시킬 수 있습니다. 이를 interface 라고 부르기도 하고 protocol이라고 부르기도 합니다.

이 경우에는 비어있으니까 super를 쓸 일이 없습니다. 실무에서는 상속을 할 일이 많음.

상속이라 말하지말고 subclassing , has_a,is_a 라고 하는게 좀 더 맞음 . subclassing은 빈 메서드로 이름을 통일하는 경우, has_a 와 is_a 는 위와 아래의 연결과정을 만들 수 있습니다.

 

2)장점

=>모델을 생성할 때 필요한 매개변수를 직접 설정할 수 있기 떄문에 동적인 모델을 만들 수 있습니다.

 

2.CallBack

=>callback: 어떤 사건이 발생했을 때 특정 작업을 수행하도록 하는 것

1)조기 종료

=>epochs을 크게 지정하면 훈련을 많이 수행하기 때문에 성능이 좋아질 가능성이 높은데 늘리게 되면 훈련 시간이 오래 걸립니다.

=>일정 epoch 동안 검증 세트에 대한 점수가 향상되지 않으면 훈련을 멈추도록 할 수 있습니다

=>keras.callbacks.EarlyStopping 클래스의 인스턴스를 만들 떄 patience 매개변수에 원하는 에포크 지정해서 만들고 모델이 fit 메서드를 호출할 때 callbacks 파라미터에 list 형태로 대입하면 됩니다.

early_stopping_cb = keras.callbacks.EarlyStopping(patience=5)

history = model.fit((X_train_A,X_train_B),y_train,
                    epochs = 20,validation_data = ((X_valid_A,X_valid_B),y_valid),callbacks=[early_stopping_cb])

 

2)학습률 스케쥴러

=>학습률을 동적으로 변화시켜서 수행하는 것이 가능

=>LearningRateScheduler 라는 클래스를 이용하는데 이 때는 에포크와 학습률을 매개변수로 갖는 함수를 만들어서 인스턴스를 생성할 때 대입해주면 됩니다

early_stopping_cb = keras.callbacks.EarlyStopping(patience=5)

#5번의 epoch 동안은 기존 학습률을 유지하고 그 이후에는 학습률을 감소시키는 함수
def scheduler(epoch,lr):
    if epoch < 5:
        return lr
    else:
        lr = lr - 0.0001
        return lr

#학습률을 동적으로 변화시키는 checkpoint
lr_scheduler = tf.keras.callbacks.LearningRateScheduler(scheduler)

    

history = model.fit((X_train_A,X_train_B),y_train,
                    epochs = 20,validation_data = ((X_valid_A,X_valid_B),y_valid),callbacks=[early_stopping_cb])

 

3.모델 저장과 복원

=>딥러닝은 fit 함수를 호출한 후 다음에 다시 fit 을 호출하면 이전에 훈련한 이후 부터 다시 훈련하는 것이 가능

=>기존 모델을 저장하고 이를 읽어들여서 재훈련이 가능

 

1)모델 저장

save함수를 이용해서 저장
모델을 저장할 때 loss는 반드시 full name으로 적어야함
버전에 따라서는 확장자를 제한하는 경우도 있음. 
최신 버전에서 이 부분이 적용됨.

# 모델 저장
model.save('my_model.keras')

 

2)모델 복원

#모델 복원

m = keras.models.load_model('my_model.keras')

print(m.predict(X_new))

 

3)모델 가중치 저장하고 복원

=>save_weights 와 load_weights 를 이용

#모델 가중치 저장하고 가져오기

model.save_weights("my_model.wights.h5")

model.load_weights("my_model.wights.h5")

가중치 저장할때는 weights.h5을 반드시 붙여줘야 에러가 나질 않음

 

4)모델을 저장하는 체크 포인트 사용

=>ModelCheckPoint라는 클래스를 이용해서 모델의 이름을 설정하면 됩니다

=>체크 포인트를 만들 때 이름만 설정하면 마지막 모델이 저장됩니다.

마지막 모델은 최상의 모델이 아닐 수 있습니다.

save_best_only = True 를 설정하면 최상의 모델을 저장합니다

#체크포인트 활용
checkpoint_cb = keras.callbacks.ModelCheckpoint('my_model.keras',
                                              save_best_only=True)

history = model.fit(X_train,y_train,epochs=10,
                   validation_data =(X_valid,y_valid),
                   callbacks = [checkpoint_cb])

 

4.사용자 정의 콜백

=>Callback 클래스를 상속받아서 원하는 콜백을 생성할 수 있는 기능

 

=>on_train_begin,on_train_end, on_epoch_begin,on_epoch-end,on_batch_bigin,on_batch_end 이 메서드들은 훈련 전후 그리고 한 번의 에포크 전 후 그리고 배치 전 후에 작업을 수행시키고자 하는 경우에 사용

 

=>on_test로 시작하는 메서드를 오버라이딩하면 검증 단계에서 작업을 수행할 수 있습니다

 

5.신경망의 하이퍼 파라미터 튜닝

1)개요

=>신경망의 유연성은 단점이 되기도 하는데 조정할 하이퍼 파라미터가 많음
=>복잡한 네트워크 구조에서 뿐 만 아니라 간단한 다층 퍼셉트론에서도 층의 개수, 층마다 존재하는 뉴런의 개수, 각 층에서 사용하는 활성화 함수, 가중치 초기화 전략 등 많은 것을 바꿀 수 있는데 어떤 하이퍼 파라미터 조합이 주어진 문제에 대해서 최적인지 확인을 해야 합니다.
=>이전 머신러닝 모델들은 GridSearchCV 나 RandomizedSearchCV를 이용해서 하이퍼 파라미터 공간을 탐색할 수 있었는데 딥러닝에서는 이 작업을 할려면 Keras 모델을 sklearn 추정기 처럼 보이도록 바꾸어야 합니다.

 

 

2)신경망의 하이퍼 파라미터 튜닝

# 딥러닝 모델을 만드는 함수
def build_model(n_hidden=1, n_neurons=30, learning_rate=0.003, input_shape=[8]):
    model = keras.models.Sequential()
    #입력 레이어 추가
    model.add(keras.layers.InputLayer(input_shape=input_shape))
    #n_hidden 만큼 히든 층 추가
    for layer in range(n_hidden):
        model.add(keras.layers.Dense(n_neurons, activation="relu"))
    #출력 층 추가
    model.add(keras.layers.Dense(1))
    #최적화 함수 생성
    optimizer = keras.optimizers.SGD(learning_rate = learning_rate)
    model.compile(loss="mean_squared_error", optimizer=optimizer, metrics=['mse'])
    return model

 

#하이퍼 파라미터 튜닝을 위해서는 scikit-learn 추정기로 딥러닝 모델을 생성
!pip install scikeras

from scikeras.wrappers import KerasRegressor

keras_reg = KerasRegressor(build_model())


from sklearn.model_selection import RandomizedSearchCV

param_distribs = {
    "epochs":[100, 200, 300, 400, 500, 600]
}

#랜더마이즈드 cv를 생성
rnd_search_cv = RandomizedSearchCV(keras_reg, param_distribs, n_iter=10, cv=3,
                                  verbose=2)
#하이퍼파라미터 튜닝
rnd_search_cv.fit(X_train, y_train, epochs=100,
                 validation_data=(X_valid, y_valid),
                 callbacks=[keras.callbacks.EarlyStopping(patience=10)])

 

=>은닉 층 개수
 - 이론적으로는 은닉 층이 하나인 다층 퍼셉트론이라도 뉴런 개수가 충분하다면 아주 복잡한 함수도 모델링할 수 있다라고 하지만 심층 신경망이 얕은 신경망보다 파라미터 효율성이 좋다라고 알려져 있음
 - 심층 신경망은 복잡한 함수를 모델링하는데 얕은 신경망보다 적은 수의 뉴런을 사용하기 때문에 동일한 양의 훈련 데이터에서 더 높은 성능을 나타낼 수 있음
 - 심층 신경망을 만들면 아래쪽 은닉 층은 저수준의 구조를 모델링하고 중간 은닉 층은 저수준의 구조를 연결해서 중간 수준의 구조를 모델링하고 가장 위쪽 은닉 층 과 출력 층은 중간 수준의 구조를 연결해서 고수준의 구조를 모델링 
 - MNIST 데이터 세트에서 하나의 은닉층에 수백개의 뉴런을 사용한 경우 정확도가 97% 정도 나왔는데 동일한 수의 뉴런을 사용하고 은닉층을 2개로 늘렸을 때 98% 정도의 정확도
 - 복잡한 문제들은 훈련 세트에 과대 적합이 될 때 까지 점진적으로 은닉 층의 개수를 늘릴 수 있는데 대규모 이미지 분류나 음성 인식 분야에서는 수십개에서 수백개의 은닉층을 가지게 되는데 이런 경우는 훈련 데이터가 아주 많이 필요함
일반적으로 이런 네트워크를 처음부터 훈련하는 경우는 거의 없음
비슷한 작업에서 가장 뛰어난 성능을 낸 미리 훈련된 네트워크 일부를 재사용

=>은닉 층의 뉴런의 개수
 - 입력 층 과 출력 층의 뉴런의 개수는 수정할 수 없음
 - 은닉 층의 뉴런의 개수는 일반적으로 각 층의 뉴런의 개수를 줄여가면서 깔대기처럼 구성하는데 저수준의 많은 특성이 고수준의 적은 특성으로 합쳐질 수 있기 때문
 - 뉴런의 개수는 일단 많이 가지고 시작을 하다가 조기 종료 나 규제를 이용해서 조정

=>학습률
 - 좋은 학습률을 찾는 방법은 아주 작은 학습률(0.00001) 에서 시작해서 점진적으로 큰 학습률(10) 까지 수백 번 반복하여 모델을 훈련
보통의 경우는 0.00001 ~ 10 까지 exp(log (10의 6승 / 500)) 을 곱해가면서 조정

=>Optimizer
 - SGD 보다는 최근에 등장한 Optimizer 사용을 권장

=>Batch_Size
 - 32 이하의 크기를 사용하는 권장하지만 최근에는 학습률을 작은 값에서 큰 값으로 변경해가면서 수행하는 경우는 192 정도의 크기도 괜찮다고 합니다.
 - 배치 크기가 커지면 훈련 시간을 단축할 수 있음
 - 학습률을 변경하면서 수행할 때 큰 배치 크기를 사용하고 훈련이 불안정해지면 작은 배치 크기를 선택

=>활성화 함수
 - 일반적으로 ReLU 가 좋은 값

=>반복 횟수
 - 대부분 크게 설정하고 조기 종료를 설정

 

 

6.딥 러닝에서의 문제점 과 해결책

1)그라디언트 소실 과 폭주 문제
=>역전파 알고리즘: 출력 층에서 입력 층으로 오차 그라디언트를 전파하면서 진행되는데 알고리즘이 신경망의 모든 파라미터에 대한 오차 함수의 그라디언트를 계산하면 경사 하강법 단계에서 이 그라디언트를 사용해서 파라미터를 수정

=>알고리즘이 하위 층으로 진행될 수 록 그라디언트는 점점 작아지는 경우가 많은데 경사 하강법이 하위 층의 연결 가중치를 변경하지 않은채로 둔다면 훈련이 좋은 솔루션을 찾지 못하게 되는데 이를 vanishing gradient(그라디언트 소실) 이라고 하고 그라디언트가 점점 더 커져서 여러 층이 비정상적으로 큰 가중치를 가지되면 알고리즘은 발산한다고 하는데 이를 exploding gradient(폭주) 라고 하는데 RNN에서 주로 나타남

=>이 문제를 발생시키는 요인으로 찾은 것 중의 하나가 활성화 함수로 로지스틱 시그모이드 함수를 사용하고 가중치 초기화를 할 때 평균이 0이고 표준 편차가 1인 정규 분포에서 추출한 값으로 한 것
로지스틱 시그모이드 함수는 평균이 0이 아니고 0.5 이기 때문에 이 문제가 발생한다고 보고 하이퍼볼릭 탄젠트 함수(평균이 0)를 사용

 

2)글로럿 과 He 초기화
=>딥러닝 초창기에는 가중치 초기화를 할 때 평균이 0이고 표준 편차가 1인 정규 분포에서 추출한 값을 가지고 사용
=>글러럿 초기화 또는 세이비어 초기화
 - 평균이 0이고 분산이 1/fan(avg) 인 정규 분포 나 제곱근(3/fan(avg)) 한 값에 -1 과 1을 곱한 값의 균등 분포에서 초기화
   fan-in: 입력의 개수
   fan-out: 출력의 개수
   fan(avg) = (fan-in + fan-out)/2
 - 훈련 속도도 향상
 - fan(avg) 대신에 fan-in을 사용하면 르쿤 초기화라고 함
=>He 초기화
 - ReLU 와 그 변종들
 2/fan-in 인 정규분포를 가지고 초기화
=> keras 에서 지원하는 목록: https://www.tensorflow.org/api_docs/python/tf/keras/initializers
=>기본값은 Glorot 초기화 인데 다른 방법을 사용하고자 하면 kernel_initializer="초기화 방법" 을 지정하거나 클래스의 인스턴스로 지정
=>지원 가능한 목록

print([name for name in dir(keras.initializers) if not name.startswith("_")])


=>사용

keras.layers.Dense(10, activation="relu", kernel_initializer = "he_normal")

 

3)수렴하지 않은 활성화 함수

=>딥러닝 모델을 만들 때 잘못된 활성화 함수를 선택하면 그라디언트 소실이나 폭주 문제가 발생할 수 있음

=>초창기에는 sigmoid 함수가 최선이라고 생각했지만 이후에 다양한 활성화 함수 등장

=>많이 사용되는 relu,softmax  , hyperbolic tanh 등이 있습니다.

=>ReLU

-특정 양수 값에 수렴하지 않는다는 장점이 있음

-계산이 빠름

-학습률을 너무 높게 설정하면 가중치의 합이 음수가 되서 뉴런이 죽어버림

     max(0,x) 

-뉴런이 죽는 문제를 해결하기 위해서 max(알파*x, x)를 적용하는 방식이 있는데 이것이 LeakyReLU

-활성화 함수로 적용해도 되지만 층을 만들어서 추가해도 됩니다

-최근의 Tensorflow 에서 제공하는 Keras API 는 문자열로 설정하는 부분을 인스턴스를 생성해서 적용하는 부분으로 변경되고 있습니다

=>데이터 생성

##사용할 데이터

(X_train_full, y_train_full), (X_test, y_test) = keras.datasets.fashion_mnist.load_data()
X_train_full = X_train_full / 255.0
X_test = X_test / 255.0
X_valid, X_train = X_train_full[:5000], X_train_full[5000:]
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]

=>모델 생성 및 학습

#모델 생성
tf.random.set_seed(42)
np.random.seed(42)

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(300, kernel_initializer="he_normal"),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(100, kernel_initializer="he_normal"),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(10, activation="softmax")
])

#모델 컴파일
model.compile(loss="sparse_categorical_crossentropy",
              optimizer=keras.optimizers.SGD(lr=1e-3),
              metrics=["accuracy"])
history = model.fit(X_train, y_train, epochs=10,
                    validation_data=(X_valid, y_valid))

 

 

=>알파를 무작위로 선택하고 테스트할 때는 평균을 사용하는 방식인 RReLU 방식도 제안

=>알파 값을 훈련하는 동안 역전파에 의해서 변경하는 PReLU 도 등장

대규모 이미지셋에서는 매우 잘 동작하는데, 소규모 데이터에서는 과대 적합이 발생하는 경우가 있음

 

 

=>ELU함수

-ReLU와 유사한데 0보다 작을 때 알파(exp(데이터)-1)로 계산

-Dense 층에 activation 매개변수에 elu로 바로 적용

-훈련 속도는 좋은데 검증 속도가 느림

 

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(300, kernel_initializer="he_normal",activation='elu'),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(100, kernel_initializer="he_normal",activation='elu'),
    keras.layers.LeakyReLU(),
    keras.layers.Dense(10, activation="softmax")
])

#모델 컴파일
model.compile(loss="sparse_categorical_crossentropy",
              optimizer=keras.optimizers.SGD(learning_rate=1e-3),
              metrics=["accuracy"])

history = model.fit(X_train, y_train, epochs=10,
                    validation_data=(X_valid, y_valid))

 

=>SELU

-Scaled ELU 인데, 출력을 평균이 0 이고 표준편차가 1이 되도록 만들어주는 활성화 함수

-다른 활성화 함수보다 뛰어난 성능을 발휘

-제약조건 : 입력 특성이 반드시 표준화 되어 있어야 합니다( 평균이 0 이고 표준편차가 1이어야함)

                   가중치 초기화는 르쿤 초기화가 되어 있어야함 =>'he_normal'같은 애는 못씀

                   Sequential API로 생성된 모델이어야함

 

-단점: 

         훈련 시간이 오래 걸림

-activation 매개변수에 selu를 설정하면 됩니다.

- relu 보다 6배 정도 차이가 남

 

=>컴퓨팅 자원이 허락한다면 여러가지 초기화 방법을 적용해서 학습을 진행하면 의미 있는 결과를 얻어 낼 수 있습니다.

 

4)배치 정규화

=>활성화 함수를 변경하고 초기화 기법을 변경해서 그라디언트 소실 문제나 폭주 문제를 어느정도 해결할 수 있는데 훈련을 많이 하게 되면 완전히 해결했다고 보기는 어려움

 

=>활성화 함수에 대입하기 전이나 후에 데이터를 원점에 맞추고 정규화한 다음 2개의 새로운 파라미터를 추가해서 값의 스케일을 조정하고 이동시킴

 

이 기법을 배치 정규화 (Batch Normalization)이라고 합니다.

=>새로 만들어진 2개의 파라미터는 하나는 스케일 조정에 사용하고 하나는 원점 이동에 사용

=>배치 정규화 층을 신경망의 첫번째 층으로 사용하면 훈련 세트를 표준화 할 필요가 없음

=>이 역할을 Batch Normalization이라는 층을 이용해서 수행

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300, activation="relu"),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(100, activation="relu"),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(10, activation="softmax")
])

model.summary()

배치 정규화 층은 입력마다 네 개의 파라미터를 추가

배치 정규화를 이용하면 일부 뉴런이 죽는 현상을 거의 해소하기 때문에 성능이 좋아질 가능성이 높습니다

 

5)그라디언트 클리핑

=>역전파 될 때 일정 임계값을 넘어서지 못하게 그라디언트를 잘라내는 것

=>RNN에서는 배치 정규화를 사용하기가 어려움

이 경우에는 Optimizer 를 만들 때 clipvalue 나 clipnorm 매개변수를 이용해서 Gradient 값을 제한하는 방법으로 유사한 효과를 나타냅니다

배치 정규화가 0~ 1 사이의 값으로 정규화하므로 임계값은 1.0을 사용합니다

그라디언트의 값이 -1 보다 작거나 1보다 크면 -1 이나 1로 수정해서 기울기가 소실되는 것을 방지

 

6)사전 훈련된 층 재사용

=>큰 규모의 NN을 처음부터 훈련시키는 것은 많은 자원을 소모하게 되는데 이런 경우 해결하려는 것과 비슷한 유형의 문제를 처리하는 신경망이 있는지 확인해보고 그 신경망의 하위층을 재사용하는 것이 효율적인데 이러한 학습을 전이학습(Transfer learning)이라고 합니다

=>전이 학습을 이용하게 되면 훈련속도를 개선 및 훈련 데이터의 양도 줄어들게 됩니다

=>동일한 이미지 분류 모델이라면 하위 층에서 유사하게 점이나 선들의 모형을 추출하는 작업을 할 가능성이 높으므로 하위 층을 공유해도 유사한 성능을 발휘하게 됩니다.

=>출력 층은 하고자 하는 마지막 작업이 서로 다를 가능성이 옾고(ImageNet의 이미지를 분류하는 모델은 20000개 의 카테고리를 가지고 있고 우리는 개와 고양이를 분류하는 이진 분류의 경우)상위 층은 거의 출력과 유사한 형태가 만들어 진것이므로 역시 재사용할 가능성은 낮습니다.

 

=>샌달과 셔츠를 제외한 모든 이미지를 가지고 분류 모델을 만들고, 이를 이용해서 샌들과 셔츠 이미지중 200개만 가진 작은 훈련 세트를 훈련해서 정확도를 확인

def split_dataset(X, y):
    y_5_or_6 = (y == 5) | (y == 6) # sandals or shirts
    y_A = y[~y_5_or_6]
    y_A[y_A > 6] -= 2 # 6보다 큰 레이블은 2를 빼서 연속된 레이블로 만들기
    y_B = (y[y_5_or_6] == 6).astype(np.float32) # binary classification
    return ((X[~y_5_or_6], y_A),
            (X[y_5_or_6], y_B))

(X_train_A, y_train_A), (X_train_B, y_train_B) = split_dataset(X_train, y_train)
(X_valid_A, y_valid_A), (X_valid_B, y_valid_B) = split_dataset(X_valid, y_valid)
(X_test_A, y_test_A), (X_test_B, y_test_B) = split_dataset(X_test, y_test)
X_train_B = X_train_B[:200]
y_train_B = y_train_B[:200]

 

=>모델 A 실행

많은 양의 데이터로 모델을 훈련

이 작업은 실제로는 우리는 검색하고 모델은 load 하면 됩니다

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

#모델 생성
model_A = keras.models.Sequential()

#입력 층
model_A.add(keras.layers.Flatten(input_shape=[28, 28]))

#히든 층
for n_hidden in (300, 100, 50, 50, 50):
    model_A.add(keras.layers.Dense(n_hidden, activation="selu"))

#출력 층
model_A.add(keras.layers.Dense(8, activation="softmax"))

#컴파일
model_A.compile(loss="sparse_categorical_crossentropy",
                optimizer=keras.optimizers.SGD(lr=1e-3),
                metrics=["accuracy"])
#학습
history = model_A.fit(X_train_A, y_train_A, epochs=20,
                    validation_data=(X_valid_A, y_valid_A))

 

=>모델 저장

model_A.save("my_model_A.h5")

 

=>모델 B 실행

model_B = keras.models.Sequential()
#입력층
model_B.add(keras.layers.Flatten(input_shape=[28, 28]))

#히든 층
for n_hidden in (300, 100, 50, 50, 50):
    model_B.add(keras.layers.Dense(n_hidden, activation="selu"))

#출력 층:2개를 분류하는 것은 2가지 방법이 있음
#1일 확률을 구하는 것과 0 과 1의 확률을 구하는 것
model_B.add(keras.layers.Dense(1, activation="sigmoid"))
model_B.compile(loss="binary_crossentropy",
                optimizer=keras.optimizers.SGD(lr=1e-3),
                metrics=["accuracy"])

history = model_B.fit(X_train_B, y_train_B, epochs=20,
                      validation_data=(X_valid_B, y_valid_B))

 

=>모델 B 저장

model_B.summary()

 

=>기존  모델을 이용해서 훈련

#기존 모델 가져오기
model_A = keras.models.load_model("my_model_A.keras")

#기존 모델에서 출력층을 제외한 레이어를 가져오기
model_B_on_A = keras.models.Sequential(model_A.layers[:-1])
#출력층 추가
model_B_on_A.add(keras.layers.Dense(1, activation="sigmoid"))

model_A_clone = keras.models.clone_model(model_A)
model_A_clone.set_weights(model_A.get_weights())


#모든 레이어가 다시 훈련하지 않도록 설정
for layer in model_B_on_A.layers[:-1]:
    layer.trainable = False

model_B_on_A.compile(loss="binary_crossentropy",
                     optimizer=keras.optimizers.SGD(lr=1e-3),
                     metrics=["accuracy"])

history = model_B_on_A.fit(X_train_B, y_train_B, epochs=4,
                           validation_data=(X_valid_B, y_valid_B))

#모든 레이어 추가
for layer in model_B_on_A.layers[:-1]:
    layer.trainable = True

model_B_on_A.compile(loss="binary_crossentropy",
                     optimizer=keras.optimizers.SGD(lr=1e-3),
                     metrics=["accuracy"])
history = model_B_on_A.fit(X_train_B, y_train_B, epochs=16,
                           validation_data=(X_valid_B, y_valid_B))

 

 

기존 모델의 layer에게 새로 들어온 데이터를 학습하도록 설정

둘이 거의 비슷하게 나온것을 볼 수 있음

- 일반화 오차 비교를 하려면

성능이 model_B_on_A가 더 좋게 나온 것임을 볼 수 있음

 

7)고속 옵티마이저 사용

=>모멘텀 최적화:SGD를 사용할 때 momentum 이라는 파라미터에 규제를 가하면 일반적으로 성능이 좋아집니다

=>모멘텀 최적화에서 훈련 속도를 향상시키고자 할 때는 네스테로프 가속 경사를 이용하게 되는데 이 경우에는 use_nestrov=True를 추가

=>Adagrad, RMSProp, Adam, Nadam 등의 옵티마이저가 추가

 

8)완전 연결층에 규제를 가하는 방법

=>L1 이나 L2 규제를 추가

=>Kernel_regulaizer 라는 파라미터에다가 keras.regularizers.L1 이나 L2(값)

 

9)Dropout

=>입력 뉴런은 그대로 사용하고 출력 뉴런은 임시적으로 Drop될 확률을 설정

이전 레이어에서 전달되는 데이터를 일부분 제거하는 방식

=>확률은 일반적으로 10~ 50 % 사이로 설정하는데  RNN에서는 20~ 30 정도로 설정하고, CNN에서는 40~ 50 %정도 설정

=>이를 사용하고자 할 때는 Dropout 이라는 클래스에 rate 로 확률을 설정하면 됩니다

입력 층 앞에는 넣으면 안되고, 입력층 다음부터 추가해서 사용합니다.

 

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

Deep learning(6) - 자연어처리  (1) 2024.03.25
Deep Learning(5)-RNN  (0) 2024.03.22
Deep learning(4) - CNN  (0) 2024.03.21
DeepLearning(2)-딥러닝 기초  (0) 2024.03.19
Deep Learning(1) - 개요  (0) 2024.03.18