KimTory 2022. 2. 14. 01:06

▶ Batch Normalization

신경망의 각 층의 활성화값을 고루 분포하게 하기 위해 나온 기법

 

  • 학습 속도가 개선된다 (학습률을 높게 설정할 수 있기 때문)
  • 가중치 초깃값 선택의 의존성이 적어진다 (학습을 할 때마다 출력값을 정규화하기 때문)
  • 과적합(overfitting) 위험을 줄일 수 있다 (드롭아웃 같은 기법 대체 가능)
  • Gradient Vanishing 문제 해결

▶ 일반화 오류

일반화(generalization)란 훈련 데이터가 아닌 새로운 데이터에 대해 모델이 예측을 얼마나 잘 하는지를 의미합니다.

모델의 성능이 좋다는 말은 일반화가 잘 되었다는 것을 의미합니다.

모델의 train 성능과 validation/test 성능의 차이를 일반화 오류(generalization error)라고 합니다.

일반화 오류가 적을 수록 일반화가 잘 된 모델입니다.

두 성능의 차이가 작아야 모델이 훈련 데이터에 과적합되지 않고 새로운 데이터에 대해 일반화를 잘하는 모델이 됩니다.

정규화는 일반화를 잘 하는 모델을 만드는 기법이라고도 합니다.

→ Underfitting / Overfitting는 과소 - 과대 적합이라고 하며, 훈련 및 테스트 Data Set이 어느 한 쪽으로 치우치거나

   부족 할 경우 생기는 문제

 

Batch Normalization 알고리즘

배치 정규화는 학습 시의 미니배치를 한 단위로 정규화를 하는 것으로 분포의 평균이 0, 분산이 1이 되도록 정규화하는 것을 말한다.

hidden layer의 활성화값/출력값에 대해서 평균이 0, 분산이 1이 되도록 정규화(Normalization)를 하며

데이터 분포가 덜 치우치게 되고 배치 정규화 단계마다 확대scale와 이동shift 변환(transform)을 수행한다.


▶ 배치 정규화 효과

파란선이 배치 정규화된 선으로 점선은 정규화가 되지 않는 부분이다.

배치 정규화가 된 부분은 학습 속도가 빠르며, 가중치 초깃값에 크게 의존 하지 않아도 학습이 잘 되고 있다.

 


▶ Over fitting (과소 적합, 과대 적합)

  • 기계학습에서는 오버피팅 문제가 자주 발생하며, 신경망이 훈련 데이터에만 지나치게 적응 되어 그 외의 데이터에
    제대로 대응하지 못하는 상태를 말한다.
  • 과적합은 모델 Training 과정에서 Vali Loss가 지속적으로 감소하다가 증가하는 지점부터 발생

  • Training Loss와 Test Loss가 같이 감소하는 구간 (Underfitting)
  • Training Loss 감소, Test Loss는 증가하는 구간 (Overfitting)

    import os
    import sys
    
    sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
    import numpy as np
    import matplotlib.pyplot as plt
    from dataset.mnist import load_mnist
    from common.multi_layer_net import MultiLayerNet
    from common.optimizer import SGD
    
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
    
    # 오버피팅을 재현하기 위해 학습 데이터 수를 줄임
    # 기존 60000개 data set에서 300개만 사용하여 오버피팅을 발생 시킴
    # 훈련 데이터 셋이 적으면, 오버 피팅을 발생 시킬 수 있음
    x_train = x_train[:300]
    t_train = t_train[:300]
    
    # weight decay(가중치 감소) 설정 =======================
    
    # weight decay를 사용하지 않을 경우
    weight_decay_lambda = 0 
    
    # weight decay 0.1 적용
    #weight_decay_lambda = 0.1
    # ====================================================
    
    network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                            weight_decay_lambda=weight_decay_lambda)
    optimizer = SGD(lr=0.01) # 학습률이 0.01인 SGD로 매개변수 갱신
    
    max_epochs = 201
    train_size = x_train.shape[0]
    batch_size = 100
    
    train_loss_list = []
    train_acc_list = []
    test_acc_list = []
    
    iter_per_epoch = max(train_size / batch_size, 1)
    epoch_cnt = 0
    
    for i in range(1000000000):
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        t_batch = t_train[batch_mask]
    
        grads = network.gradient(x_batch, t_batch)
        optimizer.update(network.params, grads)
    
        if i % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            test_acc = network.accuracy(x_test, t_test)
            train_acc_list.append(train_acc)
            test_acc_list.append(test_acc)
    
            print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))
    
            epoch_cnt += 1
            if epoch_cnt >= max_epochs:
                break
    
    
    # 그래프 그리기==========
    markers = {'train': 'o', 'test': 's'}
    x = np.arange(max_epochs)
    plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
    plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.ylim(0, 1.0)
    plt.legend(loc='lower right')
    plt.show()​

    → 우측 상단, 네모 박스를 보면, Train과 Test의 정확도가 크게 차이가 나며, 훈련 데이터에만 적응 해버린 결과
    → Train 데이터 측정한 정확도는 100 epochs를 지나는 무렵 거의 100% 도달

 

▶ 가중치 감소

가중치 감소는 학습 중에 가중치가 큰 것에 대해서는 일종의 패널티를 부과해 과적합의 위험을 줄이는 방법이다. 가중치의 제곱 법칙(L2 법칙; 많이 사용된다)를 손실함수에 더해 손실함수 값이 더 커지게 한다. 그만큼 가중치가 커지는 것을 억제하기 되는 것이다.

 

L2 법칙은 1/2곱하기 λ(람다) 곱하기 W제곱 이다.

람다는 정규화의 세기를 조절하는 하이퍼파라미터이다.

람다를 크게 설정할수록 가중치에 대한 페널티가 커진다. 

import os
import sys

sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.optimizer import SGD

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 오버피팅을 재현하기 위해 학습 데이터 수를 줄임
# 기존 60000개 data set에서 300개만 사용하여 오버피팅을 발생 시킴
# 훈련 데이터 셋이 적으면, 오버 피팅을 발생 시킬 수 있음
x_train = x_train[:300]
t_train = t_train[:300]

# weight decay(가중치 감소) 설정 =======================

# weight decay를 사용하지 않을 경우
#weight_decay_lambda = 0 

# weight decay 0.1 적용
weight_decay_lambda = 0.1
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        weight_decay_lambda=weight_decay_lambda)
optimizer = SGD(lr=0.01) # 학습률이 0.01인 SGD로 매개변수 갱신

max_epochs = 201
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0

for i in range(1000000000):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    grads = network.gradient(x_batch, t_batch)
    optimizer.update(network.params, grads)

    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)

        print("epoch:" + str(epoch_cnt) + ", train acc:" + str(train_acc) + ", test acc:" + str(test_acc))

        epoch_cnt += 1
        if epoch_cnt >= max_epochs:
            break


# 그래프 그리기==========
markers = {'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

가중치 감소 이전 (Overfitting)

가중치 감소 이후

가중치 감소 이후 오버피팅 문제 개선 효과 있음.

 

▶ DropOut

  • 드롭 아웃은 뉴런을 무작위로 선택해 삭제하여 신호 전달을 차단하는 오버피팅 억제 기법 중 한 가지이다.
  • 입력값의 일부를 0으로 두기에, 역전파 시, 파라미터 업데이트가 되지 않고, 이는 모형의 불확실성을 증가시켜
    과적합 해결에 기여한다.
  • 훈련 때는 데이터를 흘릴 때마다 삭제할 뉴런을 무작위로 선택하고, 시험 때는 모든 뉴런에 신호를 전달
    → 시험 때는 각 뉴런의 출력에 훈련 때 삭제한 비율을 곱하여 출력
    import os
    import sys
    sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
    import numpy as np
    import matplotlib.pyplot as plt
    from dataset.mnist import load_mnist
    from common.multi_layer_net_extend import MultiLayerNetExtend
    from common.trainer import Trainer
    
    from common.util import shuffle_dataset
    
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
    
    x_train, t_train = shuffle_dataset(x_train, t_train)
    
    val_rate = 0.2
    val_num = int(x_train.shape[0] * val_rate)
    print(val_num)
    # 검증 Data Set x_train shape 60000 중 20% 추출하여 12,000개 검증 Data Set 선정
    
    # x_val = x_train[:vali_num]
    
    # 오버피팅을 재현하기 위해 학습 데이터 수를 줄임
    x_train = x_train[:300]
    t_train = t_train[:300]
    
    # 드롭아웃 사용 유무와 비울 설정 ========================
    use_dropout = True  # 드롭아웃을 쓰지 않을 때는 False
    dropout_ratio = 0.2
    # ====================================================
    
    network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
                                  output_size=10, use_dropout=use_dropout, dropout_ration=dropout_ratio)
    trainer = Trainer(network, x_train, t_train, x_test, t_test,
                      epochs=301, mini_batch_size=100,
                      optimizer='sgd', optimizer_param={'lr': 0.01}, verbose=True)
    trainer.train()
    
    train_acc_list, test_acc_list = trainer.train_acc_list, trainer.test_acc_list
    
    # 그래프 그리기==========
    markers = {'train': 'o', 'test': 's'}
    x = np.arange(len(train_acc_list))
    plt.plot(x, train_acc_list, marker='o', label='train', markevery=10)
    plt.plot(x, test_acc_list, marker='s', label='test', markevery=10)
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.ylim(0, 1.0)
    plt.legend(loc='lower right')
    plt.show()​
  • 순전파 때 신호를 통과 시키는 뉴런은 역전파 때도 신호를 그대로 통과 시키고, 순전파 때 통과시키지 않은 뉴런은
    역전파 때도 신호를 차단
  • 위 코드에서 self.mask는 x와 형상이 같은 배열을 무작위로 생성하고, 그 값이 드롭아웃 비율보다 큰 원소만 true로 설정

    → 7층 네트워크로 진행 (각 층의 뉴런 수는 100개, 활성화 함수는 ReLU 사용)

# 앙상블 학습 (ensemble learning)

1. 개별적으로 학습싴니 여러 모델의 출력을 평균 내어 추론하는 방식.

2. 추론 때는 뉴런의 출력에 삭제한 비율을 곱함으로써 앙상블 학습에서 여러 모델의 평균을 내는 것과 동일

 

 

▶ 결론

 

1. 다수의 신경망이 구축이 되었을 경우, 하이퍼파라미터를 조절해 가면서 모니터링 후, 최적화

2. Dropout과 Regularization를 골고루 사용 하여, 해당 모델의 최적화 성능 찾기