Python Tensorflow로 인공신경망 구현하기

실습 환경은 Python 3.6과 tensorflow 1.5이다.

 

인공 신경망(artificial neural network, ANN)

사진 출처: 위키백과

 

인공 신경망이란, 뇌내 신경 세포(뉴런)의 동작 원리에 기초한 학습 알고리즘이다. 학습의 목표는 원하는 출력값 y를 만들어내기 위해 입력값 x에 곱하고 더할 적절한 가중치(W)와 편향(b)을 도출하는 것이다. 이러한 과정은 인공신경망 내에서 이루어진다.

 

인공 신경망을 구현하기 이전에, 구현에 필요한 사전 지식을 먼저 학습하도록 하자.

 

 

활성화 함수

인공 신경망을 통과해 도출된 출력 값 y를 입력으로 받아 의도한 변화를 적용하여 반환하는 함수. 대표적으로 ReLU(렐루)가 있다. 신경망의 출력층에서는 잘 사용되지 않는다고 한다.

 

원-핫 인코딩(one-hot encoding)

레이블 데이터(label data)의 구성 방식으로, 데이터가 가질 수 있는 값들을 일렬로 나열한 배열을 생성한 뒤 표현하려는 값(데이터가 해당되는 속성)의 원소를 1, 그렇지 않은 원소를 0으로 표기하는 표기법이다.

 


 

단일 인공신경망 구축

 

1. 데이터 구성

먼저 원-핫 인코딩 방식을 이용하여 데이터를 구성하도록 하자.

# 특징 데이터 x
x_data = [[0, 0], [1, 0], [1, 1], [0, 0], [0, 0], [0, 1]]
# 레이블 데이터 y
y_data = np.array([
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1],
    [1, 0, 0],
    [1, 0, 0],
    [0, 0, 1]
])

입력 데이터로는 2 개의 속성이 주어지며, 출력 데이터로는 3 개의 결과가 주어진다.

예를 들자면, x_data(특징 데이터)로 털과 날개의 유무를 선정했다고 가정하면

y_data(레이블 데이터)는

털도 날개도 없는 경우([0, 0])인 기타 종([0, 0, 0]),

털은 있지만 날개가 없는 경우([1, 0])인 포유류([0, 1, 0]),

털과 날개가 모두 있는 경우([1, 1])인 조류([0, 0, 1])로 구성할 수 있다.

 

 

2. 신경망 모델 구성

# in, out
X = tf.placeholder(tf.float32, name='X')
Y = tf.placeholder(tf.float32, name='Y')

입출력은 역시 플레이스홀더로 구성한다.

 

# 가중치
W = tf.Variable(tf.random_uniform([2, 3], -1., 1.))
# 편향
b = tf.Variable(tf.zeros([3]))

1행 2열인 입력값에 곱해져 1행 3열인 출력값을 도출하는 가중치 W는 2행 3열으로 이루어져 있어야 한다. 랜덤 변수는 -1부터 1까지의 값을 갖는다.

편향 b는 0으로 채운 1행 3열의 값을 갖는다.

 

L = tf.add(tf.matmul(X, W), b)
L = tf.nn.relu(L)

출력값 L을 선언하고나면, 인공 신경망의 구성이 끝난다.

model = tf.nn.softmax(L)

추가적으로, 출력값을 확률로 만들기 위해 배열 내 모든 요소의 합이 1이 되도록 변환하는 함수(softmax)를 적용해 그 반환 값을 최종적인 model로 삼는다.

 

 

3. 손실 계산

 

방법 1) 직접 계산하기

cost = tf.reduce_mean(-tf.reduce_sum(Y*tf.log(model), axis=1))

방법 2) 텐서플로우에서 제공하는 교차 엔트로피 손실 함수 사용

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=model))

다음과 같이 교차 엔트로피를 이용하여 손실을 구하였다.

 

 

4. 최적화

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train_op = optimizer.minimize(cost)

경사 하강법을 이용하여 최적의 cost를 구한다.

 

 

5. 학습

 

# 세션 초기화
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

# 학습
for step in range(100):
    sess.run(train_op, feed_dict={X: x_data, Y: y_data})

    # 학습 중 10 번에 1 번씩 손실값 출력
    if (step + 1) % 10 == 0:
        print(step+1, sess.run(cost, feed_dict={X: x_data, Y: y_data}))

학습은 100회를 진행하였다. 손실이 감소하는 것을 확인하기 위해 열 번에 한 번 꼴로 손실값이 출력되도록 하였다.

 

 

6. 결과 확인

 

prediction = tf.argmax(model, axis=1)
target = tf.argmax(Y, axis=1)

전자는 예측값, 후자는 실제 값에 해당한다.

# 출력
print("예측값:", sess.run(prediction, feed_dict={X: x_data}))
print("실제값:", sess.run(target, feed_dict={Y: y_data}))

# 정확도 확인
is_correct = tf.equal(prediction, target)
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))
print("정확도:%.2f"%sess.run(accuracy*100, feed_dict={X: x_data, Y: y_data}))

다음과 같은 코드를 통해 예측값과 실제값을 확인하고, 정확도를 구해보았다.

 

결과는 다음과 같이 아주아주 처참했다.

예측값: [2 0 2 2 2 2]
실제값: [0 1 2 0 0 2]
정확도:33.33

 


 

이렇게 정확도가 상당히 낮은 이유는, 단일 신경망을 이용하였기 때문이다. 이러한 문제점을 해결하기 위해 실제로 모델을 구현할 때에는 심층 신경망을 이용한다.

 

 

심층 신경망(Deep Neural Network, DNN)

사진 출처: 위키백과

심층 신경망이란, 신경망의 층을 여러 개로 구성한 신경망에 해당한다. 이를 통한 학습을 우리는 딥 러닝(Deep Learning)이라고 부른다. 위의 사진은 아까 인공 신경망의 개념을 설명할 때 등장했던 사진이다. 이 중 Hidden(은닉층)의 수를 늘림으로써 심층 신경망을 구현할 수 있다.

 

아까 만들었던 인공 신경망에서, 가중치와 편향을 추가함으로써 심층 신경망(다층 신경망)을 구현해보도록 하자.

# 가중치
W1 = tf.Variable(tf.random_uniform([2, 10], -1., 1.))
W2 = tf.Variable(tf.random_uniform([10, 3], -1., 1.))

# 편향
b1 = tf.Variable(tf.zeros([10]))
b2 = tf.Variable(tf.zeros([3]))

인공 신경망에 10 개의 인공 뉴런을 가진 은닉층을 추가했다고 볼 수 있다.

# 은닉층을 거쳐 생성되는 출력 L1
L1 = tf.add(tf.matmul(X, W1), b1)
# 활성화 (은닉층은 활성화를 주로 사용함)
L1 = tf.nn.relu(L1)

먼저 은닉층을 거쳐 1차 출력물 L1이 생성된다. 이 때 활성화가 이루어진다.

model = tf.add(tf.matmul(L1, W2), b2)

출력물 L1이 다시 출력층을 통과해 모델이 만들어진다. 보통 활성화는 출력층에서는 잘 이루어지지 않는다.

 

당연하게도, 출력층을 통과한 이후의 내용은 앞서 구현한 인공신경망과 동일하다. 은닉층의 개수만 증가시켰기 때문이다.

 

그럼 다시 학습을 수행하고 정확도를 확인해보자.

예측값: [0 1 2 0 0 2]
실제값: [0 1 2 0 0 2]
정확도:100.00

훨씬 더 좋은 성능을 확인할 수 있다! 딥러닝의 위력은 대단한 것 같다.