Loan Approval Prediction
お正月休みなのでkaggleに挑戦。Loan Approval Prediction(ローン承認予測)に取り組んでみました。
https://www.kaggle.com/competitions/playground-series-s4e10/overview
本格的なEDA、前処理、モデリングをしていくと沼に入りそうなので、まずはベーシックにRandomForestClassifierで特段深く考えずにモデリングをして、評価指標を出してみます。
test dataset(ターゲット変数)
id:ID
person_age:年齢
person_income:年収
person_home_ownership:住宅所有状況
person_emp_length:雇用期間(年単位)
loan_intent:ローンの目的
loan_grade:ローンの信用グレード
loan_amnt:申請したローンの金額
loan_int_rate:ローンの利率
loan_percent_income:ローン金額が収入に占める割合
cb_person_default_on_file:使用履歴に過去のデフォルト記録があるか
cb_person_cred_hist_length:信用履歴の長さ
loan_status:ローン承認のステータス
0:ローン否認
1:ローン承認
やたらと審査が早い、◯◯秒で即融資!みたいなCMをみると、このようなモデルで機械的にやっているのかなぁとかいろいろ考えてしまいます。
まずはEDA
import pandas as pd
train=pd.read_csv("train.csv")
train.info()
気になるラベルを可視化したり、基本統計情報を出したりしますが、ここでは割愛します。
前処理
trianとtestのカテゴリデータに対して前処理を実施。
カテゴリデータを扱うことが最近は多いので、ラベルエンコーダーは使い慣れておきたい。
# カテゴリデータの前処理を実施
from sklearn.preprocessing import LabelEncoder
# カテゴリ列を特定
categorical_columns = train.select_dtypes(include=['object']).columns
# 各カテゴリ列をエンコード
label_encoders = {}
for column in categorical_columns:
le = LabelEncoder()
train[column] = le.fit_transform(train[column])
label_encoders[column] = le
# 前処理後の最初の数行を表示
train.head()
冗長だけど一旦速度重視でtestデータも前処理。
こういう無駄なコードを書かずにできるようになりたい。。
# カテゴリ列を特定(文字列型またはオブジェクト型)
categorical_columns = test.select_dtypes(include=['object']).columns
# 各カテゴリ列をエンコード
label_encoders = {}
for column in categorical_columns:
le = LabelEncoder()
# ラベルエンコードを適用
test[column] = le.fit_transform(test[column].astype(str))
label_encoders[column] = le
# 前処理後のデータ型を確認
test.dtypes
モデリング
初手としてRandomForestClassifierをまずは適用してみる。
XGBTとかも強いそうですけど、堅牢なRandomForestを初手で使うことが自分は多いです。
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score
X=train.drop(columns=["id","loan_status"],axis=1)
y=train["loan_status"]
# 学習用データとテスト用データに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# ランダムフォレストを使用してモデル構築
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
# テストデータで予測
y_pred = model.predict(X_test)
評価指標
from sklearn.metrics import roc_auc_score
roc_auc=roc_auc_score(y_test,y_pred)
print(roc_auc)
[Out]0.8459977368634657
これだとコンペとしては3000名中下から数えたほうが早いくらいの性能なので、もう少し工夫をしてみる。
再チェレンジ
先人様のコードも参考にさせていただきながら、もう少し良い指標がでるように粘ってみます。
EDA
columns = ['person_age', 'person_income', 'loan_amnt', 'loan_int_rate', 'loan_percent_income', 'cb_person_cred_hist_length']
plt.figure(figsize=(15, 10))
for i, col in enumerate(columns, 1):
plt.subplot(3, 2, i) # Adjust layout depending on the number of plots
sns.histplot(df_train[col], bins=50, kde=True, color='blue') # Histogram with KDE
plt.title(f'Distribution of {col}')
plt.xlabel(col)
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()
numerical_columns = ['person_age', 'person_income', 'person_emp_length', 'loan_amnt', 'loan_int_rate', 'loan_percent_income', 'cb_person_cred_hist_length']
plt.figure(figsize=(15, 10))
for i, col in enumerate(numerical_columns, 1):
plt.subplot(4, 2, i) # Adjust layout depending on the number of plots
sns.boxplot(x=df_train[col], color='lightblue') # Boxplot for each numerical column
plt.title(f'Boxplot of {col}')
plt.xlabel(col)
plt.tight_layout()
plt.show()
sns.pairplot(df_train, hue='loan_status', vars=['person_age', 'person_income', 'loan_amnt', 'loan_int_rate', 'loan_percent_income'], kind='reg')
plt.show()
histplot,boxplot,pairplotで全体的な傾向を把握します。
2値分類の際はpairplotで可視化すると思わぬ組合せなどの傾向が発見できたり、直感的な相互作用がみえてきたりします。
前処理
df_train.columns[df_train.dtypes == 'object']
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
le = LabelEncoder()
df_train['loan_grade'] = le.fit_transform(df_train['loan_grade'])
df_test['loan_grade'] = le.fit_transform(df_test['loan_grade'])
nominal_features = ['person_home_ownership', 'loan_intent', 'cb_person_default_on_file']
df_train = pd.get_dummies(df_train, columns=nominal_features, drop_first=True)
df_test = pd.get_dummies(df_test, columns=nominal_features, drop_first=True)
#%%
from sklearn.preprocessing import StandardScaler
categorical_cols = ['loan_grade', 'person_home_ownership_OTHER', 'person_home_ownership_OWN',
'person_home_ownership_RENT', 'loan_intent_EDUCATION','loan_intent_HOMEIMPROVEMENT',
'loan_intent_MEDICAL','loan_intent_PERSONAL', 'loan_intent_VENTURE','cb_person_default_on_file_Y', 'loan_status']
numerical_cols = [col for col in df_train.columns if col not in categorical_cols]
scaler = StandardScaler()
X_scaled_numerical = scaler.fit_transform(df_train[numerical_cols])
df_train = pd.concat([pd.DataFrame(X_scaled_numerical, columns=numerical_cols), df_train[categorical_cols].reset_index(drop=True)], axis=1)
categorical_cols = ['loan_grade', 'person_home_ownership_OTHER', 'person_home_ownership_OWN',
'person_home_ownership_RENT', 'loan_intent_EDUCATION','loan_intent_HOMEIMPROVEMENT',
'loan_intent_MEDICAL','loan_intent_PERSONAL', 'loan_intent_VENTURE','cb_person_default_on_file_Y']
numerical_cols = [col for col in df_test.columns if col not in categorical_cols]
scaler = StandardScaler()
X_scaled_numerical = scaler.fit_transform(df_test[numerical_cols])
df_test = pd.concat([pd.DataFrame(X_scaled_numerical, columns=numerical_cols), df_test[categorical_cols].reset_index(drop=True)], axis=1)
基本的には同じ。違いはOneHotEncoderの導入とStandardScalerで標準化しています。
モデリング
X = df_train.drop('loan_status', axis=1) # Features
y = df_train['loan_status']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_auc_score
y_train.unique()
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
# Prediction
y_pred = rf_model.predict(X_test)
# Evaluation
roc_auc = roc_auc_score(y_test, y_pred)
print(f'ROC AUC Score: {roc_auc}')
ROC AUC Score: 0.8561526896270413
先ほどよりかは幾分精度向上しましたが、0.90以上を叩き出しているコードをみると特徴量エンジニアリングやハイパーモデルチューニングをしています。
一旦は、今日のアウトプットということで引き続き他のkaggleのコンペとかも楽しんでいきたいと思います。