다항 로지스틱 회귀의 이해


처음 텐서플로우 공부를 시작하면서 단순선형회귀에서 다중선형회귀로 넘어가는 과정에서 상당히 혼란스러웠었다.
단순한 1차 방정식의 형태에서 독립 변수가 늘어난 것만으로도 엄청나게 골머리를 싸매야 했다. 그리고 이제 이항
로지스틱회귀에서 다항로지스틱회귀로 넘어가려고 한다. 이번에도 역시 기대를 저버리지 않고 뇌이랑이 메워질만큼
알 듯 모를 듯한 수식들을 붙들고 씨름을 하게 만들었다.


설명은 단순한데 그 단순한 것을 제대로 이해하기 위해서는 상당한 공을 들여야 하는 것이 이 바닥인가보다…ㅠ.ㅠ


준비운동 - sigmoid 복습


먼저 이전 포스팅인 이항로지스틱회귀에서 언급되었던 식 몇개를 되살려보자. 거기에는 두 가지 개념이 등장했는데
바로 오즈 비(ODDS Ratio)와 이 오즈 비에 자연로그를 씌운 로짓(logit) 변환이었다. 


오즈 비는 어떤 일이 발생하지 않을 확률에 대한 발생 확률의 비로 발생 확률을 p로 본다면 전체 확률 1에서 발생
확률 p를 뺀 것이 바로 발생하지 않을 확률 1-pr가 되므로 오즈 비는 아래와 같은 식으로 표현된다.




그리고 여기에 자연로그를 취해 아래와 같이 로짓 변환을 마무리 한다. (왜 이런 짓을 하는지는 이전 글인 ‘로지스틱
(Logistic) 회귀 함수 살펴보기’
를 참조하라~)




이런 과정을 통해 다음과 같은 등식을 얻을 수 있었다.




이 등식을 확률 p에 대해 정리하게 되면 우리가 익히 들어왔던 시그모이드 함수의 식이 나타난다.


메인이벤트 - 다항로지스틱회귀


이제 본격적으로 다항로지스틱회귀에 대해 살펴보도록 하자. 앞서 살펴본 로지스틱회귀는 결과가 1 또는 0이 나오는
이항로지스틱회귀였다. 이항로지스틱회귀에 대해 조금 더 부연을 하자면 실제로 이항로지스틱회귀를 통해 나오는
결과 값은 0 ~ 1 사이의 실수이다. 그렇기 때문에 기준점을 하나 두어 그 기준점보다 크면 1로 기준점보다 작으면
0으로 처리하는 것이다. 이 때 이 기준점을 cutoff value라고 하며 0 ~ 1 사이의 값에 대한 기준점이브로 보통
cutoff value는 0.5를 사용한다고 한다.


다항로지스틱회귀의 경우 결과 항이 3개 이상인 경우이다. 현실적인 예를 들자면 최근 대선을 앞두고 쉴새없이 여론
조사 자료들이 뉴스에 보도된다. 이 때 문-안 2자 대결에 대한 여론조사가 이항로지스틱회귀라면 5명의 후보자들에
대해 모두 조사를 하게 되면 다항로지스틱회귀가 되는 것이다. 이 다항로지스틱회귀 역시 결과는 확률 값이며 따라서
각각의 항에 대한 모든 결과으의 합은 1( = 100%)이 된다.


다항로지스틱을 유도하는 식은 이항로지스틱의 경우와 동일하다. 각각의 항에 대해 이항로지스틱을 수행한 후 이 
결과를 일반화 하여 식을 만들게 된다.


대통령 선거를 예로 조금 더 상세하게 들어가보자. 다항로지스틱회귀를 통해 분류를 해야 하는 지지자들은 문, 안, 홍,
유, 심의 5 부류이다(다행이 후보자들 성이 다 다르다…^^). 이 경우 각각에 대해 이항로지스틱을 수행한다 함은 바로
다음과 같다(편의상 이하 순서대로 A,B,C,D,E로 부르도록 하자).


  1. A를 지지하는 사람과 지지하지 않는 사람의 이항
  2. B를 지지하는 사람과 지지하지 않는 사람의 이항
  3. C를 지지하는 사람과 지지하지 않는 사람의 이항
  4. D를 지지하는 사람과 지지하지 않는 사람의 이항
  5. E를 지지하는 사람과 지지하지 않는 사람의 이항


그런데 여기서 약간의 문제가 있다. 이항일 경우에는 오즈 비가 쉽게 나왔다. 경우의 수가 p 또는 1-p로 쉽게 식을
만들 수 있었는데 경우의 수가 많다보니 우리같은 수포자는 여지없이 멘붕에 빠지고 만다…ㅠ.ㅠ


엄밀하게 말하자면 이런 경우에는 각각의 결과를 서로간에 비교를 해야 한다. A와 B를, A와 C를… B와 C를…C와 D…
하지만 이렇게 하자니 결과 항이 많아질수록 복잡해지고 일이 커진다. 누누히 강조하거니와 수학은 곧 단축이 
미덕이다…^^ 현명하신 우리의 수학자들께서는 여기서도 지혜를 발휘하셨다. 그 방법인 즉슨…기준이 되는 하나의
항을 정하고 그 항에 대한 나머지 항들 각각의 비율을 계산하도록 한 것이다. 즉, 편의상 맨 마지막의 E를 기준으로
했다고 가정하면,


  1. A에 대해 p(A) / p(E)
  2. B에 대해 p(B) / p(E)
  3. C에 대해 p(C) / p(E)
  4. D에 대해 p(D) / p(E)


E에 대한 E의 확률은 의미가 없으니 생략하게 되어 총 4개, 다시 말해 전체 항의 갯수를 K개라고 하면 K-1개의 식이
만들어진다. 그리고 이항로지스틱에서와 마찬가지로 자연로그를 취하면 다음과 같이 표현할 수 있다.


이와 같은 식을 항의 갯수가 K일 경우로 일반화 시켜보면 다음과 같이 표현할 수 있다.




잠깐 되돌아가서 보면 특정 항에 대한 각 항의 비만을 계산했는데(A와 E, B와 E, C와 E, D와 E) 그럼 다른 항들
간의 비는 어떻게 확인해야 할까? 매우 간단하다. 이미 E에 대한 각 항목의 비를 구해놓았기 때문에 간단한 로그
법칙으로 구할 수 있다. 만일 B에 대한 A의 비를 구한다면 아래 식과 같이 하면 된다.


파이널 라운드 - sigmoid에서 softmax로


늘상 수학적인 문제에서 길을 잃는다. 이 포스팅을 쓰기 시작한 것이 4월 14일 경이었는데 바로 전 섹션까지가 그 때
쓴 글이다. 이렇게 써내려가면서 나는 내가 sigmoid 함수와 softmax 함수의 상관 관계를 이해하고 있다고 오해했다.
다항로지스틱회귀를 통해 나온 식으로 자연스럽게 softmax 함수가 유도될 것으로 생각했는데 나의 착각이었다.
가장 근접하게 접근한 것이 위키피디아의 로지스틱 회귀 항에 나오는 로그-선형 모델 항목인데, 이 로그-선형 모델
자체를 모르니 알듯 모를듯한 혼란만 계속되었다. 그렇다고 다시 로그-선형모델로 거슬러 올라갈 수도 없고…


결국 그렇게 매우 유사하게 생긴 두 함수 사이의 관계를 밝혀본답시고 또 일주일이 그렇게 흘러가버리고 말았다.
그리고 찾은 답은 sigmoid에서 softmax를 유도할 수 없다면 거꾸로 softmax로부터 sigmoid를 이끌어내보자는
것이다. 이미 sigmoid가 softmax의 특수한 형태 (결과 항이 2개인 경우의 특수한 형태)라는 것은 검색 과정에서
알 수 있었다. 다만 그 과정을 유도할 수 없었을 뿐. 그리고 앞서 말했듯이 이제 거꾸로 한 번 거슬러 올라가보고자 한다.


우선 주의할 것은 이전 포스팅에서도 언급한 바가 있듯이 치환에 주의하라는 것이다. 이 치환의 마법으로 동일한
식이 다양한 형태로 등장하게 된다. 내가 검색을 하며 찾았던 softmax 함수에 대한 표현만 해도 아래 이미지와 같이
다양했다.




하지만 이 것들이 모두 동일한 의미의 식이라는 것… 이쯤 되면 이 건 수학보다는 문학에 가깝다…-.-
일단 변수와 지수, 밑, 첨자까지 너무 많은 알파벳과 그리스 문자가 뒤섞여 있으면 혼란만 더할 것이니 가장 단순한
식을 기준으로 진행해 가보자. 바로 김성훈 교수님의 유튜브 강좌에 나온 식이다.




군더더기 없이 깔끔해서 좋다^^. 일단 이 식 자체에 대해 간단하게 설명을 해보자. 위의 식에서는 생략이 되었지만
softmax 함수에서 각 항을 의미하는 아래 첨자 i의 범위는 우변의 분모에 있는 시그마의 밑이 j의 범위와 동일하다.
즉 i 항에 대한 값 / 모든 결과항에 대한 값의 합이라고 간단하게 말할 수 있을 것이다. 


다시 말해 전체에 대한 부분의 비율이므로 각 항에 대한 결과로써의 좌변의 값은 각 항에 대해 1보다 작은 값이
출력되며 모든 항에 대한 결과값의 총 합은 1(=100%)가 된다.


사실 이정도만 이해하면 그냥 다음 진도 뽑아도 된다. 그래도 궁금하다. 단지 결과 항이 늘어는 것 뿐이고 또 웬만한
검색 결과에도 sigmoid 함수가 softmax 함수의 특수한 형태라고 하니 두 함수의 관계를 한 번 짚고 넘어가고 싶은
것이다. 그래서 일주일도 넘는 시간을 투자했고…ㅠ.ㅠ 완변하진 않지만 지금부터 한 번 살펴보자.


위에 적은 식은 i(또는 j)가 임의의 갯수인 경우의 식이다. 그런데 만일 i가 0과 1 두가지의 경우밖에 없는 경우를
가정해보자 그렇다면 각각의 항에 대해 다음과 같이 표현할 수 있을 것이다.




이 때 y0이 0인 경우를 생각해보면 식은 다음과 같이 바뀐다.




첫 번째 줄의 가장 오른쪽 식을 보면 바로 sigmoid 함수가 나온다. 이렇게 뭔가 어설프지만 softmax와 sigmoid
함수가 모종의 은밀한 관계(?)가 있다는 것을 알 수는 있었다…-.-


정리


겨우 풀어놓긴 했지만 여전이 뭔가 덜 풀린듯한 느낌이다. 하지만 이 것만 붙잡고 시간을 보낼 수 없으니 이정도에서
마무리 하고자 한다. 사실 이 과정에서 중요한 것은 식이 어떻게 도출되느냐가 아니라 딥 러닝으로 가는 과정에서
그 단초를 살짝 엿볼 수 있었다는 점이다.


텐서플로우에서의 hello world라고 하는 MNIST 분석이라든지 이미지 분석 등이 따지고 보면 결국 확률의 문제다.
어떤 이미지가 개인지 고양이인지를 판단한다는 것은 곧 학습 데이터를 기반으로 개일 확률과 고양이일 확률을
구하는 것에 다름 아닌 것이다.



갈길은 멀고 마음은 급하지만 그래도 차근차근 황소 걸음으로 가보련다. 다음 포스팅에서는 다항로지스틱회귀의
cost 함수인 cross-entropy에 대해 알아보도록 하겠다.






블로그 이미지

마즈다

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




텐서플로우를 이용한 로지스틱(Logistic) 회귀

로지스틱 회귀에 들어오면서 시그모이드 함수에 난데없이 등장한 자연상수 e에 대한 정체를 밝히느라 무려 2주를
보냈다. 물론 그 과정이 쓸모없는 일은 아니었지만 꽤나 고민스럽고 답답한 시간들이었다. 이제 어느정도 그 정체를
밝혔으니 본격적으로 로지스틱 회귀 실습을 진행해보도록 하자.


샘플 데이터는 http://www.dodomira.com/2016/02/12/logistic-regression-in-r/ 블로그를 참조하였고
자료 원본 경로는 http://www.ats.ucla.edu/stat/data/binary.csv 이다.


자료 불러오기


주제에서 조금 벗어나는 이야기지만 현재 나는 Docker for MAC에서 TensorFlow 이미지를 설치한 상태이고
따라서 Docker를 통해 Jupyter 환경에서 작업을 진행 중이다. 이런 환경에서 작업의 불편을 조금이라도 줄이기
위해 Docker 컨테이너에서 MAC OSX의 디렉토리에 접근하여 데이터 파일을 읽어들이도록 사전 작업이 필요하였다. 


Docker for MAC은 기본적으로 File Sharing이라는 기능을 제공하는데 작업표시줄에 떠있는 아이콘을 클릭하면
표시되는 메뉴에서 'Preferences...'를 선택하면 설정 창이 표시되고 설정 창의 두 번째 탭에 'File Sharing'이 있다.
기본적으로 /Users,/Volumes, /private, /tmp가 등록이 되어있다. 추가로 등록도 가능하지만 기본적으로 등록된
경로의 하위 경로는 등록하지 못한다.


이렇게 등록한 후 docker run 명령어를 통해 컨테이너를 실행할 때 '-v [MAC 경로]:[docker 경로]' 형식으로
옵션을 추가하면 된다.


예)
docker run -it -v /Volumes/Data1/Tesorflow:/data -p 8888:8888 -p 6006:6006 \
gcr.io/tensorflow/tensorflow


위과 같이 한 경우 Docker 컨테이너 쉘로 진입하면 cd /data로 MAC 디렉토리에 접근이 가능하다. 이번 예제에서는
이와 같이 데이터 파일을 /data로부터 읽어 사용할 것이다.


파일을 읽어 출력한 결과는 아래와 같다.

import tensorflow as tf
import numpy as np

xy = np.loadtxt('/data/sample.txt', unpack=True, dtype='float32')

x_data = xy[0:-1]
y_data = xy[-1]

print (x_data)
print (y_data)

출력 결과 ---------------------------------------------------------------

[[ 1.          1.          1.         ...,  1.          1.          1.        ]
 [ 3.79999995  6.5999999   8.         ...,  4.5999999   7.          6.        ]
 [ 3.6099999   3.67000008  4.         ...,  2.63000011  3.6500001
   3.8900001 ]
 [ 3.          3.          1.         ...,  2.          2.          3.        ]]
[ 0.  1.  1.  1.  0.  1.  1.  0.  1.  0.  0.  0.  1.  0.  1.  0.  0.  0.
  0.  1.  0.  1.  0.  0.  1.  1.  1.  1.  1.  0.  0.  0.  0.  1.  0.  0.
  0.  0.  1.  1.  0.  1.  1.  0.  0.  1.  1.  0.  0.  0.  0.  0.  0.  1.
  0.  1.  0.  0.  0.  0.  1.  0.  0.  1.  0.  0.  0.  0.  0.  0.  0.  0.
  0.  0.  0.  0.  0.  1.  0.  1.  0.  0.  0.  0.  1.  0.  0.  0.  0.  1.
  0.  1.  0.  0.  1.  0.  0.  0.  0.  0.  0.  0.  0.  0.  1.  1.  1.  0.
  0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  1.  0.  1.  1.  0.  0.  0.  0.
  1.  0.  0.  0.  1.  0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  1.  0.  0.
  0.  0.  0.  0.  1.  0.  1.  0.  1.  0.  0.  1.  0.  1.  0.  0.  0.  0.
  1.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  1.  0.  1.  0.  0.
  0.  0.  0.  1.  0.  0.  0.  0.  0.  0.  1.  0.  0.  0.  1.  0.  0.  1.
  0.  0.  0.  1.  1.  0.  1.  1.  0.  1.  0.  0.  0.  0.  0.  0.  1.  1.
  0.  1.  0.  1.  0.  0.  1.  0.  0.  1.  0.  0.  0.  1.  0.  0.  0.  0.
  1.  0.  1.  0.  0.  0.  0.  1.  1.  0.  0.  0.  0.  0.  0.  0.  0.  0.
  1.  1.  1.  0.  1.  1.  0.  0.  0.  0.  1.  1.  1.  0.  0.  1.  1.  0.
  1.  0.  1.  0.  0.  1.  0.  1.  1.  1.  0.  0.  0.  0.  1.  0.  1.  1.
  0.  0.  1.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  1.  1.  1.  0.  0.
  1.  0.  0.  0.  0.  0.  0.  1.  0.  1.  1.  1.  1.  0.  0.  0.  0.  0.
  0.  0.  0.  1.  0.  0.  0.  0.  0.  0.  1.  1.  0.  0.  0.  1.  0.  1.
  0.  0.  0.  0.  0.  0.  0.  0.  1.  0.  1.  0.  1.  1.  0.  0.  1.  0.
  1.  1.  0.  0.  1.  0.  0.  0.  0.  0.  1.  1.  1.  1.  0.  0.  0.  1.
  0.  0.  0.  1.  0.  0.  1.  0.  1.  0.  0.  0.  1.  1.  1.  1.  1.  0.
  0.  0.  0.  0.]

로지스틱 회귀 시작하기

우선 기본적인 흐름은 김성훈 교수님의 동영상 강좌를 기준으로 따라가도록 하겠다.
위에서 이미 데이터를 준비해 놓았으므로 이후 진행을 살펴보자.


먼저 학습 이후에 테스트를 위한 임의의 값을 넣을 수 있도록 x와 y에 대한 placeholder를 생성한다.

X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)


그리고 각 독립변수 (X)에 대한 기울기 변수 W를 만들자. 위 출력 결과를 보면 알 수 있듯이 x_data은 1행 3열의
벡터이다. 즉 len(x_data)의 값은 3이 될 것이다.

W = tf.Variable(tf.random_uniform([1, len(x_data)], -1.0, 1.0))


다음으로 선형 회귀에서 사용했던 가설함수를 만들고...

h = tf.matmul(W, X)


이 식은 선형 회귀에 대한 가설 함수이므로 이 것을 이용하여 이전 포스팅에서 열심히 공부한 로지스틱 회귀에 적합한
가설 함수를 새롭게 만든다.

hypothesis = tf.div(1., 1. + tf.exp(-h))


로지스틱 회귀의 가설 함수 공식은 이미 알아본 바가 있으므로 tensorflow의 API만 간단히 살펴보자.


  1. tf.div : 굳이 설명할 것도 없이 첫 번째 인자를 두 번째 인자로 나누는 함수이다.
  2. tf.exp : exp라는 함수는 tensorflow 뿐만 아니라 수학 계산을 위해 만들어진 대부분의 프로그램 및 언어에서 대체로 동일한 이름으로 사용되며 자연상수 e의 거듭제곱을 의미한다. 괄호 안의 파라미터만큼 거듭제곱을 하는 것이다. 따라서 이 식은 e^(-W * X)로 우리가 알고있는 로지스틱 회귀 함수의 분모에 보여지는 식이 된다.


이렇게 가설 함수를 만들었으니 이번에는 cost 함수를 만들 차례이다. 로지스틱 회귀에서의 cost 함수에 대해서는
별도로 정리한 적은 없으나 다른 사이트의 훌륭한 자료들을 참고로 식만 살펴보자(이 블로그에서는 다음에 자세히
다뤄보도록 하겠다).

with tf.name_scope("cost") as scope:
cost = -tf.reduce_mean(Y*tf.log(hypothesis) + (1 - Y)*tf.log(1 - hypothesis))
cost_summ = tf.scalar_summary("cost", cost)
cost_summ = tf.summary.scalar("cost", cost)


앞서 말했듯이 자세한 내용은 뒤로 미루고 간단하게 몇가지만 짚고 넘어가자.


우리는 로지스틱 회귀가 결과로써 0 또는 1 사이의 값을 갖도록 한다고 했다. 그 기준에 따라 Y가 0일 경우와
Y가 1일 경우 각각 다른 cost 함수가 적용되는데 이 것을 두 개의 식으로 사용하기 복잡하므로 위와 같은 식을
만들었다. Y가 1이라면 tf.reduce_mean의 파라미터로 들어가있는 덧셈식의 오른쪽 항이 0이되고 Y가 0이라면
반대로 왼쪽항이 사라져 둘 중 한 식만 적용이 되는 것이다.


그밖에 제곱 평균(tf.reduce_mean)을 하는 것은 선형 회귀와 동일하다.
그리고 아래와 같이 학습 계획을 세우는 부분 역시 이전의 선형 회귀외 동일하다.

a = tf.Variable(0.1)
optimizer = tf.train.GradientDescentOptimizer(a)
train = optimizer.minimize(cost)


그리고 이미 알고 있겠지만 tensorflow의 Variable들은 초기화를 하기 전까지는 아무것도 아니다~
변수를 초기화 하고 Session을 생성하고 학습을 진행하는 것까지 한번에 가보자!


학습 과정에서는 결과에 대해 프린트도 하고 TesorBoard용 로그도 찍도록 내용을 추가하였다.
참고로 TensorBoard를 이용하기 위한 API가 변경되었다. 관련 내용은 글 말미에 간단하게 정리하겠다.

init = tf.global_variables_initializer()

with tf.Session() as sess:
merged = tf.merge_all_summaries()
merged = tf.summary.merge_all()
writer = tf.train.SummaryWriter("/logs", sess.graph_def)
writer = tf.summary.FileWriter("/logs", graph=sess.graph)

sess.run(init)
for step in range(20000):
sess.run(train, feed_dict = {X:x_data, Y:y_data})
if step % 200 == 0 :
summary = sess.run(merged, feed_dict = {X:x_data, Y:y_data})
writer.add_summary(summary, step)
print(step, sess.run(cost, feed_dict = {X:x_data, Y:y_data}), sess.run(W))

correct_prediction = tf.equal(tf.floor(hypothesis + 0.5), Y)
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print("Accuracy: ", accuracy.eval({X:x_data, Y:y_data}))


(0, 3.9219272, array([[ 0.56839818,  0.54351538,  0.33454522,  0.35521972]], dtype=float32))
(200, 0.58839369, array([[ 0.26840821,  0.14631633, -0.08747143, -0.65875155]], dtype=float32))
(400, 0.58729297, array([[ 0.12495378,  0.15614627, -0.05624871, -0.66897833]], dtype=float32))
(600, 0.58630282, array([[-0.01207304,  0.15915234, -0.02467063, -0.66446435]], dtype=float32))
(800, 0.58538932, array([[-0.14362514,  0.16177623,  0.00590925, -0.65985072]], dtype=float32))
(1000, 0.58454657, array([[-0.26996911,  0.16426219,  0.03536379, -0.65547818]], dtype=float32))
...
중간 생략
...
(17400, 0.57432276, array([[-3.30464649,  0.22630024,  0.74326813, -0.56382245]], dtype=float32))
(17600, 0.57432127, array([[-3.30992198,  0.22641255,  0.74449652, -0.56368262]], dtype=float32))
(17800, 0.57432002, array([[-3.31500411,  0.2265207 ,  0.74568129, -0.56354982]], dtype=float32))
(18000, 0.57431865, array([[-3.31990051,  0.22662495,  0.74682134, -0.56342   ]], dtype=float32))
(18200, 0.57431746, array([[-3.32461858,  0.22672606,  0.74791992, -0.56329668]], dtype=float32))
(18400, 0.57431638, array([[-3.32916546,  0.22682308,  0.74897873, -0.56317711]], dtype=float32))
(18600, 0.57431531, array([[-3.33354449,  0.22691627,  0.74999893, -0.56306177]], dtype=float32))
(18800, 0.57431442, array([[-3.33776498,  0.22700654,  0.75098211, -0.56295192]], dtype=float32))
(19000, 0.57431358, array([[-3.34183216,  0.22709353,  0.75192851, -0.5628444 ]], dtype=float32))
(19200, 0.57431275, array([[-3.34575057,  0.22717719,  0.75284076, -0.56274116]], dtype=float32))
(19400, 0.57431203, array([[-3.34952545,  0.22725755,  0.753721  , -0.56264317]], dtype=float32))
(19600, 0.57431132, array([[-3.35316253,  0.22733469,  0.75456882, -0.5625475 ]], dtype=float32))
(19800, 0.57431066, array([[-3.356668  ,  0.22740975,  0.75538445, -0.56245506]], dtype=float32))
('Accuracy: ', 0.70749998)


유튜브 강좌에 있던 짧은 샘플로는 강좌에서와 거의 유사한 결과를 얻었다. 그런데 이렇게 별도로 가져온 데이터를
사용하나 cost가 영 줄어들지를 않는다...ㅠ.ㅠ 게다가 정확도도 0.7 수준...
참고로 TensorBoard로도 확인 가능하도록 했으니 확인이나 한 번 해보자. 출력한 cost 값의 챠트는 다음과 같다.


그리고 그래프는 아래와 같다.


아직 나의 수준에서는 뭔가 제대로 된 결과가 나오기는 힘든가보다. 조금 더 열심히 공부를 해봐야겠다...ㅠ.ㅠ

앞서 말했듯이 전체적인 진행을 김성훈 교수님의 유튜브 강의를 기반으로 이번 실습을 진행을 했다. 그런데 tensorflow를 1.0으로
업그레이드한 탓인지 TensorBoard 설정을 위한 API 일부가 모두 바뀌었다. 일단 코드 상에 강의에 등장하는 이전 버전의 API는
모두 주석 처리하여 남겨두었다. 간단하게 정리하면 변화는 다음과 같다. 

  • tf.scalar_summary("cost", cost) -> tf.summary.scalar("cost", cost)
  • tf.merge_all_summaries() -> tf.summary.merge_all()
  • tf.train.SummaryWriter("/logs", sess.graph_def) -> tf.summary.FileWriter("/logs", graph=sess.graph)



마지막으로 위 실습을 진행한 Jupyter 노트북과 샘플 데이터 파일을 첨부한다.


sample.txt

chapter2_logistic_regression.py.ipynb


블로그 이미지

마즈다

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


자연로그! 너 죽고 나 죽자! - 고등 수학을 초등학생처럼 배워보기~


지난 글에서 내가 문돌이라는 것을 무기 삼아 겨우 자연상수 설명 조금하고 자연로그는 그저 그런게 있는갑다 하고
그냥 넘어가버렸다. 하지만 역시…뒤처리가 안된 것 마냥 여전히 찜찜하기 그지없다. 그래서 한 번 더 자연로그를
이해하는데 도전해보기로 하였다. 과연 잘 설명이 될 지는 모르겠으나 이미 언급한 바와 같이 이러한 과정 하나하나가
분명 앞으로 나아가는 길이라 믿는다.


문돌이 혹은 수포자의 한계


어떤 수학 공식을 풀이 한다는 것, 왜 그런 공식이 도출 되었나 하는 것은 곧 수학의 입장에서는 검증을 하는 것, 다시
말해 ‘증명’을 하는 것이다. 그런데 아이러니컬 하게도 증명을 하게 되면 처음의 단순했던 식이 매우 복잡해진다.
이것은 문돌이나 수포자(이하 우리들…-.-)의 수와 연산에 대한 알레르기에 불을 붙이는 격이다. 하지만 이 과정을 
거치지 않고는 ‘이해’라는 한 단어에 접근할 수 없다.


그렇다면 어떻게 해야 이 알레르기를 극복할 수 있을까…최근 여러가지 수학 공식을 정리하면서 개인적으로 터득한
것을 정리해보고자 한다. ‘개인적’인, 지극히 ‘개인적인’ 방법이며 이 방법이 누구에게나 통한다고 장담할 수는 없다.


  1. 너무나 당연한 이야기부터 해볼까? 적어도 각 연산자의 또는 수학 기호의 의미는 알아야 한다. 굳이 그 성질이나 활용까지 이해하지는 못하더라도 적어도 개념은 알아야 한다. 예를 들어 우리들의 가장 큰 적 중 하나인 적분을 생각해보자. ∫(인테그랄, integral)이라는 기호가 정확히 뭔진 모르겠지만 어떤 범위 내에서 어떤 값들을 무한히 더해가는 것이라는 것 정도는 알아야 한다.
  2. ‘치환’에 유의해야 한다. 다른 말로 하면 가장 처음에 주어지는 ‘등식(=)’을 유심히 봐야 한다. 나는 최근까지 그 의미를 몰랐다. f(𝒙) = a𝒙 + b라는 것이 증명이 진행되어감에 따라 a𝒙 + b 대신 f(𝒙)를 사용해도 된다는 의미인지를 까맣게 몰랐다. 그래서 도대체 f(𝒙)라는 것은 어떤 의미일까을 계속 궁금해 했다. 이밖에도 길어질 수 있는 공식을 간단하게 표현하기 위해 치환을 자주 한다. 간단하게는 한 개의 변수에서부터 복잡하게는 어떤 수식을 하나의 문자로 바꾸어 쓰는 것이다. 이 것을 놓치게 되면 증명에 마지막에 가서 우리는 ‘음…도대체 이 문자는 언제부터 여기에 있었지?’하는 지극히 근혜스러운 고민을 하게 되고 만다.
  3. 2번과 같은 맥락에서 수학 공식을 풀이할 때는 단지 길어서 보기 힘든 공식을 ‘단순화’하기 위한 장치들을 많이 사용한다. 우리들은 이 부분을 이해하지 못한다. 심지어는 이렇게 축약된 부분을 다시 길게 늘여놔야 더 잘 이해하는 경우도 있다. 대체로 이런 장치들은 치환에 의해 이루어지므로 2번과 잘 연결해서 생각해야 한다.
  4. 마지막 방법도 매우 당연한 이야기다. 바로 끈기있게 보고 또 봐야 한다는 것이다. 길고 긴 증명의 과정을 따라가다보면 돌연 어느 단계에서 ‘왜 이 공식의 좌변과 우변이 같은 것이지?’라는 의문이 드는 그런 단계가 있다. 마치 몇만 광년은 워프한 듯한 지극히 아득한 느낌으로 양 변의 공식이 동일하다는 것을 이해하지 못하는 순간…아는 사람만 알 것이다. 인터스텔라급 괴리감이다…-.- 이 과정에서 1번과 2번의 자세가 필요하다.


이러한 마음가짐으로 자연로그에 대해 다시 한 번 알아보고자 한다.


다시 미분으로


처음 회귀분석과 관련된 수학 내용을 정리할 때 미분을 한 번 정리했었고 그 때 주로 참고했던 자료가 바로 위키백과의
미분 항목이었다. 그런데 앞서 설명한 그 알레르기 때문에 그냥 내가 필요한 내용만을 보고 나머지는 현기증을 느끼며
피해갔던 것이 실수였다. 바로 그 미분을 설명하는 내용 중에 자연스럽게 자연로그가 도출되고 있었던 것이다.


지금부터 우리들의 입장에서 아주 지겨울 정도로 자세하게 하나 하나 되짚어볼 것이다.


단서가 된 항목은 바로 ‘지수 함수의 미분’이었다. 우선 미분에 대해 다시 한 번 짚어보자. 미분이란 어떤 연속적인 곡선
(엄밀하게는 맞는 표현이 아니지만 우선은 이렇게 표현하자)에 대해 특정 위치에서의 기울기를 구하는 것이라고 했다.
위키의 표현으로 말하자면 평균변화율을 구할 수 있을 때 이 평균 변화율의 극한이 바로 특정 위치의 기울기(를 갖는
접선)라고 할 수 있는 것이다. 최초 출발은 여기다. 아래 공식을 이해하도록 해보자.



사실 우리네 입장에서 공식만 가지고 이해하긴 어렵다. 이해할 수 있다면 이고생도 안한다…-.- 그러니 그래프를
보면서 차근차근 알아보자. 



일단 ∆는 라틴어 델타의 대문자로 주로 변화량을 의미한다. 즉 ∆ 𝒙라는 것은 얼마인지는 모르겠지만 𝒙의 변화량을 
의미한다. 즉 아래 그래프에서 보면 𝒙는 𝒙에서부터 𝒙 + ∆ 𝒙까지 변한 것이다. 이에 대해 𝒙에 대한 함수인 𝑓(𝒙)는 
𝑓(𝒙)에서부터 𝑓(𝒙 + ∆ 𝒙)까지 변했다. 이 내용을 𝒙를 기준으로 설명하면 바로 위의 공식이 나오는 것이다. 𝒙의 변화량
∆ 𝒙에 대한 𝑓(𝒙)의 변화량 ∆ 𝑓(𝒙)의 비율이란 바로 이런 의미이다.


그리고 이 평균 변화율에 대해 극한을 적용한 것이 바로 순간변화율(미분계수)이며 우리가 미분을 통해서 얻고자 하는
값인 것이다(극한에 대한 이해는 위키에 있는 아래 이미지를 참고하자).


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


일단 이 순간 변화율 공식을 기억해두자.


지수 함수 미분하기


우선 아래 링크(위키피디아의 미분에 대한 문서)의 ‘지수 함수의 미분’을 같이 보면서 살펴보자


https://ko.wikipedia.org/wiki/미분


특정 조건만 만족하면 많은 함수들을 미분할 수 있다. 그리고 그 많은 예가 위키에 잘 설명이 되어있다. 여기서는 그 중
지수 함수에 대한 미분을 살펴볼 것이다. 이전에는 그냥 자연로그가 있어서 그걸 사용하기만 했는데 이 지수 함수의
미분 과정에서 자연로그가 톡 튀어나온다. 신기하게도…


먼저 수학 알레르기를 극복할 수 있는 방법을 상기하자. 그리고 위키에 설명된 지수 함수의 미분 내용의 첫 줄을 살펴
보자. 𝑓(𝒙) = b^ 𝒙라는 등식이 제일 처음 보인다. 그리고 이 함수의 도함수(순간변화율을 얻을 수 있는 함수)가
다음과 같이 표시되어있다.



설명에 ∆ 𝒙를 ℎ로 표기한다고 했으니 다시 ℎ를 ∆ 𝒙로 돌려놓고 보면 다음과 같이 된다(편의상 순간변화율에서 극한을
제거하고 평균변화율만 보자).



그런데 제일 처음 등식에서 𝑓(𝒙) = b^ 𝒙라고 하였으니 𝑓(𝒙 + ∆ 𝒙) = b ^ (𝒙 + ∆ 𝒙)가 성립하고 따라서 위 식에서
b^ 𝒙를 𝑓(𝒙)로 b ^ (𝒙 + ∆ 𝒙)를 𝑓(𝒙 + ∆ 𝒙)로 바꾸어 놓으면 우리가 최초에 확인했던 평균변화율과 동일한 공식이
나온다. 이렇게 해서 지수 함수 𝑓(𝒙) = b^ 𝒙에 대한 도함수를 확인했다.


이후 내용은 이 도함수에 대한 풀이 과정이다. 비교적 어렵지 않은 내용이므로 조금만 검색을 하면서 보면 이해할 수 
있다. 그런데…'이때 상수 𝑘를'이라는 문장 바로 다음에 난데없이 자연로그가 튀어나왔다. 우변의 공식은 앞서 
진행된 풀이에 등장했던 공식이므로 당연한 등장인데 이 공식이 ln 𝑏와 같다고 하니 이 우변의 공식을 지지고 볶으면 
뭔가 자연로그에 대한 단서가 나온다는 것이렷다. 매번 이해도 못하는 자연로그를 가져다 쓰다가 처음으로 자연로그가
없는 상태에서 자연로그를 도출하게 되었다. 이제 본격적으로 어떻게 아래 공식이 자연로그가 되는지 확인해보자.



위의 등식에서 우측의 식이 어떻게 해서 자연로그로 이어지는 지를 분석해볼 것이다. 역시나 가장 먼저 할 일은
바로 ‘치환’이다. 우측 식에서 가장 복잡한 분자 𝑏^ℎ - 1을 𝑡라고 하자. 즉 𝑡 = 𝑏^ℎ - 1가 되는 것이다. 그런데 우리는
지금 지수에 대해서 알고자 하는 것이니 𝑏^ℎ를 기준으로 식을 바꾸어보면 𝑏^ℎ = 𝑡 + 1로 놓을 수 있다. 여기서 한 번
더! 분모가 ℎ이니 마지막에 바꾼 식을 ℎ에 대해 정리한다. 그러면 우리는 최초의 식에서 2개의 항을 다음과 같이
바꿔놓을 수 있다.



이 때 lim의 밑이 ℎ →0에서 𝑡 →0으로 바뀌었는데 이 것은 극한의 성질을 조금 이해해야 한다. 극한이란 
엄밀하게 말하면 정확한 어떤 수가 아닌 ‘어떤 수에 가장 근접하는 상태’라고 봐야 한다. 하지만 계산의 편의를 
위해 화살표 우측의 수라고 생각해도 무관하다. 그렇게 봤을 때 ℎ →0으로 무한히 가는 경우 

𝑏^ℎ - 1 = 𝑏^0 - 1 = 1 - 1 = 0

위 등식이 성립하므로 𝑏^ℎ - 1 = 𝑡라는 조건에 의해 결국 𝑡또한 0이 된다. 이 조건으로 인해 𝑏^ℎ - 1이 𝑡로 
치환된 식에서는 lim의 밑을 𝑡 →0로 놓을 수 있는 것이다.


원래의 식을 치환한 항들로 대체하면 다음과 같이 변경된 식이 나온다.



여기까지는 이해가 되었는가? 여기서 한가지만 더하자. 미지수가 분자와 분모에 모두 존재하니 아무래도 뭔가 더
복잡해 보인다. 그러니 미지수를 분모쪽으로 모두 몰아버리자. 이러한 방법도 식을 간단하기 위해 매우 자주 쓰이는
방법이다. 자~원래의 식의 결과를 변화시키지 않고 처리하려면? 1을 곱하면 된다. 다만 1을 어떻게 표현하는가가
관건이다. 결과부터 보자.



절묘하지 않은가? 나만 그런가? 분자의 미지수를 없애기 위해 1이라는 수를 (1/𝑡) / (1/𝑡) 로 바꾸었다. 분모와
분자가 같기만 하면 1이니까. 그래서 분모는 조금 복잡해졌지만 분자는 𝑡 * (1/𝑡) = 1로 미지수를 없애고 
1이라는 아주 쉬운 수로 바뀌었다. 이런 너무도 당연해보이는 기법들이 우리들에게는 그토록 어려운 것이다…ㅠ.ㅠ
이렇게 정리된 식은 다음과 같다.



분모를 한 번 더 정리해보자. 로그의 성질에 의해 다음과 같이 바꿀 수 있다.



드디어 나왔다! 기억하시는가? 자연상수 e의 식을! 다시 한 번 보자.



극한이 변수 t에 대해 적용되므로 극한을 t에 대해서만 놓고보면 결국 자연상수 e가 튀어나온다. 
그리고 이제 정말 마지막 정리이다.



이후 진행은 위에 링크한 위키피디아의 ‘지수 함수의 미분’항목을 참고하면 되겠다. 특이한 것은 그 내용의 마지막에
보면 지금까지 진행된 식의 𝑏라는 상수가 자연상수 𝑒일 경우, 즉 자연상수 e가 밑이 되는 지수 함수는 미분을 해도 그
도함수가 처음 형태와 동일하다는 것이며 아마도 이 성질이 매우 유용한 것 같다


정리


자연상수 e가 처음 발견된 것은 수학자 네이피어가 로그를 계산하는 과정에서라고 한다. 결국 어찌보면 자연상수보다
자연로그가 더 먼저 발견되었다고 할 수 있겠다. 어쨌든 지수함수를 미분하다보니 자연로그가 자연스럽게 도출이 되는
과정을 보았다. 적어도 자연로그와 자연상수 𝑒가 밑도 끝도 없이 뿅! 하고 태어난 것은 아니라는 것은 알 수 있게 
되었다.


이렇게 까지 해도 여전히 이해가 안가는 부분은 남아있지만 그 것은 아마도 실제로 자연로그와 자연상수 𝑒가 얼마나
유용하게 사용되는지 그 실례를 많이 접하지 못해서이리라. 하지만 그 건 어디까지나 이과충과 공돌이의 영역이므로
우리는 이정도 선에서 만족해도 될 것 같다.


도대체 텐서플로우 공부해보자고 시작해서 이게 웬 난리인지 모르겠다…ㅠ.ㅠ
이제부터는 다시 본연의 텐서플로우로 돌아가야겠다.
남들은 다 얼굴 이미지 분석하고 막 그러고 있던데…ㅠ.ㅠ

블로그 이미지

마즈다

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


문돌이를 위한 자연상수 e와 자연로그의 이해

음…제목을 ‘이해’라고 달았지만…본인 스스로도 전혀 이해하고 있지 못한 이 슬픈 현실…ㅠ.ㅠ
일단 염두에 두어야 할 것은 자연상수 e의 존재 가치는 경험으로 얻을 수 밖에 없다는 것이다. 수많은 수학과 물리학 등
자연과학은 물론 공학에 이르기 까지 그 사용은 너무나 많다고들 한다. 하지만 문돌이의 처지에서 도대체 그 ‘많은’ 
것들이 대체 뭔가 하는데 이르면 그야말로 노답인 것이다. 그래서 오늘은 그저 딱 문돌이가 알면 도움이 될…까? 하는
선에서 자연상수 e와 그 자연상수 e를 밑으로 하는 자연로그에 대해 짚고 넘어가겠다.


자연상수 e에 대해

일단 네이피어에서 시작해 오트리드, 베르누이, 라이프니츠 그리고 오일러에 이르기까지 많은 수학자들이 자연상수 
e를 발견하고 사용을 했다. 하지만 마지막에 이 자연상수에 e라는 이름을 붙이고 체계적으로 정리한 사람은 바로
오일러(Euler)이다. 자연상수 e는 최초 발견자인 네이피어를 기리는 의미로 ‘네이피어 상수’라고도 하고 또 그냥
‘오일러의 수’라고도 하지만 가장 대표적인 이름은 오일러의 첫 이니셜을 딴 ‘자연상수 e’이다.


그런데 오늘 설명에서 가장 주목해야 할 사람은 네이피어도, 오일러도 아닌 바로 베르누이다.
베르누이는 복리식을 계산을 연구하다가 그 이자가 무한대의 횟수만큼 지급되는 경우를 계산해보았더니 그 수가
특별한 어떤 수에 가까워지는 것을 발견하였는데 이 수가 바로 자연상수 e였던 것이다.


원금 1원에 연 이율이 100%인 경우를 가정해보자. 1원을 저축하면 1년 후에는 원금 1원과 1원에 대한 이자(100%)
1원을 더해 2원을 받을 수 있다. 그렇다면 이자를 1년 꽉 채운 후 받지 말고 6개월 후에 한 번 그리고 1년 후에 한 번,
이렇게 나누어 받으면 어떨까? 그러면 연 이율이 100%니까 6개월후에 50% 이자를 받고 1년 후에 나머지 50%를
추가로 받게 되는 것이다. 그러면 6개월이 된 시점에서 원금 1월과 50%의 이자 0.5원이 생긴다. 그리고 또 6개월이
지나면 1원에 대한 이자 0.6원과 처음 6개월이 지난 시점에서 받은 이자 0.5원에 대한 이자 0.25원이 추가로 생긴다.
그러면 1년 후의 총 금액은 1 + 0.5 + 0.5 + 0.25 = 2.25로 1년에 1번 받을 때보다 0.25원을 더 많이 받게 된다.
바로 이런 식으로 이자를 받는 기간을 점점 짧게 자주 받을 경우를 계산해본 것이다. 아마도 주기가 짧아질수록 금액이
점점 더 커질 것을 예상했으리라


수식으로 보면 이렇다.


1년에 1번 이자 받기
1 + 1 * 1


1년에 2번 이자 받기
(1 + 1/2 * 1) + (1/2 * 1 + 1/2 * 1/2)
식을 간단하게 해보면
1 + 1/2 + 1/2 + 1/4


1년에 3번 이자 받기
(1 + (1/3 * 1)) + 
(1 + (1/3 * 1)) + (1/3 * 1 + 1/3 * 1/3) + 
(1 + (1/3 * 1)) + (1/3 * 1 + 1/3 * 1/3) + (1/3 * 1/3 + 1/3 * 1/3 * 1/3)
역시 식을 간단히 해보면
1 + 1/3 + 1/3 + 1/9 + 1/9 + 1/27


위 3개의 식을 다르게 표현해보면 다음과 같다(아무리 문돌이지만 아래 식을 풀어보면 위의 식과 같다는 정도는
알겠지…-.-)
1번 이자 받기 : 1 + 1 = (1 + 1/1)^1
2번 이자 받기 : (1 + 1/2) * (1 + 1/2) 
3번 이자 받기 : (1 + 1/3) * (1 + 1/3) * (1 + 1/3)


이 것을 일반화 시켜보면 (1 + 1/x)^x의 형태가 나온다. 그리고 이자 받는 횟수를 무한히 증가시켜보면 결국 
최종 식은 다음과 같다.


그리고 이 식의 값은 대략 2.71828…정도라고 한다.


사실 이런 과정은 다 필요 없고 다음의 성질만 기억해두면 될 것 같다.


  1. 자연상수 e는 증가에 대한 개념으로부터 출발한다(그래서 일부러 위의 식을 적었다).
  2. 어떤 수(1보다는 크지만 1에 극히 가까운 수)가 무한히 증가하더라도 이 식의 결과는 무한히 증가하지 않고 특정 값 (2.71828…)에 수렴한다.
  3. 마치 원주율의 𝜋와 같이 e 또한 상수이며 어떤 계산의 편의를 위해 사용된다(다만 𝜋의 경우 ‘원’이라는 구체적 도형으로부터 도출된 것이지만 그에 비해 e는 매우 추상적이어서 문돌이들의 접근이 쉽지 않은 것 같다).

물론 미적분과 관련하여 더 중요한 의미가 있는 것 같지만 우린 문돌이니까 적절한 선에서 멈추자…-.-


자연로그에 대해

사실 로그까지 오면 더 난감해진다. 역시나 그저 문돌이 수준에서 정리를 하고 넘어가자.
일단 로그는 지수(거듭제곱)와 관련된다. 간단하게 예를들면 1초에 2마리씩 분열하는 박테리아가 있다. 이 박테리아는
10초 후에 몇마리가 되었을까? 1초에 2마리, 2초에 4마리 3초에 8마리 4초에 16마리…10초 후에는 1024마리가 
된다. 이 것을 식으로 나타내면 다음과 같을 것이다.

2^10 = x

이 것을 역으로 물어볼 수도 있다. 이 박테리아가 1024마리가 되기 위해서는 몇초가 필요한가? 이 것은 식으로 
나타내면 다음과 같다.

2^x = 1024

이 식을 x를 기준으로 표현한 것이 바로 로그(log, logarithm)이며 다음과 같이 표현할 수 있다.


이 때 2를 밑, 1024를 진수라고 한다.


이러한 로그에는 특수한 케이스가 존재하는데 그 중 하나가 바로 밑이 10인 상용로그이다. 우리가 10진법을 주로
사용하기 때문에 매우 쉽고 유용하게 접근할 수 있다. 이러한 상용로그는 밑을 표시하지 않고 간단히 log100처럼
사용한다. 즉, 다음 두 식은 같은 것이다.


다른 하나가 바로 자연상수 e를 밑으로 하는 자연로그인데 아직 완전히 이해하지 못한 e를 밑으로 하는 만큼
자연로그 역시 아직은 그 가치를 잘 모르겠다…ㅠ.ㅠ 역시나 미적분과 밀접한 관련이 있다는 것만 어렴풋이 알 수
있을 뿐…


이 자연로그는 역시 밑을 생략하고 쓰기도 하는데 이미 사용 로그에서 log100과 같이 표현을 했기 때문에 같이
쓰지는 못하고 자연(nature)의 n을 따와서 ln으로 표시한다. 즉 아래의 두 식은 동일한 식이다.


그런데 고등 수학으로 가면 상용로그를 거의 안쓰기 대문에 log7.398…이 자연로그를 의미하는 경우가 더 많다고
한다.


정리

앞서 로지스틱 회귀에 대한 내용을 정리할 때도 말했지만 굳이 이런 부분까지 알아야 할까 하는 의문이 없진 않다.
이미 모든 것이 API화 되어있어 그 API만 호출하면 원하는 결과를 쉽게 출력할 수 있는데…하지만 비록 완벽하지는
못하다 할지라도 이러한 내용을 찾아가고 정리하는 과정에서 전체적인 흐름을 이해하는데 도움이 된 것 또한 분명하다.
텐서플로우를, 인공지능을 공부하는 내내 아마도 이런 어려운 수학들과 씨름을 하게 되겠지만 그 힘든 과정이 왠지
즐거울 것만 같은 묘한 느낌이 든다…^^ 


스스로도 이해하지 못하는 내용을 정리하다보니 정확하지 않은 지식을 남들에게 보여주어 피해를 입해는 것은 아닐까 
하는 두려움도 있으나 일단 이렇게라도 정리를 해두어야 나중에 더 깊이 공부할 때 도움이 될 것 같아 우선은 남겨둔다.
언젠가는 보다 정확한 지식을 기반으로 더 쉽게 정리해보겠다!!!

블로그 이미지

마즈다

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


로지스틱 회귀 분석 이해를 위한 수학 지식


들어가는 말


앞서 단순 선형 회귀에서 다중 선형 회귀로 넘어갈 때도 단지 변수 하나가 더 추가되었다는 이유만으로 한참을
헤맸다. 그런데 로지스틱 회귀는… 나를 지옥으로 몰고갔다…ㅠ.ㅠ


이놈에 로지스틱 회귀 모형을 이해하기 위해 장장 2주간의 기간에 걸쳐 로지스틱 회귀, 자연상수 e, 자연로그,
로그의 일반 성질 등을 공부해야 했고 대략 열 서너 편의 유튜브 강좌와 구글과 네이버를 통해 수많은 자료를 
검색하여 대략적으로는 감을 잡았다. 하지만 여전히 답을 찾지 못한 내용들이 조금 있다.


사실 우리가 중고등학교 때 수학을 배운 것과 같은 방법으로 그저 공식 하나 외우고 그 공식을 사용하면 그뿐이다.
그리고 애초에 이 공부를 시작하면서 깊숙한 원리를 깨우친다기 보다는 텐서플로우라는 툴을 잘 사용할 수 있을
정도만 공부를 하자는 목표가 있었다(단지 그 목표만을 따르자면 sigmoid라는 API만 알면 된다).


하지만 예전에 공부를 할 때도 그랬거니와 지금 이 시점에도 왜 그리 그 ‘공식’이란 것이 어떤 의미인지가 궁금한지…
그걸 그냥 지나치자니 화장실에서 뒷처리 않하고 나온 듯 찜찜하기가 이를데 없다. 그래서 비록 수학과는 거리가 먼
천상 문돌이 이지만 한 번 도전해보기로 했다.


지금부터는 문돌이 식으로 해석한 로지스틱 회귀에 대한 설명이다. 위키피디아의 로지스틱 회귀 항목을 기준으로
문돌이의 관점에서 설명을 한 번 해보도록 하겠다.


로지스틱 회귀 모델 개요

우선 위키피디아에서 정의한 로지스틱 함수는 다음과 같다.


이 함수는 다른 말로 시그모이드(sigmoid) 함수라고 하며 그 모양이 S자와 유사한 성질이 있다.
지금부터 이 함수가 어떻게 도출되는지에 대해 알아볼 것이다.


이전까지 알아본 선형 회귀 분석의 경우 그 모델을 위해 만들어지 함수들은 모두 다음과 같은1차 함수들이었다. 

특히 다중 선형 회귀식은 벡터를 이용하여 다음과 같이 표현하기도 했다.



이 1차 함수가 표현하는 것은 독립 변수들(보통은 x로 표현)이 변화해 갈 때 종속 변수( 보통 y로 표현)가 어떻게 변화해
가는지를 알아보는 것이 목적이었다. 그리고 이 때 독립 변수 x와 종속 변수 y는 모두 그 범위가 음의 무한대(이하 -∞)에서 

양의 무한대(이하 ∞)까지로 제약이 없었다.


그런데…
때에 따라서는 연속적으로 변화하는 어떤 값들에 대해 두 개의 결과만을 확인할 필요가 있었다.
예를 들면 흡연량과 폐암 발병에 대한 관계를 보면 연속적으로 변화하는 흡연량에 대해 그 결과로 ‘폐암에 걸렸다’와
‘폐암에 걸리지 않았다’라는 두 개의 결과만이 필요한 경우 또는 소득 수준을 독립 변수로 했을 때 어느 정도 이상의
소득이 있으면 ‘투표를 했고’ 그 이하인 경우에는 ‘투표를 안했다’더라라는 식으로 말이다.


이러한 경우 수학적 처리를 위해 흔히 구하고자 하는 종속 변수의 값을 1로 놓고 그 반대의 경우를 0으로 놓아 y가
1또는 0의 값을 갖도록 모델을 만든다. 그리고 이러한 독립 변수를 한 건 한 건 검사를 하게 되면 종속 변수 y가 0 
또는 1의 값만을 표현하게 되지만 무수히 많은 독립 변수를 검사하게 되면 그 결과는 확률로 표현할 수 있게 된다.


즉, 하루에 담배 10개피를 피운 사람 a를 조사했더니 폐암에 걸렸다(1). 그런데 하루에 담배 10개피를 피운 사람을
10명 조사했더니 6명은 폐암에 걸렸으나 4명은 걸리지 않았다. 결국 10개피의 담배를 피운 사람은 60%의 확률로
폐암에 걸렸다고 말할 수 있는 것이다.


이처럼 조사 횟수가 많아지게 되면 종속 변수 y는 확률로 표현할 수 있게 되는 것이며 이럴 경우에도 종속 변수의
값은 0 ~ 1의 범위(확률상 0% ~ 100%)를 넘지 않는다. 


위키피디아의 로지스틱 회귀 항목 첫 줄에 설명되어있는 내용이 바로 이러한 의미이다.

“독립 변수의 선형 결합을 이용하여 사건의 발생 가능성을 예측하는데 사용되는 통계 기법이다.”


로지스틱 함수 도출

그런데 이 과정에서 문제가 있다. 독립 변수의 선형 식은 그 결과가 -∞에서 ∞까지 제약이 없는데 우리는 0과 1사이의
값만을 얻기를 원한다. 


다시 한 번 담배 이야기로 돌아가보자. 하루에 담배 5개피를 피는 사람을 기준으로 1의 값을 얻기 위해 기울기(W)는 
편의상 1로 놓고 b는 -4로 초기 설정을 해보자. 그러면 y = 1 X 5 -4로 y = 1의 결과를 얻을 수 있다. 하지만 담배
개비 수가 10개비로 늘어나면 y = 1 X 10 - 4가되어 y = 6으로 1을 훌쩍 넘어버린다. 기존의 선형 식으로는 이러한
조건을 절대 만족할 수 없는 것이다.


일단 선형 식이 0 ~ 1 사이의 값을 가질 수 없음이 분명해졌으니 그렇다면 종속 변수쪽을 -∞에서 ∞의 범위로 확장을
할 수는 없을까? 바로 이러한 발상에서 로지스틱 함수가 시작된다.


앞서 설명한 내용 중 종속 변수 y는 x에 대한 조사의 수가 증가하게 되면 확률값을 갖게 된다고 하였다. 사례를 1건만
조사하는 경우는 없으니 결국 y는 확률의 값을 갖게 될 것이다. 그렇다면 우리가 어떤 확률 p에 대해 -∞에서 ∞값을 
출력하는 새로운 함수를 만들면 이 함수는 곧 기존의 선형 함수와 같다고 할 수 있어 새로운 등식을 만들 수 있을 
것이다. 이 때 굳이 p에 대한 함수를 만들어야 하는 것은 우리가 최종적으로 만들어진 함수를 통해 확률 값(p)를 
얻어야 하기 때문이다.


이렇게 하기 위해 2가지 과정이 필요한데 그 것이 바로 odds 비라는 것과 log이다.


odds비는 쉽게 말해 실패 확률에 대한 성공 확률의 비율이다. 성공 확률을 p라고 한다면 실패 확률은 1-p가 된다.
이렇게 보았을 때 odds 비는 아래 식과 같이 표현할 수 있다.


p는 0에서 1사이의 값을 가지므로 위 식을 계산해보면 p가 가장 작은 0일 경우 0 / 1 - 0이 되어 0의 값을 갖게 되고
p가 가장 큰 1이 되는 경우 1 / 1-1 = 1 / 0이 되어 무한대가 된다(프로그래밍에서는 분모가 0인 경우 에러가 발생하나
수학에서는 극한의 개념을 도입하면 1/0(에 수렴)으로 생각할 수 있어 무한대로 정의한다).


이렇게 일단 odds 비를 적용하면 p에 대하여 0부터 양의 무한대까지를 범위로 갖는 새로운 함수를 생각할 수 있다.
하지만 최솟값이 0이므로 아직 음의 무한대를 만족하지 못한다. 이 음의 무한대를 범위에 포함시키기 위해 이번에는
log를 취하게 되는데 log 중에서도 자연상수 e를 밑으로 하는 자연로그를 취하게 된다(밑이 e인 로그를 특별히 ln
이라는 기호를 사용하여 표시한다).


오래 전에 배웠던 로그의 계산을 되새겨보자. 위 로그 식에서 밑은 e이고 진수는 앞서 보았던 odds비의 계산 식이다.
그리고 odds비의 계산 식은 0과 ∞의 범위를 갖는다. 이 점을 생각한다면 odds 비에 자연로그를 취한 값의 범위는 
다음과 같다고 할 수 있다.

계산을 해보면 일단 자연상수 e라는 값은 나중에 설명을 하겠지만 대략 2.71828…인 무리수이다. 이 무리수 e를
몇제곱 했을 때 0이 될까? 상식적으로 분수의 분모가 무한히 커질 때 그 분수는 0에 가까워진다는 것은 잘 알것이다.
즉 e를 x제곱했을 때 0이 나오는 x는 -∞가 되는 것이다(e^-∞란 1 / e^ ∞과 동일하므로). 한편 e를 x 제곱했을 때 
∞가 나오는 x는 바로 ∞란 것은 보다 쉽게 알 수 있을 것이다.


결국 odds 비를 구하고 거기에 자연로그를 취했더니 확률 p에 대해 -∞부터 ∞를 범위로 갖는 함수가 만들어졌다. 
그러면 이제 다음과 같이 표현할 수 있다.


하지만 우리의 최종 목적은 이 함수를 통해 확률 p를 찾는 것이므로 이 함수를 p에 대한 함수로 만들어야 한다.
다음과 같이 진행해보자.




정리

어찌어찌 로지스틱 회귀 함수 도출 과정을 살펴보았다. 위 내용을 간단하게 정리를 해보면 다음과 같다.

  1. 무한의 값을 갖는 선형 회귀 식을 통해 두 개의 값(0또는 1)만을 갖는 결과를 가져와야 한다.
  2. 두 개의 결과만을 도출하는 과정이 연속되면 확률이 되며 확률 또한 범위는 0부터 1까지로 제한된다.
  3. 0부터 1까지로 값이 제한된 확률 값의 범위를 0부터 ∞로 확장하기 위해 odds 비를 취한다.
  4. 0부터 ∞까지로 확장된 결과를 다시 -∞에서 ∞로 확장하기 위해 odds 비의 식에 자연로그를 취한다(이 과정을 logit 변환이라고도 한다).
  5. 이제 양쪽의 식이 모두 -∞에서 ∞까지의 값을 갖게 되므로 등식이 성립하고 이 등식을 변형하면 우리가 원하는 로지스틱 함수가 도출된다.


그런데 여전히 이해가 안가는 부분은 이 과정에서 등장하는 자연상수 e와 이 e를 밑으로 하는 자연로그의 역할이다.
일단 얼추 생각하기에는 자연상수 e가 성장에 관한 연구(베르누이의 복리 식)로부터 탄생했다는 것, 그리고 e가 갖는
미분상의 특징(이 부분은 아직 이해 불가…-.-) 때문에 자연상수를 사용한 것으로 보이는데…


암튼 자연상수 e는 문돌이와 이과충을 구분하는 가장 핵심적인 개념이라고 하니 사실 뭐 몰라도 흠될 것은 없지만
AI를 공부하고자 하는데 그러한 장벽을 두고 간다는 것도 내키지 않는 문제이다. 자연상수 e와 관련된 것은 앞으로도
계속 그 의미를 이해하도록 노력할 것이다. 그래서 다음 글에서는 불쌍한 문돌이들을 위해 간단하게나마 자연상수 e를
조금 정리해보도록 하고 오늘은 여기서 마치겠다.

블로그 이미지

마즈다

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


TensorFlow 학습을 위한 간단한 Tip ~ #1

TensorFlow 공부를 시작하면서 몇가지 도구를 사용하게 되었다. 하지만 그 도구들에 대해 자세힌 알고있는
상태에서 시작을 한 것이 아니라 학습을 진행하면서 시행착오를 통해 알게되는 사실들이 꽤 많다.
이 그룹의 게시물들은 바로 이러한 팁을 정리하는 공간이 될 것이다.


첫 포스팅은 Docker와 Jupyter에 대한 몇가지 내용들을 다룰 것이다.


Docker에서 TensorFlow 관리하기

우선 처음 설치에 대한 내용과 1.0.0으로 업그레이드 하는 내용에서 Docker에 대한 약간의 내용들이 언급되었다.
오늘은 거기에 더해서 Container를 다루면서 실수했던 내용 몇가지를 정리한다.


처음 Docker를 통해 텐서플로우 이미지를 다운로드 받고 실행을 시키면 바로 Jupyter가 실행되면서 사용가능한
상태가 된다. 바로 그저께까지도 Docker의 container 사용 방법을 몰라 조금 미련한 짓을 하고 있엇다.


내가 분명 Jupyter상에서 ‘텐서플로우 첫걸음’의 예제를 업로드하여 학습을 하고 난 뒤 Jupyter를 종료시켰다가
다시 실행을 하면 이상하게도 내가 업로드한 내용들이 모두 사라지고 없었다. 처음 Jupyter를 실행했을 때의
상태로 초기화 되어있는 것이다.


이 것은 내가 Docker의 container에 대한 개념을 잘 몰랐기 때문에 발생한 문제였다.


일단 처음 이미지를 다운로드 받을 때 사용하는 명령어는 다음과 같다.

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


이 명령어를 실행하면 이미지가 없는 경우에는 이미지를 다운로드 받은 후 container 하나를 생성하여 바로
Jupyter를 실행하고 이미지가 있는 경우에는 그 이미지를 이용하여 새로 container를 생성하여 Jupyter를 실행한다. 


나는 이 사실을 모르고 계속 Jupyter를 실행할 때마다 위의 명령어를 사용했던 것이다. 그러니 항상 새로운
container가 만들어진 것이고 이전에 작업했던 내용은 이전에 작업했던 container에 있을 뿐이나 당연히
초기화된 새로운 Notebook 목록이 열렸던 것이다.


따라서 이전에 했던 작업을 계속 진행하려고 한다면 당연히 이전에 작업했던 container를 실행시켜 진행해야
하는 것이다.


우선 콘솔에서 다음과 같이 입력하여 자기가 사용했던 컨테이너의 ID나 이름을 확인해두자

docker ps -a


그러면 다음과 같이 현재 생성된 container의 목록과 그 상태가 나온다. STATUS 항목에 Exited라고 표시된
container는 정지된 container이고 실행되고 있는 container들은 Up xx hours 처럼 표시된다.


이 container 목록 중 사용하고자 하는 것에 대해 start 또는 stop 명령을 통해 시작하거나 종료할 수 있게 되는
것이다. 그러니 run 명령은 최초 한 번만 실행하고 이후에는 start/stop을 사용하면 된다.

docker start trusting_booth
docker stop trusting_booth


이 때 주의할 것은 Jupyter가 실행되면 Jupyter에 로그인할 때 필요한 token이 생성되는데 이 token을
잘 기억하고 있어야 나중에 동일한 Jupyter 커널에 접속하여 사용할 수 있다. 만일 token이 올바르지 않다고
한다면 다음과 같이 진행하자


우선 아래와 같이 현재 실행 중인 container의 shell로 들어간다.

docker exec -it trusting_booth bash
root@cbec422fa6de:/notebooks#


여기서 다음과 같이 입력한다. 그러면 현재 실행 중인 Jupyter의 URL이 token과 함께 표시된다. 이 주소를 이용하여
접속하면 된다.

docker exec -it trusting_booth bash
root@cbec422fa6de:/notebooks# jupyter notebook list
Currently running servers:
http://localhost:8888/?token=2587d5ea8dd373c169e8042c4ab15dd956a2a0dc9a50bac9 :: /notebooks
root@cbec422fa6de:/notebooks# 


Jupyter에서 필요한 라이브러리 추가하기


사실 이 부분도 위에 설명한 Docker container의 사용 방법만 제대로 알고 있엇다면 실수를 하지 않았을 부분이다.
나는 다중 선형 회귀에서 사용하는 산점도 행렬을 그리기 위해 pandas라는 라이브러리를 추가하기 위해 작업을
진행했다.


우선 Jupyter에서 라이브러리를 추가하는 것은 매우 쉽다. 아무 Notebook이나 하나 실행해서 code cell에
다음과 같이 입력하고 실행하면 된다. 내가 pandas를 설치했으니 pandas를 설치하는 명려어를 쓴다.

!pip install pandas


그러면 다음과 같이 설치되는 모습이 보여진다.


이제는 현재 실행된 커널의 어떤 Notebook에서도 pandas를 import하여 사용할 수 있다.
처음에는 앞서 Docker에서의 실수로 매번 Jupyter를 실행할 때마서 라이브러리를 추가해주어야 하나 했는데
동일한 container를 사용한다면 그런 실수는 할 일이 없을 것이다.


Jupyter Notebook 활용하기

아직 사용한 지 얼마 되지 않지만 그 짧은 시간에 Jupyter가 얼마나 유용한 툴인지 깨닫고 있다. 바로 직전에
포스팅한 다중 선형 회귀에 대한 내용은 Jupyter Notebook에서 아래 메뉴를 실행하여 다운로드 받은 후
MacDown이라는 Markdown 편집기를 이용해 열어 그 preview를 그대로 복사해 붙여넣은 것이다.


File > Download as > Markdown (.md)


만일 바로 다른 Jupyter에서 실행 가능한 상태로 저장하고 싶다면 다음과 같이 선택하여 저장하면 된다.


File > Download as > Notebook (.ipynb)


어쩧게 저장하느냐에 따라 문서가 될 수도 있고 싫행 가능한 소스가 될 수도 있는 것이다.


참고로 나는 현재 다부분의 블로그를 작성할 때 Ulysses라는 편집기를 사용하고 있다. 이 글도 역시 Ulysses로
작성한 글이다. 그런데 이 Ulysses가 다수의 문서를 관리하고 publishing하는데는 매우 좋은데 일반적인
Markdown과 좀 다른 듯하다. 그래서 Jupyter에서 Markdown으로 저장한 문서를 열면 제대로 표현이 안되는
경우가 많다. 그래서 Jupyter에서 저장한 Markdown 문서는 무료 편집기인 MacDown을 사용한다.


꿩먹고 알먹고

사실 Docker도 그렇고 Python도 그렇고 앞으로 공부해야 할 목록에 들어있었는데 이렇게 TensorFlow 덕분에
함께 공부하게 되어 그야말로 일석이조다. 다만 간혹 오늘 적은 시행착오와 같은 문제로 불필요한 문제로 시간을
소모하게 되는 것은 불만스럽기도 하지만…앞으로도 새롭게 알게되는 내용들은 잘 정리를 해놓아야겠다.

블로그 이미지

마즈다

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




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

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

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


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

그나마 독립변수가 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 변수들의 어느정도 비율로 관련이 
있는지(기울기)를 알아내고 그렇게 알아낸 기울기의 직선과 실제 데이터와의 오차를 절편을 이용해서 보정하는 
작업이라 할 수 있겠다.

블로그 이미지

마즈다

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