Files
ts-as-modality/docs/superpowers/specs/2026-06-29-ts-as-modality-design.md
2026-06-29 21:16:52 +08:00

344 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 时序作为模态的多模态模型 · 设计规格
> **日期**2026-06-29
> **状态**:已通过设计评审,待写实现计划
> **背景**:基于 `MTSAD-技术深度调研报告.md`,沿用 ChatTS「时序作为新模态」路线,在消费级硬件上做可复现的实验性落地
> **目标读者**:算法工程师(实现者)、技术决策者(评审者)
---
## 1. 目标与约束
### 1.1 目标
构建一个把**多变量时序视为独立模态**、与文本/事件模态联合注入小型 LLM 的多模态模型,端到端任务是**时序问答与推理**(ChatTS 路线):以多变量时序为输入,支持自然语言问答、根因分析、异常解释等推理任务。
### 1.2 硬约束
| 约束 | 取值 | 影响 |
|------|------|------|
| 算力 | **单张 3060 12GB 消费级显卡** | 骨干 ≤0.5B;依赖 LoRA/QLoRA + 短上下文 + 小批量;TS 编码器单独轻量训练 |
| 模态范围 | 数值序列 + 变量属性 + 时间戳 + 事件 | 投影层需支持 TS token 与多类文本 token 并存;面向 AIOps 时序+事件联合推理 |
| 数据 | **合成数据为主** | 需要 CPU 离线合成管线;评测锚定真实 benchmark 防止合成过拟合 |
| 模型尺寸 | ≤0.5B(可更小) | 选用 Qwen2.5-0.5B-Instruct 作为骨干 |
### 1.3 非目标(YAGNI
- 不做从零预训练 LLM(成本不允许,且非本实验目的)
- 不做多卡分布式训练(单卡约束)
- 不追求学术 SOTA 排名,追求「在 12GB 上跑通、可复现、时序模态确有增益」的可验证结论
- 不做端侧部署优化(先验证方法)
---
## 2. 整体架构
### 2.1 数据流
```
多变量时序 [T×C]
│ Patchify (patch=P, stride=S, 通道独立)
Patches [(T/P)×d] ──► TS Encoder (2层Transformer) ──► Projector (Linear→LLM hidden)
▼ TS Tokens [(T/P)×h]
变量属性 / 时间戳文本 / 事件标记 / 指令问题 ──► Tokenize ──► 文本/事件 Tokens
[属性][时间戳][TS tokens][事件][问题] 拼接
LLM Backbone (Qwen2.5-0.5B, LoRA)
自然语言回答 (+ 可选 JSON 异常区间)
```
### 2.2 关键设计决策
| 项 | 选择 | 理由 |
|----|------|------|
| 骨干 LLM | Qwen2.5-0.5B-Instruct | 0.5B 中英均衡、有 Instruct 版、社区生态成熟;BF16 约 1GB,12GB 余量充足 |
| TS Encoder | 2 层 Transformerd=2564 头 | 够表达局部时序结构;参数约 4M,可与投影层一起全参训练 |
| Patch | P=8, stride=4(重叠),通道独立 | 重叠 patch 保局部连续;通道独立避免 C 增大失配;T=512 → 128 个 TS token |
| Projector | Linear(256→896) + LayerNorm | 把 TS 表示映射到 LLM 隐藏维(Qwen2.5-0.5B hidden=896),作为可学习 soft token 注入 |
| LLM 微调 | LoRA r=16 (q/v_proj) + 冻结其余 | 12GB 关键约束;可训练参数约 2-3M |
| 上下文 | ≤1024 token128 TS + ~500 文本/事件) | 12GB 显存友好;T=512 时间步足够覆盖 AIOps 局部窗口 |
| 输入拼接顺序 | `[属性][时间戳][TS tokens][事件][问题]` | 属性先给语义,时序居中,事件定位在对应时间,问题在末尾便于回答 |
### 2.3 与候选方案的对比(为什么选 Patch-Project + 轻量编码器)
- **方案 A(本设计)Patch-Project + 轻量编码器**:时序切 patch → 小编码器 → 线性投影成独立 TS token。真正的"时序作为模态",12GB 可训,patch 捕捉局部结构、变长友好。需训练编码器+投影层(已在显存预算内)。
- **方案 B 时序 TokenizationChronos 风格)**:数值量化成离散 token、扩展词表。最"时序即语言"但量化丢精度、异常细节易损;逐点 token 序列长,12GB 上下文吃紧。放弃。
- **方案 C ReprogrammingTime-LLM 风格)**:冻结 LLM 用文本原型重映射。参数最省但受限于已有文本嵌入流形、表达力弱,时序算不上"真新模态",复杂推理/事件联合弱。放弃。
---
## 3. 数据与合成管线
### 3.1 总体管线(全 CPU 离线)
```
① 属性采样 → ② 时序生成 → ③ 自动标注 → ④ 指令生成(Evol-Instruct) → ⑤ 回答生成
```
产物为 JSONL,每条含:`series` / `attributes` / `timestamps` / `events` / `instruction` / `answer` / `labels`
### 3.2 合成时序的成分模型
每条时序由可配置成分叠加,覆盖 AIOps 常见形态:
| 成分 | 实现 | 覆盖场景 |
|------|------|----------|
| 趋势 trend | 分段线性 / log / 阶跃 | 容量增长、版本切换 |
| 周期 seasonal | 多周期正弦叠加(日/周) | 流量昼夜规律 |
| 基线水平 | 常数 + 缓慢漂移 | 稳态负载 |
| 噪声 | 高斯 + 重尾(Student-t | 真实传感器抖动 |
| 异常注入 | 尖刺/水平偏移/方差膨胀/缺失段/缓慢漂移 | 对应 MTSAD 报告异常类型谱 |
| 事件耦合 | 在指定 t 注入阶跃,并生成对应事件文本 | 部署/扩容 → 指标跳变 |
| 缺失/不规则 | 随机丢弃 5-10% 点 | 真实采集缺陷 |
**核心红利**:异常段、事件时间戳、各成分参数全部作为 ground-truth 自动记录——免费且精确的标签。
### 3.3 指令任务类型(Evol-Instruct 扩展)
| 类别 | 示例问题 | 对齐目的 |
|------|----------|----------|
| 描述/统计 | "这段时序的均值/趋势是什么?" | 基础数值→文本对齐 |
| 异常检测 | "哪些时段出现异常?类型?" | 核心任务,带段标签 |
| 根因/解释 | "t=320 后为何突增?" | 结合事件做因果推理 |
| 预测 | "未来 32 步走势?" | 时序外推能力 |
| 比较 | "变量 A 和 B 谁更稳定?" | 跨通道关系建模 |
| 事件关联 | "这次异常和部署事件有关吗?" | 时序+事件模态融合 |
### 3.4 规模(12GB 友好)
- 对齐预训练阶段:**~50 万条**合成三元组(CPU 生成,约 1-2 天可跑完)
- 指令微调阶段:从 50 万中 **Evol-Instruct 演化出 ~10 万条**高质量多轮对话
- 评测集:**固定 2,000 条** held-out 合成 + 公开 benchmark 抽样
- 每条时序 T=512, C∈[3,12],单条约 2-4KB,总量约 2-3GB,训练时流式加载
### 3.5 评测用的真实锚点
合成数据训练,但评测锚定真实分布,避免"合成上 SOTA、真实上崩盘"
| 用途 | 数据 | 处理 |
|------|------|------|
| 真零样本问答评测 | SMD / MSL / SMAP / SWaT 窗口 | 用 LLM 把异常段转成"问答对"做评测(不参与训练) |
| 异常检测指标 | 同上 + PSM | 模型输出文本中的异常区间 → 解析为二值分数 |
| 对照基线 | — | ChatTS / Time-LLM / 纯 LLM(数值喂文本)同口径对比 |
---
## 4. 训练流程与阶段
### 4.1 三阶段
**阶段 ① 时序-文本对齐预训练**~50 万条合成三元组)
- 可训练:TS Encoder + Projector(全参,~4.2M
- 冻结:LLM 主体
- 目标:让 TS token 在 LLM 表示空间"能被读懂"
- 损失:回答 token 的 LM loss(仅回答段计 loss)+ 对比损失(扰动时序→回答应变)
- ~2-3 epoch
**阶段 ② 指令微调 SFT**~10 万条 Evol-Instruct 多轮)
- 可训练:TS Encoder + Projector + LoRA(r=16)~7M
- 冻结:LLM 其余权重
- 目标:建立指令遵循 + 推理 + 异常/事件关联
- 损失:SFT LM loss,对回答做 mask
- ~3 epoch
**阶段 ③ 评测与可选 DPO**2k held-out + 真实 benchmark
- 评测:问答准确率 + 异常检测指标
- 可选:DPO 用正确/错误回答对偏好优化
- 可选:真实数据少量微调做领域适配
### 4.2 显存预算(12GB 3060
| 项 | 阶段① 对齐预训练 | 阶段② SFT |
|----|------------------|-----------|
| 可训练参数 | TS Encoder(4M)+Projector(0.2M) ≈ **4.2M** | + LoRA(2-3M) ≈ **7M** |
| LLM 状态 | BF16 冻结(~1GB 权重) | BF16 + LoRA 适配器 |
| 优化器 | AdamW(仅训 4.2M,显存极省) | AdamW + LoRA |
| 批量 | per_device=8, grad_accum=4 → eff 32 | per_device=4, grad_accum=8 → eff 32 |
| 上下文 | 512 token | 1024 token |
| 梯度检查点 | 开 | 开 |
| 混合精度 | BF163060 支持) | BF16 |
| 预估峰值显存 | **~4-5GB** | **~9-10GB** |
| 单 epoch 时长(估) | ~6-10 小时 | ~8-12 小时 |
### 4.3 关键工程要点
- **阶段① 省显存的核心**:LLM 冻结 → 不需要存 LLM 的优化器状态和梯度,12GB 绰绰有余,甚至能加大批量
- **阶段② 才上 LoRA**:仅给 LLM 加 r=16 的 q/v_proj 适配器,可训练参数 ~2-3M,配合梯度检查点压在 10GB 内
- **分阶段 checkpoint**:阶段①产物 = "TS Encoder+Projector";阶段②在此之上叠加 LoRA,互不污染、可回滚
- **损失 masking**:仅对回答段 token 计算 LM lossprompt(属性/时序/事件/问题)段不计
- **断点续训**:每 2000 step 存 checkpointJSONL 流式加载支持任意 step 续
### 4.4 防止"时序模态塌缩"的训练技巧
| 风险 | 对策 |
|------|------|
| LLM 忽略 TS token,只看文本答题 | 构造"必须看时序才能答"的样本(如具体数值/段位置),并在阶段①用对比损失:扰动时序→回答应变 |
| TS Encoder 过拟合合成分布 | 成分参数随机化 + 多套合成配置 + 阶段②引入真实 benchmark 改写样本 |
| 异常稀有导致模型回避 | 合成时强制每条至少含 1 类异常,指令中异常类任务占比 ≥40% |
| 回答格式不稳定 | 回答模板约束输出 JSON 区间(如 `{"segments":[[320,360]],"type":"spike"}`),便于评测解析 |
---
## 5. 评估方法
### 5.1 三条评测支柱
**A · 时序问答准确率**
- 主:LLM-as-judgeGPT-4o 评分 0-5
- 辅:规则解析 JSON 区间精确匹配
- 6 类指令任务分别报分
- held-out 合成 2k + 真实改写
**B · 异常检测指标**(严守 MTSAD 报告评估纪律)
- 主:**VUS-PR**(阈值无关)
- Affiliation-F1(λ=0.1/0.5/1.0
- AUC-PR、Point-F1(无 PA
- **PA-F1 仅对照、不得作主指标**
**C · 泛化与消融**
- 跨数据集:SMD→MSL/SWaT
- 模态消融:去掉事件/属性/时序
- trivial 基线对照(Random/Constant
- 同口径对比 ChatTS/Time-LLM/纯 LLM
### 5.2 异常检测:从自然语言回答 → 数值分数
| 步骤 | 做法 |
|------|------|
| 1. 约束输出格式 | 训练时要求回答含 `{"segments":[[s,e],...],"type":...,"score":...}` |
| 2. 解析区间 | 正则/JSON 解析模型回答中的异常段 |
| 3. 生成逐点分数 | 段内点赋 1.0,段外 0.0;可叠加模型自报置信度 score |
| 4. 算指标 | 用 `tsb_uad` / 自实现 VUS-PR、Aff-F1、AUC-PR、Point-F1 |
| 5. 鲁棒性兜底 | 解析失败 → 分数全 0(计为漏报),单独统计解析成功率 |
### 5.3 问答评测:LLM-judge + 规则双轨
| 轨 | 适用任务 | 方法 |
|----|----------|------|
| 规则解析(客观) | 异常检测、统计描述、预测 | JSON 区间/数值容差比对,可重复、零成本 |
| LLM-judge(主观) | 根因解释、事件关联、比较 | GPT-4o 对 (问题,时序摘要,参考答案,模型答案) 打 0-5 分 + 理由 |
| 交叉校验 | 全部 | 两类不一致的样本人工抽检,定位系统性偏差 |
### 5.4 必报指标与报告规范
**必报(每数据集×每任务):**
- 异常检测:VUS-PR(主)、Affiliation-F1(多λ)、AUC-PR、Point-F1(无PA)
- 问答:LLM-judge 均分、规则解析准确率、解析成功率
- 泛化:跨数据集 F1/VUS-PR 衰减比例
- 消融:去掉各模态后的指标变化
**禁止:** 仅报 PA-F1 / 单数据集称 SOTA / 不报 trivial 基线 / 不标是否用 PA。
### 5.5 对照基线(同口径同评测脚本)
- **纯 LLM 数值喂文本**:把时序数值直接拼成文本喂 Qwen2.5-0.5B —— 检验"TS 模态"是否真带来增益
- **Time-LLM 风格重映射**:同样 0.5B 骨干,对比"重映射 vs 我们的新模态"
- **ChatTS**(若可复现):对比同类多模态时序 LLM
- **Trivial**Random / Constant / "全报异常" —— 确保不是评估虚高
---
## 6. 工程落地与代码结构
### 6.1 项目布局
```
ts-as-modality/
├── README.md
├── pyproject.toml # 依赖:torch, transformers, peft, accelerate, datasets
├── configs/
│ ├── ts_encoder.yaml # TS Encoder/Projector 超参
│ ├── stage1_align.yaml # 对齐预训练
│ ├── stage2_sft.yaml # 指令微调
│ └── eval.yaml
├── src/tsmm/
│ ├── __init__.py
│ ├── data/
│ │ ├── synthesis.py # 成分模型 + 异常注入 + 事件耦合
│ │ ├── attributes.py # 属性词表采样
│ │ ├── instruct.py # Evol-Instruct 指令/回答生成
│ │ ├── collator.py # 拼接 [属性][时间戳][TS tok][事件][问题]→ token流
│ │ └── real_bench.py # SMD/MSL/SMAP/SWaT/PSM 加载 + 问答改写
│ ├── model/
│ │ ├── ts_encoder.py # Patchify + 2层TF + Projector
│ │ ├── projector.py # Linear→LLM hidden
│ │ ├── multimodal.py # 组装 TS token + 文本 token 进 LLM
│ │ └── wrapper.py # 训练/推理统一封装(含 LoRA 挂载)
│ ├── train/
│ │ ├── stage1.py # 冻结LLM,训 Encoder+Projector
│ │ ├── stage2.py # 加 LoRA SFT
│ │ └── losses.py # LM loss mask + 对比损失
│ ├── eval/
│ │ ├── parse_answer.py # 文本→JSON 区间→逐点分数
│ │ ├── ts_metrics.py # VUS-PR / Aff-F1 / AUC-PR / Point-F1
│ │ ├── qa_judge.py # 规则解析 + LLM-judge
│ │ └── baselines.py # 纯LLM/Time-LLM/ChatTS/trivial 同口径
│ └── utils/
├── scripts/
│ ├── gen_synthetic.py # 离线生成 50万 + 10万 JSONL
│ ├── train_stage1.sh
│ ├── train_stage2.sh
│ └── run_eval.sh
├── data/ # 生成产物(git-ignored
├── checkpoints/ # 分阶段 ckptgit-ignored
└── tests/ # 各模块单元测试
```
### 6.2 技术栈
| 层 | 选型 | 理由 |
|----|------|------|
| 骨架 | Qwen2.5-0.5B-Instruct (HF Transformers) | 0.5B、中英、Instruct 版现成 |
| 微调 | PEFT (LoRA) + Accelerate | 12GB 标配组合 |
| 数据 | datasets 流式 + JSONL | 2-3GB 无需全载内存 |
| 合成 | NumPy + tqdm,纯 CPU 多进程 | 不占显存,可后台跑 |
| 评估 | tsb_uadVUS-PR+ 自实现 Aff-F1 | 对齐报告推荐实现 |
| LLM-judge | OpenAI/GPT-4o API(评测时才调) | 仅评测用,不进训练 |
| 实验追踪 | TensorBoard / wandb(可选) | 轻量 |
### 6.3 交付里程碑
| 里程碑 | 内容 | 验证点 |
|--------|------|--------|
| M1 · 数据管线 | 合成生成 + 真实 benchmark 加载,产出 1k 样本可视检 | 成分/异常/事件正确,JSONL schema 合规 |
| M2 · 模型可跑通 | TS Encoder+Projector+多模态拼接,单 batch 前向+反向在 3060 不 OOM | 形状对、显存<12GB、loss 下降 |
| M3 · 阶段① 对齐训练 | 50万条 2-3 epochTS token 不被忽略 | 扰动时序→回答变化 |
| M4 · 阶段② SFT | 10万条 3 epoch + LoRA6 类任务能遵循指令 | held-out 问答可用 |
| M5 · 完整评测 | VUS-PR/Aff-F1/问答分,4 类基线同口径对比 | 超纯LLM基线、非PA虚高 |
### 6.4 风险与对策
| 风险 | 概率 | 对策 |
|------|------|------|
| 12GB 仍 OOM(阶段②) | 中 | 降批量/上下文、开 CPU offload 优化器、退回阶段①-only 报告 |
| 合成→真实泛化差距大 | 高 | 阶段②掺真实改写样本;评测以真实 benchmark 为准 |
| LLM 忽略 TS token | 中 | 对比损失 + "必看时序"样本 + 消融实验定位 |
| 回答 JSON 解析失败率高 | 中 | 训练强化格式约束 + 解析兜底 + 单独报成功率 |
| GPT-4o judge 成本 | 低 | 仅评测 2k 样本调用;可换开源 judge 做 sanity |
### 6.5 整体成功标准(可验证)
1. 全流程在单张 3060 12GB 跑通,阶段②峰值显存 ≤ 11GB
2. 真实 benchmarkSMD/MSL/SMAP/SWaTVUS-PR **显著优于纯 LLM 数值喂文本基线**
3. 时序消融后指标明显下降,证明"时序模态"确有贡献
4. 6 类问答任务 LLM-judge 均分 > 纯 LLM 基线
5. 评测脚本可复现,含 trivial 基线对照、标注是否用 PA
---
## 7. 开放问题(实现阶段再定)
- TS Encoder 层数/d 是否需在小规模上 sweep(2 层是否够)
- Patch P=8/stride=4 是否最优,是否需对比 P=16
- 对比损失的具体形式(InfoNCE vs 扰动一致性)与权重
- DPO 是否纳入首版(建议首版先不做,M5 后视效果决定)
- 真实 benchmark 改写问答的数量与质量校验流程
---
> 本规格定义了「长什么样、吃什么数据、怎么训、怎么评、怎么落地」。下一步进入实现计划(writing-plans)。