# 赛题二交付格式说明

本文档规定赛题二的模型、代码、运行环境、技术报告和 ZIP 目录结构。赛题二
提交的是可由评测程序逐采样调用的主动降噪模型，不提交预先生成的测试集
WAV、CSV 或其他测试结果。

---

## 1. 赛题二提交内容

每支队伍提交一个 ZIP，ZIP 内包含一套正式参赛模型。模型必须：

- 采样率固定为 48 kHz；
- 支持严格因果的逐采样推理；
- 能够在每段录音开始前清空状态；
- 返回有限的标量控制输出；
- 声明模型总参数量和峰值事件 MAC；
- 在离线环境中完成加载和推理。

参赛者可以提交以下任一种模型：

1. 前馈模型：时刻 \(t\) 接收参考信号 \(x[t]\)；
2. 反馈模型：时刻 \(t\) 接收参考信号 \(x[t]\) 和上一采样点残差
   \(e[t-1]\)。

赛题二不接收预计算的测试集输出。正式成绩由评测程序加载提交模型后统一
计算。

参赛者工具包提供一个固定的公开车载场景，可在提交前查看真实闭环指标。
该场景仅用于本地验证，不是正式测试集，也不得作为文件放入提交 ZIP。

声学评分协议为 v4。降噪主分数是 50 Hz～5 kHz 内各有效 1/3 倍频带
降噪量的等权算术平均：

$$
\mathrm{NR}_{1/3,\mathrm{average}}
=
\frac{1}{N_{\mathrm{B}}}
\sum_{i\in\mathcal{B}_{\mathrm{NR}}}\mathrm{NR}_i
$$

其中
\(\mathcal{B}_{\mathrm{NR}}=\{i\mid50\ \mathrm{Hz}\le f_i\le
5000\ \mathrm{Hz}\}\)，
\(N_{\mathrm{B}}=|\mathcal{B}_{\mathrm{NR}}|\)。各有效频带等权参与，
不施加频率计权。

1 kHz～8 kHz 内的 1/3 倍频带反弹峰值为：

$$
R_{1/3,\mathrm{peak}}
=
\max
\left(
0,
-\min_{i\in\mathcal{B}_{\mathrm{R}}}\mathrm{NR}_i
\right)
$$

其中
\(\mathcal{B}_{\mathrm{R}}=\{i\mid1000\ \mathrm{Hz}\le f_i\le
8000\ \mathrm{Hz}\}\)。该指标数值越低越好，按标准 1/3 倍频带计算，
不使用单个 FFT 栅格。

---

## 2. 固定模型入口

`Task2/` 根目录必须直接包含 `submission.py`。该文件必须提供：

```python
def create_model(device: str = "cpu"):
    return model
```

返回的模型对象必须具有：

```python
sample_rate = 48_000
requires_error = False  # 前馈模型
# 或 requires_error = True  # 反馈模型

reset()
process_sample(...)
get_complexity()
```

前馈接口：

```python
def process_sample(self, reference_sample: float) -> float:
    ...
```

反馈接口：

```python
def process_sample(
    self,
    reference_sample: float,
    previous_error_sample: float,
) -> float:
    ...
```

`previous_error_sample` 固定表示 \(e[t-1]\)，不是当前 \(e[t]\)。

`reset()` 必须清空输入历史、卷积缓存、循环网络状态和其他跨录音状态。

---

## 3. 模型文件

模型结构代码、配置和权重必须全部包含在 `Task2/` 内。推荐：

- PyTorch 权重使用 `.pt` 或 `.pth`；
- 优先保存和加载 `state_dict`；
- 权重路径相对 `submission.py` 解析；
- 自定义网络代码与提交一起提供；
- 加载后调用 `eval()`；
- 使用 `create_model()` 收到的 `device`，不要写死本机设备。

模型文件可以直接放在 `Task2/`，也可以按以下方式整理：

```text
model/
├─ weights.pt
└─ config.json

code/
├─ __init__.py
├─ model.py
└─ streaming.py
```

无论采用哪种整理方式，`submission.py` 都必须位于 `Task2/` 根部。

不应提交训练数据、优化器状态、训练缓存或与推理无关的文件。

---

## 4. 代码与运行环境

`Task2/` 根目录必须包含：

- `submission.py`：固定模型入口；
- `requirements.txt`：模型额外依赖；
- `README.md`：模型运行和复现说明。

提交内的 `README.md` 至少说明：

1. Python 和主要依赖版本；
2. CPU/GPU 支持情况；
3. 模型文件用途；
4. 本地公开检查命令；
5. 模型类型是前馈还是反馈；
6. 运行所需的额外注意事项。

`requirements.txt` 只列出推理所需依赖，每行一个。不要加入只在训练阶段
使用的工具。

---

## 5. 模型复杂度声明

模型必须实现：

```python
def get_complexity(self) -> dict:
    return {
        "parameter_count": ...,
        "steady_state_macs_per_sample": ...,
        "startup_macs": ...,
        "peak_macs_in_one_sample_event": ...,
    }
```

正式复杂度指标为模型总参数量和峰值事件 MAC。
本规则只规定统计边界，不预设或推荐任何模型结构。

参数量：

$$
N_{\mathrm{param}}
=
\sum_{\theta_j\in\Theta_{\mathrm{resident}}^{\mathrm{unique}}}
\operatorname{numel}(\theta_j)
$$

峰值事件 MAC：

$$
\mathrm{MAC}_{\mathrm{peak}}
=
\max_{q\in\mathcal{Q}}
\left[
\sum_{\ell\in\mathcal{E}(q)}
\mathrm{MAC}_{\ell}
\right]
$$

所有常驻模型、子模型、辅助模块和冻结参数都必须计入参数量。如果多种模型
或多个计算模块会在同一采样点同时运行，必须累计该采样点的全部 MAC，并按
最坏采样边界和最坏动态分支报告峰值。

---

## 6. 技术方案报告

技术报告放在 `Task2/report/`，必须且只能包含一份 PDF。推荐命名：

```text
TeamID_Task2_Report.pdf
```

报告至少包括：

- 技术路线和整体方案；
- 模型架构；
- 前馈或反馈接口说明；
- 严格因果和流式状态实现；
- 数据处理和训练过程；
- 参数量与峰值事件 MAC 分析；
- 开发集或自有验证集结果；
- 稳定性和 1/3 倍频带反弹分析；
- 推理环境和复现方法。

---

## 7. ZIP 命名和队伍 ID

推荐 ZIP 文件名：

```text
TeamID.zip
```

`TeamID` 必须以英文字母开头，只能包含英文字母、数字和下划线。例如：

```text
DeepANC001
Team_A
```

不要在 `TeamID` 中使用空格、中文、连字符或路径分隔符。

---

## 8. 正式 ZIP 目录结构

最终 ZIP 必须采用：

```text
TeamID/
└─ Task2/
```

完整示例：

```text
TeamID.zip
└─ TeamID/
   └─ Task2/
      ├─ submission.py               # 固定入口，必须
      ├─ requirements.txt            # 推理依赖，必须
      ├─ README.md                   # 运行说明，必须
      ├─ __init__.py                 # 可保留
      ├─ model.py                    # 示例：模型结构代码
      ├─ weights.pt                  # 示例：模型权重
      ├─ model/                      # 可选：模型文件子目录
      │  ├─ weights.pt
      │  └─ config.json
      ├─ code/                       # 可选：其他推理代码
      │  ├─ __init__.py
      │  ├─ model.py
      │  └─ streaming.py
      └─ report/
         └─ TeamID_Task2_Report.pdf  # 技术报告，必须且只能一份
```

示例中的 `model.py`、`weights.pt`、`model/` 和 `code/` 不要求同时存在。
参赛者应按自己的模型选择一种清晰且可正常导入的组织方式。

以下结构不正确：

```text
Task2/submission.py                  # 缺少 TeamID 层
TeamID/submission.py                 # 缺少 Task2 层
TeamID/Task2/my_submission/submission.py
                                      # submission.py 没有直接位于 Task2
```

---

## 9. 使用公开工具生成 ZIP

打包前先完成接口检查和公开场景闭环测试：

```bat
python validate_submission.py --entry-point my_submission.submission:create_model --device cpu
python run_public_demo.py --entry-point my_submission.submission:create_model --device cpu
```

确认无误后，从参赛者工具包根目录运行：

```bat
python make_submission_zip.py --team-id TeamID --source my_submission --output dist\TeamID.zip
```

脚本会：

1. 检查 `my_submission/submission.py`；
2. 检查 `my_submission/report/` 中有且仅有一份有效 PDF；
3. 排除 Python 缓存、报告占位说明和已有 ZIP；
4. 自动写入 `TeamID/Task2/` 前缀；
5. 输出最终 ZIP 的 SHA-256。

上传前解压检查，应看到：

```text
TeamID/Task2/submission.py
```

---

## 10. 提交前检查

- [ ] ZIP 文件名与队伍 ID 一致；
- [ ] ZIP 第一层目录是 `TeamID/`；
- [ ] 第二层目录固定为 `Task2/`；
- [ ] `Task2/submission.py` 存在；
- [ ] `Task2/requirements.txt` 存在；
- [ ] `Task2/README.md` 存在；
- [ ] `Task2/report/` 中有且仅有一份有效 PDF；
- [ ] 模型代码、配置和权重完整；
- [ ] 所有权重使用相对路径加载；
- [ ] 前馈或反馈公开检查已经通过；
- [ ] 固定公开车载场景能够完成闭环评测；
- [ ] 参数量包含所有常驻模型；
- [ ] 峰值 MAC 覆盖最坏动态事件；
- [ ] ZIP 不含训练数据、日志、缓存和无关文件；
- [ ] ZIP 不含 `public_demo_data/` 和本地评测 JSON；
- [ ] 已保存最终 ZIP 的 SHA-256。
