[2024.11.06] 필수 온라인 강의 Part16 Machine Learning Advanced CH05 기본 ML 모델
이론
머신러닝 모델의 목적 - 분류와 회귀 문제
- 분류(Classification) : 예측하고 싶은 종속변수(Y)가 범주형일 때 - 회귀(Regression) : 예측하고 싶은 종속변수(Y)가 연속형일 때 -> 대부분의 머신러닝 모델들은 두 문제 모두 해결 가능 e.g. DecisionTreeClassifier, DecisionTreeRegressor
머신러닝 모델의 분류 - 선형모델과 비선형모델
- 선형모델 : X와 Y의 관계를 선형으로 매핑 ➞ 선형 결정경계(Decision boundary) 생성 (e.g. 선형회귀모형) - 비선형모델 : X와 Y의 관계를 비선형으로 매핑 ➞ 비선형 결정경계(Decision boundary) 생성 (e.g. KNN, Tree)
Linear Regression (선형회귀) - 데이터를 가장 잘 대변하는 최적의 선을 찾는 과정 - 독립변수들(X)과 연속형 종속변수(Y) 사이의 선형 관계를 학습
- 최소자승법(Ordinary Least Square, OLS) : 선을 잘 찾기 위한 방법 - X와 Y의 관계를 가장 잘 나타내는 Best line을 찾는 방법 - 잔차제곱의 합(SSE, Sum of Square Error)을 최소화 하는 회귀계수 β 들을 구하는 방법 - 잔차(Residual) : 실제 관측값 - 예측값 - 잔차 제곱의 합(SSE) : 회귀 모형의 Cost function → Minimize
- 장점 - 학습 및 예측 속도가 빠른 것이 특징 - 모델의 해석이 명확 (회귀계수 해석 가능) - 단점 - X와 Y의 선형관계를 가정하기 때문에, 이러한 가정이 현실에서는 잘 적용되지 않을 가능성 - 이상치에 다소 민감
- 가정 - 선형성 : 독립변수(X)와 종속변수(Y) 사이에는 선형 관계가 성립 ➞ 주로 시각화를 통해 확인 - 잔차 관련 가정 ➞ 회귀 분석의 신뢰성을 높이는 요소 - 정규성 : 잔차들은 평균이 0인 정규분포를 구성 - 등분산성 : 잔차들의 분산은 일정 - 위배시 -회귀계수의 신뢰 구간 및 가설 검정 결과의 부정확 동반 - 모델의 예측 능력 저하 증상 - 대표 확인 방법 : Q-Q(Quantile-Quantile) plot - 잔차를 오름차순으로 나열 했을때의 분위수와 이론적인 잔차의 분위수 값을 비교해서 정규성을 확인 -> 시각화를 해서도 확인을 가능. - 해결 방법 - 데이터에 로그나 루트를 취하기 - 더 많은 데이터를 수집해 중심극한정리에 가까워 지도록 기대
- 독립성 : 독립변수들(X) 간 상관관계(Correlation)가 존재하지 않아야 함 - 다중공선성 : 회귀 모형에 사용된 일부 독립변수가 다른 독립변수와 상관성이 높아, 모델 결과에 부정적 영향을 미치는 현상 - 위배하면 : 독립변수들의 상관관계의 영향으로 회귀 모형의 예측 결과가 부정확해 질 수 있음 → 독립성 가정이 위배되는 상황으로, 제거 필요 - 다중공선성 확인 방법 : VIF(Variance Inflation Factor, 분산팽창계수) - 보통 VIF가 10이 넘으면 다중공선성에 문제가 있다고 여겨짐 ➞ <해결 방법 > 다중공선성이 높은 변수 조합 중 하나의 변수를 제거하는 방향으로 진행
- 사용시 주의할 점 - 이상치 처리 - 회귀분석의 결과는 이상치의 유무에 민감한 경향 → 적절한 이상치 제거 필요 - 이상치 확인 방법 - IQR(사분범위) : Q3(3사분위수, 전체 데이터 분포의 75% 지점) - Q1(1사분위수, 전체 데이터 분포의 25% 지점) - 하위이상치 : Q1 - 1.5 * IQR 보다 작은 값 - 상위이상치 : Q3 + 1.5 * IQR 보다 큰 값
- 모델 평가와 결과 해석 - R-square(결정계수) - 회귀 모형 내에서 독립변수들(X)이 설명할 수 있는 종속변수(Y)의 변동성 0 ≤ R-square ≤ 1 범위를 가짐 ○ R-square = 0 : X와 Y는 어떤 선형관계도 존재하지 않음 ○ R-square = 1 : X와 Y는 회귀모형으로 설명될 수 있는 완벽한 선형관계를 가짐
- 모델 결과 해석 - 회귀 계수에 대한 표준편차, 신뢰구간, p-value 등으로 회귀 모형 해석 가능
- 실전 사례 - 최신 LLM 데이터의 평가 모델로 활용 - 데이터의 평가 지표를 Variable로 두고 Quality를 예측하는 모델을 학습해서새로운 데이터가 들어왔을때 Quality를 예측하는 방법 ➞ 각 데이터 평가 지표가 얼마나 유의미하고 중요한지도 확인 가능 (선형 회귀를 동일한 방법으로도 활용 가능)
KNN(K-Nearest Neighbor, K-최근접 이웃) - 가까운 이웃에 위치한 K개의 데이터를 보고, 데이터가 속할 그룹을 판단하는 과정 → 거리 기반 모델, 사례 기반 학습(Instance-Based Learning)
- 거리 측정 방법 - 거리 측정 방법에 따른 KNN 모델의 성능 차이 존재 - 인접한 K개의 데이터를 고려하기 위해 “인접함”의 정의가 필요 → 거리 측정 Metric 선택 - 유클리드 거리(Euclidean Distance) - 맨해튼 거리 (Manhattan Distance)
- 분류와 회귀를 위한 KNN 뿐 아니라 유사한 데이터(인접한 이웃)를 구하기 위한 방법론으로도 사용 가능
- 실전 사례 - 이웃 정보를 추천시스템 모델의 Side information으로도 활용 가능 - 결측치 보간과 KNN : KNN을 통해 인접한 이웃들을 구해 결측치 보간에도 활용 가능
- 장점 - 단순하고, 특별한 훈련을 거치지 않아 빠르게 수행 - 데이터에 대한 특별한 가정이 존재 하지 않는 특징 - 단점 - 적절한 K 선택 필요 - 데이터가 많아지면 분류가 느려지는 현상 - 데이터 스케일에 민감하기에 스케일링이 필수적
- K 값 결정하기 - K값에 따라 고려하는 이웃 수가 달라지기 때문에 K값에 민감 → 주로 Cross Validation 을 통해 경험적으로 K 값을 선택
Decision Tree(의사결정나무) - 데이터 내 규칙을 찾아 Tree 구조로 데이터를 분류/회귀 하는 과정 - 특정 기준(질문)에 따라 데이터를 구분 - 노드 명칭
- Tree 분기 기준 - 불순도 - 의사결정나무의 목표는 맨 마지막 리프 노드의 불순도 (Impurity)를 최소화하는 것 → 정보획득량 (Information Gain, 분기 이전의 불순도와 이후의 불순도 차이)을 노드 분기의 기준으로 이용 - 불순도 함수 : 불순도가 높을 수록 아래 지표들이 높게 나타남 - 지니 계수(Gini coefficient) : 줄어드는 방향으로 분기를 진행 - 엔트로피(Entropy) : 줄어드는 방향으로 분기를 진행
- 과정 - 분할 종료 조건 확인 → 순도가 100%가 되는 지점에서 분할 종료 - 분할 기준 선택 → 모든 변수에 대해 불순도를 계산하고 해당 불순도가 가장 낮은 값을 기준으로 진행
- 장점 - 직관적으로 이해가 쉽고, 데이터 스케일에 민감하지 않음 - 범주형과 연속형 데이터를 모두 처리 가능 - 단점 - 트리 깊이가 깊어지면 과적합의 위험 존재 → 적절한 Pruning이 필요 - 새로운 Sample에 취약할 수 있음
- 주의할 점 - 가지치기(Pruning) - 트리의 깊이가 깊어지거나 Leaf가 많아지면 과적합의 위험이 커진다는 단점을 보완하기 위함 - 형성된 결정트리의 특정 노드 아래 트리나 Leaf를 제거해 일반화 성능을 높이기 위한 전략 - 트리의 Depth 조절하기 - 결국 과적합 방지에 중요한 것은 트리의 Depth(깊이)와 Leaf(노드)를 적절히 조정하는 것! - 가지치기 이외에도 아래처럼 sklearn에는 트리 깊이와 노드를 조정할 수 있는(=분할을 규제하는) Hyperparameter가 존재 - Max depth : 트리의 최대 깊이 : 과적합 방지해야함 - Max Leaf Nodes : 최대 몇개 Leaf 노드가 만들어 질 때까지 split 할건지? : leaf가 많아지면 과적합 - Min sample split : 최소 샘플이 몇개 이상이어야 하위 노드로 split 할건지? : 많은 값- 너무 적으면 과적합
- 모델 평가와 결과 해석 - Tree 시각화 : 결정경계와 트리 분할 과정을 시각화 가능 - Feature Importance(변수 중요도) - 트리 분기 과정에서 불순도를 많이 낮출 수 있는 feature = 중요한 feature → Feature selection의 기준으로 활용되기도 함 - 해당 지표는 절대적이지 않음에 유의 : 분할 기준, 모델 학습 과정 등에 따라 달라질 수 있음
Random Forest
- Ensemble 개념의 등장 - 앙상블(Ensemble) : 주어진 데이터로 여러개의 서로 다른 예측 모형을 생성해, 이들의 결과를 종합하는 것 → 여러 모델의 결과를 voting(범주형)이나 average(연속형)로 종합해 하나의 최종 예측 결과 도출 → 더 정확한 예측 결과를 기대 가능
- 트리 앙상블(Tree Ensemble) → 대표적으로 배깅(Bagging), 부스팅(Boosting) 방법이 존재
- 배깅(Bagging, Bootstrap aggregation) - 앞선 부트스트랩의 복원추출 과정을 머신러닝 앙상블에 사용 - 부트스트랩을 통해 표본을 여러번 뽑아 모델을 학습시키고, 그 결과를 집계(Aggregation) 하는 앙상블 방법 - 부트스트랩(Bootstrap) - 복원추출을 사용해 표본을 추출해 모집단의 통계량을 추론하는 통계적 방법 - 여기서 각 표본들은복원추출로 생성되었으므로 독립적
- 랜덤 포레스트(Random forest) - 배깅(Bagging)의 일종 - 학습시키는 데이터 뿐 아니라 독립변수(X)들도 무작위로 선택해 트리를 생성하는 방법
- 모델 학습 과정에서 서로 다른 N개의 Tree 생성 - N개의 Tree에서 발생한 Output을 Voting(범주형, 분류문제)하거나, Average(연속형, 회귀문제)해 최종 Output 생성 → High variance, Low bias 상황에서 분산(Variance) 감소에 도움
- 장점 - 부트스트랩으로 인해, 단일 의사결정나무의 단점인 높은 분산이 줄어 예측 성능 향상 기대 - 여러 트리의 융합으로 과적합 완화 기대 - 그 외 의사결정나무의 장점을 모두 흡수 - 단점 - 데이터 크기에 비례해 N개의 트리를 생성하기에, 대용량 데이터 학습의 경우 오랜 시간 소요 - 생성하는 모든 트리를 모두 확인하기 어려워, 해석 가능성이 단일 트리모델보다 떨어지는 문제
- 기초 ML 모델들(선형회귀, KNN, 의사결정나무)을 활용해 종가(Close)를 예측하는 실습 2-1 Linear Regression
- 컬럼 구성 확인하기
X_train.columns # train_data의 칼럼은 아래와 같이 구성
- 회귀 가정 체크 + 시각화
# 회귀 가정 체크 -> 시계열 데이터이다보니 변수(종가, 시가 등)들의 상관관계가 아래와 같이 너무 높음
# 하지만 회귀 가정 중에서는 변수간의 독립성을 고려해야한다는 가정이 존재
# 따라서 본 시계열 데이터를 바로 회귀모델에 적용
corr = X_train.corr()
sns.heatmap(corr)
# 시각화
plt.title("Correlation plot")
plt.show()
# 일단 독립성에 위배되지만, 모델 결과는 어떻게 나타나는지 sklearn으로 살펴봄
lin_reg = LinearRegression()
lin_reg.fit(X_train,Y_train) # sklearn based의 회귀 모델을 적합
- 선형회귀를 사용하기에 적합한 데이터 셋은 아님. 그래서 그냥 일단 어떤 식으로 나타내는가만 알아보기
# 위에서 적합된 모델로 MSE와 R^2를 계산
y_pred_sk = lin_reg.predict(X_valid)
J_mse_sk = np.sqrt(mean_squared_error(y_pred_sk, Y_valid))
R_square_sk = lin_reg.score(X_valid,Y_valid)
display(f"RMSE : {J_mse_sk}")
display(f"R square : {R_square_sk}")
# 동일한 과정을 statsmodel 패키지로 사용
# 모델 적합 결과는 sklearn과 동일함
model = sm.OLS(Y_train, X_train)
result = model.fit()
print(result.summary())
# QQ plot으로 가정을 만족하는지 체크
f,ax = plt.subplots(1,2,figsize=(10,4))
_,(_,_,r)= sp.stats.probplot((Y_valid - y_pred_sk),fit=True,plot=ax[0])
ax[0].set_title('Check for Multivariate Normality: \nQ-Q Plot')
# 등분산성도 체크
sns.scatterplot(y = (Y_valid - y_pred_sk), x= y_pred_sk, ax = ax[1],color='r')
ax[1].set_title('Check for Homoscedasticity: \nResidual Vs Predicted');
# QQ plot이 비선형적으로 휘어져있고, 등분산성은 다소 만족하는 모습을 보여줌
# QQ plot으로 보아 이는 선형회귀 가정이 만족되고 있지 않음을 보여줌
# 이론 수업 시간에 KNN은 K가 하이퍼 파라미터로서, K값에 따라 KNN의 성능이 달라질 수 있음
# 최적의 K값 찾기
knn_db=[]
for i in tqdm(range(3,15,2)):
M=[]
knn = KNeighborsRegressor(n_neighbors=i)
knn.fit(X_train, Y_train)
y_pred = knn.predict(X_valid)
rmse = np.sqrt(mean_squared_error(Y_valid, y_pred)) # MSE를 K값에 따라 저장하도록 설정합니다.
M.append(i)
M.append(rmse)
knn_db.append(M)
# 최적의 k값을 찾기 위한 장치. MSE가 가장 작게 나오는 모델을 적합시킨 K값을 최적의 값으로 설정
min=knn_db[0];
for i in range(len(knn_db)):
if knn_db[i][1]<min[1]:
min=knn_db[i];
n=min[0];
# 최적의 K 값이 반환됩니다.
display(n)
# K값에 따른 MSE 변화를 시각화. K=3일 때 가장 낮은 MSE를 보임
# K값이 올라갈수록 MSE가 증가하는 것을 확인
x = [x[0] for x in knn_db]
y = [x[1] for x in knn_db]
# 시각화를 진행
plt.plot(x, y, color='red')
plt.title('K selection using MSE')
plt.xlabel('K')
plt.ylabel('MSE')
plt.show()
# 위에서 찾은 최적의 K값을 이용해 모델을 학습시켜 결과를 관찰
knn = KNeighborsRegressor(n_neighbors=3)
knn.fit(X_train, Y_train)
# ytrainpredict_kk = knn.predict(X_train) # 시간이 꽤 오래걸리므로 생략
ytestpredict_kk = knn.predict(X_valid)
# 회귀 관련 metric을 통해 train/valid의 모델 적합 결과를 관찰.
display(f'RMSE Test: {np.sqrt(metrics.mean_squared_error(Y_valid, ytestpredict_kk))}')
# DecisionTreeRegressor 이용해 의사결정나무 생성
dt = DecisionTreeRegressor(
max_depth=10, # 트리의 깊이를 규제
random_state=1214, # 트리의 랜덤시드를 설정
min_samples_split=100 # 해당하는 샘플이 100개 이상이면 split
)
dt.fit(X_train, Y_train)
ytrainpredict_df = dt.predict(X_train)
ytestpredict_df = dt.predict(X_valid)
# 회귀 관련 metric을 통해 train/valid의 모델 적합 결과를 관찰
display(f"RMSE Train: {np.sqrt(metrics.mean_squared_error(Y_train, ytrainpredict_df))}")
display(f"RMSE Test: {np.sqrt(metrics.mean_squared_error(Y_valid, ytestpredict_df))}")
# 트리를 시각화.
export_graphviz(dt, out_file ='tree.dot')
with open("tree.dot") as f:
dot_graph = f.read()
pydot_graph = pydotplus.graph_from_dot_file("tree.dot")
Image(pydot_graph.create_png())
# 모델 적합 결과를 시각화(잔차플롯)
plt.scatter(ytestpredict_df, ytestpredict_df-Y_valid, c='limegreen', marker='s', edgecolors='white', s=35, alpha=0.9, label="Valid data")
plt.xlabel('Predicted Values')
plt.ylabel('Residuals')
plt.legend(loc='upper left')
plt.hlines(y=0, xmin=ytestpredict_df.min()-1, xmax=ytestpredict_df.max()+1, lw=1, color='black')
plt.xlim([ytestpredict_df.min()-1, ytestpredict_df.max()+1])
plt.title('Residual plot from DecisionTreeRegressor')
plt.show()
2-4 Random Forest
# RandomForestRegressor를 이용해 회귀 모델을 적합
forest_rf = RandomForestRegressor(n_estimators=10, criterion='squared_error', random_state=1, n_jobs=-1)
forest_rf.fit(X_train, Y_train)
ytestpredict_rf = forest_rf.predict(X_valid)
# 회귀 관련 metric을 통해 train/valid의 모델 적합 결과를 관찰
print(f'RMSE test: {np.sqrt(metrics.mean_squared_error(Y_valid, ytestpredict_rf))}')
# 데이터
data_file = "House_X_train_val.csv"
df = pd.read_csv(data_file)
# Target과 독립변수들을 분리
y_train = df['target']
X_train = df.drop(['target'], axis=1)
# 학습 데이터와 검증 데이터를 8:2 비율로 분리
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=2023)
# 상관계수를 체크해봤을 때 위 주식데이터와 비교하여 매우 양호한 상황
corr = X_train.corr()
sns.heatmap(corr);
plt.show()
# 회귀모델을 적합다. 총 80개의 변수가 존재하기에 각 독립변수별로 회귀계수가 출력된 것을 확인
# p-value가 낮은 것도 있고, 높은 변수. 이는 모든 변수가 모두 유효하지는 않음
# 하지만 F-statistics도 높고, R-squard도 꽤 높은 경향을 보이므로, 독립변수들이 타겟변수를 비교적 잘 설명.
model = sm.OLS(y_train, X_train)
result = model.fit()
print(result.summary())
# 사이킷런도 이용
lin_reg = LinearRegression()
lin_reg.fit(X_train,y_train) # sklearn based의 회귀 모델을 적합
# 위에서 적합된 모델로 MSE와 R^2를 계산
y_pred_sk = lin_reg.predict(X_val)
J_mse_sk = mean_squared_error(y_pred_sk, y_val)
R_square_sk = lin_reg.score(X_val,y_val)
display(f"The Mean Square Error(MSE) : {J_mse_sk}")
display(f"R square : {R_square_sk}")
# 앞선 sm.OLS 결과와 비슷한 결과를 얻는 것을 확인.