根据Datawhale给的参考代码与自己的习惯,一般首先会选择合适的模型,接着调整数据集的划分方式,最后调整参数。
模型选择
随着近几年集成学习的发展,越来越多人在竞赛中会选择集成学习训练模型,而在实际中使用集成学习所得的结果也确实会优于传统的模型,对此我们可以使用模型进行验证。根据Datawhale所提供的课件,分别对逻辑回归模型、决策树模型和LightGBM模型进行验证。
为了较为准确的量化模型的泛化能力,首先将数据集拆分为训练集和测试集,测试集不参与所有的模型的训练。
逻辑回归模型
首先训练逻辑回归模型
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
sc=StandardScaler()
sc.fit(X_train)
X_train_std=sc.transform(X)
X_test_std=sc.transform(x)
lr=LogisticRegression(C=1000.0,random_state=0)
starttime = datetime.datetime.now()
lr.fit(X_train_std,y_train)
endtime = datetime.datetime.now()
print((endtime - starttime).seconds)
y_pred=lr.predict_proba(X_test_std)
preds = np.argmax(y_pred, axis=1)
score = f1_score(y_true=y, y_pred=preds, average='macro')
print('逻辑回归模型的f1:{}'.format(score))
根据训练结果,得到模型训练时间为9s,f1_score为0.7685。
决策树模型
接着训练决策树模型
from sklearn import tree
from sklearn.metrics import classification_report
clf = tree.DecisionTreeClassifier(criterion='entropy')
clf.fit(X, Y)
answer = clf.predict_proba(x)
preds = np.argmax(answer, axis=1)
score = f1_score(y_true=y, y_pred=preds, average='macro')
print('决策树模型的f1:{}'.format(score))
根据训练结果,得到模型训练时间为19s,f1_score为0.9185。
LightGBM
最后训练LightGBM模型(这里会想对训练集再做一次划分)
X_train_split, X_val, y_train_split, y_val = train_test_split(X, Y, test_size=0.2)
train_matrix = lgb.Dataset(X_train_split, label=y_train_split)
valid_matrix = lgb.Dataset(X_val, label=y_val)
params = {
"learning_rate": 0.1,
"boosting": 'gbdt',
"lambda_l2": 0.1,
"max_depth": -1,
"num_leaves": 128,
"bagging_fraction": 0.8,
"feature_fraction": 0.8,
"metric": None,
"objective": "multiclass",
"num_class": 4,
"nthread": 10,
"verbose": -1,
}
starttime = datetime.datetime.now()
model = lgb.train(params,
train_set=train_matrix,
valid_sets=valid_matrix,
num_boost_round=2000,
verbose_eval=50,
early_stopping_rounds=200,
feval=f1_score_vali)
endtime = datetime.datetime.now()
print((endtime - starttime).seconds)
val_pre_lgb = model.predict(x, num_iteration=model.best_iteration)
preds = np.argmax(val_pre_lgb, axis=1)
score = f1_score(y_true=y, y_pred=preds, average='macro')
print('未调参前lightgbm单模型在验证集上的f1:{}'.format(score))
根据训练结果,得到模型训练时间为160s,f1_score为0.9575。
由此可以看出各类模型的优缺点。
逻辑回归模型虽然拟合速度快,但是其泛化能力一般,同时需要对数据做预处理。
决策树模型拟合速度较快,泛化能力也不错。
LightGBM模型虽然拟合速度最慢,但是其泛化效果最好。
数据集划分
对于数据集的划分方法有三种,留出法,交叉验证法和自助法。
留出法
留出法即直接将数据集进行拆分训练集和测试集,常见的比例为训练集:测试集=7:3。一般可以通过sklearn库实现。
from sklearn.model_selection import train_test_split
X_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
k折交叉验证法
通常将数据集D分为k份,其中k-1份作为训练集,剩余的一份作为测试集,这样就可以获得k组训练测试集,可以进行k次训练与测试,最终返回的是k个测试结果的均值。交叉验证中数据集的划分依然是依据分层采样的方式来进行。对于交叉验证法,其k值的选取往往决定了评估结果的稳定性和保真性,通常k值选取5或10。
自助法
没怎么接触过,所以参考Datawhale所提供的解释为:我们每次从数据集D中取一个样本作为训练集中的元素,然后把该样本放回,重复该行为m次,这样我们就可以得到大小为m的训练集,在这里面有的样本重复出现,有的样本则没有出现过,我们把那些没有出现过的样本作为测试集。 进行这样采样的原因是因为在D中约有36.8%的数据没有在训练集中出现过。留出法与交叉验证法都是使用分层采样的方式进行数据采样与划分,而自助法则是使用有放回重复采样的方式进行数据采样。
实际效果
为了检验数据集划分对泛化能力的影响,我们在前面LightGBM模型的基础上,进行五折交叉验证,之后再进行预测。
"""使用lightgbm 5折交叉验证进行建模预测"""
starttime = datetime.datetime.now()
cv_scores = []
for i, (train_index, valid_index) in enumerate(kf.split(X_train, y_train)):
print('************************************ {} ************************************'.format(str(i+1)))
X_train_split, y_train_split, X_val, y_val = X_train.iloc[train_index], y_train[train_index], X_train.iloc[valid_index], y_train[valid_index]
train_matrix = lgb.Dataset(X_train_split, label=y_train_split)
valid_matrix = lgb.Dataset(X_val, label=y_val)
params = {
"learning_rate": 0.1,
"boosting": 'gbdt',
"lambda_l2": 0.1,
"max_depth": -1,
"num_leaves": 128,
"bagging_fraction": 0.8,
"feature_fraction": 0.8,
"metric": None,
"objective": "multiclass",
"num_class": 4,
"nthread": 10,
"verbose": -1,
}
model = lgb.train(params,
train_set=train_matrix,
valid_sets=valid_matrix,
num_boost_round=2000,
verbose_eval=100,
early_stopping_rounds=200,
feval=f1_score_vali)
val_pred = model.predict(X_val, num_iteration=model.best_iteration)
val_pred = np.argmax(val_pred, axis=1)
cv_scores.append(f1_score(y_true=y_val, y_pred=val_pred, average='macro'))
print(cv_scores)
endtime = datetime.datetime.now()
print((endtime - starttime).seconds)
print("lgb_scotrainre_list:{}".format(cv_scores))
print("lgb_score_mean:{}".format(np.mean(cv_scores)))
print("lgb_score_std:{}".format(np.std(cv_scores)))
val_pre_lgb = model.predict(x, num_iteration=model.best_iteration)
preds = np.argmax(val_pre_lgb, axis=1)
score = f1_score(y_true=y, y_pred=preds, average='macro')
print('交叉验证后模型在验证集上的f1:{}'.format(score))
根据训练结果,得到模型训练时间为302s,f1_score为0.9615。可以看出,调整数据集的切割方式,是可以提升模型的泛化能力。
模型调参
这一部分代码运行失败,所以只能根据所提供的文档进行解释。
文档所提供的调参方法有三种,贪心调参、网格搜索和贝叶斯调参。
贪心调参
先使用当前对模型影响最大的参数进行调优,达到当前参数下的模型最优化,再使用对模型影响次之的参数进行调优,如此下去,直到所有的参数调整完毕。 这个方法的缺点就是可能会调到局部最优而不是全局最优,但是只需要一步一步的进行参数最优化调试即可,容易理解。 需要注意的是在树模型中参数调整的顺序,也就是各个参数对模型的影响程度,这里列举一下日常调参过程中常用的参数和调参顺序:
1、max_depth、num_leaves
2、min_data_in_leaf、min_child_weight
3、bagging_fraction、 feature_fraction、bagging_freq
4、reg_lambda、reg_alpha
5、min_split_gain
网格搜索
sklearn 提供GridSearchCV用于进行网格搜索,只需要把模型的参数输进去,就能给出最优化的结果和参数。相比起贪心调参,网格搜索的结果会更优,但是网格搜索只适合于小数据集,一旦数据的量级上去了,很难得出结果。
贝叶斯调参
贝叶斯调参的主要思想是:给定优化的目标函数(广义的函数,只需指定输入和输出即可,无需知道内部结构以及数学性质),通过不断地添加样本点来更新目标函数的后验分布(高斯过程,直到后验分布基本贴合于真实分布)。简单的说,就是考虑了上一次参数的信息,从而更好的调整当前的参数。
贝叶斯调参的步骤如下:
1、定义优化函数(rf_cv)
2、建立模型
3、定义待优化的参数
4、得到优化结果,并返回要优化的分数指标