또 한 가지 그래프를 통해 알 수 있는 것은 각 피처 간의 단위가 다르다는 것입니다. 예를 들어 '홈런/9'라는 피처는 X축이 0~8사이 값인 반면, '이닝'이라는 피처는 0~200 사이의 값을 포함하고 있습니다.
이러한 데이터는 피처의 정규화 혹은 스케일링이 되어있지 않은 데이터입니다.
반응형
피처의 정규화란?
데이터 분석에서 피처 정규화(Feature Scaling)는 피처(데이터의 열 또는 속성)의 값 범위를 조정하는 프로세스를 말합니다. 일반적으로, 데이터 세트에는 여러 피처가 있으며, 이러한 피처들은 서로 다른 값 범위를 가질 수 있습니다. 피처 정규화는 이러한 값 범위의 차이를 줄여서 알고리즘이 올바르게 작동하도록 도와줍니다.
피처 정규화의 주요 목적은 다음과 같습니다:
모든 피처가 동일한 스케일을 갖도록 만듭니다. 이렇게 하면 피처 간의 상대적인 중요도를 쉽게 비교할 수 있습니다. 경사 하강법 등과 같은 일부 머신 러닝 알고리즘은 피처의 스케일에 민감할 수 있으므로, 정규화를 통해 알고리즘이 수렴하는 데 도움을 줄 수 있습니다. 일반적으로 사용되는 두 가지 주요 피처 정규화 기법은 다음과 같습니다:
Min-Max 정규화: 이 방법은 피처 값을 특정 범위로 변환합니다(일반적으로 0과 1 사이로 조정). 수식은 다음과 같습니다:
X_new = (X - X_min) / (X_max - X_min)
여기서 X는 원래 값, X_new는 정규화된 값, X_min은 피처의 최소값, X_max는 피처의 최대값입니다.
표준화 (Standardization): 이 방법은 피처 값을 평균이 0이고 표준 편차가 1인 표준 정규 분포로 변환합니다. 수식은 다음과 같습니다:
X_new = (X - 평균) / 표준 편차
이 방법은 데이터가 정규 분포를 따를 때 특히 유용합니다.
피처 정규화는 일반적으로 데이터 전처리 단계에서 수행되며, 주어진 데이터와 알고리즘의 특성에 따라 적절한 방법을 선택해야 합니다. 피처 정규화는 데이터 분석에서 중요한 단계이며, 모델의 성능 향상에 기여할 수 있습니다.
쉽게 설명하자면,,
'키'와 '몸무게'라는 2개의 피처로 나이를 예측하는 회귀 분석을 한다고 할 때, 회귀 분석은 '키를 나타내는 상수'에 의해 더 큰 영향을 받을 수 있습니다. 같은 단위라면 몸무게보다는 키의 값이 더 크기 때문입니다. 이러한 피처들의 단위를 0~1 사이 혹은 상대적 값을 표현할 수 있는 수치로 맞춰주는 것이 피처 스케일링입니다.
투수의 연봉 예측하기
다음 코드에서는 여러 가지 피처 스케일링의 방법 중 표준화 방법을 적용하였습니다. 표준화는 정규 분포에서의 z-값을 구하는 과정과 동일합니다.
# pandas 형태로 정의된 데이터를 출력할 때, scientific-notation이 아닌 float 모양으로 출력되게 해줍니다.
pd.options.mode.chained_assignment = None
# 피처 각각에 대한 scaling을 수행하는 함수를 정의합니다.
def standard_scaling(df, scale_columns):
for col in scale_columns:
series_mean = df[col].mean()
series_std = df[col].std()
df[col] = df[col].apply(lambda x: (x-series_mean)/series_std)
return df
# 피처 각각에 대한 scaling을 수행합니다.
scale_columns = ['승', '패', '세', '홀드', '블론', '경기', '선발', '이닝', '삼진/9',
'볼넷/9', '홈런/9', 'BABIP', 'LOB%', 'ERA', 'RA9-WAR', 'FIP', 'kFIP', 'WAR', '연봉(2017)']
picher_df = standard_scaling(picher, scale_columns)
picher_df = picher_df.rename(columns={'연봉(2018)': 'y'})
picher_df.head(5)
#출력 결과
피처 스케일링 코드 출력 결과
연속형이 아닌 범주형 피처들은 어떻게 정규화 할 수 있을까요? 범주형 피처에는 [원-핫 인코딩]이라는 방법을 적용해야 합니다. 판다스에서는 get_dummies() 함수로 간단하게 원-핫인코딩을 적용할 수 있습니다.
다음 코드의 실행 결과, (X_train, y_train):(X_test, y_test) = 8:2 비율로 총 4개의 데이터가 생성됩니다.
X_train과 y_train은 학습 데이터셋, 그리고 X_test와 y_test는 테스트 데이터셋을 의미합니다.
from sklearn import linear_model
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error #평균 제곱 오차 계산
from math import sqrt #제곱근 계산
# 학습 데이터와 테스트 데이터로 분리합니다.
X = picher_df[picher_df.columns.difference(['선수명', 'y'])] #데이터프레임에서 선수명과 y열을 제외한 모든열을 지정
y = picher_df['y'] #y열은 y로 지정
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=19)
#x는 입력데이터, y는 출력 데이터 / 테스트 사이즈는 20% / 데이터를 무작위로 섞을 때 사용할 시드는 19로 지정
이제 회귀 분석을 수행하는 코드를 살펴보겠습니다. 다음 코드에서는 사이킷런 라이브러리의 LinearRegression()으로 모델 오브젝트를 선언한 뒤, 해당 오브젝트에 model = lr.fit(X_train, y_train) 코드를 실행시킵니다.
그러면 아래의 결과처럼 학습이 완료된 회귀식의 계수를 출력할 수 있습니다.
**위에서 2018년도 연봉은 y로 이름을 변경해주었었죠? 잊지마세용!!
# 회귀 분석 계수를 학습합니다 (회귀 모델 학습)
lr = linear_model.LinearRegression()
model = lr.fit(X_train, y_train)
# 학습된 계수를 출력합니다.
print(lr.coef_)
#출력 결과
회귀 분석 계수 학습&학습된 계수 출력 결과
예측 모델 평가하기
회귀 분석은 statsmodel 라이브러리의 OLS 클래스로도 실행이 가능합니다. OLS 클래스의 summary()함수를 사용하면 다음의 실행 결과처럼 계수에 대한 자세한 분석 내용을 살펴볼 수 있습니다.
pip install statsmodels
#statsmodels 설치해주시구요!
#1. 예측 모델 평가하기: statsmodels
import statsmodels.api as sm
# statsmodel 라이브러리로 회귀 분석을 수행합니다.
X_train = sm.add_constant(X_train)
model = sm.OLS(y_train, X_train).fit()
model.summary()
# 출력 결과
statsmodel 코드 출력 결과
위 실행 결과에서 결정계수(R-squared), 수정 결정 계수(Adj. R-squared)라는 점수를 눈여겨 봐야 합니다.
이 점수들은 회귀 분석이 얼마나 잘 되었는지 평가하는 지표이며, 회귀 분석으로 추정한 모델이 주어진 데이터를 얼마나 잘설명하는가?에 대한 점수입니다.
이 점수들이 1에 가까울수록 데이터를 잘 설명하는 모델이라고 할 수 있습니다.
다음으로 F통계량(F-statistic)이라는 수치를 살펴봅니다. F 통계량은 회귀식의 유의성 검정에 사용되는 값으로 F 통계량에 대한 p-value인 Prob(F-statistics) 수치와 함께 살펴봐야 합니다. 일반적으로 p-value가 0.05이하면 'F 통계량이 유의미한 의미를 가진다'라는 결론을 내려도 무방하고 이는 회귀 분석이 유의미한 결과를 가진다는 것 입니다.
또한 표의 P>|t|라는 정보는 각 피처의 검정 통계량(t-statistics)이 얼마나 유의미한지에 대한 p-value를 나타내는 것입니다.
위 분석에서는 'WAR','연봉(2017)', '한화' 3개의 피처의 p-value가 0.05미만으로 나타났기 때문에
결정 계수 (R-squared)는 0.928입니다. 이 값은 모델이 종속 변수의 변동성의 약 92.8%를 설명한다는 것을 의미합니다. 높은 결정 계수는 모델이 데이터에 잘 적합되었음을 나타냅니다. Adj. R-squared: 0.907
조정된 결정 계수 (Adjusted R-squared)는 0.907입니다. 이 값은 독립 변수의 수와 데이터의 크기를 고려한 결정 계수입니다. 일반적으로 독립 변수의 수가 많아질수록 결정 계수는 증가하지만, 조정된 결정 계수는 증가하지 않습니다. 따라서, 0.907의 값은 모델이 독립 변수에 과적합되지 않고 일반화되었음을 나타냅니다. F-statistic: 44.19
F-통계량은 44.19입니다. 이 값은 회귀 모델이 통계적으로 유의미한지를 평가하는데 사용됩니다. F-통계량은 독립 변수와 종속 변수 사이의 선형 관계의 유의성을 평가합니다. 유의한 F-통계량은 모델이 종속 변수를 예측하는 데 유의미한 독립 변수를 포함하고 있다는 것을 의미합니다. Prob (F-statistic): 7.70e-42
F-통계량에 대한 p-value입니다. 이 값은 F-통계량이 유의미한지를 나타냅니다. p-value가 매우 작은 값인 7.70e-42이므로, 모델의 F-통계량은 유의미하다고 할 수 있습니다. coef, std err, t, P>|t|, [0.025, 0.975]
회귀 모델의 각 독립 변수에 대한 추정된 계수(coef), 표준 오차(std err), t-통계량(t), p-value(P>|t|), 95% 신뢰 구간([0.025, 0.975])이 제공됩니다. 이 정보들은 각 독립 변수의 회귀 계수의 유의성을 평가하는데 사용됩니다. p-value가 작은 값인 변수들은 유의미한 독립 변수로 간주됩니다.
Omnibus: 28.069
Omnibus 검정은 잔차의 정규성을 평가하는데 사용됩니다. Omnibus 값이 클수록 잔차가 정규분포에서 벗어난다는 것을 의미합니다. Durbin-Watson: 2.025
Durbin-Watson 검정은 잔차 간의 자기상관을 평가하는데 사용됩니다. Durbin-Watson 값이 2에 가까울수록 잔차 간에 자기상관이 없다는 것을 나타냅니다. 이 경우, 2.025는 자기상관이 거의 없다는 것을 의미합니다. Jarque-Bera (JB): 194.274
Jarque-Bera 검정은 잔차의 정규성을 평가하는데 사용됩니다. JB 값이 클수록 잔차가 정규분포에서 벗어난다는 것을 의미합니다. Skew: -0.405
Skewness(왜도)는 분포의 비대칭 정도를 나타내는 통계적 척도입니다. 음수인 경우 왼쪽으로 비대칭되어 있음을 나타냅니다. 따라서, -0.405는 왼쪽으로 약간 비대칭된 분포를 의미합니다. Kurtosis: 9.155
Kurtosis(첨도)는 분포의 꼬리의 두께를 나타내는 통계적 척도입니다. Kurtosis 값이 3보다 크면 분포가 보다 중심으로부터 더 꼬리가 두꺼운 형태를 나타냅니다. 따라서, 9.155는 분포가 꼬리가 두꺼운 형태를 의미합니다. Cond. No.: 3.47e+16
조건 수 (Condition Number)는 다중공선성(multicollinearity)을 평가하는데 사용됩니다. 조건 수가 매우 큰 경우, 다중공선성 문제가 발생할 가능성이 있습니다. 이 경우, 3.47e+16은 다중공선성의 가능성이 높음을 나타냅니다. 위의 결과를 종합하면, 회귀 모델은 종속 변수를 예측하는 데 좋은 성능을 보이고 있습니다. R-squared 값이 높고, F-통계량의 p-value가 매우 작으며, 독립 변수들 중 일부는 유의미한 계수를 가지고 있습니다. 하지만, 일부 독립 변수들은 p-value가 크거나 95% 신뢰 구간이 0을 포함하고 있어 유의미하지 않을 수 있습니다.
다음으로 지금까지 학습한 coef(계수) 값들을 시각화 하여 살펴봅시다.
# 한글 출력을 위한 사전 설정 단계입니다.
mpl.rc('font', family='Malgun Gothic')
plt.rcParams['figure.figsize'] = [20, 16]
# 회귀 계수를 리스트로 반환합니다.
coefs = model.params.tolist()
coefs_series = pd.Series(coefs)
# 변수명을 리스트로 반환합니다.
x_labels = model.params.index.tolist()
# 회귀 계수를 출력합니다.
ax = coefs_series.plot(kind='bar')
ax.set_title('feature_coef_graph')
ax.set_xlabel('x_features')
ax.set_ylabel('coef')
ax.set_xticklabels(x_labels)
# 출력 결과
coef(계수) 값들을시각화 코드 출력 결과
이에 대한 위 코드의 시각화 결과, FIP, WAR, 홈런, 작년 연봉 피처가 가장 영향력이 큰것으로 보입니다. 현재까지의 피처 탐색결과는 다음과 같이 정리할 수 있습니다.
피처명
유의미한 피처인가?
영향력이 큰 피처인가?
FIP
X
O
WAR
O
O
홈런
X
O
작년 연봉
O
O
팀(한화)
O
△
다음으로 아래의 출력 결과는 수정 결정 계수(R2 score)를 사이킷런의 LinearRegression 클래스로 출력해볼게요.
#2. 예측 모델 평가하기 : R2 score
# 학습 데이터와 테스트 데이터로 분리합니다.
X = picher_df[picher_df.columns.difference(['선수명', 'y'])]
y = picher_df['y']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=19)
# 회귀 분석 모델을 학습합니다.
lr = linear_model.LinearRegression()
model = lr.fit(X_train, y_train)
# 회귀 분석 모델을 평가합니다.
print(model.score(X_train, y_train)) # train R2 score를 출력합니다.
print(model.score(X_test, y_test)) # test R2 score를 출력합니다.
# 출력 결과
R2 score 코드 출력 결과
실행 결과의 각 값은 학습 데이터셋과 테스트 데이터셋에 대한 평가 점수를 의미하며, 이 두 점수는 최대한 벌어지지 않는 것이 좋습니다. 만약 학습 점수가 테스트 점수에 비해 높다면 과적합이 발생한 것입니다.
모의고사에만 특화된 공부를 한 나머지, 실제 시험의 새로운 유형에 적응하지 못 하는 경우라고 비유 할 수 있어요.
#3. 예측 모델평가: RMSE score
이 점수는 실제값과 예측값의 차이를 절대적인 수치로 나타낸 것입니다. 이 값이 높으면 높을수록 예측이 부정확하다는 것을 의미합니다.
다음 코드는 RMSE socre를 출력한 것입니다.
# 회귀 분석 모델을 평가합니다.
y_predictions = lr.predict(X_train)
print(sqrt(mean_squared_error(y_train, y_predictions))) # train RMSE score를 출력합니다.
y_predictions = lr.predict(X_test)
print(sqrt(mean_squared_error(y_test, y_predictions))) # test RMSE score를 출력합니다.
#출력 결과
RMSE score 코드 출력 결과
RMSE는 실제 값과 예측 값 간의 차이를 평균화하고, 제곱하여 제곱근을 취한 값으로, 모델의 예측 정확도를 평가하는 지표입니다. 일반적으로 RMSE가 낮을수록 모델의 예측이 더 정확하다고 볼 수 있습니다.
위의 결과를 해석하면 다음과 같습니다:
학습 데이터의 RMSE score: 7282.718684746374
학습 데이터에 대한 회귀 모델의 예측 값과 실제 값의 차이를 평균화하고 제곱근을 취한 값은 약 7282.72입니다. 즉, 학습 데이터에 대한 회귀 모델의 평균 예측 오차는 약 7282.72입니다. 테스트 데이터의 RMSE score: 14310.696436889137
테스트 데이터에 대한 회귀 모델의 예측 값과 실제 값의 차이를 평균화하고 제곱근을 취한 값은 약 14310.70입니다. 즉, 테스트 데이터에 대한 회귀 모델의 평균 예측 오차는 약 14310.70입니다. RMSE를 비교하면, 학습 데이터의 오차가 테스트 데이터의 오차보다 작으므로, 모델이 학습 데이터에 대해 좀 더 잘 예측하는 경향이 있습니다. 그러나 테스트 데이터의 오차가 학습 데이터의 오차에 비해 큰 경우, 모델이 테스트 데이터에 대해 과적합(Overfitting)되었을 수 있습니다. 따라서, 모델의 일반화 능력을 향상시키기 위해 조정이 필요할 수 있습니다.
그렇다면 위 평가 결과는 테스트 데이터가 과적합되었다고 볼 수 있는건가?
위의 평가 결과만으로는 테스트 데이터가 과적합되었다고 단정짓기는 어렵습니다. RMSE 값 자체로는 모델의 예측 정확도를 판단하는 데에 제한적인 정보를 제공하기 때문입니다.
과적합 여부를 판단하기 위해서는 다른 평가 지표나 그래프를 종합적으로 고려해야 합니다.
따라서, 위의 평가 결과만으로는 테스트 데이터가 과적합되었다고 단정짓기 어렵습니다. 추가적인 평가 지표나 시각화를 통해 모델의 성능과 과적합 여부를 더 정확하게 평가하는 것이 좋습니다.
이번에는 피처들의 상관 관계를 살펴보기 위해 heatmap 방식의 시각화를 사용하겠습니다.
이를 통해 승-이닝, kFIP-FIP, RA9_WAR-WAR 등의 피처 쌍에서 높은 연관성을 발견할 수 있습니다.