谷歌SensorFM生理基础模型深度解析:500万人、1万亿分钟,穿戴设备的ImageNet时刻

📅 2026/7/13 15:27:08
谷歌SensorFM生理基础模型深度解析:500万人、1万亿分钟,穿戴设备的ImageNet时刻
一、引言:可穿戴健康数据的"大型基础模型"时代2026年7月9日,Google Research正式发布SensorFM——面向可穿戴健康数据的大型传感器基础模型。这不是又一款"健康监测算法",而是一个真正的"生理基础模型":基于全球500万参与者的超过1万亿分钟(约20亿小时)多模态传感器数据,通过自监督掩码重建预训练,学习通用的生理表征。SensorFM的发布,被业界视为可穿戴健康领域的"ImageNet时刻"——就像2012年AlexNet在ImageNet上点燃深度学习革命一样,SensorFM为可穿戴健康数据的AI分析建立了一个通用的基础模型范式。从此,健康监测不再需要为每个生理指标单独构建定制化的监督流水线,一个模型就能覆盖心血管、代谢、睡眠、心理健康等六大类35项健康预测任务。二、数据规模:前所未有的"生理数据宇宙"2.1 数据集构成SensorFM的预训练语料库规模令人震撼:SensorFM预训练数据集: ├─ 参与者规模:500万人(已授权同意) ├─ 数据采集周期:2024年9月 - 2025年9月(12个月) ├─ 地理覆盖:100+国家、美国50个州 ├─ 设备覆盖:20+种Fitbit和Pixel Watch型号 ├─ 每人采样:数周数据 ├─ 总数据量:20亿小时(1万亿分钟) │ ├─ 传感器模态(5种): │ ├─ PPG(光电容积脉搏波):心率、心率变异性、血氧饱和度 │ ├─ 加速度计:运动、步数、活动强度 │ ├─ EDA(电皮肤活动):皮肤电导、压力反应 │ ├─ 皮肤温度:体温、昼夜节律 │ └─ 高度计:海拔变化、楼层攀爬 │ └─ 聚合特征:34个一分钟级聚合特征 ├─ 24小时窗口的心率曲线 ├─ 睡眠阶段分布(深睡/浅睡/REM) ├─ 活动强度分级 ├─ 皮肤电导变化趋势 └─ 体温昼夜节律这是目前公开的最大规模、最多样化的可穿戴数据集。相比之下,此前最大的公开可穿戴数据集(如UK Biobank的加速度计数据)仅有约10万参与者,数据量相差50倍。2.2 数据质量挑战真实世界可穿戴数据面临的核心挑战是数据缺失——传感器因充电周期、设备取下、省电模式、传感器开关等原因,几乎不可能获得完整连续的数据流。传统方法要么插补缺失值(引入偏差),要么丢弃不完整窗口(浪费数据)。SensorFM通过Adaptive and Inherited Masking(AIM)机制,将真实世界的缺失视为一种自然的"掩码模式",直接从不完整记录中学习,使模型天生具备缺失感知能力。三、技术架构:AIM掩码自编码器3.1 模型架构SensorFM基于LSM-2(Large Sensor Model 2)架构的掩码自编码器框架:SensorFM模型架构: ┌──────────────────────────────────────────────────┐ │ 输入层(传感器数据) │ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ │ │ │ PPG │ │加速度计 │ │ EDA │ │皮肤温度 │ │ │ │ 34特征 │ │ 34特征 │ │ 34特征 │ │ 34特征 │ │ │ └───┬────┘ └───┬────┘ └───┬────┘ └────┬─────┘ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────┐ │ │ │ Adaptive and Inherited Masking │ │ │ │ ┌────────────────────────────────────────┐ │ │ │ │ │ 真实缺失片段 → 继承掩码Token │ │ │ │ │ │ 人工掩码片段 → 随机掩码Token │ │ │ │ │ │ 两者等价处理 → 统一掩码表示 │ │ │ │ │ └────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────┐ │ │ │ Transformer Encoder │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ │ │Layer1│→│Layer2│→│Layer3│→...→│LayerN│ │ │ │ │ │ 自注意 │ 前馈 │ 掩码 │ │ 输出 │ │ │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ │ └──────────────────────────────────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────┐ │ │ │ 重建头(Reconstruction Head) │ │ │ │ 目标:最小化掩码区域的重建均方误差(MSE) │ │ │ └──────────────────────────────────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────┐ │ │ │ 通用生理表征(Encoder Output) │ │ │ │ 可迁移至:心血管/代谢/睡眠/心理健康/... │ │ │ └──────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘3.2 AIM机制的核心创新AIM(Adaptive and Inherited Masking)的关键设计是:将真实设备缺失与人工掩码视为等价,让模型直接从不完整记录中学习。""" SensorFM AIM掩码机制实现 """importnumpyasnpimporttorchimporttorch.nnasnnimporttorch.nn.functionalasFfromtypingimportTuple,OptionalclassAdaptiveInheritedMasking(nn.Module):""" Adaptive and Inherited Masking (AIM) 将真实缺失与人工掩码统一处理 """def__init__(self,mask_token_id:int=1,mask_ratio:float=0.4,min_mask_span:int=3,max_mask_span:int=30):super().__init__()self.mask_token_id=mask_token_id self.mask_ratio=mask_ratio self.min_mask_span=min_mask_span self.max_mask_span=max_mask_spandefdetect_real_missing(self,x:torch.Tensor)-torch.Tensor:""" 检测真实数据中的缺失片段 返回值:缺失掩码(1=缺失,0=存在) """# 多种缺失信号is_nan=torch.isnan(x)# NaN值is_zero_run=self._detect_zero_runs(x)# 连续零值(设备取下)is_flat=self._detect_flat_signal(x)# 平坦信号(传感器关闭)# 合并缺失信号missing_mask=is_nan|is_zero_run|is_flatreturnmissing_mask.float()def_detect_zero_runs(self,x:torch.Tensor,min_run:int=5)-torch.Tensor:"""检测连续零值运行"""is_zero=(x==0).float()# 使用卷积检测连续零值kernel=torch.ones(1,1,min_run)conv=F.conv1d(is_zero.unsqueeze(1),kernel,padding=min_run//2)return(conv=min_run).squeeze(1)def_detect_flat_signal(self,x:torch.Tensor,threshold:float=0.001)-torch.Tensor:"""检测平坦信号(传感器关闭)"""diff=torch.abs(x[:,:,1:]-x[:,:,:-1])flat=torch.cat([torch.zeros_like(diff[:,:,:1]),(diffthreshold).float()],dim=-1)returnflatdefgenerate_artificial_masks(self,batch_size:int,seq_len:int,device:torch.device)-torch.Tensor:""" 生成人工掩码(用于自监督训练) 使用块掩码(block masking)策略 """mask=torch.zeros(batch_size,1,seq_len,device=device)target_mask_ratio=self.mask_ratioforiinrange(batch_size):masked_count=0total=seq_lenwhilemasked_count/totaltarget_mask_ratio:span_len=np.random.randint(self.min_mask_span,self.max_mask_span+1)start=np.random.randint(0,seq_len-span_len+1)# 避免重复掩码ifmask[i,0,start:start+span_len].sum()==0:mask[i,0,start:start+span_len]=1masked_count+=span_len# 随机抖动掩码比ifnp.random.random()0.3:extra=np.random.randint(0,int(seq_len*0.2))indices=torch.where(mask[i,0]==0)[0]iflen(indices)0andextra0:extra_idx=indices[torch.randperm(len(indices))[:extra]]mask[i,0,extra_idx]=1returnmaskdefapply_masking(self,x:torch.Tensor,mask_token:torch.Tensor)-Tuple[torch.Tensor,torch.Tensor]:""" 应用统一掩码(真实缺失 + 人工掩码) 参数: x: 输入数据 [B, C, T] mask_token: 掩码Token嵌入 [1, 1, D] 返回: masked_x: 掩码后的数据 unified_mask: 统一掩码(真实缺失=1,人工掩码=1,未掩码=0) """# 检测真实缺失real_missing=self.detect_real_missing(x)# 生成人工掩码B,C,T=x.shape artificial_mask=self.generate_artificial_masks(B,T,x.device)# 统一掩码:合并真实缺失和人工掩码unified_mask=(real_missing+artificial_mask).clamp(0,1)# 用掩码Token替换被掩码位置masked_x=x.clone()mask_broadcast=unified_mask.expand(-1,C,-1)masked_x[mask_broadcast0]=mask_tokenreturnmasked_x,unified_maskclassSensorFMModel(nn.Module):""" SensorFM模型主体 """def__init__(self,input_dim:int=34,hidden_dim:int=768,num_layers:int=12,num_heads:int=12,max_seq_len:int=1440):# 24小时 × 60分钟super().__init__()self.input_proj=nn.Linear(input_dim,hidden_dim)self.pos_embedding=nn.Parameter(torch.randn(1,max_seq_len,hidden_dim)*0.02)self.mask_token=nn.Parameter(torch.zeros(1,1,hidden_dim)