머신 러닝은 주로 어떤 데이터를 넣고 그 결과값을 예측하는 데 쓰인다. 이 중에서도 가장 간단한 방법은 하나의 선을 바탕으로 모델링을 하는 것.

예를 들어 위 그래프처럼 키와 몸무게로 그래프를 그렸다고 하자.
이 때 각 값에 대해 최소한의 오차를 가지는 하나의 선형 함수가 있을 것이고,
선형 회귀는 이 함수를 구하는 과정이다.
위와 같은 직선은 방정식으로 와 같은 식을 가지는데, 계수와 절편을 찾아내 식을 세우는 모델로 생각하면 될 듯.
독립변수가 하나인 선형 회귀를 단순 선형 회귀, 두개 이상인 선형 회귀를 다중 선형 회귀라고 한다.
당연히 저렇게 세운 직선의 방정식과 실제 데이터에는 차이가 있다. 이를 손실(Loss)이라 한다.
이 오차의 절대적인 양을 측정하기 위해 대체로 평균제곱오차(Mean Squared Error, MSE)를 사용하는데, 식은 다음과 같다.
그냥 오차제곱끼리 다 더해서 평균내는 것.
물론 이 말고도 평균절대오차, 후버 손실 등의 다른 방법도 있다 한다.
손실을 그래프로 나타내면 아래와 같은 모양이 된다고 한다.

따라서 손실을 최대한 줄일 수 있는 방법이 필요한데, 이때 사용되는 기법이 경사하강법.
경사를 따라 조금씩 내려가면서 손실 최저점을 찾는 방법이다.
다만 내려가는 정도가 너무 작으면 시간이 오래 걸리고, 너무 크면 최솟값을 휙휙 지나다녀 문제가 생길 수 있다고.
이렇게 기울기와 절편을 수정해가면서 손실을 최소화하다보면, 특정 최적값에서 수렴하여 그 이후의 반복은 의미가 없어진다.
실습은 [혼자 공부하는 머신러닝+딥러닝]을 참고하였다.
import numpy as np
perch_length = np.array([8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0, 21.0,
21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5, 22.5, 22.7,
23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5, 27.3, 27.5, 27.5,
27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0, 36.5, 36.0, 37.0, 37.0,
39.0, 39.0, 39.0, 40.0, 40.0, 40.0, 40.0, 42.0, 43.0, 43.0, 43.5,
44.0])
perch_weight = np.array([5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0, 110.0,
115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0, 130.0,
150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0, 197.0,
218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0, 514.0,
556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0, 820.0,
850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0, 1000.0,
1000.0])
import matplotlib.pyplot as plt
plt.scatter(perch_length, perch_weight)
plt.show()

이쁜 직선형은 아니긴 하네..
from sklearn.model_selection import train_test_split
#특성이 길이 하나이므로 2차원 배열을 만들어줘야 함
#훈련 세트와 테스트 세트 분류
train_input, test_input, train_target, test_target =
train_test_split(perch_length, perch_weight, random_state=42)
train_input = train_input.reshape(-1, 1) #원래 사이즈 유지하면서 2차원으로
test_input = test_input.reshape(-1, 1)
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(train_input, train_target)
print(lr.predict([[50]])) #1241.xxxx
훈련 세트를 통해서 학습시킨 객체에 길이가 50인 농어의 무게를 예측해보라 하니, 1241정도가 나왔다.
이 때 계수와 절편은 다음과 같이 확인할 수 있다.
# 계수와 y절편
print(lr.coef_, lr.intercept_)
이걸로 선형 회귀로 만들어진 직선을 그려볼 수 있다.
# 직선 그려보기
#데이터 분포
plt.scatter(train_input, train_target)
#선형회귀 직선
plt.plot([15, 50], [15*lr.coef_+lr.intercept_, 50*lr.coef_+lr.intercept_])
#농어
plt.plot(50, 1241.8, marker='^')
plt.show()

예측 데이터가 직선상에 있음을 볼 수 있음.
선형 회귀에 정확한 답은 없으므로, 얘네는 성능을 확인할 때 결정계수()라는 것을 쓴다.
식은 .
(ssr 뭐시기를 쓰는 휘황찬란한 식도 있는데 아직 짬 부족으로 못 씀. 열심히 공부하고 따로 포스팅해야지 !)
# R^2점수
print(lr.score(train_input, train_target))
print(lr.score(test_input, test_target))
#0.93, 0.82
비교했을 때 테스트 점수가 너무 낮으므로 과대적합같기도 하지만, 전체적으로 점수가 너무 낮다. 오히려 과소적합에 가깝다.
또 위의 그림을 봤을 때, 20cm 미만의 물고기들은 음수의 무게를 가지게 된다. 이상하다 !
이럴 때는 최적의 곡선을 찾아보자.
다중 선형 회귀를 위해서는 축을 제곱한 값도 훈련 세트에 넣어줘야 한다.
이 때 numpy의 column_stack을 이용한다.
그 후 학습 !
# 다항 회귀
train_poly = np.column_stack((train_input**2, train_input))
test_poly = np.column_stack((test_input**2, test_input))
lr = LinearRegression()
lr.fit(train_poly, train_target)
print(lr.predict([[50**2, 50]]))
#1573.984
계수와 절편도 확인하자.
print(lr.coef_, lr.intercept_)
#[ 1.01433211 -21.55792498] 116.0502107827827
계수가 두 개가 나오는데, 각각 이차항과 일차항의 계수가 된다.
다중 선형회귀로 구해낸 그래프도 그려보자.
# 다항식 그려보기
point = np.arange(15, 50)
#테스트 데이터
plt.scatter(train_input, train_target)
#이차방정식
plt.plot(point, 1.01*point**2-21.55*point+116.05)
#농어 데이터
plt.scatter([50], [1574], marker='^')
plt.show()

직선보다 한결 옳은 것을 알 수 있다.
그렇담 결정계수는?
#R^2 결정계수 확인
print(lr.score(train_poly, train_target))
print(lr.score(test_poly, test_target))
#0.9706807451768623
#0.9775935108325122
두 값 모두 충분히 크고, 차이가 거의 나지 않으므로 과대적합-과소적합 또한 해결되었다고 볼 수 있다.
오늘은 선형 회귀에 대한 것을 포스팅해봤다. 아직 겉핥기 느낌으로 배운 것 같아서 개인적으로 깊게 들어가보고 싶은 욕심이 생기긴 한데, 스불재(스스로 불러온 재앙)에 짓눌려서 당장 해야 할 것이 좀 많다... ㅋㅋㅋㅋㅋ
그나저나 선형 회귀는 이름이 참 멋있는 것 같다.