텐서플로우를 이용한 다중 선형 회귀

단순 선형 회귀가 쉽기에 다중 선형 회귀도 쉬운 줄 알았다...하지만 결코 쉽지 않다...ㅠ.ㅠ 물론 독립변수만 늘어났을 뿐 대부분의 식을 그대로 사용해도 되므로 

그냥 그렇게만 알고 넘어가면 이보다 쉬운 것도 없다. 하지만 수학적 사고방식이 모자란 문돌이에게는 변수가 하나 늘어난다는 것은 천지가 개벽하는 변화다.


예를들어 단순 선형 회귀는 그 결과를 그래프를 통해서 시각적으로 쉽게 확인이 가능했다. 그런데 다중 선형 회귀는 도대체 시각적으로 어떻게 표현해야 할지를 모르겠다. 

그나마 독립변수가 2개인 경우는 3차원그래프로 설명을 해놓은 곳이 많아 그러려니 했는데 3개 이상부터는 도대체 어찌 할 수 있는지 할 수는 있는 것인지...ㅠ.ㅠ


일단은 cost 함수로 성공 여부를 가늠하면서 한 번 진행해보기로 했다.


일단 사용한 데이터는 아래 문서의 16쪽에 있는 예제의 데이터이다.

 https://ita.kaist.ac.kr/data/board/ITAMATLAB09_02.pdf


단순 선형 회귀에서 확장

# '텐서플로우 첫걸음'의 예제에서 구현된 변수. 여기서는 직접 회귀 분석에 사용되지는 않고 산점도 행렬을 그리는데만 사용됨
# num_points = 1000
vectors_set = [[2.0, 3.0, 3.2],[1.3, 1.1, 3.0],[2.4, 3.5, 3.6],[1.5, 2.5, 2.6],[0.6, 1.9, 0.6],[2.0, 2.8, 3.5],
               [1.0, 1.3, 2.1], [2.0, 3.3, 3.4], [1.3, 2.0, 2.8], [0.9, 1.0, 2.3]
               ]

#vectors_set2 = [[36.2, 206.2, 1.0, 32.0],[39.0, 218.6, 4.0, 39.0],[81.7, 264.6, 4.0, 41.0],[39.0, 330.5, 2.0, 33.0],
#               [68.3, 334.7, 3.0, 37.0],[106.3, 365.6, 4.0, 31.0],[123.9, 379.3, 5.0, 35.0],[114.8, 456.4, 6.0, 29.0], 
#               [97.0, 502.7, 3.0, 27.0], [100.2, 531.0, 7.0, 36.0]
#               ]

최초에 "텐서플로우 첫걸음"의 예제 노트북으로 시작을 했기에 코드역시 기본적으로는 예제와 동일한 구조다.

y_data = [2.0, 1.3, 2.4, 1.5, 0.6, 2.0, 1.0, 2.0, 1.3, 0.9]
x1_data = [3.0, 1.1, 3.5, 2.5, 1.9, 2.8, 1.3, 3.3, 2.0, 1.0]
x2_data = [3.2, 3.0, 3.6, 2.6, 0.6, 3.5, 2.1, 3.4, 2.8, 2.3]
#x3_data = [v[3] for v in vectors_set]

참고 : 위에서 기존에 사용하던 vectors_set이라는 변수를 그대로 살려놓았는데 그 이유는 다중 회귀 분석을 시각화 하는 방법 중 산점도 행렬을 그리기 위해서다. 

다중 회귀를 구성하는 각 변수들의 관계를 볼 수 있는 산점도 행렬을 만들 수 있도록 데이터를 구조화 해주는 pandas라는 라이브러리를 찾아서 사용해 보았는데. 

이 pandas에서 데이터를 만들 때 조금 더 편하게 사용할 수 있다. 아래 코드를 수행했을 때 처음 나오는 그래프가 바로 산점도 행렬이다.

import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame(vectors_set)

pd.tools.plotting.scatter_matrix(df)
plt.tight_layout()
plt.show()

plt.plot(x1_data, y_data, 'ro')
plt.show()
plt.plot(x2_data, y_data, 'b+')
plt.show()
#plt.plot(x3_data, y_data, 'cs')
#plt.show()


그리고 다음과 같이 가설 함수를 만든다. 다중 회귀 분석의 가설함수는 단순 회귀 분석의 가설함수에서 추가되는 독립 변수에 대해 W * X를 추가로 더해주면 된다. 

즉 기존 단순 선형 회귀의 가설 함수가 y = W * x + b였다면 독립변수가 2개인 다중 회귀의 가설함수는 y = W1 * x1 + W2 * x2 + b의 형식이 되는 것이다. 이런 식으로 

독립변수가 늘어남에 따라 가설함수는 다음과 같이 표현할 수 있다.


위 예제 데이터는 독립변수가 2개이기 때문에 아래와 같이 가설 함수를 만들 수 있다.

import tensorflow as tf

W1 = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
W2 = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
#W3 = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
#b = tf.Variable(tf.random_uniform([1], 88.0, 89.0))
b = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
y = W1 * x1_data + W2 * x2_data + b

가설 함수 단순화 하기

위와 같은 진행은 다중 선형 회귀 가설 함수의 일반 공식에서 보는 바와 같이 독립변수가 늘어나는 만큼 공식이 한도 끝도 없이 늘어날 수가 있다. 그래서 이 공식을 조금 더 

간단하게 표현하기 위해 행렬의 곱을 이용하여 표현할 수 있다. 이 행렬 곱의 성질을 이용하면 b까지도 간단하게 정리할 수 있다. 아래 공식과 같이 표현이 가능한 것이다.

독립변수가 2개인 경우


독립변수가 3개인 경우


b까지 포함시킨 경우


위 방법을 이용하여 가설함수를 다시 구성하면 다음과 같이 간단하게 정리할 수 있게된다. 먼저 두 개의 독립변수를 하나의 벡터로 만들어보자

x_data = [[3.0, 1.1, 3.5, 2.5, 1.9, 2.8, 1.3, 3.3, 2.0, 1.0],
          [3.2, 3.0, 3.6, 2.6, 0.6, 3.5, 2.1, 3.4, 2.8, 2.3]]
y_data = [2.0, 1.3, 2.4, 1.5, 0.6, 2.0, 1.0, 2.0, 1.3, 0.9]

x1_data, x2_data 두개의 변수가 x_data 하나의 변수에 모두 포함되었다. y_data는 그냥 사용하면 된다. 다음은 W1과 W2도 하나로 합쳐보자

W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))
b = tf.Variable(tf.random_uniform([1], -1.0, 1.0))

바뀐 부분은 tf.random_uniform의 첫번째 파라미터가  [1]에서 [1, 2]로 바뀐 것이다. 즉, 하나의 행만 가지고 있던 배열 형태에서 1행 2열 형태의 벡터로 바꾸었다. 

아직까지는 b는 그대로 사용을 하자.


이제 행렬 곱셈을 해보자. 텐서플로우에서 행렬 곱셈을 해주는 함수는 matmul이다.

y = tf.matmul(W, x_data) + b

y = W1 * x1_data + W2 * x2_data + b라는 긴 공식이 y = tf.matmul(W, x_data) + b로 짧아졌다. 새삼 수학적 사고의 대단함을 느끼는 순간이었다.

b까지 단순화 시키려면 다음과 같이 하면 된다.

import tensorflow as tf

x_data = [[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
          [3.0, 1.1, 3.5, 2.5, 1.9, 2.8, 1.3, 3.3, 2.0, 1.0],
          [3.2, 3.0, 3.6, 2.6, 0.6, 3.5, 2.1, 3.4, 2.8, 2.3]]
y_data = [2.0, 1.3, 2.4, 1.5, 0.6, 2.0, 1.0, 2.0, 1.3, 0.9]

W = tf.Variable(tf.random_uniform([1, 3], -1.0, 1.0))
y = tf.matmul(W, x_data)

정말 단순해졌다.

이후 진행은 단순회귀 분석과 동일하다. loss 함수를 정의하고 최적화 방법은 학습속도 0.01인 그래디언트 디센트를 사용한다.

loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

그리고 모든 변수를 초기화하고 세션을 시작한다.

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

일단 10000번 정도 반복을 하면서 1000번 째마다 현재 step과 W와 loss값을 출력하여 보았다. 행렬 곱을 이용하여 식을 단순화 하기 전에는 W1, W2, b의 값이 

각각 별개의 배열로 출력되었으나 식을 단순화 한 후에는 W가 하나의 벡터로 전체 값을 모두 표시해준다. 

for step in range(10000):
    sess.run(train)
    if step % 1000 == 0 :
        print(step, sess.run(W), sess.run(loss))
(0, array([[ 0.77657259,  0.304876  ,  0.05037546]], dtype=float32), 0.10678037)
(1000, array([[-0.0653717 ,  0.31436804,  0.32436851]], dtype=float32), 0.01154841)
(2000, array([[-0.24788393,  0.33180454,  0.3721334 ]], dtype=float32), 0.0072818799)
(3000, array([[-0.28713715,  0.33556959,  0.38239372]], dtype=float32), 0.0070845596)
(4000, array([[-0.29557922,  0.3363795 ,  0.38460028]], dtype=float32), 0.0070754252)
(5000, array([[-0.29739448,  0.33655375,  0.38507465]], dtype=float32), 0.0070749982)
(6000, array([[-0.2977854 ,  0.33659121,  0.38517687]], dtype=float32), 0.0070749833)
(7000, array([[-0.29786775,  0.33659837,  0.38519898]], dtype=float32), 0.007074981)
(8000, array([[-0.29788083,  0.33659986,  0.38520217]], dtype=float32), 0.0070749745)
(9000, array([[-0.29788083,  0.33659986,  0.38520217]], dtype=float32), 0.0070749745)

일단 결과는 위와 같이 나왔는데 이 결과는 비용함수의 결과가 상당히 낮은 값임에도 불구하고 예제 데이터가 있던 문서에서 정리한 결과 값과 상당한 차이를 보인다. 

왜 차이가 나는지 어떻게 이 차이를 줄이도록 조정할 수 있는지는 아직 잘 모르겠다.


문서 상의 결과값 : y(x1,x2) = -0.4503 + 0.3067 * x1 + 0.4589 * x2


정리

이번 포스팅 내용울 3줄 정리해보도록 하자.

  1. 다중 선형 회귀는 단순 선형 회귀에서 독립변수의 수만 늘어난 형태이다.
  2. 가설함수는 y = W1 * x1 + W2 * x2 + ... Wn * xn + b의 형태이다.
  3. 비용함수는 단순 회귀 분석과 동일하다.

적절한 값을 구한다는 목적만 생각한다면 달리 어려운 부분은 없다. 하지만 처음 단순 선형 회귀를 정리할 때 그래프를 보면서 변화를 비교했던 것이 이해를 돕는데 

도움이 되었기에 다중 선형 회귀에서도 그런 방법이 없을까 생각했는데 독립변수 2개까지는 3차원 그래프로 표현을 하는 내용들이 간혹 보였지만 독립변수가 3개 이상인 

경우에는 그래프로 표시 하는 내용을 전혀 찾을 수가 없었다. 일단 크게 중요한 부분은 아니기에 호기심을 누르고 그냥 넘어가도록 하자. 


전체 글은 Jupyter Notebook에서 작성되었으며 전체 Notebook 파일도 아래 첨부한다.

chapter2_multi_regression.py.ipynb


블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


Docker에서 TensorFlow 1.0 업그레이드 하기

최근 텐서플로우가 1.0을 발표하면서 많은 분들이 1.0 업그레이드에 여념이 없다. 이런 시류에 편승해서 나도
Docker 이미지로 설치한 텐서플로우 0.12.1 버전을 1.0.0 버전으로 업그레이드를 해보기로 했다.


사실 뭐가 어떻게 변했는지도 모르고 그냥 남들 하니까 따라한다…ㅠ.ㅠ
게다가 Docker 자체를 잘 모르는 상태에서 달리 뭘 어찌해야 하는지도 잘 모르겠고. 텐서플로우 홈페이지에 가도
그냥 기존 코드를 1.0에 맞게 변경하는 내용만 있고…Installing 항목에는 기존과 달라진 내용은 안보이고…
가장 상투적인 것이 가장 확실하다고 했던가. 그냥 기존 버전 삭제 후 재설치 하기로 했다.


그 과정에서도 많은 시행착오를 거쳤지만 굳이 그 모든 실수를 다 거론할 필요는 없을 것 같아 깔끔한 성공 과정만
정리한다.



텐서플로우 버전 확인

우선 업그레이드를 하고 나서 제대로 업그레이드가 되었는지 확인이 필요하니 먼저 텐서플로우 버전을 확인하는
방법을 확인해보았다. 간단하게 아래 그림과 같이 코드를 입력하면 된다. 이 화면은 jupyter Notebook 화면이다.



오호라 0.12.1 버전이 현재 내가 사용하는 버전이구나. 업그레이드 해야지~


Docker에 설치된 텐서플로우 이미지 확인

콘솔 창에서 아래 이미지와 같이 명령어를 입력하면 현재 설치된 모든 이미지의 목록이 나온다. 나는 이번에 처음
텐서플로우를 설치하면서 Docker를 사용했기에 이미지가 달랑 하나다. 만약 많은 이미지가 있다면 아래의 코드와
같이 필터링 할 수 있다.

docker images | grep tensorflow




Docker에 설치된 이미지 삭제

역시 아래 코드와 같이 입력하면 된다. 

docker rmi [이미지 ID]

그런데…뭔가 에러가 떴다. 정지된 컨테이너에서 이 이미지를 사용하고 있단다. 정지했으면 좀 풀어주지…-.-




Docker에서 컨테이너 조회 및 삭제 그리고 다시 이미지 삭제

아래와 같이 입력하면 현재 프로세스로 떠있는 모든 컨테이너의 목록을 확인한다.

docker ps -a



나는 현재 3개의 컨테이너가 프로세스로 등록되었고 모두 정지된 상태이며 사용하는 이미지는 모두 텐서플로우다.
가차없이 모두 죽여버리자!

docker rm $(docker ps -a -q)



다시 한 번 docker ps -a 명령을 실행하면 이제 목록에는 아무 것도 보이지 않는다.
이어서 바로 다시 한 번 이미지를 삭제해보자. 아래 그림과 같이 삭제 과정이 표시된다.





텐서플로우 이미지 설치

일단 1.0.0을 설치해야 하니까 뒤에 태그를 명시적으로 지정해주어야 할 것 같아서 아래와 같이 명령어를 입력했다.

docker run -it -p 8888:8888 -p 6006:6006 gcr.io/tensorflow/tensorflow:1.0.0-devel

음…그런데 이거 이미지 다 설치되고 나면 자동으로 jupyter가 실행되어야 하는데…이상하게 shell로 들어가버린다.
뭐냐? 이러면 나 흔들린다…ㅠ.ㅠ 여기서 뭘 어째야 하는거냐…?



일단 당황하지 않고~ 텐서플로우 홈페이지에 있었던 것처럼 다시 한 번 해본다.

docker run -it -p 8888:8888 -p 6006:6006 gcr.io/tensorflow/tensorflow

아하~! 별다른 태그를 주지 않으니까 자동으로 가장 최신의 버전이 설치되는군~
그리고 바로 Jupyter가 실행되면서 로그인에 필요한 토큰과 함께 Jupyter URL이 표시된다.



docker images를 통해 이미지 목록을 다시 한 번 조회해보면 2개의 이미지가 보인다. 위에 보이는 것은
나중에 설치한 이미지이고 아래 보이는 것은 1.0.0-devel 태그를 붙여 설치한 이미지이다.




최종 확인

마지막으로 Jupyter Notebook을 하나 만들어 다시 한 번 버전을 확인해보자.




이렇게 뭐로 가도 서울로 가면 된다고 어찌어찌 텐서플로우 1.0.0을 설치하였다~

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

머신러닝을 위한 기초 수학 #2

지난 포스팅에서는 선형회귀분석의 가설 함수에 들어있는 개념인 기울기와 절편에 대해 알아보았다.
이번 포스팅에서는 비용함수와 관련하여 ∑ 연산과 제곱함수의 U자 형태 그래프로부터 경사하강법을 이용하여
비용의 최솟값을 찾아내는데 필요한 미분에 대해서 알아보도록 하자.


∑ 연산

∑는 특정 범위 내에 있는 일련의 수들의 합을 표시하는 기호이다. 이 기호를 이용한 수식의 각 항을 보면 아래
그림과 같다.


시그마 기호를 기준으로 밑에는 변화하는 값을 표현할 문자(보통 i나 k를 사용)와 그 시작값을 등호로 연결하여
표시한다. 위 그림과 같이 i = 1이라고 표시하면 i라는 기호는 1부터 시작인 것이다. i = 10이라고 한다면
당연히 i가 10부터 시작된다는 의미이다. 다음으로 기호 위에는 밑에 표시한 기호의 마지막 값을 표시한다. 
이 때는 기호는 생략하고 값만 표시한다. 위 이미지와 같이 문자를 표시하게 되면 임의의 수를 의미하는 것이고
숫자를 표시하면 정확히 그 숫자까지 이다. 간단하게 몇가지 예를 보자.


A는 i가 1부터 50번 째까지 일반항에 해당하는 값들을 더하고 B는 50번 째부터 100번 째까지, 그리고 C는 
100번 째부터 임의 수 m까지 더하는 것이다.


마지막으로 가장 중요한 일반항이며 시그마 기호의 오른쪽에 표시한다. 이 일반항은 보통 i에 대한 식으로 
표현되며 i 자체가 될 수도 있다. 만일 i가 1부터 100번 째까지 더해지는 시그마의 일반항이 i 자체일 경우에는
1부터 100까지의 자연수를 더하라는 의미인 것이다. 아래 몇가지 예를 보자


위 각각의 식을 설명하다면 다음과 같다.

A : 1 + 2 + 3 + 4 + … + 47 + 48 + 49 + 50
B : (a^50) + (a^51) + (a^52) + … + (a^98) + (a^99) + (a^100)
C : (a + 100) + (a + 101) + (a + 102) + … + (a + m -2) + (a + m - 1) + (a + m)
D와 E는 같은 의미로 1번 째 a부터 50번 째 a까지를 모두 더하라는 의미이다.


선형회귀분석의 비용함수 복습


지난 포스팅에서 보았던 비용함수의 수식이다. 우선 앞부분만 떼어놓고 보자면 지난 번 포스팅에도 언급했지만
평균을 의미한다는 것을 알 수 있다. 시그마 기호의 밑이 i = 1이고 위가 m이니 i가 1부터 임의의 수 m까지
변하는 동안 어떤 일반항을 모두 더한 후 다시 m으로 나눈 것이니 바로 평균이 된다. 그리고 여기에서의 일반항은
역시 이미 설명한 바가 있지만 y에 대한 예측값 - 실제 y값 즉, 실제값에 대한 예측값의 오차를 제곱한 것이다.


그리고 이 제곱의 역할로 전체 그래프는 U자형 그래프가 나오는데 지난 시간까지 진행하면서 아직도 이해를 못한
부분이 바로 어떻게 이 U자형의 기울기 중 가장 낮은 지점인 0(제곱 함수이니 0보다 작을 수는 없다)에 접근하는가 
하는 것이다. 이 것을 알기 위해 필요한 것이 바로 미분법이다.


함수의 미분

위에 보았던 비용함수는 반복해서 말하지만 오차에 대한 함수이다. 따라서 값이 가장 작을수록 바람직하다.
다시 말해 전체 수식 중 H(x(i)) - y(i)가 0에 가까울수록 이 함수는 가장 작은 값을 갖게 되는 것이다.
이 것은 U자형 그래프의 가장 오목한 곳이며 바로 y축의 값이 0이되는 지점이다.

참고 : H(x(i))는 선형회귀분석 가설함수의 결과이다. 즉 예측 값인 y이다.


사람의 눈으로는 쉽게 이 지점을 발견할 수 있지만 우리는 컴퓨터에게 이 지점을 찾도록 해야 하며 그러기 위해서는
수학적으로 그 지점을 찾아낼 수 있도록 컴퓨터에게 알려주어야 한다. 그 방법 중에 하나가 바로 기울기를 이용
하는 것이다. 그래프를 봤을 때 x축이 0인 지점에서는 경사가 이루어진 부분이 없으므로 기울기가 0이다. 바로
이 기울기가 0인 지점을 찾으면 되는 것이다. 게다가 우리는 이미 기울기를 어떻게 구해야 하는지도 알고있다.


그런데…
우리가 기울기라 할 때에는 전체 그래프의 어느 지점에서 어떤 범위의 값을 취하더라도 항상 동일한 값이
나와야 한다. 아래 2개의 그래프를 보면서 살펴보자


왼쪽의 직선 그래프를 보면 x1과 x3은 시작점이 서로 다르다. 더군다나 x2 - x1의 값은 x4 - x3의 값보다 작다.
하지만 x1 ~ x2 위치에서 측정한 기울기는 x3 ~ x4 위치에서 측정한 기울기와 동일하다. 하지만 곡선으로 된
그래프의 경우에는 사정이 다르다. 측정하는 지점에 따라 기울기가 다르다. 오른쪽 그래프를 보게 되면 x와 x1의
위치는 다르지만 x ~ x’의 간격과 x1 ~ x’1의 간격은 동일하다. 그러나 직관적으로 보아도 기울기는 정 반대로
표현될 수 있다. x ~ x’에서의 기울기는 x가 증가함에 따라 y는 감소하는 -값의 기울기를 갖는 반면 x1 ~ x’1
에서의 기울기는 x가 증가함에 따라 y도 증가하는 +값의 기울기를 갖는다. 더 어려운 것은 x2 ~ x’2인데 이
범위에서는 양의 값의 기울기와 음의 값의 기울기가 동시에 보인다.


그렇다면 이번에는 x ~ x’ 사이를 반으로 갈라보면 어떨까. 이 그래프는 곡선이기 때문에 나누어진 각각은 다시
서로 다른 기울기 값을 갖게 될 것이다.


이렇게 곡선에서는 그래프 전체를 일관되게 표현해줄 기울기가 존재하지 않는다. 역으로 말하면 무수히 많은 
기울기가 존재한다. 그럼에도 불구하고 우리는 이 곡선의 그래프에서 기울기가 0인 지점 즉, 그래프에서 최소의
값(혹은 다른 상황이라면 최댓값이 될 수도 있다)을 찾아내야 하는 것이다.


그래서 사용하는 것이 바로 미분(微分)이다. 한자의 뜻 그대로 말하자면 아주 작게 나눈다는 것이다. 앞서
설명한 바와 같이 곡선에서는 무수히 많은 기울기가 존재하는데 이는 곧 x의 변화량이 달라질 때마다 각각의
시점에서의 기울기가 다르다는 말이다. 이러한 이유로 특정 지점에서 x의 변화량이 가장 작을 때의 기울기를
구하는 것이 바로 미분이다.


하지만 여기에서도 문제는 발생을 한다. 우리의 좌표계를 실수(實數)계로 놓고 보면 최소값은 무한히 생길 수
있다. 그렇다고 0이되면 안된다. 기울기라는 것이 변화량간의 관계이기 때문에 변화량이 0이라는 것은 하나의
점을 의미하는 것이고 점에 대한 기울기는 있을 수가 없기 때문이다. 여기서 다시 알아야 할 개념이 바로 극한이다.
극한이란 어떤 수가 되면 안되지만 그 수에 가장 가까운 수를 찾기 위한 일종의 편법이라고 할 수 있다(이 때 그
수에 가장 가까이 다가간다고 하는 것을 수렴이라는 용어로 표시한다).

극한 기호 : ∆𝑥는 x의 변화량을 의미하며 →0은 0으로 수렴함을 의미한다. 기호 lim은 limit를 의미한다.



By Brnbrnz - 자작, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=43249235


이와 같이 x의 변화량이 0으로 수렴할 때의 기울기를 찾아내는 것이 미분이고 이 미분을 이용하여 오차 함수의 
U자형 그래프로부터 최소값을 찾아낼 수 있는 것이다.
미분에 대한 추가적인 수학적 설명은 링크로 대신한다.


http://terms.naver.com/entry.nhn?docId=2073828&cid=47324&categoryId=47324

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

머신러닝을 위한 기초 수학

웬만하면 가급적 수학적인 지식은 상식적인 차원에서 알고 넘어가고자 했으나 아무래도 무리인 것 같다.
일단 기본적으로 필요한 수학적 지식을 그 때 그 때 정리하고 넘어가도록 하자. 오늘은 지난 글에서 정리한
두 개의 함수 중 선형회귀분석의 가설함수를 이해하는데 필요한 수학적 지식인 기울기와 절편에 대해 정리를 
해보고자 한다.


이 글은 어디까지나 나와 같이 수학적 지식이 모자라는 사람을 위한 것이며 따라서 수학적 증명같은 것은
없다 오로지 직관이 있을 뿐…-.-


직선의 기울기

기울기는 우리가 흔히 아는 바와 같이 직선이 얼마나 기울어져있는가 하는 정도이다. 일상 생활에서는 일반적으로
각도로 표시를 하지만 여기서는 수학적 계산이 필요하므로 수학적인 부분만 살펴보자. 아래 그림을 보자


높이를 조절할 수 있는 유아용 미끄럼틀을 생각해보자. 직관적으로 보아 A 미끄럼틀이 B 미끄럼들보다 가파르다.
즉 기울기가 크다. 역으로 B는 A 보단 완만하다. 조금 더 수학적으로 살펴보면 기울기가 클 경우에는 아래 평면인
x의 길이보다 높이인 y이가 크고 반대로 기울기가 작을 경우에는 x의 길이가 y의 길이보다 크다. 즉, 기울기라는
것은 수평의 선 x와 높이 y의 관게에 의해서 결정되며 이 관계에 의하면 바로 위에 본 것처럼 y가 커지면 기울기도
커지고 y가 작아지면 기울기는 작아지는 관계다. 즉 기울기를 m이라고 하면 이 기울기는 다음과 같이 수학적으로
표현할 수 있다.


x는 분모이고 y는 분자이기 때문에 x가 작아질수록 혹은 y가 커질수록 기울기 m은 커지고 그 반대가 되면 기울기는
작아진다. 여기서 또 한가지! 아무리 수포자라도 양 변에 같은 값을 곱하면 여전히 양 변의 값은 같다는 것 정도는
알 것이다…-.- 양 변에 분자인 x를 곱하면 수식은 다음과 같이 바뀐다.


어렴풋이, 하지만 쉽게 잊혀지지 않을만큼 기억나는 1차 방정식의 공식이다. 이제 그림도 수학적으로 바꿔보자.
미끄럼틀이 아닌 x축과 y축으로 이루어진 평면 좌표가 있을 때 이 좌표상의 한 직선(파란색 직선)의 기울기는
y의 변동량을 x의 변동량으로 나눈 값 즉, (y2 - y1) / (x2 - x1)이 되는 것이다.

절편

위에 설명한 기본 1차함수의 경우 x가 0이면 곧 값도 0이된다. 즉, 이 직선은 좌표 평면에서 항상 x축과 y축이
만나는 원점을 지나는 직선이 되는 것이다. 하지만 때로는 원점을 지나지 않는 직선도 있을 것이다. 


현실의 예를 들어보자. 마침 최근 넷마블의 100억 인센티브가 이슈이니 회사의 총 이익과 직원의 총 급여의 관계를 
그래프로 나타낸다고 생각해보자. 총 급여는 기본급 + 인센티브다. 회사의 총 이익을 x, 직원의 총 급여를 y로 본다면
x가 증가함에 따라 직원의 총 급여는 증가할 것이다. 하지만 회사의 이익이 0이 되더라도 직원의 총 급여는 0이 되지 
않는다. 회사가 공식적으로 파산을하지 않는 한 기본급은 지급을 해야 하므로 매출이 0이더라도 y축은 0이 아닌
기본급을 표시하는 위치에 있을 것이다. 그러다가 이익이 -로 돌아서면 어느 순간 직원의 급여를 지급하지 못하게
되어 결국은 x축인 직원의 총 급여도 0이 될 것이다.


이렇게 평면 좌표상의 어떤 직선이 원점을 지나지 않는 경우 이 직선이 y축과 만나는 점을 y절편, x축과 만나는 점을
x절편이라고 한다. 


이 것은 다시 말하면 기본적인 1차 방정식에서 x에 0을 대입했을 때의 y의 값은 y절편, y에 0을 대입했을 때의 
x의 값은 x 절편으로 볼 수 있는 것이다. 그래서 1차 방정식은 다음과 같이 확장된다.


절편은 다른 의미로 같은 기울기의 직선이 상하 또는 좌우로 얼마나 움직였는지를 나타내주기도 한다.


선형회귀분석 가설함수 복습

우선 선형회귀분석의 가설함수를 다시 한 번 보자.


이전에 알아본 내용과 오늘 학습한 내용으로 알 수 있듯이 서로 관계가 있는 두 변수 x(총 이익)와 y(총 급여)를 
분석해보고 이 두 변수 사이의 관계를 밝혀내기 위한 기법이 선형회귀분석이다. 학습을 위해 만들어낸 데이터가
아닌 현실의 데이터라면 웬만해서는 완전한 직선으로 나타나는 경우는 거의 없을 것이다. 따라서 그러한 현실
데이터를 가장 근접하게 표현해줄 수 있는 직선 즉, 1차 방정식을 찾아내는 것이 바로 선형회귀분석의 목적이다.


우리가 학교에서 수학을 배울 때는 x 또는 y의 값을 구하는 것이 보통이었다. 하지만 현실의 상황을 분석하는
경우 x와 y는 이미 알고 있는 데이터들이다. 이런 상황에서 x와 y가 만나는 많은 점들 중 가장 많은 수와 근접한
직선을 찾아내는 것이 목적인 바 결국 이 것은 직선의 기울기를 알아내는 것이라 할 수 있다. 결국 위의 공식에서
x와 y는 이미 우리가 알고 있는 값이고 우리가 찾아야 할 것은 W인 것이다.


한편 단순히 기울기만을 가지고 찾는다는 것은 불가능한 것이 기울기가 같은 직선은 무수히 많기 때문에 단순히
기울기만 같다고 해서 데이터를 가장 잘 표현해준다고 할 수는 없는 것이다. 직선을 위나 아래로 움직이면서
x와 y가 만나는 점들과 가장 오차가 적은 위치를 찾아야 할 것이다. 이 것이 바로 공식의 b에 해당하는 값이고
한쪽으로 치우쳤다는 의미에서 편향이라는 용어로도 사용된다.


마지막으로 정리하자면 선형회귀분석을 학습시킨다는 의미는 x 변수들과 y 변수들의 어느정도 비율로 관련이 
있는지(기울기)를 알아내고 그렇게 알아낸 기울기의 직선과 실제 데이터와의 오차를 절편을 이용해서 보정하는 
작업이라 할 수 있겠다.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

선형회귀분석과 관련된 2가지 함수의 의미 정리

이 글은 홍콩 과기대의 김성훈 교수님의 유튜브 강좌를 보고 제 나름대로 조금 더 이해를 하려고 풀어서
정리해본 글입니다.


  • 선형회귀분석 가설함수(Hypothesis function)

선형회귀분석의 기본적인 가설 함수 (Hypothesis function). 기존 데이터를 가장 잘 표현하는 
직선을 결정하는 함수이며 이 직선은 W와 b를 변화시켜가면서 찾을 수 있다. 바로 이 최적의 W와 b를
찾는 것이 선형회귀분석의 목적이다. 그리고 이렇게 찾아진 W와 b를 함수에 대입하게 되면 기존의
데이터가 아닌 새로운 x값이 나타났을 때 그 새로운 x에 대한 y값을 예측할 수 있게 되는 것이다.



이 과정에서 가장 중요한 것은 기울기를 나타내는 W값이다. 즉, 전체적인 데이터가 그래프 상에서
어떤 모양(어떤 기울기의 직선형 그래프)과 가장 유사한가 하는 것을 확인하는 일이며 b는 그렇게
예측된 직선과 실제 데이터간의 오차를 보정해준다고 생각하면 될 것이다.

W의 역할b의 역할



  • 선형회귀분석 비용함수(cost function)



선형회귀분석에서 가장 적절한 W와 b의 값을 찾는 과정에서 변화하는 W와 b의 값을 검증하는 함수.
검증은 아주 상식적이며 단순하다. 우리가 예측할 직선상의 y값(가설함수의 결과인 y값)과 실제 
좌표상의 y값 (우리가 이미 알고있는 실제의y의 값)의 오차가 작으면 작을수록 좋은 값인 것이다.
그래서 우선 H(x(i)) - y(i)라는 공식이 도출된다(H(x(i))는 가설 함수의 결과를 의미한다). 
즉, 이 비용 함수는 ‘오차’에 대한 함수인 것이다.



그런데 이 값을 그냥 사용한 것이 아니라 제곱을 했다. 이 것은 그냥 사용하게 될 경우 이 값은
음수와 양수로 그 부호가 달라진다. 위 그림에서 가설함수인 빨간 선 상의 H(x(i))값에서 a의 y값을
뺄 경우에는 그 결과가 음수가 나오고 b의 값을 뺄 경우에는 양수가 나온다. 각각의 데이터에 대해
이렇게 계산을 한 후 평균을 구하기 위해 합산을 하게 되면 양수와 음수가 상쇄되어 계산에 어려움이
생길 것이다. 따라서 제곱을 함으로써 양의 정수로 만들어주는 것이다. 여기까지 만들어진 공식이
(H(x(i)) - y(i))^2이다. 그리고 이렇게 제곱을 할 경우 결과 값이 크면 클수록 제곱 값은 
기하급수적으로 커지기 때문에 일종이 패널티 역할을 하게 되는 것이다.

이후 공식은 중고등 수학책을 잠시 열어보면 쉽게 알 수 있다. 시그마(∑) 기호는 밑에 있는 i가 1부터
특정 수인 m까지 늘어나는 동안 기호 우측에 있는 계산식을 모두 더하라는 의미이고 수식의 가장
앞의 1/m은 m개의 계산 결과를 더한 것을 다시 m으로 나눈 것이니 바로 평균의 의미가 되는 것이다. 
즉, 이 cost 함수는 예측값과 실제 값의 차(오차)를 제곱한 모든 값의 평균이 되는 것이다.

이렇게 구한 cost 함수의 식을 그래프로 그려보면 다음과 같은 곡선이 만들어진다. 즉, 이 함수는 제곱에
대한 함수이기 때문에 H(x(i)) - y(i)의 절대값이 크면 클수록 cost가 커지고 절대값이 작을 수록 cost가
0에 가까워지는 U자 형태의 그래프로 표현된다.



블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


선형회귀분석 - 문돌이 식으로 해석하기~ 2


일단 지난 시간에 주저리주저리 별거 없이 지껄이다가 글이 너무 길어졌다.
오늘은 Jupyter에서 직접 코드를 수정하면서 ‘텐서플로우 첫걸음’에 실린 선형회귀분석 예제를
조금 더 상세하게 살펴보도록 하겠다. 일단 원래의 소스는 이전 글에서 Jupyter에 업로드 하는
방법을 설명하였으니 그대로 따라하면 볼 수 있을 것이다. 여기서는 데이터를 바꿔서 사용할 예정이다.


데이터 적용하기


지난 글에서도 밝혔듯이 데이터를 찾는다는 것이 결코 만만한 일이 아니었다. 그나마 찾은 데이터도
이게 적절한 데이터가 맞는지조차 모를 지경이다…ㅠ.ㅠ 일단 데이터 중 변경된 부분은 ‘서울시 시민
행복지수 통계’ 데이터가 10점 만점을 기준으로 한 점수 데이터인데 이 것을 백분률 데이터인
‘서울시 소득대비 부채현황 통계’  데이터와 맞추기 위해 백분률 데이터로 바꾸었다. 이렇게 바꾸면
두 데이터간에 상관관계가 없음(…-.-)을 명확하게 알 수 있다.


일단 Jupyter는 물론 Python 자체도 왕초보 신세이기에 파일로부터 직접 데이터를 읽어오는 방법은
모르겠고 그냥 무식하게 vectors_set에 하드코딩으로 데이터를 입력했다. 그리고 기존에 난수를
통해 데이터를 생성한 for문은 모두 주석처리 하였다. 그리고 이 새 데이터로 만든 그래프는 아래
그림과 같다. 참고로 수정 후 실행을 위해서는 상단 툴바의 >| 모양의 버튼을 클릭하면 된다. 그리고
plt에서 오류가 발생하므로 다음 코드를 plt 사용 전에 추가해주자.


import matplotlib.pyplot as plt


딱 봐도 두 데이터 간에 아무런 관계가 없음을 알 수 있다. 소득 대비 부채가 많건 적건 행복할 사람은
행복하고 아닌 사람은 아닌 것이다…-.- 그나저나 밑바닥의 점 2개는 뭔지…-.-


하지만 이 글을 통해 내가 정리하고 싶은 것은 이러한 과정에서 어느 곳에 어떤 값을 사용해야 제대로
분석이 되는가 하는 것을 밝히는 것이다.


본격적으로 텐서플로우 가동!!!


선형회귀 분석을 하기 위해 아래 이미지와 같은 과정을 거친다. 우선은 그러려니 하고 봐두자.


그리고 코드는 수정하지 말고 앞서 설명한대로 >| 버튼을 눌러 각 코드를 run 시켜보도록 하자.
아…마지막의 ‘In [9] :’라고 되어있는 부분의 소스 코드 중 plt.xlim와 plt.ylim는 적절한 범위로
수정을 해주자. 그냥 처음 이미지에서와 같은 범위로 주면 될 것이다.


.
.
plt.xlim(15,60)
plt.ylim(50,62)
.
.


그리고 결과를 보면 다음과 같다.


뭔가 좀 이상하다…원래 예제에는 그래프와 함께 파란 선이 그려졌었는데…어디갔나???


이제 코드를 하나하나 보면서 문제의 원인을 찾아보도록 하자.


처음 코드이다.


import tensorflow as tf


설명할 것도 없이 텐서플로우 라이브러리를 import하여 tf라는 이름으로 사용하겠다는 것이다.
다음은…


W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b


이제 슬슬 설명할 것들이 생긴다.
W와 b는 이전 글에서 언급한대로 y = W * x + b 방정식에서의 그놈들 맞다. 값을 변경해가면서
최적의 값을 찾아야 하므로 범위를 지정하고 있다. 여기 사용된 텐서플로우 API들을 간단히 설명하면


Variable : 이름에서 짐작할 수 있듯이 텐서플로우에서 사용할 변수를 만든다.
random_uniform : 특정 형태의 배열을 만든다. 첫 번째 파라미터는 배열의 차수(여기서는 [1]이므로
1차원 배열이다)이고 두 번째와 세 번째는 이 배열에 들어갈 값의 범위이다. 즉 -1.0에서 1.0 
사이의 임의의 실수가 배열에 들어가는 것이다.
zeros : 역시 배열을 만드는데 이름에서도 알 수 있듯이 0으로 채워진 배열을 만든다. 0으로 채우니까
당연히 범위를 지정하는 파라미터는 필요없고 배열의 차원을 정하는 파라미터만 받는다. 


즉, W는 -1.0 ~ 1.0 사이의 임의의 실수를 바꾸어가면서 그리고 b는 0값을 유지하면서 가장 그래프와
일치하는 값을 찾아내는 것이다.


다음 코드를 보자


loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)


여기서부터는 살짝 어려워진다. 일단 선형회귀분석에서 최적의 값을 찾기 위한 알고리즘을 알아야 한다.
일단 API를 살펴보고 그 이름으로부터 미루어 짐작해보자.


square : 제곱을 해주는 함수
reduce_mean : 파라미터로 받은 배열의 평균을 구하는 함수
tran : 최적화를 위한 학습을 담당하는 객체
GradientDescentOptimizer : 경사하강법이라는 학습 알고리즘을 실행하는 함수. 파라미터는 학습
계수로 변경할 값(여기서는 W)을 얼마나 정밀하게 변경할 것인지를 결정하게 된다. 예를들어
이 값이 작으면 0.01씩 빼면서 변경하고 이 값이 크면 1씩 빼면서 변경하는 식이다.
minimize : 이름 그대로 최소값을 가져오는 함수


자 이제 정리를 해보면 loss는 y(모델 방정식을 통해 만들어낸 새로운 값)와 y_data(그래프상의
원래 y값)간의 오차를 제곱한 후 그 평균을 가져온다. 그리고 경사하강법이라는 알고리즘을 수행하는
optimizer를 만들고 이 optimizer를 통해 loss의 최소값을 가져오는 것이다. 즉, 추측한 값과
원래 값의 오차를 제곱한 후 그 평균 중 가장 작은 값을 가져오는 방식으로 학습을 진행하는 것이다.


쓰는 놈도 뭔소린지 모르니 그냥 그러려니하고 넘어가자. 어차피 중요한 내용은 마지막에 3줄 요약
할테니까…-.-


다음 코드를 보면…


init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)


이전까지의 코드는 연산에 필요한 데이터를 만든 것이고 실제 연산은 Session을 생성하여 이로부터
run을 실행할 때 비로소 연산이 시작되는 것이다. 즉, 위의 코드는 모든 변수를 초기화 하는 작업이다.


마지막 코드를 보면 다음과 같다


for step in range(8):
sess.run(train)
print(step, sess.run(W), sess.run(b), sess.run(loss))
산포도 그리기
plt.plot(x_data, y_data, 'ro')
직선 그리기
plt.plot(x_data, sess.run(W) * x_data + sess.run(b))
x, y 축 레이블링을 하고 각 축의 최대, 최소값 범위를 지정합니다.
plt.xlabel('x')
plt.xlim(15,60)
plt.ylim(50,62)
plt.ylabel('y')
plt.show()


여기서 비로소 학습을 실행하고 그 결과를 그래프로 표시하게 된다. plt를 사용한 부분은 모두 그래프를
그리기 위한 코드이고 실제 학습을 진행하는 것은 sess.run(train)이다. 이 학습을 총 8번 수행한다.


문제점 찾기


자…이제 대충은 흐름을 알것 같은데…도대체 왜 학습을 통해 나온 결과가 그래프에 표시되지 않는
것일까?


여기서 중요하게 생각해야 하는 부분은 아직까지는 컴퓨터가 모든 것을 스스로 알아서 처리하는 단계가
아니라는 점이다. 즉, 어느정도 사람이 도움을 주어야 한다. 그렇다면 어디에서 어떻게 도움을 주어야
할까?


우선 떠오르는 부분은 바로 W와 b의 예상값의 범위를 만드는 부분이다. 우리는 이미 그 이전에
원래의 데이터로 생성된 그래프의 형태를 알고 있다. 즉, 그 그래프에 그려진 데이터의 대부분을
관통하는 직선의 기울기와 y절편을 어느 정도는 파악할 수 있다는 것이다. 따라서 무작정 아무생각
없이 W와 b의 범위를 정하기 보다는 원래 그래프와 근사한 값으로 범위를 좁혀주는 것이 텐서플로우가
작업하기 수월할 것이다.


여기서 내가 사용한 데이터 분포를 보니 거의 수평에 가까운 직선이다. 즉 기울기는 0에 가깝고 y절편은
60에서 61 언저리다. 따라서 나는 코드를 이렇게 바꾸었다. 기울기가 0에 가까우므로 W는 0으로 
채워진 배열을 주었고 y절편인 b는 59~61 사이의 임의의 실수가 들어가도록 하였다.


W = tf.Variable(tf.zeros([1]))
b = tf.Variable(tf.random_uniform([1], 59.0, 61.0))
y = W * x_data + b


그리고 다시 한번 실행을 해보자


아…여전히 예측 값을 표현할 파란 선이 안보인다. 그런데 이상한 점 하나를 발견했다. 코드에서 print로
각 값들을 찍고 있는데 그 출력 결과를 한 번 보자. 좌측부터 각각 학습 수행 횟수, W값, b값, 그리고
오차의 제곱 평균의 최소 값이다. 특히 눈여겨봐야 할 것은 제대로 학습이 진행된다면 이 loss값은

적절한 범위 내에서 점점 작아져야 한다.


print(step, sess.run(W), sess.run(b), sess.run(loss))


출력 결과이다.


(0, array([ 33.58777618], dtype=float32), array([ 59.98347092], dtype=float32), 1446895.1)
(1, array([-43044.61328125], dtype=float32), array([-1115.60925293], dtype=float32), 2.3799283e+12)
(2, array([ 55205580.], dtype=float32), array([ 1506647.75], dtype=float32), 3.9146391e+18)
(3, array([ -7.08022108e+10], dtype=float32), array([ -1.93222899e+09], dtype=float32), 6.4390147e+24)
(4, array([  9.08052220e+13], dtype=float32), array([  2.47812089e+12], dtype=float32), 1.0591254e+31)
(5, array([ -1.16459456e+17], dtype=float32), array([ -3.17823849e+15], dtype=float32), inf)
(6, array([  1.49361495e+20], dtype=float32), array([  4.07615184e+18], dtype=float32), inf)
(7, array([ -1.91559007e+23], dtype=float32), array([ -5.22774354e+21], dtype=float32), inf)


loss 값이 지나치도록 급격하게 작아지고 있다. 그러다보니 W와 b값은 그래프에 표현될 범위를 벗어나고 만 것이다.


이 것을 조정해 줄 것이 바로 학습 계수이다(학습 횟수와는 다르다. for문을 통해 8번의 학습을 진행한 것은
학습 횟수이고 학습 계수는 학습할 값의 범위에 대한 정밀도라고 생각하면 되겠다).


그래서 이번에는 다음의 코드를 수정했다.


loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.000006)
train = optimizer.minimize(loss)


이렇게 하고나서야 겨우 파란 선형 그래프를 볼 수 있었다. 보다시피 엄청나게 작은 학습 계수를 주었다.
이렇게 하면 정밀도가 높아지는 대신 좁은 범위를 자주 왔다갔다 해야 하기 때문에 당연히 시간이 오래
걸린다. 물론 지금과 같은 적은 데이터에서는 별 차이를 못느끼지만.


결론


나의 문제는 늘 불필요하게 글이 길어지는 것이다…-.-
사실 내가 늘어놓은 많은 이야기들이 쓸모없는지도 모르겠다. 더 잘 설명해놓은 곳이 바닷가 모래알만큼이나
많을 것이다. 그럼에도 굳이 이 글을 적은 이유는 오로지 한가지 이유에서였다. 자신만의 데이터를 가지고
작업을 하는 것이 최종 목적일진대 적어도 그 과정에서 무엇을 어떻게 조작해야 하는지 정도는 알고 있어야
할 것이기 때문이다. 이 선형회귀분석에서는 W와 b에 대한 적절한 범위를 지정해주는 것, 그리고 적절한
범위의 학습 계수를 설정해 주는 것
이 매우 중요한 일이었다. 사용한 데이터가 영 쓸모 없는 것이 문제는
문제였지만…-.-


W나 b 값은 사전 데이터로 쉽게 범위를 지정할 수 있지만 학습 계수같은 경우 상당한 시행착오를 거쳐야
했다. 적절한 학습 계수를 찾는 공식이 또 따로 있다는데…-.- 그건 차차 배우기로 하자.


일단 선형회귀분석은 여기까지…

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


선형회귀분석 - 문돌이 식으로 해석하기~

지난 번 Docker를 이용하여 텐서플로우를 설치해보았다. 일단 설치는 잘 되었고 부수적으로 Jupyter라는
웹 기반의 Python IDE도 알게 되었다. 그리고 일단 설치를 했으니 공부좀 해보자 하는 마음에 책을 한 권
e-Book으로 구매했다. “텐서플로우 첫걸음(조르디 토레스 지음, 박해선 옮김, 한빛미디어 출판)”이라는
책이었다. 0장 한국어판 서문과 1장 텐서플로우 기본 다지기를 어렵지 않게 넘기고 드디어 2장 에 다다르게


되었다. 2장의 내용은 텐서플로우를 이용하여 선형회귀분석을 하는 것이다.


선형회귀분석…선형…직진하다가…회귀…유턴하는건가…-.-?
아무튼 AI에 관심을 가지면서 많이 들어보던 용어인데 사실 수학 용어에 알러지 반응이 있어 이런 용어들이
나오면 무의식적으로 워프를 해버린다…-.- 그래도 본격적으로 공부를 해보기로 했으니 이제는 그러면
안될 것 같아 조금 자세히 들여다 보려는데…아무래도 텐서플로우 입문서이다보니 설명이 매우 간략하다.
그리고 뒤에서 말해주겠다고 약올리는 부분도 있고…


애초에 어려운 알고리즘에 대한 내용은 우선 피하고 텐서플로우 자체에 대해 알아보자고 계획을 잡았지만
이놈에 호기심이란…내가 만일 판도라였다면 제우스 면전에서 상자를 열어재꼈을 것이다…-.-


그래서…지난 1주일 내내 도대체 선형회귀분석이 뭔지 알아보느라 시간을 보냈다. 그 것을 이번 포스팅에
정리해보고자 한다. 물론 문돌이의 정리이기에 어디로 튈지는 장담 못한다…ㅠ.ㅠ


Jupyter부터…


지난 시간에 다음에는 Jupyter를 좀 알아보자고 했었는데…사실 Jupyter는 IDE 툴이라서 사용법 외에
특별히 설명할 만한 것이 없는 것 같다. 우선 Jupyter 공식 홈페이지와 사용법을 비교적 잘 설명한 블로그
하나를 링크하는 것으로 대신하고 본문 내용 중에 예제 업로드 하는 방법 정도 설명하겠다.


Jupyter 공식 홈페이지 : http://jupyter.org
옥수별님 블로그 : http://sams.epaiai.com/220763757405


예제 업로드 하기


우선 시간 절약을 위해 텐서플로우 첫걸음의 제 2장 예제를 Jupyter에 업로드 하도록 하겠다.
원 저자의 소스도 있지만 번역을 하신 박해선 님의 소스가 좀 더 깔끔하고 Jupyter Notebook
으로 설명도 곁들여져 있어 공부하기 좋다. 


소스 link : https://github.com/rickiepark/first-steps-with-tensorflow


아래와 같은 과정으로 Jupyter에 소스를 업로드 하자.


  1. Jupyter Notebook 목록 우측 상단에 있는 Upload 버튼을 클릭한다.


  2. 파일 선택 창이 나오면 소스코드를 다운로드 받은 위치로 이동하여 선형회귀분석 소스인

    chapter2_regression.py.ipynb를 선택하고 선택 버튼을 누른다.


  3. Notebook 목록에 방금 올린 소스 파일명이 가장 위에 보여지면 그 오른쪽의 파란색 Upload
    버튼을 한 번 더 눌러준다.


  4. 소스 파일의 업로드가 완료되면 아래 그림처럼 기존의 다른 Notebook들과 같은 형태로
    표시된다.


  5. chapter2_regression.py.ipynb를 클릭하면 드디어 소스를 조작할 수 있는 Notebook
    화면이 등장한다.


선형회귀분석 간단 정리


우선 가장 단순하게 시작을 해보도록 하자. 우선 아래 2가지만 알고 가도록 하자.


  • 한 변수(y, 종속 변수)가 다른 변수(x, 독립 변수)에 의해 어떻게 영향을 받는지 분석한다. 
    즉, 두 변수간에 인과관계가 있을 때 유효하다. (예 : 소득 수준과 문화 생활에 지출하는 비용간의 관계)
  • 선형회귀 모델을 표현하는 함수는 y = W * x + b이다.


일단 종속 변수에 대해 독립 변수가 1개인 것을 단순 선형회귀라고 하며 오늘은 이 단순 선형회귀만
다룬다. 선형 회귀라는 이름에서 ‘선형’이 의미하는 것은 분석의 결과가 직선의 형태를 띠고 있기 때문이다.


(회귀라는 용어에 대해서는 다음 링크 참조 : 
http://terms.naver.com/entry.nhn?docId=1625383&cid=42251&categoryId=42262
)


이 것은 두 번째 적은 함수에서도 알 수 있다. 바로 W라는 기울기와 b라는 y절편을 갖는 1차방정식이다.
이는 좌표 평면상에서 직선을 표현하게 된다.


이러한 기본 지식을 가지고 선형회귀분석을 간단하게 요약 설명하면 과거의 데이터를 통해 선형회귀 모델
방정식을 가장 잘 표현하는 W값과 b값을 찾고 이렇게 찾은 W값과 b값을 방정식에 대입하게 되면 나중에
새로운 x가 발견되었을 때 이 방정식을 통해 y의 값을 추측해낼 수 있는 것이다. 또 다른 측면에서는 
y와 x가 관계가 있는지 없는지, 관계가 있다면 얼마나 있는지를 확인하는 목적에도 사용될 수 있다.


아래 우측 그림과 같은 그래프로 표현되는 데이터 집단이 있다면 이 그래프를 가장 잘 표현하는 직선은
오른쪽 그림의 파란 선이 될 것이다. 바로 이 파란 선을 만들 수 있는 W와 b의 값을 찾는 것이 1차 목적이다.
(아래 그림은 텐서플로우 첫걸음의 예제로부터 가져온 것이다.)


실제로 해보기


설명을 하고나서도 뭔소린지 당췌 모르겠다…ㅠ.ㅠ 게다가 예제에서는 아무런 의미도 없는 난수를
통해 생성한 데이터를 가지고 설명을 하니 왜 이런 짓거리를 해야 하는건가 하는 생각조차 든다.
그러니 실생활에서 접할 수 있는 데이터를 가지고 한 번 진행을 해보자


집고 넘어가자!
구글이 텐서플로우같은 훌륭한 라이브러리를 공개하는 것에 대해 매체에서 언급하는 내용 중 2가지만
짚어보자


  1. 알고리즘이나 툴 자체보다는 데이터가 더 중요하다.
  2. 세상에는 많은 업무 분야들이 있고 아무리 구글같은 회사라 하더라도 그 모든 분야를 모두 아우를
    수는 없다. 따라서 각 분야의 전문가들이 텐서플로우를 어떻게 이용하는지 알 수 있는 기회가 된다.


이 이야기가 선뜻 와 닿지 않았는데 이번에 ‘실생활에서 접할 수 있는 자료로 공부해보자’라고 생각하고
진행하는 과정에서 뼈저리게 느꼈다. 적절한 데이터를 찾는 작업이 결코 만만한 작업이 아니었다.
실제로 데이터 사이언티스트들이 이런 일을 하는지는 모르겠지만 분석이 필요한 데이터를 발굴하고
이 데이터사이의 연관성을 찾아내는 작업이 얼마나 어려운 일인지를 새삼 깨다았다.


실생활에서 접할 수 있는 데이터를 찾기 위해 우선 생각한 것이 공공데이터 포털이었다. 사실 일주일 동안
가장 시간을 많이 들인 것이 이 공공데이터 포탈에서 분석이 가능할만한 데이터, 인과관계가 있을 법한
데이터를 찾는 일이었다. 결과부터 말하면 결국 찾지 못했다. 독립 변수로 쓸 데이터와 종속 변수로 쓸
데이터를 찾아야 하는데 시계열의 범위가 맞지 않거나 아예 시계열 데이터가 없거나 한 것들이 많았다.
일단 ‘서울특별시 사회인식 통계’라는 항목에서 몇가지 자료를 추출했지만 겨우 시계열 범위가 일치할
뿐, 구별로 분산된 데이터인데다가 얼핏 봐도 하나의 독립 변수로는 설명이 되지 않는 그런 데이터였다.


그래서…엉망이나마 이번에 사용할 데이터는 아래 2개의 데이터인데 앞서 말한 바와 같이 구별로
측정된 데이터를 구분없이 하나로 통합해서 사용하였다.


‘서울시 소득대비 부채현황 통계’ 

서울시 소득대비 부채현황 통계.xls

‘서울시 시민행복지수 통계’

서울시 시민행복지수 통계_2003.xls

서울시 시민행복지수 통계_2004.xls

서울시 시민행복지수 통계_2005.xls

서울시 시민행복지수 통계_2006.xls


생각보다 글이 길어져 본격적인 분석은 다음 글에 이어서 하도록 하겠습니다~

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^


나…해도 될까?

이것 저것 벌여 놓은 일들이 산더미이지만 AI만큼은 왠지 수박 겉핦기라도 해야 할 것 같다.
그래서 올 2017년 목표 중 하나로 잡고 회사에도 함께 공부할 분들을 모아볼까 하고 동호회
제안도 해놓고 하긴 했는데…과연 내가 해도 될만한 분야인지 부담스럽기만 하다.


그래도…

쇠뿔은 단김에 뽑아야 하고 시작은 반이니 일단 무작정 시작해보기로 했다.
아무래도 지명도가 높고 그만큼 학습자료(특히 서적)가 많은 텐서플로우를 타겟으로 잡았다.
그리고 무작정 텐서플로우 홈페이지로 가서 당장 설치부터 해보기로 했다.


뭐라뭐라 설치 방법도 참 다양하다…-.-
그 중 눈에 띄는 것이 있었으니 바로 Docker 이미지로 설치하는 것이었다.
마침 올해 공부해야 할 것 중 Docker도 있었으니 겸사겸사 죽을 쒀보자는 생각으로 
홈페이지에 적힌대로 따라해보았다. 그나마 Docker는 전에 설치를 해놓은터라 바로
홈페이지에 있는 명령어만 치면 되었다.


docker run -it -p 8888:8888 gcr.io/tensorflow/tensorflow


음…근데 이상하다…뭔가 반응이 있어야 하는데 얘가 그냥 멍때리고 있다.
docker —version등 기초적인 명령어는 반응을 하는데 run을 하게 되면 콘솔에 아무런 
출력도 없고…혹시나 해서 메뉴 막대에 떠있는 Docker 아이콘을 클릭하여 Docker 메뉴를 
열어보니 아니나 다를까 맨 위의 ‘Docker is running’ 앞부분의 표시가 초록색이 아니라 
노란색이다. Docker를 처음 써보는터라 이유도 모르겠거니와 로그같은 것도 어디서 보는


지도 모르겠고…로그가 있기는 한건지…ㅠ.ㅠ


Docker is running 앞의 신호등이 노란색이었다...



이리저리 검색을 하다가 구글에서 다음과 같이 해보라는 계시를 받았다.


  1. Preferences… 선택
  2. 가장 우측 폭탄모양의 Uninstall/reset 선택
  3. Reset to factory defaults 선택
  4. Docker is running의 restart 선택


이렇게 했더니 다행히도 Docker is running 표시가 초록색으로 돌아왔다.
그리고 다시 한 번 docker run… 실행… 그런데 이번에는 콘솔창에 다음과 같은
메시지가 출력되면서 뭔가 진행이 안되는 듯하다…


Unable to find image 'gcr.io/tensorflow/tensorflow:latest' locally


이건 또 뭔가 하고 Ctrl-C를 눌러 취소하고 다시 실행하기를 몇 번…
사실 개발자 별거 아니다. 메시지만 잘 눈여겨보면 대부분의 문제는 해결이 되는
것을…-.- 처음에는 ‘Unable to find image’만 보고 왜 자꾸 이미지를 못찾는다고
하는 걸까 하며 성질만 벅벅 냈는데 찬찬히 보니 마지막에 ‘locally’가 있다…
그래…아직 이미지를 안받았으니 당연히 로컬에는 이미지가 없지…ㅠ.ㅠ 
메시지를 무시하고 잠시 기다려보니 뭔가 된다.^^




마지막에 보면 URL이 하나 나오고 브라우저를 열어서 접속하라고 부추킨다.
시킨다면 시키는 대로 하는 것이 인지상정~ 사파리를 열고 접속을 해보았다. 그러자
다음과 같은 화면이 등장한다.




화면을 보니 상단에 Jupyter라는 로고가 보이고 아래쪽으로는 뭔가 목록이 보인다.
목록을 보니 뭔가 tensorflow라는 이름도 보이고 mnist라는 이름도 보이는 것이
TensorFlow가 설치가 된 것 같긴 한데 도대체 이놈에 Jupyter라는 것은 뭐하는
놈인가…-.- 항목을 하나 클릭해보니 뭔가 영어로 잔뜩 설명이 나오고 Python 코드도
보이고…검색을 해보니 Python, iPython, Notebook 등등의 단어가 보이고…
결국 확인한 내용은 웹 기반의 Python IDE라는 것…


오늘은 여기까지 삽질기를 마치고 다음에는 Jupyter Notebook에 대해서 조금
알아보도록 하자. 아무래도 얘량 친해져야 텐서플로우든 뭐든 할 수 있을 것 같다.


Docker도 Python도 잘 모르는데…앞길이 구만리다…ㅠ.ㅠ

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^