init version

This commit is contained in:
张宗平
2026-06-29 21:16:52 +08:00
commit 7a5a1d033f
101 changed files with 22202 additions and 0 deletions
@@ -0,0 +1,221 @@
# 时序作为模态的多模态模型 · 实现计划
> **对应规格**`docs/superpowers/specs/2026-06-29-ts-as-modality-design.md`
> **日期**2026-06-29
> **目标**:把规格拆成有序、可验证的实现任务,按里程碑 M1→M5 推进
> **原则**:每个任务都有明确的"完成判定"(可跑一条命令/测试验证);任务间依赖清晰;先建最小可跑通骨架,再迭代填充
---
## 阶段总览
```
M1 数据管线 ──► M2 模型可跑通 ──► M3 阶段①对齐训练 ──► M4 阶段②SFT ──► M5 完整评测
(data) (model) (train stage1) (train stage2) (eval)
└─ 合成+真实 └─ TS Encoder └─ 50万 2-3ep └─ 10万 3ep └─ VUS-PR/问答/基线
```
每里程碑结束有"出口验证",通过才进下一个。首版先做"小规模冒烟"(数据 1k、训练几十步)确认管线通,再放大到全量。
---
## M1 · 数据管线
**目标**:能产出合法 JSONL 样本(合成)+ 能加载真实 benchmark 窗口。
### T1.1 项目脚手架
- 创建 `pyproject.toml`:依赖 `torch`, `transformers`, `peft`, `accelerate`, `datasets`, `numpy`, `tqdm`, `pyyaml`, `pytest`
- 创建 `src/tsmm/__init__.py``configs/``scripts/``tests/` 目录骨架
-`data/``checkpoints/` 并加 `.gitignore`
- **完成判定**`python -c "import tsmm"` 不报错;`pytest tests/` 能跑(哪怕空)
### T1.2 属性词表与采样 `data/attributes.py`
- 定义变量名词表(CPU/内存/流量/延迟/温度/请求量…)、单位、采样率档位
- `sample_attributes(n_channels)` → 返回 `[{"name","unit","freq"}, ...]`
- **完成判定**:单元测试 `tests/test_attributes.py` — 采样结果字段齐全、数量正确、可复现(设 seed)
### T1.3 成分模型与时序合成 `data/synthesis.py`
- 实现 `generate_series(T, C, seed)`:趋势+周期+基线+噪声(高斯/Student-t)叠加
- 实现 `inject_anomaly(series, types)`:尖刺/水平偏移/方差膨胀/缺失段/缓慢漂移,返回 `(series, labels)`labels 为逐点 0/1 + 段元数据
- 实现 `couple_event(series, t, kind)`:在 t 注入阶跃并返回事件文本描述
- 实现 `add_missing(series, rate)`:随机丢弃 5-10% 点(用 NaN 占位)
- **完成判定**`tests/test_synthesis.py` — 给定 seed 输出确定;异常段位置与 labels 一致;事件 t 处确有阶跃;可视化 1 条样本人工确认形态合理
### T1.4 指令与回答生成 `data/instruct.py`
- 定义 6 类指令模板(描述/异常/根因/预测/比较/事件关联)
- `build_instruction(series_meta, labels, events, category)``(instruction, answer)`answer 含 JSON 区间(异常类)或数值(统计类)或文本(解释类)
- Evol-Instruct 演化:用模板组合 + 随机改写生成多样化问法
- **完成判定**`tests/test_instruct.py` — 每类至少产 1 条,answer 的 JSON 可被 `json.loads` 解析;异常类 answer 段与 labels 吻合
### T1.5 离线生成脚本 `scripts/gen_synthetic.py`
- 多进程跑 T1.2-T1.4,产出 `data/align.jsonl`50万)+ `data/sft.jsonl`10万,Evol 演化)+ `data/eval_synth.jsonl`2k held-out,固定 seed
- 支持 `--n``--out``--seed``--workers` 参数;带 tqdm 进度
- **完成判定**`python scripts/gen_synthetic.py --n 1000` 跑通;抽样 5 条人工可视检 schema 合规(含 series/attributes/timestamps/events/instruction/answer/labels
### T1.6 真实 benchmark 加载 `data/real_bench.py`
- 加载 SMD/MSL/SMAP/SWaT/PSM(先用本地缓存或公开下载脚本;若无现成包,至少实现 SWaT/MSL 两个)
- `load_windows(name, T=512, stride=256)` → 滑窗 + 归一化 + 异常标签
- 用 LLM/模板把异常段转成问答对(评测用,不进训练),存 `data/eval_real.jsonl`
- **完成判定**:能加载至少 2 个数据集;窗口形状 `[N, T, C]`;标签形状 `[N, T]`;问答对非空
> **M1 出口验证**`gen_synthetic.py --n 1000` 产出可视检合法的 JSONL`real_bench.py` 能加载 ≥2 数据集。人工抽看 5 条合成样本确认成分/异常/事件正确。
---
## M2 · 模型可跑通
**目标**TS Encoder + Projector + 多模态拼接能在 3060 上前向+反向不 OOM。
### T2.1 TS Encoder `model/ts_encoder.py`
- `Patchify(patch=8, stride=4)`:通道独立切 patch,线性嵌入到 d=256
- `TSEncoder(d=256, layers=2, heads=4)`2 层 Transformer + 可学习位置编码
- 前向:`[B, T, C]``[B, n_patches, 256]`
- **完成判定**`tests/test_ts_encoder.py` — 随机输入 `[2, 512, 5]` 输出 `[2, 128, 256]`;参数量 ≈4M
### T2.2 Projector `model/projector.py`
- `Projector(256 → 896)`Linear + LayerNorm
- **完成判定**:输出 `[B, 128, 896]` 对齐 Qwen2.5-0.5B hidden
### T2.3 多模态拼接 `model/multimodal.py`
- 用 Qwen2.5-0.5B tokenizer 把 `[属性][时间戳][事件][问题]` 文本 tokenize
- 把 TS token[B,128,896])作为 inputs_embeds 插入对应位置:`[属性][时间戳][TS tok][事件][问题]`
- 统一构造 `inputs_embeds`(文本走 `get_input_embeddings()`TS 走 Projector 输出)+ `attention_mask` + `labels`(仅回答段非 -100
- **完成判定**`tests/test_multimodal.py` — 拼接后 seq_len ≤1024mask/labels 形状对;回答段 label 非 -100
### T2.4 训练/推理封装 `model/wrapper.py`
- `MultimodalTSModel`:组合 TS Encoder + Projector + LLM + LoRA 挂载开关
- `forward(batch)` 返回 loss`generate(batch)` 返回文本
- 支持 `freeze_llm`(阶段①)和 `enable_lora(r=16)`(阶段②)两种模式
- **完成判定**:单 batch `forward` 在 3060 不 OOM`generate` 能出文本(哪怕乱码)
### T2.5 Collator `data/collator.py`
- 把 JSONL 样本 batch 化为 `(series, attributes, timestamps, events, instruction, answer)` → 张量
- 处理变长 Cpadding + mask)、变长文本(padding + attention_mask
- **完成判定**`tests/test_collator.py` — batch=4 输出张量形状一致、无 NaN
> **M2 出口验证**:单 batch 前向+反向在 3060 跑通,loss 是有限值且下降趋势;阶段①模式峰值显存 ~5GB。
---
## M3 · 阶段① 对齐训练
**目标**50万条 2-3 epochTS token 不被忽略。
### T3.1 损失函数 `train/losses.py`
- `lm_loss(logits, labels)`:仅回答段计 LM losslabels mask
- `contrastive_loss(model, batch)`:扰动时序(加噪/平移)→ 回答 embedding 应变化(InfoNCE 或扰动一致性)
- 总 loss = `lm_loss + λ * contrastive_loss`(λ 可配,默认 0.1
- **完成判定**`tests/test_losses.py` — loss 标量、可反传、对比损失对扰动敏感
### T3.2 阶段① 训练脚本 `train/stage1.py` + `scripts/train_stage1.sh`
- 加载 `data/align.jsonl`datasets 流式)
- `freeze_llm=True`,仅训 TS Encoder + ProjectorAdamW, lr=1e-4
- 配置:bs=8, grad_accum=4, ctx=512, BF16, 梯度检查点
- 每 2000 step 存 ckpt 到 `checkpoints/stage1/`TensorBoard 记录
- **完成判定**`--max_steps 100` 冒烟跑通不 OOM;loss 下降;峰值显存 ≤6GB
### T3.3 TS-token 有效性检验
- 训练后取 held-out 样本:原时序 vs 扰动时序,对比模型回答是否变化
- "必看时序"样本:问具体数值/段位置,看模型能否答对
- **完成判定**:扰动后回答变化率 >50%;"必看时序"样本答对率显著高于纯 LLM 基线
> **M3 出口验证**:阶段① ckpt 产出;扰动检验通过;显存 ≤6GB。失败则回查对比损失权重/数据质量。
---
## M4 · 阶段② SFT
**目标**10万条 Evol-Instruct 3 epoch + LoRA6 类任务遵循指令。
### T4.1 阶段② 训练脚本 `train/stage2.py` + `scripts/train_stage2.sh`
- 加载阶段① ckptTS Encoder + Projector
- `enable_lora(r=16)` on q/v_projLLM 其余冻结
- 加载 `data/sft.jsonl`,配置:bs=4, grad_accum=8, ctx=1024, BF16, 梯度检查点
- 每 2000 step 存 ckpt(含 LoRA 适配器)到 `checkpoints/stage2/`
- **完成判定**`--max_steps 100` 冒烟跑通;峰值显存 ≤11GB;loss 下降
### T4.2 指令遵循抽检
- 6 类任务各取 10 条 held-out,人工/规则看回答是否遵循格式与语义
- 异常类:JSON 可解析、段位置合理
- 解释类:回答切题、引用事件
- **完成判定**:6 类任务回答可用率 >70%JSON 解析成功率 >80%
> **M4 出口验证**:阶段② ckpt 产出;6 类任务抽检达标;显存 ≤11GB。若 OOM 按风险对策降批量/开 CPU offload。
---
## M5 · 完整评测
**目标**:产出 VUS-PR/Aff-F1/问答分 + 4 类基线对比。
### T5.1 回答解析 `eval/parse_answer.py`
- 从模型文本回答中提取 JSON 异常段 → 逐点分数 `[T]`(段内 1.0,段外 0.0
- 解析失败 → 全 0 + 记录失败标记
- **完成判定**`tests/test_parse.py` — 多种回答格式(含噪声/嵌套)能解析;失败有兜底
### T5.2 异常检测指标 `eval/ts_metrics.py`
-`tsb_uad` 算 VUS-PR(主);自实现 Affiliation-F1(λ=0.1/0.5/1.0)、AUC-PR、Point-F1(无PA)
- PA-F1 单独算作对照
- **完成判定**`tests/test_ts_metrics.py` — 对已知段能算出合理值;trivial 基线(全报异常)VUS-PR 不虚高
### T5.3 问答评测 `eval/qa_judge.py`
- 规则解析轨:JSON/数值容差比对,产出准确率
- LLM-judge 轨:调 GPT-4o 对 (问题,时序摘要,参考答案,模型答案) 打 0-5 分(可换开源 judge 做 sanity
- 不一致样本导出供人工抽检
- **完成判定**:能对 eval 集产出 6 类任务的 {规则准确率, judge 均分, 解析成功率}
### T5.4 对照基线 `eval/baselines.py`
- 纯 LLM 数值喂文本:时序数值拼字符串喂 Qwen2.5-0.5B,同评测脚本
- Time-LLM 风格重映射:同样 0.5B 骨干(简化复现)
- ChatTS(若可复现则纳入,否则标注未复现)
- TrivialRandom / Constant / 全报异常
- 全部走同一套 `parse_answer` + `ts_metrics`,保证同口径
- **完成判定**:4 类基线均产出同口径指标表
### T5.5 评测脚本与报告 `scripts/run_eval.sh`
-`eval_synth.jsonl` + `eval_real.jsonl`,产出 markdown 报告 `reports/eval-YYYYMMDD.md`
- 报告含:每数据集×每任务的 VUS-PR/Aff-F1/AUC-PR/Point-F1/PA-F1(对照)/问答分/解析成功率/消融/基线对比
- 标注是否用 PA、含 trivial 对照
- **完成判定**:一键 `run_eval.sh` 产出完整报告;报告满足规格 §5.4 必报清单
> **M5 出口验证**:报告满足规格 §6.5 成功标准 —— 真实 benchmark VUS-PR 显著优于纯 LLM 基线;时序消融后指标明显下降;问答均分超基线;评测可复现含 trivial。
---
## 执行顺序与依赖
```
T1.1 ─► T1.2 ─► T1.3 ─► T1.4 ─► T1.5 ┐
├─► [M1 出口] ─► T2.1 ─► T2.2 ─► T2.3 ─► T2.4 ─► T2.5 ─► [M2 出口]
T1.6 ────────────────┘ │
T3.1 ─► T3.2 ─► T3.3 ─► [M3 出口]
T4.1 ─► T4.2 ─► [M4 出口]
T5.1 ─► T5.2 ┐
T5.3 ────────┼─► T5.4 ─► T5.5 ─► [M5 出口]
```
## 风险触发时的回退
- **M2 OOM**:先降 ctx 到 256 / bs 到 2;仍不行退到只验证阶段①模式
- **M3 TS token 被忽略**:提升对比损失 λ;增加"必看时序"样本占比;检查 Projector 是否饱和
- **M4 OOM**:降 bs=2/grad_accum=16;开 `optim=adamw_8bit` + CPU offloadctx 降到 768
- **M5 合成→真实泛化差**:在阶段②掺入 `real_bench` 改写样本重训;报告以真实 benchmark 为准
## 首次跑通的"冒烟路径"(先做这个)
不等全量数据,先用最小路径确认端到端通:
1. `gen_synthetic.py --n 200` 产小数据
2. T2.1-T2.4 单 batch 跑通
3. `stage1.py --max_steps 50` + `stage2.py --max_steps 50` 冒烟
4. `run_eval.sh` 在 200 样本上产出报告骨架
冒烟通过后再放大到 M1 全量数据 + 完整训练。
---
> 本计划把规格拆成 22 个任务、5 个带出口验证的里程碑。每个任务有"完成判定"可独立验证。建议从"冒烟路径"开始,确认端到端通后再放大规模。
@@ -0,0 +1,343 @@
# 时序作为模态的多模态模型 · 设计规格
> **日期**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)。