从数学建模到工程实践:人类活动识别(HAR)全流程解析
1. 项目概述:从一道赛题看人类活动识别的核心挑战
刚拿到2022年小美赛C题“人类活动分类”这个题目时,很多同学的第一反应可能是:这不就是个分类问题吗?用个机器学习模型,比如SVM或者随机森林,把数据喂进去训练一下不就完事了?如果你也这么想,那可能从一开始就低估了这道题的深度和它背后所代表的整个研究领域的复杂性。这道题之所以能成为一道经典的建模赛题,恰恰是因为它完美地封装了从现实世界模糊问题到清晰数学模型,再到工程化解决方案的全链条挑战。它考察的绝不仅仅是你会不会调包调用几个分类算法,而是你如何定义“活动”、如何获取和预处理数据、如何从噪声中提取有效特征、如何选择并解释模型,以及最终如何评估一个分类系统在真实场景下的可用性。
人类活动识别,英文叫Human Activity Recognition,简称HAR,听起来很高大上,但其实离我们非常近。你手机里的健康App记录你走了多少步、睡了多久,智能手表提醒你“该起来活动一下了”,甚至一些家庭安防摄像头能判断画面里的人是正常行走还是突然摔倒——这些功能的背后,核心都是HAR技术。而这道赛题,就是把这样一个前沿且实用的研究方向,抽象成了一个经典的、有明确数据边界和评价指标的数学建模问题。它要求参赛者扮演一个算法工程师的角色,去构建一个能够根据传感器数据(可能是加速度计、陀螺仪等)自动判别佩戴者正在进行何种活动(如走路、跑步、上下楼梯、坐着、站着等)的系统。
这道题适合所有对数据科学、机器学习应用感兴趣的同学,尤其是那些已经学过一些基础模型(如KNN、决策树),但还没经历过完整项目实战的朋友。通过拆解这道题,你不仅能巩固分类算法的知识,更能学到一整套数据驱动的建模方法论:如何将业务问题转化为数学问题,如何处理现实世界中“脏乱差”的数据,如何设计实验验证模型的有效性,以及如何用严谨的论文呈现你的思考过程。接下来,我就结合自己多次带队参赛和工业界项目实践的经验,带你从头到尾拆解这道题,我会重点讲清楚每个环节“为什么”要这么做,以及那些容易踩坑的“实操细节”。
2. 问题拆解与建模思路设计
面对“人类活动分类”这样一个题目,第一步也是最关键的一步,不是急着找代码,而是彻底想清楚:我们要解决的具体是什么问题?题目给出的条件和约束是什么?只有把问题定义清晰了,后续所有工作才有正确的方向。
2.1 核心需求与问题定义
通常,这类赛题会提供一个数据集。我们假设数据集包含多个受试者在身体不同部位(如手腕、腰部、脚踝)佩戴传感器所采集的时序数据,每个数据样本对应一段固定时间窗口(例如2秒)内的传感器读数,并带有活动类型的标签(如“Walking”, “Sitting”, “Upstairs”)。那么,我们的核心任务就非常明确了: 构建一个分类模型,该模型能够根据一段新的、未标记的传感器时序数据,准确预测其对应的人类活动类型。
但这只是表面任务。深层需求包括:
- 高精度 :分类准确率(Accuracy)要高,这是最直接的评估指标。
- 强鲁棒性 :模型对于不同个体、不同传感器佩戴位置、不同数据采集环境下的差异应具有一定的适应性,不能只在训练集上表现好。
- 可解释性 (在某些场景下):虽然深度学习模型可能精度更高,但如果能用决策树、逻辑回归等模型达到可接受的精度,其模型本身提供的特征重要性或决策规则对于理解活动特征更有价值。
- 低计算成本 :考虑到最终可能部署在手机、手表等嵌入式设备上,模型不宜过于复杂,推理速度要快。
基于这些需求,我们的建模思路可以形成一个清晰的流水线: 数据预处理 -> 特征工程 -> 模型选择与训练 -> 模型评估与优化 。这个流水线是解决绝大多数HAR问题乃至通用机器学习问题的基本框架。
2.2 技术路线选型与权衡
确定了流水线,接下来就要为每个环节选择具体的技术方案。这里没有唯一的“正确答案”,只有针对不同侧重点的“权衡之选”。
方案A:传统机器学习流水线 这是最经典、最可控,也最适合数学建模竞赛展示清晰逻辑的路线。
- 核心 :手动从原始时序信号中提取大量统计特征(时域、频域),然后使用经典分类器(如随机森林、XGBoost、SVM)进行训练。
- 优点 :
- 可解释性强 :特征含义明确,模型(如决策树)可以生成规则。
- 对数据量要求相对较低 :在特征设计得当的情况下,几千个样本也能取得不错效果。
- 计算效率高 :训练和预测速度快。
- 非常适合竞赛 :每一步都可以详细阐述原理,展示你的思考过程。
- 缺点 :
- 特征工程依赖经验 :需要领域知识来设计有效的特征,这是一个试错过程。
- 可能无法捕捉深层时序模式 :手动特征可能遗漏原始信号中的复杂关联。
方案B:深度学习端到端学习 这是目前工业界前沿的主流方向,尤其是基于卷积神经网络或循环神经网络的方法。
- 核心 :将原始时序数据(或进行最小化预处理,如归一化)直接输入神经网络(如1D-CNN, LSTM, CNN-LSTM混合模型),让网络自动学习特征表示并进行分类。
- 优点 :
- 省去繁琐的特征工程 :模型自动学习最优特征。
- 潜力上限高 :在大规模数据上,深度学习模型往往能达到比传统方法更高的精度。
- 能更好地建模时序依赖 :LSTM等结构专为序列数据设计。
- 缺点 :
- 需要大量数据 :参数多,容易过拟合,小数据集上可能反而不如传统方法。
- 黑盒模型,可解释性差 :难以说清模型为什么做出某个判断。
- 计算成本高 :训练耗时,对硬件有要求。
- 在竞赛中 :如果只是调包搭建一个复杂网络,而不深入理解其原理和针对性的结构调整,在论文阐述上会显得深度不足。
对于小美赛这类竞赛,我的强烈建议是:以方案A为主,方案B作为对比和升华。 原因有三:第一,竞赛数据集通常不会特别巨大,传统方法完全有发挥空间;第二,竞赛论文看重逻辑链条的完整性和你对问题的理解深度,手动特征工程和模型选择的过程正是展示你思考的舞台;第三,你可以用一个精心调优的随机森林作为基线模型,然后尝试一个简单的1D-CNN作为对比,分析两者在不同活动上的表现差异,这能让你的论文内容更丰满、更有层次。
3. 数据预处理与特征工程实战解析
数据决定了模型性能的上限,而模型和算法只是逼近这个上限。在HAR中,原始传感器数据往往是嘈杂且冗余的,直接使用效果极差。因此,预处理和特征工程是提升模型性能最关键、最有效的步骤,没有之一。
3.1 数据预处理:为分析打下坚实基础
拿到数据(通常是.csv或.txt文件)后,第一步不是跑模型,而是“看”数据。
- 数据加载与探查 :用Pandas加载数据,查看数据维度、列名、数据类型。检查是否有缺失值(
isnull().sum()),标签分布是否均衡(value_counts())。一个严重失衡的数据集会误导模型。 - 噪声过滤 :加速度计、陀螺仪数据包含高频噪声。通常采用低通滤波器(如Butterworth滤波器)来平滑信号,保留人体活动的主要频率成分(一般集中在0-20Hz)。你可以这样操作:
from scipy.signal import butter, filtfilt def butter_lowpass_filter(data, cutoff_freq, fs, order=4): nyquist = 0.5 * fs # 奈奎斯特频率 normal_cutoff = cutoff_freq / nyquist b, a = butter(order, normal_cutoff, btype='low', analog=False) y = filtfilt(b, a, data) # 使用filtfilt实现零相位滤波 return y # 假设采样频率fs=50Hz,截止频率设为10Hz filtered_acc_x = butter_lowpass_filter(acc_x, cutoff_freq=10.0, fs=50.0)注意 :
filtfilt比普通的lfilter更好,因为它没有相位延迟,处理后的信号在时间上是对齐的。 - 信号分割 :原始数据是长序列,我们需要将其切分成固定长度、有重叠或无重叠的窗口(例如2秒一个窗口,50Hz采样率就是100个数据点一个窗口)。重叠窗口(如50%重叠)可以增加样本量,缓解数据不足,但也会引入样本间的相关性。
def create_segments(data, labels, window_size, step_size): segments = [] segment_labels = [] for start in range(0, len(data) - window_size, step_size): end = start + window_size segment = data[start:end] label = stats.mode(labels[start:end])[0][0] # 取窗口内众数作为该窗口标签 segments.append(segment) segment_labels.append(label) return np.array(segments), np.array(segment_labels) - 归一化 :为了避免某些数值范围大的特征(如加速度幅值)主导模型训练,需要对特征进行归一化。通常使用 针对每个特征维度 的Z-score标准化(减均值除以标准差)。切记:用训练集的均值和标准差去标准化测试集,这是防止数据泄露的铁律。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) # 在训练集上拟合scaler X_test_scaled = scaler.transform(X_test) # 用训练集的参数转换测试集
3.2 特征工程:从信号中提炼“精华”
这是传统方法的核心,也是最能体现你思考和创造力的地方。好的特征应该能最大程度地区分不同活动。我们从时域、频域和时频域三个角度来挖掘。
时域特征 :直接从信号幅值随时间变化中提取。
- 基本统计量 :均值、方差、标准差、最大值、最小值、范围。
- 形态特征 :信号曲线下面积、过零点率、峰谷计数。
- 相关性 :三轴加速度(x, y, z)之间的相关系数。走路时,三轴运动是协调的;而打字可能主要在于腕的特定轴。
- 自定义特征 :例如,静止状态下,加速度矢量和应接近重力加速度g;运动时,其方差会增大。可以计算
body_acc = sqrt(ax^2+ay^2+az^2) - g的统计量。
频域特征 :通过快速傅里叶变换将信号转换到频率域,提取周期性信息。
- FFT变换 :
frequencies, magnitudes = np.fft.rfft(signal), np.abs(np.fft.rfft(signal)) - 频域统计量 :频谱熵(信号复杂度)、频谱重心(主要频率成分)、频谱幅值的均值、方差等。
- 频带能量 :将频谱划分为几个频带(如0-5Hz, 5-10Hz等),计算每个频带的能量占比。不同活动的主导频率不同。
时频域特征 :结合时间和频率信息,适合非平稳信号。
- 小波变换 :比FFT更能捕捉信号的局部频率特征。可以提取小波系数的统计量作为特征。
实操心得 :
- 不要盲目堆砌特征 :提取几十上百个特征很容易,但其中很多可能是冗余或无关的。这会导致“维度灾难”,增加计算负担,还可能降低模型泛化能力。
- 一定要做特征选择 :在特征送入模型前,使用
sklearn.feature_selection中的方法(如基于树模型的特征重要性、递归特征消除RFE)筛选出最重要的特征子集。你会发现,可能只需要20%的特征就能达到95%的精度。 - 为不同传感器和轴分别提取特征 :腰部加速度计和手腕加速度计反映的信息不同,分别提取再合并,能提供更丰富的视角。
4. 模型构建、训练与评估全流程
特征准备好了,我们就可以开始搭建和训练模型了。这里我们以经典的随机森林为例,因为它效果好、不易过拟合、能输出特征重要性,非常适合作为基线模型。
4.1 模型选择与训练
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import classification_report, confusion_matrix
# 1. 划分训练集和测试集(如果官方没提供)
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42, stratify=labels)
# 使用stratify确保训练测试集标签分布一致
# 2. 初始化模型
# 关键超参数:n_estimators(树的数量), max_depth(树的最大深度), min_samples_split(节点分裂所需最小样本数)
rf_model = RandomForestClassifier(n_estimators=200,
max_depth=15,
min_samples_split=5,
random_state=42,
n_jobs=-1) # n_jobs=-1使用所有CPU核心加速
# 3. 训练模型
rf_model.fit(X_train, y_train)
# 4. 查看特征重要性
importances = rf_model.feature_importances_
indices = np.argsort(importances)[::-1]
# 可以将特征名和重要性对应打印出来,分析哪些特征最有用
为什么选择这些参数?
n_estimators=200:树越多,模型越稳定,性能通常越好,但计算量也越大。可以通过学习曲线观察准确率随树数量增加的变化,在收益变小时停止。max_depth=15:限制树深度是防止过拟合的关键手段。太深会记住训练集噪声,太浅则学不到模式。需要交叉验证调优。min_samples_split=5:节点至少需要5个样本才继续分裂,也是防止过拟合。random_state:固定随机种子,确保结果可复现,这对竞赛和实验至关重要。
4.2 模型评估:不止看准确率
模型训练完,在测试集上一测,准确率95%!是不是就大功告成了?远远不是。一个全面的评估需要多维度审视。
-
基础指标 :使用
classification_report查看精确率、召回率、F1-score。 对于不平衡数据集,宏观平均F1(macro-F1)比准确率更有参考价值 。y_pred = rf_model.predict(X_test) print(classification_report(y_test, y_pred)) -
混淆矩阵 :这是发现模型弱点的“显微镜”。它能清晰显示模型具体在哪些类别上容易混淆。
import seaborn as sns cm = confusion_matrix(y_test, y_pred) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues') plt.xlabel('Predicted') plt.ylabel('True')- 典型问题 :你很可能发现“上楼”和“下楼”容易互相误判,“坐着”和“站着”在传感器数据不典型时也难区分。这非常正常,也是你后续优化模型和分析的重点。
-
交叉验证 :为了更稳健地估计模型性能,避免因一次数据划分带来的偶然性,必须使用K折交叉验证。
cv_scores = cross_val_score(rf_model, features, labels, cv=5, scoring='accuracy') print(f"CV Accuracy: {cv_scores.mean():.4f} (+/- {cv_scores.std()*2:.4f})")交叉验证的均值和方差能告诉你模型性能是否稳定。
4.3 模型优化与调参
基线模型有了,下一步就是优化。这里主要两个方向: 特征优化 和 模型超参数优化 。
-
特征优化 :根据随机森林输出的特征重要性,剔除重要性极低(如接近0)的特征,重新训练模型,观察性能变化。有时特征减少后,精度反而微升,因为模型更专注了。
-
超参数调优 :手动调参效率低,我们可以用网格搜索或随机搜索。
from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [100, 200, 300], 'max_depth': [10, 15, 20, None], 'min_samples_split': [2, 5, 10] } grid_search = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=3, scoring='accuracy', n_jobs=-1, verbose=1) grid_search.fit(X_train_scaled, y_train) print(f"Best parameters: {grid_search.best_params_}") print(f"Best CV score: {grid_search.best_score_:.4f}")注意 :网格搜索非常耗时,尤其是参数组合多的时候。可以先进行大范围粗调,确定最优区间后再精细调整。
verbose=1可以让你看到搜索进度。
5. 进阶探索与方案对比
当你的传统机器学习流水线已经调优到不错的状态后,为了提升论文的深度和广度,可以进行一些进阶探索。
5.1 尝试深度学习模型
我们可以构建一个简单的1D-CNN来作为对比。CNN能自动捕捉信号的局部模式。
from tensorflow.keras import layers, models
# 假设输入形状为 (window_size, num_channels),例如 (100, 3) 代表100个时间点,3个轴
model = models.Sequential([
layers.Input(shape=(100, 3)),
layers.Conv1D(filters=64, kernel_size=3, activation='relu'),
layers.MaxPooling1D(pool_size=2),
layers.Conv1D(filters=128, kernel_size=3, activation='relu'),
layers.GlobalAveragePooling1D(), # 替代Flatten,参数更少,对时序长度不敏感
layers.Dense(64, activation='relu'),
layers.Dropout(0.5), # 防止过拟合
layers.Dense(num_classes, activation='softmax')
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
history = model.fit(X_train_cnn, y_train, validation_split=0.2, epochs=50, batch_size=32, verbose=1)
关键点 :
GlobalAveragePooling1D比Flatten更适合可变长度输入,且能减少参数。- 一定要使用
Dropout和validation_split来监控过拟合。 - 深度学习模型在小数据集上容易过拟合,如果效果不如随机森林,不必气馁,可以尝试数据增强(如添加噪声、时间拉伸)或使用预训练模型(如果领域相关)。
5.2 集成学习与模型融合
如果单个模型性能遇到瓶颈,可以尝试集成学习。除了随机森林本身是一种Bagging集成,你还可以:
- Stacking :用几个不同的基模型(如SVM、KNN、决策树)的预测结果作为新特征,训练一个次级模型(元模型,如逻辑回归)来做最终预测。这往往能提升少许性能,但复杂度大增。
- Voting :对多个训练好的模型(如RF、SVM、CNN)的预测结果进行硬投票(少数服从多数)或软投票(平均概率)。
实操心得 :在竞赛中,如果时间有限, 优先把单个模型(如随机森林)做到极致 ,而不是追求复杂的集成。一个充分调优的RF通常已经非常强大。集成是锦上添花,而不是雪中送炭。
6. 结果分析与论文撰写要点
模型做完了,最后一步是把你的工作清晰、专业地呈现出来。论文是评审专家了解你工作的唯一窗口。
6.1 如何系统地展示结果
-
可视化是关键 :
- 特征重要性柱状图 :展示Top-20最重要的特征,这能直接证明你特征工程的有效性。
- 混淆矩阵热力图 :清晰展示模型的分类弱点,并针对性地进行分析(例如:“我们发现模型在‘上楼’和‘下楼’活动上存在12%的相互误判率,分析原因是两者在垂直方向的加速度模式非常相似,未来可考虑引入气压计数据来获取高度变化信息以进行区分。”)。
- 学习曲线 :绘制训练集和验证集的准确率/损失随训练轮次(对于深度学习)或树数量(对于RF)的变化图,用以判断模型是否过拟合或欠拟合。
- t-SNE/PCA降维图 :将高维特征降至2维或3维进行可视化,观察不同活动类别的样本在特征空间中是否能够被较好地区分开。这能从几何角度解释模型为什么有效。
-
量化对比 :制作一个清晰的表格,对比不同模型(如基线模型、调优后的RF、CNN)在测试集上的各项指标(准确率、宏F1、加权F1等)。
| 模型 | 准确率 | 宏平均F1 | 训练时间 | 备注 |
|---|---|---|---|---|
| 随机森林(基线) | 92.5% | 0.923 | 15s | 特征未选择 |
| 随机森林(调优后) | 95.8% | 0.956 | 18s | 使用网格搜索调参 |
| 1D-CNN | 94.2% | 0.940 | 2min | 存在轻微过拟合 |
6.2 论文核心章节逻辑
一篇好的数模论文,结构比文采更重要。
- 摘要 :用一段话浓缩整个工作:针对什么问题、用了什么方法(数据预处理、特征工程、XX模型)、得到了什么结果(关键指标)、有什么结论和亮点。务必精炼、包含所有关键信息。
- 问题重述与分析 :不要照抄题目,要用自己的话分析问题的背景、核心任务、难点和解决思路。
- 模型假设与符号说明 :列出合理的假设(如“假设传感器佩戴位置固定”、“忽略轻微的环境电磁干扰”),并定义文中用到的主要数学符号。
- 数据处理与特征工程 : 这是最能体现工作量的部分 。详细说明每一步处理的理由(为什么滤波?为什么用这个窗口大小?为什么提取这些特征?)。配上关键代码片段或流程图。
- 模型建立与求解 :介绍所选模型的原理(如随机森林的基本思想)、为什么适合本问题、以及具体的实现和调参过程。可以简述对比的其他模型。
- 结果分析与验证 :展示上一步中的各种图表和表格,并配以深入的分析。 不要只说“准确率高”,要分析“为什么高”以及“为什么在这里会出错” 。
- 模型评价与推广 :客观评价自己模型的优点(精度高、鲁棒性好)和缺点(对某类活动识别差、计算成本等),并提出可行的改进方向(如融合更多传感器、使用更复杂的网络结构)。这部分体现了你的批判性思维。
- 参考文献与附录 :规范引用,附录可以放核心代码。
最后的叮嘱 :数学建模竞赛是团队合作,也是时间管理艺术。合理分工(一人主攻建模与算法、一人负责数据处理与实验、一人专注论文写作与图表)、定期同步、留出足够时间撰写和修改论文,这些和算法本身一样重要。人类活动分类这道题就像一个微缩的AI产品开发流程,走完这一遍,你对如何用数据驱动的方法解决一个实际问题,会有完全不一样的理解。希望这份超详细的拆解能帮你少走弯路,祝你在比赛中取得好成绩!
更多推荐

所有评论(0)