Datawhale AI夏令营 task3笔记

前面的task2已经学习了历史平移特征窗口统计特征,在task3中,将会学习和实现特征优化,并尝试使用深度学习方法

1.特征优化

这里通过创建各种历史特征(如l历史平移特征、差分特征、窗口统计特征等)来增强机器学习模型的表现
代码如下:

​

# 历史平移  
for i in range(10, 36):  
    data[f'target_shift{i}'] = data.groupby('id')['target'].shift(i)  
  
# 历史平移 + 差分特征  
for i in range(1, 4):  
    data[f'target_shift10_diff{i}'] = data.groupby('id')['target_shift10'].diff(i)  
  
# 窗口统计  
for win in [15, 30, 50, 70]:  
    data[f'target_win{win}_mean'] = data.groupby('id')['target'].rolling(window=win, min_periods=3,  
                                                                         closed='left').mean().values  
    data[f'target_win{win}_max'] = data.groupby('id')['target'].rolling(window=win, min_periods=3,  
                                                                        closed='left').max().values  
    data[f'target_win{win}_min'] = data.groupby('id')['target'].rolling(window=win, min_periods=3,  
                                                                        closed='left').min().values  
    data[f'target_win{win}_std'] = data.groupby('id')['target'].rolling(window=win, min_periods=3,  
                                                                        closed='left').std().values  
  
# 历史平移 + 窗口统计  
for win in [7, 14, 28, 35, 50, 70]:  
    data[f'target_shift10_win{win}_mean'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3,  
                                                                                         closed='left').mean().values  
    data[f'target_shift10_win{win}_max'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3,  
                                                                                        closed='left').max().values  
    data[f'target_shift10_win{win}_min'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3,  
                                                                                        closed='left').min().values  
    data[f'target_shift10_win{win}_sum'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3,  
                                                                                        closed='left').sum().values  
    data[f'target_shift710win{win}_std'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3,  
                                                                                        closed='left').std().values

[点击并拖拽以移动]
​
  1. 历史平移
    首先这里创建了一系列的历史平移特征,范围从10到35。然后根据每个 id 分类,将 target 列的数值向下移动 i 个单位,这样的特征通常用于捕捉历史信息
  2. 历史平移 + 差分特征
    创建一个基于 target_shift10 的差分特征的循环,范围是1到3,计算每个 id 下 target_shift10相邻值之间的差异,可以捕捉到变化的幅度
  3. 窗口统计
    这里是对于 target 列计算了不同窗口大小(15, 30, 50, 70)的统计特征,即计算了每个窗口的均值、最大值、最小值和标准差
  4. 历史平移 + 窗口统计
    这一部分的逻辑与上一个部分相似,但针对的是刚才创建的 target_shift10特征,计算了窗口大小为7, 14, 28, 35, 50, 70的均值、最大值、最小值、和标准差,这些特征帮助捕捉到了基于历史平移的数据的统计特性

2.模型融合

首先,要进行模型融合,其前提是有多个模型的输出结果,最简单的融合是将结果进行加权平均融合
代码如下:

​

def cv_model(clf, train_x, train_y, test_x, clf_name, seed=2024):  
    '''  
    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 ** 5,  
                'lambda_l2': 10,  
                'feature_fraction': 0.7,  
                'bagging_fraction': 0.7,  
                'bagging_freq': 4,  
                'learning_rate': 0.1,  
                'seed': 2023,  
                'nthread': 16,  
                'verbose': -1,  
            }  
            model = clf.train(params, train_matrix, 1000, 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.6,  
                'colsample_bytree': 0.6,  
                'colsample_bylevel': 0.6,  
                '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=1000, 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,  'allow_writing_files': False}  
  
            model = clf(iterations=1000, **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[train_cols], train['target'], test[train_cols], 'lgb')  
# 选择xgboost模型  
xgb_oof, xgb_test = cv_model(xgb, train[train_cols], train['target'], test[train_cols], 'xgb')  
# 选择catboost模型  
cat_oof, cat_test = cv_model(CatBoostRegressor, train[train_cols], train['target'], test[train_cols], 'cat')  
  
# 进行取平均融合  
final_test = (lgb_test + xgb_test + cat_test) / 3

[点击并拖拽以移动]
​

在上面的代码中,定义了一个名为 cv_model 的函数,用于进行交叉验证模型训练和预测

1.初始化交叉验证参数

  • folds = 5指定进行 5 折交叉验证。
  • kf = KFold(n_splits=folds, shuffle=True, random_state=seed) 创建 KFold 实例,用于将数据分成 5 个折。
  • ooftest_predict:初始化为零数组,oof 用于存储每个样本的预测值,test_predict 用于存储测试集的预测平均值。
  • cv_scores:用于存储每个折的交叉验证得分。

2.交叉验证循环

  • 使用 kf.split(train_x, train_y) 生成训练集和验证集的索引,并进行循环。
  • 在每次迭代中,输出当前折的序号。
  • 根据索引 split 数据集,得到训练集(trn_x trn_y)和验证集(val_x val_y

3.模型训练及预测

  • LightGBM
  • 创建 LightGBM 的数据矩阵,设定各种超参数。
  • 使用 clf.train 进行模型训练,并利用验证集进行早停。
  • 预测验证集和测试集。
  • XGBoost
  • 创建 DMatrix 对象,设定超参数。
  • 使用 clf.train 进行模型训练,并应用早停。
  • 预测验证集和测试集。
  • CatBoost
  • 设置 CatBoost 的超参数,并调用 fit 方法进行模型训练。
  • 预测验证集和测试集。

4.记录结果

  • 将每个折的验证集预测值存入 oof
  • 将测试集预测值累加到 test_predict,并做归一化处理。
  • 计算当前折的平均绝对误差(MAE),并加入 cv_scores 列表。

最后是取平均融合

另一种融合是stacking融合,Stacking结构中有两层算法串联,第一层为level 0,第二层为level 1,level 0里面可能包含1个或多个强学习器,而level 1只能包含一个学习器。在训练中,数据会先被输入level 0进行训练,训练完毕后,level 0中的每个算法会输出相应的预测结果。我们将这些预测结果拼凑成新特征矩阵,再输入level 1的算法进行训练,那么融合模型最终输出的预测结果即为level 1的学习器输出的结果

代码如下:

​

from sklearn.linear_model import Ridge  
  
  
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)  
    #使用pd.concat函数将三个模型的oof和test结果按列合并,形成两个新的DataFrame:train_stack和test_stack  
  
    oof = np.zeros((train_stack.shape[0],))  
    predictions = np.zeros((test_stack.shape[0],))  
    scores = []  
    #oof和predictions被初始化为零数组,用于存放模型在验证集和测试集上的最终预测结果  
    #scores用于记录每一折的模型性能(例如平均绝对误差)  
  
    from sklearn.model_selection import RepeatedKFold  
    folds = RepeatedKFold(n_splits=5, n_repeats=2, random_state=2021)  
    #使用RepeatedKFold进行重复的K折交叉验证,这里设置为5折交叉验证,重复2次,以增强模型的鲁棒性  
  
    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)#使用L2正则化回归模型Ridge拟合训练数据和目标值  
  
        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)  
        '''  
        在验证集上进行预测并保存到oof中  
        对测试集进行预测并累加到predictions中,除以折数和重复次数以获得合适的平均值  
        计算当前折的性能分数(这里使用平均绝对误差)并保存  
        '''        print(f'{fold_ + 1}/{5}', score_single)  
    print('mean: ', np.mean(scores))  
  
    return oof, predictions  
  
  
stack_oof, stack_pred = stack_model(pd.DataFrame(lgb_oof), pd.DataFrame(xgb_oof), pd.DataFrame(cat_oof),  
                                    pd.DataFrame(lgb_test), pd.DataFrame(xgb_test), pd.DataFrame(cat_test),  
                                    train['target'])  
  
# 保存结果文件到本地  
test['target'] = stack_pred  
test[['id', 'dt', 'target']].to_csv('submit.csv', index=None)

[点击并拖拽以移动]
​

Stacking能够捕捉到基础模型之间的复杂关系和互补性,从而提升模型的整体性能,能通过在元模型中学习如何将基础模型的预测结果组合起来,可以减少模型的偏差和方差,从而提高预测准确性,也可以组合不同类型的模型,以充分利用它们各自的优势

Stacking更适合于需要提高模型表现和捕捉复杂模型关系的场景,它能利用模型之间的互补性,通常能得到更好的性能,但计算和实现复杂

加权平均融合更适合于希望简单实现模型融合的场景,它计算开销小,易于实现,但需要选择合适的权重,并且效果高度依赖于基础模型的性能

在这里Stacking融合比加权平均融合更适用

3.深度学习方案尝试

具体代码:

​

import numpy as np  
import pandas as pd  
from sklearn.preprocessing import MinMaxScaler  
from keras.models import Sequential  
from keras.layers import LSTM, Dense, RepeatVector, TimeDistributed, Dropout  
from keras.optimizers import Adam  
from keras.callbacks import EarlyStopping  
  
train = pd.read_csv('./data/data283931/train.csv')  
test = pd.read_csv('./data/data283931/test.csv')  
  
  
# 数据预处理  
def preprocess_data(df, look_back=100):  
    # 将数据按照id进行分组  
    grouped = df.groupby('id')  
    datasets = {}  
    for id, group in grouped:  
        datasets[id] = group.values  
  
    # 准备训练数据集  
    X, Y = [], []  
    for id, data in datasets.items():  
        for i in range(10, 15):  # 每个id构建5个序列  
            a = data[i:(i + look_back), 3]  
            a = np.append(a, np.array([0] * (100 - len(a))))  
            X.append(a[::-1])  
            Y.append(data[i - 10:i, 3][::-1])  
  
    # 准备测试数据集  
    OOT = []  
    for id, data in datasets.items():  
        a = data[:100, 3]  
        a = np.append(a, np.array([0] * (100 - len(a))))  
        OOT.append(a[::-1])  
  
    return np.array(X, dtype=np.float64), np.array(Y, dtype=np.float64), np.array(OOT, dtype=np.float64)  
  
  
# 定义模型  
def build_model(look_back, n_features, n_output):  
    model = Sequential()  
    model.add(LSTM(100, input_shape=(look_back, n_features)))  
    model.add(Dropout(0.2))  
    model.add(RepeatVector(n_output))  
    model.add(LSTM(100, return_sequences=True))  
    model.add(TimeDistributed(Dense(1)))  
    model.compile(loss='mean_squared_error', optimizer=Adam(0.001))  
    return model  
  
  
# 构建和训练模型  
look_back = 100  # 序列长度  
n_features = 1  # 假设每个时间点只有一个特征  
n_output = 10  # 预测未来10个时间单位的值  
  
# 预处理数据  
X, Y, OOT = preprocess_data(train, look_back=look_back)  
  
# 构建模型  
model = build_model(look_back, n_features, n_output)  
  
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)  
# 训练模型  
model.fit(X, Y, epochs=50, batch_size=64, verbose=1, validation_split=0.2, callbacks=[early_stopping])  
  
# 进行预测  
predicted_values = model.predict(OOT)  
  
# 准备符合要求格式的预测结果  
predictions = []  
for i, data in enumerate(OOT):  
    id = train['id'].unique()[i]  
    for j in range(n_output):  
        predictions.append([id, j + 1, predicted_values[i, j, 0]])  
  
# 将预测结果转换为DataFrame  
df_predictions = pd.DataFrame(predictions, columns=['id', 'dt', 'target'])  
  
# 导出预测结果到CSV文件  
df_predictions.to_csv('predictions.csv', index=False, encoding='utf-8')

[点击并拖拽以移动]
​

1.数据预处理

  • 定义preprocess_data函数,用于对输入的DataFrame进行预处理,look_back是序列的长度(即输入序列的时间步数)
  • 根据id对数据进行分组,并将每个组的数据存入datasets
  • 对于每个id 根据索引构建输入序列X和目标序列Y,从索引10到索引14生成5个序列,序列长度被设置为look_back(100),如果序列长度不足100,会用0填充,目标序列是从当前时间点向前提取10个时间步的数据
  • 生成测试集OOT,每个id的前100个时间步被收集并进行填充和翻转

2.定义LSTM模型

定义build_model函数来构建LSTM模型
创建一个顺序模型,添加LSTM层,用Dropout层来防止过拟合。使用RepeatVector将输入格式转换为符合下一个LSTM层需要的格式,并添加一个时间分布的Dense层以输出结果

3.模型训练

设置参数并调用预处理函数处理训练数据。构建模型后,利用fit方法训练模型,此处使用了早停机制,以防止过拟合

4.预测

使用训练好的模型对测试集OOT进行预测

总结

本次任务实现了特征优化、模型融合,还尝试了深度学习方案,对于机器学习有了更深的了解。不过对于深度学习方案,我认为还有可优化空间,可以利用深度学习方案得到更好的分数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值