한 페이지 머신러닝

Tensorflow로 인공신경망을 만든다는 것

[CODE] https://goo.gl/1wAbJ5

인공신경망 만들기

텐서플로우로 인공신경망을 설계한다는 것은, 여러 모양의 파이프로 배수관을 설계하는 것과 같습니다.
어떤 목적으로 어떤 종류의 인공신경망을 만들든 간에, 다음 세 가지 절차를 따르게 됩니다.

1. 데이터(물)를 준비합니다. 데이터의 모양(shape)을 잘 봐둡니다.
2. 신경망(파이프)을 설계합니다. 준비된 데이터의 모양을 잘 보고 그에 맞추어 설계해줍니다.
3. 물을 파이프 안으로 부어넣습니다.

1. 데이터 준비

지도학습(Supervised Learning)을 수행할 때는,
입력 데이터(X)를 집어넣고
정답 레이블(Y)에 맞추어 학습을 시킵니다.
이번 예제에서는 

6마리의 동물들(X)을 집어넣고, 3가지 이름으로 분류(Y)로 분류하는 인공신경망 모델을 구현해보겠습니다.

입력 데이터(X)는 animal_0부터 animal_5까지 6마리 동물들의 특징을
꼬리(tail), 짖는가(bark), 다리개수(legs), 크기(size)로 각각 설정했습니다.

정답 레이블(Y)은 각각의 동물에 해당하는 정답입니다.

가령 animal_0은 dog이고, animal_3은 rat 이라는 식으로요.
컴퓨터는 dog이니 cat이니 하는 글자를 읽을 줄 모르므로, 
dog은 [1, 0, 0]
cat은 [0, 1, 0]
rat은 [0, 0, 1]
의 숫자로 표현해줍니다. 이런 표현 방식을 원-핫 인코딩(one-hot encoding)이라고 합니다.

나중에 학습이 끝난 후에는
[ㅁ, ㅁ, ㅁ] 형식의 숫자에서 가장 높은 값을 정답으로 출력하게 됩니다.
아래 그림에서 빨갛게 칠해진 부분 처럼요.


                  

입력 데이터의 모양은 [6 x 4]입니다. (6행 4열)
6마리의 동물들 각각을 4가지 특징으로 기억하고 있지요.
행(row)의 수가 6, 
열(column)의 수가 4입니다.

이번 예제에서는 총 6마리의 동물들로 학습을 시킬 것이지만,
나중에 언제든 10마리 100마리도 입력받을 수 있게끔
동물의 마리수는 지정하지 않은 채로 놓아둡니다.
그림에는 [?x4]로 표기되어 있고,
코드에는 [None, 4]로 표기합니다.

마찬가지로 정답 레이블의 모양은 [6 x 3]입니다. (6행 3열)
나중에 언제든 10마리 100마리를 입력받아도 되게끔
동물의 마리수는 지정하지 않은 채로 놓아둡니다.
그림에는 [?x3]으로 표기되어 있고,
코드에는 [None, 3]으로 표기합니다.

numpy 라는 파이썬 패키지는, 이렇게 데이터가 담기는 그릇의 모양을 다루는 용도로 사용합니다.

                                                   

import tensorflow as tf
import numpy as np

# [꼬리(tail), 짖음(bark), 다리갯수(legs), 크기(size(cm))]
x_data = np.array(
    [[1, 1, 4, 50], [1, 0, 4, 5], [1, 0, 4, 20], [1, 0, 4, 10], [1, 1, 4, 100], [1, 0, 4, 12]])

# [dog, rat, cat]
y_data = np.array([
    [1, 0, 0],  # 개(dog)
    [0, 0, 1],  # 쥐(rat)
    [0, 1, 0],  # 고양이(cat)
    [0, 0, 1],  # 쥐(rat)
    [1, 0, 0],  # 개(dog)
    [0, 0, 1]   # 쥐(rat)
])
2. 신경망 설계

tf.placeholder는, 파이프의 입구와 출구 부분입니다.
입력 데이터와 정답 레이블을 받는 곳이므로, 각각 [None, 4], [None, 3]으로 잡아줍니다.

                                                                          

이제 신경망의 중추를 설계합니다. 
아래 그림에서 
세로로 노란 동그라미 네 개를 묶어 인풋 레이어(input layer)
세로로 빨강 동그라미 세 개를 묶어 아웃풋 레이어(output layer)
세로로 파랑 동그라미 각각을 히든 레이어(hidden layer)들 이라고 부릅니다.
그리고 각각의 레이어 사이의 연결 부위를 tf.Variable이라는 빈 파이프들로 이어줍니다.

그래서 tf.Variable은 '컴퓨터가 학습하는 값'을 담게 됩니다.
tf.Variable은, 일단 모양을 정하고 내용물을 랜덤으로 아무렇게나 채운 뒤,
데이터를 붓고 정답과의 차이(cost)를 최소화 하는(optimize) 방향으로 계산이 일어나는데
이 때 tf.Variable에 담긴 초기 랜덤값이 특정한 수치를 향하여 변해갑니다. 이 과정을 학습(learning)이라고 부릅니다.

히든 레이어는 아래 그림에서 다섯 층으로 만들어져 있는데, 
첫째 층은 4개
둘째 층은 5개
셋째 층은 6개
넷째 층은 4개
다섯째 층은 3개의 노드(node)로 구성되어 있습니다.

인공신경망을 몇 층짜리 히든 레이어로 구성할지,  각각의 히든 레이어마다 몇 개의 노드로 구성할지는
파이프를 설계하는 사람 마음입니다.
이렇게도 해보고, 저렇게도 해볼 수 있습니다.


각각의 히든레이어에는 수도꼭지 밸브가 들어있어서, 
이전 단계에서 받은 값이 정해진 특정 수준이 되는지 아닌지를 평가하여, 이후로 전달할지 말지를 결정합니다.
이 밸브는 활성화 함수(activation function)이라고 부르고,
sigmoid, ReLU 등의 종류가 있습니다. 


#########
# 신경망 모델 구성
######
X = tf.placeholder(tf.float32, shape=[None, 4])
Y = tf.placeholder(tf.float32, shape=[None, 3])

# 첫번째 가중치의 차원은 [특성, 히든 레이어의 뉴런갯수] -> [4, 4] 으로 정합니다.
W1 = tf.Variable(tf.random_uniform([4, 4], -1., 1.))

# b1은 편향(bias)이며, 첫 번째 히든 레이어의 뉴런 갯수입니다. 
b1 = tf.Variable(tf.zeros([4]))

# 신경망의 히든 레이어에 가중치 W1과 편향 b1을 적용합니다
L1 = tf.nn.relu(tf.add(tf.matmul(X, W1), b1))


W2 = tf.Variable(tf.random_normal([4, 5]))
b2 = tf.Variable(tf.zeros([5]))
L2 = tf.nn.relu(tf.add(tf.matmul(L1, W2), b2))

W3 = tf.Variable(tf.random_normal([5, 6]))
b3 = tf.Variable(tf.zeros([6]))
L3 = tf.nn.relu(tf.add(tf.matmul(L2, W3), b3))

W4 = tf.Variable(tf.random_normal([6, 4]))
b4 = tf.Variable(tf.zeros([4]))
L4 = tf.nn.relu(tf.add(tf.matmul(L3, W4), b4))

W5 = tf.Variable(tf.random_normal([4, 3]))
b5 = tf.Variable(tf.zeros([3]))
L5 = tf.nn.relu(tf.add(tf.matmul(L4, W5), b5))

W6 = tf.Variable(tf.random_normal([3, 3]))
b6 = tf.Variable(tf.zeros([3]))
model = tf.add(tf.matmul(L5, W6), b6)



# 텐서플로우에서 기본적으로 제공되는 크로스 엔트로피 함수를 이용해
# 복잡한 수식을 사용하지 않고도 최적화를 위한 비용 함수를 다음처럼 간단하게 적용할 수 있습니다.
cost = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits_v2(labels=Y, logits=model))

optimizer = tf.train.AdamOptimizer(learning_rate=0.01)
train_op = optimizer.minimize(cost)
3. 데이터를 신경망에 부어넣습니다.

tf.global_variables_initializer()는 컴퓨터가 학습할 값들을 초기화 합니다.
sess = tf.Session() 으로 세션을 열고
학습을 하고
sess.close()로 세션을 닫습니다.

#########
# 신경망 모델 학습
######
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})

    if (step + 1) % 10 == 0:
        print(step + 1, sess.run(cost, feed_dict={X: x_data, Y: y_data}))


#########
# 결과 확인
# 0: 개(dog) 1: 고양이(cat), 2: 쥐(rat)
######
prediction = tf.argmax(model, 1)
target = tf.argmax(Y, 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}))
sess.close()

 

댓글

댓글 본문
  1. Woneui Hong
    네. 테스트 데이터는 없습니다. 간단하게 만들려는 예제라서요 :)
    대화보기
    • 질문드려요
      죄송한데 질문하나만 드리겠습니다.

      다름이 아니라 해당 코드를 보다보니 테스트데이터와 학습데이터를 분할하는 부분이 없는것같은데

      제가 볼때는 학습 데이터로 곧바로 테스트를 진행한것같은데 맞는건가요?
    • Woneui Hong
      - 히든레이어 층 수는 5층으로 만들 수도, 9층이나 10층이나 11층으로 만들 수도 있습니다. 모델의 성능을 보고 사람이 그때그때 정하는 값입니다.

      - 정답 레이블에서 6행이라고 말씀하신것은 6행이 아니고 6행보다 더 많이 있을 수 있습니다. 데이터 갯수가 1000개 10000개 100000개도 될 수 있으니까요. 그림을 그리는데 1000개를 전부 그릴 수 없으니 6개만 예시로 그려놓은 것입니다.

      - 정답 레이블에서 3행은 본 예제에서 구분하려는 동물이 3종류이기 때문입니다 (개, 고양이, 쥐).

      - 특징 역시도 본 예제에서는 4개 (tail, bark, legs, size)로 만들어놓았지만 상황에 따라서 10개 100개도 될 수 있습니다.

      따라서 특징이 4개라는 것과 정답 레이블이 6행 3열이라는 것은 서로 아무 관련이 없습니다.


      정답 레이블에서 dog>0.6 0.2 0.2에서 소숫점의 점수 배점은 총합이 1이 되게 만듭니다. 결과를 확률로 읽기 위해서입니다. 이 예제가 아니라 다른 많은 머신러닝 모델이 내놓는 결과도 확률로 해석하기 위해 점수 배점을 1로 만드는게 대부분입니다.

      어려운 내용이라도 건너뛰시지 말고 천천히, 지금처럼 꼼꼼하게 보시면 좋겠습니다. 그리고 설명에서 각 기호나 숫자가 의미하는 바가 단지 이 설명을 위해서만 제시된 예시인지, 아니면 일반적으로 적용될 수 있는 원리인지를 구별해보시면 도움이 될 듯 합니다.
      대화보기
      • scyanny7
        자세한 답변 덕분에 정말 쉽게 이해했어요! 감사합니다!!@@
        그런데 몇가지 궁금한점이 더있어서요 ㅠㅠ

        히든레이어는 왜 5개층인가요?

        특징을 4개로 잡는데 왜 정답 레이블이 6행 3열인가요? 사이즈는 정답레이블에 속하지 않는건가요?

        정답레이블에서 dog> 0.6 0.2 0.2 에서 소숫점의 점수 배점은 자신이 임의로 결정해서 총합이 1이되게 결정하는건가요?

        텐서플로우를 공부하고있는데 텐서플로우 설치하는것부터 결과출력하는것까지 코딩 초보로서 너무 어렵더라구요.ㅠ
        지식인에도 질문하고 관련책도 보고 예제도 보고하는데 궁금한점이 잘 안풀려서 답답했는데
        덕분에 정말 정말 많이 배우고갑니다! 하나하나 자세히 주석도 달아주셔서 훨씬 더 이해하기 쉬운것같아요!!
        정말 감사드려요
        대화보기
        • Woneui Hong
          x_data는 동물의 특징이고, y_data는 동물의 종류 입니다. 아래와 같이 읽을 수 있습니다.
          [꼬리(tail), 짖음(bark), 다리갯수(legs), 크기(size(cm))], [동물종류]

          [1, 1, 4, 50], [1, 0, 0] # 개 <--------꼬리가 있고 (1), 짖고 (1), 다리갯수가 (4)개고, 크기가 (50)인 것은: 개
          [1, 0, 4, 5], [0, 0, 1], # 쥐 <----------꼬리가 있고 (1), 안짖고 (0), 다리갯수가 (4)개고, 크기가 (5)인 것은: 쥐
          [1, 0, 4, 20],[0, 1, 0], #고양이 <-꼬리가 있고 (1),안짖고 (0),다리갯수가 (4)개고,크기 (20)인 것은: 고양이
          [1, 0, 4, 10], [0, 0, 1], # 쥐 <----꼬리가 있고 (1), 안짖고 (0), 다리갯수가 (4)개고, 크기가 (10)인 것은: 쥐
          [1, 1, 4, 100],[1, 0, 0], # 개 <----꼬리가 있고 (1), 짖고 (1), 다리갯수가 (4)개고, 크기가 (100)인 것은: 개
          [1, 0, 4, 12], [0, 0, 1] # 쥐 <----꼬리가 있고 (1), 안짖고 (0), 다리갯수가 (4)개고, 크기가 (12)인 것은: 쥐

          같은 동물을 여러번 쓰는 이유는
          개도 여러 특징의 개가 있고
          쥐도 여러 특징의 쥐가 있고
          고양이도 여러 특징의 고양이가 있기 떄문입니다. 서로 다른 특징을 가진 쥐라도 다같이 '쥐'라고 판정해야 하기 때문이에요.
          대화보기
          • scyanny7
            x_data = np.array(
            [[1, 1, 4, 50], [1, 0, 4, 5], [1, 0, 4, 20], [1, 0, 4, 10], [1, 1, 4, 100], [1, 0, 4, 12]])
            에서 괄호안에 있는 숫자가 의미하는것은 무엇인가요>??

            # [dog, rat, cat]
            y_data = np.array([
            [1, 0, 0], # 개(dog)
            [0, 0, 1], # 쥐(rat)
            [0, 1, 0], # 고양이(cat)
            [0, 0, 1], # 쥐(rat)
            [1, 0, 0], # 개(dog)
            [0, 0, 1] # 쥐(rat)
            ])
            왜 개와 쥐를 2번 3번씩 또 쓰는 건가요

            덕분에너무 잘 이해되고 몰랐던부분에대해서 많이 배웠습니다
          버전 관리
          Woneui Hong
          현재 버전
          선택 버전
          graphittie 자세히 보기