신경망 모델 코드로 구현하기
- 딥러닝 모델 구현 시, 1개의 층을 코드로 작성하면 다음과 같다.
W = tf.Variable(tf.truncated_normal([n_in, n_hidden], stddev=0.01))
b = tf.Variable(tf.zeros([n_hidden]))
h = tf.nn.relu(tf.matmul(x, W) + b)
h_drop = tf.nn.dropout(h, keep_prob)
model = Sequential()
model.add(Dense(input_dim=n_in, units=n_hidden))
model.add(Activation('relu'))
model.add(Dropout(0.5))
- 딥러닝 모델에서 층을 늘려갈 떄에는 위의 코드를 반복적으로 사용하여 구현하였다.
효율적으로 신경망 모델 구현하기
- 층의 개수를 더 늘리고 싶거나 모델의 일부분을 변경(예를 들어 활성화 함수를 다른 것으로 변경)하고 싶을 때 유연하게 적용할 수 있도록 신경망 모델을 설계해보자
TensorFlow 설계 방법
- 신경망 모델을 정의하는 전체적인 과정은 그림 10.1과 같다.

그림 10.1 드롭아웃이 적용된 신경망의 예
- TensorFlow에서는 위의 과정을 단계별로 함수화할 것을 권장하고 있으며 각각 inference(), loss(), training() 함수로 정의해 사용해야 한다.
- inference() : 모델 전체를 설정하고 모델의 출력(예측 결과)를 반환하는 함수
- loss() : 모델의 오차 함수를 정의하고 오차를 반환하는 함수
- training() : 모델을 학습시키고 학습시킨 결과(진행 상황)을 반환하는 함수
def inference(x):
# 모델을 정의
def loss(hypothesis, y):
# 오차함수를 정의
def training(loss):
# 학습 알고리즘을 정의
if __name__ == '__main__':
# 1. 데이터를 준비한다
# 2. 모델을 설정한다
hypothesis = inference(x)
loss = loss(hypothesis, y)
# 3. 모델을 학습시킨다
train = training(loss)
# 4. 모델을 평가한다
- hypohesis = inference(x) 함수 설계
- 지금까지 각 층을 h0, h1 등으로 함수를 정의했지만 이제는 함수들을 한꺼번에 정의하기 위해 각 층에 있는 뉴런의 개수를 인수로 받게 만들어야 한다.
- 드롭아웃을 지원하기 위해 x 이외에 keep_prob 변수를 인수로 받아야 하기 때문에 다음과 같이 코드를 작성한다.
def inference(x, keep_prob, n_in, n_hiddens, n_out):
# 모델을 정의
if __name__ == '__main__':
# 1. 데이터를 준비한다
MNIST = datasets.fetch_mldata('MNIST original', data_home='.')
n = len(MNIST.data)
N = int(n * 1.00) # 70,000개의 데이터를 모두 사용
N_train = int(n * 0.2)
N_val = int(N_train * 0.2)
# 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
indices = np.random.permutation(range(n))[:N]
X = MNIST.data[indices]
Y = MNIST.target[indices]
Y = np.eye(10)[Y.astype(int)] # 1-of-K hot coding 표현으로 변환
# 학습 데이터와 테스트 데이터를 80:20으로 나눈다
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=N_val)
# 2. 모델을 설정한다
# 입출력 데이터와 은닉층의 뉴런 개수 설정
n_in = len(X[0]) # 입력 데이터 이미지의 크기 28x28(=784)
n_hiddens = [200, 200, 200] # 각 은닉층의 뉴런 개수
n_out = len(Y[0]) # 출력 데이터의 개수 (0~9)
p_keep = 0.5 # 드롭아웃 확률의 비율
x = tf.placeholder(tf.float32, shape=[None, n_in])
y = tf.placeholder(tf.float32, shape=[None, n_out])
keep_prob = tf.placeholder(tf.float32)
# 가설식을 그래프에 추가
hypothesis = inference(x, keep_prob, n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)
- 입력층에서 출력층의 바로 앞 층까지는 모든 출력을 같은 식으로 나타낼 수 있지만 출력층만은 활성화 함수가 소프트맥스 함수(또는 시그모이드 함수)로 지정돼 있어 드음과 같이 코드를 작성한다.
- weight_variable()과 bias_variable()을 정의해, 웨이트와 바이어스를 초기화 처리를 한다.
- for 문의 내부에서 '입력층 - 은닉층'과 '은닉층-은닉층'은 각각 처리가 다르지만 기본적으로 이전 층의 output이 다음 층의 input이 되는 구조를 구현
def inference(x, keep_prob, n_in, n_hiddens, n_out):
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.01)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.zeros(shape)
return tf.Variable(initial)
# 입력층-은닉층과 은닉층-은닉층 설계
for i, n_hidden in enumerate(n_hiddens):
if i == 0: # 입력층-은닉층
input = x
input_dim = n_in
else: # 은닉층-은닉층
input = output # 출력층이 다음 입력층이 된다
input_dim = n_hiddens[i-1]
W = weight_variable([input_dim, n_hidden]) # weight의 shape은 입력층 차원(784) x 뉴런의 개수
b = bias_variable([n_hidden]) # bias의 shape은 뉴런의 개수
hidden_layer = tf.nn.relu(tf.matmul(input, W) + b) # 은닉층의 활성화 함수로 ReLU를 사용
output = tf.nn.dropout(hidden_layer, keep_prob=keep_prob) # 출력은 드롭아웃을 적용
# 은닉층-출력층 설계
W_out = weight_variable([n_hiddens[-1], n_out]) # 출력층의 weight shape은 뉴런의 개수 x 출력층의 차원(10개)
b_out = bias_variable([n_out]) # bias의 shape은 출력층의 차원
hypothesis = tf.nn.softmax(tf.matmul(output, W_out) + b_out) # 출력 활성화 함수는 softmax를 사용
return hypothesis
- loss(hypothesis, y) 함수와 training(loss) 함수 설계
def loss(hypothesis, y):
cross_entropy = tf.reduce_sum(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))
return cross_entropy
def training(loss):
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train = optimizer.minimize(loss)
return train
- 모델 평가를 위한 accuracy(hypothesis, y) 함수 설계
def accuracy(hypothesis, y):
correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
return accuracy
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
import numpy as np
import tensorflow as tf
def inference(x, keep_prob, n_in, n_hiddens, n_out):
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.01)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.zeros(shape)
return tf.Variable(initial)
# 입력층-은닉층과 은닉층-은닉층 설계
for i, n_hidden in enumerate(n_hiddens):
if i == 0: # 입력층-은닉층
input = x
input_dim = n_in
else: # 은닉층-은닉층
input = output # 출력층이 다음 입력층이 된다
input_dim = n_hiddens[i-1]
W = weight_variable([input_dim, n_hidden]) # weight의 shape은 입력층 차원(784) x 뉴런의 개수
b = bias_variable([n_hidden]) # bias의 shape은 뉴런의 개수
hidden_layer = tf.nn.relu(tf.matmul(input, W) + b) # 은닉층의 활성화 함수로 ReLU를 사용
output = tf.nn.dropout(hidden_layer, keep_prob=keep_prob) # 출력은 드롭아웃을 적용
# 은닉층-출력층 설계
W_out = weight_variable([n_hiddens[-1], n_out]) # 출력층의 weight shape은 뉴런의 개수 x 출력층의 차원(10개)
b_out = bias_variable([n_out]) # bias의 shape은 출력층의 차원
hypothesis = tf.nn.softmax(tf.matmul(output, W_out) + b_out) # 출력 활성화 함수는 softmax를 사용
return hypothesis
def loss(hypothesis, y):
# cross entropy
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))
return cross_entropy
def train(loss):
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train = optimizer.minimize(loss)
return train
def accuracy(hypothesis, y):
correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
return accuracy
if __name__ == '__main__':
# 1. 데이터를 준비한다
MNIST = datasets.fetch_mldata('MNIST original', data_home='.')
n = len(MNIST.data)
N = int(n * 1.00) # 70,000개의 데이터를 모두 사용
N_train = int(n * 0.2)
N_val = int(N_train * 0.2)
# 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
indices = np.random.permutation(range(n))[:N]
X = MNIST.data[indices]
Y = MNIST.target[indices]
Y = np.eye(10)[Y.astype(int)] # 1-of-K hot coding 표현으로 변환
# 학습 데이터와 테스트 데이터를 80:20으로 나눈다
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=N_val)
# 2. 모델을 설정한다
# 입출력 데이터와 은닉층의 뉴런 개수 설정
n_in = len(X[0]) # 입력 데이터 이미지의 크기 28x28(=784)
n_hiddens = [200, 200, 200] # 각 은닉층의 뉴런 개수
n_out = len(Y[0]) # 출력 데이터의 개수 (0~9)
p_keep = 0.5 # 드롭아웃 확률의 비율
x = tf.placeholder(tf.float32, shape=[None, n_in])
y = tf.placeholder(tf.float32, shape=[None, n_out])
keep_prob = tf.placeholder(tf.float32)
# 가설식을 그래프에 추가
hypothesis = inference(x, keep_prob, n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)
# 오차 함수를 그래프에 추가
loss = loss(hypothesis, y)
# 학습 함수를 그래프에 추가
train = train(loss)
# 정확도 예측 함수 그래프에 추가
accuracy = accuracy(hypothesis, y)
# 3. 모델을 학습시키면서 그래프를 실행
epochs = 50
batch_size = 200
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
n_batches = N_train // batch_size
for epoch in range(epochs):
X_, Y_ = shuffle(X_train, Y_train)
for i in range(n_batches):
start = i * batch_size
end = start + batch_size
sess.run(train, feed_dict={x: X_[start: end], y: Y_[start: end], keep_prob: p_keep})
accuracy_rate = accuracy.eval(session=sess, feed_dict={x: X_val, y: Y_val, keep_prob: p_keep})
print(f'Validation accuracy rate per epoch: {accuracy_rate}')
# 4. 모델을 평가한다
accuracy_rate = accuracy.eval(session=sess, feed_dict={x: X_test, y: Y_test, keep_prob: 1.0})
print(f'Test Accuracy : {accuracy_rate}')
Keras 설계 방법
- Keras는 단순하게 구현하려고 사용하는 것이기 때문에 TensorFlow에서 했던 것처럼 구현 방침같은 것이 정해져 있지 않다.
- Keras에서는 각 층을 한꺼번에 정의할 수 있기 때문에 모델을 설정하는 전체적인 코드는 다음과 같다.
- for 문의 내부에서 ([n_in] + n_hiddens)[:-1]을 반복하여 출력층 바로 앞까지의 각 층의 입력과 출력의 차원을 Dense()에 넘겨준다.
- '입력층 - 은닉층'과 '은닉층-은닉층'은 각각 처리가 다르지만 기본적으로 이전 층의 output이 다음 층의 input이 되는 구조를 구현
from sklearn import datasets
from sklearn.model_selection import train_test_split
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.optimizers import SGD
MNIST = datasets.fetch_mldata('MNIST original', data_home='.')
n = len(MNIST.data)
N = int(n * 1.00) # 70,000개의 데이터를 모두 사용
N_train = int(n * 0.2)
N_val = int(N_train * 0.2)
# 학습 데이터와 테스트 데이터를 나누기 전에 입력 데이터를 무작위로 섞는다
indices = np.random.permutation(range(n))[:N]
X = MNIST.data[indices]
Y = MNIST.target[indices]
Y = np.eye(10)[Y.astype(int)] # 1-of-K hot coding 표현으로 변환
# 학습 데이터와 테스트 데이터를 80:20으로 나눈다
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)
n_in = len(X[0]) # 입력 데이터 이미지의 크기 28x28(=784)
n_hiddens = [200, 200] # 각 은닉층의 뉴런 개수
n_out = len(Y[0]) # 출력 데이터의 개수 (0~9)
activation = 'relu'
p_keep = 0.5 # 드롭아웃 확률의 비율
model = Sequential()
for i, input_dim in enumerate(([n_in] + n_hiddens)[:-1]):
model.add(Dense(input_dim=input_dim, units=n_hiddens[i]))
model.add(Activation(activation))
model.add(Dropout(p_keep))
model.add(Dense(units=n_out))
model.add(Activation(activation))
model.compile(loss='categorical_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy'])
epochs = 50
batch_size = 200
model.fit(X_train, Y_train, epochs=epochs, batch_size=batch_size)
loss_and_metrics = model.evaluate(X_test, Y_test)
print(f'\nLoss : {loss_and_metrics[0]:6}')
print(f'Accuracy : {loss_and_metrics[1]*100:.7}%')
TensorFlow에서 신경망 모델을 클래스로 설계하기
- TensorFlow에서는 각각의 과정을 inference(), loss(), training()이라는 함수 3개로 분리했기 떄문에 구현하기가 쉬워졌다.
- 모델을 실제로 학습을 시키는 부분은 이 3개의 함수에 포함되어 있지 않기 때문에 main 함수 부분에 코드로 작성할 내용이 많아질 수 있기 때문에 학습시키는 부분도 포함해 모든 것을 하나의 클래스로 정리해놓으면 아래 코드처럼 Keras와 비슷하게 코딩을 할 수 있다.
model = DNN()
model.fit(x_train, y_train)
model.evaluate(x_test, y_test)
- 클래스를 정의하는 전체적인 코드는 다음과 같다.
class DNN(objec):
def __init__(self):
# 초기화 처리
def weight_variable(self, shape):
initial = tf.truncated_normal(shape, stddev=0.01)
return tf.Variable(initial)
def bias_variable(self, shape):
initial = tf.zeros(shape)
return tf.Variable(initial)
def inference(self, x, keep_prob):
# 모델을 정의한다
return hypothesis
def loss(self, hypothesis, y):
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(hypothesis), reduction_indices=[1]))
return cross_entropy
def training(self, loss):
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train = optimizer.minimize(loss)
return train
def accuracy(self, hypothesis, y):
correct_prediction = tf.equal(tf.argmax(hypothesis, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
return accuracy
def fit(self, x_train, y_train):
# 학습 처리
def evaluate(self, x_test, y_test):
# 예측 결과 처리
신경망 모델 초기화
- 신경망 모델 초기화 부분에서는 모델의 구성을 결정하는 것이 바람직하므로 각 층의 차원을 매개변수로 받아야 한다.
- 모델의 구성도 결정해야 하기 때문에 각 층에 관련된 weight와 bias로 정의한다.
class DNN(objec):
def __init__(self, n_in, n_hiddens, n_out):
self.n_in = n_in
self.n_hiddens = n_hiddens
self.n_out = n_out
self.weight = []
self.bias = []
inference() 함수 정의
- 초기화 부분에 따라 inference() 함수는 다음과 같이 구현할 수 있다. 기존 코드에서는 각 층의 차원 수를 매개변수를 통해 받아들였지만 self.n_in 등을 통해 대체할 수 있게 되었다.
class DNN(objec):
def inference(self, x, keep_prob):
# 입력층 - 은닉층, 은닉층 - 입력층
for i, n_hidden in enumerate(self.n_hiddens):
if i == 0:
input = x
input_dim = self.n_in
else:
input = output
input_dim = self.n_hiddens[i-1]
self.weight.append(self.weight_variable([input_dim, n_hidden]))
self.bias.append(self.bias_variable([n_hidden]))
h = tf.nn.relu(tf.matmul(input, self.weight[-1]) + self.bias[-1])
output = tf.nn.dropout(hypothesis, keep_prob)
# 은닉층 - 출력층
self.weight.append(self.weight_variable([self.n_hiddens[-1], self.n_out]))
self.bias.append(self.bias_variable([self.n_out]))
hypothesis = tf.nn.softmax(tf.matmul(output, self.weight[-1]) + self.bias[-1])
return hypothesis
fit() 함수 정의
- Keras처럼 학습 데이터와 epoch 수, batch size 등 매개변수를 받아들일 수 있어야 한다. 또한 dropout 변수도 사용할 수 있어야 한다.
class DNN(objec):
def fit(self, X_train, Y_train, epochs=100, batch_size=100, p_keep=0.5, verbose=1):
# 학습처리
x = tf.placeholder(tf.float32, shape=[None, self.n_in])
y = tf.placeholder(tf.float32, shape=[None, self.n_out])
keep_prob = tf.placeholder(tf.float32)
# 예측을 위한 evauate() 함수용으로 작성해둔다
self._x = x
self._y = y
self._keep_prob = keep_prob
# 학습을 위한 오차 함수 및 예측 정확도 함수 그래프 생성
hypothesis = self.inference(x, keep_prob)
loss = self.loss(hypothesis, y)
train = self.train(loss)
# evaluate() 함수에서 참조할 수 있도록 함
self.accuracy = self.accuracy(hypothesis, y)
# 그래프 초기화
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
# 예측을 위한 evaluate() 함수용으로 작성해둔다
self._sess = sess
N_train = len(X_train)
n_batches = N_train // batch_size
for epoch in range(epochs):
X_, Y_ = shuffle(X_train, Y_train)
for i in range(n_batches):
start = i * batch_size
end = start + batch_size
sess.run(train, feed_dict={x: X_[start:end],
y: Y_[start: end],
keep_prob: p_keep})
loss_ = loss.eval(session=sess, feed_dict={x: X_train,
y: Y_train,
keep_prob: p_keep})
# evaluate() 함수에서 참조할 수 있도록 함
accuracy_ = self.accuracy.eval(session=sess, feed_dict={x: X_train,
y: Y_train,
keep_prob: p_keep})
# 오차와 정확도를 그래프로 그리기 위해 값을 기록한다
self._history['loss'].append(loss_)
self._history['accuracy'].append(accuracy_)
if verbose:
print(f'epoch : {epoch:03}, loss : {loss_:.7}, accuracy : {accuracy_*100:.6} %')
return self._history
- 초기화 부분 다시 정리
- 테스트할 때 사용되는 변수는 evaluate() 과정에도 필요하기 때문에 클래스 내부에 둬야 한다.
- 학습 진행 상황을 클래서에서 파악하고 있으면 학습 종료 후에 데이터를 처리하기 쉬워지무로 self.history를 정의해 사용한다.
class DNN(objec):
def __init__(self, n_in, n_hiddens, n_out):
self.n_in = n_in
self.n_hiddens = n_hiddens
self.n_out = n_out
self.weight = []
self.bias = []
# 학습 진행 상황을 확인하기 위하여 오차와 정확도를 _history 변수에 저장하고 한다
self._x = None
self._y = None
self._keep_prob = None
self._sess = None
self._history = {'accuracy': [],
'loss': []}
evaluate() 함수 정의
- 앞에서 정의한 self._sess 등을 사용해 다음과 같이 구현한다.
class DNN(objec):
def evaluate(self, X_test, Y_test):
# 모델 예측 결과 처리
return self.accuracy(session=self._sess, feed_dict={self._x: X_test,
self._y: Y_test,
self._keep_prob: 1.0})
정의한 class를 사용하여 모델 설계하기앞서
- 앞서 정의한 class를 사용한다면 main 함수에서 다음과 같이 코딩하여 사용할 수 있다.
if __name__ == '__main__':
MNIST = datasets.fetch_mldata('MNIST original', data_home='.')
n = len(MNIST.data)
N = int(n * 1.00)
N_train = int(n * 0.2)
N_val = int(N_train * 0.2)
indices = np.random.permutation(range(n))[:N]
X = MNIST.data[indices]
Y = MNIST.target[indices]
Y = np.eye(10)[Y.astype(int)]
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=N_train)
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=N_val)
n_in = len(X[0])
n_hiddens = [200, 200, 200]
n_out = len(Y[0])
# 신경망 모델링 정의
model = DNN(n_in=n_in, n_hiddens=n_hiddens, n_out=n_out)
# 신경망 모델 학습
model.fit(X_train, Y_train, epochs=50, batch_size=200, p_keep=0.5)
accuracy = model.evaluate(X_test, Y_test)
print(f'accuracy : {accuracy*100:.6} %')
- 위의 예제에서는 fit() 함수가 수행하는 처리 내용을 단순한 형태로 코딩을 했는데 내부에 있는 처리 내용을 다시 분할해서 코딩하면 더욱 범용으로 편리하게 사용할 수 있는 API가 된다.
- TensorFlow에서는 이러한 높은 수준의 API도 제공하고 있는데, tf.contrib.learn을 통해 이용할 수 있다.
출처 : 정석으로 배우는 딥러닝