파이썬도 시작하기
이번 글에선 아래 페이지를 번역하면서 따라 해보려고 한다.
https://www.tensorflow.org/get_started/get_started
가이드 첫 단락에서 이번 가이드를 통해 얻는 걸 극대화하려면 3가지가 필요하다고 한다.
1. 파이썬으로 프로그래밍할 줄 알고
2. 적어도 배열에 대해서 좀 알고
3. 이상적으론, 머신러닝에 대해서 좀 알아야 하지만.. 이 가이드가 첫 번째 가이드여도 좋다.
이번 기회에 파이썬도 같이 배워보려고 하는 거니 1번은 자격미달.
배열에 대해선 좀 아니 2번은 만족,
3번은 어중간한 조건이니 적당히 만족한다고 치면,
3가지 중 2가지를 패스했으니 과반은 넘었다. 킵 고잉 하겠다.
우선 텐서(Tensor) 가 무슨 뜻인지 궁금하여 검색해봤다.
처음 만난 결과는 아래 링크였다.
http://www.scienceall.com/%ED%85%90%EC%84%9Ctensor/
벡터의 확장 개념. 수학 · 물리학에서 중요한 역할을 하는 텐서는 벡터의 개념을 포함한다.
텐서의 어원은「탄성변형의 장력」의 영어명 tension에 연유한다
구글에서 새로 만든 단어인 줄 알았는데 수학에서 기본적으로 사용되는 개념이었다.
다만 고등교육과정까지는 나오지 않고 대학 이후 과정에서 배울 수 있는 내용인 것 같다.
뒤에 내용이 더 있었지만 여기까지만 이해됐다. 그 뒤의 말은 눈에서 뇌로 전달되지 않았다.
일단 실습 먼저 해볼까 하다가
이름을 이해 못하면 텐서플로우를 이해하는데도 한계가 있을 것 같아서 더 찾아봤다.
그러다 아래 링크를 찾을 수 있었다.
http://blog.naver.com/rlaghlfh/220914107525
전자공학도로 추정되는 김회로님의 블로그인데, 어려운 개념을 이해하기 쉽게 잘 설명해주셨다.
물론 이 설명도 완전히 이해하진 못했다. 큰 개념만 머리에 잡고 진도를 나갔다.
다음으로 영어로 되어 있는 첫 단락을 번역해봤다.
텐서플로우는 여러 API를 제공한다.
가장 낮은 레벨의 API인 텐서플로우 코어는 사용자에게 완벽한 프로그래밍 제어를 제공한다.
텐서플로우 코어는 머신러닝 researcher나 적용하고자 하는 모델에 미세한 컨트롤이 필요한 사람들에게 추천한다.
텐서플로우 코어 기반으로 만들어진 보다 높은 레벨의 API도 있다.
이런 API들은 보다 배우고 사용하기 쉽다.
또한 서로 다른 사용자들 간 반복 작업을 쉽고 일관적으로 할 수 있게 해준다.
tf.contrib.learn 같은 하이레벨 API는 데이타셋, estimators, 트레이닝, 추론 관리를 도와준다.
*estimator : 추정 법칙. 모집단의 매개 변수를 추정하는 법칙, 또는 그 결과 얻어진 값. 보통 샘플 값의 함수로서 주어진다. 따라서 랜덤 변수이고, 그 분포 상태는 거기에 유도한 견적의 신뢰성을 사전 평가하는 데 매우 도움이 된다. 예를 들면, 샘플 값에서 모집단의 평균값 μ를 추정하는 경우의 추정 법칙으로서는 샘플 데이터의 평균값 x, 데이터를 크기의 순서로 배열했을 때의 중앙값 등, 여러 가지가 생각된다. 그러나 추정 값으로서는
① 편중이 없다는 것(매개 변수에 대하여 과대하지도 과소하지도 않은 것).
② 샘플수가 늘어날수록 매개 변수에 대한 좋은 추정 값이 얻어지는 것.
③ 분산이 작은 것 등의 성질을 갖춘 것이 바람직하다.
http://terms.naver.com/entry.nhn?docId=751763&cid=42341&categoryId=42341
[네이버 지식백과] 추정 법칙 [estimator, 推定法則] (전자용어사전, 1995. 3. 1., 성안당)
'contrib'을 메서드 명에 포함하고 있는 몇몇 하이레벨 텐서플로우 API들은 아직 개발 중이니 주의가 필요하다. 텐서플로우 릴리즈에 따라 바뀌거나 더 이상 쓰이지 않을 수 있다.
이 가이드는 텐서플로우 코어 튜토리얼로 시작한다.
그리고 같은 모델을 tf.contrib.learn으로 어떻게 구현하는지도 설명할 것이다.
텐서플로우 코어의 규칙을 배우는 건 나중에 더 높은 수준의 API를 사용할 때 내부적으로 어떻게 작동하는지에 대한 훌륭한 멘탈 모델(mental model) 을 제공해줄 것이다.
* 멘탈모델 :
멘탈모델이란 실제 세상에서 어떤 것이 어떻게 움직이는지를 알기 위해 한 사람이 취하는 생각 처리 과정을 설명하는 것이다. 주위 환경, 주위 환경의 다양한 파트들 간의 관계, 한 사람의 행동과 결과에 대한 직관적인 통찰력을 표현한다. 멘탈모델은 행위, 문제를 해결하기 위한 접근 방법, 행하는 task 들을 표현하는데 도움을 준다. (https://en.wikipedia.org/wiki/Mental_model)
사용자 리서치 방법 중 하나인 ‘멘탈모델(Mental model)’은 정보구조 설계에 익숙하지 않은 사람에게도 사용자 조사 시 유용하게 쓰일 수 있을 만한 방법이라고 합니다. 이를 테면 내가 머신러닝을 공부하기 위해서 어떻게 할지 생각하고 텐서플로우를 검색하고 홈페이지를 찾아서 시작하는 행위 등을 표현하는 방법이라고 생각하면 될 거 같다. (http://egloos.zum.com/hisprite/v/3810333)
고작 두 문단 번역하면서 모르는 용어가 이렇게 많았다.
estimator는 설명을 봐도 아직 구체적으로 이해되진 않는다.
두 문단 번역하고 나니 Tensors 라는 소제목을 달고 텐서(tensor)에 대한 설명이 나왔다.
텐서플로우에서 가장 중요한 데이터의 단위는 텐서다.
텐서는 dimension 개수의 배열로 표현된 primitive 값의 집합으로 구성된다.
텐서에서 rank는 dimension의 개수다. 아래는 텐서의 몇 가지 예시이다.
3 # a rank 0 tensor; this is a scalar with shape []
[1. ,2., 3.] # a rank 1 tensor; this is a vector with shape [3]
[[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]
위 김회로님의 글과 함께 읽으면 이해하는데 도움이 된다.
텐서플로우 import 문장은 아래와 같다.
import tensorflow as tf
이 홈페이지에서 가장 잘 이해가 된 문장이다.
텐서플로우 사용하려면 일단 import 해야겠지.
텐서플로우 코어 프로그램은 두 가지 별개의 섹션으로 구성되었다고 생각하란다.
1. computational graph를 만들기
2. computational graph를 실행하기
컴퓨테이셔널 그래프.
발음은 할 수 있지만 언뜻 무얼 말하는지 이해가 되지 않았다. 다행히 연이어 설명된다.
computational graph는 연속된 텐서플로우 명령어들이 노드 그래프로 배열되어 있는 걸 말한다.
아래 그림을 보면 이해가 한결 쉽다.
각 노드는 0 혹은 하나 이상의 텐서들을 인풋으로 받고, 텐서 하나를 아웃풋으로 만든다.
노드 타입엔 상수 타입이 있다. 상수는 인풋을 받지 않고, 아웃풋으로 내부에 저장된 값을 내보낸다.
다음으로 간단한 computational graph를 만들어 본다. (드디어 실습이다.)
아래와 같이 두 개의 부동소수점 텐서, node1, node2를 만들어보자.
실행시킬 코드는 아래와 같다. constant가 상수를 만드는 명령어다.
node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node2)
노드에 값을 주고 출력(print)해봐도 3.0이나 4.0 값은 보이지 않는다.
대신 노드가 평가될 때 각 노드는 3.0과 4.0을 생산할 것이라고 한다.
노드를 평가하기 위해선 '세션' 안에서 computational graph를 실행해야 한다.
세션은 텐서플로우 실행시간의 제어와 상태를 캡슐화한다.
이제 세션 생성을 실습해보자.
아래 코드는 노드 1과 노드 2를 평가하는 computational graph를 실행하기 위해 세션 오브젝트를 생성하고 run 메서드를 invoke 한다.
실행시킬 코드는 아래와 같다. Session() 명령어를 통해 세션을 생성할 수 있다.
sess = tf.Session()
print(sess.run([node1, node2]))
세션을 생성해 run 메서드를 통해 노드 1과 노드 2를 평가했더니 처음에 넣어줬던 3.0과 4.0 값을 볼 수 있다.
다음으로 명령어를 가지고 있는 텐서노드를 더해 좀 더 복잡한 실습을 해보자.
이번 예시는 두 개의 상수 노드를 더해 새로운 그래프를 생산할 것이다.
실행시킬 코드는 아래와 같다.
node3 = tf.add(node1, node2)
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))
아래 그래프는 텐서플로우에서 제공하는 텐서 보드라는 유틸리티를 이용해 그린 computational graph라고 한다. 아직 텐서 보드는 이용해 보지 못했다.
아래에 계속 나올 텐서 보드 그림은 텐서플로우 홈페이지
(https://www.tensorflow.org/images/getting_started_add.png)에서 가져왔다.
현재 상태 그대로의 그래프는 항상 같은 값을 내기 때문에 특별히 흥미가 생기진 않는다.
그래프는 외부 인풋을 받기 위해 매개변수화 할 수 있다. placeholder라고 한다.
placeholder는 나중에 값을 제공하겠다는 약속이다.
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # + provides a shortcut for tf.add(a, b)
위 코드는 인풋 파라미터 두 개를 정의하고 두 파라미터에 대한 연산을 정의한다는 측면에서
함수나 람다와 비슷하다.
이 그래프는 복수의 인풋을 넣고 평가할 수 있다.
feed_dict 파라미터를 이용하면 텐서가 명시화 되고 플레이스 홀더에 구체적인 값을 제공하게 된다.
먼저 feed_dict가 무엇인지 고민해보자,
뭔가 텐서플로우의 세션의 run에 input으로 주어져서 그런지 굉장히 거창한 클래스 같이 느껴진다. 그렇지만, 실제로는 python의 기본 타입인 딕셔너리 타입에 지나지 않는다. 즉 다시 말해, key-value 페어들의 집합이며, 해쉬 테이블과 유사한 개념이다.
출처: http://superelement.tistory.com/14 [아마추어 엔지니어 노트]
아래 코드에선 중괄호{}로 묶인 부분으로 보면 될 거 같다.
이제 코드를 살펴보자
print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))
다른 명령어를 더해서 좀 더 복잡하게 computational graph를 만들 수도 있다.
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))
위 코드의 결과는 22.5이다.
머신러닝을 할 때 가장 필요한 모델은 위와 같이 임의의 인풋을 취할 수 있는 모델이다.
훈련 가능한 모델을 만들기 위해선
같은 입력값으로 새로운 아웃풋을 얻을 수 있도록 모델을 변경할 수 있어야 한다.
Variable 은 훈련 가능한 파라미터를 그래프에 추가할 수 있도록 해준다.
Variable은 타입과 초기값으로 만들어진다. 참고로 코드에서도 V 대문자다.
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
Constant는 tf.constant를 호출하는 순간 값이 설정되고 다신 바꿀 수 없다.
하지만 Variable은 tf.Variable을 호출할 때 초기화되지 않는다.
텐서플로우 프로그램 내 모든 variable을 초기화하기 위해선
아래처럼 명시적으로 특정 명령을 호출해야 한다.
init = tf.global_variables_initializer()
sess.run(init)
init은 모든 global 변수를 초기화하는 텐서플로우 서브 그래프의 핸들이다.
sess.run(init) 까지 호출해야 초기화된다. init 만으로는 초기화되지 않는다.
x가 플레이스 홀더이므로 linear_model은 여러 개의 x 값으로 동시에 평가할 수 있다.
print(sess.run(linear_model, {x:[1,2,3,4]}))
어떤 에러가 나는지 궁금해서
sess.run(init) 전에 print(sess.run(linear_model, {x:[1,2,3,4]})) 를 실행해보았다.
Attempting to use uninitialized value Variable...이라는 에러 메시지가 보인다.
sess.run(init) 실행 후 print 하면 에러 없이 잘 실행된다.
이렇게 모델을 만들어봤는데, 아직 어떻게 좋은 건지 잘 모르겠다.
트레이닝 데이터 위에서 모델을 평가하려면
원하는 값들을 제공하기 위한 y 플레이스 홀더가 필요하다.
그리고 손실 함수(loss function)를 만들어야 한다.
손실 함수는 정답에 대한 오류를 숫자로 나타내는 것으로
오답에 가까울수록 큰 값이 나온다. 반대로 정답에 가까울수록 작은 값이 나온다.
[출처] 딥러닝 손실 함수 MSE(Mean Squared Error), CEE(Cross Entropy Error)|작성자 똑똑이
손실 함수는 현재 모델이 제공된 데이터에서 얼마나 멀리 떨어져 있는지 측정하는 함수다.
우리는 선형 회귀를 위해 표준 손실 모델을 사용할 것이다.
통계학에서, 선형 회귀(線型回歸, linear regression)는 종속 변수 y와 한 개 이상의 독립 변수 (또는 설명 변수) X와의 선형 상관관계를 모델링하는 회귀분석기법이다.
https://ko.wikipedia.org/wiki/%EC%84%A0%ED%98%95_%ED%9A%8C%EA%B7%80
표준 손실 모델은 현재 모델과 제공된 데이터 사이, 즉 오차 델타의 제곱을 더한다.
linear_model - y 명령은 벡터를 생성한다. 이 벡터는 각각의 원소들이 상응하는 예시의 에러 델타가 된다.
tf.square를 호출하면 그 에러를 제곱한다.
그리고 tf.reduce_sum 명령을 사용해 모든 에러 제곱들을 합(sum)해 하나의 스칼라를 만든다.
이 스칼라는 모든 예시의 에러를 함축(abstract) 한다.
(아직 위 내용이 정확히 어떤 작업인지 이해가 안 되었지만 손실 함수를 검색하다 보니 손실 함수를 만드는 방법으로 평균 제곱 오차나 최소 평균 제곱 오차 같은 개념을 볼 수 있었다. 그런 과정이지 싶다.)
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
첫 번째 결과를 보면 23.66 이 출력된다.
아까 말했듯, 손실 함수는 값이 클수록 오차가 크다는 얘기다. 오차가 없으면 0이 된다.
이 값은 수작업으로 향상할 수 있다.
W와 b 의 값을 완벽한 값인 -1과 1로 다시 할당해주면 된다.
variable은 tf.Variable 명령에 넣은 값으로 초기화됐지만,
tf.assign 같은 명령을 이용해 바꿀 수 있다.
따라서 위 실행의 아래 부분처럼 W와 b에 -1과 1을 넣을 수 있다.
이렇게 하면 손실은 두 번째 결과처럼 0이 된다.
이렇게 W와 b의 완벽한 값을 추측할 수 있지만, 이건 머신러닝이 아니다.
머신러닝의 포인트는 자동으로 옳은 모델 파라미터를 찾아내는 것이다.
이 후 소제목으로 tf.train API가 등장한다. train은 이름부터가 굉장히 머신러닝스럽다.
드디어 머신 러닝스럽게 내가 추측하며 배우는 과정을 모델링 해볼 수 있는 것인가.
다음 글에서 번역 및 실습해보려 한다.