NLP_자연언어처리, Colab

[NLP]합성곱 신경망_CNN : CIFAR-10

vhxpffltm 2019. 8. 5. 23:06
반응형

 

CNN은 Convolutional Neural Network의 약자로 완전연결계층과는 다르게 합성곱 계층과 풀링 계층이 추가되는 구조를 가지고 있다.

 

 

간단하게 위의 그림과 같이 나타낼 수 있다. 필자는 첫 번째 방식인 합성곱 + 풀링층 + 합성곱 ... + Fully Connected Layer의 구조를 사용한다.

 

Convolution Layer

 

합성곱에서는 3차원 데이터(1, 28, 28)를 입력하고 3차원 데이터로 출력하므로 형상을 유지할 수 있다. CNN에서는 이러한 입출력 데이터를 Feature map(특징맵)이라고 한다.

 

합성곱 계층에서의 연산은 데이터와 필터의 모양을 나타내는데 이를 윈도우(Window)라고 부른다. 입력 데이터가 (4,4), 필터가 (3,3)일 때, 필터가 Convolution Layer의 가중치에 해당한다.

연산은 필터의 윈도우를 일정한 간격으로 이동해가며 계산한다. 입력데이터와 필터간에 서로 대응하는 원소끼리 곱한 후 총합을 구하게 되는 방식이다. 

 

 

즉, 위와 같은 연산을 하게 된다. 결과의 (1,1) 값인 32를 예를 들면 필터를 좌측상단부터 시작해서 모양에 맞게 덧붙인다고 생각하여, 각 값들을 곱한 후 계산된 모든 값들을 더하게 되는 방식이다.

 

이후, 패딩(Padding)이라는 개념이 나온다. 패딩은 연산을 수행하기 전, 입력데이터 주변을 특정값으로 채워 늘린다. 사용하지 않을 경우, 데이터의 공간적 크기는 필터(Conv)를 지날 때마다 작아지게 되어 가장자리의 정보들이 사라지는 문제가 발생, 이를 보완하기 위한 기법.

 

스트라이드(Stride)는 입력데이터에 필터를 적용할 때, 이동할 간격을 조절하는 것이다. 위의 예제에서는 당연히 스트라이드가 1이다. 보통 1과 같이 작은 값에서 잘 작동하며 1일 경우 입력데이터의 공간적 크기는 폴링 계층에서만 조절하게 할 수 있다.

 

이후, 필터(Conv)를 통해, Feature map이 추출되면 활성화 함수를 적용한다. CNN에서는 ReLu함수를 사용한다. Back Propagation의 문제를 해결하기 위해서이다.

 

Pooling Layer

 

합성곱 계층이 끝나면 풀링 계층(Pooling Layer)에 대해 알아가야 한다. 풀링 계층은 합성곱 계층의 Padding과 Stride처럼 데이터의 공간적 크기를 축소하는데 사용한다. 

합성곱 계층의 출력데이터의 크기를 입력데이터의 크기 그대로 유지하고, 풀링계층에서만 그 크기를 조절한다. 보통 Max-Pooling방식을 사용한다.

 

Max-Pooling의 예는 위의 그림을 참고하면 된다.

 

SoftMax

 

Softmax를 잠깐 살펴보면 활성화 함수의 일종이며 값을 조정하기 위해 사용한다. Sigmoid, ReLU와 같은 ㅓ것이다. tanh(하이퍼볼릭 탄젠트) 등이 있으며 이들의 역할은 함수에 따라 다르지만 Sigmoid기준으로 값을 0~1로 나타낸다. 그리고 목표는 작은 값은 더 작게 만들고 큰 값은 더 크게 만들기 위해 사용한다. 이 내용은 나중에 따로 다루겠다.

 

Fully Connected network

기존의 뉴럴 네트워크에 넣어 분류를 진행한다. 이 과정에서 'Drop Out'방식을 사용한다. 이것은 '오버피팅(Overfittng)'을 방지하기 위한 기법이며 이 개념과 오버피팅 현상을 방지하는 것도 따로 정리해서 다루겠다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import tensorflow as tf
import numpy as np
 
# CIFAR-10 데이터를 다운로드 받기 위한 keras의 helper 함수인 load_data 함수를 임포트합니다.
 
 
# 다음 배치를 읽어오기 위한 next_batch 유틸리티 함수를 정의합니다.
def next_batch(num, data, labels):
    '''
    `num` 개수 만큼의 랜덤한 샘플들과 레이블들을 리턴합니다.
    '''
    idx = np.arange(0len(data))
    idx = idx[:num]
    data_shuffle = [data[i] for i in idx]
    labels_shuffle = [labels[i] for i in idx]
 
    return np.asarray(data_shuffle), np.asarray(labels_shuffle)
 
 
# CNN 모델을 정의합니다.
def build_CNN_classifier(x):
    # 입력 이미지
    x_image = x
 
    # 첫번째 convolutional layer - 하나의 grayscale 이미지를 64개의 특징들(feature)으로 맵핑(maping)합니다.
    W_conv1 = tf.Variable(tf.truncated_normal(shape=[55364], stddev=5e-2))
    b_conv1 = tf.Variable(tf.constant(0.1, shape=[64]))
    h_conv1 = tf.nn.relu(tf.nn.conv2d(x_image, W_conv1, strides=[1111], padding='SAME'+ b_conv1)
 
    # 첫번째 Pooling layer
    h_pool1 = tf.nn.max_pool(h_conv1, ksize=[1331], strides=[1221], padding='SAME')
 
    # 두번째 convolutional layer - 32개의 특징들(feature)을 64개의 특징들(feature)로 맵핑(maping)합니다.
    W_conv2 = tf.Variable(tf.truncated_normal(shape=[556464], stddev=5e-2))
    b_conv2 = tf.Variable(tf.constant(0.1, shape=[64]))
    h_conv2 = tf.nn.relu(tf.nn.conv2d(h_pool1, W_conv2, strides=[1111], padding='SAME'+ b_conv2)
 
    # 두번째 pooling layer.
    h_pool2 = tf.nn.max_pool(h_conv2, ksize=[1331], strides=[1221], padding='SAME')
 
    # 세번째 convolutional layer
    W_conv3 = tf.Variable(tf.truncated_normal(shape=[3364128], stddev=5e-2))
    b_conv3 = tf.Variable(tf.constant(0.1, shape=[128]))
    h_conv3 = tf.nn.relu(tf.nn.conv2d(h_pool2, W_conv3, strides=[1111], padding='SAME'+ b_conv3)
 
    # 네번째 convolutional layer
    W_conv4 = tf.Variable(tf.truncated_normal(shape=[33128128], stddev=5e-2))
    b_conv4 = tf.Variable(tf.constant(0.1, shape=[128]))
    h_conv4 = tf.nn.relu(tf.nn.conv2d(h_conv3, W_conv4, strides=[1111], padding='SAME'+ b_conv4)
 
    # 다섯번째 convolutional layer
    W_conv5 = tf.Variable(tf.truncated_normal(shape=[33128128], stddev=5e-2))
    b_conv5 = tf.Variable(tf.constant(0.1, shape=[128]))
    h_conv5 = tf.nn.relu(tf.nn.conv2d(h_conv4, W_conv5, strides=[1111], padding='SAME'+ b_conv5)
 
    # Fully Connected Layer 1 - 2번의 downsampling 이후에, 우리의 32x32 이미지는 8x8x128 특징맵(feature map)이 됩니다.
    # 이를 384개의 특징들로 맵핑(maping)합니다.
    W_fc1 = tf.Variable(tf.truncated_normal(shape=[8 * 8 * 128384], stddev=5e-2))
    b_fc1 = tf.Variable(tf.constant(0.1, shape=[384]))
 
    h_conv5_flat = tf.reshape(h_conv5, [-18 * 8 * 128])
 
    # Dropout - 모델의 복잡도를 컨트롤합니다. 특징들의 co-adaptation을 방지합니다.
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
    # Fully Connected Layer 2 - 384개의 특징들(feature)을 10개의 클래스-airplane, automobile, bird...-로 맵핑(maping)합니다.
    W_fc2 = tf.Variable(tf.truncated_normal(shape=[38410], stddev=5e-2))
    b_fc2 = tf.Variable(tf.constant(0.1, shape=[10]))
    logits = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
    y_pred = tf.nn.softmax(logits)
 
    return y_pred, logits
 
 
# 인풋 아웃풋 데이터, 드롭아웃 확률을 입력받기위한 플레이스홀더를 정의합니다.
= tf.placeholder(tf.float32, shape=[None, 32323])
= tf.placeholder(tf.float32, shape=[None, 10])
keep_prob = tf.placeholder(tf.float32)
 
# CIFAR-10 데이터를 다운로드하고 데이터를 불러옵니다.
(x_train, y_train), (x_test, y_test) = load_data()
# scalar 형태의 레이블(0~9)을 One-hot Encoding 형태로 변환합니다.
y_train_one_hot = tf.squeeze(tf.one_hot(y_train, 10), axis=1)
y_test_one_hot = tf.squeeze(tf.one_hot(y_test, 10), axis=1)
 
# Convolutional Neural Networks(CNN) 그래프를 생성합니다.
y_pred, logits = build_CNN_classifier(x)
 
# Cross Entropy를 비용함수(loss function)으로 정의하고, RMSPropOptimizer를 이용해서 비용 함수를 최소화합니다.
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=logits))
train_step = tf.train.RMSPropOptimizer(1e-3).minimize(loss)
 
# 정확도를 계산하는 연산을 추가합니다.
correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
 
# 세션을 열어 실제 학습을 진행합니다.
with tf.Session() as sess:
    # 모든 변수들을 초기화한다.
 
    # 10000 Step만큼 최적화를 수행합니다.
    for i in range(10000):
        batch = next_batch(128, x_train, y_train_one_hot.eval())
 
        # 100 Step마다 training 데이터셋에 대한 정확도와 loss를 출력합니다.
        if i % 100 == 0:
            train_accuracy = accuracy.eval(feed_dict={x: batch[0], y: batch[1], keep_prob: 1.0})
            loss_print = loss.eval(feed_dict={x: batch[0], y: batch[1], keep_prob: 1.0})
 
            print("반복(Epoch): %d, 트레이닝 데이터 정확도: %f, 손실 함수(loss): %f" % (i, train_accuracy, loss_print))
        # 20% 확률의 Dropout을 이용해서 학습을 진행합니다.
        sess.run(train_step, feed_dict={x: batch[0], y: batch[1], keep_prob: 0.8})
 
    # 학습이 끝나면 테스트 데이터(10000개)에 대한 정확도를 출력합니다.
    test_accuracy = 0.0
    for i in range(10):
        test_batch = next_batch(1000, x_test, y_test_one_hot.eval())
        test_accuracy = test_accuracy + accuracy.eval(feed_dict={x: test_batch[0], y: test_batch[1], keep_prob: 1.0})
    test_accuracy = test_accuracy / 10
    print("테스트 데이터 정확도: %f" % test_accuracy)
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 

CIFAR-10 실습 예제코드이다. 시간이 매우 오래 걸리기 때문에 batch사이즈를 적절히 낮추어 테스트해보길 바란다. dropout, Optimizer 등을 사용하여 결과가 어떻게 변하는지 테스트를 하면 가장 좋을 것이다.

 

필자는 step의 크기를 조절하여 테스트를 진행하였다.

 

이 결과는 스텝을 10000 / 100으로 조절하여 진행하였다. batch사이즈와 기울기함수 등을 다른방식으로 조절하여 실험을 진행하면 된다. 정확도가 너무 낮게 나왔기 때문에 다른 조정이 필요하다.

 

출처 및 Reference

 

http://solarisailab.com/archives/2325

https://excelsior-cjh.tistory.com/79?category=1013831

https://bcho.tistory.com/1149

 

반응형

'NLP_자연언어처리, Colab' 카테고리의 다른 글

[Colab] Histogram 코드  (0) 2020.04.07
[NLP] Word2Vec : CBOW, Skip-gram  (0) 2019.08.30
[NLP]의사 결정 트리  (0) 2019.08.25
[NLP]MNIST 문자 인식  (0) 2019.07.22
[자연언어처리] 다층 퍼셉트론  (0) 2019.07.09