无接触血压监测是长期健康管理中的一个难点。传统袖带血压计需要在特定姿势下充气测量,无法连续、无感地跟踪血压变化。三轴体震信号(Triaxial Bodyseismography)通过高灵敏度加速度计或地震传感器采集人体表面由心脏泵血、血液流动和身体微振动产生的机械信号,为连续血压估计提供了一条非接触、无约束的感知路径。这个方向的问题在于:体震信号并不是稳定的血压波形,它混入了呼吸、身体运动和传感器噪声,且不同受试者之间的形态差异非常明显。单纯把三轴信号放入卷积神经网络做回归,虽然能在某些数据上得到不错结果,却经常出现预测值偏离生理范围、相邻心跳结果不连贯、模型在小数据集上过拟合等难以解释的问题。物理约束深度学习试图在神经网络训练过程中加入血压形成机制、信号能量变化、相邻心搏一致性等人先验,让模型不仅拟合数据,还遵守生理规律。这篇文章会围绕这个思路,说明如何组织三轴体震数据的预处理,如何设计带物理约束的深度模型,如何训练、评估、排查,并给出一个最小示例结构和可复用检查清单。
1. 理解三轴体震信号与血压监测的关系
1.1 什么是三轴体震信号
体震信号记录的是心脏每次搏动时,血液在主动脉和周围血管内流动产生的反作用力在体表上的表现。传统心冲击图(Ballistocardiography, BCG)记录的是人在固定床面或秤体上的整体反作用力,而三轴体震信号更侧重用加速度传感器分别记录上下、左右、前后三个方向的微振动分量。三个方向都能提供信息:上下轴对应心室射血时对体表的垂直反作用力,左右和前後轴可以捕捉主动脉弯曲、血流转向产生的横向力。相比单轴信号,三轴信号能更完整地表示心动周期中的机械事件,但同时也带来更多噪声和复杂维度。
在实际采集系统中,传感器可以放在胸部、背部、床垫下方、座椅下方,也可以做成可穿戴贴片。核心是传感器与人体之间有稳定接触,或者通过床垫、座椅等介质传递力学信号。三轴加速度计的采样频率一般需要在 100 Hz 到 1000 Hz 之间,覆盖心搏信号的主要频率范围。采样频率过低会丢失脉搏波形细节,过高则产生大量冗余数据,工程上常用 250 Hz 或 500 Hz。
体震信号直接表现为随时间变化的三轴加速度或速度波形。由于每个心动周期大约 0.6 到 1.2 秒,一段 10 秒的信号中可能包含 8 到 16 个心搏周期。深度学习模型要处理的不是单一波形,而是一段包含多个心搏的信号片段,以及对应的血压标签。
1.2 血压与心脏机械活动的关系
每次心脏收缩时,左心室将血液泵入主动脉,血液对动脉壁产生压力,这就是血压的来源。射血过程会让主动脉根部和周围组织产生位移和振动,这种振动通过组织传导到体表,被传感器记录。因此,体震信号中的许多特征与血压有间接或直接关系。
例如,心脏射血开始到脉搏波到达远端位置的时间差,与血压水平相关。血压越高,动脉壁张力越大,脉搏波速度越快,到达时间越短。早期研究经常把脉搏波传播时间(Pulse Transit Time, PTT)作为无袖带血压估计的基础。但 PTT 需要同步心电信号和远端脉搏信号,三轴体震信号里没有明确的心电 R 波,只能通过机械事件来估计射血开始时间。类似地,心输出量每搏输出量增大时,体震信号的波峰幅值通常也会增大,这种机械信号能量与每搏输出之间的关联,也可以作为物理约束的来源。
生理规律还体现为时序一致性:一个连续时间内,血压不会发生跳变性突变。相邻两次心搏的血压虽然有波动,但波动范围有限。深度学习模型如果独立处理每个片段而忽略相邻心搏的关系,可能输出前后矛盾的值。物理约束就是把这些规律写进模型设计或损失函数里,让输出更符合生理常识。
1.3 为什么需要使用物理约束深度学习模型
纯深度学习血压估计存在的问题很典型:
第一,数据量不足时,模型容易学到与血压无关的噪声模式。体震信号受姿势、呼吸、传感器放置位置影响很大,模型可能把受试者姿势变化误认为血压变化。
第二,生理机制复杂,但可用的标注血压有限。无创血压计通常每 5 到 15 分钟测量一次,而体震信号是连续采集的,两者之间有时间差,造成标注噪声。
第三,纯粹回归模型缺少可解释性。如果模型输出收缩压 250 mmHg,明显超出正常范围,纯黑盒模型很难说清为什么。
物理约束深度学习模型通过额外信息约束输出空间和训练过程,包括:
- 血压输出限制在生理可行区间。
- 损失函数中加入相邻心搏一致性。
- 信号能量特征与血压变化方向一致。
- 模型中间层联合预测与脉搏传播时间相关的辅助任务。
这些约束不是替代数据驱动,而是让模型在有限数据下仍保持合理的生理行为。
2. 数据采集与预处理:三轴体震数据的标准流程
2.1 传感器选择与布点位置
采集三轴体震信号需要确认传感器量程、灵敏度和采样率。常用传感器包括 MEMS 加速度计,量程在 ±2g 到 ±8g 之间,灵敏度需能分辨微小机械振动。更专业的方案是地震传感器,灵敏度可以达到高精度,但价格和数据处理成本也更高。学习环境可以先使用加速度计开发板,比如常见的 LIS3DH、ADXL345 或更高采样率的加速度计。
放置位置会影响信号质量。胸部或背部靠近心脏,信号强,但受呼吸运动影响大;床垫下方信号微弱但更平稳,适合睡眠监测场景。无论放哪里,都需要固定方式,避免传感器自身晃动。同时记录传感器的三轴方向定义,确保后续分析时轴顺序一致。
采集时应同步记录参考血压。参考血压用标准袖带血压计获得。推荐使用经过验证的上臂式电子血压计或医用级设备,测量时记录时间戳,便于和体震信号对齐。
2.2 数据同步与标注
信号同步是物理约束模型中最容易出错的一步。体震信号是连续流,而血压计测量是离散点。需要把时间戳统一到同一时钟,可以通过网络时间协议、硬件同步或离线插值实现。一个可行的做法:采集程序中同时记录传感器时间戳和血压计测量时间戳,然后在预处理阶段为每个信号样本生成一个标签。
如果某段血压测量完成后,受试者动作剧烈,这段信号就不能作为训练样本。更合理的标注方式是取血压测量开始前 10 秒和测量中的体震信号作为对应输入,但需要在日志中记录测量开始和结束时间,避免把测量前后的动作噪声混进去。
对于每个受试者,数据记录应包括:收缩压、舒张压、平均动脉压、心率、采样时间、测量姿势、传感器位置。这些元数据对后续数据清洗很重要。
2.3 预处理步骤:去噪、滤波、分段、规范化
三轴体震信号预处理没有完全固定的流程,但以下步骤是常见基线。
首先去除趋势和均值。使用高通滤波去除低频漂移,通常截止频率在 0.5 Hz 到 1 Hz,保留心搏相关的高频成分。高通滤波可以消除呼吸和姿势缓慢变化带来的基线漂移。然后使用低通滤波去除高频噪声,截止频率根据采样率设定,常用在 30 Hz 到 50 Hz。因为心搏机械信号能量集中在低频范围,过高的频率成分更多是噪声和共振。
滤波后需要分段。将连续信号切成固定长度片段,比如 6 秒、8 秒或 10 秒。片段太短会丢失完整的慢呼吸周期,太长会增加标签不确定性和计算成本。每个片段需要有对应的血压标签,如果标签来自前后时间点的测量,还要记录时间偏移。
规范化方式需要谨慎选择。归一化到 [0, 1] 或零均值单位方差都可以。但三轴信号的三个方向幅值范围可能不同,应分别计算,也可以统一归一化。测试时,使用训练集的均值和方差进行同样变换,不能在测试集上重新计算。
2.4 数据增强与数据集划分
体震数据的小样本问题非常突出。数据增强思路包括:
- 时域随机裁剪:从同一次测量中裁剪不同起始位置的片段。
- 幅度扰动:对三个轴施加小的随机缩放或偏移,模拟传感器灵敏度和耦合差异。
- 噪声叠加:加入小幅高斯噪声或模拟呼吸基线,提升鲁棒性。
- 方向翻转:上下轴的物理意义与重力方向相关,翻转可能导致语义改变,应谨慎使用。
数据集划分必须按受试者划分,而不是按片段划分。因为同一受试者的多个片段高度相关,如果交叉使用,会导致非常大的乐观偏差。典型做法是留出一个或多个受试者作为测试集,剩余受试者按 8:2 分为训练集和验证集。如果参加多中心数据或不同条件的测试,还要保证测试集包含不同姿势和不同时间段。
注意:因为血压个体差异很大,训练集和测试集如果包含同一个人的数据,模型很可能通过“记住”该受试者的数据模式来降低误差,但真实应用面对的是新用户,这会严重高估模型泛化性能。因此受试者划分是整个实验设计中最重要的一条边界。
3. 物理约束深度学习模型的设计
3.1 模型架构参考:一维卷积与时序编码
三轴体震信号本质是时间序列,建模时常用一维卷积提取局部形态特征,再使用时序编码器捕捉长程依赖。一个可复用的基线架构包含三个部分:
输入层接收形状为(batch, channels, time_length)的三轴信号,其中channels=3。使用多个一维卷积层提取信号局部波形,每个卷积层后加 BatchNorm 和 ReLU,然后使用 MaxPooling 降低频率维度。卷积核大小需要覆盖一个心搏周期的不同比例,例如在 250 Hz 采样率下,50 ms 到 200 ms 对应 12 到 50 个样本点,所以卷积核可取 16、32、64。为了避免过拟合,卷积层通道数不要一开始就很大,可以从 16 或 32 起步,逐渐增加到 64 或 128。
卷积层之后是一个全局时序编码器,可以用双向 LSTM 或 Transformer。LSTM 对长序列的序列顺序敏感,适合心跳周期较规律的数据;Transformer 并行度高,但需要相对位置编码或绝对位置信息。如果训练数据量较小,LSTM 更容易收敛。
模型输出为收缩压、舒张压两个值,或者输出收缩压、舒张压、平均动脉压三个值。输出层激活函数使用 ReLU 后加一个缩放,保证输出为正。还可以使用 sigmoid 乘以预设上限,比如sigmoid(x) * 220,将收缩压限制在 0 到 220 mmHg 之间。这种限制属于最简单的物理约束。
示例架构代码(PyTorch):
import torch import torch.nn as nn class PhysicalBCGModel(nn.Module): def __init__(self, in_channels=3, seq_len=2500, hidden_size=64): super().__init__() # 卷积特征提取 self.conv1 = nn.Conv1d(in_channels, 16, kernel_size=16, stride=2, padding=7) self.bn1 = nn.BatchNorm1d(16) self.conv2 = nn.Conv1d(16, 32, kernel_size=16, stride=2, padding=7) self.bn2 = nn.BatchNorm1d(32) self.conv3 = nn.Conv1d(32, 64, kernel_size=32, stride=2, padding=15) self.bn3 = nn.BatchNorm1d(64) self.relu = nn.ReLU() self.pool = nn.MaxPool1d(2) # 计算经过卷积和池化后的序列长度 # 这里简化,实际需要根据输入长度计算 self.lstm = nn.LSTM(input_size=64, hidden_size=hidden_size, num_layers=2, batch_first=True, bidirectional=True, dropout=0.2) # 输出头 self.head = nn.Sequential( nn.Linear(hidden_size * 2, 32), nn.ReLU(), nn.Dropout(0.2), nn.Linear(32, 2) ) def forward(self, x): # x shape: (batch, channels, seq_len) x = self.pool(self.relu(self.bn1(self.conv1(x)))) x = self.pool(self.relu(self.bn2(self.conv2(x)))) x = self.pool(self.relu(self.bn3(self.conv3(x)))) x = x.permute(0, 2, 1) # (batch, seq_len_after, channels) out, _ = self.lstm(x) out = out[:, -1, :] # 取最后一个时间步的输出 bp = self.head(out) # 限制在生理范围 sbp = torch.sigmoid(bp[:, 0]) * 220.0 dbp = torch.sigmoid(bp[:, 1]) * 160.0 return torch.stack([sbp, dbp], dim=1)这里使用sigmoid将输出限制在生理范围内,属于最基础的物理约束。实际项目中,序列长度、卷积核和网络深度需要根据你的采样率和片段长度调整。
3.2 物理约束如何嵌入:从输出限制到辅助任务
输出范围限制只是第一层约束。更复杂的物理约束包括:
心搏级血压变化限制:如果模型对每个片段内的心搏分别输出血压值,相邻心搏之间的收缩压变化不应超过某个阈值,比如 10 mmHg。可以通过正则项惩罚相邻心搏差值的最大值或方差。
信号能量和血压关联:体震信号的波峰能量越大,通常对应每搏输出量越高。可以在模型中间层加入一个辅助分支,预测信号包络能量,然后要求该能量与输出血压存在单调相关性。这种约束并不需要严格因果,而是趋势约束。
脉搏波传播时间辅助任务:如果片段中包含心脏机械活动起始点和远端反射点,可以用一个分支预测疑似 PTT,再把 PTT 作为特征输入到血压回归头部。训练时,如果没有真实 PTT 标签,可以设计一个自监督损失,例如要求 PTT 在相邻片段间平滑变化。
血压波形与真值周期一致:通过体震信号可以估计心搏周期,要求输出的血压值随时间变化的频率不超过心率范围。例如,10 秒内收缩压变化次数不能超过实际心搏次数。
这些约束可以写进损失函数,而不是全部依赖模型结构。损失函数一般形如:
total_loss = mse_loss(pred_bp, true_bp) + lambda_phys * physical_constraint_loss这里的lambda_phys是平衡因子,需要调参。初始时先让 MSE 占主导,再逐步增大物理约束权重。如果物理约束权重过大,可能导致模型只满足约束而忽略真实血压。
3.3 自定义损失函数:回归损失与生理一致性损失
下面是一个可运行的损失函数示例,包含三项:
- MSE 回归损失。
- 相邻片段输出变化惩罚:让连续两个片段预测的血压差不能过大。
- 范围惩罚:输出超出常见生理范围时额外惩罚,虽然 sigmoid 已经限制了范围,但可以增加更强的惩罚。
实际训练数据中,相邻片段是指同一次测量中按时间相邻的两个片段。损失函数的输入需要包含这些片段的索引或相邻关系。更简单的实现是只在同一批内对相邻样本计算平滑损失。
def loss_with_physical_constraints(pred_bp, true_bp, penalty_adjacent=0.5, range_weight=0.1): # pred_bp shape: (batch, 2) [SBP, DBP] # true_bp shape: (batch, 2) mse = torch.mean((pred_bp - true_bp) ** 2) # 相邻样本间血压变化惩罚:默认同一批内按顺序相邻 if pred_bp.size(0) > 1: diff_adj = torch.abs(pred_bp[1:] - pred_bp[:-1]).mean() else: diff_adj = torch.tensor(0.0, device=pred_bp.device) # 超出范围惩罚(双软阈值) low = torch.tensor([60.0, 30.0], device=pred_bp.device) high = torch.tensor([220.0, 130.0], device=pred_bp.device) below_low = torch.relu(low - pred_bp) above_high = torch.relu(pred_bp - high) range_penalty = (below_low.mean() + above_high.mean()) loss = mse + penalty_adjacent * diff_adj + range_weight * range_penalty return loss, {"mse": mse.item(), "adjacent": diff_adj.item(), "range_penalty": range_penalty.item()}这段代码中的相邻样本默认是 batch 中的顺序,实际数据加载时需要保证同一个人的连续片段被顺序放入 batch,并且 batch 中不要混过多人的边界。更严谨的做法是记录片段 id,只对同一 id 的相邻片段计算。示例代码用于说明思路,生产项目需要调整。
3.4 训练策略和超参数
训练这样的模型,建议使用 Adam 优化器,学习率先从1e-3开始,配合学习率调度器ReduceLROnPlateau。batch size 受显存限制,常见为 32 或 64。如果样本量小,batch size 也可以小到 8 或 16,但需要配合梯度裁剪。
卷积和 LSTM 都比较容易过拟合。可以在卷积层后加 Dropout,在 LSTM 后加 Dropout。早停(early stopping)是必须的,监控验证集 MAE,当连续 20 个 epoch 不下降时停止。
训练过程中需要把损失函数中的各项分开记录,方便观察物理约束项是否在合理范围。如果adjacent项从一开始就是 0,可能说明网络学会了输出常数,需要检查标签或模型。
还需要处理样本不均衡。血压数据中健康人群收缩压集中在 110 到 130 mmHg,高血压样本少会导致低血压区和高血压区误差大。可以在损失中按人群类别加权重,或采用分位数损失。
# 示例训练超参数 optimizer: Adam initial_lr: 0.001 batch_size: 32 epochs: 200 early_stopping_patience: 20 scheduler: ReduceLROnPlateau scheduler_factor: 0.5 scheduler_patience: 10 dropout: 0.2 lstm_hidden: 64 loss_adjacent_weight: 0.5 loss_range_weight: 0.1这里不要认为示例参数适用于所有数据。采样率、信号长度、数据量都会影响最优配置。应当把训练参数固定后,先跑通一轮,再逐步调参。
4. 模型训练与验证
4.1 训练步骤和代码实现
将数据加载、模型、损失函数组合成训练脚本。主要步骤如下:
- 读取全部受试者数据,按受试者划分 train/val/test。
- 对每个受试者的信号进行预处理,生成片段和标签。
- 构建 DataLoader,设置打乱和批量加载。
- 初始化模型、损失函数、优化器。
- 循环训练,每个 epoch 后在验证集上计算 MAE。
- 保存验证集最优模型。
下面提供一个最小 PyTorch 训练循环示例:
import torch from torch.utils.data import DataLoader, TensorDataset def train_model(model, train_loader, val_loader, epochs=100, lr=0.001): optimizer = torch.optim.Adam(model.parameters(), lr=lr) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode='min', factor=0.5, patience=10 ) best_mae = float('inf') for epoch in range(epochs): model.train() train_losses = [] for x_batch, y_batch in train_loader: optimizer.zero_grad() pred = model(x_batch) loss, loss_dict = loss_with_physical_constraints(pred, y_batch) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() train_losses.append(loss.item()) # 验证 model.eval() val_loss, val_mae = evaluate_model(model, val_loader) scheduler.step(val_loss) if val_mae < best_mae: best_mae = val_mae torch.save(model.state_dict(), 'best_model.pth') if (epoch + 1) % 10 == 0: print(f'Epoch {epoch+1}: train_loss={np.mean(train_losses):.4f}, ' f'val_mae={val_mae:.3f}, best_mae={best_mae:.3f}')这个示例为了简洁没有做早停和详细日志,实际项目需要添加。
4.2 模型评估指标和标准
血压估计模型的评估不能只看一个 MAE。常用指标包括:
- 收缩压和舒张压的平均绝对误差(MAE)
- 均方根误差(RMSE)
- 标准偏差(SD)
- 准确率:误差在 ±5 mmHg、±10 mmHg、±15 mmHg 内的占比
- Bland-Altman 分析:评估预测与真值的一致性
- AAMI 标准:通常要求平均误差在 ±5 mmHg 内、标准差不超过 8 mmHg。但不同数据集硬件条件下标准不同,需要参考具体研究指南。
如果模型输出收缩压总是和真值相差一个固定偏移,MAE 可能不大,但个体趋势可能是错的。因此应计算预测值与真值的皮尔逊相关系数,以及误差随真值血压变化的比例趋势。还要按受试者分组评估,查看模型是否在少数受试者上特别差,而不是只报告整体平均值。
一个典型评估函数:
import numpy as np def evaluate_predictions(pred_sbp, true_sbp, pred_dbp, true_dbp): sbp_mae = np.mean(np.abs(pred_sbp - true_sbp)) dbp_mae = np.mean(np.abs(pred_dbp - true_dbp)) sbp_rmse = np.sqrt(np.mean((pred_sbp - true_sbp) ** 2)) dbp_rmse = np.sqrt(np.mean((pred_dbp - true_dbp) ** 2)) sbp_10 = np.mean(np.abs(pred_sbp - true_sbp) < 10) * 100 dbp_10 = np.mean(np.abs(pred_dbp - true_dbp) < 10) * 100 return {"SBP MAE": sbp_mae, "SBP RMSE": sbp_rmse, "DBP MAE": dbp_mae, "DBP RMSE": dbp_rmse, "SBP within 10 mmHg": sbp_10, "DBP within 10 mmHg": dbp_10}评估时要注意不同血压水平的样本分布。如果数据集中高血压样本很少,模型可能整体 MAE 不错,但高血压区表现很差。此时可以按真值血压范围分组,分别报告误差。
4.3 结果验证流程与预期范围
完成训练后,需要做以下验证:
- 训练集和验证集的 loss 曲线是否下降稳定。
- 物理约束项数值是否合理变更。
- 测试集输出是否都在生理范围内。
- 随机抽几个受试者,画出真值血压和预测血压随时间变化的曲线,观察趋势。
- 检查是否存在某一轴信号对预测起决定作用。可以分别用单轴和多轴输入训练,对比指标。
合理的验证过程是:先用训练集和验证集调参,最后只在测试集上评估一次。如果反复在测试集上查看结果并调整模型,会引入选择偏差。
4.4 学习环境与生产环境的差异
在实验室环境中,数据是干净的,传感器固定,受试者行为受控。在真实生产环境中,需要考虑更多因素。
学习环境可以这样配置:
- 数据离线处理,忽略运动片段。
- 模型用单机 GPU 训练。
- 评估使用留出受试者测试集。
生产环境需要额外补充:
- 信号质量实时监测:计算信噪比、运动伪迹检测,质量差时输出“不可用”而不是乱报血压。
- 模型部署时使用 ONNX 或 TensorRT 加速,确保推理延迟满足实时要求。
- 需要个性化校准:每个新用户使用前可以先测量 1 到 3 次参考血压,用少量数据对模型进行微调,大幅提高个体准确性。
- 数据隐私:体震信号属于健康数据,存储和传输需要加密和权限控制。
- 监控与回滚:记录模型输出分布,如果出现连续异常,触发报警并回滚到旧版本。
下表列出学习环境和生产环境需要关注的差别:
| 环节 | 学习环境 | 生产环境 |
|---|---|---|
| 数据 | 已清洗、分段、带有标签 | 连续流式数据,需要实时分段和质量筛查 |
| 真值 | 离线配准袖带血压 | 可能需要定期采集参考血压做校准 |
| 推理 | 批量评估 | 流式推理,单条或短窗预测 |
| 模型更新 | 离线训练,保存 checkpoint | 灰度发布、回滚策略 |
| 异常处理 | 人工检查 loss 和指标 | 自动告警,标记低质量信号 |
| 隐私 | 实验环境下可控 | 访问控制、加密、脱敏 |
5. 常见问题排查:训练效果差或结果不可靠
5.1 数据同步或标注错误导致 loss 下降但指标很差
这是最常见的问题。现象是训练 loss 能正常下降,验证集 loss 也不高,但测试集 MAE 很大,或者误差集中在某些受试者上。
原因很可能是体震信号和血压标签在时间上没有对齐。例如,体震片段取自血压测量完成后的 10 秒,而血压计的放气过程还在进行中,测量值不代表这段时间的血压。
检查方式:
- 打印每个样本的时间戳和标签时间戳,检查间隔是否一致。
- 随机抽取数据对,人工看信号片段和标签是否符合该受试者的生理状态。
- 对同一受试者,对比相邻片段预测的血压变化,如果变化异常大,很可能标签错位。
解决办法是重新设计同步协议,记录传感器时间戳和血压计读数完成时间,确保片段完全落在血压测量窗口内或窗口后的稳定期。
5.2 信号质量差导致模型无法收敛
现象:loss 在某个值附近震荡,验证集 MAE 不下降。可能原因是信号中包含大量运动伪迹、传感器饱和、基线漂移。
检查方式:
- 画出原始三轴波形,观察是否存在大幅度尖刺或平台。
- 计算信号的高频能量比例。
- 查看传感器峰值是否触及量程上限。
解决方案:
- 增加运动检测算法,自动剔除运动污染片段。
- 对信号做中值滤波和带通滤波。
- 调整传感器固定位置或接触介质。
- 如果信号是在座椅上采集,可以尝试多个位置的传感器融合。
还要注意滤波参数是否合适。高通截止频率太低会保留呼吸噪声,太高会削弱心搏信号。可以看滤波后频谱,确认心搏谐波是否明显。
5.3 物理约束权重设置不当导致模型输出平滑但误差大
现象:预测曲线很平滑,相邻值差距很小,但和真值偏差大。这时可能是相邻样本惩罚权重penalty_adjacent过大,模型宁愿把所有输出拉到平均值,也不愿意拟合真实波动。
原因分析:血压在一天内是有波动的,在静息状态短时间内变化较小,但实验数据可能混合了运动前后的数据,导致相邻片段之间真实血压有较大差异。如果过度惩罚相邻差,就会抹平真实变化。
检查方式:
- 分别报告没有物理约束时的 MAE 和加入约束后的 MAE。
- 查看物理约束项的 loss 在训练结束时的值,确认它没有远大于回归 loss。
- 在验证集上统计真值血压相邻差分布,和模型相邻差分布对比。
解决方案:把penalty_adjacent从 0.1 逐步调大,观察验证集 MAE 变化。最优权重应该让相邻平滑项带来的偏差最小。也可以使用可学习的温度参数,让模型自动调整。
5.4 模型在小数据集上过拟合或欠拟合
现象:训练 loss 很低,验证 loss 很高,说明过拟合。或者训练 loss 和验证 loss 都高,说明欠拟合。
过拟合原因可能是模型参数量过大、数据量太小、没有按受试者划分或者没有充分数据增强。
检查方式:
- 看训练集样本数量和模型参数的比例,如果训练样本只有几千,模型参数有几十万,过拟合风险很高。
- 用测试集对训练样本做最近邻查找,或者看特征可视化是否明显按受试者聚类。
处理过拟合:
- 减少 LSTM 层数或隐藏单元数。
- 加大 Dropout。
- 使用更激进的数据增强。
- 增加物理约束损失,相当于正则化。
- 使用迁移学习,先在大规模其他生理信号上预训练,再微调。
欠拟合处理:
- 增加模型容量。
- 延长训练时间。
- 降低学习率或使用更合适的优化器。
- 检查归一化是否导致信号幅值过小。
6. 最佳实践与扩展方向
6.1 可复现实验检查清单
为了让结果可复现,以下列表很重要:
- 记录传感器型号、采样率、量程、放置位置和方向定义。
- 记录数据采集时间和每个受试者的测量条件。
- 明确划分受试者的随机种子,并在代码中固定。
- 保存预处理参数:滤波截止频率、片段长度、重叠率、归一化参数。
- 保存模型结构、超参数、优化器和损失权重。
- 每次实验记录训练集、验证集、测试集的 MAE 和 RMSE,并保存预测结果。
- 用固定随机种子初始化网络和 DataLoader,确保可重复。
如果在论文或博客中复现实验,还应公开代码和数据脱敏后的特征,方便他人验证。
6.2 发布前最佳实践与部署建议
发布模型前至少完成以下检查:
- 检查测试集是否与训练集无受试者重叠。
- 检查血压标签是否经过校准和过滤。
- 检查预测输出是否都在生理范围。
- 检查不同真值血压分层的误差是否可接受。
- 检查是否存在某一轴信号损坏导致模型异常输出。
部署时不要使用需要完整 GPU 的庞大模型,可以先蒸馏成小模型。模型的输入输出需要标准化,数据预处理逻辑要与训练时完全一致。比如训练时使用了 250 Hz 采样,生产输入如果只有 125 Hz,需要重采样。
对于实时监测,建议使用滑动窗口,每 2 到 5 秒输出一次估计值,并对输出做平滑滤波。但不应该直接对血压值做太强低通滤波,否则会掩盖真实的短时血压波动。可以使用中值滤波,窗口大小为 5,去除单个异常点。
6.3 后续研究:更完善的生理模型与多模态融合
物理约束深度学习的扩展方向包括:
- 把心血管模型参数作为可学习变量,比如动脉硬度、外周阻力,再通过模型生成压强波形。
- 联合估计心率、呼吸率、心输出量,让多任务学习互相促进。
- 将三轴体震信号与血氧饱和度、心电信号结合,使用注意力机制融合多模态信息。
- 在损失函数中加入基于流体力学方程的隐式先验,例如 Navier-Stokes 方程简化模型的残差。
- 在时域和频域同时建模,提取心搏谐波能量与血压变化的关系。
实际落地时可以分阶段:先验证模型在静息状态和固定姿势下的准确性,再逐步扩展到动作状态下。不要一开始就期望从日常活动数据中直接获得医学级血压,目前无袖带设备更多用于趋势监测而非单一读数诊断。
对新手来说,最有价值的练习是使用公开数据集或自采的少量数据,完成一个带有基本物理约束的血压估计模型。哪怕模型误差较大,也比直接跑通一个纯黑盒 CNN 更能理解这一问题的核心:数据质量、生理先验和模型评估。这部分能力会直接迁移到其他医学信号处理项目里。