首页
/ RuView ruvllm CSI 传感模型训练管线实战:从 .csi.jsonl 采集数据到多格式边缘部署模型的五阶段完整流程

RuView ruvllm CSI 传感模型训练管线实战:从 .csi.jsonl 采集数据到多格式边缘部署模型的五阶段完整流程

2026-09-07 13:59:12作者:明树来

导读

本文基于 RuView 仓库 ADR-071-ruvllm-training-pipeline.md(技术架构决策记录)展开,系统讲解如何用 @ruvector/ruvllm(v2.5.4,Node.js 原生运行)把 ESP32 节点采集的 WiFi CSI 数据(.csi.jsonl 帧)训练成可用于存在检测(presence detection)、活动分类(activity classification)与生命体征估计(vital sign estimation)的可部署模型。文中给出五阶段训练管线的完整配置参数、TurboQuant 量化、LoRA 逐节点微调、EWC 持续学习、SafeTensors/RVF 导出格式,并补充讲解仓库内 train-ruvllm.jsbenchmark-ruvllm.js 与无相机监督扩展脚本 train-camera-free.js 的源码级实现细节。读完本文,你可以完整掌握从原始 CSI 录制到模型量化、导出与基准测试的整条实操链路。

背景与决策动机:为什么选 ruvllm 而非 PyTorch

该 ADR 面向 WiFi 人体感知项目(RuView 的 wifi-densepose 方向):需要把 ESP32 节点采集的 CSI 数据转换为可部署模型。此前 ADR-070 建立了数据采集/自监督预训练协议,ADR-069 确立了 Cognitum Seed(Pi Zero 2W 边缘盒子)作为推理目标,而缺失的正是“原始 CSI 录制 → 可部署模型”之间实际训练、精调、量化与导出的环节。

ADR 通过对比表格论证选型。表中的数据为项目决策记录原文,其中部分(如“M4 Pro performance”)标注来自 ruvllm 规格说明:

准则 ruvllm PyTorch ONNX Runtime
运行时依赖 仅 Node.js Python + CUDA + pip C++ runtime
安装体积 约 5 MB(npm) 约 2 GB(torch+cuda) 约 50 MB
SONA 自适应 <1ms 原生 N/A N/A
量化 2/4/8-bit TurboQuant INT8/FP16(独立工具) 仅 INT8
LoRA 微调 内置 LoraAdapter 需 PEFT 库 N/A
EWC 保护 内置 EwcManager 手动实现 N/A
SafeTensors 导出 原生 SafeTensorsWriter 依赖 safetensors 库 N/A
对比学习训练 内置 ContrastiveTrainer 手动 triplet loss N/A
边缘部署 ESP32、Pi Zero、浏览器 仅 GPU 服务器 ARM(有限)
M4 Pro 性能 88–135 tok/s 原生 约 30 tok/s(MPS) 约 50 tok/s
生态集成 RuVector、Cognitum Seed 独立 独立

核心决策:使用 ruvllm 的 ContrastiveTrainerTrainingPipelineLoraAdapterEwcManagerSafeTensorsWriterModelExporter 完成 CSI 模型全生命周期训练。零 Python 依赖意味着训练与推理可以跑在与 Cognitum Seed 推理引擎相同的 Node.js 运行时上。

仓库源码印证了这一点:train-ruvllm.jsvendor/ruvector/npm/packages/ruvllm/src/ 分别 require 了 contrastive.js(ContrastiveTrainer、tripletLoss、infoNCELoss、computeGradient)、training.js(TrainingPipeline)、lora.js(LoraAdapter/LoraManager)、sona.js(EwcManager、SonaCoordinator)与 export.js(SafeTensorsWriter/ModelExporter)。vendor 目录是 git submodule 管理(见 vendor/README.md),ruvllm 即 ruvnet/ruvector 上游 npm 包之一,这也是 ADR 中“将包 vendoring 在 vendor 下以对冲 API 稳定性”风险对策的落点。

五阶段训练管线详解

管线按顺序执行五个阶段(Phase 1–5),源码将其实现在 main() 的 9 个步骤中(train-ruvllm.js,日志依次为 [1/9][9/9])。

Phase 1:对比预训练(Contrastive Pretraining)

目标:学习一个嵌入空间,使时间/空间上相似的 CSI 状态彼此接近,相异状态彼此远离。

  • 编码器结构:8 维 CSI 特征向量 → 64 维隐藏层(ReLU)→ 128 维 L2 归一化嵌入。
  • 损失函数:Triplet loss(margin=0.3)+ InfoNCE(temperature=0.07)。
  • 三元组(triplet)策略(源码 generateTriplets()train-ruvllm.js):
    • 时间正样本:1 秒内的相邻帧(同一环境状态);
    • 时间负样本:相隔 >30 秒的帧(不同状态)——注意源码 CONFIG 中 negativeWindowSec: 10.0,注释明确“从 30s 降到 10s,因为 120s 录制需要更紧的阈值”;
    • 跨节点正样本:不同 ESP32 节点同一时间戳(同一人、不同视角);
    • 跨节点负样本:不同时间戳 + 不同节点;
    • 硬负样本(hard negatives):运动能量(motion_energy)突变边界附近的帧。
  • 超参数:20 epochs、batch size 32、硬负样本比例 0.7。
  • 实现:源码中先由 ContrastiveTrainer.addTriplet() 装载三元组,再在其上对编码器 w2 输出层做梯度更新,计算 initial/final loss 与 improvement([4/9] 步骤),并用确定性洗牌保证可复现。

Phase 2:任务头训练(Task Head Training)

在冻结的 128 维嵌入之上训练各任务监督头:

  • Presence head:128 → 1(sigmoid)。源码中 createLabels()train-ruvllm.js)把 presenceScore > 0.3 作为正样本标签;专门的 PresenceHead 用 BCE 训练 30 个 epoch、lr=0.01(步骤 [5b/9]),推理判定阈值为 sigmoid 输出 >0.5。
  • Activity head:128 → 3(softmax:still/moving/empty)。由 motion_energy 阈值派生:presenceScore <= 0.1 → empty;motionEnergy > 2.0 → moving;否则 still。
  • Vitals head:128 → 2(线性:呼吸 BPM、心率 BPM)。目标做归一化:breathingBpm/30heartrateBpm/120
  • 实现TrainingPipeline.addData() + .train(),采用 cosine LR scheduler、early stopping(patience=5)、quality-weighted MSE loss。源码将任务向量编码为 128 维目标向量([presence(1), activity(3), vitals(2), padding(122)])输入训练,并以 validationSplit: 0.1 做校验(train-ruvllm.js)。

Phase 3:LoRA 逐房间/逐节点精调

为每个 ESP32 节点创建独立 LoRA adapter,在不遗忘基座模型的前提下适配特定房间/节点:

  • 配置:rank=4、alpha=8、dropout=0.1(源码 per-node 传 { rank: 4, alpha: 8, dropout: 0.1 })。
  • 逐节点训练:每个节点用自身数据以 0.5× 基础学习率微调(源码 learningRate: learningRate * 0.5,5 epochs,ewcLambda: 3000)。
  • 实现LoraManager.create() 为每个节点建 adapter,并把 adapter 传入 TrainingPipeline。ADR 估算 rank-4 adapter 每节点仅约 2KB 存储开销。

Phase 4:TurboQuant 量化

为边缘部署缩小模型体积,压缩率与目标设备对应关系如下:

位宽 压缩率 典型 RMSE 目标设备
8-bit 4x <0.001 Cognitum Seed(Pi Zero)
4-bit 8x <0.01 标准边缘推理
2-bit 16x <0.05 ESP32-S3 特征提取
  • 方法:逐张量的 uniform affine quantization(scale/zero-point)。源码 quantizeWeights()train-ruvllm.js)实现真实位打包:8-bit 每字节 1 权重、4-bit 每字节 2 权重(高低 nibble)、2-bit 每字节 4 权重,压缩率分别达到 4x/8x/16x。
  • 质量校验:原始 fp32 与反量化权重之间计算 RMSE(quantizationQuality() + dequantizeWeights())。训练脚本默认输出 q2/q4/q8 三种量化产物,--quantize-bits 决定默认变体。
  • 注意:该方法为训练后量化(post-training),不包含 QAT——ADR 将其列为负面后果,指出 2-bit 若质量退化未来可能需要引入 QAT。

Phase 5:EWC 持续学习整合

弹性权重巩固(Elastic Weight Consolidation),防止未来在新房间数据或更新的 CSI 条件下微调时发生灾难性遗忘:

  • Fisher 信息:由训练数据梯度计算。
  • Lambda:基座 2000,per-node 3000(与源码 taskPipeline ewcLambda: 2000、节点管线 ewcLambda: 3000 一致)。
  • 注册任务:基座预训练 + 每个 ESP32 节点各一项。源码在 [8/9]taskPipeline.getEwcManager() 依次 registerTask('csi-pretraining-v1', ...)registerTask('node-{id}-adaptation', ...)train-ruvllm.js)。

数据管线与 .csi.jsonl 输入格式

ADR 给出整体数据流:

.csi.jsonl files
    ↓
Parse frames: feature (8-dim), vitals, raw CSI
    ↓
Generate contrastive triplets (temporal, cross-node, hard negatives)
    ↓
Encode through CsiEncoder (8 -> 64 -> 128)
    ↓
Phase 1: ContrastiveTrainer (triplet + InfoNCE loss)
    ↓
Phase 2: TrainingPipeline (presence + activity + vitals heads)
    ↓
Phase 3: LoRA per-node refinement
    ↓
Phase 4: TurboQuant (2/4/8-bit quantization)
    ↓
Phase 5: EWC consolidation
    ↓
Export: SafeTensors, JSON config, RVF manifest, per-node LoRA adapters

输入格式与字段可从源码 loadCsiData()train-ruvllm.js)得到精确界定——每行一个 JSON 帧,按 frame.type 分流:

帧类型 关键字段 用途
feature timestampnode_idfeatures(8 维 float 数组)、rssiseq 对比训练/推理主输入
vitals timestampnode_idbreathing_bpmheartrate_bpmn_personsmotion_energypresence_scorerssi 生成任务头标签
raw_csi timestampnode_idsubcarriersiq_hexrssi 原始副载波(训练阶段主要统计计数)

8 维特征的语义可追溯到 ADR-069:presence score、motion energy、breathing rate(/30 clamp)、heart rate(/120 clamp)、phase variance、person count(/4 clamp)、fall detected、RSSI 归一化——维度范围统一在 0.0–1.0,与上文 vitals 归一化分母完全一致。

训练脚本另有三个值得注意的工程化细节:

  1. 数据量不足时实时补采:若特征帧总数 <500,脚本会尝试监听 UDP 5006 端口收集实时数据 60 秒(collectLiveData()),ESP32 不可达时优雅降级继续训练。
  2. 数据增强augmentData() 默认以 10× 扩充数据集,采用三种策略——时间插值(相邻帧按 0.2–0.8 系数混合,占 50%)、高斯噪声(sigma=0.02,占 30%)、跨节点插值(两节点同时间戳特征混合,占 20%),种子固定 7919 保证确定性。
  3. glob 解析:自带轻量 glob 实现(resolveGlob()),无需额外 npm 依赖。

导出格式一览

训练结束后输出目录包含(对应 ADR 表格 + 源码导出步骤):

格式 文件 消费者
SafeTensors model.safetensors HuggingFace 生态、通用推理
JSON config config.json 模型加载元数据
JSON model model.json Node.js 全量加载
量化二进制 quantized/model-q{2,4,8}.bin 边缘部署
逐节点 LoRA lora/node-{id}.json 房间级适配
RVF manifest model.rvf.jsonl Cognitum Seed ingest(ADR-069)
训练指标 training-metrics.json 仪表盘、CI 校验

源码在第 [9/9] 步骤通过 ModelExportertoSafeTensors()toHuggingFace()toJSON() 分别写出前三种格式(train-ruvllm.js),SafeTensors 张量按 encoder.w1/w2/b1/b2encoder.bn1/bn2_*presence_head.weights/bias 命名,并额外输出 presence-head.jsonbenchmark-ruvllm.js 直接加载;RVF 由 5 条 JSONL 记录组成(metadata / encoder / lora / ewc / quantization)。training-metrics.json 汇总了总耗时、数据统计、对比损失曲线、LoRA/EWC/量化各阶段指标与完整 CONFIG,可直接用于 CI 回归校验。

硬件目标与性能基线

ADR 规划的目标设备矩阵(期望值属规划口径,非实测数据):

设备 角色 量化 期望延迟
Mac Mini M4 Pro 训练(主) fp32 总时长 <5 min
Cognitum Seed Pi Zero 推理 4-bit / 8-bit 每帧 <10 ms
ESP32-S3 仅特征提取 2-bit(编码器权重) 每帧 <5 ms
浏览器(WASM) 可视化 4-bit 每帧 <20 ms

性能目标表同样以 ADR 为准(Measured 列为 TBD,尚待在目标硬件上回填):

指标 目标 Measured
训练时间(5,783 帧,M4 Pro) <5 min TBD
推理延迟(M4 Pro) <1 ms TBD
推理延迟(Pi Zero) <10 ms TBD
SONA 自适应 <1 ms <0.05 ms(ruvllm 规格)
存在检测准确率 >85% TBD
4-bit 质量损失(RMSE) <0.01 TBD
2-bit 质量损失(RMSE) <0.05 TBD

仓库提供配套基准脚本 benchmark-ruvllm.js 可回填上述指标:它测量单样本推理延迟的 Mean/P50/P95/P99、批量吞吐(batch 1/8/32/64)、fp32/8/4/2-bit 各级别的体积、压缩率与 RMSE、时间对与跨节点对的嵌入余弦相似度及正负样本分离 margin,并用 confusion matrix 计算存在检测的 accuracy/precision/recall/F1,最后以 --json 把结果写入模型目录的 benchmark-results.json。其中的跨框架对比表明确标注:PyTorch/ONNX/TFLite 数值是按 ruvllm 实测值估算的相对值,不是实测。

命令行实操

ADR 提供的用法均可直接套用(源码 parseArgs 还支持 -d/-o/-b/-e/-v 缩写):

# 用已采集的 CSI 数据训练(默认 epochs=20)
node scripts/train-ruvllm.js \
  --data data/recordings/pretrain-1775182186.csi.jsonl \
  --output models/csi-v1 \
  --epochs 20

# 训练 + 内置基准
node scripts/train-ruvllm.js \
  --data data/recordings/pretrain-*.csi.jsonl \
  --output models/csi-v1 \
  --benchmark

# 独立基准
node scripts/benchmark-ruvllm.js \
  --model models/csi-v1 \
  --data data/recordings/pretrain-*.csi.jsonl \
  --samples 5000 \
  --json

可调参数与默认值(见 train-ruvllm.jsparseArgs 与 CONFIG):--epochs(默认 20)、--batch-size(默认 32)、--lora-rank(默认 4)、--quantize-bits(默认 4)、--output(默认 models/csi-ruvllm);对比预训练固定超参为 margin=0.3、temperature=0.07、hardNegativeRatio=0.7、learningRate=0.001、正样本窗口 1s、负样本窗口 10s。基准脚本额外支持 --samples(默认 1000)与 --warmup(默认 100)。

需要提醒:命令中的 data/recordings/*.csi.jsonl 需为本地运行 ESP32 采集链路后产生的录制文件(见 ADR-069 的 UDP 5006 → bridge → Seed 链路),仓库不内置该录制数据;若样本不足 500,脚本会自动尝试从 UDP 5006 实时补采。

输出目录结构

models/csi-v1/
  model.safetensors          # SafeTensors(HuggingFace 兼容)
  config.json                # 模型配置
  model.json                 # 完整 JSON 模型状态
  presence-head.json         # 存在检测头权重(额外产物)
  model.rvf.jsonl            # Cognitum Seed 的 RVF manifest
  training-metrics.json      # 训练损失曲线、计时、配置
  contrastive/
    triplets.jsonl           # 对比训练三元组
    triplets.csv             # 供分析的 CSV
    embeddings.json          # 嵌入矩阵
  quantized/
    model-q2.bin             # 2-bit 量化(ESP32 边缘)
    model-q4.bin             # 4-bit 量化(Pi Zero 默认)
    model-q8.bin             # 8-bit 量化(高质量)
  lora/
    node-1.json              # ESP32 节点 1 的 LoRA adapter
    node-2.json              # ESP32 节点 2 的 LoRA adapter

无相机监督扩展(Camera-Free Supervision)

动机

传统 WiFi 姿态估计(WiFlow、Person-in-WiFi)依赖相机监督训练:采集 CSI 时用相机拍摄真实姿态作为 ground truth,模型学习“CSI → 姿态”的映射。这形成部署悖论——训练需要相机,而 WiFi 感知的意义恰恰在于不装相机。

scripts/train-camera-free.js 用 Cognitum Seed 的 10 路传感器信号 + 2 个 ESP32 节点替代相机监督,通过传感器融合生成弱标签。脚本头部注释(train-camera-free.js)逐条列出信号来源。

10 路监督信号(无相机)

# 信号 来源 提供
1 PIR 传感器 Seed GPIO 6 二值存在 ground truth
2 BME280 温度 Seed I2C 0x76 占用代理(人导致温度上升)
3 BME280 湿度 Seed I2C 0x76 呼吸确认/区域
4 跨节点 RSSI 2 个 ESP32 节点 粗略 XY 位置(差分三角定位)
5 生命体征稳定性 ESP32 CSI HR/BR 方差指示活动级别
6 时间 CSI 模式 ESP32 CSI 周期性=行走、平稳=静坐、平坦=空
7 kNN 聚类标签 Seed 向量库 嵌入空间的自然分组
8 边界脆弱度 Seed Stoer-Wagner 状态机切换检测(进出/活动)
9 干簧管 Seed GPIO 5 门开/关事件
10 振动传感器 Seed GPIO 13 脚步检测

扩展的训练阶段

无相机管线在基础 5 阶段之上扩展为 12 步([1/12][12/12]),核心新增阶段如下(ADR 原文的流程设计):

Phase 0: Multi-Modal Data Collection
  ├── UDP port 5006 → ESP32 CSI features + vitals
  ├── HTTPS → Seed sensor embeddings (45-dim, every 100ms)
  ├── HTTPS → Seed boundary/coherence (every 10s)
  └── Build synchronized MultiModalFrame timeline

Phase 1: Weak Label Generation
  ├── Presence: PIR || CSI_presence > 0.3 || temp_rising > 0.1°C/min
  ├── Position: RSSI differential → 5×5 grid (25 zones)
  ├── Activity: CSI variance + FFT periodicity → stationary/walking/gesture/empty
  ├── Occupancy: max(node1_persons, node2_persons) validated by temp
  ├── Body region: upper/lower subcarrier groups → which body part moves
  ├── Entry/exit: reed_switch + PIR transition + boundary fragility spike
  ├── Breathing zone: humidity change rate → person location
  └── Pose proxy: 5-keypoint coarse pose from RSSI + subcarrier asymmetry + vibration

Phase 2: Enhanced Contrastive Pretraining
  ├── Base triplets (temporal, cross-node, transition, scenario boundary)
  ├── Sensor-verified negatives: PIR=0 vs PIR=1 must differ
  ├── Activity boundary: before/after fragility spike must differ
  └── Cross-modal: CSI embedding ≈ Seed embedding for same state

Phase 3: Pose Proxy Training (5-keypoint)
  ├── Head: RSSI centroid between 2 nodes
  ├── Hands: per-subcarrier variance asymmetry (left/right from 2 nodes)
  ├── Feet: vibration sensor + RSSI ground reflection
  └── Skeleton physics constraints (anthropometric bone length limits)

Phase 4: 17-Keypoint Interpolation
  ├── Shoulders = 0.3 × head + 0.7 × hands
  ├── Elbows = midpoint(shoulder, hand)
  ├── Hips = midpoint(head, feet)
  ├── Knees = midpoint(hip, foot)
  ├── Face = derived from head position
  └── Iterative bone length constraint projection (3 iterations)

Phase 5: Self-Refinement Loop (3 rounds)
  ├── Run inference on all collected data
  ├── Keep predictions where temporal consistency confidence > 0.8
  ├── Use as pseudo-labels for next training round
  └── Decaying learning rate per round (diminishing returns)

源码实证:PoseDecoder 为 128 → 64(ReLU)→ 10 的两层 FC(5 关键点 × 2 坐标),对应函数 interpolateTo17Keypoints() 完成 5→17 关键点插值,另有骨骼长度约束投影;位置弱标签通过 RSSI 差分映射到 5×5=25 网格(xPos/yPos(rssiDiff+30)/60*4 量化到 0–4,见 train-camera-free.js);自精化轮数默认 3(--self-refine)。关键点遵循 COCO 17 点格式(poseKeypoints17: 17)。

用到的 Seed API 端点

端点 数据 采集频率
GET /api/v1/sensor/stream SSE 传感器读数 持续(100ms)
GET /api/v1/sensor/embedding/latest 45 维传感器嵌入 逐帧
GET /api/v1/boundary 脆弱度分数 每 10s
GET /api/v1/coherence/profile 时间相边界 每 10s
GET /api/v1/store/query kNN 相似度检索 按需
POST /api/v1/boundary/recompute 触发分析 状态切换时

优雅降级

无论 Cognitum Seed 是否可用,管线都能工作:

模式 信号 姿态质量
完整(Seed + 2 ESP32) 10 路信号 5 关键点训练 + 17 关键点插值
仅 CSI(2 ESP32) 3 路信号(RSSI、vitals、时间模式) 较粗的位置/活动
单节点 2 路信号(vitals、时间模式) 仅存在 + 活动

Seed 不可达时(--no-seed 或 API 连接失败)自动回退到 CSI-only 训练,输出格式保持一致(SafeTensors、HuggingFace、量化产物),只是标签质量下降。CLI 参数:--seed-url(默认 https://169.254.42.1:8443)、--seed-token(或环境变量 SEED_TOKEN)、--seed-collect-sec(默认 120)、--self-refine(默认 3)。

额外输出

与基础管线输出一致(SafeTensors + HuggingFace 兼容),另增:

文件 描述
pose-decoder.json 5 关键点姿态解码器权重
model.rvf.jsonl 追加 camera_free_supervision 记录
training-metrics.json 含弱标签统计与多模态三元组计数

代码确认:RVF manifest 会写入 JSON.stringify({ type: 'camera_free_supervision', signals: seedAvailable ? 10 : 3, ... }),如实记录本次训练用了多少路监督信号。

用法示例:

# 带 Seed 的完整管线
node scripts/train-camera-free.js \
  --data data/recordings/pretrain-*.csi.jsonl \
  --seed-url https://169.254.42.1:8443 \
  --output models/csi-camerafree-v1

# 仅 CSI(无 Seed)
node scripts/train-camera-free.js \
  --data data/recordings/pretrain-*.csi.jsonl \
  --no-seed \
  --output models/csi-camerafree-v1

# 带基准
node scripts/train-camera-free.js \
  --data data/recordings/*.csi.jsonl \
  --benchmark

后果、风险与权衡

正面效果

  • 零 Python 依赖:训练与推理全程 Node.js,省去训练机/部署目标上的 Python/CUDA/pip 依赖管理。
  • 一体化生命周期:对比预训练、任务头、LoRA 精调、EWC 巩固与量化在一份脚本、一个库内完成。
  • 边缘优先:2-bit 量化可把编码器压进 ESP32-S3;4-bit 适合 Cognitum Seed(Pi Zero)。
  • 持续学习:EWC 保护使模型可增量学习新房间数据而不遗忘旧模式。
  • 逐节点适配:rank-4 LoRA adapter 约 2KB/节点,房间级适配成本极低。
  • HuggingFace 兼容:SafeTensors 导出便于共享与跨框架加载。
  • 可复现:编码器播种初始化(seed=42)+ 确定性数据管线。

负面效果与风险

  • 无 GPU 加速:ruvllm 的 JS 训练循环不使用 GPU。对 CSI 这种小模型(8→64→128)可接受(M4 Pro 上约数秒级),但不适合扩展到大型视觉模型。
  • 简化反向传播:LoRA 反向传播与对比训练使用近似梯度更新,而非完整自动微分,不等价于 PyTorch autograd,但够用。
  • 仅训练后量化:无 QAT。4-bit/8-bit 质量损失可接受;2-bit 若质量退化未来需引入 QAT。
  • 质量上限风险:简化训练精度可能低于等价 PyTorch 模型。缓解:模型足够小收敛快、推理期 SONA 自适应可补偿、必要时仅训练环节换 PyTorch 而推理保留 ruvllm。
  • ruvllm API 稳定性:库处于活跃开发(v2.5.4)。缓解:vendoring 到 vendor/ruvector/npm/packages/ruvllm/。ruvllm 的能力边界(预测记忆与推理职责划分)可进一步参考 ADR-350-ruvector-predictive-memory-and-ruvllm-boundary.md

相关文档索引

登录后查看全文
热门项目推荐
相关项目推荐