Complete standalone final deliverable and unaligned Q2 results

This commit is contained in:
2026-09-25 22:22:37 +08:00
parent c6b018e5d0
commit adc9c2064b
267 changed files with 15479 additions and 7976 deletions
+2
View File
@@ -2,6 +2,8 @@
本目录按《E题V2》第四章构建 Q1 对齐特征。规格核对与限制见 [`V2_REVIEW.md`](V2_REVIEW.md),可供后续训练读取的主特征包位于 `features_v2/`。
Q1 物理时间与附件二 `unaligned_50.pkl` 的相对进程现在共用 [`UNIFIED_ADAPTER.md`](UNIFIED_ADAPTER.md) 所述接口和投影核;Relative 模式的详细假设见 [`UNALIGNED_ADAPTER.md`](UNALIGNED_ADAPTER.md)。
## V2 特征包
- 100 个样本、300 条模态明细;主物理时间视图为 0.1 秒,并保留真实末段长度。
+123
View File
@@ -0,0 +1,123 @@
# 附件二未对齐版到 Q2 的输入适配:Relative 模式原理
当前统一接口、调用方式和全量审计以 [`UNIFIED_ADAPTER.md`](UNIFIED_ADAPTER.md) 为准。本文继续解释 Relative 坐标的假设、长度处理和限制。旧 `unaligned_adapter.py` 现在只是委托给统一接口的兼容层;Q2 实际调用 `adapter.adapt_official_split`。
## 结论与适用范围
`unaligned_50.pkl` 只有独立的序列索引,没有原始视频 PTS、音频采样时刻或词边界。Q1 主干的物理时间区间投影不能直接用于该文件。这里复用 Q1 的“来源区间与目标区间求交、只汇聚真实观测”算子,将每个模态的**相对索引进程** `[0,1)` 分成 50 段,供 Q2 的固定 50 步接口开展探索性训练。结果不是秒级同步结果,也不能生成原词到音频窗/视频帧的物理查询矩阵。《E题V2》4.5.3 的原式使用视频真实时长 `D_i`,本文件没有 `D_i`,因此这里是受其启发的近似适配,并非该式的严格复现。5.1.1 要求的可核验共同时间步仍需真实时间戳才能确立;本适配的 Q2 指标应单列为探索性结果。
Q1 的 B0 可借用区间交叠的计算形式,但权重中的区间在这里是归一化索引而非秒。B1 的 OpenFace 几何处理、B2 的 CTC 词边界后验、B4 的 Wav2Vec2 辅助特征和 B5 的原词查询矩阵都要求本文件未提供的来源定义或原始素材,不能因为维度相同就接到官方预计算特征上。Q2 使用的仍是附件二原有 `text/audio/vision` 特征列,只改变时序组织。
| 对比项 | Q1 原始素材主干 | 本适配器 |
|---|---|---|
| 来源定位 | 词边界、音频采样/窗口、视频帧 PTS | 各模态自身的整数行索引和可用长度 |
| 目标轴 | 真实秒数划分的 0.1 秒主网格 | 没有秒单位的 50 个相对索引进程格 |
| 可核验结论 | 可回查某词、某秒对应的原始素材 | 可回查目标格聚合了哪些预计算来源行 |
| 不能互换的内容 | 原生特征、真实质量与物理来源图 | 官方预计算特征、索引覆盖和长度歧义 |
## 为什么需要单独适配
Q1 原始样本流程知道词的起止秒数、音频窗口对应的采样范围、视频帧的真实 PTS,因此可以先定义共同的物理时间区间,再计算各来源与目标区间的交长。附件二未对齐版只给三组已提取的数字序列:文本最多 50 行,音频和视觉各最多 500 行。它们的第 10 行既不表示同一个时刻,也不带有足以恢复时刻的采样率和起点。直接把三个数组裁到前 50 行,或者把第 `r` 行视为共同时间步,都会制造未经数据支持的对应关系。
适配器采用一项明确的近似假设:**每个模态的有效索引范围分别覆盖同一片段的相对进程,索引顺序不变**。它把“片段前 2%”映射到统一的第 0 格,把“片段最后 2%”映射到第 49 格。若不同模态的采样不均匀、起止范围不同或存在长段未标注缺口,相同进程格也未必对应相同真实秒数。这是 Q2 固定长度输入的工程接口,不是从数字特征中推回了物理时间。
这里没有用情感标签、内容相似度、DTW 或可学习注意力去移动来源位置。附件二没有可核验的时间真值,用这些分数重排序列无法证明音画或词帧的物理对应正确;原始行顺序和来源索引因此始终保留。
## 第一步:确定来源范围与真实观测
对每条样本、每个模态分别记来源行数为 `L`,行观测标记为 `o_r`。适配器首先核对形状和数值是否有限,再按下表确定来源范围。来源全维零行仅用来判断官方预计算特征的行观测状态,不能把单个维度的正常零值判为缺失。
| 模态 | 原数组与维度 | 采用的 `L` | 行观测规则与例外 |
|---|---|---|---|
| 文本 | `text[50,768]` | `text_bert` 注意力掩码中连续有效前缀的长度 | 有效前缀内 `text` 行非全零才观测;掩码外的非零填充向量也排除。`raw_text` 不参与投影。 |
| 音频 | `audio[500,74]` | 官方 `audio_lengths` | 长度内非全零行才观测;若长度后仍有非零行,直接报错,不静默截断。 |
| 视觉 | `vision[500,35]` | `max(vision_lengths, 最后一个非零行索引 + 1)` | 若官方长度后仍有非零行,保留这些已知观测,并标记 `length_conflict` 与 `tail_ambiguous`。此时 `L` 只是包含所有已知观测的最小跨度,不代表已证实的真实帧数。 |
视觉长度冲突时,最后一个非零行之后的零尾段可能是填充,也可能仍属于有效但缺失的内容;数组本身无法区分。适配器没有把这段零尾宣布为已确认填充,也没有把缺失行数值插补后送入 Q2。文本注意力掩码还必须是从位置 0 开始的连续前缀;异常输入直接报错。
## 第二步:在相对进程轴上做区间投影
对某模态的 `L` 个来源行,以从 0 开始的索引 `r=0,…,L-1` 定义来源区间;对目标索引 `t=0,…,49` 定义统一的 50 个目标区间:
```text
J_r = [r/L, (r+1)/L)
I_t = [t/50, (t+1)/50)
w_tr = o_r · max(0, min((t+1)/50, (r+1)/L) - max(t/50, r/L))
W_t = Σ_r w_tr
```
只有真正观测的来源行才获得非零权重。区间均采用左闭右开形式;交长由区间端点确定,不通过情感标签或内容相似度调整顺序。`W_t>0` 时,第 `d` 个特征维度的投影值为
```text
x̂_t,d = (Σ_r w_tr · x_r,d) / W_t
P_tr = w_tr / W_t
```
`P_tr` 是目标位置 `t` 对来源行 `r` 的归一化贡献,整行和为 1。若 `W_t=0`,输出数值零仅作占位,目标观测掩码为假,`P` 的这一行保持全零;后续模型不能把占位零当成实际观测。计算不跨缺失区间插值,也不依据语义相似度把来源行重排。没有可靠的来源质量字段,因此所有有效行只使用单位质量权重。
一个简单例子:某音频片段有 `L=100` 个来源行,目标第 0 格覆盖 `[0,0.02)`,恰好与来源行 0、1 各重叠 `0.01`。若两行同一维取值分别为 2 和 4,投影值就是 3,来源权重各为 0.5。若来源行 1 缺失,投影值为 2、来源权重变为 1,但该格只有一半的索引区间有观测;不会用零把均值拉到 1。若某格内所有来源行均缺失,该格的掩码为假。这些数值展示的是索引交叠规则,不意味着每行对应 0.01 秒。
## 第三步:保存可回查的投影信息
`project_modality(...)` 对单条样本的一个模态返回 `ProjectedView`,各字段含义如下。
| 字段 | 形状或类型 | 含义 |
|---|---|---|
| `x` | `(50,d)`,`float32` | 交叠加权后的特征;`d` 保持为文本 768、音频 74、视觉 35。 |
| `observed` | `(50,)`,布尔值 | 目标格是否得到至少一个实际观测来源。 |
| `coverage` | `(50,)` | `min(1,50·W_t)`;目标格在**相对索引区间**内的观测覆盖比例,并非秒级覆盖率。 |
| `source_count` | `(50,)` | 对该目标格贡献非零交长的观测来源行数。 |
| `first_source`、`last_source` | 各 `(50,)` | 首末贡献来源索引;空格记为 `-1`。它们只是摘要,不能替代完整来源权重。 |
| `source_weights` | CSR 稀疏矩阵 `(50,L)` | 每个目标格到各原始来源行的 `P_tr`;可追溯平均值究竟来自哪些行。 |
| `reported_length`、`source_span`、`length_conflict`、`tail_ambiguous` | 样本级字段 | 对比官方长度与本次投影采用的跨度,保留视觉长度歧义。 |
例如 `source_weights.getrow(10)` 可列出目标第 10 格使用的原始索引及其权重。多格可能共享一个原始来源行,因为来源区间可能跨过 50 格的边界;共享不会产生新的独立观测。完整权重保存在单样本返回对象中,批量训练接口不会把所有样本的 CSR 矩阵装入模型。
## 第四步:交给 Q2 训练
`adapt_split(...)` 对一个官方划分逐样本调用上述算子,返回三个模态的 `(N,50,d)` 特征、`(N,50,3)` 观测掩码和划分级审计。`math/Q2/data.py` 的 `load_official_splits(..., version="unaligned_50")` 将它们封装成原有 `SplitData`;显式传入投影掩码,避免有效的正负特征在均值中恰好抵消为全零时被误判为缺失。样本 ID、官方 train/valid/test 划分和标签原样保留。
Q2 随后从官方训练集再按来源 `video_id` 划出拟合、可靠度选择和温度校准组。标准化器只在拟合组的已观测行上拟合;标准化后缺失位置继续保持零占位及假掩码。C5 按既有流程生成特征层连续缺口进行训练,模型输入仍为文本/音频/视觉 50 位序列及观测掩码。当前 C5 **不读取** `coverage`、`source_count` 或 CSR 权重作为质量值;官方未对齐文件没有真实质量分数,可靠度分支按 `q*=1, J_Q=0` 的回退规则运行。覆盖率可用于核验投影和后续独立研究,但不能直接宣称为检测质量。
适配过程不读情感标签,`raw_text` 不进入学生模型,也不使用验证或测试集拟合对齐参数。Q2 才读取标签执行监督训练与评估。附件二的 train、valid、test 都通过同一转换规则,测试集只用于最终评估。
## 接口
`unaligned_adapter.py` 的 `project_modality(...)` 返回一条样本的 50 位特征、观测掩码、覆盖率、来源行数、首末来源索引及完整的 CSR 来源权重矩阵;`adapt_split(...)` 处理官方一个划分并返回审计摘要。Q2 的 `load_official_splits(path, version="unaligned_50")` 直接调用此适配器,输出既有 `SplitData` 接口。调用方可按下面的方式读取一个划分,代码在仓库根目录运行时需把 `math/Q2` 加入 Python 导入路径:
```python
import sys
from pathlib import Path
sys.path.insert(0, "math/Q2")
from data import load_official_splits
splits = load_official_splits(
Path("E题数据/附件2-数据集特征文件/unaligned_50.pkl"),
version="unaligned_50",
)
train = splits["train"]
audio_features = train.x["audio"] # (3395, 50, 74)
observed = train.mask # (3395, 50, 3),顺序为文本、音频、视觉
audit = train.alignment_audit
```
单样本核验时直接调用 `project_modality`,可从 `source_weights.getrow(t)` 得到第 `t` 个目标格的原始行索引与权重。`load_official_splits` 只把三模态特征、掩码及审计摘要交给 Q2;单样本覆盖率和 CSR 来源权重不会默认送入预测网络。
Q2 训练入口:
```bash
uv run --project math/Q1 python math/Q2/train_unaligned_c5.py
```
未对齐版权重、尺度、指标和清单单独写入 `math/Q2/results_unaligned/`,与原有 aligned 版结果隔离。该入口固定使用原 aligned 实验已选定的 C5 结构,在未对齐版训练数据上重新拟合全部参数;未对齐版只选择可靠度与温度,不重新挑选架构。若需要完整 C0–C7 对照,可使用 `train.py --input-version unaligned_50 --skip-attachment3`。
批量转换在内存中确定性执行,没有另存一份接近原数据体积的派生特征文件;每次训练都从官方未对齐文件重建同一 50 位视图。`results_unaligned/` 保存权重、训练折尺度、指标、预测和含输入 SHA-256 的运行清单,不保存原始数据或大体积缓存。
## 已核对的输入异常与专项推理限制
附件二 train/valid/test 分别为 3395/728/727 条;`vision_lengths` 后仍出现观测行的样本分别为 618/141/131 条。文本注意力掩码外的非零 `text` 行分别有 86078/17772/18041 行,均按填充排除。这些数字是输入结构审计,不是对齐准确率。没有人工时间真值,不能报告秒级边界误差。
固定 C5 后,未对齐索引视图的验证集 Accuracy/Macro-F1/MAE 为 `0.6099/0.5253/0.6701`,测试集为 `0.6726/0.5668/0.7059`。这些指标说明该输入接口能够完成 Q2 训练与预测,不测量某个目标格是否与真实视频秒数吻合。与 aligned 版的指标差异还受到特征组织、训练随机性和视觉长度歧义影响,不能单独归因于投影规则。测试预测、视频组 Bootstrap 区间和运行参数分别保存在 `math/Q2/results_unaligned/test_predictions.csv`、`group_bootstrap_ci.csv` 和 `run_manifest.json`。
附件三的未对齐版本只有 `raw_text`、`audio`、`vision`,没有 `text`、`text_bert`、`audio_lengths` 或 `vision_lengths`。直接用 `raw_text` 重编码会绕过专项文本缺失;从最后一个非零行推断完整长度也无法区分尾部缺失和填充。因此当前训练比较不输出附件三未对齐版预测;要完成同版本专项推理,需补充可信长度/填充元数据,并为文本提供不泄漏缺失状态的数值特征或明确将文本全程标为不可观测。
+70
View File
@@ -0,0 +1,70 @@
# Q1 Unified Alignment Adapter:统一数据接口与审计
## 目的与边界
Q1 现在对外提供一个 `Q1AlignmentAdapter` 和一种 `AlignedMultimodalSample`。Q2、后续 Q3/Q4 使用相同的 `features / observed / coverage / provenance / metadata` 字段读取对齐结果。对齐的**坐标证据**仍必须区分:有原视频、音频与词级时间记录的 Q1 样本采用 `physical`;官方 `unaligned_50.pkl` 只有三条有序特征序列和长度字段,采用 `relative`。两者进入同一个区间求交投影核,但 `relative` 的位置绝不是秒、词边界或物理同步真值。
本次只重构数据接口。Q1 原有的词、CTC、视频 PTS、音频采样定位、0.1 秒密集主视图和查询矩阵仍由原生特征包保存;既有 `features_v2/` 文件没有重建。Q2 的结构、损失、超参数、权重和历史指标没有重训或改选。
## 共同投影核
给定来源区间 `J_r`、目标区间 `I_t`、观测标记 `O_r,d` 和来源质量 `q_r`,核先计算交长 `h_tr = |I_t ∩ J_r|`,再用 `h_tr q_r O_r,d` 对特征逐维加权平均。输出零值但 `observed=false` 表示目标格没有证据;不插值、不移动来源次序。目标格的 `coverage` 单独用 `h_tr O_r` 除以目标宽度,**不乘质量**。`quality_mean` 与 `quality_available_fraction` 分开返回;缺质量字段的 Relative 输入使用单位权重并明确标为不可用。
`source_weights` 是目标格到原始来源行的 CSR 权重矩阵:有观测的行和为 1,无观测的行全零。`source_count`、`first_source`、`last_source` 用于快速审计;`observed_dimensions` 与 `coverage_dimensions` 保留物理特征的逐维有效性。`source_span` 是参与构造坐标的跨度,`original_source_length` 是原数组行数。来源区间、目标区间由 [`adapter/coordinates.py`](adapter/coordinates.py) 的物理与相对构造函数建立,数值汇聚只有 [`adapter/projection.py`](adapter/projection.py) 一处实现。
| 模式 | 来源区间 | 50 个目标区间 | 可以声称的对应关系 |
|---|---|---|---|
| `physical` | Q1 保存的词、音频窗、视频帧真实时间区间 | `[tD/50,(t+1)D/50)`,`D` 为真实片段时长 | 可回查原素材时间与来源行;原有 0.1 秒视图仍单独保存 |
| `relative` | 每模态独立的 `[r/L,(r+1)/L)` | `[t/50,(t+1)/50)` | 仅表示各自序列中的相对进程,不能声称秒级同步 |
`mode="auto"` 只依据输入证据:Q1 物理源须有有效 `duration_s`、`media.status=ok`、源视频 SHA-256 和三模态原生时间区间;官方未对齐行须通过 `from_unaligned_record` 确认顺序、注意力掩码和长度。缺证据或物理证据损坏会报错,不根据数组是 50 行还是 500 行猜模式,也不在物理失败时悄悄退化为 Relative。
## 官方未对齐数据的来源规则
文本 `L` 取 `text_bert` 注意力掩码的连续有效前缀;掩码外的非零填充向量不参与投影。音频 `L` 取 `audio_lengths`,其后若仍有非零观测行则报错。视觉 `L=max(vision_lengths, 最后非零行位置+1)`;发现冲突时保留已知观测行,并设置 `length_conflict` 和 `tail_ambiguous`。全维零行代表该官方预计算序列中的未观测行,单个特征维度的零值不代表缺失。每个模态先各自规范化到 `[0,1)`,因此同一目标格只是一种有明确假设的输入组织方式。
## 标准接口
```python
import sys
sys.path.insert(0, "math/Q1")
from adapter import Q1AlignmentAdapter
adapter = Q1AlignmentAdapter(target_steps=50)
physical = adapter.from_q1_sample("-iRBcNs9oI8/8")
# split 是官方 unaligned_50.pkl 的 train、valid 或 test 字典
relative = adapter.from_unaligned_record(split, index=0)
features, mask = relative.q2_arrays()
assert features["text"].shape == (50, 768)
assert mask.shape == (50, 3)
weights = relative.provenance["audio"].source_weights.getrow(10)
original_rows, contributions = weights.indices, weights.data
```
`features[m]` 为 `(50,d)` float32,`observed[m]` 与 `coverage[m]` 各为 `(50,)`;`quality_mean[m]` 和 `quality_available_fraction[m]` 也按目标位置给出。`provenance[m]` 保存 CSR、来源跨度、原数组长度、官方报告长度、冲突标记和逐维有效性。`metadata` 明确写出 `coordinate_mode`、`coordinate_unit`、`physical_time_alignment`、`target_steps`、质量字段可用性、版本及样本 ID。Q2 的 [`data.py`](../Q2/data.py) 仅调用统一接口的 `adapt_official_split`,再沿用已有 `SplitData` 和模型输入;旧 [`unaligned_adapter.py`](unaligned_adapter.py) 只保留委托到新接口的兼容入口。
## 全量审计与兼容性
审计覆盖官方未对齐 train/valid/test 全部 **3,395 / 728 / 727,共 4,850 条**,并读取 Q1 物理样本 100 条。新 Relative 结果与重构前冻结的基线比较:每个划分的文本、音频、视觉 float32 数组和 `(N,50,3)` mask 的 SHA-256 **全部逐字节一致**。各划分的有效长度分布、`L<K / L=K / L>K` 数量、目标观测格及覆盖率分布、文本掩码外非零行、长度冲突、全缺失模态与目标格、非有限值、来源权重行和误差、输出形状及坐标模式均见 [`full_audit.json`](results/unified_adapter/full_audit.json)。冻结基线和直接对照分别见 [`legacy_relative_baseline.json`](results/unified_adapter/legacy_relative_baseline.json) 与 [`equivalence_report.json`](results/unified_adapter/equivalence_report.json)。
| 划分 | 样本 | 视觉长度冲突及尾部歧义 | 文本掩码外非零行 | 视觉无观测目标格 | 视觉全缺失样本 | 非有限输出 |
|---|---:|---:|---:|---:|---:|---:|
| train | 3,395 | 618 | 86,078 | 6,448 | 30 | 0 |
| valid | 728 | 141 | 17,772 | 1,085 | 0 | 0 |
| test | 727 | 131 | 18,041 | 1,316 | 8 | 0 |
物理样本 100/100 均进入 `physical`,三模态均为 50 步、无非有限输出,来源权重最大行和误差 `2.39e-7`。Relative 来源权重最大行和误差 `1.20e-7`。视觉长度冲突来自官方长度与非零行不一致,无法用当前文件证明尾部的真实视频时长。审计没有把索引覆盖率或质量回退值解释为检测质量或物理时间准确率。
Q2 的真实加载入口已完成**只读冒烟检查**:三个划分均保留原样本数及标签,输出文本 `(N,50,768)`、音频 `(N,50,74)`、视觉 `(N,50,35)`、掩码 `(N,50,3)`;记录见 [`q2_smoke.json`](results/unified_adapter/q2_smoke.json)。16 个单元测试涵盖 `L=K`、展开、聚合、部分/全部缺失、文本填充、音频/视觉长度冲突、来源权重守恒、两类坐标、Q2 形状、非有限值、全量旧新等价以及 `auto` 证据拒绝规则。
在仓库根目录复核:
```bash
uv run --project math/Q1 python -m unittest discover -s math/Q1/tests -p 'test_unified_adapter.py' -v
uv run --project math/Q1 python math/Q1/audit_unified_adapter.py
uv run --project math/Q1 python math/Q1/smoke_q2_adapter.py
```
附件三的未对齐版仍缺少这里所需的 `text`、`text_bert`、`audio_lengths`、`vision_lengths`,不能在没有可信缺失/长度元数据时自动套用这个 Relative 构造器。相关限制和原理细节见 [`UNALIGNED_ADAPTER.md`](UNALIGNED_ADAPTER.md)。
+17
View File
@@ -0,0 +1,17 @@
"""Unified Q1 alignment interface for physical time and relative progress."""
from .core import (
AlignedMultimodalSample,
AlignmentError,
ModalityProvenance,
Q1AlignmentAdapter,
adapt_official_split,
)
__all__ = [
"AlignedMultimodalSample",
"AlignmentError",
"ModalityProvenance",
"Q1AlignmentAdapter",
"adapt_official_split",
]
+26
View File
@@ -0,0 +1,26 @@
"""Coordinate constructors; no feature aggregation happens here."""
from __future__ import annotations
import numpy as np
def physical_targets(duration_s: float, steps: int) -> np.ndarray:
"""K fixed bins spanning verified real media duration, in seconds."""
duration = float(duration_s)
if not np.isfinite(duration) or duration <= 0 or steps < 1:
raise ValueError("physical targets require positive finite duration and steps")
edges = np.linspace(0.0, duration, steps + 1, dtype=np.float64)
return np.column_stack((edges[:-1], edges[1:]))
def relative_cells(length: int) -> np.ndarray:
"""Ordered source cells on a unit progress axis, with no time claim."""
if length < 1:
raise ValueError("relative source length must be positive")
left = np.arange(length, dtype=np.float64) / length
return np.column_stack((left, left + 1.0 / length))
def relative_targets(steps: int) -> np.ndarray:
"""K fixed cells on the same unit progress axis."""
return relative_cells(steps)
+238
View File
@@ -0,0 +1,238 @@
"""Q1's common sample contract and evidence-based coordinate dispatch."""
from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
import numpy as np
from scipy import sparse
from .coordinates import physical_targets, relative_cells, relative_targets
from .projection import project_intervals
MODALITIES = ("text", "audio", "vision")
DIMS = {"text": 768, "audio": 74, "vision": 35}
STEPS = {"text": 50, "audio": 500, "vision": 500}
K = 50
VERSION = "q1-unified-1"
class AlignmentError(ValueError):
"""The source cannot be assigned a defensible alignment coordinate."""
@dataclass
class ModalityProvenance:
source_weights: sparse.csr_matrix
source_count: np.ndarray
first_source: np.ndarray
last_source: np.ndarray
source_span: int
original_source_length: int
reported_length: int | None
length_conflict: bool = False
tail_ambiguous: bool = False
observed_dimensions: np.ndarray | None = None
coverage_dimensions: np.ndarray | None = None
quality_available: np.ndarray | None = None
@dataclass
class AlignedMultimodalSample:
features: dict[str, np.ndarray]
observed: dict[str, np.ndarray]
coverage: dict[str, np.ndarray]
provenance: dict[str, ModalityProvenance]
metadata: dict[str, Any]
target_intervals: np.ndarray
quality_mean: dict[str, np.ndarray] = field(default_factory=dict)
quality_available_fraction: dict[str, np.ndarray] = field(default_factory=dict)
def q2_arrays(self) -> tuple[dict[str, np.ndarray], np.ndarray]:
"""The existing Q2 model input shapes, without changing that model."""
return self.features, np.stack([self.observed[m] for m in MODALITIES], axis=-1)
@dataclass
class _Prepared:
values: np.ndarray
intervals: np.ndarray
observed: np.ndarray
quality: np.ndarray
reported_length: int | None
length_conflict: bool = False
tail_ambiguous: bool = False
quality_available: np.ndarray | None = None
def _relative_modality(record: dict[str, Any], name: str) -> _Prepared:
raw = np.asarray(record[name])
if raw.shape != (STEPS[name], DIMS[name]) or not np.isfinite(raw).all():
raise AlignmentError(f"{name}: expected finite {(STEPS[name], DIMS[name])}, got {raw.shape}")
if name == "text":
attention = np.asarray(record["attention_mask"], bool)
if attention.shape != (50,) or not np.array_equal(attention, np.arange(50) < int(attention.sum())):
raise AlignmentError("text attention mask must be a 50-position prefix")
length = int(attention.sum())
if length < 1:
raise AlignmentError("empty text attention mask")
span = length
observed = attention[:span] & np.any(raw[:span] != 0, axis=1)
conflict = ambiguous = False
else:
length = int(record[f"{name}_length"])
if length < 1 or length > STEPS[name]:
raise AlignmentError(f"{name}: invalid official length {length}")
nonzero = np.any(raw != 0, axis=1)
last = int(np.flatnonzero(nonzero)[-1]) + 1 if nonzero.any() else 0
conflict = last > length
if name == "audio" and conflict:
raise AlignmentError("audio contains observed positions beyond audio_lengths")
span = max(length, last)
observed = nonzero[:span]
ambiguous = bool(conflict)
return _Prepared(raw[:span].astype(np.float32, copy=False), relative_cells(span),
observed, np.ones(span, np.float32), length, bool(conflict), bool(ambiguous),
np.zeros(span, bool))
def _validate_physical(source: dict[str, Any]) -> tuple[float, dict[str, Any]]:
meta = source.get("_meta")
if not isinstance(meta, dict):
raise AlignmentError("physical mode requires stored Q1 metadata")
duration = float(meta.get("duration_s", float("nan")))
if not np.isfinite(duration) or duration <= 0:
raise AlignmentError("physical mode requires a finite positive duration")
if meta.get("media", {}).get("status") != "ok" or not meta.get("source_video_sha256"):
raise AlignmentError("physical mode requires verified media status and source hash")
for name in MODALITIES:
if f"native_{name}_intervals" not in source:
raise AlignmentError(f"physical mode lacks {name} timestamps")
return duration, meta
class Q1AlignmentAdapter:
"""Align either verified Q1 physical sources or official ordered sequences.
`auto` uses evidence in the input contract only; tensor shape never decides
whether time is physical. A malformed physical source is an error rather
than a silent relative fallback.
"""
def __init__(self, target_steps: int = K):
if target_steps < 1:
raise ValueError("target_steps must be positive")
self.target_steps = target_steps
def align(self, source: dict[str, Any], mode: str = "auto") -> AlignedMultimodalSample:
if mode not in {"auto", "physical", "relative"}:
raise AlignmentError(f"unsupported coordinate mode: {mode}")
if mode == "auto":
if "_meta" in source or any(k.startswith("native_") for k in source):
mode = "physical"
elif source.get("sequence_order_verified") is True:
mode = "relative"
else:
raise AlignmentError("auto mode requires Q1 physical evidence or verified sequence order")
if mode == "physical":
from q1_io import _native
duration, meta = _validate_physical(source)
target = physical_targets(duration, self.target_steps)
prepared = {}
for name in MODALITIES:
values, observed, intervals, quality, available = _native(source, name)
if np.asarray(intervals).shape != (len(values), 2) or np.any(np.asarray(intervals) < -1e-5) or np.any(np.asarray(intervals) > duration + 1e-5):
raise AlignmentError(f"{name}: physical timestamps outside media duration")
prepared[name] = _Prepared(values, intervals, observed, quality, len(values),
quality_available=available)
metadata = {"sample_id": meta.get("sample_id", f"{meta.get('video_id')}/{meta.get('clip_id')}"),
"coordinate_mode": "physical", "coordinate_unit": "seconds",
"physical_time_alignment": True, "duration_s": duration,
"source_video_sha256": meta["source_video_sha256"],
"dense_view": "views_sec_* (stored 0.1 s Q1 artifact)",
"quality_fields_available": {m: bool(np.asarray(prepared[m].quality_available).any()) for m in MODALITIES}}
else:
if source.get("sequence_order_verified") is not True:
raise AlignmentError("relative mode requires verified source order")
target = relative_targets(self.target_steps)
prepared = {name: _relative_modality(source, name) for name in MODALITIES}
metadata = {"sample_id": str(source.get("id", "")), "coordinate_mode": "relative",
"coordinate_unit": "normalized_progress", "physical_time_alignment": False,
"word_or_frame_timestamps_available": False,
"quality_fields_available": {m: False for m in MODALITIES}}
features = {}
observed = {}
coverage = {}
quality_mean = {}
quality_available_fraction = {}
provenance = {}
for name in MODALITIES:
item = prepared[name]
result = project_intervals(item.values, item.intervals, target, item.observed, item.quality,
item.quality_available)
features[name] = result.x
observed[name] = result.observed
coverage[name] = result.coverage
quality_mean[name] = result.quality_mean
quality_available_fraction[name] = result.quality_available_fraction
provenance[name] = ModalityProvenance(result.source_weights, result.source_count,
result.first_source, result.last_source, len(item.values),
len(source[name]) if mode == "relative" else len(item.values), item.reported_length,
item.length_conflict, item.tail_ambiguous, result.observed_dimensions,
result.coverage_dimensions, item.quality_available)
metadata.update({"target_steps": self.target_steps, "adapter_version": VERSION})
return AlignedMultimodalSample(features, observed, coverage, provenance, metadata, target,
quality_mean, quality_available_fraction)
def from_q1_sample(self, sample_id: str, feature_dir: Path | None = None) -> AlignedMultimodalSample:
from q1_io import FEATURE_DIR, load_sample
return self.align(load_sample(sample_id, FEATURE_DIR if feature_dir is None else feature_dir), "auto")
def from_unaligned_record(self, split: dict[str, Any], index: int) -> AlignedMultimodalSample:
"""Build verified ordered input from one official unaligned pickle row."""
attention = np.asarray(split["text_bert"][index, 1], bool)
raw_id = split["id"][index]
if isinstance(raw_id, bytes):
raw_id = raw_id.decode("utf-8", errors="replace")
record = {"id": str(raw_id), "sequence_order_verified": True,
"attention_mask": attention,
"text": split["text"][index], "audio": split["audio"][index],
"vision": split["vision"][index],
"audio_length": int(split["audio_lengths"][index]),
"vision_length": int(split["vision_lengths"][index])}
return self.align(record, "auto")
def adapt_official_split(split: dict[str, Any]) -> tuple[dict[str, np.ndarray], np.ndarray, dict[str, Any]]:
"""Q2's batch bridge; all rows are produced through Q1AlignmentAdapter."""
n = len(split["id"])
output = {m: np.zeros((n, K, DIMS[m]), np.float32) for m in MODALITIES}
masks = np.zeros((n, K, len(MODALITIES)), bool)
conflicts = ambiguous = padding = 0
coverage_sum = {m: 0.0 for m in MODALITIES}
observed_rows = {m: 0 for m in MODALITIES}
adapter = Q1AlignmentAdapter()
for i in range(n):
attention = np.asarray(split["text_bert"][i, 1], bool)
padding += int(np.count_nonzero(np.any(split["text"][i] != 0, axis=1) & ~attention))
sample = adapter.from_unaligned_record(split, i)
for j, name in enumerate(MODALITIES):
output[name][i] = sample.features[name]
masks[i, :, j] = sample.observed[name]
coverage_sum[name] += float(sample.coverage[name].sum())
observed_rows[name] += int(sample.observed[name].sum())
conflicts += int(sample.provenance["vision"].length_conflict)
ambiguous += int(sample.provenance["vision"].tail_ambiguous)
audit = {"method": "shared_interval_overlap_on_normalized_progress",
"coordinate_mode": "relative", "physical_time_alignment": False,
"samples": n, "vision_length_conflict_samples": conflicts,
"vision_tail_ambiguous_samples": ambiguous,
"nonzero_text_rows_outside_attention": padding,
"observed_target_rows": observed_rows,
"mean_target_coverage": {m: coverage_sum[m] / (n * K) for m in MODALITIES},
"quality_fields_available": False,
"word_or_frame_timestamps_available": False}
return output, masks, audit
+108
View File
@@ -0,0 +1,108 @@
"""The sole interval overlap projection kernel used by both coordinate modes."""
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
from scipy import sparse
@dataclass
class Projection:
x: np.ndarray
observed: np.ndarray
coverage: np.ndarray
source_count: np.ndarray
first_source: np.ndarray
last_source: np.ndarray
source_weights: sparse.csr_matrix
observed_dimensions: np.ndarray
coverage_dimensions: np.ndarray
quality_mean: np.ndarray
quality_available_fraction: np.ndarray
def project_intervals(
values: np.ndarray,
source_intervals: np.ndarray,
target_intervals: np.ndarray,
observed: np.ndarray,
quality: np.ndarray | None = None,
quality_available: np.ndarray | None = None,
) -> Projection:
"""Project source cells using overlap * quality * observed validity.
Row provenance uses any valid dimension. Feature values use validity per
dimension, so partially observed physical features remain partially missing.
"""
source = np.asarray(values, dtype=np.float32)
src = np.asarray(source_intervals, dtype=np.float64)
dst = np.asarray(target_intervals, dtype=np.float64)
if source.ndim != 2 or src.shape != (len(source), 2) or dst.ndim != 2 or dst.shape[1] != 2:
raise ValueError("inconsistent source features or interval dimensions")
if not np.isfinite(source).all() or not np.isfinite(src).all() or not np.isfinite(dst).all():
raise ValueError("non-finite source features or intervals")
if np.any(src[:, 1] < src[:, 0]) or np.any(dst[:, 1] <= dst[:, 0]):
raise ValueError("source widths must be nonnegative and target widths positive")
obs = np.asarray(observed, bool)
if obs.shape == (len(source),):
obs_dim = np.broadcast_to(obs[:, None], source.shape)
elif obs.shape == source.shape:
obs_dim = obs
obs = obs.any(axis=1)
else:
raise ValueError("observed must have source-row or source-feature shape")
q = np.ones(len(source), dtype=np.float64) if quality is None else np.asarray(quality, dtype=np.float64)
if q.shape != (len(source),) or not np.isfinite(q).all() or np.any(q < 0):
raise ValueError("quality must be finite and nonnegative per source row")
overlap = np.maximum(0.0, np.minimum(dst[:, None, 1], src[None, :, 1])
- np.maximum(dst[:, None, 0], src[None, :, 0]))
available = np.zeros(len(source), bool) if quality_available is None else np.asarray(quality_available, bool)
if available.shape != (len(source),):
raise ValueError("quality availability must be per source row")
# Keep the same multiplication and accumulation order as the original
# official-unaligned projection when quality is uniformly one.
physical = overlap.copy()
physical *= obs[None, :]
row_weight = physical.copy()
row_weight *= q[None, :]
mass = row_weight.sum(axis=1)
row_valid = mass > 0
normalized = np.zeros_like(row_weight, dtype=np.float32)
normalized[row_valid] = (row_weight[row_valid] / mass[row_valid, None]).astype(np.float32)
support = row_weight > 0
count = support.sum(axis=1).astype(np.uint16)
first = np.full(len(dst), -1, dtype=np.int32)
last = np.full(len(dst), -1, dtype=np.int32)
if row_valid.any():
first[row_valid] = support[row_valid].argmax(axis=1)
last[row_valid] = len(source) - 1 - support[row_valid, ::-1].argmax(axis=1)
width = dst[:, 1] - dst[:, 0]
physical_mass = physical.sum(axis=1)
coverage = np.clip(physical_mass / width, 0.0, 1.0).astype(np.float32)
qmean = np.ones(len(dst), np.float32)
qavailable = np.zeros(len(dst), np.float32)
physical_valid = physical_mass > 0
qmean[physical_valid] = (mass[physical_valid] / physical_mass[physical_valid]).astype(np.float32)
qavailable[physical_valid] = ((physical[physical_valid] @ available.astype(np.float64))
/ physical_mass[physical_valid]).astype(np.float32)
# The common full-dimension case follows the original matrix product
# exactly; this is also much faster for 500 x 768 input.
if np.array_equal(obs_dim, np.broadcast_to(obs[:, None], source.shape)):
x = np.zeros((len(dst), source.shape[1]), np.float32)
x[row_valid] = ((row_weight[row_valid] @ source) / mass[row_valid, None]).astype(np.float32)
observed_dimensions = np.broadcast_to(row_valid[:, None], x.shape).copy()
coverage_dimensions = np.broadcast_to(coverage[:, None], x.shape).copy()
else:
dim_physical = overlap[:, :, None] * obs_dim[None, :, :]
dim_weight = dim_physical * q[None, :, None]
dim_mass = dim_weight.sum(axis=1)
observed_dimensions = dim_mass > 0
x = np.zeros((len(dst), source.shape[1]), np.float32)
numerator = np.einsum("ksd,sd->kd", dim_weight, source, optimize=True)
x[observed_dimensions] = (numerator[observed_dimensions] / dim_mass[observed_dimensions]).astype(np.float32)
coverage_dimensions = np.clip(dim_physical.sum(axis=1) / width[:, None], 0, 1).astype(np.float32)
return Projection(x, row_valid, coverage, count, first, last,
sparse.csr_matrix(normalized), observed_dimensions, coverage_dimensions,
qmean, qavailable)
+151
View File
@@ -0,0 +1,151 @@
"""Full Q1 physical and official unaligned split audit; no model training."""
from __future__ import annotations
import hashlib
import json
import sys
from collections import Counter
from pathlib import Path
import numpy as np
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
sys.path.insert(0, str(HERE))
sys.path.insert(0, str(ROOT / "math" / "Q2"))
from adapter import Q1AlignmentAdapter # noqa: E402
from data import restricted_load # noqa: E402
MODALITIES = ("text", "audio", "vision")
RESULTS = HERE / "results" / "unified_adapter"
OFFICIAL = ROOT / "E题数据" / "附件2-数据集特征文件" / "unaligned_50.pkl"
def _distribution(values: list[float]) -> dict[str, float | int]:
arr = np.asarray(values, np.float64)
return {"count": int(len(arr)), "min": float(arr.min()) if len(arr) else 0.0,
"p25": float(np.percentile(arr, 25)) if len(arr) else 0.0,
"median": float(np.median(arr)) if len(arr) else 0.0,
"p75": float(np.percentile(arr, 75)) if len(arr) else 0.0,
"max": float(arr.max()) if len(arr) else 0.0,
"mean": float(arr.mean()) if len(arr) else 0.0}
def _audit_split(name: str, split: dict, baseline: dict) -> dict:
adapter = Q1AlignmentAdapter()
n = len(split["id"])
hashes = {m: hashlib.sha256() for m in MODALITIES}
mask_hash = hashlib.sha256()
lengths = {m: [] for m in MODALITIES}
observed_bins = {m: [] for m in MODALITIES}
all_missing_bins = Counter()
coverages = {m: [] for m in MODALITIES}
relations = {m: Counter() for m in MODALITIES}
all_missing = Counter()
conflicts = Counter()
text_padding_nonzero = 0
max_provenance_error = 0.0
max_nonfinite = 0
modes = Counter()
shapes = {m: Counter() for m in MODALITIES}
for i in range(n):
sample = adapter.from_unaligned_record(split, i)
modes[sample.metadata["coordinate_mode"]] += 1
stacked_mask = np.stack([sample.observed[m] for m in MODALITIES], axis=-1)
mask_hash.update(stacked_mask.tobytes(order="C"))
attention = np.asarray(split["text_bert"][i, 1], bool)
text_padding_nonzero += int(np.count_nonzero(np.any(split["text"][i] != 0, axis=1) & ~attention))
for m in MODALITIES:
x = sample.features[m]
p = sample.provenance[m]
hashes[m].update(x.tobytes(order="C"))
shapes[m][str(list(x.shape))] += 1
lengths[m].append(p.source_span)
observed_bins[m].append(int(sample.observed[m].sum()))
all_missing_bins[m] += int((~sample.observed[m]).sum())
coverages[m].extend(sample.coverage[m].tolist())
relations[m]["L<K" if p.source_span < 50 else "L=K" if p.source_span == 50 else "L>K"] += 1
all_missing[m] += int(not sample.observed[m].any())
conflicts[m] += int(p.length_conflict)
if m == "vision":
all_missing["vision_tail_ambiguous"] += int(p.tail_ambiguous)
row_sums = np.asarray(p.source_weights.sum(axis=1)).reshape(-1)
if sample.observed[m].any():
max_provenance_error = max(max_provenance_error,
float(np.max(np.abs(row_sums[sample.observed[m]] - 1.0))))
max_provenance_error = max(max_provenance_error,
float(np.max(np.abs(row_sums[~sample.observed[m]]))) if (~sample.observed[m]).any() else 0.0)
max_nonfinite += int(np.count_nonzero(~np.isfinite(x)))
actual = {m: h.hexdigest() for m, h in hashes.items()}
expected = baseline[name]["sha256"]
return {"samples": n, "coordinate_modes": dict(modes), "feature_shapes": {m: dict(v) for m, v in shapes.items()},
"source_length": {m: _distribution(v) for m, v in lengths.items()},
"source_length_vs_K": {m: dict(v) for m, v in relations.items()},
"observed_target_bins_per_sample": {m: _distribution(v) for m, v in observed_bins.items()},
"coverage_per_target_bin": {m: _distribution(v) for m, v in coverages.items()},
"text_nonzero_rows_outside_attention": text_padding_nonzero,
"length_conflict_samples": dict(conflicts),
"tail_ambiguous_samples": all_missing["vision_tail_ambiguous"],
"all_missing_target_bins": dict(all_missing_bins),
"all_missing_modality_samples": {m: all_missing[m] for m in MODALITIES},
"nonfinite_output_values": max_nonfinite,
"max_provenance_row_sum_error": max_provenance_error,
"sha256": actual, "legacy_sha256": expected,
"exact_feature_equivalence": {m: actual[m] == expected[m] for m in MODALITIES},
"mask_sha256": mask_hash.hexdigest(),
"exact_mask_equivalence": mask_hash.hexdigest() == baseline[name]["mask_sha256"]}
def _audit_physical() -> dict:
manifest = HERE / "features_v2" / "manifest_q1.jsonl"
ids = [json.loads(line)["sample_id"] for line in manifest.read_text(encoding="utf-8").splitlines()]
adapter = Q1AlignmentAdapter()
modes = Counter()
nonfinite = 0
provenance_error = 0.0
shapes = {m: Counter() for m in MODALITIES}
duration = []
for sample_id in ids:
sample = adapter.from_q1_sample(sample_id)
modes[sample.metadata["coordinate_mode"]] += 1
duration.append(sample.metadata["duration_s"])
for m in MODALITIES:
x = sample.features[m]
shapes[m][str(list(x.shape))] += 1
nonfinite += int(np.count_nonzero(~np.isfinite(x)))
sums = np.asarray(sample.provenance[m].source_weights.sum(axis=1)).ravel()
observed = sample.observed[m]
if observed.any():
provenance_error = max(provenance_error, float(np.max(np.abs(sums[observed] - 1))))
return {"samples": len(ids), "coordinate_modes": dict(modes), "feature_shapes": {m: dict(v) for m, v in shapes.items()},
"duration_s": _distribution(duration), "nonfinite_output_values": nonfinite,
"max_provenance_row_sum_error": provenance_error,
"stored_dense_0_1_s_views_untouched": True}
def main() -> None:
RESULTS.mkdir(parents=True, exist_ok=True)
baseline = json.loads((RESULTS / "legacy_relative_baseline.json").read_text(encoding="utf-8"))
obj = restricted_load(OFFICIAL)
report = {"adapter": "q1-unified-1", "official_input": str(OFFICIAL.relative_to(ROOT)),
"relative": {name: _audit_split(name, obj[name], baseline) for name in ("train", "valid", "test")}}
del obj
report["physical"] = _audit_physical()
(RESULTS / "full_audit.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
equivalence = {"comparison": "SHA-256 of full float32 feature arrays and boolean masks against frozen old adapter output",
"splits": {name: {"samples": v["samples"], "exact_feature_equivalence": v["exact_feature_equivalence"],
"exact_mask_equivalence": v["exact_mask_equivalence"],
"old_feature_sha256": v["legacy_sha256"], "new_feature_sha256": v["sha256"],
"old_mask_sha256": baseline[name]["mask_sha256"],
"new_mask_sha256": v["mask_sha256"]}
for name, v in report["relative"].items()}}
(RESULTS / "equivalence_report.json").write_text(json.dumps(equivalence, indent=2), encoding="utf-8")
print(json.dumps({"relative": {name: {"samples": v["samples"],
"exact_feature_equivalence": v["exact_feature_equivalence"],
"exact_mask_equivalence": v["exact_mask_equivalence"]} for name, v in report["relative"].items()},
"physical_samples": report["physical"]["samples"]}, indent=2))
if __name__ == "__main__":
main()
+1 -1
View File
@@ -63,7 +63,7 @@ def _native(sample: dict[str, Any], modality: str) -> tuple[np.ndarray, np.ndarr
sample[f"native_{modality}_mask"].astype(bool),
sample[f"native_{modality}_intervals"].astype(np.float32),
sample[f"native_{modality}_quality"].astype(np.float32),
np.full(len(sample[f"native_{modality}_times"]), bool(sample[f"native_{modality}_quality_available"]), dtype=bool),
np.asarray(sample[f"native_{modality}_quality_available"], dtype=bool),
)
if modality == "speech":
meta = sample["_meta"]
@@ -0,0 +1,68 @@
{
"comparison": "SHA-256 of full float32 feature arrays and boolean masks against frozen old adapter output",
"splits": {
"train": {
"samples": 3395,
"exact_feature_equivalence": {
"text": true,
"audio": true,
"vision": true
},
"exact_mask_equivalence": true,
"old_feature_sha256": {
"text": "7dd4282937b91f5f654ab1aee176df729c0e6bd2499c5c7a3072cff459ce1ae3",
"audio": "44a746e4b03bed665537f124544df8331fe53d33a639f3edcb39cb5bcc2a96e0",
"vision": "516a29f75c4c251c1cb1bcb2eef58d4bfd7b4d27c96badc1023f4a06f15d95b8"
},
"new_feature_sha256": {
"text": "7dd4282937b91f5f654ab1aee176df729c0e6bd2499c5c7a3072cff459ce1ae3",
"audio": "44a746e4b03bed665537f124544df8331fe53d33a639f3edcb39cb5bcc2a96e0",
"vision": "516a29f75c4c251c1cb1bcb2eef58d4bfd7b4d27c96badc1023f4a06f15d95b8"
},
"old_mask_sha256": "8609e08eb50aff524442bef5acfc1a7dfa201819486a64e00cd6d34f2be34f3a",
"new_mask_sha256": "8609e08eb50aff524442bef5acfc1a7dfa201819486a64e00cd6d34f2be34f3a"
},
"valid": {
"samples": 728,
"exact_feature_equivalence": {
"text": true,
"audio": true,
"vision": true
},
"exact_mask_equivalence": true,
"old_feature_sha256": {
"text": "16bdef07347baf00985e7964301eef2272701716220e846c415651806bb5eb0a",
"audio": "b7d6f3092d9ddbcd70afe4b0ecff0490181a83a308a67246613185167cb08e87",
"vision": "fbd9f93e8cae02de59e5d513c4c057d759db626894d298124575bdf18863a024"
},
"new_feature_sha256": {
"text": "16bdef07347baf00985e7964301eef2272701716220e846c415651806bb5eb0a",
"audio": "b7d6f3092d9ddbcd70afe4b0ecff0490181a83a308a67246613185167cb08e87",
"vision": "fbd9f93e8cae02de59e5d513c4c057d759db626894d298124575bdf18863a024"
},
"old_mask_sha256": "adcd9b7bac94c6523b571c7c60eb3e4ad0efc820062bd528b102a4239aaf2325",
"new_mask_sha256": "adcd9b7bac94c6523b571c7c60eb3e4ad0efc820062bd528b102a4239aaf2325"
},
"test": {
"samples": 727,
"exact_feature_equivalence": {
"text": true,
"audio": true,
"vision": true
},
"exact_mask_equivalence": true,
"old_feature_sha256": {
"text": "6cc0c3ee828ab2f322318011b10764aabca2f5d2bcba40abddd714529928a05a",
"audio": "d64dca47fffbbd00a2d8f5628f05efdb9b8743ff51187b1c4cac58ccf3f5a50b",
"vision": "2a8b12d9c3a0d57f8f14bb830dbd2f8acdcfa147f55c50927798692ae6630d6d"
},
"new_feature_sha256": {
"text": "6cc0c3ee828ab2f322318011b10764aabca2f5d2bcba40abddd714529928a05a",
"audio": "d64dca47fffbbd00a2d8f5628f05efdb9b8743ff51187b1c4cac58ccf3f5a50b",
"vision": "2a8b12d9c3a0d57f8f14bb830dbd2f8acdcfa147f55c50927798692ae6630d6d"
},
"old_mask_sha256": "85706ad6842e8a0642bf5d24642463d0488a6cba55848a2276e397e8592604d6",
"new_mask_sha256": "85706ad6842e8a0642bf5d24642463d0488a6cba55848a2276e397e8592604d6"
}
}
}
@@ -0,0 +1,503 @@
{
"adapter": "q1-unified-1",
"official_input": "E题数据/附件2-数据集特征文件/unaligned_50.pkl",
"relative": {
"train": {
"samples": 3395,
"coordinate_modes": {
"relative": 3395
},
"feature_shapes": {
"text": {
"[50, 768]": 3395
},
"audio": {
"[50, 74]": 3395
},
"vision": {
"[50, 35]": 3395
}
},
"source_length": {
"text": {
"count": 3395,
"min": 3.0,
"p25": 16.0,
"median": 22.0,
"p75": 32.0,
"max": 50.0,
"mean": 24.645655375552284
},
"audio": {
"count": 3395,
"min": 8.0,
"p25": 86.0,
"median": 127.0,
"p75": 183.0,
"max": 500.0,
"mean": 147.28836524300442
},
"vision": {
"count": 3395,
"min": 1.0,
"p25": 61.0,
"median": 93.0,
"p75": 135.0,
"max": 500.0,
"mean": 107.47304860088366
}
},
"source_length_vs_K": {
"text": {
"L<K": 3142,
"L=K": 253
},
"audio": {
"L>K": 3173,
"L<K": 209,
"L=K": 13
},
"vision": {
"L>K": 2797,
"L<K": 573,
"L=K": 25
}
},
"observed_target_bins_per_sample": {
"text": {
"count": 3395,
"min": 50.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 50.0
},
"audio": {
"count": 3395,
"min": 50.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 50.0
},
"vision": {
"count": 3395,
"min": 0.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 48.10073637702504
}
},
"coverage_per_target_bin": {
"text": {
"count": 169750,
"min": 1.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 1.0
},
"audio": {
"count": 169750,
"min": 1.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 1.0
},
"vision": {
"count": 169750,
"min": 0.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 0.9540337704196024
}
},
"text_nonzero_rows_outside_attention": 86078,
"length_conflict_samples": {
"text": 0,
"audio": 0,
"vision": 618
},
"tail_ambiguous_samples": 618,
"all_missing_target_bins": {
"text": 0,
"audio": 0,
"vision": 6448
},
"all_missing_modality_samples": {
"text": 0,
"audio": 0,
"vision": 30
},
"nonfinite_output_values": 0,
"max_provenance_row_sum_error": 1.1920928955078125e-07,
"sha256": {
"text": "7dd4282937b91f5f654ab1aee176df729c0e6bd2499c5c7a3072cff459ce1ae3",
"audio": "44a746e4b03bed665537f124544df8331fe53d33a639f3edcb39cb5bcc2a96e0",
"vision": "516a29f75c4c251c1cb1bcb2eef58d4bfd7b4d27c96badc1023f4a06f15d95b8"
},
"legacy_sha256": {
"text": "7dd4282937b91f5f654ab1aee176df729c0e6bd2499c5c7a3072cff459ce1ae3",
"audio": "44a746e4b03bed665537f124544df8331fe53d33a639f3edcb39cb5bcc2a96e0",
"vision": "516a29f75c4c251c1cb1bcb2eef58d4bfd7b4d27c96badc1023f4a06f15d95b8"
},
"exact_feature_equivalence": {
"text": true,
"audio": true,
"vision": true
},
"mask_sha256": "8609e08eb50aff524442bef5acfc1a7dfa201819486a64e00cd6d34f2be34f3a",
"exact_mask_equivalence": true
},
"valid": {
"samples": 728,
"coordinate_modes": {
"relative": 728
},
"feature_shapes": {
"text": {
"[50, 768]": 728
},
"audio": {
"[50, 74]": 728
},
"vision": {
"[50, 35]": 728
}
},
"source_length": {
"text": {
"count": 728,
"min": 3.0,
"p25": 17.0,
"median": 23.5,
"p75": 33.0,
"max": 50.0,
"mean": 25.587912087912088
},
"audio": {
"count": 728,
"min": 12.0,
"p25": 98.75,
"median": 138.0,
"p75": 189.25,
"max": 500.0,
"mean": 154.11263736263737
},
"vision": {
"count": 728,
"min": 1.0,
"p25": 72.75,
"median": 102.0,
"p75": 141.25,
"max": 500.0,
"mean": 114.28846153846153
}
},
"source_length_vs_K": {
"text": {
"L<K": 681,
"L=K": 47
},
"audio": {
"L>K": 697,
"L<K": 27,
"L=K": 4
},
"vision": {
"L>K": 645,
"L<K": 77,
"L=K": 6
}
},
"observed_target_bins_per_sample": {
"text": {
"count": 728,
"min": 50.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 50.0
},
"audio": {
"count": 728,
"min": 50.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 50.0
},
"vision": {
"count": 728,
"min": 2.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 48.50961538461539
}
},
"coverage_per_target_bin": {
"text": {
"count": 36400,
"min": 1.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 1.0
},
"audio": {
"count": 36400,
"min": 1.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 1.0
},
"vision": {
"count": 36400,
"min": 0.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 0.9609031476994342
}
},
"text_nonzero_rows_outside_attention": 17772,
"length_conflict_samples": {
"text": 0,
"audio": 0,
"vision": 141
},
"tail_ambiguous_samples": 141,
"all_missing_target_bins": {
"text": 0,
"audio": 0,
"vision": 1085
},
"all_missing_modality_samples": {
"text": 0,
"audio": 0,
"vision": 0
},
"nonfinite_output_values": 0,
"max_provenance_row_sum_error": 1.1920928955078125e-07,
"sha256": {
"text": "16bdef07347baf00985e7964301eef2272701716220e846c415651806bb5eb0a",
"audio": "b7d6f3092d9ddbcd70afe4b0ecff0490181a83a308a67246613185167cb08e87",
"vision": "fbd9f93e8cae02de59e5d513c4c057d759db626894d298124575bdf18863a024"
},
"legacy_sha256": {
"text": "16bdef07347baf00985e7964301eef2272701716220e846c415651806bb5eb0a",
"audio": "b7d6f3092d9ddbcd70afe4b0ecff0490181a83a308a67246613185167cb08e87",
"vision": "fbd9f93e8cae02de59e5d513c4c057d759db626894d298124575bdf18863a024"
},
"exact_feature_equivalence": {
"text": true,
"audio": true,
"vision": true
},
"mask_sha256": "adcd9b7bac94c6523b571c7c60eb3e4ad0efc820062bd528b102a4239aaf2325",
"exact_mask_equivalence": true
},
"test": {
"samples": 727,
"coordinate_modes": {
"relative": 727
},
"feature_shapes": {
"text": {
"[50, 768]": 727
},
"audio": {
"[50, 74]": 727
},
"vision": {
"[50, 35]": 727
}
},
"source_length": {
"text": {
"count": 727,
"min": 4.0,
"p25": 16.0,
"median": 23.0,
"p75": 32.0,
"max": 50.0,
"mean": 25.184319119669876
},
"audio": {
"count": 727,
"min": 23.0,
"p25": 91.0,
"median": 135.0,
"p75": 190.0,
"max": 500.0,
"mean": 155.87757909215955
},
"vision": {
"count": 727,
"min": 1.0,
"p25": 65.0,
"median": 101.0,
"p75": 141.5,
"max": 500.0,
"mean": 114.78404401650619
}
},
"source_length_vs_K": {
"text": {
"L=K": 52,
"L<K": 675
},
"audio": {
"L>K": 681,
"L<K": 43,
"L=K": 3
},
"vision": {
"L>K": 615,
"L<K": 109,
"L=K": 3
}
},
"observed_target_bins_per_sample": {
"text": {
"count": 727,
"min": 50.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 50.0
},
"audio": {
"count": 727,
"min": 50.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 50.0
},
"vision": {
"count": 727,
"min": 0.0,
"p25": 50.0,
"median": 50.0,
"p75": 50.0,
"max": 50.0,
"mean": 48.189821182943604
}
},
"coverage_per_target_bin": {
"text": {
"count": 36350,
"min": 1.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 1.0
},
"audio": {
"count": 36350,
"min": 1.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 1.0
},
"vision": {
"count": 36350,
"min": 0.0,
"p25": 1.0,
"median": 1.0,
"p75": 1.0,
"max": 1.0,
"mean": 0.9549938173243645
}
},
"text_nonzero_rows_outside_attention": 18041,
"length_conflict_samples": {
"text": 0,
"audio": 0,
"vision": 131
},
"tail_ambiguous_samples": 131,
"all_missing_target_bins": {
"text": 0,
"audio": 0,
"vision": 1316
},
"all_missing_modality_samples": {
"text": 0,
"audio": 0,
"vision": 8
},
"nonfinite_output_values": 0,
"max_provenance_row_sum_error": 1.1920928955078125e-07,
"sha256": {
"text": "6cc0c3ee828ab2f322318011b10764aabca2f5d2bcba40abddd714529928a05a",
"audio": "d64dca47fffbbd00a2d8f5628f05efdb9b8743ff51187b1c4cac58ccf3f5a50b",
"vision": "2a8b12d9c3a0d57f8f14bb830dbd2f8acdcfa147f55c50927798692ae6630d6d"
},
"legacy_sha256": {
"text": "6cc0c3ee828ab2f322318011b10764aabca2f5d2bcba40abddd714529928a05a",
"audio": "d64dca47fffbbd00a2d8f5628f05efdb9b8743ff51187b1c4cac58ccf3f5a50b",
"vision": "2a8b12d9c3a0d57f8f14bb830dbd2f8acdcfa147f55c50927798692ae6630d6d"
},
"exact_feature_equivalence": {
"text": true,
"audio": true,
"vision": true
},
"mask_sha256": "85706ad6842e8a0642bf5d24642463d0488a6cba55848a2276e397e8592604d6",
"exact_mask_equivalence": true
}
},
"physical": {
"samples": 100,
"coordinate_modes": {
"physical": 100
},
"feature_shapes": {
"text": {
"[50, 768]": 100
},
"audio": {
"[50, 74]": 100
},
"vision": {
"[50, 35]": 100
}
},
"duration_s": {
"count": 100,
"min": 2.256999969482422,
"p25": 5.084147214889526,
"median": 6.722493410110474,
"p75": 8.725488424301147,
"max": 29.288021087646484,
"mean": 7.874980225563049
},
"nonfinite_output_values": 0,
"max_provenance_row_sum_error": 2.384185791015625e-07,
"stored_dense_0_1_s_views_untouched": true
}
}
@@ -0,0 +1,86 @@
{
"train": {
"sha256": {
"text": "7dd4282937b91f5f654ab1aee176df729c0e6bd2499c5c7a3072cff459ce1ae3",
"audio": "44a746e4b03bed665537f124544df8331fe53d33a639f3edcb39cb5bcc2a96e0",
"vision": "516a29f75c4c251c1cb1bcb2eef58d4bfd7b4d27c96badc1023f4a06f15d95b8"
},
"mask_sha256": "8609e08eb50aff524442bef5acfc1a7dfa201819486a64e00cd6d34f2be34f3a",
"audit": {
"method": "index_cell_overlap_on_independent_normalized_progress_axes",
"physical_time_alignment": false,
"samples": 3395,
"vision_length_conflict_samples": 618,
"vision_tail_ambiguous_samples": 618,
"nonzero_text_rows_outside_attention": 86078,
"observed_target_rows": {
"text": 169750,
"audio": 169750,
"vision": 163302
},
"mean_target_coverage": {
"text": 1.0,
"audio": 1.0,
"vision": 0.9540337701314328
},
"quality_fields_available": false,
"word_or_frame_timestamps_available": false
}
},
"valid": {
"sha256": {
"text": "16bdef07347baf00985e7964301eef2272701716220e846c415651806bb5eb0a",
"audio": "b7d6f3092d9ddbcd70afe4b0ecff0490181a83a308a67246613185167cb08e87",
"vision": "fbd9f93e8cae02de59e5d513c4c057d759db626894d298124575bdf18863a024"
},
"mask_sha256": "adcd9b7bac94c6523b571c7c60eb3e4ad0efc820062bd528b102a4239aaf2325",
"audit": {
"method": "index_cell_overlap_on_independent_normalized_progress_axes",
"physical_time_alignment": false,
"samples": 728,
"vision_length_conflict_samples": 141,
"vision_tail_ambiguous_samples": 141,
"nonzero_text_rows_outside_attention": 17772,
"observed_target_rows": {
"text": 36400,
"audio": 36400,
"vision": 35315
},
"mean_target_coverage": {
"text": 1.0,
"audio": 1.0,
"vision": 0.96090314748523
},
"quality_fields_available": false,
"word_or_frame_timestamps_available": false
}
},
"test": {
"sha256": {
"text": "6cc0c3ee828ab2f322318011b10764aabca2f5d2bcba40abddd714529928a05a",
"audio": "d64dca47fffbbd00a2d8f5628f05efdb9b8743ff51187b1c4cac58ccf3f5a50b",
"vision": "2a8b12d9c3a0d57f8f14bb830dbd2f8acdcfa147f55c50927798692ae6630d6d"
},
"mask_sha256": "85706ad6842e8a0642bf5d24642463d0488a6cba55848a2276e397e8592604d6",
"audit": {
"method": "index_cell_overlap_on_independent_normalized_progress_axes",
"physical_time_alignment": false,
"samples": 727,
"vision_length_conflict_samples": 131,
"vision_tail_ambiguous_samples": 131,
"nonzero_text_rows_outside_attention": 18041,
"observed_target_rows": {
"text": 36350,
"audio": 36350,
"vision": 35034
},
"mean_target_coverage": {
"text": 1.0,
"audio": 1.0,
"vision": 0.9549938172651288
},
"quality_fields_available": false,
"word_or_frame_timestamps_available": false
}
}
}
@@ -0,0 +1,80 @@
{
"train": {
"samples": 3395,
"feature_shapes": {
"text": [
3395,
50,
768
],
"audio": [
3395,
50,
74
],
"vision": [
3395,
50,
35
]
},
"mask_shape": [
3395,
50,
3
],
"labels_preserved": true
},
"valid": {
"samples": 728,
"feature_shapes": {
"text": [
728,
50,
768
],
"audio": [
728,
50,
74
],
"vision": [
728,
50,
35
]
},
"mask_shape": [
728,
50,
3
],
"labels_preserved": true
},
"test": {
"samples": 727,
"feature_shapes": {
"text": [
727,
50,
768
],
"audio": [
727,
50,
74
],
"vision": [
727,
50,
35
]
},
"mask_shape": [
727,
50,
3
],
"labels_preserved": true
}
}
+36
View File
@@ -0,0 +1,36 @@
"""Load official unaligned data through Q2's real input entry, without training."""
from __future__ import annotations
import json
import sys
from pathlib import Path
import numpy as np
Q1 = Path(__file__).resolve().parent
ROOT = Q1.parents[1]
sys.path.insert(0, str(ROOT / "math" / "Q2"))
from data import load_official_splits # noqa: E402
def main() -> None:
source = ROOT / "E题数据" / "附件2-数据集特征文件" / "unaligned_50.pkl"
splits = load_official_splits(source, version="unaligned_50")
report = {}
for name, split in splits.items():
expected = {"text": (split.n, 50, 768), "audio": (split.n, 50, 74),
"vision": (split.n, 50, 35)}
actual = {m: x.shape for m, x in split.x.items()}
assert actual == expected
assert split.mask.shape == (split.n, 50, 3)
assert all(np.isfinite(x).all() for x in split.x.values())
assert split.class_y is not None and len(split.class_y) == split.n
report[name] = {"samples": split.n, "feature_shapes": {m: list(s) for m, s in actual.items()},
"mask_shape": list(split.mask.shape), "labels_preserved": True}
path = Q1 / "results" / "unified_adapter" / "q2_smoke.json"
path.write_text(json.dumps(report, indent=2), encoding="utf-8")
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()
+139
View File
@@ -0,0 +1,139 @@
"""Acceptance tests for the shared Q1 alignment contract."""
from __future__ import annotations
import json
import sys
import unittest
from pathlib import Path
import numpy as np
Q1 = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(Q1))
from adapter import AlignmentError, Q1AlignmentAdapter # noqa: E402
from adapter.projection import project_intervals # noqa: E402
def cells(n: int) -> np.ndarray:
left = np.arange(n, dtype=np.float64) / n
return np.column_stack((left, left + 1 / n))
def record(text_length: int = 50, audio_length: int = 50, vision_length: int = 50):
text = np.zeros((50, 768), np.float32)
audio = np.zeros((500, 74), np.float32)
vision = np.zeros((500, 35), np.float32)
text[:text_length] = 1
audio[:audio_length] = 2
vision[:vision_length] = 3
return {"id": "video$_$clip", "sequence_order_verified": True,
"attention_mask": np.arange(50) < text_length,
"text": text, "audio": audio, "vision": vision,
"audio_length": audio_length, "vision_length": vision_length}
class UnifiedAdapterTests(unittest.TestCase):
def test_01_identity_L_equals_K(self):
x = np.arange(50, dtype=np.float32)[:, None]
result = project_intervals(x, cells(50), cells(50), np.ones(50, bool))
np.testing.assert_array_equal(result.x[:, 0], x[:, 0])
def test_02_expand_L_less_than_K(self):
x = np.arange(10, dtype=np.float32)[:, None]
result = project_intervals(x, cells(10), cells(50), np.ones(10, bool))
np.testing.assert_allclose(result.x[:, 0], np.repeat(x[:, 0], 5))
def test_03_aggregate_L_greater_than_K(self):
x = np.arange(100, dtype=np.float32)[:, None]
result = project_intervals(x, cells(100), cells(50), np.ones(100, bool))
np.testing.assert_allclose(result.x[:, 0], x.reshape(50, 2).mean(axis=1))
def test_04_partial_missing(self):
x = np.ones((50, 2), np.float32)
valid = np.ones((50, 2), bool)
valid[3, 0] = False
result = project_intervals(x, cells(50), cells(50), valid)
self.assertFalse(result.observed_dimensions[3, 0])
self.assertTrue(result.observed_dimensions[3, 1])
self.assertEqual(result.x[3, 0], 0)
def test_05_all_missing(self):
result = project_intervals(np.ones((10, 2), np.float32), cells(10), cells(50), np.zeros(10, bool))
self.assertFalse(result.observed.any())
self.assertFalse(result.x.any())
self.assertFalse(result.source_weights.nnz)
def test_06_text_padding_excluded(self):
r = record(text_length=10)
r["text"][10:] = 99
sample = Q1AlignmentAdapter().align(r)
self.assertTrue(np.all(sample.features["text"] == 1))
self.assertEqual(sample.provenance["text"].source_span, 10)
def test_07_audio_length_conflict_rejected(self):
r = record()
r["audio"][70] = 7
with self.assertRaisesRegex(AlignmentError, "audio contains observed"):
Q1AlignmentAdapter().align(r)
def test_08_vision_length_conflict_retained(self):
r = record()
r["vision"][70] = 7
p = Q1AlignmentAdapter().align(r).provenance["vision"]
self.assertTrue(p.length_conflict)
self.assertTrue(p.tail_ambiguous)
self.assertEqual(p.source_span, 71)
def test_09_provenance_conservation(self):
sample = Q1AlignmentAdapter().align(record())
for m, p in sample.provenance.items():
sums = np.asarray(p.source_weights.sum(axis=1)).ravel()
np.testing.assert_allclose(sums[sample.observed[m]], 1, atol=1e-6)
def test_10_physical_coordinate(self):
from q1_io import load_sample
sample = Q1AlignmentAdapter().align(load_sample("-iRBcNs9oI8/8"))
self.assertEqual(sample.metadata["coordinate_mode"], "physical")
self.assertTrue(sample.metadata["physical_time_alignment"])
self.assertAlmostEqual(sample.target_intervals[-1, 1], sample.metadata["duration_s"])
def test_11_relative_coordinate(self):
sample = Q1AlignmentAdapter().align(record())
self.assertEqual(sample.metadata["coordinate_unit"], "normalized_progress")
self.assertFalse(sample.metadata["physical_time_alignment"])
self.assertAlmostEqual(sample.target_intervals[-1, 1], 1.0)
def test_12_q2_shapes(self):
sample = Q1AlignmentAdapter().align(record())
features, mask = sample.q2_arrays()
self.assertEqual(mask.shape, (50, 3))
self.assertEqual({m: v.shape for m, v in features.items()},
{"text": (50, 768), "audio": (50, 74), "vision": (50, 35)})
def test_13_nonfinite_rejected(self):
r = record()
r["audio"][0, 0] = np.inf
with self.assertRaises(AlignmentError):
Q1AlignmentAdapter().align(r)
def test_14_old_new_full_equivalence(self):
report = json.loads((Q1 / "results" / "unified_adapter" / "full_audit.json").read_text())
for split in ("train", "valid", "test"):
self.assertTrue(all(report["relative"][split]["exact_feature_equivalence"].values()))
self.assertTrue(report["relative"][split]["exact_mask_equivalence"])
def test_15_auto_requires_evidence(self):
r = record()
r.pop("sequence_order_verified")
with self.assertRaisesRegex(AlignmentError, "requires"):
Q1AlignmentAdapter().align(r)
def test_16_invalid_physical_does_not_fall_back(self):
r = record()
r["_meta"] = {"duration_s": 2.0}
with self.assertRaisesRegex(AlignmentError, "verified media"):
Q1AlignmentAdapter().align(r)
if __name__ == "__main__":
unittest.main()
+35
View File
@@ -0,0 +1,35 @@
"""Compatibility imports for callers of the former Relative-only module.
All projection is now performed by :mod:`adapter`; this file has no separate
alignment calculation.
"""
from __future__ import annotations
import numpy as np
from adapter import adapt_official_split
def project_modality(values: np.ndarray, modality: str, *, reported_length: int | None = None,
attention_mask: np.ndarray | None = None):
"""Legacy single-modality call, delegated to the unified projection kernel."""
from adapter.core import _relative_modality
from adapter.coordinates import relative_targets
from adapter.projection import project_intervals
record = {modality: values}
if modality == "text":
record["attention_mask"] = attention_mask
else:
record[f"{modality}_length"] = reported_length
item = _relative_modality(record, modality)
view = project_intervals(item.values, item.intervals, relative_targets(50),
item.observed, item.quality)
view.source_span = len(item.values)
view.reported_length = item.reported_length
view.length_conflict = item.length_conflict
view.tail_ambiguous = item.tail_ambiguous
return view
adapt_split = adapt_official_split