brunch

You can make anything
by writing

C.S.Lewis

by 코딩하는 수학쌤 Nov 15. 2021

43. 파이썬으로 간단히 구현해보는 선형 회귀

[5악장-concerto] 머신러닝과 코딩

 여기서 다룰 선형 회귀 분석은 31번 글에서 다루었던 선형 회귀 분석의 예시 문제입니다. 파이썬을 아직 잘 모르는 분들은 이 내용을 보면서 '파이썬은 이런 형태로 코딩을 하는구나.' 라고 이해하면 좋겠습니다. 파이썬을 기본적으로 아는 분들이라면 금방 이해할 수 있을만큼 쉬운 코딩입니다. 


 선형 회귀 분석이 무엇인지 기억나세요? 선형 회귀 분석은 데이터를 가장 잘 예측하는 직선을 찾는 머신러닝 방법입니다. 

위의 표의 내용을 5장에서 평균 제곱 오차로 손실함수를 택해서 최적화를 진행한 결과 y=0.95x라는 식을 얻었습니다. 이 방법을 실제 경사하강법을 활용해 직접 구해보려고 합니다. 


 전체적인 프로그램의 순서는 간단히 다음과 같이 진행할 수 있습니다. 


먼저 전체 코드를 한 번 살펴본 후 부분적으로 나누어보겠습니다. 아래 함수는 위의 과정을 간단하게 나타난 하나의 예시입니다. 그림을 그리는 matplotlib과 임의의 값을 생성하는 random 외의 library는 일부러 다루지 않았습니다. :)


import matplotlib.pyplot as plt

from random import random


#데이터 입력하기

data=[[4,6],[8,5],[12,15],[16,12],[20,20]]

x=[4,8,12,16,20]

y=[6,5,15,12,20]


#그래프로 나타내기.

plt.figure(figsize=(5,5))

plt.scatter(x, y)

plt.show()


#초깃값과 학습률, 반복 횟수 정하기

a = random()

lr = 0.00002

epochs = 2001

print(a)


#경사하강법 실행하기

for i in range(epochs): 

    a_diff = 2*176*a-1672/5 

    a = a - lr * a_diff  

    if i % 100 == 0:    

         print("epoch=%.f, 기울기 a=%.04f" % (i, a))


#결과 나타내기

y_pred=[]

for i in range(5) :

    y_pred.append(a*x[i])

plt.scatter(x, y)

plt.plot([min(x), max(x)], [min(y_pred), max(y_pred)])

plt.show()



이 과정은 딥러닝까지 다루고 있지 않기 때문에 굳이 tensorflow를 사용하지 않았습니다. 게다가 데이터를 복잡하게 처리하지 않기 때문에 numpy나 분석을 위한 pandas도 사용하지 않았습니다. (물론 numpy를 쓰면 좀 더 간편하게 처리가 됩니다만, 보통 초급 과정에서 list를 다루기 때문에 list를 활용해서 구현해봤습니다.)다만 처리 결과를 시각화하기 위해 matplotlib의 라이브러리에 있는 pyplot 함수를 사용하였습니다.



① 시작

import matplotlib.pyplot as plt
from random import random

 import라고 하는 명령어는 명령어들의 묶음인 라이브러리를 가져와서 사용할 때 사용합니다. 쉽게 이야기하면 파이썬에 포함된 matplotlib이라는 연장통을 꺼내겠다는 의미죠.  여기서는 수학의 다양한 차트, 그래프 등을 그려주는 matplotlib.pyplot 이라는 연장통을 꺼낼텐데, 너무 길어지니까 plt라고 줄여서 부르겠다는 명령어가 

 import matplotlib.pyplot as plt 
입니다. 그러면 앞으로 matplotlib.pyplot에 있는 명령어를 쓸 때는 plt.명령어 형태로 사용하게 됩니다. 

 시작 위치를 임의로 정하기 위해 random이라는 라이브러리에서 random 함수도 불러옵니다. random 라이브러리는 임의로 어떤 값들을 발생시키거나 표본을 임의추출할 때 사용하는 책들이 꽃혀있는 책장이라고 생각하면 됩니다. 책장 전체가 아니라 그 중 random 함수만 불러오는 명령문이 from random import random입니다.




② 데이터 입력하기

y=[6,5,15,12,20]
data=[[4,6],[8,5],[12,15],[16,12],[20,20]]
x=[4,8,12,16,20]

 이 예시에서는 데이터의 개수가 많지 않으므로 data라는 리스트에 그냥 수작업으로 데이터를 직접 입력을 했습니다. 만약 데이터가 수백개가 된다고 할 때는 위의 작업처럼 할 수 없죠. 만약 엑셀처럼 csv 파일에 저장이 되었을 때는 pandas 라이브러리를 활용해 파일에 저장된 데이터를 쉽게 받아오기도 합니다. 받아온 데이터의 x좌표, y좌표를 따로 떼어내어 리스트로 담았습니다. 




③ 자료 표현

#그래프로 나타내 봅니다.
plt.figure(figsize=(5,5))
plt.scatter(x, y)
plt.show()


이제 앞에서 불러왔던 matplotlib이라는 라이브러리를 활용해 데이터를 시각화해봅시다. 

plt.figure(figsize=(5,5))

: 그림을 그리려고 합니다. 사이즈는 가로 5, 세로 5의 크기라는 뜻입니다.

plt.scatter(x, y)

: plt 중에서 산점도를 그립니다. 앞의 x 리스트, y리스트의 값들을 각각 대응시킵니다.

plt.show()

: 방금 만든 그림을 보이도록 하는 명령입니다.

짜잔! 아주 멋있지는 않을지 모르지만 점 5개가 그려진 산점도가 그려졌습니다.




④ 초깃값, 학습률, 반복 횟수 정하기

a = random()
lr = 0.00002
epochs = 2001
print(a)

결과 : 0.1914743880188018 (random 함수 때문에 실행 때마다 다릅니다)

a = random()

a의 값은 y=ax의 기울기입니다. a의 값은 random()으로 생성되는 0과 1사이의 임의의 값을 가져오도록 했기 때문에 실행할 때마다 값이 달라질 수 있습니다. 이번의 실행에서는 0.19147… 로 시작하는 값을 가져왔습니다.

lr = 0.00002

: 경사하강법의 속도를 조절하는 학습률은 0.00002로 설정했습니다. 학습률은 보통은 매우 작은 값으로 설정합니다. 만약 성공할 경우 조금 늘릴 수도 있고, 너무 커서 최적화에 실패할 경우 값을 줄여나갑니다. 

epochs = 2001

: epoch는 경사하강법에서 a의 값을 업데이트 하는 횟수를 의미합니다. 이 프로그램에서는 약 2000번 정도 반복하도록 설정하였습니다. (여기서 epoch를 2001로 설정한 것은 나중에 반복구문에서 2000번째 결과를 출력하기 위해 임의로 한 것입니다. 





⑤ 경사하강법으로 a의 값 업데이트 하기.

for i in range(epochs): 
    a_diff = 2*176*a-1672/5 
    a = a - lr * a_diff  
    if i % 100 == 0:    
            print("epoch=%.f, 기울기 a=%.04f" % (i, a))


for i in range(epochs): 

for라는 구문은 정해진 횟수만큼 정해진 절차를 반복하는 구문입니다. 우리는 epoch의 값을 2001으로 주었기 때문에 기울기를 2000번 업데이트를 할 예정입니다.


a_diff = 2*176*a-1672/5 

 이 식은 입력된 값에 평균 제곱 오차법을 적용해 구한 손실함수의 접선의 기울기를 구하는 방법입니다. 이렇게 접선의 기울기를 계산하는 함수(도함수)를 구하는 방법은 주로 고등학교 2학년 때 수강하는 수학2의 미분 단원에서 배웁니다. (미분을 구하는 과정을 쓰지는 않았습니다..)

 평균 제곱 오차로 구한 손실함수에서 x=a인 지점의 접선의 기울기를 계산해 a_diff 라는 변수에 담아둡니다. 


a = a - lr * a_diff  

 기존의 함수값에 기울기x학습률만큼 이동을 합니다. 접선의 기울기가 음수일 때는 + 방향으로, 접선의 기울기가 음수일 때는 - 방향으로 이동하기 때문에 lr 앞에 - 부호가 붙어있습니다. 


if i % 100 == 0:    

   print("epoch=%.f, 기울기 a=%.04f" % (i, a))

a의 값이 어떻게 변화가 되어가는지 확인하기 위한 코드입니다. i는 0부터 2000까지 증가하는데, i가 100의 배수가 되면 epoch의 값과 a의 값을 출력하라는 명령입니다. a의 값을 기울기로 출력한 이유는 a의 값이 y=ax의 기울기이기 때문입니다.


실제 수행한 결과는 아래와 같습니다. 

epoch=0, 기울기 a=0.1968

epoch=100, 기울기 a=0.5784

epoch=200, 기울기 a=0.7667

epoch=300, 기울기 a=0.8595

epoch=400, 기울기 a=0.9054

epoch=500, 기울기 a=0.9280

epoch=600, 기울기 a=0.9391

epoch=700, 기울기 a=0.9446

epoch=800, 기울기 a=0.9474

epoch=900, 기울기 a=0.9487

epoch=1000, 기울기 a=0.9494

epoch=1100, 기울기 a=0.9497

epoch=1200, 기울기 a=0.9498

epoch=1300, 기울기 a=0.9499

epoch=1400, 기울기 a=0.9500

epoch=1500, 기울기 a=0.9500

epoch=1600, 기울기 a=0.9500

epoch=1700, 기울기 a=0.9500

epoch=1800, 기울기 a=0.9500

epoch=1900, 기울기 a=0.9500

epoch=2000, 기울기 a=0.9500

위의 결과를 보면 a의 값이 임의로 0.19를 받아왔지만 epoch 수가 증가함에 따라 0.95로 변화된 것을 볼 수 있습니다. 이미 우리는 0.95라는 정답을 알고 있죠? 0.95로 다가갈수록 기울기의 변화 속도가 매우 느려집니다. 결국 일정한 epoch 이상 되면 기울기의 변화가 없죠. 




⑥ 실제로 구한 그래프를 한 번 그려보기.

y_pred=[]
for i in range(5) :
      y_pred.append(a*x[i])
      plt.scatter(x, y)
plt.plot([min(x), max(x)], [min(y_pred), max(y_pred)])
plt.show()

위의 명령어는 실제 데이터를 산점도로 그리고, 경사하강법으로 구한 기울기로 y=ax의 그래프를 그려본 것입니다.


회귀 분석에서 경사하강법으로 구한 추세선을 그려봤습니다!

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari