写点什么

手把手带你打一场时间序列比赛—优化

作者:打工人!
  • 2023-10-27
    江苏
  • 本文字数:4622 字

    阅读完需:约 15 分钟

手把手带你打一场时间序列比赛—优化

进阶优化:

在进阶实践部分,将在原有 Baseline 基础上做更多优化,一般优化思路,从特征工程与模型中来思考。

优化方法建议:

  1. 提取更多特征:在数据挖掘比赛中,特征总是最终制胜法宝,去思考什么信息可以帮助我们提高预测精准度,然后将其转化为特征输入到模型。

  2. 尝试不同的模型:模型间存在很大的差异,预测结果也会不一样,比赛的过程就是不断的实验和试错的过程,通过不断的实验寻找最佳模型,同时帮助自身加强模型的理解能力。

特征优化:

这里主要构建了历史平移特征、差分特征、和窗口统计特征;每种特征都是有理可据的,具体说明如下:

(1)历史平移特征:通过历史平移获取上个阶段的信息;

(2)差分特征:可以帮助获取相邻阶段的增长差异,描述数据的涨减变化情况。在此基础上还可以构建相邻数据比值变化、二阶差分等;

(3)窗口统计特征:窗口统计可以构建不同的窗口大小,然后基于窗口范围进统计均值、最大值、最小值、中位数、方差的信息,可以反映最近阶段数据的变化情况。

参考代码如下:

# 合并训练数据和测试数据df = pd.concat([train_df, test_df], axis=0).reset_index(drop=True)
# 历史平移for i in range(7,36): df[f'power_shift{i}'] = df.groupby('id_encode')['power'].shift(i)
# 历史平移 + 差分特征for i in range(1,4): df[f'power_shift7_diff{i}'] = df.groupby('id_encode')['power_shift7'].diff(i) # 窗口统计for win in [7,14,28,35,50,70]: df[f'power_win{win}_mean'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').mean().values df[f'power_win{win}_max'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').max().values df[f'power_win{win}_min'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').min().values df[f'power_win{win}_std'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').std().values
# 历史平移 + 窗口统计for win in [7,14,28,35,50,70]: df[f'power_shift7_win{win}_mean'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').mean().values df[f'power_shift7_win{win}_max'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').max().values df[f'power_shift7_win{win}_min'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').min().values df[f'power_shift7_win{win}_sum'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').sum().values df[f'power_shift7_win{win}_std'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').std().values
复制代码

模型融合:

进行模型融合的前提是有多个模型的输出结果,比如使用 catboost、xgboost 和 lightgbm 三个模型分别输出三个结果,这时就可以将三个结果进行融合,最常见的是将结果直接进行加权平均融合。

下面我们构建了 cv_model 函数,内部可以选择使用 lightgbm、xgboost 和 catboost 模型,可以依次跑完这三个模型,然后将三个模型的结果进行取平均进行融合。

def cv_model(clf, train_x, train_y, test_x, clf_name, seed = 2023):    '''    clf:调用模型    train_x:训练数据    train_y:训练数据对应标签    test_x:测试数据    clf_name:选择使用模型名    seed:随机种子    '''    folds = 5    kf = KFold(n_splits=folds, shuffle=True, random_state=seed)    oof = np.zeros(train_x.shape[0])    test_predict = np.zeros(test_x.shape[0])    cv_scores = []        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]                if clf_name == "lgb":            train_matrix = clf.Dataset(trn_x, label=trn_y)            valid_matrix = clf.Dataset(val_x, label=val_y)            params = {                'boosting_type': 'gbdt',                'objective': 'regression',                'metric': 'mae',                'min_child_weight': 6,                'num_leaves': 2 ** 6,                'lambda_l2': 10,                'feature_fraction': 0.8,                'bagging_fraction': 0.8,                'bagging_freq': 4,                'learning_rate': 0.1,                'seed': 2023,                'nthread' : 16,                'verbose' : -1,            }            model = clf.train(params, train_matrix, 2000, valid_sets=[train_matrix, valid_matrix],                              categorical_feature=[], verbose_eval=200, early_stopping_rounds=100)            val_pred = model.predict(val_x, num_iteration=model.best_iteration)            test_pred = model.predict(test_x, num_iteration=model.best_iteration)                if clf_name == "xgb":            xgb_params = {              'booster': 'gbtree',               'objective': 'reg:squarederror',              'eval_metric': 'mae',              'max_depth': 5,              'lambda': 10,              'subsample': 0.7,              'colsample_bytree': 0.7,              'colsample_bylevel': 0.7,              'eta': 0.1,              'tree_method': 'hist',              'seed': 520,              'nthread': 16              }            train_matrix = clf.DMatrix(trn_x , label=trn_y)            valid_matrix = clf.DMatrix(val_x , label=val_y)            test_matrix = clf.DMatrix(test_x)                        watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]                        model = clf.train(xgb_params, train_matrix, num_boost_round=2000, evals=watchlist, verbose_eval=200, early_stopping_rounds=100)            val_pred  = model.predict(valid_matrix)            test_pred = model.predict(test_matrix)                    if clf_name == "cat":            params = {'learning_rate': 0.1, 'depth': 5, 'bootstrap_type':'Bernoulli','random_seed':2023,                      'od_type': 'Iter', 'od_wait': 100, 'random_seed': 11, 'allow_writing_files': False}                        model = clf(iterations=2000, **params)            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),                      metric_period=200,                      use_best_model=True,                       cat_features=[],                      verbose=1)                        val_pred  = model.predict(val_x)            test_pred = model.predict(test_x)                oof[valid_index] = val_pred        test_predict += test_pred / kf.n_splits                score = mean_absolute_error(val_y, val_pred)        cv_scores.append(score)        print(cv_scores)            return oof, test_predict
# 选择lightgbm模型lgb_oof, lgb_test = cv_model(lgb, train_df[cols], train_df['power'], test_df[cols], 'lgb')# 选择xgboost模型xgb_oof, xgb_test = cv_model(xgb, train_df[cols], train_df['power'], test_df[cols], 'xgb')# 选择catboost模型cat_oof, cat_test = cv_model(CatBoostRegressor, train_df[cols], train_df['power'], test_df[cols], 'cat')
# 进行取平均融合final_test = (lgb_test + xgb_test + cat_test) / 3
复制代码

另外一种就是 stacking 融合,stacking 是一种分层模型集成框架。以两层为例,第一层由多个基学习器组成,其输入为原始训练集,第二层的模型则是以第一层基学习器的输出作为特征加入训练集进行再训练,从而得到完整的 stacking 模型。

第一层:(类比 cv_model 函数)

  1. 划分训练数据为 K 折(5 折为例,每次选择其中四份作为训练集,一份作为验证集);

  2. 针对各个模型 RF、ET、GBDT、XGB,分别进行 5 次训练,每次训练保留一份样本用作训练时的验证,训练完成后分别对 Validation set,Test set 进行预测,对于 Test set 一个模型会对应 5 个预测结果,将这 5 个结果取平均;对于 Validation set 一个模型经过 5 次交叉验证后,所有验证集数据都含有一个标签。此步骤结束后:5 个验证集(总数相当于训练集全部)在每个模型下分别有一个预测标签,每行数据共有 4 个标签(4 个算法模型),测试集每行数据也拥有四个标签(4 个模型分别预测得到的)

第二层:(类比 stack_model 函数)

  1. 将训练集中的四个标签外加真实标签当作五列新的特征作为新的训练集,选取一个训练模型,根据新的训练集进行训练,然后应用测试集的四个标签组成的测试集进行预测作为最终的 result。

Stacking 参考代码:

def stack_model(oof_1, oof_2, oof_3, predictions_1, predictions_2, predictions_3, y):    '''    输入的oof_1, oof_2, oof_3可以对应lgb_oof,xgb_oof,cat_oof    predictions_1, predictions_2, predictions_3对应lgb_test,xgb_test,cat_test    '''    train_stack = pd.concat([oof_1, oof_2, oof_3], axis=1)    test_stack = pd.concat([predictions_1, predictions_2, predictions_3], axis=1)        oof = np.zeros((train_stack.shape[0],))    predictions = np.zeros((test_stack.shape[0],))    scores = []        from sklearn.model_selection import RepeatedKFold    folds = RepeatedKFold(n_splits=5, n_repeats=2, random_state=2021)        for fold_, (trn_idx, val_idx) in enumerate(folds.split(train_stack, train_stack)):         print("fold n°{}".format(fold_+1))        trn_data, trn_y = train_stack.loc[trn_idx], y[trn_idx]        val_data, val_y = train_stack.loc[val_idx], y[val_idx]                clf = Ridge(random_state=2021)        clf.fit(trn_data, trn_y)
oof[val_idx] = clf.predict(val_data) predictions += clf.predict(test_stack) / (5 * 2) score_single = mean_absolute_error(val_y, oof[val_idx]) scores.append(score_single) print(f'{fold_+1}/{5}', score_single) print('mean: ',np.mean(scores)) return oof, predictions stack_oof, stack_pred = stack_model(lgb_oof, xgb_oof, cat_oof, lgb_test, xgb_test, cat_test, traindata['age'])
复制代码


用户头像

打工人!

关注

智慧交通扫地僧 2019-11-10 加入

2021年InfoQ年度最佳内容获得者。 InfoQ签约作者 会点深度强化学习算法; 会点融合跟踪; 喜欢用手机拍拍拍;

评论

发布
暂无评论
手把手带你打一场时间序列比赛—优化_机器学习_打工人!_InfoQ写作社区