#3 인공신경망 동작 원리 - MLP(Multilayer Perceptron)

2020. 2. 12. 19:03 개발 이야기/머신러닝(딥러닝)

 

 

안녕하세요

 

오늘은 인공신경망의 동작 원리MLP에 대해서 알아보도록 하겠습니다. 참고한 블로그는 아래와 같습니다.

Multilayer Perceptron(MLP) Tutorial
(흰고래의 꿈 블로그)


 

텐서플로우(tensorflow)

 

먼저 MLP는 뭘까요? Multilayer Perceptron으로 다층 퍼셉트론을 의미하고 있습니다. 그러면 퍼셉트론은 뭘까요?

#2 인공신경망 동작 원리 - 뉴런

지난번 블로그에서 뉴런에 대해서 다뤘었는데요 이 뉴런을 딥러닝에서 구현한 것을 퍼셉트론이라고 합니다. 여러개의 층으로 구현된 뉴런이 바로 MLP가 되겠죠?

이 MLP를 어떻게 구현하고 어떻게 학습시키는지 테스트 코드를 디버깅하면서 알아보도록 하겠습니다.

 

먼저 테스트 데이터는 위에서 소개한 흰고래의 꿈님의 블로그에서 가져왔는데요 테스트 데이터를 보면

 

 

순서대로 쭉 나열된 데이터를 볼 수 있습니다. 왼쪽의 sequential하게 나열된 데이터와 그에 맞는 값이 있는데요 순서에 따라 값이 증감하는 것을 볼 수 있습니다. 자 그럼 디버깅을 하면서 하나씩 확인해보도록 하겠습니다. 먼저 hidden_node_num 리스트는 층 별 노드의 갯수, hidden_layer_num층 수 입니다. 텍스트 데이터를 import 해서 텍스트 데이터를 numpy로 generate 해줍니다


NumPy행렬이나 일반적으로 대규모 다차원 배열을 쉽게 처리 할 수 있도록 지원하는 파이썬의 라이브러리이다. NumPy는 데이터 구조 외에도 수치 계산을 위해 효율적으로 구현된 기능을 제공한다.


hidden_node_num = [10, 9, 8]  # 층별 노드의 갯수
hidden_layer_num = len(hidden_node_num)  # 히든 레이어 층 수

# load data
data_file_name = 'x_square.txt'
xy = np.genfromtxt(data_file_name, dtype='float32')  # txt 파일 generate

temp_x = xy[:, 0]  # file 내에 0열 값을 temp_x에 저장 
temp_y = xy[:, 1]  # file 내에 1열 값을 temp_y에 저장

x_data = [temp_x]
y_data = [temp_y]  # ndarray를 List 형태로 변환

x = tf.placeholder(dtype=tf.float32)  # tensor placeholder 생성 (값 저장소), 매개변수
y = tf.placeholder(dtype=tf.float32)

 

파일의 0번째 열 값들을 temp_x1번째 열 값들을 temp_y에 저장해주고 list 형태로 변환해줍니다. 직접 디버깅해보겠습니다.

x_data에 list 형태로 값이 담겨있는 것을 볼 수 있습니다. 테스트 데이터의 0번째 열데이터를 가져온 것이죠?

 

y_data에는 1열 값들이 전부 순서대로 담겨있는 것을 볼 수 있습니다. 그리고 여기서 중요한 것은 placeholder 개념이 등장합니다. 어려운 개념은 아니지만 머신러닝을 접해본 사람이 아니라면 익숙하지 않은 부분입니다.

placeholder는 tensor를 담을 수 있는 변수인데요!


tensor란? 벡터의 확장 개념. 수학 · 물리학에서 중요한 역할을 하는 텐서는 벡터의 개념을 포함한다.


사용을 위해서는 먼저 만들어주어야합니다. 그리고는 

w = []  # 가중치 list
b = []  # 편향 List
layer = []  # model list
x_data_len = len(x_data)

 

노드로 사용할 list를 만들어줍니다. 하위 부분 부터는 이해를 위해 그림과 함께하도록 하겠습니다.

# first layer
w.append(tf.Variable(tf.random_normal([hidden_node_num[0], x_data_len]), name="w0"))  # 10, 1 인 2차원 텐서 생성
b.append(tf.Variable(tf.random_normal([hidden_node_num[0], 1]), name="b0"))

name = w0을 갖는 tensor를 추가해줍니다.

 

여기서 몇 가지 알아야할 텐서플로우 메서드가 있는데요 Variable 함수는 텐서에 값을 정의해주는 함수이고 random_normal 함수는 정규분포의 무작위 값으로 초기화 해주는 함수입니다!

hidden_node_num[0]에는 10이 들어있고 x_data_len은 1이기 때문에 w, b 모두 10, 1 모양의 텐서가 추가된 것을 알 수 있습니다.

W는 가중치(weight), B는 편향(bias)를 의미하는데요

입력값 X가중치(W)하고 편향(B)더한활성화 함수(Sigmoid, ReLU 등)을 거쳐

결괏값 Y를 만들어 내는것이 인공 뉴런, 퍼셉트론의 기본입니다. 

 

원하는 Y 값을 찾아 내기위해 W, B의 값을 변경해 가면서 적절한 값을 찾아내는 최적화 과정학습(Learning) 또는 훈련(training) 이라고 합니다.

 

 

 

먼저 w, b에 append된 것을 그려보면 아래와 같습니다.

 

Hidden layer의 추가 부분을 보겠습니다.

 

# add hidden layers (variable number)
for i in range(1, hidden_layer_num):
    wName = "w" + str(i)
    bName = "b" + str(i)
    w.append(tf.Variable(tf.random_normal([hidden_node_num[i], hidden_node_num[i - 1]]), name=wName))
    b.append(tf.Variable(tf.random_normal([hidden_node_num[i], 1]), name=bName))

 

for 문을 따라 w1, w2, w3 히든 레이어가 추가 되는 것을 확인할 수 있습니다.

 

중요한 것은 w의 히든레이어 노드 갯수가 10, 9, 8 순서로 하나씩 줄여가면서 append 되는것을 볼수 있습니다.

 

히든레이어까지 append된 모양을 보면 아래와 같습니다.

자 이제 마지막 Layer를 추가하겠습니다.

# add final layer
wName = "w" + str(hidden_layer_num)
bName = "b" + str(hidden_layer_num)
w.append(tf.Variable(tf.random_normal([1, hidden_node_num[-1]]), name=wName))
b.append(tf.Variable(tf.random_normal([1], 1), name=bName))

마지막 Output Layer는 1로 노드는 아래와 같게 됩니다.

 

 

자 이제 모델을 정의하겠습니다. 먼저 사용하는 활성화 함수는 Sigmoid를 사용했습니다. 위에서 설명했듯 가중치(w)와 입력값(x)의 곱 + 평향으로 layer를 정의해줍니다. 첫 줄은 첫번째 입력층이고, for문 안에 있는 것은 은닉층을 append 해줍니다.

# define model
layer.append(tf.nn.sigmoid(tf.matmul(w[0], x) + b[0]))
for i in range(1, hidden_layer_num):
    layer.append(tf.nn.sigmoid(tf.matmul(w[i], layer[i - 1]) + b[i]))
y_out = tf.matmul(w[-1], layer[-1]) + b[-1]

 

경사하강법(GradientDescent)보다 좋은 효과를 나타낸다는 Adam을 이용해 Optimizing을 해줍니다

cost = tf.nn.l2_loss(y_out - y)
opt = tf.train.AdamOptimizer(0.1)  # GradientDescent 보다 성능이 좋은 옵티마이저

 

모델을 트레이닝하고 테스트 데이터를 만들어 그래프로 그려줍니다.

 

with tf.Session() as sess:
    sess.run(init)
    for i in range(5000):
        sess.run(train, feed_dict={x: x_data, y: y_data})
    # generate test data
    x_temp = np.linspace(0, 20, 50)
    x_test = [x_temp]
    y_test = sess.run(y_out, feed_dict={x: x_test})

np.linspace 함수를 이용해 50개의 테스트 데이터를 추출합니다.

 


y = linspace(x1,x2)는 x1과 x2 사이에서 균일한 간격의 점 100개로 구성된 행 벡터를 반환합니다.
y = linspace(x1,x2,n)은 n개의 점을 생성합니다. 점 사이의 간격은 (x2-x1)/(n-1)입니다.

linspace는 콜론 연산자 “:”과 유사하지만, 점 개수를 직접 제어할 수 있으며 항상 끝점을 포함합니다.
이름 “linspace”의 “lin”은 선형 간격 값을 생성하는 것을 나타내며,
#는 로그 간격 값을 생성하는 형제 함수 logspace와 대조됩니다.


 

matplotlib 모듈을 이용해 그래프로 표현해줍니다.

# design graph
plt.plot(x_data, y_data, 'ro', alpha=0.05)
plt.plot(x_test, y_test, 'h', alpha=1)
plt.show()

 

여기서 "ro"와 "h" 값이 궁금한 사람들을 위해 plot 사용방법을 첨부합니다.



 

실행하게되면 다음과 같은 결과물이 출력됩니다.

 

 

빨간색 선러닝된 모델이고 테스트 데이터를 input 했을때 나온 output이 알록달록한 동그라미 입니다.

 

소스코드는 다음과 같습니다.

# coding=utf-8

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

hidden_node_num = [10, 9, 8, 7]
hidden_layer_num = len(hidden_node_num)

# load data
data_file_name = 'x_square.txt'
xy = np.genfromtxt(data_file_name, dtype='float32')

temp_x = xy[:, 0]
temp_y = xy[:, 1]

x_data = [temp_x]
y_data = [temp_y]  # ndarray를 List 형태로 변환

x = tf.placeholder(dtype=tf.float32)  # tensor placeholder 생성 (값 저장소), 매개변수
y = tf.placeholder(dtype=tf.float32)

w = []
b = []
layer = []
x_data_len = len(x_data)

# first layer
w.append(tf.Variable(tf.random_normal([hidden_node_num[0], x_data_len]), name="w0"))  # 10, 1 인 2차원 텐서 생성
b.append(tf.Variable(tf.random_normal([hidden_node_num[0], 1]), name="b0"))

# add hidden layers (variable number)
for i in range(1, hidden_layer_num):
    wName = "w" + str(i)
    bName = "b" + str(i)
    w.append(tf.Variable(tf.random_normal([hidden_node_num[i], hidden_node_num[i - 1]]), name=wName))
    b.append(tf.Variable(tf.random_normal([hidden_node_num[i], 1]), name=bName))

# add final layer
wName = "w" + str(hidden_layer_num)
bName = "b" + str(hidden_layer_num)
w.append(tf.Variable(tf.random_normal([1, hidden_node_num[-1]]), name=wName))
b.append(tf.Variable(tf.random_normal([1], 1), name=bName))

# define model
layer.append(tf.nn.sigmoid(tf.matmul(w[0], x) + b[0]))
for i in range(1, hidden_layer_num):
    layer.append(tf.nn.sigmoid(tf.matmul(w[i], layer[i - 1]) + b[i]))
y_out = tf.matmul(w[-1], layer[-1]) + b[-1]

# setup cost function and optimizer
# cost = tf.reduce_mean(tf.square(y_out - y))
# opt = tf.train.GradientDescentOptimizer(learning_rate=0.01)
cost = tf.nn.l2_loss(y_out - y)
opt = tf.train.AdamOptimizer(0.1)  # GradientDescent 보다 성능이 좋은 옵티마이저

train = opt.minimize(cost)

# training model
init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)
    for i in range(5000):
        sess.run(train, feed_dict={x: x_data, y: y_data})
    # generate test data
    x_temp = np.linspace(0, 20, 50)
    x_test = [x_temp]
    y_test = sess.run(y_out, feed_dict={x: x_test})

print(len(x_temp))
print(x_test)
print(y_test)

# design graph
plt.plot(x_data, y_data, 'ro', alpha=0.05)
plt.plot(x_test, y_test, 'h', alpha=1)
plt.show()

 

 

이상으로 tensorflow를 이용한 MLP 구현예제를 디버깅해봤습니다.