智慧海洋task04 利用数据进行建模并调参

智慧海洋 task 4

 本次task的主要任务是学习利用清洗好的数据来进行建模。学习任务分为3点:(1) 学习如何选择合适的模型并通过模型来进行特征选择(2)掌握随机森林、lightGBM、Xgboost的使用(3)掌握贝叶斯优化方法的使用

1.模型训练与预测

(1) 随机森林算法
 从直观来讲,我们可讲决策树视为对于一系列问题结果选择的流程,因此其具有可解释性,而随机森林则是以决策树作为基学习器的集成学习模型(bagging类型)。随机森林中“随机”一词来源于:(a)在构建决策树时基于BootStrap法对样本进行随机抽样(b)在节点进行分裂时考虑随机采用特征的部分子集(通常设置为 n f e a t u r e s \sqrt{n_{features}} nfeatures )。从偏差(算法的期望预测值与真实值之间的偏离程度)-方差(方差则衡量了同等大小的数据集的变动导致预测结果发生的变动大小)分解的角度来看,若假定单个模型的方差为 σ 2 \sigma^2 σ2,且任意两个模型的分布具有相同的相关性 ρ \rho ρ,此时基于n个基学习器构成的随机森林模型的方差为:
     V a r ( ∑ i = 1 n x i n ) = ρ σ 2 + 1 − ρ n σ 2 Var(\frac{\sum_{i=1}^nx_{i}}{n})=\rho\sigma^2+\frac{1-\rho}{n}\sigma^2 Var(ni=1nxi)=ρσ2+n1ρσ2
可以看出随着n增大,模型的方差会逐渐降低;值得注意的是,随机森林中的一个随机性来自于节点进行分裂时的属性选择,该操作可以进一步降低各个子模型之间的相关性,使得模型的方差进一步减少(随机森林会比单纯对决策树进行bagging效果好的原因正在于方差更小)。但是需要注意的是,由于每个模型均是对原始数据进行随机抽样,在抽样分布相同的情况下:
        E ( ∑ i n x i n ) = E ( x i ) E(\frac{\sum_{i}^n{x_{i}}}{n})=E(x_{i}) E(ninxi)=E(xi)
模型的偏差并不会降低。

  • random forest模型在sklearn中的使用:
from sklearn import datasets
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
#数据集导入
iris=datasets.load_iris()
feature=iris.feature_names
X = iris.data
y = iris.target
#随机森林
clf=RandomForestClassifier(n_estimators=200)
train_X,test_X,train_y,test_y = train_test_split(X,y,test_size=0.1,random_state=5)
clf.fit(train_X,train_y)
test_pred=clf.predict(test_X)
# 查看特征的重要性
print(str(feature)+'\n'+str(clf.feature_importances))
# F1-score 用于模型评价
# 如果是二分类问题则选择参数‘binary’
# 如果考虑类别的不平衡性,需要计算类别的加权平均,则使用‘weighted’
# 如果不考虑类别的不平衡性,计算宏平均,则使用‘macro’
score=f1_score(test_y,test_pred,average='macro')
print("随机森林-macro:",score)
score=f1_score(test_y,test_pred,average='weighted')
print("随机森林-weighted:",score)

随机森林算法的优点:
在这里插入图片描述
随机森林的缺点:
在这里插入图片描述

(2) boosting算法
  bagging算法是对同一组数据进行抽样,采用不同的子数据集来构造多个基学习器来进行分类,而boosting算法则是通过对同一组数据进行反复学习,得到一系列简单的组合,最后组合这些简单模型得到一个具有优良性能的分类器。从偏差-方差分解的角度来看,假定有 n n n个学习器,第 i i i个学习器的权重为 w i w_{i} wi,则此时学习器的偏差 E ( ∑ i n w i x i n ) E(\frac{\sum_{i}^nw_{i}x_{i}}{n}) E(ninwixi)(其中 ∑ w i = 1 \sum{w_{i}}=1 wi=1),对于偏差较大的基学习器,boosting算法会对其设置较小的权重,而偏差较小的学习器则会设置较大的权重,此时集成得到的学习器会具有较小的偏差。boosting算法目前最流行的分为两种:Adaptive Boosting(自适应提升) 和 Gradient Boosting(梯度提升) 以及GBDT的变体Xgboost、LightGBM以及Catboost。

  • (1) Adaboost
      Adaboost是一种二分类模型(-1、1类),算法的基本流程如下:
    在这里插入图片描述
    在这里插入图片描述
    【注】:由于Adaboost算法采用指数损失函数,其仅可以处理二分类问题(当然,可通过一对一、一对多等方式处理为多分类);且Adaboost算法对于异常值比较敏感

  • (2) GBDT
      
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • (3)XgBoost
    XgBoost参数介绍
    XgBoost参数调优方法
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    XgBoost算法的使用:

from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score   # 准确率
# 加载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565) # 数据集分割
# 算法参数
params = {
    'booster': 'gbtree',
    'objective': 'multi:softmax',
    'eval_metric':'mlogloss',
    'num_class': 3,
    'gamma': 0.1,
    'max_depth': 6,
    'lambda': 2,
    'subsample': 0.7,
    'colsample_bytree': 0.75,
    'min_child_weight': 3,
    'eta': 0.1,
    'seed': 1,
    'nthread': 4,
}

# plst = params.items()

train_data = xgb.DMatrix(X_train, y_train) # 生成数据集格式
num_rounds = 500
model = xgb.train(params, train_data) # xgboost模型训练

# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)

# 计算准确率
F1_score = f1_score(y_test,y_pred,average='macro')
print("F1_score: %.2f%%" % (F1_score*100.0))

# 显示重要特征
plot_importance(model)
plt.show()

  • (4)LGBM:[可参见DataWhale的文章]
  • 在这里插入图片描述
    (https://mp.weixin.qq.com/s/64xfT9WIgF3yEExpSxyshQ)
import lightgbm as lgb
from sklearn import datasets
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn.metrics import roc_auc_score, accuracy_score
import matplotlib.pyplot as plt

# 加载数据
iris = datasets.load_iris()
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3)
# 转换为Dataset数据格式
train_data = lgb.Dataset(X_train, label=y_train)
validation_data = lgb.Dataset(X_test, label=y_test)
# 参数
results = {}
params = {
    'learning_rate': 0.1,
    'lambda_l1': 0.1,
    'lambda_l2': 0.9,
    'max_depth': 1,
    'objective': 'multiclass',  # 目标函数
    'num_class': 3,
    'verbose': -1 
}
# 模型训练
gbm = lgb.train(params, train_data, valid_sets=(validation_data,train_data),valid_names=('validate','train'),evals_result= results)
# 模型预测
y_pred_test = gbm.predict(X_test)
y_pred_data = gbm.predict(X_train)
y_pred_data = [list(x).index(max(x)) for x in y_pred_data]
y_pred_test = [list(x).index(max(x)) for x in y_pred_test]
# 模型评估
print(accuracy_score(y_test, y_pred_test))
print('训练集',f1_score(y_train, y_pred_data,average='macro'))
print('验证集',f1_score(y_test, y_pred_test,average='macro'))
# 有以下曲线可知验证集的损失是比训练集的损失要高,所以模型可以判断模型出现了过拟合
lgb.plot_metric(results)
plt.show()
#因此可以尝试将lambda_l2设置为0.9
lgb.plot_metric(results)
plt.show()
# 绘制重要的特征
lgb.plot_importance(gbm,importance_type = "split")
plt.show()
  • (5)Catboost
    【注】:一般而言,boosting算法的分类精度高于bagging算法

2 参数交叉验证

  • (a) 简单交叉验证
    简单交叉验证是讲原始数据分为两组,一组作为训练集,另外一组作为验证集,利用训练集训练模型,然后利用验证集度以模型进行验证,将最后的分类准确率作为此分类器的性能指标.通常是划分30%的数据作为测试数据
from sklearn.model_selection import train_test_split
from sklearn import datasets
#数据集导入
iris=datasets.load_iris()
feature=iris.feature_names
X = iris.data
y = iris.target
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.4,random_state=0)
  • (b) K折交叉验证
      将原始数据分为K组,然后将每个子集数据分别做一次验证集,其余的K-1组子集作为训练集,这样就会得到K个模型,将K个模型最终的验证集的分类准确率取平均值,作为K折交叉验证分类器的性能指标。通常设置为K为5或者10.
from sklearn.model_selection import KFold,StratifiedKFold
KF= KFold(n_splits=10, shuffle=True)
SKF= StratifiedKFold(n_splits=10, shuffle=True)
for train_index , test_index in KF.split(X):
  ...

StratifiedKFold:是K-fold的变种,会返回stratified(分层)的折叠:每个小集合中的各个类别的样本比例大致和完整数据集相同。
StratifiedShuffleSplit:是ShuffleSplit的一种变种,会返回直接的划分,比如创建一个划分,但是划分中的每个类的比例和完整数据集中的相同。

  • (c ) 留一法交叉验证
      留一法交叉验证是指每个训练集由除一个样本之外的其余样本组成,留下的一个样本组成检验集。这样对于N个样本的数据集,可以组成N个不同的训练集和N个不同的验证集,因此该方法会得到N个模型,用N个模型最终的验证集的分类准确率的平均是作为分类器的性能指标
from sklearn.model_selection import LeaveOneOut
loo=LeaveOneOut()
  • (d)留P法交叉验证
      该方法与留一法类似,是从完整数据集中删除P个样本,产生所有可能的训练集和验证集。
from sklearn.model_selection import LeavePOut
lpo=LeavePOut(p=5)

3 模型调参

  • (1) 网格调参
      网格搜索就是一种穷举搜索,在所有候选的参数选择中通过循环遍历去在所有候选参数中寻找表现最好的结果。
#以Xgboost为例,该网格搜索代码示例如下
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import GridSearchCV

cancer = load_breast_cancer()
x = cancer.data[:50]
y = cancer.target[:50]
train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0)  # 分训练集和验证集
# 这里不需要Dmatrix

parameters = {
    'max_depth': [5, 10, 15, 20, 25],
    'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15],
    'n_estimators': [50, 100, 200, 300, 500],
    'min_child_weight': [0, 2, 5, 10, 20],
    'max_delta_step': [0, 0.2, 0.6, 1, 2],
    'subsample': [0.6, 0.7, 0.8, 0.85, 0.95],
    'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9],
    'reg_alpha': [0, 0.25, 0.5, 0.75, 1],
    'reg_lambda': [0.2, 0.4, 0.6, 0.8, 1],
    'scale_pos_weight': [0.2, 0.4, 0.6, 0.8, 1]
}

xlf = xgb.XGBClassifier(max_depth=10,
                        learning_rate=0.01,
                        n_estimators=2000,
                        silent=True,
                        objective='binary:logistic',
                        nthread=-1,
                        gamma=0,
                        min_child_weight=1,
                        max_delta_step=0,
                        subsample=0.85,
                        colsample_bytree=0.7,
                        colsample_bylevel=1,
                        reg_alpha=0,
                        reg_lambda=1,
                        scale_pos_weight=1,
                        seed=1440,
                        missing=None)

# 有了gridsearch我们便不需要fit函数
gsearch = GridSearchCV(xlf, param_grid=parameters, scoring='accuracy', cv=3)
gsearch.fit(train_x, train_y)

print("Best score: %0.3f" % gsearch.best_score_)
print("Best parameters set:")
best_parameters = gsearch.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
    print("\t%s: %r" % (param_name, best_parameters[param_name]))
#极其耗费时间,电脑没执行完
  • (2)贝叶斯调参
##############超参数优化的超参域###################
spaceParam = {
    'boosting': hp.choice('boosting',['gbdt','dart']),
    'learning_rate':hp.loguniform('learning_rate', np.log(0.01), np.log(0.05)),
    'num_leaves': hp.quniform('num_leaves', 3, 66, 3), 
    'feature_fraction': hp.uniform('feature_fraction', 0.7,1),
    'min_data_in_leaf': hp.quniform('min_data_in_leaf', 10, 50,5), 
    'num_boost_round':hp.quniform('num_boost_round',500,2000,100), 
    'bagging_fraction':hp.uniform('bagging_fraction',0.6,1)  
}
# 超参数优化 ---------------------------------------------------------------------------------
def getParam(param):
    for k in ['num_leaves', 'min_data_in_leaf','num_boost_round']:
        param[k] = int(float(param[k]))
    for k in ['learning_rate', 'feature_fraction','bagging_fraction']:
        param[k] = float(param[k])
    if param['boosting'] == 0:
        param['boosting'] = 'gbdt'
    elif param['boosting'] == 1:
        param['boosting'] = 'dart'
    # 添加固定参数
    param['objective'] = 'multiclass'
    param['max_depth'] = 7
    param['num_threads'] = 8
    param['is_unbalance'] = True
    param['metric'] = 'None'
    param['train_metric'] = True
    param['verbose'] = -1
    param['bagging_freq']=5
    param['num_class']=3 
    param['feature_pre_filter']=False
    return param
def f1_score_eval(preds, valid_df):
    labels = valid_df.get_label()
    preds = np.argmax(preds.reshape(3, -1), axis=0)
    scores = f1_score(y_true=labels, y_pred=preds, average='macro')
    return 'f1_score', scores, True
def lossFun(param):
    param = getParam(param)
    m = lgb.train(params=param,train_set=train_data,num_boost_round=param['num_boost_round'],
                          valid_sets=[train_data,valid_data],valid_names=['train','valid'],
                          feature_name=features,feval=f1_score_eval,
                          early_stopping_rounds=earlyStopping,verbose_eval=False,keep_training_booster=True)
    train_f1_score = m.best_score['train']['f1_score']
    valid_f1_score = m.best_score['valid']['f1_score']
    loss_f1_score = 1 - valid_f1_score
    print('训练集f1_score:{},测试集f1_score:{},loss_f1_score:{}'.format(train_f1_score, valid_f1_score, loss_f1_score))
    return {'loss': loss_f1_score, 'params': param, 'status': STATUS_OK}

features = model_feature
train_data = lgb.Dataset(data=X_train[model_feature],label=y_train,feature_name=features)
valid_data = lgb.Dataset(data=X_verify[features],label=y_verify,reference=train_data,feature_name=features)

best_param = fmin(fn=lossFun, space=spaceParam, algo=tpe.suggest, max_evals=100, trials=Trials())
best_param = getParam(best_param)
print('Search best param:',best_param)
# 对模型进行交叉验证调参
# Lightgbm调参:https://blog.csdn.net/sanjianjixiang/article/details/104528478/
# https://www.imooc.com/article/43784
from bayes_opt import BayesianOptimization
def LGB_CV(num_leaves,
           feature_fraction,
           bagging_fraction,
           bagging_freq,
           learning_rate):
    seed = 2000
    folds = KFold(n_splits=5, shuffle=True, random_state=15)
    onehot_encoder = OneHotEncoder(sparse=False)
    folds_scores = []
    for fold_, (train_idx, val_idx) in enumerate(folds.split(x_train, y_train)):
        print("fold {}".format(fold_+1))
        train_data = lgb.Dataset(x_train.iloc[train_idx],label=y_train[train_idx],)
        val_data = lgb.Dataset(x_train.iloc[val_idx],label=y_train[val_idx],)

        params = {
            'boosting_type': 'gbdt',
            'objective': 'multiclass',
            'num_class': 4,
            'num_leaves': int(round(num_leaves)),
            'feature_fraction': feature_fraction,
            'bagging_fraction': bagging_fraction,
            'bagging_freq':int(round(bagging_freq)),
            'learning_rate': learning_rate,
            'seed': seed,
            'nthread': 28,
            'n_jobs': 24,
            'verbose': -1,
        }

        clf = lgb.train(params,train_data, 1000,
                        valid_sets=[train_data, val_data],
                        verbose_eval=500,early_stopping_rounds=200)

        y_pred = clf.predict(x_train.iloc[val_idx],num_iteration=clf.best_iteration)

        val_y = np.array(y_train.iloc[val_idx]).reshape(-1,1)
        val_y = onehot_encoder.fit_transform(val_y)

        score = abs_sum(y_pred,val_y)
        folds_scores.append(score)
        del clf,train_idx,val_idx

    return -np.mean(folds_scores)

LGB_BO = BayesianOptimization(LGB_CV, {'num_leaves': (5,130),
                                       'feature_fraction': (0.7,1.0),
                                       'bagging_fraction': (0.7,1.0),
                                       'bagging_freq':(1,10),
                                       'learning_rate':(0.001,0.2)})
print(LGB_BO.maximize(init_points=5,n_iter=95))
best_params = LGB_BO.max['params']
print(f'Best parameters:{best_params}')
  • (3) 学习曲线
      学习曲线是在训练集大小不同的时候通过绘制模型在训练集和测试集上的准确率来观察模型在新数据上的表现,进而判断模型是高偏差还是高方差,以及增大训练集是否会减小过拟合
      1、当训练集和测试集的误差收敛但却很高时,为高偏差。偏差很高,训练集和验证集的准确率都很低,很可能是欠拟合。我们可以增加模型参数,比如,构建更多的特征,减小正则项。此时通过增加数据量是不起作用的。
      2、当训练集和测试集的误差之间有大的差距时,为高方差。当训练集的准确率比其他独立数据集上的测试结果的准确率要高时,一般都是过拟合。右上角方差很高,训练集和验证集的准确率相差太多,应该是过拟合。我们可以增大训练集,降低模型复杂度,增大正则项,或者通过特征选择减少特征数。理想情况是是找到偏差和方差都很小的情况,即收敛且误差较小。
      3、验证曲线 和学习曲线不同,验证曲线的横轴为某个超参数的一系列值,由此比较不同超参数设置下的模型准确值。从下图的验证曲线可以看到,随着超参数设置的改变,模型可能会有从欠拟合到合适再到过拟合的过程,进而可以选择一个合适的超参数设置来提高模型的性能。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值