본문 바로가기
DeepLearning Framework & Coding/Tensorflow 1.X

[코드로 이해하는 딥러닝 17] - Ensemble with CNN

by 노마드공학자 2021. 1. 3.

[코드로 이해하는 딥러닝 0] - 글연재에 앞서 https://limitsinx.tistory.com/27

[코드로 이해하는 딥러닝 1] - Tensorflow 시작 https://limitsinx.tistory.com/28 

[코드로 이해하는 딥러닝 2] - Tensorflow 변수선언 https://limitsinx.tistory.com/29

[코드로 이해하는 딥러닝 3] - Tensorflow placeholder변수 https://limitsinx.tistory.com/30

[코드로 이해하는 딥러닝 4] - 선형회귀(Linear Regression) https://limitsinx.tistory.com/31

[코드로 이해하는 딥러닝 5] - 다중선형회귀(Multiple Linear Regression) https://limitsinx.tistory.com/32

[코드로 이해하는 딥러닝 6] - 회귀(Regression)에 대한 다른 접근 https://limitsinx.tistory.com/33

[코드로 이해하는 딥러닝 7] - .txt(.csv)파일 불러오기 https://limitsinx.tistory.com/34

[코드로 이해하는 딥러닝 8] - Logistic Regression(sigmoid) https://limitsinx.tistory.com/35

[코드로 이해하는 딥러닝 9] - Softmax Regression(multiple classification) https://limitsinx.tistory.com/36

[코드로 이해하는 딥러닝 10] - MNIST 데이터 분류/One hot encoding https://limitsinx.tistory.com/37

[코드로 이해하는 딥러닝 11] - Deep Neural Network/XOR https://limitsinx.tistory.com/38

[코드로 이해하는 딥러닝 11-EX] - MNIST를 DNN으로 학습해보기/Adam optimizer https://limitsinx.tistory.com/39

[코드로 이해하는 딥러닝 12] - RELU(Rectified Linear Unit) https://limitsinx.tistory.com/40

[코드로 이해하는 딥러닝 13] - .txt(.csv)파일로 저장하기 https://limitsinx.tistory.com/44

[코드로 이해하는 딥러닝 14] - Drop out https://limitsinx.tistory.com/45

[코드로 이해하는 딥러닝 15] - 초기화(Initialization)의 중요성 https://limitsinx.tistory.com/46

[코드로 이해하는 딥러닝 16] - CNN(Convolutional Neural Network) https://limitsinx.tistory.com/47

 

※이 전글에서 정리한 코드/문법은 재설명하지 않으므로, 참고부탁드립니다

※해당 글은 PC에서 보기에 최적화 되어있습니다.

 

 

출처 : https://untitledtblog.tistory.com/156

 

출처 : https://www.kdnuggets.com/2020/03/making-sense-ensemble-learning-techniques.html

 

이번에는 Ensemble이라는 기법에 대해 정리를 해보겠습니다.

 

Ensemble(앙상블)이란, 위의 이미지 한장으로 모든것이 설명됩니다.

 

이제까지는 N장의 이미지들을 CNN이든 DNN이든 한가지 모델링으로 학습하여 결과를 예측했었습니다.

 

여기서 앙상블은, 여러개의 모델들로(혹은 동일한 모델로) 학습을해서 가장 나은것을 쓰자! 

 

가 핵심입니다.

 

즉, 같은 모델이라도 초기화/Learning-rate등... 많은 변수들에 의해 다른 결과값과 Accuracy가 나오는데요

 

 

"여러개의 모델들을 비교해보고 가장 나은것을 선택해보자"

 

 

라는것입니다.

 

여기서 여러개의 모델들 중 어떤것을 쓸래? 에서도 기술들이 상당히 나뉩니다.

 

민주주의 선거처럼 가장 정확도가 높은것을 1개 골라쓰는 기술도있고, 여러모델에서 일부분씩 십시일반하여 섞어서 결과값이 나오는 기술도 있는데요

 

Ensemble의 목표는 "과적합 최소화"입니다. 여러개의 모델들에서 얻은 데이터로 내가 취사선택하여 사용한다면 

 

아무래도 한개의 모델에서 나온 결과만을 적용할때보다는 유연성이 생길것입니다.

 

하지만, 당연하게도 여러모델을 한번에 돌리기에 연산량이 엄청납니다..

 

제가 쓰고 있는 Laptop의 CPU가 구리긴 하지만(intel 5500U 저전력), 모델 2개만 돌려도 MNIST기준으로 한시간은 걸리는듯합니다.. 회사에 Xeon CPU로도 한번 테스트를 해봐야겠네요..

 

 

[코드 전문]

import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()

import numpy as np

import matplotlib.pyplot as plt

import random

from tensorflow.examples.tutorials.mnist import input_data

 

tf.set_random_seed(777)  # reproducibility

 

mnist = input_data.read_data_sets("MNIST_data/"one_hot=True)

 

# hyper parameters

learning_rate = 0.001

training_epochs = 20

batch_size = 100



class Model:

 

    def __init__(selfsessname):

        self.sess = sess

        self.name = name

        self._build_net()

 

    def _build_net(self):

        with tf.variable_scope(self.name):

            # dropout (keep_prob) rate  0.7~0.5 on training, but should be 1

            # for testing

            self.training = tf.placeholder(tf.bool)

 

            # input place holders

            self.X = tf.placeholder(tf.float32, [None784])

 

            # img 28x28x1 (black/white), Input Layer

            X_img = tf.reshape(self.X, [-128281])

            self.Y = tf.placeholder(tf.float32, [None10])

 

            # Convolutional Layer #1

            conv1 = tf.layers.conv2d(inputs=X_img, filters=32kernel_size=[33],

                                     padding="SAME"activation=tf.nn.relu)

            # Pooling Layer #1

            pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[22],

                                            padding="SAME"strides=2)

            dropout1 = tf.layers.dropout(inputs=pool1,

                                         rate=0.3training=self.training)

 

            # Convolutional Layer #2 and Pooling Layer #2

            conv2 = tf.layers.conv2d(inputs=dropout1, filters=64kernel_size=[33],

                                     padding="SAME"activation=tf.nn.relu)

            pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[22],

                                            padding="SAME"strides=2)

            dropout2 = tf.layers.dropout(inputs=pool2,

                                         rate=0.3training=self.training)

 

            # Convolutional Layer #3 and Pooling Layer #3

            conv3 = tf.layers.conv2d(inputs=dropout2, filters=128kernel_size=[33],

                                     padding="SAME"activation=tf.nn.relu)

            pool3 = tf.layers.max_pooling2d(inputs=conv3, pool_size=[22],

                                            padding="SAME"strides=2)

            dropout3 = tf.layers.dropout(inputs=pool3,

                                         rate=0.3training=self.training)

 

            # Dense Layer with Relu

            flat = tf.reshape(dropout3, [-1128 * 4 * 4])

            dense4 = tf.layers.dense(inputs=flat,

                                     units=625activation=tf.nn.relu)

            dropout4 = tf.layers.dropout(inputs=dense4,

                                         rate=0.5training=self.training)

 

            # Logits (no activation) Layer: L5 Final FC 625 inputs -> 10 outputs

            self.logits = tf.layers.dense(inputs=dropout4, units=10)

 

        # define cost/loss & optimizer

        self.cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(

            logits=self.logits, labels=self.Y))

        self.optimizer = tf.train.AdamOptimizer(

            learning_rate=learning_rate).minimize(self.cost)

 

        correct_prediction = tf.equal(

            tf.argmax(self.logits, 1), tf.argmax(self.Y, 1))

        self.accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

 

    def predict(selfx_testtraining=False):

        return self.sess.run(self.logits,

                             feed_dict={self.X: x_test, self.training: training})

 

    def get_accuracy(selfx_testy_testtraining=False):

        return self.sess.run(self.accuracy,

                             feed_dict={self.X: x_test,

                                        self.Y: y_test, self.training: training})

 

    def train(selfx_datay_datatraining=True):

        return self.sess.run([self.cost, self.optimizer], feed_dict={

            self.X: x_data, self.Y: y_data, self.training: training})

 

# initialize

sess = tf.Session()

 

models = []

num_models = 2

for m in range(num_models):

    models.append(Model(sess, "model" + str(m)))

 

sess.run(tf.global_variables_initializer())

 

print('Learning Started!')

 

# train my model

for epoch in range(training_epochs):

    avg_cost_list = np.zeros(len(models))

    total_batch = int(mnist.train.num_examples / batch_size)

    for i in range(total_batch):

        batch_xs, batch_ys = mnist.train.next_batch(batch_size)

 

        # train each model

        for m_idx, m in enumerate(models):

            c, _ = m.train(batch_xs, batch_ys)

            avg_cost_list[m_idx] += c / total_batch

 

    print('Epoch:''%04d' % (epoch + 1), 'cost =', avg_cost_list)

 

print('Learning Finished!')

 

# Test model and check accuracy

test_size = len(mnist.test.labels)

predictions = np.zeros([test_size, 10])

for m_idx, m in enumerate(models):

    print(m_idx, 'Accuracy:', m.get_accuracy(

        mnist.test.images, mnist.test.labels))

    p = m.predict(mnist.test.images)

    predictions += p

 

ensemble_correct_prediction = tf.equal(

    tf.argmax(predictions, 1), tf.argmax(mnist.test.labels, 1))

ensemble_accuracy = tf.reduce_mean(

    tf.cast(ensemble_correct_prediction, tf.float32))

print('Ensemble accuracy:', sess.run(ensemble_accuracy))

 

 

[코드 분석-1]

Ensemble을 하기위해선, CNN을 한줄로 쭉 코딩해주는것이 아니라, Class로 묶어서 함수를 저장해두어야 호출하기에 편합니다.

※ C++ Class : 한 묶음 안에 함수/변수들을 모두 통틀어서 저장하여, 클래스만 불러오면 내부 코드가 돌도록 하는것

 

Model이라는 Class 이름으로, 초기화를 해주고(def __init__), CNN Frame을 구축(_build_net)하는 코드입니다.

 

Class가 호출되면 바로 돌수있도록 변수나,함수들에 "self"를 붙여준것 말고는 기존 CNN코드와 차이가 없습니다.

 

 

[코드 분석-2]

3개의 Layer를 통과시키며, 이제까지 배운것들을 총 망라하여,

Zero padding, max pooling, Dropout, ReLU 까지 모두 진행하는 코드입니다.

 

 

[코드 분석-3]

마지막 Layer는 학습을 할 수 있도록 다시 벡터형식으로 값들을 바꿔주는것을 의미합니다.

 

self.logits 함수를 통해 CNN 모델에서는 최종적으로 625개의 input이 들어왔지만, 10개만의 output을 내보내고, 이후 코드는 이전과 동일하게 학습을 진행합니다.

 

 

[코드 분석-4]

학습을 완료하고 난뒤, 모델 갯수와 몇개를 앙상블 할건지 지정해 줘야하는데요

num_models가 바로 그부분입니다. 저는 CPU가 구려서 2개만 해도 엄청나게 시간이 걸려서... 이렇게만 돌려보았습니다.

 

 

[코드 분석-5]

최종적으로 Ensemble_accuracy를 위의 코드와 같이 구현해주면, 결과값이 나오는데요

 

CNN 단일모델만써도 이미 99%의 성능이 나오므로, 크게 괄목할만한 효과를 보여주진 않지만,

 

조금더 난이도가 높고 이미지수가 많으며, Classification수가 많은 복잡한 모델에서는 눈에띄는 성능향상을 얻을 수 있습니다.

 

댓글