在今天的机器学习时代,各种算法和工具层出不穷,其中LightGBM凭借其出色的性能和速度,在众多机器学习算法中脱颖而出。本次实战项目,将通过一个简单但不简约的案例,展示如何使用LightGBM进行建模,并通过网格搜索进行模型优化。整个流程将包含数据探索性分析(EDA)、建模和模型优化三个主要部分。
首先,我们需要对数据进行探索性分析,以了解数据的分布、特征之间的相关性以及目标变量的趋势。这一步骤对于后续的建模至关重要,因为只有深入了解数据,我们才能选择合适的特征和算法进行建模。
数据加载与预处理
使用Python的pandas库加载数据,并进行必要的预处理,如缺失值填充、异常值处理等。
数据描述性分析
通过绘制直方图、箱线图等可视化图表,我们可以直观地了解每个特征的分布情况。同时,计算特征的均值、中位数、标准差等统计量,有助于我们进一步了解数据的整体情况。
特征相关性分析
利用热力图或相关矩阵,我们可以分析特征之间的相关性。这对于后续的特征选择和模型构建具有重要的指导意义。
在完成数据探索性分析后,我们将使用LightGBM进行建模。LightGBM是一种基于决策树算法的梯度提升框架,具有高效、快速、准确等优点。
数据划分
将数据集划分为训练集和测试集,以便在训练模型时评估其性能。
模型训练
使用LightGBM的API进行模型训练。我们可以调整LightGBM的参数,如学习率、树的数量、叶子节点的最小样本数等,以寻找最佳模型。
模型评估
通过计算准确率、召回率、F1值等指标,评估模型在测试集上的性能。同时,绘制ROC曲线和AUC值,进一步了解模型的分类性能。
虽然LightGBM具有许多优秀的默认参数,但针对不同的问题和数据集,我们仍然需要调整参数以获得更好的性能。网格搜索是一种常用的参数优化方法,它通过遍历所有可能的参数组合来找到最优参数。
参数空间定义
根据LightGBM的文档和经验,我们定义一个包含多个参数的参数空间。这些参数可能包括学习率、树的数量、最大深度、叶子节点的最小样本数等。
网格搜索执行
使用scikit-learn的GridSearchCV函数进行网格搜索。该函数将自动遍历参数空间中的所有组合,并使用交叉验证评估每个组合的性能。最终,它将返回具有最佳性能的参数组合。
最优模型构建与评估
使用网格搜索找到的最优参数组合构建新的LightGBM模型,并在测试集上进行评估。与之前的模型相比,优化后的模型应该具有更好的性能。
实战项目使用的数据集,具体的实现代码如下:
import pandas as pd
import numpy as np
pd.set_option('display.max_columns', 100)
from IPython.display import display_html
import plotly_express as px
import plotly.graph_objects as go
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"] = False #解决“-”负号乱码问题
import seaborn as sns
%matplotlib inline
import missingno as ms
import gc
from datetime import datetime
from sklearn.model_selection import train_test_split, StartifiedKFold, Gr
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.decomposition import PCA
from imblearn.under_sampling import ClusterCentroids
from imblearn.over_sampling import KMeansSMOTE, SMOTE
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score, recall_score, precision_sco
from sklearn.metrics import roc_auc_score, precision_recall_curve, confu
# Classifiers
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn import tree
from pydotplus import graph_from_dot_data
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from catboost import CatBoostClassifier
import lightgbm as lgb
import xgboost as xgb
from scipy import stats
import warnings
warnings.filterwarnings("ignore")
df = pd.read_csv("random_data.csv")
df.columns
# 缺省值情况
df.isnull().sum()
df["History_Default_Times"].value_counts() # 历史违约次数统计:
df["Sex"].value_counts() # 男女人数几乎相同,很均衡。
df["Default"].value_counts() # 目标变量是否违约的人数对比
df[df["History_Default_Times"] == 2] # 需要注意的是:历史违约次数大于0,不代表一定是违约客户。比如历史违约次数为2,最终是否违约的客户两种情况都有。
# 是否违约的客户收入存在差异:
fig = px.violin(df, x="Default",y="Income")
fig.show()
# 基于seaborn绘制密度图:
sns.displot(data=df,x="Income",hue="Default",kind="kde")
plt.show() # 可以看到在低收入和高收入人群中容易发生违约。
fig = px.violin(df, x="Default",y="Age")
fig.show()
sns.displot(data=df,x="Age",hue="Default",kind="kde")
plt.show() # 可以看到是是否违约客户的年龄段分布是一致的。
# 提取特征和目标变量
X = df.drop(columns="Default")
Y = df["Default"]
# 划分训练集和测试集数据
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 lightgbm import LGBMClassifier
model = LGBMClassifier()
model.fit(X_train, y_train) # 模型训练
y_pred = model.predict(X_test)
y_pred
### 1、对比测试集中的实际值和预测值:
predict_true = pd.DataFrame()
predict_true["预测值"] = list(y_pred)
predict_true["实际值"] = list(y_test)
predict_true
# 模型在测试集上的准确率
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_pred, y_test)
accuracy
# ROC-AUC曲线的绘制:
y_pred_proba = model.predict_proba(X_test)
y_pred_proba[:5]
from sklearn.metrics import roc_curve # ROC-AUC曲线
fpr, tpr, thres = roc_curve(y_test, y_pred_proba[:,1])
plt.plot(fpr, tpr)
plt.title("ROC_AUC Curve of Default")
plt.show()
# 3、查看具体的AUC值:
from sklearn.metrics import roc_auc_score
score = roc_auc_score(y_test, y_pred_proba[:,1])
score
model.feature_importances_
model.feature_name_
features_df = pd.DataFrame({"features": model.feature_name_,"importances": model.feature_importances_})
features_df
# 基于网格搜索的模型调优:
from sklearn.model_selection import GridSearchCV
# 设定待搜索的参数及其取值范围:
parameters = {"num_leaves": [10, 15, 13],
"n_estimators":[10,20,30],
"learning_rate":[0.05,0.1,0.2]
}
model = LGBMClassifier() # 基础模型实例化
# 定义搜索实例化对象
grid_search = GridSearchCV(model, # 基础模型
parameters, # 搜索参数
scoring="roc_auc", # 评价指标
cv=5 # 交叉验证5次
)
grid_search.fit(X_train, y_train) # 模型训练
import os
import gc
import math
import pandas as pd
import numpy as np
import lightgbm as lgb
import xgboost as xgb
from catboost import CatBoostRegressor
from sklearn.linear_model import SGDRegressor, LinearRegression, Ridge
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.metrics import log_loss
from sklearn.model_selection import train_test_split
from tqdm import tqdm
import matplotlib.pyplot as plt
import time
import warnings
warnings.filterwarnings('ignore')
def cv_model(clf, train_x, train_y, test_x, seed=2024):
# 使用K折交叉验证训练和验证模型
folds = 5
kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
# 初始化oof预测和测试集预测
oof = np.zeros(train_x.shape[0])
test_predict = np.zeros(test_x.shape[0])
cv_scores = []
# KFold交叉验证
for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
print('************************************ {} ************************************'.format(str(i+1)))
trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
# 转换数据为lightgbm数据格式
train_matrix = clf.Dataset(trn_x, label=trn_y)
valid_matrix = clf.Dataset(val_x, label=val_y)
# 定义lightgbm参数
params = {
'boosting_type': 'gbdt', # GBDT算法为基础
'objective': 'multiclass', # 多分类任务
'num_class': 4, # 设置多分类问题的类别个数
'num_leaves': 2 ** 5, # 指定叶子的个数,默认值为31,大会更准,但可能过拟合。最大不能超过2^max_depth
# 构建弱学习器,对特征随机采样的比例,默认值为1,可以防止过拟合,每次迭代中随机选择80%的参数来建树
'feature_fraction': 0.8,
'bagging_fraction': 0.8, # 每次迭代时用的数据比例,用于加快训练速度和减小过拟合
'bagging_freq': 4, # 表示bagging(采样)的频率,0意味着没有使用bagging ,k意味着每k轮迭代进行一次bagging
'learning_rate': 0.025,
'seed': seed,
# 使用线程数,一般设置成-1,使用所有线程。这个参数用来控制最大并行的线程数,如果你希望取得所有CPU的核,那么你就不用管它。
'nthread': 28,
'n_jobs':24, # 使用多少个线性并构造模型
'verbose': -1,
'lambda_l1': 0.4, # L1正则化权重项,增加此值将使模型更加保守。
'lambda_l2': 0.5, # L2正则化权重项,增加此值将使模型更加保守
# 'device' : 'gpu'
}
# 使用训练集数据进行模型训练
model = clf.train(params,
train_set=train_matrix,
valid_sets=valid_matrix,
num_boost_round=2000,
verbose_eval=100,
early_stopping_rounds=200)
# 对验证集进行预测
val_pred = model.predict(val_x, num_iteration=model.best_iteration)
test_pred = model.predict(test_x, num_iteration=model.best_iteration)
oof[valid_index] = val_pred
test_predict += test_pred / kf.n_splits
# 计算打印当前折的分数
score = np.sqrt(mean_squared_erroe(vale_pred, val_y))
cv_scores.apped(score)
print(cv_scores)
return oof, test_predict
# 输出最佳的参数组合:
dict_params = grid_search.best_params_
dict_params
# 基于最佳的参数组合建立新模型:
new_model = LGBMClassifier(num_leaves=10, # 使用最佳参数
n_estimators=30,
learning_rate=0.05
)