Bagging

Bagging

본 포스팅은 고려대학교 강필성 교수님의 Business Analytics 수업의 내용과 수업자료를 참고하여 작성하였습니다.

Introduction

  • 배깅(bagging)은 앙상블 기법에서 주로 사용되는 방식으로, 분류(classification)와 회귀(regression) 문제를 다루기 위해 사용되는 기계학습 혹은 여타 다른 학습(e.g., SVM, decision tree)에서의 정확도와 안정성을 향상시키기 위해 고안된 훈련 방식이다.
  • 배깅은 데이터를 랜덤하게 선별하여 준비하는 Bootstrapping 단계와 그 선별된 데이터로 훈련한 뒤 모델의 결과를 합쳐나가는 Aggregation의 단계로 나눌 수 있다.

Algorithm Structure


[그림 1] 전체 N개의 데이터 셋을 이용하여 훈련을 진행하는 과정 중 Bootstrapping과 Aggregation이 적용되는 단계를 나타낸다. Bootstrapping은 데이터를 분할하고, Aggregation은 모델 예측값을 취합한다.

  • Bootstrapping
    • Bootstrapping은 모든 데이터 N개를 훈련에 사용하기 전, 랜덤하게 k개의 소규모 셋으로 분할하여 준비하는 작업을 의미한다. (각 훈련데이터는 input x와 output y로 한쌍을 이룬다.)

    • 예를들어 총 15개의 데이터셋(N=15)이 있다고 할 때, 이를 3개의 소규모 셋(k=3)으로 분할할 수 있으며 이 때, 각 소규모 셋에 들어가는 데이터들은 전체 데이터 셋에서 독립적으로 5개씩 랜덤하게 추출하게 된다. 따라서 각기 다른 소규모 셋에 중복된 데이터 셋이 존재할 수도 있으며 전체 소규모 셋에 한번도 등장하지 않는 데이터 셋도 존재할 수 있다. 다음의 애니메이션을 통해 bootstrapping 과정을 살펴볼 수 있다.


      [그림 2] 총 15개의 데이터를 이용하여 bootstrapping하는 과정을 보여준다.

  • Aggregation
    • Aggregation은 k개의 bootstrapping에 담긴 데이터를 이용하여 훈련을 진행한 뒤 얻어진 k개의 모델들의 결과를 합치는 것을 의미한다. k개의 각기 다른 모델들의 결과값을 합치는 방법은 다음과 같이 다양하게 제시되고 있다.

      enter image description here
      [표 1] 총 5개의 bootstrapping을 진행한 뒤, 훈련된 모델의 결과를 나타내는 한 가지 예시 표이다. "Train Accuracy"는 각 모델별 예측 정확도를 나타낸다. "Probability(Y=1) for test data"는 각 모델에 테스트 데이터를 입력하였을 때 1이라는 결과값을 도출하는 확률값을 의미하며 이 값이 0.5보다 높을 경우에는 1로 클레스 레이블이 정의되며 0.5보다 낮을 경우에는 0으로 정의된다.

    1. Majority Voting

      • MV(Majority Voting) 방식은 대다수의 의견을 따른다는 의미로 훈련된 모든 모델을 통해 얻은 클레스 레이블의 수를 이용해서 최종 결과를 도출하는 방식이다. 표1의 예시를 볼 때, 총 5가지의 훈련된 모델을 통해 얻은 레이블의 개수는 1의 경우는 3개, 0의 경우는 2개이므로 최종 결과는 개수가 1개 더 많은 1 레이블이 된다. 이를 공식으로 표현하면 다음과 같다.

        y^Ensemble=argmaxi(j=1nδ(yj^=i),i{0,1}) \hat{y}Ensemble = arg \max\limits_i (\displaystyle\sum_{j=1}^n\delta(\hat{y_j} = i), i \in \{0,1 \})
    2. Weighted Voting

      • WV(Weighted Voting) 방식은 클레스 레이블 정보를 직접 사용하지 않고 이전에 얻은 확률값을 이용하여 최종 결과를 도출하는데 이는 2가지 방식으로 나뉜다.
        1. Train Accuracy를 조정하는 방법
          • 중간고사가 끝나고 학생들이 국어 시험지의 정답을 임의로 맞춰보는 상황을 생각해보자. 그리고 학생들 사이에 1번 문제에 대한 답이 3번과 4번으로 대립된다고 하자. 이때 전교 1,2등 두 명이 1번 문제에 대한 답이 3번이라고 주장하고, 전교 500, 501, 502등 세 명이 4번이라고 주장한다면 학생들은 과연 누구의 답을 정답으로 가정하고 자신의 시험지를 채점하게 될까?
          • 위에서 언급한 MV 방식에 따르면 학생들은 1번 문제에 대한 답을 4번으로 가정하고 채점을 해야겠지만 WV 방식을 따른다면 전교 1,2등의 답에 상대적으로 큰 가중치를 줘서 1번 문제에 대한 답을 3번으로 가정하고 채점하게 될 것이다.
          • 이처럼 전교 1,2등 학생들과 501,502,503등 학생들의 정답에 대한 가중치를 학생들의 전교 등수(train accuracy)에 기반하여 판단하는 것이 WV의 한 가지 방식이며 이는 다음과 같은 공식으로 표현된다.

            y^Ensemble=argmaxi(j=1n(TrnAccj)δ(yj^=i)j=1n(TrnAccj),i{0,1}) \hat{y}Ensemble = arg \max\limits_i (\frac{\textstyle\sum_{j=1}^n(TrnAcc_j) \sdot \delta(\hat{y_j}=i)}{\textstyle\sum_{j=1}^n(TrnAcc_j)}, i \in \{0,1 \})
        2. 평균으로 조정하는 방법.
          • 클레스 레이블은 특정한 확률값에 기반하여 도출된다. 이 때 그 확률값의 정보를 더 자세하게 사용하여 최종 결과에 반영하는 것이 WV의 평균으로 조정하는 방식이다.
          • 표1의 “Probability(Y=1) for test data” 정보를 참고해 보자. 클레스 레이블은 1과 0으로 나눠져있고 여기서 첫번째 열의 1은 0.95라는 높은 확률값에 의해 도출된 것을 알 수 있지만 3번째 열의 1은 0.76이라는 다소 낮은 확률값에 의해 도출된 것을 알 수 있다. 이처럼 둘 다 1이라는 레이블을 갖지만 그 레이블을 도출하기 위해 사용된 확률값의 차이가 존재하므로 이 차이를 반영하고자 전체 클레스 레이블 개수로 평균화 해주는 것이 바로 평균으로 조정하는 방법이다. 이 방법은 다음과 같은 공식으로 표현된다.

            y^Ensemble=argmaxi(1nj=1nP(y=i),i{0,1}) \hat{y}Ensemble = arg \max\limits_i (\frac{1}{n}\displaystyle\sum_{j=1}^nP(y = i), i \in \{0,1 \})
    3. Stacking

      • Stacking은 새로운 classifier를 훈련하여 최종 결과를 도출하는 방법이다. 기존의 방식은 여러 모델로부터 얻은 결과값을 그대로 이용하여 최종 결과값을 도출하였지만, 본 방식은 모델로부터 얻은 결과값 위에 새로운 모델을 쌓아서(stacking) 그 모델로부터 최종 결과값을 직접 얻어낸다. 진행 과정은 그림3을 참조하길 바란다.

        enter image description here
        [그림 3] Stacking에 사용되는 Meta Classifier는 훈련된 각 모델들의 결과값을 인풋값으로 하고, 정답지를 아웃풋으로 하여 훈련을 한다. 그리고 훈련이 끝나면 최종 예측을 위해 Aggregation 단계에 사용한다.

Code Structure

  • 본 스크립트는 bagging에 대한 전반적인 훈련 흐름과정을 나타내며 실제 훈련을 돌려 성과를 내는 용도로 작성되지 않았다. 그리고 python 3과 tensorflow cpu버전을 기반으로 작성되었다.

  • 준비 작업

# 기본적으로 불러야하는 모듈은 다음과 같습니다.
import random  
import tensorflow as tf  
from tensorflow.examples.tutorials.mnist import input_data  
  
  
# 파라미터들을 지정합니다. hidden layer는 총 2개를 사용할 예정이며 각 레이어는 
# 256개의 hidden nodes를 가집니다.
learning_rate = 0.1  
hidden_units = 256  
batch_size = 128  
display = 10  
epoch = 100  
# bootstrapping에 사용할 파라미터로 전체 데이터를 총 10개의 bootstrap에 나눠 담습니다. 
k_idx = 10  
  • 훈련 데이터 (MNIST)
# 훈련에 사용할 MNIST 데이터를 다운받습니다.
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)  
total_data_num = mnist.train.images.shape[0]  
input_dim = mnist.train.images.shape[1]  
output_dim = mnist.train.labels.shape[1]  
  • Bootstrapping 단계
# Bootstrapping: 랜덤하게 데이터를 샘플링합니다.  
train_input = []  
train_output = []  
sub_data_size = int(total_data_num/k_idx)  
for sub_k in range(k_idx):  
    rand_sample_idx = random.sample(range(total_data_num),sub_data_size)  
    train_input.append(mnist.train.images[rand_sample_idx,:])  
    train_output.append(mnist.train.labels[rand_sample_idx,:])  
  • 네트워크 생성
# 훈련 데이터의 인풋과 아웃풋에 대한 placeholder를 생성합니다.  
X = tf.placeholder(tf.float32,[None, input_dim])  
Y =tf.placeholder(tf.float32,[None, output_dim])  
  
# neural networks를 생성합니다.  
def generate_NN(input):    
    # 웨이트를 생성합니다.
    w1 = tf.Variable(tf.random_normal([input_dim, hidden_units]))  
    w2 = tf.Variable(tf.random_normal([hidden_units, hidden_units]))  
    w3 = tf.Variable(tf.random_normal([hidden_units, output_dim]))  
  
    # 바이어스를 생성합니다.  
    b1 = tf.Variable(tf.random_normal([hidden_units]))  
    b2 = tf.Variable(tf.random_normal([hidden_units]))  
    b3 = tf.Variable(tf.random_normal([output_dim]))  
  
    # 히든 레이어를 생성합니다. 본 모델에서는 총 2개의 hidden layer를 생성합니다.
    hidden_1 = tf.add(tf.matmul(X, w1), b1)  
    hidden_2 = tf.add(tf.matmul(hidden_1, w2), b2)  
    last_layer = tf.add(tf.matmul(hidden_2, w3), b3)  
  
    return last_layer  
  
out_layer = generate_NN(X)  
  
# hidden layer를 통과한 값에 대해 예측값을 구하고 이를 기반으로 에러를 계산합니다.  
def get_accuracy(last_layer, output, learning_rate):  
    logits = last_layer  
    pred = tf.nn.softmax(logits)  
  
    # loss와 optimizer를 생성합니다.
    loss_opt = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=output))  
    optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss_opt)  
  
    # 모델을 평가합니다.  
    corrected_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(output, 1))  
    accuracy = tf.reduce_mean(tf.cost(corrected_pred, tf.float32))  
  
    return loss_opt, optimizer, accuracy  
  
loss_opt, optimizer, accuracy = get_accuracy(out_layer, Y, learning_rate)  
  • 네트워크 훈련
# 준비된 네트워크를 실행합니다.  
init = tf.global_variables_initializer()  
  
# 훈련 시작.
# 본 코드에서는 첫번째 모델에 대해 훈련하는 과정을 보여주며 나머지 모델에 대해서는 훈련을 생략합니다.   
with tf.Session() as sess:  
  
    # 생성자를 실행합니다.
    sess.run(init)  
    s_idx = 0  
    e_idx = batch_size  
    for step in range(1, epoch+1):  
  
        batch_x, batch_y = zip(train_input[s_idx:e_idx], train_output[s_idx:e_idx])  
        s_idx += batch_size  
        e_idx += batch_size  
  
        # 네트워크의 메인 훈련이 시작됩니다.  
  sess.run(optimizer, feed_dict={X: batch_x, Y: batch_y})  
        if step % display == 0 or step == 1:  
            # loss와 accuracy를 계산합니다.  
  loss, acc = sess.run([loss_opt, accuracy], feed_dict={X: batch_x,  
  Y: batch_y})  
            print("Step " + str(step) + ", Minibatch Loss= " + \  
                  "{:.4f}".format(loss) + ", Training Accuracy= " + \  
                  "{:.3f}".format(acc))  
  
    print("Training finished.")  
  
    # MNIST 테스트 이미지를 훈련된 모델에 넣어 결과를 얻습니다.
  print("Testing Accuracy:", \  
        sess.run(accuracy, feed_dict={X: mnist.test.images,  
  Y: mnist.test.labels}))  
  • Aggregation 단계
# k_idx(=10)만큼의 aggregation을 진행하였다고 가정하면 총 10개의 모델이 생성됩니다.   
# 단순한 클레스 예측 테스크의 경우 majority voting 방법을 이용하여 각 모델이 예측한 
# 클레스 레이블을 추합하여 최종 결과물을 도출합니다.
# 총 10개 모델은 각각 model_1, model_2, ... , model_10으로 명명합니다.  
model_list = [model_1, model_2, model_3, model_4, model_5, model_6, model_7, model_8, mode_9, model_10]  
vote = []  
for m in model_list:  
    vote.append(m.prediction_on_test_data)  
final_pred = max(set(vote),key=vote.count)  
print("final prediciton is {}".format(final_pred))

Reference

댓글

이 블로그의 인기 게시물

Kaldi Tutorial for Korean Model Part 1

Korean Forced Aligner: 한국어 자동강제정렬

Kaldi Tutorial for Korean Model Part 4