신경망 모델 코드로 구현하기
- 딥러닝 모델 구현 시, 1개의 층을 코드로 작성하면 다음과 같다.
- TensorFlow
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)
- Keras
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을 통해 이용할 수 있다.
출처 : 정석으로 배우는 딥러닝
'신경망(Neural Network) 스터디' 카테고리의 다른 글
12. 데이터 정규화 및 매개변수 초기화 (0) | 2018.01.23 |
---|---|
11. 학습 과정 시각화 (3) | 2018.01.19 |
9. 오버피팅 문제 해결 (0) | 2018.01.19 |
8. 경사 소실 문제 해결하기 (0) | 2018.01.18 |
7. 심층 신경망 (0) | 2018.01.10 |