About
이번 포스트에서는 Tensorflow를 이용하여 Deep Neural Networks를 구현하는 법을 간단히 알아보도록 하고, 어떻게 하면 코드 복사 붙여넣기 없이 할 수 있을까에 대해서 생각해보고 구현한 것을 공유하고자 한다.
특히 찾아보면 간단한 예제를 통해서 개념들을 설명하는 경우는 많지만 Practical한 예제를 사용한 경우는 드물어서 필자는 조금 더 Practical하게 작성하고자 노력해봤다.
다만 물론 필자도 경험이 많은 것이 아니어서, 아래의 예들이 좋은 코드 패턴은 아닐 수 있음에 양해를 구하며, 만약 더 좋은 생각이 나 궁금한 점은 댓글을 통해서 꼭 알려주시길 부탁드린다.
만약 Neural Network에 대해서 잘 모르신다면 아래 링크들을 확인하시길
1.인공신경망에 대한 이해(Part 1 - Feedforward Propagation)
2.인공신경망에 대한 이해(Part 2 - Back Propagation)
Tensorflow를 이용한 DNN 실습
연습으로 MNIST Digit 이미지를 이용하도록 하자.
코드에서는 주석을 보며 생각흐름을 따라오면 된다.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
27import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import random
from keras.utils import np_utils
mnist = tf.keras.datasets.mnist
# mnist dataset을 load한다.
(x_train, y_train),(x_test, y_test) = mnist.load_data()
# float로 변환하고 minmax 스케일링을 한다. 이는 이미지 전처리의 가장 보편적인 방법 중 하나이다.
x_train = x_train.reshape(60000, 784).astype('float32') / 255.0
x_test = x_test.reshape(10000, 784).astype('float32') / 255.0
print(x_train.shape, x_train.dtype)
# y 값을 one-hot-encoding로 변환해준다.
y_unique_num = len(np.unique(y_train))
y_train = np_utils.to_categorical(y_train, y_unique_num)
y_test = np_utils.to_categorical(y_test, y_unique_num)
y_train[:5]
# test로 이미지를 한번 출력해보자.
r = random.randint(0, x_train.shape[0] - 1)
plt.imshow(
x_train[r].reshape(28, 28),
cmap="Greys",
interpolation="nearest" # 중간에 비어있는 값 처리
)
plt.show()
먼저 클래스를 사용하지 않고 구현해보자.
아래는 Graph를 만드는 코드다.
혹시 placeholder, Variable 등 기본적인 함수에 대해서 잘 모른다면,
1 | # input data를 위한 공간(placeholder)를 만든다. |
그리고 세션을 이용해서 학습해보자.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17sess = tf.Session()
sess.run(tf.global_variables_initializer())
batch_size = 200
epoch = 100
for step in range(epoch):
total_batch = int(len(x_train)/batch_size)
c_avg = 0
for i in range(total_batch):
batch_x = x_train[batch_size*i : batch_size*(i+1)]
batch_y = y_train[batch_size*i : batch_size*(i+1)]
c, _ = sess.run([cost, train], feed_dict={X: batch_x, y: batch_y})
c_avg = c_avg + (c/total_batch)
if step % 10 == 0:
print(step, c_avg)
print(sess.run(accuracy, feed_dict={X: x_test, y: y_test}))
필자가 이 네트워크로 얻은 Accuracy값은 0.7291이었다. 그렇다면 이제 네트워크를 바꿔가며 하이퍼패러미터 튜닝을 시도해야할텐데, 그때마다 위의 Graph코드를 복사해서 붙여넣고 중간에 layer들은 변경한다거나 해야한다.
코드도 지저분해지고 자유도가 엄청 떨어지는 이 문제점을 해결하기 위해서 아래처럼 모델은 Class로 Train은 함수로 따로 구현해봤다.
코드가 많이 복잡해보이는데, 그 이유는 크게 4가지이다.
- Model은 Graph를 만드는 역할만 수행하고 Session과 결합하지 않았다.
- Model을 빌드할 때 자유롭게 미리 config에서 설정한 layer, neuron의 개수, initializer, activation 등을 적용할 수 있게 하였다.
- 각 Layer마다 사용된 variable을 가져올 수 있게 하였다.
- Tensorboard에도 기록될 수 있게 하였다.
이번 예제에서는 구현하지는 않았지만, activation이나 initializer 등을 넘어서 dropout 등도 응용해서 적용하면 된다.
그렇게되면 장점은
- 내부 layer 등을 달리한 모델 m1, m2를 객체화하고 학습은 같은 train() 함수를 이용해서 진행할 수 있어서 객체 내부에 중복된 train 함수를 들고있을 필요가 없다.
- 더 큰 네트워크를 만들기 용이하다.
이제 코드로 살펴보자.
먼저 사용법을 살펴보고 나머지들을 설명하도록 하겠다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# input 데이터가 가진 feature 개수
n_features = x_train.shape[1]
# label 개수
n_class = len(y_train[0])
# model build를 위한 config를 만든다.
config = {
"name" : "dnn_model", # 나중에 tensorboard를 확인하면 여기서 정한 이름으로 graph가 만들어진다.
"n_features" : n_features,
"n_class" : n_class,
"n_li" : [n_features, 1000, 1000, 1000, n_class], # input부터 output사이의 hidden layer neuron 개수들을 리스트형식으로 적어준다.
"initializer_li" : ["random_normal", "random_normal", "random_normal", "random_normal"], # 각 레이어마다 Variable들이 사용할 initializer를 적어준다. 코드에서는 random_normal, xavier 두 가지 경우만을 고려하였다.
"activation_li" : ["sigmoid", "sigmoid", "sigmoid", None]
# 각 레이어별로 뉴론에서 사용할 activation 함수를 적어준다. 코드에서는 sigmoid와 relu 두 가지 경우만을 고려하였다.
}
# 객체를 만들자
dnn_model = DNNModel(config)
# train함수에 만든 graph와 x_train, y_train을 넣어준다. epoch, lr, batch_size 등도 여기서 변경하며 실험해볼 수 있다.
train(dnn_model, x_train, y_train, epoch=15)
# accuracy, predict도 모델과 샘플 데이터들을 넣어주면 된다. 참고로 위에서 선언한 네트워크로 필자는 accuracy가 0.8로 나왔다.
accuracy(dnn_model, x_test, y_test), predict(dnn_model, x_test)
Model Class
1 | class DNNModel: |
Train function
1 | def train(model, X_train, y_train, lr=1e-4, epoch=15, batch_size=200): |
Predict and accuracy function
1 | def predict(model, x_test): |
마지막으로 학습한 Endpoints를 확인하고 싶다면?1
dnn_model.endpoints
참고로 아래처럼 config를 하면 accuracy는 0.9749까지 올라간다.(이 network가 최고라는 것은 결코 아니니 오해하지 마시길)1
2
3
4
5
6
7
8
9
10
11
12n_features = x_train.shape[1]
n_class = len(y_train[0])
config = {
"name" : "dnn_model",
"n_features" : n_features,
"n_class" : n_class,
"n_li" : [n_features, 1000, 1000, 1000, n_class],
"initializer_li" : ["xavier", "xavier", "xavier", "xavier"],
"activation_li" : ["relu", "relu", "relu", None]
}
dnn_model = DNNModel(config)
train(dnn_model, x_train, y_train, epoch=15)
전체 코드는 github에도 올려놨으니 필요하신분은 확인하시길..
위에서도 설명했지만 이 방법이 좋은 방법인지 필자도 알 수는 없다. 적어도 필자의 목적은 이룬 코드 패턴이어서 소개를 하였는데, 부디 도움이 되길 바란다.