From d0bbb8f3e21326c644f8714eda9150691ed58373 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com>
Date: Wed, 3 Jun 2026 13:49:19 +0800
Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=9D=E5=A7=8B=E5=8C=96=20CTI=20?=
=?UTF-8?q?=E6=8E=A8=E7=90=86=E4=BC=98=E5=8C=96=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- baseline infer.py + requirements.txt + build_env.sh
- GRAB / HSTU 两篇核心论文
- 比赛规则和提交接口说明
- 项目 CLAUDE.md
---
.gitignore | 19 +
CLAUDE.md | 67 +
代码/code/build_env.sh | 4 +
代码/code/infer.py | 728 ++
代码/code/requirements.txt | 29 +
代码/main.ipynb | 387 +
代码/任务提交接口说明.md | 115 +
...M-Inspired_Sequence-First_CTR_Prediction.pdf | 7918 +++++++++++++++++
论文/HSTU_Actions_Speak_Louder_than_Words.pdf | Bin 0 -> 1621291 bytes
9 files changed, 9267 insertions(+)
create mode 100644 .gitignore
create mode 100644 CLAUDE.md
create mode 100644 代码/code/build_env.sh
create mode 100644 代码/code/infer.py
create mode 100644 代码/code/requirements.txt
create mode 100644 代码/main.ipynb
create mode 100644 代码/任务提交接口说明.md
create mode 100644 论文/GRAB_An_LLM-Inspired_Sequence-First_CTR_Prediction.pdf
create mode 100644 论文/HSTU_Actions_Speak_Louder_than_Words.pdf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f728994
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# 模型和数据(不提交)
+ckpt.pt
+dataset/
+*.tar
+*.tar.gz
+
+# Python
+__pycache__/
+*.pyc
+.venv/
+libraries/
+
+# 提交用 zip(自动生成,不提交)
+eval.zip
+*.zip
+
+# IDE
+.vscode/
+.idea/
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..fda3d9d
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,67 @@
+# 百度商业AI技术创新大赛 — 生成式推荐广告排序推理性能优化
+
+## 比赛信息
+
+- **全称**: 百度商业AI技术创新大赛 (CTI) 2026
+- **赛题**: 生成式推荐广告排序推理性能优化
+- **主办**: 百度商业 / 百度飞桨 / NVIDIA 技术合作
+- **平台**: [AI Studio](https://aistudio.baidu.com/competition/detail/1461)
+- **大赛官网**: http://cti.baidu.com
+- **奖池**: ¥19W(含 NV-DGX-Spark)
+- **报名截止**: 2026/06/26 11:59:59
+- **夏令营决赛**: 2026年7月(4天3晚,包交通食宿)
+
+## 赛题核心
+
+给定基于 Transformer 的生成式推荐广告排序模型(GRAB),在**不改变模型结构、不在测试集上训练**的前提下,极致优化推理性能。
+
+### 双门槛评分
+
+| 维度 | 要求 | 不达标后果 |
+|------|------|------------|
+| 推理效率 | 纯推理 ≤ 5min,环境构建 ≤ 20min | 总分 0 |
+| 策略效果 | AUC ≥ 0.65,PCOC ∈ [0.85, 1.15] | 总分 0 |
+
+### 提交格式
+
+`xxx.zip` 包含:
+- `infer.py` — 推理入口脚本
+- `build_env.sh` — 环境构建脚本
+- `requirements.txt` — Python 依赖
+- 可选:打包的 Python 环境、量化后的模型文件等
+
+**注意**:不要包含数据集文件夹,不要修改模型权重参数
+
+### 约束
+
+- 组网不可进行策略性改动
+- 不可对测试集进行训练
+- 每天最多提交 10 次
+
+## 技术背景
+
+基于两篇核心论文:
+
+1. **GRAB** (百度, 2026) — 比赛 baseline 模型
+ - arXiv: 2602.01865
+ - 核心:CamA 多通道注意力 + STS 两阶段训练
+ - 模型规模:~6.5M~11.3M 参数
+
+2. **HSTU** (Meta, 2024) — GRAB 的架构基础
+ - arXiv: 2402.17152 (ICML 2024)
+ - 核心:Pointwise Aggregated Attention + 算子融合
+ - 比 FlashAttention2 Transformer 快 5.3~15.2 倍
+
+## 推理优化方向(按优先级)
+
+1. **模型量化** — FP16/INT8,Paddle-TensorRT
+2. **Flash Attention** — 减少注意力显存和计算
+3. **算子融合** — 减少 kernel launch 开销
+4. **序列精简** — 压缩/裁剪冗余历史 token
+5. **多通道合并** — CamA 通道剪枝或共享
+
+## 提交记录
+
+| 日期 | 提交次数 | 得分 | 优化手段 | 备注 |
+|------|----------|------|----------|------|
+| - | - | - | - | - |
diff --git a/代码/code/build_env.sh b/代码/code/build_env.sh
new file mode 100644
index 0000000..7fca523
--- /dev/null
+++ b/代码/code/build_env.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+
+echo "build env succeess"
\ No newline at end of file
diff --git a/代码/code/infer.py b/代码/code/infer.py
new file mode 100644
index 0000000..99ec529
--- /dev/null
+++ b/代码/code/infer.py
@@ -0,0 +1,728 @@
+import sys
+import os
+
+# 获取当前环境脚本所在目录或指定绝对路径
+if os.path.exists("../libraries"):
+ lib_path = os.path.abspath("../libraries")
+ sys.path.append(lib_path)
+
+import math
+import argparse
+from pathlib import Path
+from collections import defaultdict
+import numpy as np
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from torch.utils.data import Dataset, DataLoader
+from tqdm import tqdm
+
+
+# ============================================================
+# 数据加载(来自 train/dataset.py)
+# ============================================================
+
+def _detect_has_clk(file_path):
+ """检测 CSV 文件是否包含 clk 列(5列 vs 4列格式)。
+ 5列格式: logid,userid,adid,clk,timestamp,sign:slot...
+ 4列格式: logid,userid,adid,timestamp,sign:slot...
+ 通过第5个字段是否包含 ':' 来判断:有 ':' 说明已经是 sign:slot,即无 clk 列。
+ """
+ with open(file_path, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if not line:
+ continue
+ parts = line.split(',')
+ if len(parts) >= 5:
+ return ':' not in parts[4]
+ return False
+ return False
+
+
+def load_sample_files(sample_files_list):
+ """加载 CSV sample 文件,返回 item_dict 和 user_seq。
+ 自动检测每个文件是 5列(含clk)还是 4列(无clk)格式。
+ """
+ sample_files = sorted([Path(f) for f in sample_files_list])
+ print(f'[INFO] loading {len(sample_files)} files: {[str(f) for f in sample_files]}')
+
+ item_dict = {}
+ user_logs = defaultdict(list)
+
+ for sample_file in tqdm(sample_files, desc='Loading sample files'):
+ has_clk = _detect_has_clk(sample_file)
+ min_parts = 5 if has_clk else 4
+ print(f' {sample_file.name}: has_clk={has_clk}')
+
+ with open(sample_file, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if not line:
+ continue
+ parts = line.split(',')
+ if len(parts) < min_parts:
+ continue
+
+ logid = int(parts[0])
+ userid = int(parts[1])
+ adid = int(parts[2])
+
+ if has_clk:
+ clk = int(parts[3])
+ timestamp = int(parts[4])
+ feat_start = 5
+ else:
+ clk = 0
+ timestamp = int(parts[3])
+ feat_start = 4
+
+ signs = []
+ slots = []
+ for pair in parts[feat_start:]:
+ if ':' in pair:
+ s, sl = pair.split(':', 1)
+ signs.append(int(s))
+ slots.append(int(sl))
+
+ item_dict[logid] = {
+ 'logid': logid,
+ 'userid': userid,
+ 'adid': adid,
+ 'clk': clk,
+ 'timestamp': timestamp,
+ 'signs': np.array(signs, dtype=np.int64),
+ 'slots': np.array(slots, dtype=np.int64),
+ }
+ user_logs[userid].append((timestamp, logid))
+
+ user_seq = {}
+ for userid, logs in user_logs.items():
+ logs.sort(key=lambda x: x[0])
+ user_seq[userid] = [logid for _, logid in logs]
+
+ print(f'[INFO] loaded {len(item_dict)} records, {len(user_seq)} users')
+ return item_dict, user_seq
+
+
+def load_logids_from_file(file_path):
+ """快速读取一个 sample 文件中的所有 logid"""
+ logids = set()
+ with open(file_path, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if not line:
+ continue
+ comma = line.index(',')
+ logids.add(int(line[:comma]))
+ return logids
+
+
+class CTRUserDataset(Dataset):
+ """按用户组织的 CTR 数据集"""
+
+ def __init__(self, item_dict, user_seq=None, max_feasign_per_slot=None, pred_logids=None):
+ super().__init__()
+ self.item_dict = item_dict
+ self.user_seq = user_seq if user_seq else {}
+ self.max_feasign_per_slot = max_feasign_per_slot
+ self.pred_logids = pred_logids if pred_logids is not None else set()
+
+ self.user_items = defaultdict(list)
+ for logid, rec in item_dict.items():
+ userid = rec['userid']
+ feasign = defaultdict(list)
+ for slot, sign in zip(rec['slots'].tolist(), rec['signs'].tolist()):
+ feasign[slot].append(sign)
+ if max_feasign_per_slot is not None:
+ feasign = {slot: signs[:max_feasign_per_slot[slot]]
+ if max_feasign_per_slot.get(slot, -1) != -1 else signs
+ for slot, signs in feasign.items()}
+ feasign = dict(feasign)
+ label = rec['clk']
+ self.user_items[userid].append((logid, feasign, label))
+
+ self.user_ids = sorted(self.user_items.keys())
+ self.num_users = len(self.user_ids)
+ self.total_samples = len(item_dict)
+
+ all_signs = set()
+ for rec in item_dict.values():
+ all_signs.update(rec['signs'].tolist())
+ self.max_slot_id = 28
+ self.max_sign_id = max(all_signs) if all_signs else 0
+
+ def __len__(self):
+ return self.num_users
+
+ def __getitem__(self, index):
+ userid = self.user_ids[index]
+ items = self.user_items[userid]
+
+ if self.user_seq and userid in self.user_seq:
+ seq_order = {logid: i for i, logid in enumerate(self.user_seq[userid])}
+ items.sort(key=lambda x: seq_order.get(x[0], x[0]))
+ else:
+ items.sort(key=lambda x: x[0])
+
+ feasigns = []
+ labels = []
+ logids = []
+ for logid, feasign, label in items:
+ logids.append(logid)
+ feasigns.append(feasign)
+ labels.append(label)
+
+ return {
+ 'userid': userid,
+ 'logids': logids,
+ 'feasigns': feasigns,
+ 'labels': labels,
+ 'pred_mask': [1 if logid in self.pred_logids else 0 for logid in logids],
+ }
+
+
+def make_collate_fn(max_slot_id):
+ def collate_user_batch(batch):
+ all_userids = []
+ all_logids = []
+ all_labels = []
+ all_pred_masks = []
+ all_feasigns = []
+ user_offsets = [0]
+
+ for item in batch:
+ for i, logid in enumerate(item['logids']):
+ all_userids.append(item['userid'])
+ all_logids.append(logid)
+ all_labels.append(item['labels'][i])
+ all_pred_masks.append(item['pred_mask'][i])
+ all_feasigns.append(item['feasigns'][i])
+ user_offsets.append(len(all_labels))
+
+ slot_data = {}
+ for slot in range(1, max_slot_id + 1):
+ values = []
+ offsets = [0]
+ for feasign in all_feasigns:
+ if slot in feasign:
+ values.extend(feasign[slot])
+ offsets.append(len(values))
+ slot_data[slot] = (
+ torch.tensor(values, dtype=torch.long),
+ torch.tensor(offsets, dtype=torch.long),
+ )
+
+ result = {
+ 'userid': torch.tensor(all_userids, dtype=torch.long),
+ 'logid': torch.tensor(all_logids, dtype=torch.long),
+ 'label': torch.tensor(all_labels, dtype=torch.float32),
+ 'pred_mask': torch.tensor(all_pred_masks, dtype=torch.bool),
+ 'user_offsets': torch.tensor(user_offsets, dtype=torch.long),
+ }
+ result.update(slot_data)
+ return result
+
+ return collate_user_batch
+
+
+# ============================================================
+# 模型定义(来自 main.py)
+# ============================================================
+
+def move_batch_to_device(batch, device):
+ if isinstance(batch, dict):
+ return {k: move_batch_to_device(v, device) for k, v in batch.items()}
+ elif isinstance(batch, (list, tuple)):
+ return [move_batch_to_device(x, device) for x in batch]
+ elif torch.is_tensor(batch):
+ return batch.to(device)
+ else:
+ return batch
+
+
+class RepEncoder(nn.Module):
+ def __init__(self, vocab_size, emb_dim, padding_idx=0, slot_num=0, d_model=0):
+ super().__init__()
+ self.emb = nn.Embedding(num_embeddings=vocab_size, embedding_dim=emb_dim, padding_idx=padding_idx)
+ self.emb_dim = emb_dim
+ self.slot_num = slot_num
+ self.input_norm = nn.LayerNorm(slot_num * emb_dim)
+ self.linear = nn.Linear(in_features=slot_num * emb_dim, out_features=d_model)
+
+ def forward(self, batch):
+ pooled_embs = []
+ max_idx = self.emb.num_embeddings - 1
+ for i in range(self.slot_num):
+ values, offsets = batch[i + 1]
+ offsets = offsets.to(values.device)
+ values = values.clamp(0, max_idx) # 超出 vocab_size 的 sign id 截断,避免越界
+ sign_emb = self.emb(values)
+ res = torch.segment_reduce(sign_emb, reduce='sum', offsets=offsets, initial=0)
+ pooled_embs.append(res)
+ fused_embs = torch.cat(pooled_embs, dim=1)
+ norm_emb = self.input_norm(fused_embs)
+ rep_emb = self.linear(norm_emb)
+ return rep_emb
+
+
+def scaled_dot_product(q, k, v, extension):
+ d = q.size(-1)
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d)
+ if extension is not None and "mask" in extension:
+ mask = extension["mask"]
+ scores = scores.masked_fill(mask == 0, float("-inf"))
+ attn = torch.softmax(scores, dim=-1)
+ out = torch.matmul(attn, v)
+ return out
+
+
+class Expert(nn.Module):
+ def __init__(self, d_model, dim_ff):
+ super().__init__()
+ self.fc1 = nn.Linear(d_model, dim_ff)
+ self.fc2 = nn.Linear(dim_ff, d_model)
+
+ def forward(self, x):
+ return self.fc2(F.relu(self.fc1(x)))
+
+
+class TopKGate(nn.Module):
+ def __init__(self, d_model, num_experts, k=2, noisy_gating=True):
+ super().__init__()
+ self.w_g = nn.Linear(d_model, num_experts)
+ self.num_experts = num_experts
+ self.k = k
+ self.noisy_gating = noisy_gating
+
+ def forward(self, x):
+ # x: [B,S,D]
+ logits = self.w_g(x) # [B,S,E]
+
+ if self.noisy_gating and self.training:
+ logits = logits + torch.randn_like(logits) * 0.1
+
+ probs = torch.softmax(logits, dim=-1) # [B,S,E]
+
+ topk_score, topk_idx = torch.topk(probs, self.k, dim=-1) # [B,S,k]
+
+ return topk_idx, topk_score, probs
+
+class SMoE(nn.Module):
+ def __init__(self, d_model, dim_ff, num_experts, k=2):
+ super().__init__()
+ self.num_experts = num_experts
+ self.k = k
+
+ self.experts = nn.ModuleList([
+ Expert(d_model, dim_ff) for _ in range(num_experts)
+ ])
+
+ self.gate = TopKGate(d_model, num_experts, k=k)
+
+ def forward(self, x):
+ # x: [B,S,D]
+ B, S, D = x.shape
+
+ topk_idx, topk_score, probs = self.gate(x)
+
+ out = torch.zeros_like(x)
+
+ # flatten
+ x_flat = x.reshape(-1, D) # [B*S, D]
+ idx_flat = topk_idx.reshape(-1, self.k) # [B*S, k]
+ score_flat = topk_score.reshape(-1, self.k)
+
+ for i in range(self.num_experts):
+ # 找到被路由到 expert i 的 token
+ mask = (idx_flat == i) # [B*S, k]
+
+ if not mask.any():
+ continue
+
+ # 哪些 token 命中了 expert i
+ token_idx, k_idx = mask.nonzero(as_tuple=True)
+
+ selected_x = x_flat[token_idx] # [N, D]
+
+ expert_out = self.experts[i](selected_x) # [N, D]
+
+ weight = score_flat[token_idx, k_idx].unsqueeze(-1)
+
+ out_flat = out.reshape(-1, D)
+ out_flat[token_idx] += expert_out * weight
+
+ importance = probs.sum(dim=(0,1)) # [E]
+ moe_loss = (importance.std() / (importance.mean() + 1e-6))
+
+ return out, moe_loss
+
+
+class TransformerEncoder(nn.Module):
+ def __init__(self, d_model, n_heads, num_layers, dim_ff, act="relu",
+ attention_fn=scaled_dot_product):
+ super().__init__()
+ self.d_model = d_model
+ self.n_heads = n_heads
+ self.head_dim = d_model // n_heads
+ self.num_layers = num_layers
+ assert d_model % n_heads == 0
+
+ self.qkv_proj = nn.ModuleList([nn.Linear(d_model, 3 * d_model) for _ in range(num_layers)])
+ self.out_proj = nn.ModuleList([nn.Linear(d_model, d_model) for _ in range(num_layers)])
+ self.ffn1 = nn.ModuleList([nn.Linear(d_model, dim_ff) for _ in range(num_layers)])
+ self.ffn2 = nn.ModuleList([nn.Linear(dim_ff, d_model) for _ in range(num_layers)])
+ self.norm1 = nn.ModuleList([nn.LayerNorm(d_model) for _ in range(num_layers)])
+ self.norm2 = nn.ModuleList([nn.LayerNorm(d_model) for _ in range(num_layers)])
+ self.act = getattr(F, act)
+ self.attention_fn = attention_fn
+ self.moe = nn.ModuleList([
+ SMoE(d_model, dim_ff, num_experts=8, k=2)
+ for _ in range(num_layers)
+ ])
+
+ def forward(self, x, extension):
+ x = x.unsqueeze(0)
+ B, S, D = x.shape
+
+ moe_loss_total = 0.0
+ for i in range(self.num_layers):
+ residual = x
+ x = self.norm1[i](x)
+ qkv = self.qkv_proj[i](x)
+ qkv = qkv.view(B, S, self.n_heads, 3 * self.head_dim)
+ qkv = qkv.permute(0, 2, 1, 3)
+ q, k, v = torch.split(qkv, self.head_dim, dim=-1)
+ attn_out = self.attention_fn(q, k, v, extension)
+ attn_out = attn_out.permute(0, 2, 1, 3).reshape(B, S, D)
+ x = residual + self.out_proj[i](attn_out)
+ residual = x
+ x = self.norm2[i](x)
+
+ moe_out, moe_loss = self.moe[i](x)
+
+ x = residual + moe_out
+
+ moe_loss_total = moe_loss_total + moe_loss
+
+ return x, moe_loss_total
+
+
+class CTRModel(nn.Module):
+ def __init__(self, rep_encoder, seq_encoder, d_model):
+ super().__init__()
+ self.rep_encoder = rep_encoder
+ self.seq_encoder = seq_encoder
+ self.d_model = d_model
+ self.linear = nn.Linear(d_model, 1)
+
+ def get_sequence_causal_mask(self, seq_info):
+ lengths = seq_info[1:] - seq_info[:-1]
+ lengths = lengths.view(-1)
+ indices = torch.cumsum(torch.ones_like(lengths), dim=0) - 1
+ result = torch.repeat_interleave(indices, lengths)
+ a = result.view(1, -1) - result.view(-1, 1)
+ out_mask = torch.tril((a == 0).to(torch.int32)).bool()
+ return out_mask
+
+ def forward(self, batch):
+ seq_input = self.rep_encoder(batch)
+ seq_mask = self.get_sequence_causal_mask(batch["user_offsets"])
+ encoder_output, moe_loss = self.seq_encoder(
+ x=seq_input,
+ extension={"mask": seq_mask.unsqueeze(0).unsqueeze(0)},
+ )
+ encoder_output_dim = encoder_output.shape[-1]
+ encoder_output = encoder_output.reshape(1, -1, encoder_output_dim).squeeze(0)
+ pred = self.linear(encoder_output)
+ pred_logits = torch.clamp(pred, min=-15.0, max=15.0)
+ return pred_logits, moe_loss
+
+
+# ============================================================
+# 模型加载入口
+# ============================================================
+
+def load_model(device='cuda:0', ckpt_path=None):
+ """加载模型并返回,供 evaluation.py 调用。
+
+ Args:
+ device: 推理设备(默认 'cuda:0')
+ ckpt_path: checkpoint 文件路径,默认使用 infer.py 同目录下的 ckpt.pt
+
+ Returns:
+ (model, device) 元组
+ """
+ emb_dim = 512
+ slot_num = 28
+ vocab_size = 5000000
+ d_model = 512
+ n_heads = 8
+ num_layers = 8
+ dim_ff = 1024
+
+ rep_encoder = RepEncoder(
+ vocab_size=vocab_size,
+ emb_dim=emb_dim,
+ padding_idx=0,
+ slot_num=slot_num,
+ d_model=d_model,
+ )
+ seq_encoder = TransformerEncoder(
+ d_model=d_model,
+ n_heads=n_heads,
+ num_layers=num_layers,
+ dim_ff=dim_ff,
+ act="relu",
+ )
+ model = CTRModel(rep_encoder, seq_encoder, d_model=d_model)
+
+ dev = torch.device(device if torch.cuda.is_available() else "cpu")
+
+ # 加载 checkpoint
+ # 若需要加载自定义修改的权重,请修改 479-488行逻辑,强制使用你文件夹中的权重
+ # 测评系统默认使用原始官方权重
+ if ckpt_path is None:
+ ckpt_path = Path(__file__).parent / 'ckpt.pt'
+ else:
+ ckpt_path = Path(ckpt_path)
+ if ckpt_path.exists():
+ ckpt = torch.load(ckpt_path, map_location='cpu', weights_only=False)
+ model.load_state_dict(ckpt['model_state_dict'])
+ print(f"[INFO] Loaded checkpoint from {ckpt_path} (epoch={ckpt.get('epoch', '?')})")
+ else:
+ print(f"[WARNING] Checkpoint {ckpt_path} not found, using random weights")
+
+ model.to(dev)
+ model.eval()
+ print(f"[INFO] Model ready. Device: {dev}")
+
+ return model, dev
+
+
+# ============================================================
+# 打分工具(与 evaluation.py 保持一致)
+# ============================================================
+
+def _read_predict(file_path):
+ predictions = []
+ with open(file_path, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if line:
+ predictions.append(float(line))
+ import numpy as np
+ return np.array(predictions)
+
+
+def _read_label(file_path):
+ labels = []
+ with open(file_path, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if line:
+ parts = line.split(',')
+ if len(parts) >= 4:
+ labels.append(float(parts[3]))
+ else:
+ labels.append(float(line))
+ import numpy as np
+ return np.array(labels)
+
+
+def _cal_score(predict_file, label_file, default_latency=0.0):
+ import numpy as np
+ from sklearn.metrics import roc_auc_score
+
+ predictions = _read_predict(predict_file)
+ labels = _read_label(label_file)
+
+ unique_labels = np.unique(labels)
+ if len(unique_labels) < 2:
+ print('[WARNING] only one class present in labels, AUC is not defined, returning 0.5')
+ auc = 0.5
+ else:
+ auc = roc_auc_score(labels, predictions)
+
+ mean_pred = np.mean(predictions)
+ mean_label = np.mean(labels)
+ if mean_label == 0:
+ pcoc = 1.0 if mean_pred == 0 else float('inf')
+ else:
+ pcoc = float(mean_pred / mean_label)
+
+ latency = default_latency
+ base_latency = 300
+ score_latency = max(0.0, (base_latency - latency) / base_latency) if latency < base_latency else 0.0
+
+ if pcoc < 0.85 or pcoc > 1.15:
+ score_model = 0.0
+ else:
+ score_model = ((auc - 0.65) * 1000 + (0.15 - abs(pcoc - 1)) / 0.15 * 10) / 360
+
+ score_all = score_latency * 70 + score_model * 30
+
+ return {
+ 'auc': auc,
+ 'pcoc': pcoc,
+ 'latency': latency,
+ 'score_latency': score_latency,
+ 'score_model': score_model,
+ 'score_all': score_all,
+ }
+
+
+# ============================================================
+# main:直接运行 infer.py 进行测试
+# ============================================================
+
+def main():
+ import io
+ import time
+ import argparse
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--ckpt', type=str, default=None, help='checkpoint 文件路径,默认使用同目录下的 ckpt.pt')
+ args = parser.parse_args()
+
+ cur_path = Path(__file__).parent.absolute()
+ ref_dir = cur_path / 'dataset'
+ history_dir = ref_dir / 'history'
+ input_file = ref_dir / 'test.csv'
+ output_file = Path('predict.txt')
+ label_file = ref_dir / 'label_data.txt'
+
+ # ----- 数据加载,优先从缓存读取 -----
+ MAX_SHARD_BYTES = 2 * 1024 * 1024 * 1024 # 2GB per shard
+ batches_cache_dir = ref_dir / 'cached_batches'
+
+ if batches_cache_dir.exists() and any(batches_cache_dir.glob('shard_*.pt')):
+ print(f'[INFO] loading cached batch shards from {batches_cache_dir}')
+ all_batches = []
+ shard_files = sorted(batches_cache_dir.glob('shard_*.pt'),
+ key=lambda p: int(p.stem.split('_')[1]))
+ for sf in shard_files:
+ shard_batches = torch.load(sf, weights_only=False)
+ all_batches.extend(shard_batches)
+ print(f'[INFO] loaded {len(shard_batches)} batches from {sf.name}')
+ print(f'[INFO] loaded {len(all_batches)} cached batches total from {len(shard_files)} shards')
+ else:
+ print('[INFO] start loading data from CSV')
+ history_files = sorted(history_dir.glob('*.csv')) if history_dir.exists() else []
+ all_files = history_files + [input_file]
+
+ item_dict, user_seq = load_sample_files(sample_files_list=all_files)
+ test_pred_logids = load_logids_from_file(input_file)
+ print(f'[INFO] Test pred logids count: {len(test_pred_logids)}')
+
+ max_feasign_per_slot = {1: 2}
+ test_dataset = CTRUserDataset(
+ item_dict, user_seq,
+ max_feasign_per_slot=max_feasign_per_slot,
+ pred_logids=test_pred_logids,
+ )
+ print(f'[INFO] num_users={test_dataset.num_users}, '
+ f'total_samples={test_dataset.total_samples}, '
+ f'pred_samples={len(test_pred_logids)}, '
+ f'max_sign_id={test_dataset.max_sign_id}')
+
+ test_loader = DataLoader(
+ test_dataset,
+ batch_size=50,
+ shuffle=False,
+ num_workers=0,
+ collate_fn=make_collate_fn(test_dataset.max_slot_id),
+ )
+
+ # 收集 batches 并按分片缓存
+ print('[INFO] collecting batches and saving sharded cache...')
+ all_batches = [batch for batch in test_loader]
+
+ batches_cache_dir.mkdir(parents=True, exist_ok=True)
+ shard_idx = 0
+ current_shard = []
+ current_size = 0
+ for batch in all_batches:
+ buf = io.BytesIO()
+ torch.save(batch, buf)
+ batch_size_bytes = buf.tell()
+ if current_shard and current_size + batch_size_bytes > MAX_SHARD_BYTES:
+ shard_path = batches_cache_dir / f'shard_{shard_idx:04d}.pt'
+ torch.save(current_shard, shard_path)
+ print(f'[INFO] saved shard {shard_path.name}: {len(current_shard)} batches, '
+ f'~{current_size / 1024**3:.2f}GB')
+ shard_idx += 1
+ current_shard = []
+ current_size = 0
+ current_shard.append(batch)
+ current_size += batch_size_bytes
+ if current_shard:
+ shard_path = batches_cache_dir / f'shard_{shard_idx:04d}.pt'
+ torch.save(current_shard, shard_path)
+ print(f'[INFO] saved shard {shard_path.name}: {len(current_shard)} batches, '
+ f'~{current_size / 1024**3:.2f}GB')
+ shard_idx += 1
+ print(f'[INFO] saved {len(all_batches)} batches to {shard_idx} shards in {batches_cache_dir}')
+
+ print('[INFO] data loading done')
+
+ # ----- 加载模型 -----
+ model, dev = load_model(ckpt_path=args.ckpt)
+
+ # ----- 推理 -----
+ print('*' * 20 + ' start inference ' + '*' * 20)
+ all_logids = []
+ all_probs = []
+ time_sum = 0.0
+
+ with torch.no_grad():
+ for batch in tqdm(all_batches, desc="Inference"):
+ batch = move_batch_to_device(batch, dev)
+ pred_mask = batch["pred_mask"].bool()
+
+ t_start = time.time()
+ logits, moe_loss = model(batch)
+ logits = logits.squeeze(-1)
+ probs = torch.sigmoid(logits)
+ time_sum += time.time() - t_start
+
+ masked_logids = batch["logid"][pred_mask].cpu().tolist()
+ masked_probs = probs[pred_mask].cpu().tolist()
+ all_logids.extend(masked_logids)
+ all_probs.extend(masked_probs)
+
+ print(f'[INFO] inference time: {round(time_sum, 4)}s')
+ print('*' * 20 + ' end inference ' + '*' * 20)
+
+ # ----- 按 test.csv 顺序写预测文件 -----
+ logid_to_prob = dict(zip(all_logids, all_probs))
+ test_logids_in_order = []
+ with open(input_file, 'r') as f:
+ for line in f:
+ line = line.strip()
+ if line:
+ test_logids_in_order.append(int(line.split(',')[0]))
+ output_file.parent.mkdir(parents=True, exist_ok=True)
+ with open(output_file, 'w') as f:
+ for logid in test_logids_in_order:
+ f.write(f"{logid_to_prob[logid]}\n")
+ print(f'[INFO] predictions written to {output_file}, total: {len(test_logids_in_order)}')
+
+ # ----- 打分 -----
+ if label_file.exists():
+ result = _cal_score(output_file, label_file, default_latency=time_sum)
+ print(f'[INFO] AUC: {result["auc"]:.6f}')
+ print(f'[INFO] PCOC: {result["pcoc"]:.6f}')
+ print(f'[INFO] Latency: {result["latency"]:.4f}s')
+ print(f'[INFO] score_latency: {result["score_latency"]:.6f}')
+ print(f'[INFO] score_model: {result["score_model"]:.6f}')
+ print(f'[INFO] score_all: {result["score_all"]:.6f}')
+ return result
+ else:
+ print(f'[WARNING] label file {label_file} not found, skipping scoring')
+ return None
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/代码/code/requirements.txt b/代码/code/requirements.txt
new file mode 100644
index 0000000..5c2862d
--- /dev/null
+++ b/代码/code/requirements.txt
@@ -0,0 +1,29 @@
+filelock==3.25.2
+fsspec==2026.2.0
+Jinja2==3.1.6
+joblib==1.5.3
+MarkupSafe==3.0.3
+mpmath==1.3.0
+networkx==3.4.2
+numpy==2.2.6
+nvidia-cublas-cu12==12.4.5.8
+nvidia-cuda-cupti-cu12==12.4.127
+nvidia-cuda-nvrtc-cu12==12.4.127
+nvidia-cuda-runtime-cu12==12.4.127
+nvidia-cudnn-cu12==9.1.0.70
+nvidia-cufft-cu12==11.2.1.3
+nvidia-curand-cu12==10.3.5.147
+nvidia-cusolver-cu12==11.6.1.9
+nvidia-cusparse-cu12==12.3.1.170
+nvidia-cusparselt-cu12==0.6.2
+nvidia-nccl-cu12==2.21.5
+nvidia-nvjitlink-cu12==12.4.127
+nvidia-nvtx-cu12==12.4.127
+scikit-learn==1.7.2
+scipy==1.15.3
+sympy==1.13.1
+threadpoolctl==3.6.0
+torch==2.6.0
+tqdm==4.67.3
+triton==3.2.0
+typing_extensions==4.15.0
\ No newline at end of file
diff --git a/代码/main.ipynb b/代码/main.ipynb
new file mode 100644
index 0000000..2cec87b
--- /dev/null
+++ b/代码/main.ipynb
@@ -0,0 +1,387 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 百度2026CTI:生成式推荐广告排序推理性能优化\n",
+ "\n",
+ "> 报名链接:[https://aistudio.baidu.com/competition/detail/1461](https://aistudio.baidu.com/competition/detail/1461/0/introduction)\n",
+ "\n",
+ "## 赛道概要背景:\n",
+ "\n",
+ "传统广告排序模型已难以满足个性化推荐需求,生成式广告排序模型凭借强大的序列建模与语义理解能力成为行业趋势。该类模型依托 Transformer 架构,能深度挖掘用户点击、转化等超长行为序列中的长距离依赖关系,精准捕捉用户兴趣演化规律,从而生成更具吸引力的个性化广告内容,提升广告点击率与用户体验。但在实际应用中,存在很多挑战,如模型参数规模大、注意力计算复杂、存在超长历史序列、量化影响推理精度等。\n",
+ "因此,本次赛事聚焦于如何提升生成式广告排序的推理性能,我们期待参赛选手能够从框架优化、算法创新、高性能计算等多个角度出发,提出突破现有技术瓶颈的创新方案。\n",
+ "\n",
+ "本次任务提供百度商业真实的用户行为数据、广告信息,选手需要在保证模型推理效果的前提下,极致优化推理性能。\n",
+ "\n",
+ "## 数据集介绍:\n",
+ "\n",
+ "1. 用户行为数据:包括全局唯一的日志ID和用户ID、广告曝光时间、广告点击时间等信息;\n",
+ "2. 广告内容:包括广告的文本描述、图片信息、广告主信息等;\n",
+ "3. 上下文信息:包括用户的地理位置、职业、性别、设备类型等;\n",
+ "4. 用户统计信息:包括用户的活跃度、兴趣标签、历史点击率等统计数据。\n",
+ "\n",
+ "数据示例(按行字段,时间戳格式为Unix Timestamp):\n",
+ "\n",
+ "\n",
+ "## 评估指标\n",
+ "\n",
+ "1. 推理效率评估:参赛者提交inference脚本后,会通过统计inference脚本的运行时间,来计算在测试集上单条样本的平均推理时间。推理效率打分采用如下公示,如平均推理时间超过定义的时间限制,则本项和最终得分为0:\n",
+ "\n",
+ "\n",
+ "\n",
+ "2. 策略效果评估:综合考虑AUC及PCOC指标,PCOC需满足[0.85, 1.15],AUC需满足[0.65, 1],方可进入榜单排序,否则本项和最终得分为0,具体规则如下:得分由pcoc和auc组合而成:\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "* 指标说明:\n",
+ " * AUC:ROC曲线下的面积,越接近与1越好\n",
+ " * PCOC:预估转化率 / 真实转化率,越接近于1越好\n",
+ "\n",
+ "3. 计分规则:综合考虑推理性能和策略效果两个指标,计分规则如下所示;\n",
+ "\n",
+ "\n",
+ "\n",
+ "**警告⚠️**\n",
+ "\n",
+ "> * 评估容器有整体运行时间限制,如果超出则无法计入成绩;(build_env.sh等其他部分耗时均要在20min内)\n",
+ "> * 任何作弊行为将会取消队伍成绩。\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1 依赖库安装\n",
+ "\n",
+ "由于平台安装依赖很慢,提供了如下两种方式:\n",
+ "1. 自行安装\n",
+ "2. tar包 【建议:方便、快】\n",
+ "\n",
+ "任选一种即可。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "/opt/conda/envs/python35-paddle120-env/bin/python\r\n"
+ ]
+ }
+ ],
+ "source": [
+ "!which python"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "# 自行安装,网络好的话 也很快\n",
+ "# 平台上应该有显示 bug,等待下载一段时间后,页面状态不更新\n",
+ "# 从终端top看下进程无cpu占用,大概率已经安装完成了,点击上方的重启内核后正常下一步执行infer即可\n",
+ "!mkdir -p /home/aistudio/libraries\n",
+ "!pip install uv\n",
+ "!/home/aistudio/libraries/bin/uv pip install \\\n",
+ " -r /home/aistudio/code/requirements.txt \\\n",
+ " --target /home/aistudio/libraries \\\n",
+ " -i https://mirrors.aliyun.com/pypi/simple/\n",
+ "# -i https://mirror.baidu.com/pypi/simple/\n",
+ "# -i https://pypi.tuna.tsinghua.edu.cn/simple/"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--2026-04-09 19:53:40-- https://studio-package.bj.bcebos.com/2026-shangye-python-package/external-libraries.tar\r\n",
+ "Resolving studio-package.bj.bcebos.com (studio-package.bj.bcebos.com)... 100.64.80.160, 100.67.184.196, 100.64.80.202\r\n",
+ "Connecting to studio-package.bj.bcebos.com (studio-package.bj.bcebos.com)|100.64.80.160|:443... connected.\r\n",
+ "HTTP request sent, awaiting response... 200 OK\r\n",
+ "Length: 5699010560 (5.3G) [application/octet-stream]\r\n",
+ "Saving to: 'libraries.tar'\r\n",
+ "\r\n",
+ "libraries.tar 100%[===================>] 5.31G 120MB/s in 45s \r\n",
+ "\r\n",
+ "2026-04-09 19:54:25 (120 MB/s) - 'libraries.tar' saved [5699010560/5699010560]\r\n",
+ "\r\n",
+ "Looking in indexes: http://mirrors.baidubce.com/pypi/simple/\r\n",
+ "Requirement already satisfied: uv in ./external-libraries/lib/python3.10/site-packages (0.10.7)\r\n",
+ "\u001b[2mUsing CPython 3.10.10 interpreter at: /opt/conda/envs/python35-paddle120-env/bin/python\u001b[0m\r\n",
+ "\u001b[2mAudited \u001b[1m29 packages\u001b[0m \u001b[2min 36ms\u001b[0m\u001b[0m\r\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 强烈建议使用 tar 包 安装\n",
+ "!mkdir -p /home/aistudio/libraries /home/aistudio/external-libraries\n",
+ "!wget https://studio-package.bj.bcebos.com/2026-shangye-python-package/external-libraries.tar -O libraries.tar\n",
+ "# 解压到 libraries 目录(跳过 external-libraries 层级)\n",
+ "!tar -xf libraries.tar --strip-components=1 -C libraries\n",
+ "!pip install uv\n",
+ "!/home/aistudio/external-libraries/bin/uv pip install \\\n",
+ " -r /home/aistudio/code/requirements.txt \\\n",
+ " --target /home/aistudio/libraries \\\n",
+ " -i https://mirrors.aliyun.com/pypi/simple/"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 2 数据集和模型链接\n",
+ "\n",
+ "文件太大, 请耐心等待项目中数据和权重下载完成后进行 ln\n",
+ "\n",
+ "选手测试集样本的 auc 和 pcoc 仅供参考,以最终提交验证集结果为准\n",
+ "\n",
+ "\n",
+ "选手不可对数据集进行任何更改 【违规为0分】"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "!ln -s /home/aistudio/data/datasets/375013/2026_cti_data/dataset /home/aistudio/code/dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "!cat /home/aistudio/data/models/45703/2026_cti_model/ckpt.part.0* > /home/aistudio/code/ckpt.pt"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "total 21488359\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 39 Apr 9 15:53 build_env.sh\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 10605862351 Apr 9 19:08 ckpt.pt\r\n",
+ "lrwxrwxrwx 1 aistudio aistudio 57 Apr 9 15:53 dataset -> /home/aistudio/data/datasets/375013/2026_cti_data/dataset\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 5699010560 Apr 3 16:33 external-libraries.tar\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 25338 Apr 9 15:53 infer.py\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 5699010560 Apr 3 16:33 libraries.tar\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 161928 Apr 9 19:36 predict.txt\r\n",
+ "-rw-r--r-- 1 aistudio aistudio 652 Apr 9 19:03 requirements.txt\r\n"
+ ]
+ }
+ ],
+ "source": [
+ "!ls -l code"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3 推理\n",
+ "\n",
+ "选手不可对组网和相关参数进行修改。 【违规为0分】\n",
+ "\n",
+ "量化稀疏剪枝除外"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "/home/aistudio/code\r\n",
+ "/home/aistudio/libraries/torch/cuda/__init__.py:61: FutureWarning: The pynvml package is deprecated. Please install nvidia-ml-py instead. If you did not install pynvml directly, please report this to the maintainers of the package that installed pynvml for you.\r\n",
+ " import pynvml # type: ignore[import]\r\n",
+ "[INFO] loading cached batch shards from /home/aistudio/code/dataset/cached_batches\r\n",
+ "[INFO] loaded 217 batches from shard_0000.pt\r\n",
+ "[INFO] loaded 218 batches from shard_0001.pt\r\n",
+ "[INFO] loaded 215 batches from shard_0002.pt\r\n",
+ "[INFO] loaded 226 batches from shard_0003.pt\r\n",
+ "[INFO] loaded 240 batches from shard_0004.pt\r\n",
+ "[INFO] loaded 260 batches from shard_0005.pt\r\n",
+ "[INFO] loaded 284 batches from shard_0006.pt\r\n",
+ "[INFO] loaded 335 batches from shard_0007.pt\r\n",
+ "[INFO] loaded 44 batches from shard_0008.pt\r\n",
+ "[INFO] loaded 2039 cached batches total from 9 shards\r\n",
+ "[INFO] data loading done\r\n",
+ "[INFO] Loaded checkpoint from /home/aistudio/code/ckpt.pt (epoch=1)\r\n",
+ "[INFO] Model ready. Device: cuda:0\r\n",
+ "******************** start inference ********************\r\n",
+ "Inference: 100%|████████████████████████████| 2039/2039 [03:57<00:00, 8.57it/s]\r\n",
+ "[INFO] inference time: 229.1826s\r\n",
+ "******************** end inference ********************\r\n",
+ "[INFO] predictions written to predict.txt, total: 7774\r\n",
+ "[INFO] AUC: 0.759232\r\n",
+ "[INFO] PCOC: 1.110063\r\n",
+ "[INFO] Latency: 229.1826s\r\n",
+ "[INFO] score_latency: 0.236058\r\n",
+ "[INFO] score_model: 0.310817\r\n",
+ "[INFO] score_all: 25.848547\r\n"
+ ]
+ }
+ ],
+ "source": [
+ "%cd /home/aistudio/code\n",
+ "!/opt/conda/envs/python35-paddle120-env/bin/python infer.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 4 提交\n",
+ "\n",
+ "参赛选手需要提交一个命名为xxx.zip的压缩包,压缩包内需要包含以下内容:\n",
+ "\n",
+ "1. 额外的python包环境,选手可以通过将python环境打包放在当前工作目录\n",
+ "2. 优化过的模型文件,如量化后的模型等\n",
+ "3. 程序入口infer.py脚本\n",
+ "\n",
+ "PS: \n",
+ "> 打包不要包含 **eval 文件夹** 和 **dataset 文件夹** \n",
+ "> 权重若使用原版,无需修改权重参数且无需上传权重 \n",
+ "> 若修改权重,请自行完善和修改 infer.py相关逻辑 ****\n",
+ "> 若需要进行编译等其他复杂操作,请在 build_env.sh 中完成 "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 其他注意事项\n",
+ "\n",
+ "详见 任务提交接口说明.md"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false,
+ "jupyter": {
+ "outputs_hidden": false
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "/home/aistudio/code\r\n",
+ "updating: requirements.txt (deflated 51%)\r\n",
+ "updating: build_env.sh (stored 0%)\r\n",
+ "updating: infer.py (deflated 71%)\r\n"
+ ]
+ }
+ ],
+ "source": [
+ "%cd /home/aistudio/code\n",
+ "!zip -y -r ../eval.zip requirements.txt build_env.sh infer.py"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**❤️ 将上述压缩包提交至比赛平台 ❤️**\n",
+ "\n",
+ "[https://aistudio.baidu.com/competition/detail/1461/0/submit-result](https://aistudio.baidu.com/competition/detail/1461/0/submit-result)\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "py35-paddle1.2.0"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/代码/任务提交接口说明.md b/代码/任务提交接口说明.md
new file mode 100644
index 0000000..66caa1e
--- /dev/null
+++ b/代码/任务提交接口说明.md
@@ -0,0 +1,115 @@
+# 选手提交压缩包规范
+
+本文档说明选手提交代码压缩包的格式与运行要求。不符合以下任一约束,评测将失败或总分计 0。
+
+---
+
+## 1. 压缩包格式
+
+- **允许的后缀**:`.zip`、`.tar.gz`、`.tar`。其他后缀评测会直接失败。
+- **目录布局**:压缩包解压后,所有文件必须**直接位于根目录**,不得包含最外层包裹目录。
+
+错误示例(解压后多一层目录):
+```
+submit.zip
+└── my_code/
+ └── infer.py
+```
+
+正确示例:
+```
+submit.zip
+├── infer.py
+├── requirements.txt # 可选
+└── build_env.sh # 可选
+```
+
+---
+
+## 2. 必需文件
+
+### 2.1 `infer.py`(必需)
+
+根目录必须存在可被 Python 正常 `import` 的 `infer` 模块(即 `infer.py` 或 `infer/__init__.py`),并对外提供以下接口:
+
+| 接口 | 签名 / 说明 |
+|------|-------------|
+| `load_sample_files` | `load_sample_files(sample_files_list: List[Path]) -> (item_dict, user_seq)` |
+| `CTRTestSeqDataset` | 类;构造参数:`test_logids_ordered`, `item_dict`, `user_seq`, `max_feasign_per_slot`, `max_ctx_len`;需暴露属性 `max_slot_id` |
+| `make_collate_fn` | `make_collate_fn(max_slot_id) -> Callable`,用作 `DataLoader` 的 `collate_fn` |
+| `load_model` | `load_model(ckpt_path: Path) -> (model, device)` |
+| `move_batch_to_device` | `move_batch_to_device(batch, device) -> batch` |
+| `model(batch)` | 前向返回 `(logits, moe_loss)`;`logits.squeeze(-1)` 经 `sigmoid` 应为点击概率 |
+
+`batch` 必须包含以下键:
+- `logid`:样本 logid,需支持 `.shape[0]` 与 `[pred_mask].cpu().tolist()`
+- `pred_mask`:可转 `bool` 的掩码 Tensor
+
+### 2.2 评测端提供、选手**不得**自带的文件
+
+评测端会统一提供下列路径,选手**不要**在压缩包中包含同名文件/目录:
+
+- `dataset/`:测试与历史数据
+- `ckpt.pt`:模型权重
+
+---
+
+## 3. 可选文件
+
+| 文件 | 行为 | 失败条件 |
+|------|------|----------|
+| `requirements.txt` | 评测前安装依赖(使用阿里云 PyPI 镜像:`https://mirrors.aliyun.com/pypi/simple`) | 安装失败则评测失败 |
+| `build_env.sh` | 在代码根目录下执行 `sh build_env.sh`,**超时 720 秒** | 超时或返回码非 0 则评测失败 |
+
+依赖必须可通过阿里云 PyPI 镜像安装;`build_env.sh` 若需访问外部资源,请自行保证在 720 秒内完成。
+
+---
+
+## 4. 运行时与指标约束
+
+### 4.1 推理延迟
+
+- 延迟阈值:**300 秒**。
+- 只统计**模型前向**时间(逐 batch 累加 `model(batch)` 耗时),数据加载、模型加载不计入。
+- 延迟得分:`score_latency = (300 - latency) / 300`。
+- 若 `latency ≥ 300`,`score_latency = 0`,**总分直接置 0**。
+
+### 4.2 模型指标门槛
+
+以下任一不满足,策略分 `score_model = 0`,**总分直接置 0**:
+
+- `AUC ∈ [0.65, 1.0]`
+- `PCOC ∈ [0.85, 1.15]`,其中 `PCOC = mean(predictions) / mean(labels)`
+
+### 4.3 总分公式
+
+仅当 `score_latency > 0` 且 `score_model > 0` 时:
+
+```
+score_all = score_latency * 70 + score_model * 30
+```
+
+否则 `score_all = 0`。
+
+---
+
+## 5. 违规清单(评测失败或 0 分)
+
+1. 压缩包后缀非 `.zip` / `.tar.gz` / `.tar`。
+2. 压缩包内含最外层包裹目录,导致根目录找不到 `infer.py`。
+3. 缺失 `infer.py`,或其任一必需接口签名/返回值结构不符。
+4. `requirements.txt` 安装失败。
+5. `build_env.sh` 执行超过 720 秒或返回码非 0。
+6. 推理总耗时 ≥ 300 秒。
+7. `AUC` 不在 `[0.65, 1.0]` 或 `PCOC` 不在 `[0.85, 1.15]`。
+8. 压缩包自带 `dataset/` 或 `ckpt.pt`,覆盖评测端提供的路径。
+
+---
+
+## 6. 最小可用提交示例
+
+```
+submit.zip
+├── infer.py # 实现第 2.1 节全部接口
+└── requirements.txt # 可选;列出 torch 等依赖
+```
\ No newline at end of file
diff --git a/论文/GRAB_An_LLM-Inspired_Sequence-First_CTR_Prediction.pdf b/论文/GRAB_An_LLM-Inspired_Sequence-First_CTR_Prediction.pdf
new file mode 100644
index 0000000..06e85cd
--- /dev/null
+++ b/论文/GRAB_An_LLM-Inspired_Sequence-First_CTR_Prediction.pdf
@@ -0,0 +1,7918 @@
+%PDF-1.7
+%
+1 0 obj
+<< /Metadata 3 0 R /Names 4 0 R /OpenAction 5 0 R /PageMode /UseNone /Pages 6 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Author (Shaopeng Chen; Chuyue Xie; Huimin Ren; Shaozong Zhang; Han Zhang; Ruobing Cheng; Zhiqiang Cao; Zehao Ju; Yu Gao; Jie Ding; Xiaodong Chen; Xuewu Jiao; Shuanglong Li; Liu Lin) /Creator (arXiv GenPDF \(tex2pdf:57610bf\)) /DOI (https://doi.org/10.48550/arXiv.2602.01865) /License (http://creativecommons.org/licenses/by/4.0/) /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.28 \(TeX Live 2025\) kpathsea version 6.4.1) /Producer (pikepdf 8.15.1) /Title (GRAB: An LLM-Inspired Sequence-First Click-Through Rate Prediction Modeling Paradigm) /Trapped /False /arXivID (https://arxiv.org/abs/2602.01865v2) >>
+endobj
+3 0 obj
+<< /Subtype /XML /Type /Metadata /Length 1960 >>
+stream
+
+
+
+ GRAB: An LLM-Inspired Sequence-First Click-Through Rate Prediction Modeling ParadigmShaopeng ChenChuyue XieHuimin RenShaozong ZhangHan ZhangRuobing ChengZhiqiang CaoZehao JuYu GaoJie DingXiaodong ChenXuewu JiaoShuanglong LiLiu Linhttp://creativecommons.org/licenses/by/4.0/cs.IRcs.AI
+
+
+
+
+endstream
+endobj
+4 0 obj
+<< /Dests 7 0 R >>
+endobj
+5 0 obj
+<< /D [ 8 0 R /Fit ] /S /GoTo >>
+endobj
+6 0 obj
+<< /Count 20 /Kids [ 9 0 R 10 0 R 11 0 R 12 0 R ] /Type /Pages >>
+endobj
+7 0 obj
+<< /Kids [ 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R 18 0 R ] /Limits [ (ALC@unique.1) (table.caption.9) ] >>
+endobj
+8 0 obj
+<< /Annots [ 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R 47 0 R 48 0 R 49 0 R 50 0 R 51 0 R 52 0 R 53 0 R 54 0 R 55 0 R 56 0 R 57 0 R 58 0 R 59 0 R 60 0 R 61 0 R 62 0 R 63 0 R 64 0 R 65 0 R 66 0 R 67 0 R 68 0 R 69 0 R 70 0 R 71 0 R 72 0 R 73 0 R 74 0 R 75 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R ] /Contents [ 95 0 R 96 0 R 97 0 R 98 0 R ] /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources 99 0 R /Type /Page >>
+endobj
+9 0 obj
+<< /Count 6 /Kids [ 8 0 R 100 0 R 101 0 R 102 0 R 103 0 R 104 0 R ] /Parent 6 0 R /Type /Pages >>
+endobj
+10 0 obj
+<< /Count 6 /Kids [ 105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R ] /Parent 6 0 R /Type /Pages >>
+endobj
+11 0 obj
+<< /Count 6 /Kids [ 111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R ] /Parent 6 0 R /Type /Pages >>
+endobj
+12 0 obj
+<< /Count 2 /Kids [ 117 0 R 118 0 R ] /Parent 6 0 R /Type /Pages >>
+endobj
+13 0 obj
+<< /Kids [ 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R 124 0 R ] /Limits [ (ALC@unique.1) (cite.chen2024hllm) ] >>
+endobj
+14 0 obj
+<< /Kids [ 125 0 R 126 0 R 127 0 R 128 0 R 129 0 R 130 0 R ] /Limits [ (cite.cheng2016wide) (cite.shin2023scaling) ] >>
+endobj
+15 0 obj
+<< /Kids [ 131 0 R 132 0 R 133 0 R 134 0 R 135 0 R 136 0 R ] /Limits [ (cite.si2024twin) (figure.caption.1) ] >>
+endobj
+16 0 obj
+<< /Kids [ 137 0 R 138 0 R 139 0 R 140 0 R 141 0 R 142 0 R ] /Limits [ (figure.caption.10) (subsection.3.2) ] >>
+endobj
+17 0 obj
+<< /Kids [ 143 0 R 144 0 R 145 0 R 146 0 R 147 0 R 148 0 R ] /Limits [ (subsection.3.3) (table.caption.8) ] >>
+endobj
+18 0 obj
+<< /Kids [ 149 0 R ] /Limits [ (table.caption.9) (table.caption.9) ] >>
+endobj
+19 0 obj
+<< /A << /D (cite.naumov2019deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 100.257 266.534 159.67 277.089 ] /Subtype /Link /Type /Annot >>
+endobj
+20 0 obj
+<< /A << /D (cite.naumov2019deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 162.898 266.534 185.214 277.089 ] /Subtype /Link /Type /Annot >>
+endobj
+21 0 obj
+<< /A << /D (cite.mudigere2022software) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 57.366 230.279 118.254 241.223 ] /Subtype /Link /Type /Annot >>
+endobj
+22 0 obj
+<< /A << /D (cite.mudigere2022software) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 120.9 230.279 142.42 241.223 ] /Subtype /Link /Type /Annot >>
+endobj
+23 0 obj
+<< /A << /D (cite.zhou2018deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 145.34 230.279 188.878 241.223 ] /Subtype /Link /Type /Annot >>
+endobj
+24 0 obj
+<< /A << /D (cite.zhou2018deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 191.525 230.279 213.044 241.223 ] /Subtype /Link /Type /Annot >>
+endobj
+25 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 215.964 230.279 264.384 241.223 ] /Subtype /Link /Type /Annot >>
+endobj
+26 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 267.03 230.279 288.549 241.223 ] /Subtype /Link /Type /Annot >>
+endobj
+27 0 obj
+<< /A << /D (cite.guo2017deepfm) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 54.444 218.324 97.26 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+28 0 obj
+<< /A << /D (cite.guo2017deepfm) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 100.863 218.324 123.179 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+29 0 obj
+<< /A << /D (cite.bai2025comprehensive) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 127.067 218.324 166.498 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+30 0 obj
+<< /A << /D (cite.bai2025comprehensive) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 170.101 218.324 192.418 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+31 0 obj
+<< /A << /D (cite.wang2017deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 196.305 218.324 245.091 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+32 0 obj
+<< /A << /D (cite.wang2017deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 248.695 218.324 271.011 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+33 0 obj
+<< /A << /D (cite.ma2018modeling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 274.898 218.324 290.437 229.268 ] /Subtype /Link /Type /Annot >>
+endobj
+34 0 obj
+<< /A << /D (cite.ma2018modeling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 54.444 206.369 75.644 217.313 ] /Subtype /Link /Type /Annot >>
+endobj
+35 0 obj
+<< /A << /D (cite.ma2018modeling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 78.617 206.369 104.715 217.313 ] /Subtype /Link /Type /Annot >>
+endobj
+36 0 obj
+<< /A << /D (subsection.A.1) /S /GoTo >> /Border [ 0 0 0 ] /C [ 1 0 0 ] /H /I /Rect [ 152.737 146.593 169.145 157.537 ] /Subtype /Link /Type /Annot >>
+endobj
+37 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 261.911 122.683 290.437 133.627 ] /Subtype /Link /Type /Annot >>
+endobj
+38 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 54.444 103.89 290.437 115.709 ] /Subtype /Link /Type /Annot >>
+endobj
+39 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 54.444 93.927 290.437 103.976 ] /Subtype /Link /Type /Annot >>
+endobj
+40 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 54.444 85.906 290.437 87.898 ] /Subtype /Link /Type /Annot >>
+endobj
+41 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 54.444 72.954 290.437 84.073 ] /Subtype /Link /Type /Annot >>
+endobj
+42 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 306.444 573.723 327.139 584.666 ] /Subtype /Link /Type /Annot >>
+endobj
+43 0 obj
+<< /A << /D (cite.cheng2016wide) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 329.751 573.723 351.27 584.666 ] /Subtype /Link /Type /Annot >>
+endobj
+44 0 obj
+<< /A << /D (cite.wu2024survey) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 354.156 573.723 390.614 584.666 ] /Subtype /Link /Type /Annot >>
+endobj
+45 0 obj
+<< /A << /D (cite.wu2024survey) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 393.226 573.723 414.746 584.666 ] /Subtype /Link /Type /Annot >>
+endobj
+46 0 obj
+<< /A << /D (cite.zhang2024scaling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 366.488 513.947 415.793 524.89 ] /Subtype /Link /Type /Annot >>
+endobj
+47 0 obj
+<< /A << /D (cite.zhang2024scaling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 418.782 513.947 445.681 524.89 ] /Subtype /Link /Type /Annot >>
+endobj
+48 0 obj
+<< /A << /D (cite.mudigere2022software) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 448.948 513.947 511.533 524.89 ] /Subtype /Link /Type /Annot >>
+endobj
+49 0 obj
+<< /A << /D (cite.mudigere2022software) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 514.522 513.947 536.44 524.89 ] /Subtype /Link /Type /Annot >>
+endobj
+50 0 obj
+<< /A << /D (cite.kaplan2020scaling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 345.107 472.104 397.09 483.047 ] /Subtype /Link /Type /Annot >>
+endobj
+51 0 obj
+<< /A << /D (cite.kaplan2020scaling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 400.044 472.104 421.683 483.047 ] /Subtype /Link /Type /Annot >>
+endobj
+52 0 obj
+<< /A << /D (cite.zhang2024wukong) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 424.912 472.104 473.624 483.047 ] /Subtype /Link /Type /Annot >>
+endobj
+53 0 obj
+<< /A << /D (cite.zhang2024wukong) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 476.578 472.104 502.579 483.047 ] /Subtype /Link /Type /Annot >>
+endobj
+54 0 obj
+<< /A << /D (cite.zhang2024scaling) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 503.317 472.104 510.221 483.047 ] /Subtype /Link /Type /Annot >>
+endobj
+55 0 obj
+<< /A << /D (cite.wu2024survey) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 526.743 424.283 542.437 435.227 ] /Subtype /Link /Type /Annot >>
+endobj
+56 0 obj
+<< /A << /D (cite.wu2024survey) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 306.444 412.328 327.709 423.271 ] /Subtype /Link /Type /Annot >>
+endobj
+57 0 obj
+<< /A << /D (cite.wu2024survey) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 330.689 412.328 352.487 423.271 ] /Subtype /Link /Type /Annot >>
+endobj
+58 0 obj
+<< /A << /D (cite.li2024large) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 355.745 412.328 388.31 423.271 ] /Subtype /Link /Type /Annot >>
+endobj
+59 0 obj
+<< /A << /D (cite.li2024large) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 391.29 412.328 417.485 423.271 ] /Subtype /Link /Type /Annot >>
+endobj
+60 0 obj
+<< /A << /D (subsection.A.2) /S /GoTo >> /Border [ 0 0 0 ] /C [ 1 0 0 ] /H /I /Rect [ 478.785 412.328 495.354 423.271 ] /Subtype /Link /Type /Annot >>
+endobj
+61 0 obj
+<< /A << /D (cite.li2024large) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 401.023 388.417 433.003 399.361 ] /Subtype /Link /Type /Annot >>
+endobj
+62 0 obj
+<< /A << /D (cite.li2024large) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 435.836 388.417 461.69 399.361 ] /Subtype /Link /Type /Annot >>
+endobj
+63 0 obj
+<< /A << /D (cite.rajput2023recommender) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 464.797 388.417 514.136 399.361 ] /Subtype /Link /Type /Annot >>
+endobj
+64 0 obj
+<< /A << /D (cite.rajput2023recommender) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 516.969 388.417 538.488 399.361 ] /Subtype /Link /Type /Annot >>
+endobj
+65 0 obj
+<< /A << /D (cite.zhai2024actions) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 443.618 376.462 485.72 387.406 ] /Subtype /Link /Type /Annot >>
+endobj
+66 0 obj
+<< /A << /D (cite.zhai2024actions) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 488.703 376.462 510.621 387.406 ] /Subtype /Link /Type /Annot >>
+endobj
+67 0 obj
+<< /A << /D (cite.zhou2018deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 364.832 340.597 410.823 351.54 ] /Subtype /Link /Type /Annot >>
+endobj
+68 0 obj
+<< /A << /D (cite.zhou2018deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 414.037 340.597 436.354 351.54 ] /Subtype /Link /Type /Annot >>
+endobj
+69 0 obj
+<< /A << /D (cite.zhou2019deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 439.852 340.597 462.169 351.54 ] /Subtype /Link /Type /Annot >>
+endobj
+70 0 obj
+<< /A << /D (cite.kang2018self) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 465.668 340.597 541.141 351.54 ] /Subtype /Link /Type /Annot >>
+endobj
+71 0 obj
+<< /A << /D (cite.kang2018self) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 306.444 328.642 328.76 339.585 ] /Subtype /Link /Type /Annot >>
+endobj
+72 0 obj
+<< /A << /D (cite.sun2019bert4rec) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 333.436 328.642 376.141 339.585 ] /Subtype /Link /Type /Annot >>
+endobj
+73 0 obj
+<< /A << /D (cite.sun2019bert4rec) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 380.532 328.642 402.848 339.585 ] /Subtype /Link /Type /Annot >>
+endobj
+74 0 obj
+<< /A << /D (cite.naumov2019deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 309.366 292.776 366.015 303.72 ] /Subtype /Link /Type /Annot >>
+endobj
+75 0 obj
+<< /A << /D (cite.naumov2019deep) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 368.784 292.776 390.304 303.72 ] /Subtype /Link /Type /Annot >>
+endobj
+76 0 obj
+<< /A << /D (cite.zhai2024actions) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 393.346 292.776 434.415 303.72 ] /Subtype /Link /Type /Annot >>
+endobj
+77 0 obj
+<< /A << /D (cite.zhai2024actions) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 437.184 292.776 458.704 303.72 ] /Subtype /Link /Type /Annot >>
+endobj
+78 0 obj
+<< /A << /D (cite.zhang2024wukong) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 461.746 292.776 509.865 303.72 ] /Subtype /Link /Type /Annot >>
+endobj
+79 0 obj
+<< /A << /D (cite.zhang2024wukong) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 512.634 292.776 538.488 303.72 ] /Subtype /Link /Type /Annot >>
+endobj
+80 0 obj
+<< /A << /D (cite.vaswani2017attention) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 371.062 203.501 432.33 214.056 ] /Subtype /Link /Type /Annot >>
+endobj
+81 0 obj
+<< /A << /D (cite.vaswani2017attention) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 436.733 203.501 459.049 214.056 ] /Subtype /Link /Type /Annot >>
+endobj
+82 0 obj
+<< /A << /D (cite.krell2021efficient) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 463.737 203.501 511.536 214.056 ] /Subtype /Link /Type /Annot >>
+endobj
+83 0 obj
+<< /A << /D (cite.krell2021efficient) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 515.939 203.501 538.256 214.056 ] /Subtype /Link /Type /Annot >>
+endobj
+84 0 obj
+<< /A << /D (cite.krell2021efficient) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 492.77 167.247 541.141 178.191 ] /Subtype /Link /Type /Annot >>
+endobj
+85 0 obj
+<< /A << /D (cite.krell2021efficient) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 306.444 155.292 328.76 166.235 ] /Subtype /Link /Type /Annot >>
+endobj
+86 0 obj
+<< /A << /D (cite.baylor2017tfx) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 380.963 131.381 430.34 142.35 ] /Subtype /Link /Type /Annot >>
+endobj
+87 0 obj
+<< /A << /D (cite.baylor2017tfx) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 432.923 131.381 454.442 142.35 ] /Subtype /Link /Type /Annot >>
+endobj
+88 0 obj
+<< /A << /D (cite.polyzotis2019data) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 457.298 131.381 516.447 142.35 ] /Subtype /Link /Type /Annot >>
+endobj
+89 0 obj
+<< /A << /D (cite.polyzotis2019data) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 519.03 131.381 540.549 142.35 ] /Subtype /Link /Type /Annot >>
+endobj
+90 0 obj
+<< /A << /D (cite.sculley2015hidden) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 306.444 119.426 360.032 130.37 ] /Subtype /Link /Type /Annot >>
+endobj
+91 0 obj
+<< /A << /D (cite.sculley2015hidden) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 363.021 119.426 384.939 130.37 ] /Subtype /Link /Type /Annot >>
+endobj
+92 0 obj
+<< /A << /D (cite.han2025mtgr) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 388.207 119.426 428.655 130.37 ] /Subtype /Link /Type /Annot >>
+endobj
+93 0 obj
+<< /A << /D (cite.han2025mtgr) /S /GoTo >> /Border [ 0 0 0 ] /C [ 0 1 0 ] /H /I /Rect [ 431.643 119.426 453.561 130.37 ] /Subtype /Link /Type /Annot >>
+endobj
+94 0 obj
+<< /A << /S /URI /URI (https://arxiv.org/abs/2602.01865v2) >> /BS << /W 0 >> /NM (fitz-L0) /Rect [ 12 230.46002 32 561.54 ] /Subtype /Link >>
+endobj
+95 0 obj
+<< /Length 10 /Filter /FlateDecode >>
+stream
+x+ |
+endstream
+endobj
+96 0 obj
+<< /Filter /FlateDecode /Length 4797 >>
+stream
+xڭ[s8B_vqL֙پ۽%l$R#_EҬcUAh4nOחlվlh_Vpv LXD,Y}l o>B}EYՋ~0z'e6QfT$"Lr9xUE?U6szʯ}YmڎlleqvjMݗ+Egj3D"Ll!SkMT|V+5a`-y_ZdYz6{r'*Ad2%JH2w֦ޕ7F*@pd δH`&e(gwo}Ϭ-K)ۭ#|iwN~A`6(q/)GEUd"9Nc0xʙP!kkLѝ+B
+t' 8-u=Q } OY%4SYnjiGRI>/Lh~|ZNv(VES|SwݦO`iMDDCves[7[S(0Ւ\@G
+~$K|mWV%p_ی;ۭdm5iۘXPiC%pѲ-;g1_p 2U?8\'u:0EQ̿:/+pћUy2'+w hy;rzW%uwL\%7d:_[*c}nBiZ͂WK:^A遷+EW/JwQ8qQ
+鎐n1;LF1@7mEM'#Ӂ'Њe $$G/"V0%0s-[3#
u1}p0LgQ@6az[}GhIc P:NW:>͙]Dږ8m(YդŘ{0wT]
.|?y:QZ2[[ t.I?:/
++<}[6Vr
+o$
+[6Tl%+cexdZ8