1. 人体活动识别问题概述

人体活动识别(Human Activity Recognition, HAR)是一项通过分析传感器数据来识别人体当前活动的技术。这项技术在健康监测、智能家居、运动分析等领域有着广泛应用。典型的应用场景包括:

  • 老年人跌倒检测
  • 健身动作识别与计数
  • 工作场所安全监测
  • 日常活动模式分析

传统方法依赖手工提取特征,需要领域专家设计复杂的特征工程。而现代深度学习方法能够自动学习特征表示,大大降低了技术门槛。本教程将基于一个标准数据集,展示如何应用深度学习处理这类时间序列分类问题。

2. 数据集详解与准备

2.1 数据集来源与特点

我们使用的是巴塞罗那大学Casale等人收集的"Activity Recognition from Single Chest-Mounted Accelerometer"数据集。该数据集包含15名受试者的胸部加速度计数据,采样频率为52Hz(每秒52个数据点)。

数据集特点:

  • 数据格式:CSV文件(每个受试者一个文件)
  • 数据列:时间戳、x轴加速度、y轴加速度、z轴加速度、活动标签
  • 活动类别:7种明确活动+1类"无活动"

2.2 数据加载与预处理

加载数据时需要注意几个关键点:

  1. 文件排序问题:Python的listdir()返回的文件顺序可能与预期不同
  2. 内存管理:大数据集需要分块加载
  3. 数据标准化:不同受试者的数据尺度可能不同
import os
import numpy as np
import pandas as pd

def load_har_dataset(data_dir):
    """加载HAR数据集"""
    subjects = []
    for filename in sorted(os.listdir(data_dir), key=lambda x: int(x.split('.')[0])):
        if not filename.endswith('.csv'):
            continue
        filepath = os.path.join(data_dir, filename)
        # 使用chunksize处理大文件
        df_chunks = pd.read_csv(filepath, header=None, chunksize=10000)
        for chunk in df_chunks:
            # 移除时间戳列,保留xyz和标签
            values = chunk.values[:, 1:].astype(np.float32)
            subjects.append(values)
    return subjects

提示:在实际应用中,建议对每个受试者的数据单独进行标准化处理,消除个体差异带来的影响。

3. 数据探索与可视化

3.1 单受试者活动轨迹分析

通过可视化单个受试者的数据,我们可以直观理解不同活动对应的加速度模式:

import matplotlib.pyplot as plt

def plot_subject_activities(subject_data, subject_id):
    plt.figure(figsize=(12, 8))
    activities = ['无活动','电脑工作','站立行走','站立','行走','上下楼梯','边走边说','站立说话']
    
    for i in range(4):  # 3轴加速度+活动标签
        plt.subplot(4, 1, i+1)
        if i < 3:
            plt.plot(subject_data[:, i], label=['X','Y','Z'][i])
            plt.legend()
        else:
            # 绘制活动标签
            for act in np.unique(subject_data[:, 3]):
                mask = subject_data[:, 3] == act
                plt.scatter(np.where(mask)[0], [act]*sum(mask), 
                           label=activities[int(act)], s=1)
            plt.yticks(range(8), activities)
        plt.title(f"受试者{subject_id} - {['X轴','Y轴','Z轴','活动'][i]}加速度")
    plt.tight_layout()
    plt.show()

3.2 活动持续时间分布

分析不同活动的持续时间分布有助于理解数据平衡性:

def plot_activity_durations(subjects):
    activities_duration = {i:[] for i in range(8)}
    freq = 52  # 采样频率
    
    for subject in subjects:
        for act in range(8):
            duration = np.sum(subject[:,-1]==act)/freq/60  # 转换为分钟
            activities_duration[act].append(duration)
    
    plt.figure(figsize=(10,6))
    plt.boxplot(activities_duration.values(), labels=activities.values())
    plt.ylabel('持续时间(分钟)')
    plt.title('各活动持续时间分布')
    plt.xticks(rotation=45)
    plt.show()

从分析中我们发现:

  • "站立说话"和"电脑工作"数据量最多
  • "上下楼梯"和"边走边说"数据较少
  • 需要考虑类别不平衡问题

4. 特征工程与数据准备

4.1 滑动窗口处理

时间序列分类通常采用滑动窗口方法:

def create_sliding_windows(data, window_size=52, overlap=0.5):
    """创建滑动窗口"""
    windows = []
    labels = []
    step = int(window_size * (1 - overlap))
    
    for i in range(0, len(data) - window_size, step):
        window = data[i:i+window_size, :3]  # 只取xyz数据
        label = stats.mode(data[i:i+window_size, 3])[0][0]  # 取窗口内众数
        windows.append(window)
        labels.append(label)
    
    return np.array(windows), np.array(labels)

4.2 特征提取选项

除了原始时序数据,还可以提取多种特征:

  1. 时域特征:

    • 均值、方差
    • 过零率
    • 峰值计数
  2. 频域特征:

    • FFT系数
    • 功率谱密度
  3. 统计特征:

    • 偏度、峰度
    • 四分位距
from scipy import stats
from scipy.fft import fft

def extract_features(window):
    """从窗口数据提取特征"""
    features = []
    for axis in range(3):  # 对每个轴处理
        x = window[:, axis]
        
        # 时域特征
        features.extend([np.mean(x), np.std(x), np.max(x), np.min(x)])
        
        # 频域特征
        fft_vals = np.abs(fft(x))[:len(x)//2]
        features.extend([np.mean(fft_vals), np.max(fft_vals)])
        
        # 统计特征
        features.extend([stats.skew(x), stats.kurtosis(x)])
    
    return np.array(features)

5. 建模方法比较

5.1 传统机器学习方法

传统方法需要手工设计特征:

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

# 特征工程
X = np.array([extract_features(w) for w in windows])
y = labels

# 随机森林模型
rf = RandomForestClassifier(n_estimators=100)
scores = cross_val_score(rf, X, y, cv=5)
print(f"交叉验证准确率: {np.mean(scores):.2f} (±{np.std(scores):.2f})")

5.2 深度学习方法

深度学习可以直接处理原始时序数据:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense

def build_cnn_model(input_shape, n_classes):
    model = Sequential([
        Conv1D(64, 3, activation='relu', input_shape=input_shape),
        MaxPooling1D(2),
        Conv1D(128, 3, activation='relu'),
        MaxPooling1D(2),
        Flatten(),
        Dense(100, activation='relu'),
        Dense(n_classes, activation='softmax')
    ])
    model.compile(optimizer='adam',
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    return model

# 数据准备
X = windows.reshape(windows.shape[0], windows.shape[1], 3)  # (样本, 时间步, 特征)
y = labels

# 构建模型
model = build_cnn_model((52, 3), 8)
history = model.fit(X, y, validation_split=0.2, epochs=20, batch_size=32)

6. 实际应用建议

6.1 数据不平衡处理

针对数据不平衡问题,可以采取:

  • 类别加权损失函数
  • 过采样少数类
  • 数据增强(添加噪声、时间扭曲)
from sklearn.utils.class_weight import compute_class_weight

class_weights = compute_class_weight('balanced', classes=np.unique(y), y=y)
class_weight_dict = dict(enumerate(class_weights))

model.fit(X, y, class_weight=class_weight_dict, ...)

6.2 模型优化方向

  1. 架构选择:

    • CNN:适合捕捉局部模式
    • LSTM:适合时序依赖
    • CNN+LSTM:混合架构
  2. 超参数调优:

    • 窗口大小
    • 网络深度
    • 学习率
  3. 正则化策略:

    • Dropout
    • BatchNorm
    • 早停法

6.3 部署注意事项

实际部署时需要考虑:

  1. 实时性要求:选择适当轻量模型
  2. 传感器校准:确保数据一致性
  3. 个性化适应:增量学习或微调
  4. 能耗优化:边缘设备部署方案

7. 扩展与进阶

7.1 多模态数据融合

结合其他传感器数据提升性能:

  • 陀螺仪数据
  • 心率监测
  • 环境传感器

7.2 迁移学习应用

利用预训练模型:

  • 在大规模HAR数据集上预训练
  • 在小规模目标数据上微调

7.3 自监督学习

减少标注依赖:

  • 对比学习(SimCLR)
  • 掩码预测(Masked Modeling)
  • 时序一致性学习

在实际项目中,我发现数据质量往往比模型选择更重要。确保传感器正确佩戴、数据采集环境稳定,这些因素对最终性能的影响可能超过模型架构的改进。另外,针对特定应用场景设计适当的后处理逻辑(如活动平滑)也能显著提升用户体验。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐