주요 개념
- 단순 선형 회귀(SLR)
- 다중 선형 회귀(Multiple Linear Regression, MLR)
- 경사 하강법(Gradient Descent)
다중 선형 회귀(Multiple Linear Regression, MLR)는 단순 선형 회귀(SLR)와 달리 여러 입력변수가 들어간다. 따라서 다중 선형 회귀는 시각적으로 표현하기 힘들다. 하지만 시각화만 못할 뿐이지 기본 개념은 이전과 같다.
다중 선형 회귀를 이용해 도출되는 방정식은 아래와 같다.
$$ f(x)=w_{n}x_{n}+w_{n-1}x_{n-1}+...+w_2x_2+w_1x_1+b $$
늘어난 입력 변수와 그에 따른 가중치의 개수가 늘어난 것 외에 큰 차이는 없다. 단지 입력 변수가 많은 일차 함수일 뿐이다. 결국 최적의 가중치와 편향 값을 경사 하강법(Gradient Descent)을 통해 탐색한다.
벡터로 표현하면 아래와 같다.
$$ w^T=[b, w_1, w_2, ..., w_{n-1}, w_n], x=\begin{bmatrix}1\\x_1\\x_2\\\vdots\\x_{n-1}\\x_n\end{bmatrix} $$
다중 선형 회귀 모델 성능 평가에 사용될 MSE를 이용한 오차(손실) 함수는 아래와 같다.
$$ loss(w) = \frac{1}{2n}\sum^{n}_{i=1}(f(x_i)-y_i)^2 $$
\(x_i\)는 만약 x= [ [1, 2, 3, 4], [5, 6, 7, 8] ]이 있고, i=0일 때 x[0][i=0]인 [1, 2, 3, 4]를 의미한다.
일단 데이터부터 생성한다. 아래는 x1, x2와 y의 관계를 표현한 산포도와 python 코드이다.
import numpy as np
def make_data(size=100, noise=1):
x1 = np.linspace(-10, 15, size).reshape(-1, 1)*21
x2 = np.linspace(-10, 15, size).reshape(-1, 1)*3
y = 3*x1+x2+np.random.randn(size, 1)
noise = np.random.uniform(-abs(noise), abs(noise), size=y.shape)
yy = y + noise # 노이즈 추가
plt.scatter(x1, y)
plt.scatter(x2, y)
plt.suptitle("Sample Data", size=24)
plt.show()
return x1, x2, yy
아래는 직접 구현해본 다중 선형 회귀 python 소스 코드이다. 만약 지금의 방정식을 2차원 평면 위에 표시하면 당연히 이상하게 나올 것이다. 따라서 굳이 3차원에서 표현해보았다.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
def MLR(x, y, epochs=5000, learning_rate=0.00000001):
x1 = x[:, 0]
x2 = x[:, 1]
w1 = 0.0
w2 = 0.0
b = 0.0
n = len(x)
for i in range(epochs):
hypothesis = w1*x1+w2*x2+b
cost = np.sum((hypothesis-y)**2)/n
gradient_w1 = np.sum((w1*x1+w2*x2-y+b)*2*x1)/n
gradient_w2 = np.sum((w1*x1+w2*x2-y+b)*2*x2)/n
gradient_b = np.sum((w1*x1+w2*x2-y+b)*2)/n
w1 -= learning_rate * gradient_w1
w2 -= learning_rate * gradient_w2
b -= learning_rate * gradient_b
if i % 100 == 0:
print('Epoch ({:10d}/{:10d}) cost: {:10f}, W1: {:10f}, W2: {:10f}, b:{:10f}'.
format(i, epochs, cost, w1, w2, b))
result = (w1*x1+w2*x2+b).reshape(-1, 1)
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x1, x2, y)
ax.plot_surface(x1, x2, result, color="red")
plt.suptitle("MLR", size=24)
plt.title('w1=' + str(round(w1, 3))+'w2=' + str(round(w2, 3))+', b=' + str(round(b, 3)))
plt.show()
return w1, w2, b, result
x1, x2, y = make_data(size=100, noise=6)
data = np.concatenate((x1, x2), axis=1)
w1, w2, b, res = MLR(data, y)
data = np.concatenate((x1, x2, y, res), axis=1)
df = pd.DataFrame(data, columns=['x1', 'x2', 'y', 'predict'])
print("결정계수: ", r2_score(y, res))
print("상관계수: \n", df.corr())
print("MSE: ", mean_squared_error(y, res))
# 결정계수: 0.10524094053563571
# 상관계수:
# x1 x2 y predict
# x1 1.000000 1.000000 0.999974 1.000000
# x2 1.000000 1.000000 0.999974 1.000000
# y 0.999974 0.999974 1.000000 0.999974
# predict 1.000000 1.000000 0.999974 1.000000
# MSE: 207533.85726264297
위 직접 구현한 MLR에서 learning rate를 더 크게 만들어 테스트해보면 overshooting되는 현상 또한 확인할 수 있다.
다음은 편하게 sklearn 라이브러리의 LinearRegression 함수를 이용해 구현한 결과이다.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
def LR(x, y):
x1 = x[:, 0]
x2 = x[:, 1]
model = LinearRegression()
model.fit(x, y)
w1 = model.coef_[0][0]
w2 = model.coef_[0][1]
b = model.intercept_[0]
result = (w1*x1+w2*x2+b).reshape(-1, 1)
print('w1: ', model.coef_[0][0], ", w2: ", model.coef_[0][1], ", b:", model.intercept_[0])
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x1, x2, y)
ax.plot_surface(x1, x2, result, color="red")
plt.suptitle("LR function", size=24)
plt.title('w1=' + str(round(w1, 3))+'w2=' + str(round(w2, 3))+', b=' + str(round(b, 3)))
plt.show()
return w1, w2, b, result
x1, x2, y = make_data(size=100, noise=6)
data = np.concatenate((x1, x2), axis=1)
_, _, _, res = LR(data, y)
data = np.concatenate((x1, x2, y, res), axis=1)
df = pd.DataFrame(data, columns=['x1', 'x2', 'y', 'predict'])
print("결정계수: ", r2_score(y, res))
print("상관계수: \n", df.corr())
print("MSE: ", mean_squared_error(y, res))
# 결정계수: 0.9999470036176753
# 상관계수:
# x1 x2 y predict
# x1 1.000000 1.000000 0.999974 1.000000
# x2 1.000000 1.000000 0.999974 1.000000
# y 0.999974 0.999974 1.000000 0.999974
# predict 1.000000 1.000000 0.999974 1.000000
# MSE: 12.255338117761944
결정계수가 LinearRegression 함수 것이 더 높으니 좀 더 나은 모델일 것으로 생각된다.
경사 하강법을 숙지하고 있으면 단순 선형 회귀나 다중 선형 회귀나 기본 개념은 동일해 어렵진 않다.
아래는 전체 python 소스 코드이다.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
def make_data(size=100, noise=1):
x1 = np.linspace(-10, 15, size).reshape(-1, 1)*21
x2 = np.linspace(-10, 15, size).reshape(-1, 1)*3
y = 3*x1+x2+np.random.randn(size, 1)
noise = np.random.uniform(-abs(noise), abs(noise), size=y.shape)
yy = y + noise # 노이즈 추가
plt.scatter(x1, y)
plt.scatter(x2, y)
plt.suptitle("Sample Data", size=24)
plt.show()
return x1, x2, yy
def MLR(x, y, epochs=5000, learning_rate=0.00000001):
x1 = x[:, 0]
x2 = x[:, 1]
w1 = 0.0
w2 = 0.0
b = 0.0
n = len(x)
for i in range(epochs):
hypothesis = w1*x1+w2*x2+b
cost = np.sum((hypothesis-y)**2)/n
gradient_w1 = np.sum((w1*x1+w2*x2-y+b)*2*x1)/n
gradient_w2 = np.sum((w1*x1+w2*x2-y+b)*2*x2)/n
gradient_b = np.sum((w1*x1+w2*x2-y+b)*2)/n
w1 -= learning_rate * gradient_w1
w2 -= learning_rate * gradient_w2
b -= learning_rate * gradient_b
if i % 100 == 0:
print('Epoch ({:10d}/{:10d}) cost: {:10f}, W1: {:10f}, W2: {:10f}, b:{:10f}'.
format(i, epochs, cost, w1, w2, b))
result = (w1*x1+w2*x2+b).reshape(-1, 1)
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x1, x2, y)
ax.plot_surface(x1, x2, result, color="red")
plt.suptitle("MLR", size=24)
plt.title('w1=' + str(round(w1, 3))+'w2=' + str(round(w2, 3))+', b=' + str(round(b, 3)))
plt.show()
return w1, w2, b, result
def LR(x, y):
x1 = x[:, 0]
x2 = x[:, 1]
model = LinearRegression()
model.fit(x, y)
w1 = model.coef_[0][0]
w2 = model.coef_[0][1]
b = model.intercept_[0]
result = (w1 * x1 + w2 * x2 + b).reshape(-1, 1)
print('w1: ', model.coef_[0][0], ", w2: ", model.coef_[0][1], ", b:", model.intercept_[0])
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x1, x2, y)
ax.plot_surface(x1, x2, result, color="red")
plt.suptitle("LR function", size=24)
plt.title('w1=' + str(round(w1, 3)) + 'w2=' + str(round(w2, 3)) + ', b=' + str(round(b, 3)))
plt.show()
return w1, w2, b, result
x1, x2, y = make_data(size=100, noise=6)
data = np.concatenate((x1, x2), axis=1)
w1, w2, b, res = MLR(data, y)
# _, _, _, res = LR(data, y)
data = np.concatenate((x1, x2, y, res), axis=1)
df = pd.DataFrame(data, columns=['x1', 'x2', 'y', 'predict'])
print("결정계수: ", r2_score(y, res))
print("상관계수: \n", df.corr())
print("MSE: ", mean_squared_error(y, res))
관련 포스트
2022.02.23 - [Data Science/Statistics] - [Python] 단순 선형 회귀(Simple Linear Regression, SLR)의 이해와 구현
참고 자료
소스 코드