회귀분석(다중회귀)

이전 글에는 하나의 독립변수를 사용한 예측을 테스트했습니다. 이번에는 2개 이상의 독립변수를 포함하는 다중회귀분석에 대해서 동일한 데이터셋을 활용해서 알아보겠습니다.

dataset = pd.read_csv('./fitness.csv')
dataset[0:10]

지난 예제에서 runpulse를 통해서 maxpulse를 예측하는 회귀분석을 수행했습니다. 여기서 runpulse는 독립변수로서 종속변수인 maxpulse에 영향을 주는 변수입니다. 이번에도 maxpulse를 예측할 것인데 독립변수가 age, weight, oxygen, runtime, runpulse, rstpulse 이렇게 6개입니다.

먼저 예측할 maxpulse를 제외한 데이터셋을 만들어줍니다. 이것을 dataset2라고 정의하겠습니다. dataset2은 6개의 column과 31개의 row를 가진 [31 × 6] 행렬이 됩니다. 이것은 이전 시간에 하나의 독립변수를 통해서 예측을 하기 위해서 작성했던 수식인 y = Xw+b를 확장한 개념입니다.

이것을 풀어서 쓰게 되면 y = X1w1 + X2w2 + X3w3 + X4w4 + X5w5 + X6w6 + b가 되는 것이고 이런 y가 31개가 있다는 의미가 됩니다. 그러니까 각각의 요소에 w1~w6개의 값을 곱해주는 [31 × 6] × [6 × 1] + b의 행렬곱 연산이 되고 이는 [1 × 6] × [6 × 1] + b 이런 벡터의 내적의 합이 31개가 된다는 동일한 의미입니다.

dataset2 = dataset[dataset.columns[0:-1]]
print(dataset2.values)

이제 이전에 단순회귀에서 했던것처럼 sklearn 패키지의 LinearRegression을 임포트 하고 기울기와 절편을 계산합니다. fit()에 두개의 인자값을 주는데 하나는 6개의 독립변수들의 값이 있는 2차원 행렬이고 또 하나는 실제 계산 값이 들어있는 데이터셋입니다. 여기서는 알고자 하는 값인 maxpulse 값이 되겠습니다.

from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(dataset2.values, dataset['maxpulse'].values.reshape(-1,1))
print('W:{}, b:{}'.format(lr.coef_, lr.intercept_))
#W:[[-0.00794636  0.09986232  0.57982346  1.18233886  0.87556793  0.05439718]], b:[-25.0634534]

간단히 계산이 완료되었고 예상했던것처럼 알고자 했던 각 독립변수의 가중치 값인 6개와 절편 값을 얻었습니다. 이 모델이 어느정도 결과값과 일치하는지 차이를 보기 위해서 실제값과 예측값을 그래프로 시각화해보겠습니다.

plt.plot(lr.predict(dataset2.values),'*')
plt.plot(dataset['maxpulse'],'x')

실제값 “x”와 예측값 “*”를 비교해보니 일부 오차가 큰 것들도 있지만 제법 합리적으로 예측을 한것같습니다. 그리고 이 값은 아래와 같이 행렬 곱으로 계산할 수도 있습니다. 여기서 x는 독립 변수들의 값이고 y는 LinearRegression을 통해서 얻은 w, b 값입니다. 이 값들을 활용해서 y = Xw + b 형식으로 풀어낸 값과 동일합니다.

x = dataset2.values
y = lr.coef_.reshape(-1,1)
predict = np.matmul(x,y) + lr.intercept_

회귀분석(단순회귀)

선형모델은 100여년 전부터 사용되어 왔고 현재도 널리 사용되는 모델입니다. 이 모델은 입력 특성에 대한 선형함수 모델을 만들어서 예측을 수행하는 모델입니다. 간단히 직선의 방정식을 생각하면 됩니다. 기울기 w와 y축과 만나는 절편 b 값을 알아내는 것이 해당 모델의 핵심이라고 할 수 있습니다.

예를 들어 아래와 같은 fitness.csv 데이터셋이 있다고 가정해봅니다. 해당 데이터는 [‘age’, ‘weight’, ‘oxygen’, ‘runtime’, ‘runpulse’, ‘rstpulse’,’maxpulse’]의 7개의 Feature로 되어 있습니다. pandas 패키지로 파일을 읽어서 상위 10개의 데이터를 표시해봅니다.

dataset = pd.read_csv('./fitness.csv')
dataset[0:10]

각각의 column 혹은 feature 정보는 관계가 있을 수도 혹은 별다른 관계가 없을 수도 있습니다. 예를 들어서 위의 feature 정보를 통해서 아래와 같이 3개의 산점도(scatter)를 그려보면 1번은 두개의 변수간에 상관관계가 약하다고 할 수 있지만 아래의 두개 즉, runpulse-maxpulse, oxygen-runtime은 어느정도 상관관계가 있다고 할 수 있습니다. 이것을 방향에 따라서 양의 상관관계, 음의 상관관계라고 할 수 있습니다.

2번째 그래프와 같이 runpulse과 maxpulse의 관계를 통해서 본다면 runpulse 값이 증가할 수록 maxpulse 값도 함께 증가하는 것을 알 수 있습니다. 그렇기 때문에 이런 관계를 통해서 어떤 규칙성을 만들면 runpulse가 주어졌을 때 maxpulse를 어느정도는 예측할 수 있습니다. 이것을 통계적인 용어로 보면 독립변수인 runpulse의 변화에 따른 종속변수인 maxpulse의 상관관계를 파악하는 것이라고 할 수 있습니다.

fig, axes = plt.subplots(3,1,figsize=(10,12))
axes[0].set_title('runtime - maxpulse')
axes[0].scatter(dataset['runtime'],dataset['maxpulse'])

axes[1].set_title('runpulse - maxpulse')
axes[1].scatter(dataset['runpulse'],dataset['maxpulse'])

axes[2].set_title('oxygen - runtime')
axes[2].scatter(dataset['oxygen'],dataset['runtime'])

회귀분석을 위해서는 특정 독립 변수 값에 해당하는 종속변수의 값이 정규분포를 이뤄야합니다. 그래야 어떤 규칙성을 발견할 수 있습니다. 또 종속변수들의 값은 서로 독립적이어야 하는데 만일 그렇지 않으면 어떤 부분이 종속변수에 영향을 미치는지 발견하기가 어렵습니다. 이에 대한 연장으로 독립변수가 여러 개인 경우 변수간에 영향을 주지 않아야 합니다.

이런 이유로 회귀분석을 생각해 보면 아래의 표와 같이 독립변수의 수, 척도, 관계 등에 의해 회귀분석의 종류를 나눌 수 있습니다.

이훈영의 연구방법론, p.400

아래의 runpulse-maxpulse 그래프를 다시 살펴보면 데이터의 어떤 규칙성을 발견할 수 있습니다. 이제 이 데이터를 특징을 가장 잘 나타내는 직선을 긋는다고 생각해보겠습니다.

사람은 직관에 의해서 선을 한번에 그을 수 있습니다. 비록 정확하진 않더라도 정답과 비슷한 모양으로 선을 그을 수 있지만 컴퓨터는 그렇게 할 수 없습니다. 선형회귀는 바로 컴퓨터가 점들을 잘 표현하는 직선(Y = Xw + b)을 그리는 과정이라고 할 수 있습니다. 여기서 궁금한 것이 바로 w와 b 값입니다.

그중에서 가장 많이 사용하는 알고리즘을 sklearn에서 구현한 LinearRegression을 통해서 runpulse-maxpulse 회귀분석을 구현해보겠습니다. 구현은 생각보다 간단합니다. sklearn 패키지는 이런 복잡한 작업들을 단 2줄에 해결 할 수 있도록 편리한 함수를 제공해줍니다.

from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(dataset['runpulse'].values.reshape(-1,1),dataset['maxpulse'].values.reshape(-1,1))
print('W:{}, b:{}'.format(lr.coef_, lr.intercept_))
# W:[[0.83109283]], b:[32.78331594]

간단히 우리가 구하고자 하는 w, b 값을 구했습니다. 이제 두개의 미지수를 찾았으니 어떤 X 입력 값에 대해서 Y(여기서는 maxpulse)를 쉽게 예측할 수 있습니다. 실제로 데이터 예측을 해보겠습니다. fitness.csv 데이터가 많다면 훈련용 셋과 테스트 셋을 나눠서 정확도를 보겠지만 원본 데이터의 갯수가 30개라서 테스트용 데이터를 나누지 않고 위의 값 중에서 없는 runpulse 값을 찾고 이때에 maxpulse를 예측해보겠습니다.

dataset.loc[(dataset['runpulse']>=178) & (dataset['runpulse']<=180)]

위와 같은 조건으로 검색해보니 179가 없는 것을 확인했습니다. 179를 입력했을 때에 180과 185 사이의 값으로 예측된다면 예측 모델이 비교적 정확하다고 할 수 있겠습니다.

y_hat = lr.predict([[179]])
#array([[181.54893295]])

예측 결과 181.54 정도가 나와서 비교적 합리적인 값을 도출해냈습니다. 이렇게 해도 되고 y=Xw+b의 식에 값을 대입해도 동일한 y 값을 얻을 수 있습니다.

plt.plot(dataset['runpulse'],lr.predict(dataset['maxpulse'].values.reshape(-1,1)),'*')
plt.plot(dataset['runpulse'],dataset['maxpulse'],'x')