ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从时序数据到分类模型:人类活动识别的特征工程与机器学习实战

从时序数据到分类模型:人类活动识别的特征工程与机器学习实战 1. 从“人类活动分类”到数学建模实战一次完整的解题思路拆解最近在整理过往的竞赛资料翻到了2022年小美赛美国大学生数学建模竞赛MCM/ICM的C题“Classify Human Activities”。这道题当时在圈内讨论度很高因为它完美地融合了数据科学、机器学习与一个极具现实意义的应用场景。题目本身并不复杂核心就是给你一组人类活动的传感器数据比如加速度计、陀螺仪读数让你构建一个模型能够准确地将这些数据分类到对应的活动类别中比如走路、跑步、上楼、下楼、坐着、站着等。听起来是不是很像我们手机或智能手环里的“活动识别”功能没错这道题的本质就是一次从原始数据到分类模型的完整机器学习项目实战。对于数学建模新手来说这道题是一个绝佳的入门案例。它不像一些优化题那样需要复杂的数学推导也不像一些政策分析题那样需要大量的背景调研。它的核心脉络非常清晰数据预处理 - 特征工程 - 模型构建与训练 - 模型评估与优化。但恰恰是这种“清晰”最能考验一个建模者的基本功和工程化思维。你是否能想到有效的特征是否了解不同分类算法的特性并做出合理选择是否考虑了过拟合、类别不平衡等实际问题这些细节才是区分一篇普通论文和优秀论文的关键。今天我就以这道题为例抛开那些高大上的理论从一个一线建模者的角度复盘一次完整的解题流程。我会重点分享我们当时是怎么想的、怎么做的以及过程中踩过哪些坑、有哪些“事后诸葛亮”式的经验。无论你是正在备战数模竞赛的学生还是对机器学习应用感兴趣的朋友希望这篇“实战笔记”都能给你带来一些直接的启发。2. 赛题核心剖析我们到底要解决什么问题拿到题目第一步永远不是急着找代码、套模型而是彻底理解问题。2022年小美赛C题通常会提供一份数据集例如来自UCI机器学习库的“Human Activity Recognition Using Smartphones”数据集或其变体并明确要求对给定的传感器时间序列数据进行活动分类。2.1 问题本质与数据理解这道题的核心是一个多类别的时序数据分类问题。我们面对的输入数据通常是以固定频率如50Hz采集的、来自智能手机或穿戴设备的多维传感器原始信号。每一行数据可能包含在某个时间点或某个短时间窗口内的三轴加速度计x, y, z、三轴陀螺仪x, y, z的读数可能还有总加速度、身体加速度、重力分量等衍生数据列。对应的标签Label则是离散的活动类别如WALKING, WALKING_UPSTAIRS, WALKING_DOWNSTAIRS, SITTING, STANDING, LAYING。这里有几个关键点需要立刻明确时序性数据点不是独立同分布的。前后时刻的数据具有强烈的相关性一个“跑步”的动作必然由一系列连续的肢体摆动信号构成。这意味着我们不能简单地把每个时间点的数据当作独立样本扔进模型。高维度与冗余性原始信号维度较高6-17维不等且不同轴之间的信号可能存在共线性或冗余。例如身体加速度是总加速度减去重力分量得到的。类内方差与类间相似性不同人做同一个动作如走路的传感器模式会有差异类内方差而某些不同动作在局部信号上可能相似如慢走和快走或站起和坐下的过渡阶段。数据平衡性需要检查数据集中各个活动类别的样本量是否大致均衡。严重的不平衡会导致模型偏向多数类。我们当时拿到数据后的第一件事就是用Python的Pandas和Matplotlib做了一次彻底的探索性数据分析EDA。这不是走过场而是后续所有工作的基石。import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # 假设数据已加载到DataFrame df 中标签列名为 ‘Activity’ print(df.info()) # 查看数据概览有无缺失值 print(df[‘Activity’].value_counts()) # 查看类别分布 print(df.describe()) # 查看数值统计 # 绘制某个样本或某个时间段的三轴加速度信号 sample_subject df[df[‘subject_id’]1].iloc[1000:1200] # 假设有受试者ID fig, axes plt.subplots(3, 1, figsize(12, 8)) axes[0].plot(sample_subject[‘tBodyAcc-mean()-X’], label‘X-axis’) axes[0].set_title(‘Body Acceleration X’) axes[0].legend() # ... 绘制Y轴、Z轴 plt.tight_layout() plt.show() # 绘制不同活动的某个特征分布如总加速度的均值 plt.figure(figsize(10,6)) sns.boxplot(x‘Activity’, y‘tBodyAccMag-mean()’, datadf) plt.xticks(rotation45) plt.title(‘Distribution of Feature X across Activities’) plt.show()通过EDA我们直观地看到了不同活动信号模式的差异也确认了数据是干净、平衡的。这让我们对后续的特征提取有了信心。2.2 评价指标的选择什么才算“好模型”题目要求分类那么如何评价模型的好坏准确率Accuracy是最直观的但在类别平衡的数据集上它是一个不错的初始指标。然而我们绝不能只依赖准确率。我们当时在论文中明确列出了以下几个核心评价指标并解释了原因准确率Accuracy整体分类正确的比例。在数据平衡时有效。混淆矩阵Confusion Matrix这是最重要的分析工具之一。它能清晰显示模型具体在哪些类别上容易混淆。例如我们很可能发现“上楼”和“下楼”容易互相误判“坐着”和“站着”在静止时难以区分。混淆矩阵直接指导我们后续的特征工程和模型改进方向。精确率Precision、召回率Recall与F1分数F1-Score特别是按类别计算的这些指标。它们能更细致地反映模型对每个类别的识别能力。例如如果“躺着”这个类别非常重要在医疗监测场景下那么即使它的样本量少我们也希望模型对其有高的召回率尽量不漏检。分类报告Classification ReportSklearn提供的classification_report函数可以一次性输出所有类别的精确率、召回率、F1分数和支持度非常方便用于论文中的结果呈现。在模型训练和调优过程中我们主要以验证集上的加权平均F1分数作为核心优化指标因为它同时考虑了精确率和召回率且对类别不平衡有一定鲁棒性。最终在测试集上我们会报告完整的混淆矩阵和分类报告。注意很多新手队伍会犯一个错误——只用测试集上的准确率说事。正确的流程是将数据划分为训练集、验证集和测试集或使用交叉验证。在训练集上训练在验证集上调整超参数和选择模型最后只用一次测试集来评估最终模型的泛化性能并报告结果。这个流程必须在论文的方法部分写清楚。3. 解题的核心引擎特征工程与模型选型理解了问题和数据之后就进入了最核心的环节如何从原始的时序信号中提炼出对分类有用的信息特征工程以及选择什么样的模型来学习这些特征。3.1 特征工程从“波形”到“数字指纹”原始的一串加速度、陀螺仪读数就像一段音频波形直接喂给模型如深度学习模型也许可行但对于传统机器学习模型如随机森林、SVM以及为了可解释性我们必须进行特征提取。我们的核心思路是将连续的时序信号分割成固定长度、有重叠的窗口例如2秒一个窗口重叠50%然后对每个窗口内的每一维信号计算一组统计特征和频域特征。这是我们当时提取的部分特征列表实践证明非常有效时域特征基本统计量均值、标准差、最小值、最大值、范围极差。形态统计量偏度衡量分布不对称性、峰度衡量分布尖锐程度。不同活动的信号分布形状不同例如跑步的加速度信号峰度可能更高冲击更剧烈。相关性窗口内三轴加速度之间、三轴陀螺仪之间、以及加速度与陀螺仪特定轴之间的相关系数。这能反映肢体运动的协调模式。信号幅度面积SMA对加速度信号取绝对值后求和反映总体运动强度。过零率Zero-Crossing Rate信号穿过零点的频率对区分静态和动态活动有帮助。频域特征通过快速傅里叶变换FFT获得频谱能量将信号转换到频域后在不同频带如0-1Hz 1-3Hz 3-5Hz等内的能量积分。周期性运动如走路、跑步会在特定频率出现能量峰值。主频功率谱密度最大的频率成分。频谱熵衡量频谱的混乱程度静态活动频谱熵低复杂活动频谱熵高。我们当时用Python实现特征提取的简化框架如下import numpy as np import pandas as pd from scipy import stats, signal from scipy.fft import fft, fftfreq def extract_features_for_window(window_data): “”” window_data: 一个二维数组形状为 (window_length, n_sensors) “”” features [] for i in range(window_data.shape[1]): # 对每个传感器维度 sig window_data[:, i] # 时域特征 features.append(np.mean(sig)) features.append(np.std(sig)) features.append(np.min(sig)) features.append(np.max(sig)) features.append(np.ptp(sig)) # 极差 features.append(stats.skew(sig)) features.append(stats.kurtosis(sig)) features.append(np.sum(np.abs(sig))) # SMA # 过零率简化版 zcr np.sum(np.diff(np.sign(sig)) ! 0) / len(sig) features.append(zcr) # 频域特征 (假设采样频率fs已知) fs 50.0 n len(sig) yf fft(sig) xf fftfreq(n, 1/fs)[:n//2] # 正频率部分 psd np.abs(yf[:n//2])**2 # 计算0-3Hz, 3-6Hz, 6-10Hz频带能量 freq_bands [(0,3), (3,6), (6,10)] for low, high in freq_bands: band_mask (xf low) (xf high) band_energy np.sum(psd[band_mask]) features.append(band_energy) # 主频 if len(psd) 0: dominant_freq xf[np.argmax(psd)] features.append(dominant_freq) else: features.append(0) # 还可以添加传感器间的相关性特征 corr_matrix np.corrcoef(window_data.T) # 取上三角矩阵的非对角线元素作为特征 for i in range(corr_matrix.shape[0]): for j in range(i1, corr_matrix.shape[1]): features.append(corr_matrix[i, j]) return np.array(features)经过这样的处理一个窗口的原始数据比如100个时间点 * 6个传感器 600个数据点被转换成了一个几百维的特征向量。这个特征向量就是这个时间窗口内人类活动的“数字指纹”。实操心得特征工程是体力活也是艺术活。我们当时尝试了数十种特征但并非越多越好。后期我们使用了递归特征消除RFE或基于树模型的特征重要性排序剔除了大量不重要的特征不仅提升了模型速度有时甚至因为减少了噪声而提高了精度。一定要在论文中阐述你选择这些特征的理由。3.2 模型选型与对比没有银弹只有合适特征准备好后就是选择分类器。我们当时的策略是“先广后深”快速用几个有代表性的模型在验证集上跑一遍看看基线性能再集中精力优化最有希望的模型。我们主要对比了以下几类模型传统机器学习模型随机森林Random Forest我们的首选基线模型。它对特征量纲不敏感能处理高维特征自带特征重要性评估且不容易过拟合。通常能取得非常不错的成绩且可解释性强。梯度提升树如XGBoost, LightGBM在许多表格数据竞赛中表现抢眼。它们比随机森林更强大但需要仔细调参训练时间也更长。支持向量机SVM在小样本、高维特征上理论优势强。但对于我们这种特征多、样本量大的情况训练速度慢且对核函数和参数敏感。K近邻KNN简单直观但计算开销大需要存储所有样本且对特征尺度非常敏感必须做标准化。深度学习模型1D卷积神经网络1D-CNN非常适合处理时序信号。它能自动学习局部特征如一个步态周期内的模式省去了大量手工特征工程的工作。我们构建了一个简单的多层CNN输入是原始窗口信号输出是类别概率。长短时记忆网络LSTM专门为序列数据设计能捕捉长距离依赖。对于活动识别LSTM可以学习动作的时序演变规律。但训练比CNN更慢更容易过拟合。CNN-LSTM混合模型先用CNN层提取局部特征再将特征序列输入LSTM层捕捉时序依赖。这是一种非常强大的架构但模型复杂需要更多的数据和技巧来训练。我们当时的做法和结论我们先用标准化后的特征在同一个训练/验证集划分下快速训练了随机森林、XGBoost和一个简单的1D-CNN。结果发现在特征工程做得足够好的情况下随机森林和XGBoost的表现与1D-CNN不相上下甚至略好而且训练和预测速度快得多。这对于数学建模竞赛有限的时间来说是一个巨大优势。深度学习模型虽然潜力大但在数据量不是特别巨大的情况下其优势并不明显且调参更复杂。因此我们最终选择以XGBoost作为主力模型进行深入调优。在论文中我们展示了不同模型的对比结果用表格呈现准确率、F1分数并解释了选择XGBoost的理由性能优异、训练速度快、能输出特征重要性、对缺失值不敏感。from xgboost import XGBClassifier from sklearn.model_selection import GridSearchCV from sklearn.preprocessing import StandardScaler from sklearn.metrics import classification_report, confusion_matrix # 假设 X_train_feat, y_train, X_val_feat, y_val 是特征工程后的数据 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train_feat) X_val_scaled scaler.transform(X_val_feat) # 初始模型 base_model XGBClassifier(random_state42, n_jobs-1, eval_metric‘mlogloss’) base_model.fit(X_train_scaled, y_train) y_pred_base base_model.predict(X_val_scaled) print(“Base Model Performance:“) print(classification_report(y_val, y_pred_base)) # 网格搜索调参简化版 param_grid { ‘n_estimators’: [100, 200, 300], ‘max_depth’: [3, 5, 7], ‘learning_rate’: [0.01, 0.05, 0.1], ‘subsample’: [0.8, 1.0], ‘colsample_bytree’: [0.8, 1.0] } grid_search GridSearchCV(estimatorbase_model, param_gridparam_grid, cv3, scoring‘f1_weighted’, verbose1, n_jobs-1) grid_search.fit(X_train_scaled, y_train) print(“Best Params:“, grid_search.best_params_) best_model grid_search.best_estimator_4. 模型优化与结果分析让分数再上一个台阶有了基线模型和初步结果工作远未结束。如何从“能用”到“优秀”就需要一系列的优化技巧和对结果的深度分析。4.1 针对性的优化策略处理类别混淆通过分析混淆矩阵我们发现“WALKING_UPSTAIRS”和“WALKING_DOWNSTAIRS”的相互误判率较高。这是符合直觉的因为它们的运动模式相似。我们的优化策略是构造区分性特征专门针对上下楼我们计算了Z轴加速度的均值差下楼时初始向下的加速度冲击更明显、以及信号在特定频带如极低频反映整体趋势的能量比。调整类别权重在XGBoost中可以通过scale_pos_weight参数或为每个类别设置不同的样本权重让模型更关注难以区分的类别。我们尝试了根据验证集上各类别的F1分数倒数来设置权重。后处理规则我们甚至设计了一个简单的后处理规则——如果模型预测为“上楼”或“下楼”但该时间窗口前后的窗口多数被预测为“平地行走”则将其改为“平地行走”。这种基于时序上下文的后处理有时能纠正明显的孤立错误。特征选择与降维初始特征池有几百个维度可能存在冗余和噪声。我们使用了XGBoost自带的feature_importances_属性筛选出重要性排名前100的特征重新训练发现模型性能稳定且训练速度大大提升。我们也尝试了PCA降维但发现对于树模型保留原始特征通常比线性降维效果更好。集成学习为了进一步提升鲁棒性我们最终采用了“软投票”集成。我们训练了三个表现最好的模型调优后的XGBoost、一个随机森林、一个LightGBM。对于测试集的每个样本取三个模型预测概率的平均值然后选择概率最大的类别作为最终预测。这种集成方法通常能将准确率/F1分数提升0.5%到1%是一个有效的“提分”技巧。4.2 结果可视化与论文呈现在数学建模论文中清晰地展示结果和分析过程与得到高精度模型同等重要。混淆矩阵热力图我们用Seaborn绘制了精美的混淆矩阵热力图对角线越亮越好非对角线的色块能清晰显示主要的混淆类别对。特征重要性水平条形图展示了Top-20最重要的特征及其重要性分数。这不仅让评委知道你的模型依赖什么做决策也反向验证了你特征工程的有效性。例如如果频域能量特征排名靠前说明你提取的频域信息是关键的。各类别性能指标表格用Markdown或LaTeX表格清晰列出每个类别的精确率、召回率、F1分数和支持度。并对表现最差和最好的类别进行分析。模型对比表格列出随机森林、SVM、XGBoost、CNN等模型在验证集上的核心指标准确率、加权F1并附上简单的优缺点分析。错误案例分析选取几个被模型错误分类的样本窗口将其原始信号波形绘制出来并与真实类别和预测类别的典型波形进行对比。分析错误原因是否是动作过渡期信号噪声大这体现了深刻的思考。踩坑实录我们第一次画特征重要性图时发现“均值”类特征普遍排名很高而一些我们精心设计的复杂特征如频谱熵排名靠后。一开始很沮丧后来意识到对于区分“动”与“静”如走路和坐着信号的平均能量均值本身就是最强、最直接的特征。这提醒我们不要轻视简单特征它们往往提供了最基础的判别信息。复杂特征的作用可能在于区分那些简单特征无法区分的相似类别。5. 从项目到论文构建有说服力的叙述逻辑解决了技术问题最后一步是将整个工作整理成一篇逻辑严谨、叙述清晰的数学建模论文。论文的结构和表达决定了评委能否快速理解你的工作价值。摘要Abstract这是论文的门面。必须用精炼的语言概括问题、你的方法、核心步骤和最终结果。模板“针对人类活动分类问题本文提出了一个基于多维度特征工程与集成机器学习模型的解决方案。首先我们对原始三轴加速度计与陀螺仪时序数据进行滑窗分割并提取了时域、频域及传感器间相关性等共计XX维特征。随后我们对比了随机森林、XGBoost和1D-CNN等模型最终选择XGBoost作为基础分类器并通过网格搜索优化其超参数。为提升对易混淆活动如上/下楼的区分能力我们设计了针对性特征并采用了软投票集成策略。在测试集上我们的模型达到了XX%的整体准确率与XX的加权平均F1分数。混淆矩阵与特征重要性分析进一步验证了模型的有效性与可解释性。”引言Introduction阐述问题背景基于传感器的活动识别在健康监护、人机交互等领域的应用、问题重述将赛题翻译成学术问题和本文工作概要。模型假设与数据预处理Assumptions Data Preprocessing明确列出你的合理假设如“假设传感器佩戴位置固定”、“忽略不同个体间的生理差异”等。详细描述数据清洗处理缺失值、异常值、滑窗分割窗口长度、重叠率的选择及理由、数据标准化/归一化的过程。特征工程Feature Engineering这是展示你创造力的核心章节。系统性地介绍你提取的各类特征时域、频域、相关性等并最好能用公式或伪代码说明关键特征的计算方法。可以配一张图展示某个活动窗口的原始信号及其提取出的部分特征值。模型建立与求解Modeling and Solution详细介绍你尝试的模型、选择最终模型的理由、模型原理的简要说明不要大段抄教科书用你自己的话概括、超参数调优的过程如网格搜索的范围、交叉验证的折数。给出最终的模型参数和集成策略。结果分析与模型评估Results Analysis Model Evaluation展示所有可视化结果混淆矩阵、特征重要性图、性能指标表。对结果进行深入讨论模型在哪里表现好为什么在哪里表现差可能的原因是什么你的优化策略起到了多大效果灵敏度分析Sensitivity Analysis加分项探讨模型对关键参数的鲁棒性。例如改变滑窗长度从1秒到3秒对准确率的影响随机丢弃一定比例的特征后模型性能的变化。这能体现你对模型理解的深度。结论与展望Conclusion Future Work总结全文工作重申主要贡献。指出模型的局限性如未考虑个性化差异、对未知活动泛化能力未知等并提出可能的改进方向如引入更深的神经网络、融合更多传感器数据、进行在线学习等。参考文献与附录规范引用你参考的算法、工具包如scikit-learn, XGBoost或相关论文。在附录中可以提供核心代码的片段或额外的结果图表。我个人最深的体会是数学建模竞赛尤其是像这道题一样的“数据科学”类题目比拼的不仅仅是谁用的模型更高级更是从问题理解、数据洞察、特征构建、模型选择到结果分析的全流程严谨性与创新性。一个简单模型配上精彩的特征和深入的分析往往能击败一个复杂模型配上平庸的流水账。这道“人类活动分类”题就是一个绝佳的练兵场它几乎涵盖了监督学习项目的所有核心环节。希望这份详细的复盘能帮助你构建起解决此类问题的完整思维框架。下次再遇到类似题目你就能从容地拆解它、分析它并最终征服它。
返回列表