한글폰트 적용 - 코랩

!sudo apt-get install -y fonts-nanum
!sudo fc-cache -fv
!rm ~/.cache/matplotlib -rf

 

Fixed Random Seed 

seed 값에 의해 동일한 코드를 사용해도 결과가 다를 수 있기에, 동일한 결과를 위해 seed값을 고정시킵니다.

import numpy as np
import random
import os

def seed_everything(seed):
    random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
    np.random.seed(seed)

seed_everything(42) # Seed 고정

 

데이터 불러오기 및 확인 

import pandas as pd

train_df = pd.read_csv('/content/sample_data/train.csv')
test_df = pd.read_csv('/content/sample_data/test.csv')

display(train_df.head(3))
display(test_df.head(3))

 

 

EDA

범주형 변수 확인

# 시각화 패키지 불러오기
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

# 한글 폰트 설정하기
fe = fm.FontEntry(fname = 'MaruBuri-Regular.otf', name = 'MaruBuri')
fm.fontManager.ttflist.insert(0, fe)
plt.rc('font', family='NanumBarunGothic')

fig, axes = plt.subplots(2, 2, figsize=(25,10)) # 2x2 형식으로 4개 그래프 동시에 표시

sns.countplot(x = train_df['대출기간'], ax=axes[0][0]).set_title('대출기간')
sns.countplot(x = train_df['근로기간'], ax=axes[0][1]).set_title('근로기간')
sns.countplot(x = train_df['주택소유상태'], ax=axes[1][0]).set_title('주택소유상태')
sns.countplot(x = train_df['대출목적'], ax=axes[1][1]).set_title('대출목적')

plt.show()

 

근로기간에서 < 1 year이랑 <1 year이 있는걸 확인했습니다.

둘 다 똑같은 의미를 취하는 것이기에 같게 만들어주는 작업이 필요할 듯 합니다.

그런데 근로기간의 데이터를 다시 한번 살펴보니 year에 3이 들어가있는걸 확인했고 10+ year, 1year도 중복이 되기에 마찬가지로 바꿔줍니다.

 

fig, axes = plt.subplots(2, 2, figsize=(25,10)) # 2x2 형식으로 4개 그래프 동시에 표시

sns.countplot(x = train_df['대출기간'], ax=axes[0][0]).set_title('대출기간')
sns.countplot(x = train_df['근로기간'], ax=axes[0][1]).set_title('근로기간')
sns.countplot(x = train_df['주택소유상태'], ax=axes[1][0]).set_title('주택소유상태')
sns.countplot(x = train_df['대출목적'], ax=axes[1][1]).set_title('대출목적')

plt.show()

 

다시 확인해주면 잘 처리된걸 확인할 수 있습니다.

 

세분화된 변수형 찾기

# '범주형 변수 찾기'
numeric_columns = train_df.select_dtypes(include=['number'])
categorical_col_names = train_df.select_dtypes(include=['object']).columns.tolist()

# '범주형 변수 정보 확인하기'
summary = {}
unique_counts = {}
for col in categorical_col_names:
    summary[col] = train_df[col].value_counts().to_dict()
    unique_counts[col] = train_df[col].nunique()

print(unique_counts)

 

범주형 변수의 경우 범주가 너무 세분화되어 있을 경우, One-Hot Encoding시 차원의 저주에 빠지기 쉽습니다. 따라서 너무 세분화된 범주형 변수를 제거해주기로 결정합니다.

따라서 ID를 제거해줍니다.

 

 

EDA: 수치형 변수 확인하기

결측치 확인

numeric_columns_train = train_df.select_dtypes(include=['number'])
numeric_columns_test = test_df.select_dtypes(include=['number'])

null_tot_train = numeric_columns_train.isnull().sum()
null_tot_test = numeric_columns_test.isnull().sum()

print(null_tot_train)
print(null_tot_test)

 

Describe & info

numeric_columns_train.describe()

부채_대비_소득_비율: max가 9999인게 조금 이상합니다.

총계좌수: max가 169인게 이상합니다. 

최근_2년간_연체_횟수: max가 30번인게 이상합니다.

연간소득: 0인데 대출이 가능했는지 의문이 듭니다. 

min_income = train_df['연간소득'].min()
rows_with_min_income = train_df[train_df['연간소득'] == min_income]
rows_with_min_income

부채_대비_소득_비율의 값이 9999인 것과 연간소득 0인데이터가 중복되는걸 확인할 수 있습니다.

 

이 데이터는 모델을 학습시킬 때 좋지 않다고 판단하여 제거하기로 결정했습니다.

수치형 변수 시각화

# 수치형 변수들을 시각화 (예: 히스토그램)
numeric_columns_train.hist(bins=10, figsize=(10, 6))
plt.suptitle('Numeric Variables Distribution')
plt.show()

 

대출금액을 제외하고 대부분의 데이터들이 좌측편향돼있다는 걸 확인할 수 있습니다.

이걸 로그변환시킬지 아니면 이상치의 값들을 제거해야할지 결정을 해줘야할 듯합니다.

그러나 저는 로그변환을 시도한 데이터가 좋지 않은 예측값을 냈기에 이상치의 값들을 일부분 제거하여 모델을 학습시키는게 좋을 듯 합니다. 

데이터 전처리 1: 학습 및 추론 데이터 설정

제출을 위한 코드와 제가 확인할 코드를 나눠서 진행하였습니다.

from sklearn.model_selection import train_test_split

X = train_df.drop(columns=['ID', '근로기간','대출등급'])
y = train_df['대출등급']
train_x = train_df.drop(columns=['ID', '근로기간', '대출등급'])
train_y = train_df['대출등급']

test_x = test_df.drop(columns=['ID', '근로기간'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)

데이터 전처리 2: 범주형 변수 수치화

LabelEncoder를 통해 범주형 변수 수치화를 진행합니다.

from sklearn.preprocessing import LabelEncoder

categorical_features = ['대출기간', '주택소유상태', '대출목적']

for i in categorical_features:
    le = LabelEncoder()
    le=le.fit(train_x[i])
    le=le.fit(X[i])

    train_x[i]=le.transform(train_x[i])
    X[i]=le.transform(X[i])

    for case in np.unique(test_x[i]):
        if case not in le.classes_:
            le.classes_ = np.append(le.classes_, case)
    test_x[i]=le.transform(test_x[i])

display(train_x.head(3))
display(test_x.head(3))

 

 

모델 선정 및 학습

RandomForestClassifier

from sklearn.ensemble import RandomForestClassifier

RFC = RandomForestClassifier()
RFC.fit(X_train, y_train)
max_depth_list = range(20,25)
accuracy = []

for m in max_depth_list:
    RFC = RandomForestClassifier(random_state = 42, max_depth = m)
    RFC.fit(X_train, y_train)
    pred = RFC.predict(X_test)
    score = accuracy_score(pred, y_test)
    accuracy.append(score)

plt.plot(max_depth_list, accuracy)
plt.xlabel('max_depth')
plt.ylabel('accuracy')
plt.show()

최적의 깊이를 찾기위한 코드

from sklearn.metrics import accuracy_score

pred = RFC.predict(X_test)
score = accuracy_score(pred, y_test)
print(f"정확도: {score}")

가장 잘 나온 max_depth의 값을 선택하여 예측한 결과 정확도는 0.8231이 나왔습니다.

 

DecisionTreeClassifier

DT = DecisionTreeClassifier(random_state = 42, criterion = 'entropy', max_depth = 5)
DT.fit(X_train, y_train)
max_depth_list = range(1,101)
accuracy = []

for m in max_depth_list:
    DT = DecisionTreeClassifier(random_state = 42, criterion = 'entropy', max_depth = m)
    DT.fit(X_train, y_train)
    pred = DT.predict(X_test)
    score = accuracy_score(pred, y_test)
    accuracy.append(score)

plt.plot(max_depth_list, accuracy)
plt.xlabel('max_depth')
plt.ylabel('accuracy')
plt.show()

가장 최적의 max_depth

DT = DecisionTreeClassifier(random_state = 42, criterion = 'entropy', max_depth = 19)
DT.fit(train_x, train_y)

 

가장 잘 나온 max_depth로 학습시켜줍니다.

 

KNeighborsClassifier

n_neighbors_list = range(1,10)
accuracy = []

for m in n_neighbors_list:
    KNN = KNeighborsClassifier(n_neighbors = m)
    KNN.fit(X_train, y_train)
    pred = KNN.predict(X_test)
    score = accuracy_score(pred, y_test)
    accuracy.append(score)

plt.plot(n_neighbors_list, accuracy)
plt.xlabel('n_neighors')
plt.ylabel('accuracy')
plt.show()

정확도가 앞서 실행한 두 모델에 비해 현저하게 낮기에 이 모델은 채택안하기로 결정합니다.

 

그래서 예측을 수행하여 데이콘 대회에 제출한 결과

0.79의 정확도를 얻었습니다.

처음으로 대회에 참여하여서 이정도면 나쁘지 않은 결과라고 생각합니다. 

앞으로 더욱 정진하겠습니다!

'프로그래밍 > 프로젝트' 카테고리의 다른 글

ARIMA 모델 검증 및 예측 정확도 평가  (0) 2024.05.21
ARIMA 모델  (0) 2024.05.21
고객 유지를 위한 필요한 행동 예측  (1) 2024.01.14
원본 데이터 보존  (0) 2023.11.28
머신러닝 기초 및 순서  (0) 2023.11.21

+ Recent posts