본문 바로가기

머신러닝

[머신러닝] 단순 선형 회귀(Simple Linear Regression)

반응형

 

 

[목차]

 

1. 선형 회귀

 

2. 단순 선형 회귀 실습

 

 

  1. 선형 회귀(Linear Regression)

 

선형 회귀(Linear Regression)는 널리 사용되는 대표적인 회귀 알고리즘이다. 선형 회귀는 종속 변수 y와 하나 이상의 독립 변수 x와의 선형 상관관계를 모델링하는 기법이다. 만약 독립 변수 x가 1개라면 단순 선형 회귀라고 하고, 2개 이상이면 다중 선형 회귀라고 한다. 

 

1) 단순 선형 회귀 (Simple Linear Regression)

 

단순 선형 회귀는 $y = Wx + b$의 식으로 나타난다. 머신러닝에서는 독립 변수 x에 곱해지는 W값을 가중치(weight), 상수항에 해당하는 b를 편향(bias)이라고 부른다. 

따라서 단순 선형 회귀 모델을 훈련하는 것은 적절한 W와 b값을 찾는 것이다. 그래프의 형태는 직선으로 나타난다. 

 

2) 다중 선형 회귀 (Multiple Linear Regression)

 

다중 선형 회귀는 $y = W_1x_1 + W_2x_2 + ... + W_nx_n + b$의 식으로 나타난다. 여러 독립 변수에 의해 영향을 받는 경우이다. 만약 2개의 독립 변수면 그래프는 평면으로 나타날 것이다. 

 

 

  2. 단순 선형 회귀 실습

 

이전 게시글에서 K-NN 회귀의 한계를 알아보았다. (https://rebro.kr/184)

하지만, 선형 회귀를 이용하면 이러한 한계를 극복할 수 있다. 농어의 길이만을 이용해서 무게를 예측하기 때문에, 정확하게는 하나의 독립 변수를 사용하는 단순 선형 회귀(Simple Linear Regression)다. 

 

이전의 내용과 이어서, 농어 데이터를 가장 잘 나타낼 수 있는 직선을 찾아야 할 것이다. 

위의 세 그래프 중 어떤 직선이 농어 데이터를 가장 잘 표현할까? 두 번째 그래프의 직선이 데이터를 잘 표현함을 쉽게 알 수 있다. 

첫 번째 그래프는 모든 농어의 무게를 동일하게 예측한다. 만약 이 직선이 훈련 셋의 평균에 가깝다면 $R^2$의 값은 0에 가까워질 것이다. 

반면, 마지막 그래프는 완전히 반대로 예측한다. 이는 $R^2$의 값이 음수가 될 수도 있다. 

이제 이 직선을 실제로 어떻게 구하는지 알아보자. 

 

먼저, 사이킷런에서 선형 회귀를 구현해둔 클래스를 제공한다. 

sklearn.linear_model 패키지 아래 LinearRegression 클래스를 사용하면 된다. 

(데이터는 이전 게시글과 동일하다)

from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_input, train_target)
print(lr.predict([[100]])) # [3192.69585141]

 

이전에는 길이가 100인 농어의 무게를 예측했을 때 예상보다 훨씬 작은 무게가 나왔었다. 또 길이가 아무리 커지더라도 무게가 늘어나지 않았다. 

하지만 선형 회귀를 이용한 결과 위와 같이 무게가 약 3192로 예상대로 높게 나오는 것을 볼 수 있다. 과연 적절한 직선을 구해내었을까?

 

길이라는 하나의 특성을 사용했기 때문에 그래프는 직선인 $y = ax + b$의 형태로 나타난다. LinearRegression 클래스가 찾은 a와 b는 각각 lr 객체의 coef_intercept_ 변수에 저장된다.  

print(lr.coef_, lr.intercept_) # [39.01714496] -709.018644953547

 

즉, $y = (39.01...)x - (709.0186...) $의 직선이 구해졌다는 의미이다. 이제 이 직선을 그래프로 그려보자. 

 

구한 직선의 방정식을 함수 y = f(x)라고 하면, 직선은 두 점 (15, f(15))와 (100, f(100))을 연결하는 직선과 동일할 것이다. 

따라서 plot 함수를 이용하여 두 점을 연결하는 선을 그린다. 

plt.scatter(train_input, train_target)
plt.plot([15, 100], [15*lr.coef_ + lr.intercept_, 100*lr.coef_ + lr.intercept_])
plt.scatter(100, 3192, marker='^')
plt.show()

적절한 직선을 찾은 것을 볼 수 있다. 이제, 훈련 셋의 범위를 벗어난 농어의 무게도 예측할 수 있게 되었다. 

 

그런데, 그래프가 직선이기 때문에 길이가 10 이하로 내려가면 무게를 음수로 예측하게 되는 것을 그래프에서 볼 수 있는데, 이는 실제로 일어날 수 없는 일이다.  

print(lr.score(train_input, train_target)) # 0.9398463339976041
print(lr.score(test_input, test_target)) # 0.8247503123313562

 

훈련한 모델에 대한 $R^2$ 점수 또한, 훈련 셋과 테스트 셋의 점수에 차이가 있을뿐더러 훈련 셋의 점수도 그렇게 높지 않다. 

 

그 이유는 실제로 농어의 데이터는 직선보단 곡선에 가깝기 때문이다. 그렇기 때문에 최적의 직선을 찾기보단 최적의 곡선을 찾는다면 더 정확한 예측이 가능할 것이다. 따라서 가장 간단한 곡선인 2차 방정식을 구해보자. 

 

2차 방정식을 구하기 위해서 길이를 제곱한 항을 추가한다. 따라서, 길이의 제곱이 배열에 추가되어야 하는데, 이는 넘파이의 column_stack 함수를 이용하면 간단하게 해결할 수 있다. 

train_poly = np.column_stack((train_input**2, train_input))
test_poly = np.column_stack((test_input**2, test_input))

 

column_stack 함수는 열 방향으로 배열을 합쳐주는 기능을 제공한다. 자세한 설명은 링크를 참고하자. 

따라서 1열에는 길이의 제곱, 2열에는 길이가 들어있는 2차원 배열이 train_poly와 test_poly에 할당된다. 

다시 선형 회귀 모델을 훈련해보자. 

lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.coef_, lr.intercept_) # [  1.01433211 -21.55792498] 116.05021078278259

 

lr.coef_를 출력해보니 [1.014.... -21.5579....] 로 나왔다. 이는 2차 방정식의 이차항의 계수가 1.014..., 일차항의 계수가 -21.5579... 를 의미한다. 즉, 대략 $y = 1.014x^2 - 21.5579x + 116.05$의 그래프를 학습하였다. 

이렇게 다항식을 사용한 선형 회귀를 다항 회귀(Polynomial Regression)라고 부른다.

다중 회귀랑 헷갈릴 수 있는데, 다중 회귀는 2개 이상의 독립 변수가 존재하는 형태이고, 다항 회귀는 하나의 독립 변수를 이용하여 차수를 높이는 개념이라고 생각하면 된다. 

 

이제 훈련한 모델에 길이가 100인 농어의 무게를 예측해보자. 훈련 과정과 동일하게 길이의 제곱을 원래 길이와 함께 넣어주어야 함을 주의해야 한다. 

print(lr.predict([[100**2, 100]])) # [8103.57880667]

 

이전의 무게 결과인 약 3192보다 더 높은 값을 예측한 것을 볼 수 있다. 모델이 적절하게 훈련이 되었을까?

print(lr.score(train_poly, train_target)) # 0.9706807451768623
print(lr.score(test_poly, test_target)) # 0.9775935108325121

 

아직 약간의 과소적합이 남아있긴 하지만, 그래도 훈련 셋과 테스트 셋의 점수가 많이 높아졌음을 알 수 있다. 

산점도도 그려보자.

point = np.arange(15, 100)
plt.scatter(train_input, train_target)
plt.plot(point, 1.01*point**2 - 21.6*point +  116.05)
plt.scatter([100], [8103], marker='^')
plt.show()

 

2차 방정식의 그래프는 짧은 직선들을 이어서 그리면 곡선처럼 표현할 수 있다. 그러므로 [15, 100]을 1씩 나누어서 직선들을 이어서 그린다. 

직선 그래프보다 훨씬 샘플에 맞게 그래프가 그려졌다. 하지만 여전히 과소적합 문제는 해결되지 않았기 때문에 조금 더 복잡한 모델이 필요할 것이다. 다음엔 다중 회귀에 대해 알아보자. 

 

 

 

 

참고)

- https://wikidocs.net/21670

- 혼자 공부하는 머신러닝+딥러닝(박해선, 한빛미디어)

PC로 보시는 것을 권장합니다. 

피드백은 언제나 환영입니다. 댓글로 달아주세요 ^-^

 

 

반응형