Kwon's 데이터분석기

Daicon 경진대회 - 학습플랫폼 이용자 구독 갱신 예측 모델링(2) 본문

데이터 모델링

Daicon 경진대회 - 학습플랫폼 이용자 구독 갱신 예측 모델링(2)

DataKwon 2023. 12. 26. 00:40

이전까지 머신러닝 모델에 대한 학습 및 검정을 수행하고, 과대 과소적합이 모델에 존재하는지를 확인하는 방법 중 하나인 학습곡선을 이용해 모델 점수가 가장 높은 RandomForestClassifier에 대해 학습곡선을 그려본 결과 과대적합이 있다는 것을 확인할 수 있었다. 다른 머신러닝 모델의 학습곡선은 어떤지에 대해서 확인을 해보도록 한다.

plt.figure(figsize=(15, 10))
plt.suptitle("Learning Curves", fontsize=16)

#첫번째 DTC
plt.subplot(2, 2, 1)
plt.title("Decision Tree Classifier")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
                 train_scores_mean + train_scores_std, alpha=0.1, color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
                 test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Cross-validation score")
plt.legend(loc="best")

#두번째 SVC
plt.subplot(2, 2, 2)
plt.title("SVC")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()
plt.fill_between(train_sizes1, train_scores_mean1 - train_scores_std1,
                 train_scores_mean1 + train_scores_std1, alpha=0.1, color="r")
plt.fill_between(train_sizes1, test_scores_mean1 - test_scores_std1,
                 test_scores_mean1 + test_scores_std1, alpha=0.1, color="g")
plt.plot(train_sizes1, train_scores_mean1, 'o-', color="r", label="Training score")
plt.plot(train_sizes1, test_scores_mean1, 'o-', color="g", label="Cross-validation score")
plt.legend(loc="best")

# 세번째 KNC
plt.subplot(2, 2, 3)
plt.title("KNeighbor Classifier")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()
plt.fill_between(train_sizes2, train_scores_mean2 - train_scores_std2,
                 train_scores_mean2 + train_scores_std2, alpha=0.1, color="r")
plt.fill_between(train_sizes2, test_scores_mean2 - test_scores_std2,
                 test_scores_mean2 + test_scores_std2, alpha=0.1, color="g")
plt.plot(train_sizes2, train_scores_mean2, 'o-', color="r", label="Training score")
plt.plot(train_sizes2, test_scores_mean2, 'o-', color="g", label="Cross-validation score")
plt.legend(loc="best")

# 네번째 LR
plt.subplot(2, 2, 4)
plt.title("Logistic Regressor")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()
plt.fill_between(train_sizes3, train_scores_mean3 - train_scores_std3,
                 train_scores_mean3 + train_scores_std3, alpha=0.1, color="r")
plt.fill_between(train_sizes3, test_scores_mean3 - test_scores_std3,
                 test_scores_mean3 + test_scores_std3, alpha=0.1, color="g")
plt.plot(train_sizes3, train_scores_mean3, 'o-', color="r", label="Training score")
plt.plot(train_sizes3, test_scores_mean3, 'o-', color="g", label="Cross-validation score")
plt.legend(loc="best")

plt.tight_layout()
plt.subplots_adjust(top=0.9)

# 그래프 출력
plt.show()

랜덤포레스트 제외한 나머지 모델 학습곡선

나머지 학습모델 곡선을 확인해 본 결과 Logistic Regressor와 SVC는 Training Score가 처음부터 좋지 않으며, 점진적으로 하락하는 경향이 존재하기 때문에 모델 자체의 성능이 좋지 않은 것을 알 수 있다.

그 다음으로는 Decision Tree Classifier인데 이 모델 또한 Training Score 점수가 1점으로 유지되는 확실한 과대적합 형태의 문제가 발생하고 있다. 이는 RandomForest Classifier에서도 발생한 문제였지만 Cross Validation Score는 RandomForestClassifier보다 약간 좋지 않아 DecisionTreeClassifier보단 RandomForestClassifier를 사용하는 것이 좋다고 생각된다. 마지막으로 KNeighborsClassifier는 Training Score의 점수가 0.75에서 0.85 사이에서 움직이는 괜찮은 점수를 가지고 있으며 Cross Validation Score또한 0.65에 근접하는 모델이기에 좋은 성능을 가지고 있을 것으로 판단되지만, 아직까지 Training Score와 Cross Validation Score의 점수가 큰 차이가 존재하기에 과대적합을 가지고 있는 것으로 확인할 수 있다.

 

먼저 RandomForestClassifier에서 과대적합이 발생했기 때문에 RandomForestClassifier의 하이퍼파라미터를 조정을 하여 모델의 최적값을 찾는 방식으로 과대적합을 없앨 수 있도록 할 것이다. 우리가 사용 할 하이퍼파라미터들은 

n_estimators:

  • 의사결정나무의 개수를 지정합니다. 많은 트리를 사용하면 모델이 더 강력해지지만, 계산 비용이 증가하게 됩니다. 기본값은 100입니다.

criterion:

  • 노드의 분할 기준을 지정합니다. "gini" 또는 "entropy" 중에서 선택할 수 있습니다. 기본값은 "gini"입니다.

max_depth:

  • 각 의사결정나무의 최대 깊이를 제한합니다. 더 깊은 트리는 더 복잡한 모델을 생성할 수 있지만, 계산 비용이 증가합니다.

min_samples_split:

  • 노드를 분할하기 위한 최소한의 샘플 수를 지정합니다. 이 수보다 작으면 더 이상 분할하지 않습니다.

min_samples_leaf:

  • 리프 노드가 가져야 하는 최소한의 샘플 수를 지정합니다.

이렇게 사용을 할 것이며, 먼저 최적의 하이퍼파라미터를 찾기 위해, 하이퍼파라미터 튜닝 방법 중 하나인 GridSearchCV방식을 사용을 한다.

# GridSearchCV
from sklearn.model_selection import GridSearchCV

# 찾을 하이퍼파라미터 수치 지정
rfc_param = {
    'n_estimators' : [100, 200, 300, 400],
    'max_depth' : [10, 15, 20],
    'min_samples_split' : [2, 5, 10],
    'min_samples_leaf' : [4, 8]
}

# 최적의 하이퍼파라미터 찾기
grid_search = GridSearchCV(estimator = rfc, param_grid=rfc_param, cv=5, scoring='accuracy')
grid_search.fit(x_train, y_train)

# 최적의 하이퍼파라미터 계수 확인
grid_search.best_params_

 

GridSearchCV를 사용해 랜덤포레스트 방법의 하이퍼파라미터 중 n_estimators, max_depth, min_samples_split, criterion, min_samples_leaf의 여러개의 계수를 지정하여 최적의 하이퍼파라미터 수치를 구한다. 수행되는데 오래 걸릴 것이기 때문에 기다려줘야 한다.

위의 하이퍼파라미터 중 최적의 계수를 확인해보니, n_estimators는 400, max_depth는 20, min_samples_leaf는 4, min_samples_split은 2로 나왔으며, n_estimators와 max_depth를 다시 고치며 최적의 하이퍼파라미터를 계속 검사한다.

rfc_param = {
    'n_estimators' : [400, 450, 500, 550],
    'max_depth' : [20, 25, 30, 35],
    'min_samples_split' : [2],
    'min_samples_leaf' : [4]
}

한번더 이 조건으로 해본 결과 최적의 하이퍼파리미터 계수는 max_depth는 30, n_estimators는 500이 나오게 되었다.

이제 이러한 하이퍼파라미터를 가지고 모델을 다시 만들어준다.

# 랜덤포레스트 모델 생성
parameter_rfc = RandomForestClassifier(n_estimators = 500, max_depth = 30, min_samples_leaf=4, min_samples_split=2, random_state = 42)
parameter_rfc.fit(x_train, y_train)

# 모델을 통한 예측값 생성
y_pred_rfc_param = parameter_rfc.predict(x_test)

# f1_score과 accuracy_score 검사
f1_rfc_param = f1_score(y_test, y_pred_rfc_param, average='macro')
accuracy_rfc_param = accuracy_score(y_test, y_pred_rfc_param)
# score점수 표시
print(f"RandomForestClassifier -- f1_score: {f1_rfc_param}, accuracy_score: {accuracy_rfc_param} \n")

하이퍼파라미터 튜닝한 RandomForestClassifier 점수

이전 기본모델에 비해 0.07정도 좋아진 것을 확인할 수 있다. 그리고 모델을 복잡하게 만든 만 큼 과대적합이 해소가 되었는지 학습곡선을 통해 확인을 해본다.

# 하이퍼 파라미터 튜닝을 수행 한 RandomForestClassifier의 학습곡선
train_sizes_rfc, train_scores_rfc, test_scores_rfc = learning_curve(
    parameter_rfc, x_resampled, y_resampled, cv = 5, train_sizes=np.linspace(0.1, 1.0, 10))
    
train_scores_mean_rfc = np.mean(train_scores_rfc, axis=1)
train_scores_std_rfc = np.std(train_scores_rfc, axis=1)
test_scores_mean_rfc = np.mean(test_scores_rfc, axis=1)
test_scores_std_rfc = np.std(test_scores_rfc, axis=1)

plt.figure(figsize=(10, 6))
plt.title("Learning Curve")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()

plt.fill_between(train_sizes_rfc, train_scores_mean_rfc - train_scores_std_rfc,
                 train_scores_mean_rfc + train_scores_std_rfc, alpha=0.1, color="r")
plt.fill_between(train_sizes_rfc, test_scores_mean_rfc - test_scores_std_rfc,
                 test_scores_mean_rfc + test_scores_std_rfc, alpha=0.1, color="g")
plt.plot(train_sizes_rfc, train_scores_mean_rfc, 'o-', color="r", label="Training score")
plt.plot(train_sizes_rfc, test_scores_mean_rfc, 'o-', color="g", label="Cross-validation score")

plt.legend(loc="best")
plt.show()

하이퍼파라미터 튜닝을 수행한 RandomForestClassifier 학습곡선

그래프를 보니 모델은 Training 데이터에 대해서 과대적합이 약간 해소가 되었지만 여전히 과대적합이 되어 있다는 것을 확인할 수 있었다. 모델의 복잡성을 줄여주는 방식을 추가 수행을 하여 과대적합을 없애줘야할 것이다.

모델의 복잡성을 줄여주는 방법으로는 주요 Feature 선택, 새로운 모델 선택, 규제 적용 등이 존재하는데, 그 중 Feature 선택을 한번 해보도록 할 것이다. 먼저 RandomForestClassifier에서 모델이 중요하게 생각한 변수에 대해서 출력해준다.

# 변수 중요도 출력 -> 모델 학습을 했던 x_train의 columns 이름을 index로 사용
feature_importances = pd.Series(parameter_rfc.feature_importances_, index = x_train.columns)
print(feature_importances)

변수 중요도

변수 중요도는 이렇게 나왔다. 위 변수 중 payment_pattern은 더미변수이므로 그대로 두고, 나머지 변수의 중요도가 낮은 것을 찾아보니 상대적으로 subscription_type과 preferred_difficulty_level, community_engagement_level이 낮게 나왔다. 이를 가지고만 판단하기 힘들 것으로 보여 상관계수 히트맵을 그려 독립변수끼리의 상관성이 존재하는지 확인을 해보고 상관성을 가지는 변수 중 변수 중요도가 낮은 것을 지워줘 다시 학습을 수행을 해 볼 것이다.

# SMOTE 적용시킨 데이터에 대해 상관계수 계산
corr_data = x_resampled.corr()

# 데이터 프레임화(만약 corr_data가 데이터 프레임 형태가 아니라면)
corr_data = pd.DataFrame(corr_data)

#상관계수 히트맵 시각화
plt.figure(figsize = (10, 10))
sns.heatmap(corr_data, annot=True, annot_kws = {'size' : 6})
plt.show()

상관계수 히트맵

히트맵 결과 subscription_type과 total_completed_courses가 0.41정도의 상관관계를 가지고 있고, subscription_type과 community_engagement_level과 0.44정도의 상관관계를 가지고 있다.

이러한 상관관계 이외에 다른 데이터가 다중공선성을 가지는지에 대해서 확인해보기 위해 VIF(Variance Inflation Factor: 분산 팽창 요인)을 계산해본다.

# VIF 모듈
from statsmodels.stats.outliers_influence import variance_inflation_factor

# VIF 계산
def calculate_vif(data_frame):
    vif_data = pd.DataFrame()
    vif_data["Variable"] = data_frame.columns
    vif_data["VIF"] = [variance_inflation_factor(data_frame.values, i) for i in range(data_frame.shape[1])]
    return vif_data

# x_resampled 데이터의 VIF 계산
vif_result = calculate_vif(x_resampled)
# 출력
print(vif_result)

변수에 대한 VIF 값

VIF 값을 확인해보니 다른 변수들은 문제가 없지만 community_engagement_level에는 VIF가 약5.72로 다중공선성이 존재할 수도 있다.

community_engagement_level은 랜덤포레스트의 변수 중요도가 0.032로 낮은 편이며, 상관관계에서도 subscription_type과 상관관계가 존재하므로 삭제를 해주는 것이 좋을 것 같다. 그리고 subscription_type과 total_completed_courses간의 상관 관계또한 존재하는데 그 중 subscription_type의 변수 중요도는 0.013으로 변수 중요도가 약 0.082인 total_completed_courses보다 매우 낮기 때문에 subscription_type 변수를 삭제한 후 모델을 만들어보도록 한다.

# 데이터를 복사하여 사용할 것
x_resampled_copy = x_resampled.copy()

# 복사한 데이터에서 subscription_type과 community_engagement_level 삭제
x_resampled_copy.drop(['subscription_type', 'community_engagement_level'], axis = 1, inplace = True)

# train, test 데이터로 분리
x_train1, x_test1, y_train1, y_test1 = train_test_split(x_resampled_copy, y_resampled, test_size = 0.2, random_state = 42)

# 모델 선정(하이퍼파라미터 선정)
parameter_rfc = RandomForestClassifier(n_estimators = 500, max_depth = 30, min_samples_leaf=4, min_samples_split=2, random_state = 42)
# 모델 생성
parameter_rfc.fit(x_train1, y_train1)

# 모델로 x_test값 예측
y_pred_rfc_param = parameter_rfc.predict(x_test1)

# 점수 계산
f1_rfc_param = f1_score(y_test1, y_pred_rfc_param, average='macro')
accuracy_rfc_param = accuracy_score(y_test1, y_pred_rfc_param)

print(f"RandomForestClassifier -- f1_score: {f1_rfc_param}, accuracy_score: {accuracy_rfc_param} \n")

변수 제거 후 랜덤포레스트 점수

변수를 제거 한 후 모델을 학습하고 점수를 계산하니 점수에 대한 큰 변화는 존재하지 않았다. 이제 모델이 과대적합이 되어 있는지 확인해보기 위해 학습곡선을 그려서 확인해본 후 하이퍼파라미터 튜닝을 다시 수행하여 마무리 한 후 머신러닝 모델링에 대해서 마치고, TensorFlow Dense를 이용한 예측모델 생성을 해보도록 하겠다.

# 변수 제거한 x_resampled_copy를 이용해 학습곡선을 그려본다.
from sklearn.model_selection import learning_curve
train_sizes_rfc, train_scores_rfc, test_scores_rfc = learning_curve(
    parameter_rfc, x_resampled_copy, y_resampled, cv = 5, train_sizes=np.linspace(0.1, 1.0, 10))
    
train_scores_mean_rfc = np.mean(train_scores_rfc, axis=1)
train_scores_std_rfc = np.std(train_scores_rfc, axis=1)
test_scores_mean_rfc = np.mean(test_scores_rfc, axis=1)
test_scores_std_rfc = np.std(test_scores_rfc, axis=1)

plt.figure(figsize=(10, 6))
plt.title("Learning Curve")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()

plt.fill_between(train_sizes_rfc, train_scores_mean_rfc - train_scores_std_rfc,
                 train_scores_mean_rfc + train_scores_std_rfc, alpha=0.1, color="r")
plt.fill_between(train_sizes_rfc, test_scores_mean_rfc - test_scores_std_rfc,
                 test_scores_mean_rfc + test_scores_std_rfc, alpha=0.1, color="g")
plt.plot(train_sizes_rfc, train_scores_mean_rfc, 'o-', color="r", label="Training score")
plt.plot(train_sizes_rfc, test_scores_mean_rfc, 'o-', color="g", label="Cross-validation score")

plt.legend(loc="best")
plt.show()

변수 제거한 랜덤포레스트 모델 학습곡선

학습곡선에 큰 변화가 존재하지 않은 것 같다. 이제 하이퍼파라미터 튜닝을 통해서 과대적합을 최대한 조절해보도록 한다.

# GridSearchCV - 하이퍼파라미터 탐색하는 모듈
from sklearn.model_selection import GridSearchCV
# 검사할 하이퍼파라미터 값
rfc_grid_param = {
    'n_estimators' : [100, 200],
    'max_depth' : [5, 10],
    'min_samples_split' : [None, 2, 5],
    'min_samples_leaf' : [None, 2, 5]
}

# 최적의 하이퍼파라미터 탐색
grid_search = GridSearchCV(estimator = parameter_rfc, param_grid=rfc_grid_param, cv=5, scoring='accuracy')
grid_search.fit(x_train1, y_train1)

# 최선의 하이퍼파라미터 값 출력
grid_search.best_params_

하이퍼 파라미터 값을 계속 탐색해본 결과 max_depth는 30, min_samples_leaf와 split은 2, n_estimators는 250으로 나왔기 때문에 이에 맞춰서 모델을 생성하도록 한다.

# 랜덤포레스트 새로운 모델 생성
param_rfc_v2 = RandomForestClassifier(n_estimators = 250, max_depth = 30, min_samples_leaf = 2, min_samples_split = 2, random_state = 42)
param_rfc_v2.fit(x_train1, y_train1)

# 점수 계산 및 표시
y_pred_v1 = param_rfc_v2.predict(x_test1)

# 점수 계산
f1_rfc_param_v1 = f1_score(y_test1, y_pred_v1, average='macro')
accuracy_rfc_param_v1 = accuracy_score(y_test1, y_pred_v1)

print(f"RandomForestClassifier -- f1_score: {f1_rfc_param_v1}, accuracy_score: {accuracy_rfc_param_v1} \n")

RandomForestClassifier의 모델 점수가 상승한 것을 확인할 수 있다. 이제 이 모델이 과대적합인지 학습곡선을 한번 더 그려 확인해본 후 머신러닝 모델링은 마무리하도록 한다.

train_sizes_rfc2, train_scores_rfc2, test_scores_rfc2 = learning_curve(
    param_rfc_v2, x_resampled_copy, y_resampled, cv = 5, train_sizes=np.linspace(0.1, 1.0, 10))

train_scores_mean_rfc2 = np.mean(train_scores_rfc2, axis=1)
train_scores_std_rfc2 = np.std(train_scores_rfc2, axis=1)
test_scores_mean_rfc2 = np.mean(test_scores_rfc2, axis=1)
test_scores_std_rfc2 = np.std(test_scores_rfc2, axis=1)

plt.figure(figsize=(10, 6))
plt.title("Learning Curve")
plt.xlabel("Training examples")
plt.ylabel("Score")
plt.grid()

plt.fill_between(train_sizes_rfc2, train_scores_mean_rfc2 - train_scores_std_rfc2,
                 train_scores_mean_rfc2 + train_scores_std_rfc2, alpha=0.1, color="r")
plt.fill_between(train_sizes_rfc2, test_scores_mean_rfc2 - test_scores_std_rfc2,
                 test_scores_mean_rfc2 + test_scores_std_rfc2, alpha=0.1, color="g")
plt.plot(train_sizes_rfc2, train_scores_mean_rfc2, 'o-', color="r", label="Training score")
plt.plot(train_sizes_rfc2, test_scores_mean_rfc2, 'o-', color="g", label="Cross-validation score")

plt.legend(loc="best")
plt.show()

학습곡선을 확인해본 결과 아직까지도 과대적합이 되어있다는 것을 알 수 있다. 무조건적으로 GridSearchCV를 수행하지 않고 모델의 복잡성을 감소시키는 방향으로 모델을 짜서 다시 확인해보면서 고쳐나가야할 것이다.

 

이제부턴 DeepLearning 모델인 TensorFlow Dense 방법을 사용하여 모델을 제작하도록 한다.

# 필요 모듈들 불러오기
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import f1_score
from tensorflow.keras.regularizers import l2
from keras.layers import Dropout

# 모델의 Layer 층 값 입력
model = Sequential()
model.add(Dense(512, input_dim=x_train1.shape[1], activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(256, activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(128, activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(64, activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(32, activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(16, activation='relu'))
#model.add(Dropout(0.1))

#model.add(Dense(16, activation='relu'))
#model.add(Dropout(0.1))

model.add(Dense(1, activation='sigmoid'))

# 최적화 함수 Adam & 학습률 0.001로 지정
from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.001)

# 과대적합 방지를 위한 Early Stopping -> val_loss 기준 30번 비슷한 수치면 정지
from tensorflow.keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)
model.compile(loss = 'binary_crossentropy', optimizer = optimizer, metrics = ['accuracy'])

# 모델 학습 시작
history = model.fit(x_train1, y_train1, epochs=150, batch_size = 16, validation_split=0.2, callbacks=[early_stopping])

# 모델에 대한 loss와 val_loss 시각화
import matplotlib.pyplot as plt
plt.plot(history.history['loss'], 'b-', label = 'loss')
plt.plot(history.history['val_loss'], 'r-', label = 'val_loss')
plt.xlabel('Epoch')
plt.legend()
plt.show()

loss와 val_loss 추이

1개의 입력층과 1개의 출력층 사이에 4개의 은닉층으로 이루어진 모델이며, 활성화 함수로는 relu함수와 sigmoid함수를 사용, 최적화 함수로는 Adam을 사용하고 학습률도 0.001로 지정한 후 val_loss 기준으로 30번 점수가 유지되면 멈추는 Early Stopping을 지정했다. 모델에 대한 Epoch별 loss와 val_loss또한 시각화를 하여 과대적합인지, 과소적합인지 확인을 해보니 loss는 줄어들고 val_loss가 증가를 하면 과대적합일 가능성이 매우 높다.

과대적합인 것을 알았으니, 과대적합을 해결하기 위해 모델의 복잡도를 낮춰야한다.

방법으로는, DropOut 방법과 모델의 복잡도 감소 방법, 더 많은 데이터 추가하는 방법 등이 존재한다.

우리는 그 중 Dropout 방법과 모델 복잡도 감소 방법을 함께 사용을 하여서 과대적합을 제거를 시도할 것이다.

# 모델 + DropOut방법 + 은닉층 수 감소
model = Sequential()
model.add(Dense(128, input_dim=x_train1.shape[1], activation='relu'))
model.add(Dropout(0.3))

model.add(Dense(64, activation='relu'))
model.add(Dropout(0.3))

model.add(Dense(32, activation='relu'))
#model.add(Dropout(0.3))

model.add(Dense(16, activation='relu'))
#model.add(Dropout(0.3))

#model.add(Dense(32, activation='relu'))
#model.add(Dropout(0.1))

#model.add(Dense(16, activation='relu'))
#model.add(Dropout(0.1))

#model.add(Dense(16, activation='elu'))
#model.add(Dropout(0.1))

model.add(Dense(1, activation='sigmoid'))

from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.001)
model.compile(loss = 'binary_crossentropy', optimizer = optimizer, metrics = ['accuracy'])

from tensorflow.keras.callbacks import EarlyStopping

early_stopping = EarlyStopping(monitor='val_loss', patience=15, restore_best_weights=True)

위 코드를 통해 모델의 은닉층을 줄여 모델 복잡도를 감소시키면서 DropOut 방법과 함께 수치를 0.3으로 지정해 삭제를 수행하여 모델의 복잡도를 줄여주었다. 또한 Early Stopping을 30번이 아닌 15번 비슷한 값이 유지가 되면 정지되게 바꾸어서 모델 학습을 수행을 해보았다.

# 모델 학습
history = model.fit(x_train1, y_train1, epochs=150, batch_size = 8, validation_split=0.2, callbacks=[early_stopping])

# 시각화
import matplotlib.pyplot as plt
plt.plot(history.history['loss'], 'b-', label = 'loss')
plt.plot(history.history['val_loss'], 'r-', label = 'val_loss')
plt.xlabel('Epoch')
plt.legend()
plt.show()

결과로는 이런 loss값들이 나오게 되었다. 전과 같은 완전한 과대적합은 아닌 것으로 보인다. 더 정확하게 보기 위해 학습곡선을 이용해서 확인을 해본다.

# 학습곡선 그래프 
plt.plot(history.history['accuracy'], 'b-', label='Training Accuracy')
plt.plot(history.history['val_accuracy'], 'r-', label='Validation Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

학습곡선을 보니 학습은 얼추 잘 된 것 같지만, Validation Accuracy가 0.6에서 그 이상의 성능을 끌어올리지 못하지만 Training Accuracy는 점진적으로 점점 증가하고 있는 것을 보면서 아직까지 과대적합의 현상이 남아있는 것을 확인 할 수 있다. 만약 과대적합을 해결해서 모델이 좋게 나온 것을 확인했다면, 예측값을 만들어준다.

# 예측값 구하기(예측값이 0, 1로 나오는 것이 아닌 정수값으로 나옴)
y_pred_dense = model.predict(x_test1)

# 0.5값을 기준으로 이상이면 1, 이하이면 0의 값으로 만들어서 넣어줌
y_pred_dense = (y_pred > 0.5).astype('int')

#모델에 대한 f1_score와 m1_macro 점수
macro_f1_dense = f1_score(y_test, y_pred_dense, average='macro')
accuracy_dense = accuracy_score(y_test, y_pred_dense)
print(accuracy_dense, '\n')
print(macro_f1_dense)

Accuracy_score하고 Macro f1 score의 점수가 평범한 편이다. (아직 과대적합이 존재하는 모델이어서 그런것 같다.)

 

모델을 가지고 실제 test_data의 예측값을 구한 후 결과 값에 넣어주도록 한다.

# 위의 x_reshaped 데이터에서 상관관계와 다중공선성으로 삭제한 데이터를 test에서도 삭제
test_data.drop(['subscription_type', 'community_engagement_level'], axis = 1, inplace = True)

# test_data에 대해서 예측값을 구함
predictions_dense = model.predict(test_data)

# 구한 예측값에서 0.5를 기준으로 0과 1로 만들어줌
predictions_dense = (predictions_dense > 0.5).astype('int')

# 바꾼 예측값을 result_data의 target 열 값에 넣어줌
result_data['target'] = predictions_dense

# 잘 들어갔는지 확인
result_data

TensorFlow Dense의 예측결과

값이 잘 들어 간 것을 확인할 수 있었다.

이제 이렇게 들어간 값을 저장을 해준다.

# result_data를 경로에 저장한다.
result_data.to_csv('./dataset/resultdata/dense_f_eng_result_v1.csv', index=False)

 

 

이렇게 모든 모델링을 해보게 되었다. 위의 모델링 방법 말고도 다른 좋은 모델링 방법이 존재할 것이고, 이에 대해서도 한번 다뤄보고 싶었으며, 과대적합과 과소적합에 대한 문제를 해결하기가 어려운 것 같았던 것 같다.

 

이 대회에서의 성적으로 답을 제출한 사람 기준 380명 중 79등으로(상위 21퍼) 경진대회를 마감하게 되었다.