2026-04-02 组会:Analysis Framework
一、框架设计
1. Framework Overview
本框架是 C 端用于 A/B 对齐分析的实验分析框架,工作目录为 /root/flowctl_client/analysis_C。其目标是对 A 端控制序列与 B 端连续出口观测序列进行统一对齐,并在此基础上完成延迟估计、符号恢复与分类指标计算。
该框架面向的不是一次性 trigger-response 实验,而是持续调制实验。在这类实验中,A 端会持续输出控制符号,B 端会持续处于活跃状态,因此分析目标不再是“寻找某次触发后的单次响应”,而是“判断在某个延迟下,A 端不同符号对应的 B 端窗口统计量是否能够稳定分离”。
当前实验形态为:
- A 端输出控制序列,记录于
A_control_log.jsonl - B 端记录连续出口速率,记录于
B_egress_rate.csv - 实验为持续下载下的速率调制
- 典型符号序列为交替
adv_kbps=100与adv_kbps=133
2. Directory Layout
框架目录组织如下:
bin/analyze_run.py:执行单次实验的对齐、特征提取与指标计算render_report.py:将处理结果转换为 Markdown 报告analyze_all_runs.sh:批量分析脚本
config/analyzer.env:lag 扫描范围、步长和阈值相关配置
raw/<exp_id>/- 保存 B 端原始输入,如
B_egress_rate.csv与B_meta.json
- 保存 B 端原始输入,如
processed/<exp_id>/- 保存中间结果与结构化指标,如
aligned_trace.csv、labels.json、detection_result.json、metrics.json
- 保存中间结果与结构化指标,如
reports/<exp_id>/- 保存最终人类可读 Markdown 报告
report.md
- 保存最终人类可读 Markdown 报告
该目录设计将原始数据、中间处理结果和最终报告明确分层,便于后续复现实验、排查异常及扩展分析指标。
3. Input Specification
框架依赖三类输入:
- A side truth log:
/root/flowctl_client/runs/<exp_id>/A_control_log.jsonl - B side egress trace:
/root/flowctl_client/analysis_C/raw/<exp_id>/B_egress_rate.csv - B side metadata:
/root/flowctl_client/analysis_C/raw/<exp_id>/B_meta.json
其中:
3.1 A-side log
A_control_log.jsonl 提供 A 端控制行为的时间序列记录。当前分析仅使用满足以下条件的记录:
event_type == XON- 存在
adv_kbps - 存在
ts_unix_ms
这些字段分别表示:
- 事件类型为有效控制事件
- 当前符号对应的建议速率值
- 事件发生时间戳
3.2 B-side trace
B_egress_rate.csv 记录 B 端连续出口观测。当前主分析使用字段:
ts_ns:纳秒级时间戳,后续统一转换为ts_unix_mskbps:对应时刻的出口速率,作为主观测信号
3.3 B-side metadata
B_meta.json 用于补充实验运行信息与上下文元数据。虽然其内容不直接进入当前分类公式,但对实验一致性检查和报表生成仍有辅助价值。
4. Core Analysis Pipeline
框架主流程由五个阶段构成:
- 构造 A 端 truth symbol windows
- 将 B 端视为连续观测信号
- 扫描候选 lag 并评估窗口级可分性
- 选取最优
best_lag_ms - 在最优 lag 下完成符号分类与指标计算
下面分别展开。
5. A-side Truth Construction
5.1 Record filtering
首先读取 A_control_log.jsonl,并保留满足分析条件的 XON 记录。此步骤的目的是从原始控制日志中提取可作为真值的符号事件序列。
5.2 Cleanup step 1: dedup within the same millisecond
若同一毫秒存在多条 XON 记录,仅保留最后一条。这样处理的原因是:
- 在启动或状态切换时,日志可能在同一时间戳下连续记录多个临时值
- 同一时刻真正生效的状态应视为最后一次写入的状态
- 如果不去重,会导致同一时刻被错误拆成多个符号
5.3 Cleanup step 2: keep only change points
在时间顺序上,仅保留 adv_kbps 发生变化的位置。这样做的原因是:
- 如果 A 端连续多次记录相同
adv_kbps,这些记录并不对应新的符号切换 - 对齐分析关心的是符号边界,而不是重复日志条目
完成上述两步清理后,得到一个按时间排序的 change-point 序列。
5.4 Convert change points into symbol windows
随后,将相邻 change points 转换为 symbol windows。每个窗口由以下字段定义:
a_start_ts_unix_ms:符号起始时间a_end_ts_unix_ms:符号结束时间a_adv_kbps:该窗口内 A 端符号值a_label:用于分类评估的二值真值标签
该转换的本质是将离散事件日志重建为一段分段常值信号,以便与 B 端连续时间序列按区间对齐。
5.5 Binary labeling rule
当前版本采用简单的二值标签规则:
- 当前 run 中最低
adv_kbps档位记为0 - 任何更高
adv_kbps档位记为1
因此在本实验中:
100 -> label 0133 -> label 1
这一设计适合当前二值持续调制场景,同时也明确指出:当前框架属于 binary modulation v1,不直接支持多档位多分类。
6. B-side Observation Modeling
6.1 Continuous-signal view
B 端 B_egress_rate.csv 不再被解释为由若干独立响应事件构成,而是被视为一个连续时间观测信号。这一建模假设更符合持续下载任务,因为:
- B 端始终处于传输过程中
- 速率变化具有连续性而不是瞬时离散跳变
- 单个窗口的观测值可能受前后窗口残留影响
因此,分析对象不再是“某个 rise 事件”,而是“某个时间区间上的统计特征”。
6.2 Main signal field
主分析使用 B 端的 kbps 作为连续观测值,并将 ts_ns 统一换算到 ts_unix_ms 时间轴,以便与 A 端窗口对齐。
6.3 Legacy state-machine output
脚本仍保留旧逻辑派生的 B_state_machine.csv,其中包含:
is_lowlow_streakzero_armedrise_1
但该文件仅作为诊断性 side output。它可以帮助检查:
- B 端是否曾显著回到低状态
- 旧逻辑在某些特殊 run 中为何失效
- 当前 run 是否存在明显异常
7. Lag-Scan Based Alignment
7.1 Main assumption
当前框架采用如下假设:
- A 端是已知真实调制序列
- B 端是该序列经过传播时延、网络抖动和缓冲平滑后的延迟观测
因此,与其逐事件匹配,不如扫描候选 lag,并检验在该 lag 下高低符号窗口的可分性是否最强。
7.2 Lag search range
lag 扫描范围由 config/analyzer.env 控制,主要参数包括:
DELAY_MIN_MSDELAY_MAX_MSLAG_STEP_MS
当前配置为:
- 最小 lag:
0 ms - 最大 lag:
5000 ms - 扫描步长:
100 ms
因此,候选 lag 集合为:
1 | 0, 100, 200, ..., 5000 ms |
7.3 Per-lag window alignment
对每一个候选 lag,执行如下步骤:
- 将所有 A-side symbol windows 整体向后平移该 lag
- 在 B 端截取落入平移后窗口区间内的所有采样点
- 对每个窗口计算聚合统计量
- 将这些窗口值按 A 标签划分为 high group 与 low group
- 计算该 lag 下的类间可分性指标
这里的核心是:对齐是在窗口层面而不是事件层面完成的。
7.4 Window-level B features
当前框架为每个对齐后的窗口计算多个 B-side 聚合特征:
b_mean_kbpsb_median_kbpsb_max_kbpsb_p90_kbps
其中,当前分类与可分性分析的主特征是:
b_mean_kbps
保留其他统计量的原因是:
- 便于后续扩展更稳健的分类器
- 便于分析异常窗口
- 便于比较不同聚合统计量对分离效果的影响
8. Best Lag Selection by Separability
8.1 Separation score
对于每个候选 lag,框架计算:
1 | separation_score = (mean_high - mean_low) / (std_high + std_low + eps) |
其中:
mean_high:A 标签为1的窗口在 B 端b_mean_kbps的均值mean_low:A 标签为0的窗口在 B 端b_mean_kbps的均值std_high:高符号窗口b_mean_kbps的总体标准差std_low:低符号窗口b_mean_kbps的总体标准差eps:防止分母为零的极小项
8.2 Interpretation
该分数可解释为“类间距离相对于类内波动的归一化程度”:
- 分子越大,说明高低两类中心差距越大
- 分母越大,说明两类内部噪声越强
- 因此,分数越大,说明该 lag 下两类越容易分离
具体而言:
separation_score > 0:高符号窗口整体上高于低符号窗口- 接近
0:两类高度重叠 - 小于
0:当前 lag 与真实传播延迟不匹配
8.3 Why this criterion is used
选择 separability 而非单点误差最小化的原因在于:
- 当前实验目标是恢复符号类别,而非拟合某条连续曲线
- 持续调制实验中,B 端观测受窗口平滑与残留影响,单事件误差不稳定
- 类间可分性更直接对应“该延迟是否能支持可靠分类”
8.4 Best lag selection rule
最终,将 separation_score 最大的 lag 记为:
best_lag_ms
这意味着:当前框架将“使高低类最容易分开”的延迟,作为 A 到 B 的最佳估计传播时延。
9. Classification After Lag Fixing
9.1 Final aligned trace
在确定 best_lag_ms 后,框架生成最终 aligned_trace.csv。该文件是最关键的中间结果,每一行对应一个 A-side symbol window 在 B 端的对齐结果。
关键字段包括:
symbol_idxa_start_ts_iso,a_end_ts_isoa_adv_kbpsa_labelbest_lag_msb_mean_kbps,b_median_kbps,b_max_kbps,b_p90_kbpsb_pred_labelhas_valid_b_window
9.2 Threshold definition
在固定最优 lag 后,框架不再继续搜索分类阈值,而是直接使用:
1 | classification_threshold_kbps = (median_high_kbps + median_low_kbps) / 2 |
其中:
median_high_kbps:所有 A 标签为1的 aligned windows 中,b_mean_kbps的中位数median_low_kbps:所有 A 标签为0的 aligned windows 中,b_mean_kbps的中位数
9.3 Rationale for median-midpoint threshold
该阈值定义具有三个优点:
- 鲁棒性更强。 中位数相较均值更不容易受尖峰值影响。
- 解释性更好。 阈值位于两类中心趋势之间,符合直观分类边界。
- 避免过拟合。 阈值由类分布直接给出,而不是针对单次 run 复杂调参。
最终分类规则为:
1 | b_pred_label = 1 if b_mean_kbps >= classification_threshold_kbps else 0 |
10. Output Files and Their Roles
10.1 aligned_trace.csv
最重要的中间文件,用于保存窗口级对齐结果。它支撑:
- 单窗口案例检查
- 错误样本定位
- 可视化与后续统计分析
10.2 labels.json
记录标签与阈值定义,包括:
- A 侧标签映射规则
- B 侧预测标签规则
best_lag_msclassification_threshold_kbps
10.3 detection_result.json
保存分类评估结果,包括:
accuracyprecisionrecallf1fprfnr- confusion matrix
10.4 metrics.json
保存整体对齐与分类质量摘要,包括:
- 符号数量统计
best_lag_msseparation_score- 高低类统计特征
window_coverage_rate- 分类指标
- 附加说明
10.5 B_state_machine.csv
仅作为调试辅助输出,用于与旧方法对照,不作为主报告结论依据。
11. Metric Definitions
11.1 Delay and alignment metrics
best_lag_ms- 使高低类窗口在 B 端最可分的延迟估计
separation_score- 当前 lag 下的类间可分性得分,值越大越好
mean_high_kbps- A=1 窗口对应的 B-side
b_mean_kbps均值
- A=1 窗口对应的 B-side
mean_low_kbps- A=0 窗口对应的 B-side
b_mean_kbps均值
- A=0 窗口对应的 B-side
median_high_kbps- A=1 窗口对应的 B-side
b_mean_kbps中位数
- A=1 窗口对应的 B-side
median_low_kbps- A=0 窗口对应的 B-side
b_mean_kbps中位数
- A=0 窗口对应的 B-side
window_coverage_rate- 经过最优 lag 平移后,仍能提取到有效 B-side 样本的窗口占比
classification_threshold_kbps- 最终对
b_mean_kbps执行分类时使用的阈值
- 最终对
11.2 Classification metrics
在固定最佳 lag 与阈值后,计算如下指标:
accuracy = (TP + TN) / (TP + TN + FP + FN)precision = TP / (TP + FP)recall = TP / (TP + FN)f1 = 2 * precision * recall / (precision + recall)fpr = FP / (FP + TN)fnr = FN / (FN + TP)
11.3 Confusion matrix terms
tp:A-label-1 被正确预测为1tn:A-label-0 被正确预测为0fp:A-label-0 被误判为1fn:A-label-1 被误判为0
二、原始实验报告
Summary
- first_symbol_ts: 2026-03-31T07:25:55-04:00
- last_symbol_ts: 2026-03-31T08:50:28-04:00
- best_lag_ms: 1400
- classification_threshold_kbps: 934.272
Metrics
| metric | value |
|---|---|
| symbol_count_total | 1401 |
| symbol_count_high | 700 |
| symbol_count_low | 701 |
| best_lag_ms | 1400 |
| separation_score | 0.245 |
| mean_high_kbps | 1076.346 |
| mean_low_kbps | 851.456 |
| median_high_kbps | 1044.360 |
| median_low_kbps | 824.184 |
| window_coverage_rate | 1.000 |
| accuracy | 0.989 |
| precision | 0.983 |
| recall | 0.994 |
| f1 | 0.989 |
| fpr | 0.017 |
| fnr | 0.006 |
Confusion Matrix
| item | value |
|---|---|
| tp | 696 |
| tn | 689 |
| fp | 12 |
| fn | 4 |
Notes
- none
Sample Aligned Windows
| symbol_idx | a_adv_kbps | a_label | b_mean_kbps | b_pred_label | best_lag_ms |
|---|---|---|---|---|---|
| 0 | 100 | 0 | 826.9019999999998 | 0 | 1400 |
| 1 | 133 | 1 | 1042.0480000000007 | 1 | 1400 |
| 2 | 100 | 0 | 812.1499999999978 | 0 | 1400 |
| 3 | 133 | 1 | 1053.8739999999984 | 1 | 1400 |
| 4 | 100 | 0 | 819.2459999999992 | 0 | 1400 |
| 5 | 133 | 1 | 1035.1860000000001 | 1 | 1400 |
| 6 | 100 | 0 | 959.6419999999983 | 1 | 1400 |
| 7 | 133 | 1 | 1038.453999999995 | 1 | 1400 |
| 8 | 100 | 0 | 808.4479999999982 | 0 | 1400 |
| 9 | 133 | 1 | 1039.6280000000029 | 1 | 1400 |
| 10 | 100 | 0 | 811.0399999999936 | 0 | 1400 |
| 11 | 133 | 1 | 1050.5640000000028 | 1 | 1400 |
三、实验结果分析
这一部分开始进入具体实验结果。
1. 样本数量与平衡性
symbol_count_total = 1401symbol_count_high = 700symbol_count_low = 701
这说明本次实验总共分析了 1401 个符号窗口,而且高低符号几乎完全平衡。
这个平衡性很重要,因为这样分类准确率不会被类别分布偏置影响。
2. separation_score = 0.245
这个指标表示高低两类在 B 端观测值上的可分离程度。
可以这样讲:
- 这个值越大,说明两类越容易分开。
- 这个值不是特别夸张地大,说明两类并不是完全没有重叠。
- 但后面的分类结果依然很高,说明虽然存在一定重叠,但整体上仍然足够区分。
所以这个值传达的信息是:
两类不是完全分离,但已经具备稳定可判别性。
3. 高低符号在 B 端的均值和中位数
mean_high_kbps = 1076.346mean_low_kbps = 851.456median_high_kbps = 1044.360median_low_kbps = 824.184
这四个值可以一起讲。
先看均值,高符号比低符号大约高出:
- 1076.346 - 851.456 ≈ 224.89 kbps
再看中位数,高符号和低符号也有明显差距。
这说明:
- A 端的高低速率调制,确实在 B 端留下了清楚的统计差异。
- 这种差异不是少数异常值造成的,因为中位数也同样分开了。
所以这里的核心结论是:
高低符号在 B 端的观测分布中心是明显分离的。
4. window_coverage_rate = 1.000
这个指标表示窗口覆盖率是 100%。
也就是说,1401 个符号窗口全部都成功对齐并参与了分析,没有出现大面积丢窗或无效窗口。
这说明实验数据完整性很好。
2. 分类效果
1. accuracy = 0.989
总体准确率是 98.9%。
也就是在全部 1401 个符号窗口里,有 98.9% 被正确恢复。
这已经说明恢复效果非常强。
2. precision = 0.983
精确率表示:所有被预测成高符号 1 的窗口里,有 98.3% 真实就是 1。
这个值高,说明误报不多。
3. recall = 0.994
召回率表示:所有真实的高符号 1 里,有 99.4% 被成功检测出来。
这个值更高,说明漏检很少。
4. f1 = 0.989
F1 是 precision 和 recall 的综合指标。
接近 0.99,说明整体分类质量非常稳定。
5. fpr 和 fnr
fpr = 0.017fnr = 0.006
可以讲成:
- 真正的低符号中,只有 1.7% 被误判成高符号。
- 真正的高符号中,只有 0.6% 被误判成低符号。
这说明系统对高符号尤其敏感,漏检率很低。
3. 混淆矩阵
混淆矩阵如下:
tp = 696tn = 689fp = 12fn = 4
你可以直接这样给老师解释:
- 696 个高符号被正确识别出来。
- 689 个低符号被正确识别出来。
- 只有 12 个低符号被误报成高符号。
- 只有 4 个高符号被漏判成低符号。
所以总错误数只有:
- 12 + 4 = 16
总正确数是:
- 696 + 689 = 1385
这就是为什么准确率能达到 98.9%。
这里可以顺着给出一句结论:
B 端几乎可以完整恢复 A 端的二值符号序列。
4. Sample Aligned Windows
这张表是一些具体样本,用来展示分类过程不是黑箱,而是可以落到单个窗口上检查的。
例如:
样本 0
- A 端发出 100 kbps
- 标签是 0
- B 端测得平均吞吐率 826.902 kbps
- 低于阈值 934.272 kbps
- 所以预测成 0
这个分类是正确的。
样本 1
- A 端发出 133 kbps
- 标签是 1
- B 端测得平均吞吐率 1042.048 kbps
- 高于阈值
- 所以预测成 1
这个分类也是正确的。
样本 6
- A 端发出 100 kbps
- 标签是 0
- 但 B 端测得 959.642 kbps
- 高于阈值 934.272 kbps
- 所以被误判成 1
这个样本很有价值,因为它解释了错误是怎么来的。
也就是说,虽然 A 端真实发的是低符号,但由于网络波动、缓冲残留或者窗口串扰,B 端这个窗口的平均吞吐率被抬高了,于是被分错。