尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

时间序列分类实战:从特征工程到模型集成的人类活动识别指南

时间序列分类实战:从特征工程到模型集成的人类活动识别指南 1. 项目概述从赛题到实战的完整拆解“对人类活动进行分类”这个题目听起来是不是既熟悉又有点无从下手熟悉是因为分类问题在机器学习里简直是“Hello World”级别的存在无从下手则是因为当它和“人类活动”、“认证杯小美赛”这样的竞赛背景结合时问题就变得立体和复杂了。2022年认证杯C题本质上是一个典型的时间序列分类问题但它的魅力在于它完美地模拟了一个从原始数据到最终决策的完整数据分析流程。你不是在简单地调用一个sklearn的模型而是在扮演一个数据科学家需要思考数据从何而来、如何理解、怎样处理、用什么模型、以及如何评估和解释结果。这篇文章我就以这道赛题为蓝本结合我多次带队参赛和实际项目中的经验为你彻底拆解这道题的解题思路、技术选型、代码实现以及那些“教科书上不会写”的避坑指南。无论你是正在备赛的学生还是对时间序列分类感兴趣的开发者相信这篇近万字的深度解析都能让你有所收获。2. 赛题核心与解题框架设计2.1 问题本质与难点剖析拿到“对人类活动进行分类”这个题目第一步不是急着找代码而是精准定义问题边界。题目通常会提供或暗示一组传感器数据比如来自智能手机或穿戴设备的加速度计、陀螺仪数据目标是识别出佩戴者正在进行的具体活动如行走、跑步、上楼梯、坐下等。其核心难点在于时序依赖性人类活动是连续的当前时刻的状态高度依赖于前一刻甚至前几秒的状态。这与图像分类空间依赖性和传统表格数据分类样本独立有本质区别。数据噪声与个体差异传感器数据必然包含噪声且不同人的行走姿态、运动幅度差异很大模型需要有较强的鲁棒性。特征工程的挑战原始加速度信号x, y, z轴信息密度低如何从中提取出能表征不同活动模式的判别性特征是决定模型上限的关键。类别不平衡某些活动如静坐的样本可能远多于其他活动如跳跃需要妥善处理。基于这些难点一个稳健的解题框架应该包含以下几个核心阶段我将其概括为“数据理解-特征工程-模型构建-评估优化”四步闭环。2.2 四步闭环解题框架数据理解与预处理这是所有工作的基石。你需要像法医检查证物一样审视数据数据格式是什么CSV/TXT有哪些字段时间戳、三轴加速度、陀螺仪、可能还有标签是否存在缺失值、异常值传感器坐标轴是如何定义的标签的分布情况如何这个阶段的目标是形成对数据的“直觉”。特征工程与提取这是将原始数据“翻译”成机器学习模型能听懂的语言的过程。对于时间序列我们通常在滑动窗口内进行特征提取。特征可以分为时域特征均值、方差、标准差、均方根、过零率、相关系数如x-y轴相关性等。这些特征计算简单能有效捕捉信号的幅度和基本波动信息。频域特征通过快速傅里叶变换FFT将信号转换到频域提取频谱能量、主频、频谱熵等。这对于区分周期性活动如走路、跑步和非周期性活动非常有效。时频域特征如小波变换系数能同时捕捉时间和频率信息更精细但计算量更大。模型选择与构建特征准备好后就是选择“武器”的时候。对于这类问题主流模型有传统机器学习模型如随机森林、梯度提升树XGBoost/LightGBM。它们对结构化特征即我们提取出的特征表效果很好训练速度快可解释性强是竞赛中的“快枪手”。深度学习模型如卷积神经网络CNN、循环神经网络RNN/LSTM/GRU、以及结合两者的CNN-LSTM混合模型。CNN能自动学习局部模式如一个步态周期内的波形RNN能建模长时序依赖。深度学习模型能力更强但需要更多数据、更长的训练时间和计算资源且可解释性差。模型评估与优化竞赛中不能只看准确率。必须使用分层交叉验证来确保评估的稳健性特别是数据量不大时。关注混淆矩阵分析哪些类别容易被混淆如上楼梯 vs 下楼梯。根据结果你可能需要返回调整特征如增加频域特征、处理类别不平衡如SMOTE过采样、类别权重、或调整模型超参数。注意在竞赛有限的时间内“快”比“完美”更重要。我通常建议采用“传统模型基线 - 特征优化 - 尝试简单深度学习模型”的迭代策略。先用一个强力的随机森林或XGBoost跑出有竞争力的基线分数再考虑是否值得为可能微小的提升投入大量时间调优深度学习模型。3. 数据预处理与特征工程实战3.1 数据加载与初步探索假设我们拿到的是一个包含timestamp,acc_x,acc_y,acc_z,label的CSV文件。第一步永远是先“看”数据。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 加载数据 df pd.read_csv(human_activity_data.csv) print(f数据形状: {df.shape}) print(df.head()) print(df.info()) print(df[label].value_counts()) # 绘制标签分布 plt.figure(figsize(10, 5)) df[label].value_counts().plot(kindbar) plt.title(活动标签分布) plt.xlabel(活动类型) plt.ylabel(样本数) plt.xticks(rotation45) plt.tight_layout() plt.show() # 随机选取一段信号可视化 sample_activity walking sample_data df[df[label]sample_activity].iloc[:500] # 取前500个点 fig, axes plt.subplots(3, 1, figsize(15, 8), sharexTrue) axes[0].plot(sample_data[acc_x], labelX轴) axes[0].set_ylabel(加速度 (g)) axes[0].legend() axes[0].set_title(f{sample_activity} - 加速度信号示例) axes[1].plot(sample_data[acc_y], labelY轴, colororange) axes[1].set_ylabel(加速度 (g)) axes[1].legend() axes[2].plot(sample_data[acc_z], labelZ轴, colorgreen) axes[2].set_ylabel(加速度 (g)) axes[2].set_xlabel(时间戳/样本点) axes[2].legend() plt.tight_layout() plt.show()这段代码帮你快速了解数据全貌有多少条记录、有没有空值、标签是否平衡、原始信号长什么样。标签分布图和信号可视化是形成数据直觉的关键能让你提前预判可能的问题如某个类别样本极少或信号噪声极大。3.2 滑动窗口分割与特征提取原始数据是逐点记录的但单个点没有意义。我们需要将连续的信号切割成固定长度的窗口每个窗口代表一个“活动片段”并为其提取特征。窗口长度如2秒和重叠率如50%是关键参数。def create_windows(data, window_size, overlap_ratio): 将时序数据分割成重叠窗口 :param data: 包含特征和标签的DataFrame :param window_size: 窗口大小样本点数 :param overlap_ratio: 重叠比例如0.5表示50%重叠 :return: 窗口列表每个窗口是特征数组标签的元组 windows [] labels [] step int(window_size * (1 - overlap_ratio)) # 滑动步长 for start in range(0, len(data) - window_size 1, step): end start window_size window_data data.iloc[start:end] # 假设一个窗口内标签相同取众数 window_label window_data[label].mode()[0] # 提取特征列假设除了label和timestamp都是特征 feature_cols [c for c in data.columns if c not in [label, timestamp]] window_features window_data[feature_cols].values windows.append(window_features) labels.append(window_label) return np.array(windows), np.array(labels) # 假设采样频率为50Hz2秒的窗口就是100个点 WINDOW_SIZE 100 # 对应2秒 OVERLAP 0.5 X_windows, y_windows create_windows(df, WINDOW_SIZE, OVERLAP) print(f生成窗口数: {len(X_windows)}) print(f窗口数据形状: {X_windows.shape}) # 应为 (n_windows, window_size, n_features)接下来是重头戏特征提取。我们将为每个窗口的三轴加速度数据计算一组特征。from scipy import stats from scipy.fft import fft from scipy.signal import welch def extract_features(window): 为一个窗口shape: [window_size, n_channels]提取特征。 假设n_channels3对应acc_x, acc_y, acc_z。 features [] axis_names [x, y, z] for i in range(window.shape[1]): # 遍历每个轴 signal window[:, i] # 1. 时域特征 mean np.mean(signal) std np.std(signal) variance np.var(signal) rms np.sqrt(np.mean(signal**2)) # 均方根反映信号能量 # 峰度平坦度和偏度对称性 kurtosis stats.kurtosis(signal) skewness stats.skew(signal) # 过零率Zero Crossing Rate对周期性活动敏感 zcr np.sum(np.diff(np.sign(signal)) ! 0) / len(signal) features.extend([mean, std, variance, rms, kurtosis, skewness, zcr]) # 2. 频域特征基于FFT fft_vals np.abs(fft(signal))[:len(signal)//2] # 取单边谱 spectral_energy np.sum(fft_vals**2) spectral_entropy stats.entropy(fft_vals 1e-10) # 加小量防log(0) # 主频能量最大的频率成分 if len(fft_vals) 0: dominant_freq np.argmax(fft_vals) else: dominant_freq 0 features.extend([spectral_energy, spectral_entropy, dominant_freq]) # 3. 轴间关系特征非常重要 # 信号幅值合加速度通常能滤除方向影响突出活动强度 magnitude np.sqrt(window[:,0]**2 window[:,1]**2 window[:,2]**2) features.append(np.mean(magnitude)) features.append(np.std(magnitude)) # 轴间相关系数 corr_xy np.corrcoef(window[:,0], window[:,1])[0,1] corr_xz np.corrcoef(window[:,0], window[:,2])[0,1] corr_yz np.corrcoef(window[:,1], window[:,2])[0,1] features.extend([corr_xy, corr_xz, corr_yz]) return np.array(features) # 应用到所有窗口 X_features np.array([extract_features(w) for w in X_windows]) print(f特征矩阵形状: {X_features.shape}) # (n_windows, n_features)实操心得特征提取是门艺术。不要盲目堆砌特征这会导致维度灾难和过拟合。我建议先从一个基础特征集如均值、标准差、能量、相关系数开始训练一个基线模型。然后分析特征重要性树模型可以输出保留重要的剔除冗余的。频域特征对区分周期性运动特别有用但计算稍慢。在竞赛中可以尝试用tsfresh库进行自动特征提取但它生成的特征量巨大必须配合特征选择使用。3.3 特征标准化与编码提取的特征通常量纲和范围不同比如均值可能是0.2能量可能是100需要标准化。标签也需要从字符串转换为数字。from sklearn.preprocessing import StandardScaler, LabelEncoder from sklearn.model_selection import train_test_split # 1. 编码标签 label_encoder LabelEncoder() y_encoded label_encoder.fit_transform(y_windows) print(f标签类别: {label_encoder.classes_}) # 2. 划分训练集和测试集注意要分层划分以保持类别比例 X_train, X_test, y_train, y_test train_test_split( X_features, y_encoded, test_size0.2, random_state42, stratifyy_encoded ) # 3. 特征标准化用训练集的均值和方差来标准化训练集和测试集 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意测试集用训练集的参数转换 print(f训练集: {X_train_scaled.shape}, 测试集: {X_test_scaled.shape})为什么测试集要用训练集的参数标准化这是为了模拟真实场景在部署模型时你处理的是全新的、未知的数据你不可能知道它的全局均值和方差。因此必须用训练阶段学到的“标准”来处理新数据保证数据分布的一致性。4. 模型构建、训练与评估4.1 构建随机森林基线模型随机森林是一个非常好的起点它不容易过拟合能处理非线性关系还能给出特征重要性。from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, confusion_matrix, accuracy_score # 初始化模型设置随机种子保证可复现性 rf_model RandomForestClassifier( n_estimators200, # 树的数量适当增加可提升性能但会减慢速度 max_depth15, # 树的最大深度防止过拟合 min_samples_split5, # 内部节点再划分所需最小样本数 min_samples_leaf2, # 叶节点最少样本数 class_weightbalanced, # 自动调整类别权重处理不平衡 random_state42, n_jobs-1 # 使用所有CPU核心并行训练 ) # 训练模型 rf_model.fit(X_train_scaled, y_train) # 在训练集和测试集上预测 y_train_pred rf_model.predict(X_train_scaled) y_test_pred rf_model.predict(X_test_scaled) # 评估 print( 随机森林 - 训练集性能 ) print(f准确率: {accuracy_score(y_train, y_train_pred):.4f}) print(classification_report(y_train, y_train_pred, target_nameslabel_encoder.classes_)) print(\n 随机森林 - 测试集性能 ) print(f准确率: {accuracy_score(y_test, y_test_pred):.4f}) print(classification_report(y_test, y_test_pred, target_nameslabel_encoder.classes_)) # 绘制混淆矩阵 def plot_confusion_matrix(y_true, y_pred, classes): cm confusion_matrix(y_true, y_pred) plt.figure(figsize(10, 8)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclasses, yticklabelsclasses) plt.ylabel(真实标签) plt.xlabel(预测标签) plt.title(混淆矩阵) plt.tight_layout() plt.show() plot_confusion_matrix(y_test, y_test_pred, label_encoder.classes_)分析混淆矩阵这是调优的“地图”。如果发现“上楼梯”和“下楼梯”总是混淆说明你当前的特征集不足以区分它们可能需要引入更能表征方向或发力模式的特征比如特定轴的能量比。4.2 特征重要性分析与优化随机森林训练后我们可以查看哪些特征贡献最大。# 获取特征重要性 importances rf_model.feature_importances_ # 假设我们记录了特征名实际中需要根据extract_features函数生成 # 这里用一个示例你需要替换成你自己的特征名列表 # 例如[x_mean, x_std, ..., magnitude_mean, corr_xy] feature_names [ffeat_{i} for i in range(X_train_scaled.shape[1])] # 排序并可视化 indices np.argsort(importances)[::-1] plt.figure(figsize(12, 6)) plt.title(特征重要性 (Top 20)) plt.bar(range(20), importances[indices[:20]], aligncenter) plt.xticks(range(20), [feature_names[i] for i in indices[:20]], rotation90) plt.tight_layout() plt.show() # 可以尝试只保留重要性高的特征重新训练可能提升模型泛化能力 top_k 30 top_indices indices[:top_k] X_train_top X_train_scaled[:, top_indices] X_test_top X_test_scaled[:, top_indices] # 用筛选后的特征重新训练一个轻量级模型 rf_model_top RandomForestClassifier(n_estimators100, random_state42, n_jobs-1) rf_model_top.fit(X_train_top, y_train) y_test_pred_top rf_model_top.predict(X_test_top) print(f使用Top-{top_k}特征的测试集准确率: {accuracy_score(y_test, y_test_pred_top):.4f})4.3 尝试XGBoost模型XGBoost是竞赛中的“大杀器”通常比随机森林有更好的表现但需要更多的调参。import xgboost as xgb from sklearn.model_selection import GridSearchCV # 将标签转换为0起始XGBoost有时需要 if y_train.min() 0: y_train_xgb y_train - 1 y_test_xgb y_test - 1 else: y_train_xgb y_train y_test_xgb y_test # 创建DMatrixXGBoost的高效数据格式 dtrain xgb.DMatrix(X_train_scaled, labely_train_xgb) dtest xgb.DMatrix(X_test_scaled, labely_test_xgb) # 设置参数 params { objective: multi:softmax, # 多分类 num_class: len(np.unique(y_train_xgb)), max_depth: 6, eta: 0.1, # 学习率 subsample: 0.8, # 每棵树随机采样的样本比例 colsample_bytree: 0.8, # 每棵树随机采样的特征比例 seed: 42, eval_metric: mlogloss # 多分类对数损失 } # 训练 num_rounds 100 evals [(dtrain, train), (dtest, eval)] bst xgb.train(params, dtrain, num_rounds, evalsevals, early_stopping_rounds10, verbose_evalFalse) # 预测 y_test_pred_xgb bst.predict(dtest).astype(int) # 转换回原始标签范围 if y_train.min() 0: y_test_pred_xgb y_test_pred_xgb 1 print(\n XGBoost - 测试集性能 ) print(f准确率: {accuracy_score(y_test, y_test_pred_xgb):.4f}) print(classification_report(y_test, y_test_pred_xgb, target_nameslabel_encoder.classes_))注意事项XGBoost对参数敏感。eta学习率小一些配合更多的num_rounds迭代轮数通常更稳健。max_depth控制模型复杂度太深容易过拟合。务必使用early_stopping_rounds让模型在验证集性能不再提升时自动停止这是防止过拟合的利器。5. 深度学习模型CNN-LSTM的探索当传统模型达到瓶颈或者数据量足够大时可以尝试深度学习模型。这里构建一个经典的CNN-LSTM混合网络CNN用于提取窗口内的局部时空特征LSTM用于捕捉窗口间的长时序依赖。5.1 数据重塑与准备深度学习模型通常需要原始窗口数据而非手工特征作为输入。from tensorflow import keras from tensorflow.keras import layers, models, callbacks # 使用原始的窗口数据 X_windows形状为 (n_windows, window_size, n_channels) # 需要重新划分训练测试集与特征工程时保持一致的分割索引 # 为了简便这里假设我们重新根据索引划分 train_idx, test_idx train_test_split( np.arange(len(X_windows)), test_size0.2, random_state42, stratifyy_encoded ) X_dl_train X_windows[train_idx] y_dl_train y_encoded[train_idx] X_dl_test X_windows[test_idx] y_dl_test y_encoded[test_idx] # 对标签进行one-hot编码因为深度学习分类输出通常用softmax y_dl_train_onehot keras.utils.to_categorical(y_dl_train) y_dl_test_onehot keras.utils.to_categorical(y_dl_test) print(fDL训练集形状: {X_dl_train.shape}, 标签形状: {y_dl_train_onehot.shape})5.2 构建CNN-LSTM混合模型def build_cnn_lstm_model(input_shape, num_classes): 构建一个CNN-LSTM混合模型。 input_shape: (window_size, n_channels) model models.Sequential([ # 第一部分CNN提取局部特征 # 1D卷积沿时间轴滑动每个滤波器学习一种局部模式 layers.Conv1D(filters64, kernel_size3, activationrelu, input_shapeinput_shape, paddingsame), layers.BatchNormalization(), # 加速训练稳定收敛 layers.MaxPooling1D(pool_size2), layers.Conv1D(filters128, kernel_size3, activationrelu, paddingsame), layers.BatchNormalization(), layers.MaxPooling1D(pool_size2), layers.Conv1D(filters256, kernel_size3, activationrelu, paddingsame), layers.BatchNormalization(), layers.MaxPooling1D(pool_size2), # 第二部分LSTM捕捉时序依赖 # 将CNN输出的序列输入到LSTM layers.LSTM(units128, return_sequencesTrue), # 返回所有时间步输出 layers.Dropout(0.3), # 防止过拟合 layers.LSTM(units64), layers.Dropout(0.3), # 第三部分全连接层分类 layers.Dense(units32, activationrelu), layers.Dense(unitsnum_classes, activationsoftmax) ]) model.compile( optimizerkeras.optimizers.Adam(learning_rate0.001), losscategorical_crossentropy, metrics[accuracy] ) return model # 获取输入形状 n_timesteps, n_features X_dl_train.shape[1], X_dl_train.shape[2] num_classes y_dl_train_onehot.shape[1] model build_cnn_lstm_model((n_timesteps, n_features), num_classes) model.summary() # 打印模型结构5.3 训练与回调设置深度学习训练需要耐心和技巧好的回调函数能帮你省力。# 定义回调函数 callbacks_list [ callbacks.EarlyStopping( monitorval_loss, # 监控验证集损失 patience10, # 连续10轮不改善则停止 restore_best_weightsTrue # 恢复最佳权重 ), callbacks.ReduceLROnPlateau( monitorval_loss, factor0.5, # 学习率减半 patience5, # 连续5轮不改善则触发 min_lr1e-6 ), callbacks.ModelCheckpoint( filepathbest_model.h5, monitorval_accuracy, # 监控验证集准确率 save_best_onlyTrue, # 只保存最好的模型 modemax ) ] # 训练模型 history model.fit( X_dl_train, y_dl_train_onehot, validation_split0.1, # 从训练集中再分10%作为验证集 epochs50, # 最大迭代轮数 batch_size32, # 批大小 callbackscallbacks_list, verbose1 ) # 评估最佳模型 test_loss, test_acc model.evaluate(X_dl_test, y_dl_test_onehot, verbose0) print(f\n深度学习模型 - 测试集损失: {test_loss:.4f}, 准确率: {test_acc:.4f}) # 绘制训练历史 fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(history.history[accuracy], label训练准确率) axes[0].plot(history.history[val_accuracy], label验证准确率) axes[0].set_xlabel(Epoch) axes[0].set_ylabel(准确率) axes[0].legend() axes[0].set_title(准确率曲线) axes[1].plot(history.history[loss], label训练损失) axes[1].plot(history.history[val_loss], label验证损失) axes[1].set_xlabel(Epoch) axes[1].set_ylabel(损失) axes[1].legend() axes[1].set_title(损失曲线) plt.tight_layout() plt.show()实操心得深度学习模型是“数据饥渴”型。如果数据量不够大比如少于几千个窗口它的表现很可能不如精心调优的XGBoost。不要盲目追求深度学习。在竞赛中先确保传统模型做到极致。如果使用深度学习务必使用EarlyStopping和ReduceLROnPlateau回调它们能自动防止过拟合和帮助收敛。BatchNormalization层对于稳定训练过程非常有效。6. 模型集成与结果融合单一模型可能在某些类别上表现不佳。集成学习可以融合多个模型的优势提升鲁棒性和最终性能。这里介绍两种简单有效的集成策略。6.1 投票法集成假设我们已经训练好了随机森林模型rf_model、XGBoost模型bst和深度学习模型model。我们可以让它们“投票”决定最终结果。from scipy import stats # 获取各模型在测试集上的预测概率对于深度学习模型或类别对于树模型 # 注意需要确保所有模型的预测结果对齐同一个测试集 # 1. 随机森林预测输出类别 rf_pred rf_model.predict(X_test_scaled) # 2. XGBoost预测需要转换为DMatrix dtest xgb.DMatrix(X_test_scaled) xgb_pred_prob bst.predict(dtest, output_marginFalse) # 输出概率 xgb_pred np.argmax(xgb_pred_prob, axis1) # 注意如果之前调整过标签范围这里可能需要逆转换 # 3. 深度学习模型预测 dl_pred_prob model.predict(X_dl_test) # 输出概率矩阵 dl_pred np.argmax(dl_pred_prob, axis1) # 硬投票取三个模型预测结果的众数 final_hard_vote_pred [] for i in range(len(rf_pred)): votes [rf_pred[i], xgb_pred[i], dl_pred[i]] final_hard_vote_pred.append(stats.mode(votes)[0][0]) # 取众数 final_hard_vote_pred np.array(final_hard_vote_pred) print( 硬投票集成 - 测试集性能 ) print(classification_report(y_test, final_hard_vote_pred, target_nameslabel_encoder.classes_)) # 软投票平均概率取最大 # 需要将树模型的预测转换为概率随机森林有predict_probaXGBoost已有概率 rf_pred_prob rf_model.predict_proba(X_test_scaled) # 假设类别数一致对齐概率矩阵形状 # 平均概率 avg_prob (rf_pred_prob xgb_pred_prob dl_pred_prob) / 3.0 final_soft_vote_pred np.argmax(avg_prob, axis1) print(\n 软投票集成 - 测试集性能 ) print(classification_report(y_test, final_soft_vote_pred, target_nameslabel_encoder.classes_))6.2 堆叠法集成堆叠是更高级的集成方法用初级模型的预测结果作为特征训练一个次级模型元模型。from sklearn.linear_model import LogisticRegression # 准备次级训练数据在训练集上做交叉验证获取“干净”的初级模型预测 from sklearn.model_selection import cross_val_predict # 获取训练集的初级模型预测使用交叉验证避免数据泄露 rf_train_meta cross_val_predict(rf_model, X_train_scaled, y_train, cv5, methodpredict_proba) xgb_train_meta cross_val_predict(xgb.XGBClassifier(**params), X_train_scaled, y_train, cv5, methodpredict_proba) # 深度学习模型获取训练集预测稍复杂这里简化为用模型直接预测有小风险 dl_train_meta model.predict(X_dl_train[train_idx]) # 注意索引对应 # 堆叠特征将三个模型的预测概率拼接起来 stacked_train_features np.hstack([rf_train_meta, xgb_train_meta, dl_train_meta]) # 训练次级模型元模型这里用简单的逻辑回归 meta_model LogisticRegression(max_iter1000, random_state42) meta_model.fit(stacked_train_features, y_train) # 在测试集上获取初级模型预测 rf_test_meta rf_model.predict_proba(X_test_scaled) xgb_test_meta bst.predict(dtest, output_marginFalse) dl_test_meta model.predict(X_dl_test) stacked_test_features np.hstack([rf_test_meta, xgb_test_meta, dl_test_meta]) # 用元模型做最终预测 stacked_pred meta_model.predict(stacked_test_features) print(\n 堆叠集成 - 测试集性能 ) print(classification_report(y_test, stacked_pred, target_nameslabel_encoder.classes_))注意事项集成虽好但会增加复杂度和推理时间。在竞赛中如果单个模型已经足够好比如准确率95%集成的提升可能非常有限0.5%-1%需要权衡收益和复杂度。硬投票简单粗暴且有效软投票更精细但要求模型能输出校准良好的概率堆叠潜力最大但最容易过拟合且对实现细节要求高。7. 竞赛提交与报告撰写要点解题的最后一步是将你的工作清晰、有说服力地呈现出来。一份优秀的竞赛报告和代码同样重要。7.1 代码组织与提交你的代码应该是可复现、可读性高的。your_project/ │ ├── data/ # 存放原始数据如果允许 │ └── activity_data.csv │ ├── src/ # 源代码 │ ├── 01_data_preprocessing.py │ ├── 02_feature_engineering.py │ ├── 03_model_training_rf_xgb.py │ ├── 04_model_training_dl.py │ ├── 05_ensemble.py │ └── utils.py # 公共函数 │ ├── models/ # 保存训练好的模型 │ ├── random_forest.pkl │ └── cnn_lstm.h5 │ ├── results/ # 输出结果、图表 │ ├── confusion_matrix.png │ └── feature_importance.png │ ├── requirements.txt # Python依赖包列表 ├── README.md # 项目说明如何运行 └── main.py # 主运行脚本一键复现所有步骤requirements.txt示例pandas1.4.0 numpy1.22.0 scikit-learn1.0.0 xgboost1.6.0 tensorflow2.9.0 # 或 torch1.12.0 scipy1.8.0 matplotlib3.5.0 seaborn0.11.07.2 报告撰写核心要素报告不是代码的罗列而是你思考过程的展现。问题重述与理解用你自己的话简述问题并点明其核心挑战时序分类、噪声、个体差异等。整体解决方案概述用一张流程图概括你的“数据理解-特征工程-模型构建-评估优化”全流程。数据预处理与特征工程详解这是体现你工作量的地方。解释为什么选择2秒窗口、50%重叠为什么提取这些时域、频域特征展示数据可视化图和特征重要性图来佐证你的选择。模型选择与对比分析不要只说“我用了随机森林、XGBoost和CNN-LSTM”。要解释为什么选它们随机森林作为稳健基线XGBoost处理复杂非线性关系CNN-LSTM捕捉时空模式。并用表格清晰对比它们的性能模型准确率精确率加权平均召回率加权平均F1分数加权平均训练时间备注随机森林94.2%0.9430.9420.942快特征重要性可解释XGBoost95.1%0.9520.9510.951中等需仔细调参CNN-LSTM94.8%0.9490.9480.948慢无需手工特征数据需求大软投票集成96.0%0.9610.9600.960中等融合各模型优势结果分析与讨论重点分析混淆矩阵。指出哪些活动容易混淆如“坐”和“站”并分析原因可能特征相似提出可能的改进方向如增加压力传感器数据。讨论模型的优缺点和泛化能力。结论与展望简洁总结你的方法取得了什么效果。可以谦虚地指出局限性如未考虑不同设备差异并展望未来工作如尝试更复杂的Transformer模型、数据增强等。最后的小技巧在代码关键处和报告中使用清晰的注释和说明。确保你的代码只需运行python main.py就能从头到尾生成所有结果和图表。这会给评委留下极其专业的印象。记住竞赛比的不仅是算法精度更是解决一个实际问题的完整工程能力和严谨的科学思维。
返回列表