선형 회귀(Linear Regression)


머신러닝이나 통계학 부분을 다룰때 거의 가장 첫 챕터에 등장하는 선형 회귀(Linear Regression)에 대해 설명하고, 구글 Tensorflow를 이용하여 간단한 선형회귀 문제를 풀어보도록 하겠습니다.


어려운 설명은 모두 뒤로하고, 간단히 말해서 Linear Regression은 한국 교육과정상 중학교쯤에 배우게되는 1차 방정식이라고 생각할 수 있습니다.


현재 머신러닝을 학습하는 대부분의 사람들은 Coursera를 통해 Stanford에서 이루어진 Andrew Ng교수님의 강좌(https://class.coursera.org/ml-003/lecture) 를 수강할 것입니다. 이 강좌에서 앤드류 교수님은 

와 같은 표현을 사용하시는데, 그냥 느낌적 느낌이 괜히 복잡해 보입니다. 왜그럴까요? 표현이 낯설기 때문입니다. 한국의 수학교육과정에서는 어떻게 배웠을까요?

이것입니다. 아! 이제 기억이 좀 나시나요?

a는 일차방정식의 기울기를 의미하고, b는 y의 절편이라고 합니다. 

여기에서의 a와 b를 단지 θ에 대한 subscript 1과 2로 표현한 것입니다.

그림출처 : Andrew Ng - Linear Regression with One variable
그림출처 : Andrew Ng - Linear Regression with One variable

위와 같이 그래프의 기울기와 y절편을 사용하여 그래프를 수직선상에 표현할 수 있습니다. 일차방정식은 단순합니다. 그래서 예외사항이 거의 없는 데이터의 경우에 적합하게 사용할 수 있는 모델입니다. 그리고 단순히 곱셈과 덧셈만으로 계산이 가능하고, 그만큼 직관적으로 쉽게 이해할 수 있기 때문에 가장 기본적으로 시도해 볼 수 있는 방법입니다. 다른 알고리즘 모델에 비해 실행시간도 빠릅니다.

그림출처 : Wikipedia
그림출처 : Wikipedia

위의 그림에서 볼 수 있듯이 여러개의 x값에 따른 결과 값 y가 좌표상에 파란색 점으로 표시되어 있습니다. 이 때 이러한 점들의 상관관계를 평균적으로 예측할 수 있는 붉은색 선을 그어서 모델로 지정하였습니다.


여기에서 '평균적'이라는 말의 의미는 여러가지 방법이 존재하지만, 가장 기본적으로는 최소제곱법(Least-squares)을 사용합니다. 이 뜻은 각 점에서 선까지의 평균 거리의 제곱의 합을 최소화한다는 것을 의미합니다.

출처 : 연세대학교 일반물리학 실험실
출처 : 연세대학교 일반물리학 실험실

최소제곱법과 관련한 자세한 내용은 다른 블로그를 참고하시면 좋겠습니다. 어쨌든 결과적으로 우리가 하고자 하는 일은, 예측에 따른 오차를 최소하하고자함이며 이를 머신러닝에서는 Cost function이라고 정의합니다. 즉, 방정식을 자기마음대로 마구 예측해서 만들수 있겠지만, 그 중에서 비용(Cost)를 최소로 할 수 있는 직선만이 해당 문제에 있어서 가장 좋은 결과를 줄 것이라는 이야기입니다.

Andrew Ng 교수님의 수업에서는 최소제곱법의 Cost Function을 다음과 같이 정의하고 있습니다.

(앞의 1/2 등의 상수가 붙은 것은, 미분시에 계산을 수월하게 하기 위해 일종의 계수(coefficient)를 붙인 것으로 그 의미에 대해 깊이 고민하실 필요는 없습니다.) 어쨌든 우리의 목표는 저 Cost function 결과값을 최소(minimize)하는 θ1과  θ2를 찾는 것입니다.


해당 수식자체를 python 등의 프로그래밍 코드로 옮기는 것은 어렵지 않으나, 보통의 프로그래머가 벡터와 행렬, 외삽과 내삽 등의 다양한 수치해석적 개념을 이해하고 모든것을 직접 구현하기란 쉽지 않을 것입니다. 하지만 이미 다양한 선구자(!)들이 수많은 패키지와 API들을 개발해놓았고, 머신러닝을 공부하는 사람들은 그것을 단지 익혀서 적절하게 활용하면 됩니다. 수치해석 분야에서는 matlab이 유명하고, Andrew 교수님은 Octave를 통해 실습을 강조하셨습니다. 머신러닝 분야에서는 대표적으로 Theano, scikit 등이 있으며 최근에는 구글에서 공개한 Tensorflow가 대세입니다. 그 외에 Keras라는 것도 있는데 이것은 theano나 tensor flow를 wrapping한 패키지입니다. 자세한 것은 이곳에서 확인하실 수 있습니다. 


그렇다면 저는 지금부터 Tensorflow를 사용하여 선형 회귀 문제를 풀어보도록 하겠습니다. 우분투 환경에서 GPU를 활용하여 tensorflow를 구동하기 위한 환경을 구축하였습니다. Tensorflow 설치 가이드는 Yeramee님의 블로그를 참고하시면 좋습니다. 


그럼 간단한 예제문제를 풀어보도록 하겠습니다. 다음 문제는 한국형 온라인 공개강좌 K-MOOC에서 

KCS470 인공지능과 기계학습을 진행해주신 KAIST의 김기응 교수님과 오혜연 교수님의 강의에서 발췌하였습니다.

위의 문제를 보면, 10개의 x, y값이 주어져있고 해당 값들이 좌표상에 매칭되어 있습니다. 이 값들을 선형모델로 나타냈을 때 y = w1x + w0 로 표현할 수 있고, 각각의 w1과 w0값을 도출하는 것입니다. 여기서 w로 표현한 것은 위에서 θ1과  θ2과 동일하고, 쉽게 생각해서 그래프의 기울기 a와 y절편 b를 구하는 문제입니다. 그리고 얻어진 결과함수에서 x=5일때의 y 값을 찾아야 합니다.


다음과 같이 Tensorflow를 사용하여 선형회귀를 푸는 간단한 Python 소스코드를 작성해보았습니다.

문제에서 주어진 x_data와 y_data를 각각 배열에 할당해 줍니다. 이러한 방식은 데이터 갯수가 10개정도이기 때문에 사용할 수 있었지만, 보통은 데이터가 많아서 csv파일 형태로 주어지고 이를 입출력하는 형식으로 풀이하는 것이 바람직합니다.


다음으로는 W와 b값에 대한 범위를 지정합니다. 사람이 정답의 대략적인 범위를 추정할 수 있는 경우에 문제를 풀이하기 쉽기 때문에 컴퓨터에게 '노가다'의 범위를 줄여서 지정할 수 있습니다. hypothesis(가설)을 W * X  + b 로 주었습니다. W가 기울기이고 b가 y절편이 됩니다.


Cost Function 은 최소제곱법을 따라 정의하되, Tensorflow에서 제공하는 함수를 적절히 활용하여 

cost = tf.reduce_mean(tf.square(hypothesis - Y)) 로 아주 간단히 표현할 수 있습니다. 


가장 중요한 부분이 아래 부분입니다. 따로 떼어서 설명드리겠습니다.

이 부분이 바로 이 선형회귀 문제에서 핵심이 되는 a 값을 찾아내는 것입니다. GradientDescent 방법을 통해 최적값 a를 찾아내어서 cost를 최소화하는 방향으로 train하는 것입니다. 만약 제가 제시한 예제의 값과 다른 input을 넣고 똑같은 소스코드를 실행하셨을 때 정답이 잘 나오지 않는다면, 여러분은 저 a 값에 할당된 tf.Variable(0.01)에서 0.01값을 조금 더 크게 주시거나, 더 작게 주시는 등의 방법으로 여러분의 문제에 적합한 정답을 찾으실 수 있을 것입니다. 실제로 제가 github에서 구했던 코드는 0.1로 할당이 되어있었는데, 그 코드를 이 문제에 적용하면 함수가 발산(Divergence)해버립니다. 0.01로 세팅하니 정답을 찾을 수 있었습니다.

이 부분이 실제 Learning을 수행하는 과정입니다. xrange를 2001로 두고, 20번 수행할 때마다 중간결과를 print하여 진행상태를 모니터링하는 것입니다. feed_dict를 통해 본인이 지정하는 특정한 값을 파라미터로 전달해줄 수 있습니다.


문제에서 x=5일때의 y값을 찾으라했는데, 함수를 잘 구했다면 x값에 5를 대입한 후 직접 계산할 수 있을 것입니다. 하지만 1차 방정식이면 쉽겠지만 고차함수나 삼각함수 등이 섞여있다면 계산하기 불편하겠지요? 게다가 소수점 이하 자리가 길게 도출되기 때문에 오차가 발생하기 쉽습니다. 이를 위해서는 feed_dict를 사용하여 우리가 도출해낸 hypothesis에 자동으로 대입한 결과를 표출하도록 지시하면 됩니다.

answer = sess.run(hypothesis, feed_dict={X:5})


코드를 수행한 결과를 확인해보겠습니다.

2000번의 학습을 수행한 끝에 추정된 W값과 b값이 보입니다. 직접 돌려보시면 이해가 쉬우실 것입니다. 학습을 100번 미만으로 수행했을때만해도 오차가 꽤 큽니다. 그러다가 학습이 진행될 수록 점차적으로 근사하게 다가가는 것을 알 수 있습니다.


정답을 확인해볼까요?

확인해보면 소수점 이하의 자릿수들은 Run할때마다 조금씩 변동되는 것을 알 수 있습니다. 그정도 오차는 피할 수 없는 것이겠지요? 다만 K-mooc사이트에서 정답을 입력할 때는 소수점이하 몇자리가 좀 틀려도 정답으로 처리되는 것을 알 수 있습니다. 아마 둘째나 셋째자리까지만 맞으면 될 것으로 예상됩니다.


마지막으로, 파이썬의 matplotlib를 이용하여 직접 그래프에 도식화해보겠습니다. 코드는 다음과 같습니다.

계산 결과값은 다음과 같은 그래프가 주어집니다. 본인이 실험한 결과를 Visualization하여 확인할 때 유용하니 참고하시면 좋을 것 같습니다.

CPUU님의 창작활동을 응원하고 싶으세요?