Skip to content

About

Competition-grade local MRR validation toolkit for temporal graph link prediction. Works with Jittor framework. 第六届计图人工智能挑战赛赛道一本地MRR验证工具包,与计图框架兼容。

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

OpenJittor — 竞赛级本地 MRR 验证工具包

License: MIT Python 3.8+ Jittor Compatible

🏆 第六届计图 AI 挑战赛 · 赛道一 · 参赛团队出品

让每一次本地实验都有意义。 精确离线估算线上 MRR,不打盲猜的提交。

本工具与 Jittor 框架完全兼容。三个核心脚本都只依赖 NumPy/Pandas—— 你的 Jittor 模型只需输出一个 [Q, 100] 的 numpy 数组,就能无缝接入评分。


目录


第一部分:保姆级教程

目标读者:从未接触过时序图竞赛的初学者。 读完这一部分,你应该能独立完成从数据到 MRR 评分的完整流程。

1.1 先讲一个故事

假设你参加了一个 AI 竞赛——就像 Kaggle 或计图挑战赛。任务是时序图链接预测:

给定一个用户 ID (src) 和一个时间点 (time),评委给你 100 个候选商品 ID (dst), 你需要从中找出真正会发生交互的那一个。

你训练了一个深度学习模型(CRAFT、GCN、Transformer,随你喜欢),每天提交到平台。平台给你返回一个数字——你的 MRR 总分。这个数字在 0.93 到 1.52 之间波动。

问题来了:你花了两天改进模型,提交上去,分数反而降了 0.003。你不知道是改进方向不对,还是随机波动。你一天只有 10 次提交机会。你的 deadline 还有三周。

你需要一个办法,在本地就能知道模型的真实水平。

这就是 OpenJittor 做的事情。


1.2 你需要知道的核心概念

MRR(Mean Reciprocal Rank,平均倒数排名)

不要被名字吓到。它就是一个简单的"猜对了加多少分":

对每一个查询(总共 Q 个):
  ├─ 你的模型给 100 个候选 dst 打分(分数越高 = 越可能是真值)
  ├─ 按分数从高到低排序
  ├─ 看看真值 dst 排在第几名
  └─ 这个查询的得分 = 1 / 排名

MRR = 所有查询得分的平均值

用表格理解:

查询编号 候选数 真值 dst 排名 该查询得分 (1/rank)
1 100 第 1 名 1/1 = 1.000
2 100 第 3 名 1/3 = 0.333
3 100 第 10 名 1/10 = 0.100
4 100 第 50 名 1/50 = 0.020
5 100 第 100 名 1/100 = 0.010
平均 MRR = 0.293

几个关键数字帮你建立直觉:

场景 MRR 含义
完美模型 1.000 所有查询真值排第 1
强模型 0.45-0.60 真值通常在前 3
中等模型 0.20-0.40 真值通常在前 10
纯随机猜测 ≈0.052 100 候选,期望排名≈50.5
比随机还差 <0.052 模型学到了反方向

竞赛总分 = D1 + D2

大多数时序图竞赛会给你两个数据集:

  • D1 (dataset1):一般图,重复边多(~73%),偏"记忆"
  • D2 (dataset2):二分图,冷启动多(~54%),偏"推理"

总分 = D1_MRR + D2_MRR。本工具主要面向 D2 设计,D1 同理可类推。

"热"dst 和"冷"dst

  • 热 dst:在训练集中出现过的商品 → 模型见过它,有 embedding
  • 冷 dst:从未在训练集中出现的商品 → 模型从未见过,embedding 是随机初始化的

冷 dst 是时序图竞赛的核心难度。D2 中约 54% 的候选 dst 是冷的——你的模型对它们一无所知。


1.3 最直觉的做法——以及它为什么是错的

你拿到了 train.csv:

src,dst,time
1,100,1620000000
2,200,1620000001
1,300,1620000002
3,100,1620000003
...

你想:"取最后 15% 的数据做验证,随机采样 99 个没见过的 dst 做负样本。"

于是你写了这样的代码:

# ❌ 错误做法 —— 会让你被骗得很惨

# 1. 简单切分
n_val = int(len(df) * 0.15)
train = df.iloc[:-n_val]
val = df.iloc[-n_val:]

# 2. 从训练集 dst 中随机采负样本
unique_dst = train['dst'].unique()  # ≈ 50,640 个
for each val query:
    negatives = np.random.choice(unique_dst, 99)

# 3. 评估
mrr = compute_mrr(predictions, ground_truth)
print(f"本地 MRR = {mrr:.4f}")  # → 0.99!太好了!

提交后:

线上 MRR = 0.50  ← 怎么回事?!

问题出在三个地方:

陷阱 A:候选池不匹配

你的本地验证中,99 个负样本从 unique(train_dst) 抽取(约 50K 个热 dst)。 但线上测试的候选池有 110K 个唯一 dst,其中 54% 是冷 dst(训练集从未出现)。

冷 dst 在你的本地验证中完全不存在 → 你的模型不需要区分它们 → 本地 MRR 虚高。

PopTrack(一个只查"历史频次表"的零参数方法)在这个错误验证集上 MRR = 0.99。 提交到线上:0.50。

陷阱 B:时间泄漏

假设训练数据从 2020 年到 2024 年。你取最后 15% 做验证。但如果你在"热度"特征中使用了 2024 年的数据——你就在用验证期的信息"预测"验证期。

我们第一版 markov_val.py 犯了这个错误:

  • 泄漏版 MRR = 0.6254 ✨
  • 修复后 MRR = 0.4467 💀

凭空蒸发了 0.18。

陷阱 C:负采样太简单

纯随机负采样让模型可以靠"猜热门商品"拿分。线上的负样本是精心构造的,包含大量冷 dst——难度远高于你的本地验证。

💡 核心教训:如果你的零参数 baseline(如 PopTrack)在验证集上接近满分,你的验证集有问题。


1.4 Val-Hot 原理:一张图看懂

我们设计的 val-hot 基准精确解决了上述三个问题。核心思想非常简单:用 test.csv 中已有的信息来模拟评测机制。

时间轴
  │········ train' (信息截止前) ········│·· val (验证期) ··│
  ↑                                     ↑                  ↑
  训练集起始                          cut (信息截止点)   训练集结束
                                       ↑
                                      测试集时间跨度 = val 时间跨度

三个关键操作:

# 操作 为什么这样做
① 候选池 = test.csv 的所有候选列的唯一 dst 精确复现评测分布,包括 54% 冷 dst
② val 期长度 = test.csv 的时间跨度 模拟评测的时间尺度
③ 99 负样本 = 均匀随机采样 (有碰撞检测) 与评测的负采样机制保持一致

数据流图:

train.csv ──→ [时间切分] ──→ train' (前 85%)  ──→ 检查 cold-at-train' ──→ val_coldmask.npy
    │                          val   (后 348 天) ──→ 构造候选 ──→ val_hot.csv
    │                                                     ↑
test.csv ──→ [提取候选池] ──→ 110,368 唯一 dst ──────────┘

一个具体的查询长什么样:

val_hot.csv 第 42 行:
  src=1523, time=1719000000, dst=8842(真值), n0=5001, n1=99999, ..., n98=12345
                                                    ↑
                                    这 99 个是从 test.csv 候选池
                                    均匀采样来的负样本

重要边界:val-hot 只能衡量热 dst 排序。 所有 val 真值 dst 都在 train' 中出现过(是热的)。冷 dst 的表现需要线上提交验证——这不是工具 bug,而是"不能在本地获取测试真值"的数学限制。


1.5 第一步:构造验证基准

准备数据

你需要竞赛方提供的两个文件:

data/dataset2/
├── train.csv    ← 训练数据: src, dst, time
└── test.csv     ← 测试数据: src, time, dst, n0, n1, ..., n98

test.csv 有 102 列:

  • src, time, dst:查询的源节点、时间、真值 dst(这一列是你最终要预测的)
  • n0 到 n98:99 个候选 dst(你需要对它们排序)

运行 build_val.py

python build_val.py \
    --train data/dataset2/train.csv \
    --test  data/dataset2/test.csv \
    --out   data/dataset2/

你会看到的输出

train' (信息截止前): 2,038,500 条边
val   (验证期)    :   222,783 条边
测试集时间跨度    : 348 天
cold-at-train' 真值比例: 0.300
测试候选池大小: 110,368 个唯一 dst

✓ 验证基准已保存:
  data/dataset2/val_hot.csv       (222,783 查询 × 101 列)
  data/dataset2/val_coldmask.npy  (cold-at-train' 掩码)
  耗时: 12.3s

理解输出

输出文件 格式 内容
val_hot.csv CSV, Q行×101列 src, time, dst(真值), n0..n98
val_coldmask.npy bool数组 [Q] True = 该查询的真值 dst 在 train' 中从未出现
  • cold-at-train' 比例 ≈ 30% 是正常的。这些查询的真值 dst 虽然在 train' 中没出现,但在 val 期中出现了——它们模拟的是"训练期结束后新出现的交互模式"。
  • 如果比例为 0%,说明测试集时间跨度可能算错了。如果比例 > 50%,说明你的训练数据覆盖度很低。
  • 构造耗时约 10-15 秒(22 万查询),内存占用约 500MB。

1.6 第二步:用你的模型生成分数

原则

你的模型需要读取 val_hot.csv,对每一行的 100 个候选 dst 打分,输出一个 [Q, 100] 的分数矩阵。

唯一的要求:列 0 必须是真值 dst 的分数。

Jittor 框架示例

import jittor as jt
import numpy as np
import pandas as pd

# 加载验证数据
vd = pd.read_csv('data/dataset2/val_hot.csv')
candidates = vd.iloc[:, 2:].values.astype(np.int64)  # [Q, 100], 列0=真值

# 加载你的模型
model = YourJittorModel()
model.load_state_dict(jt.load('your_checkpoint.pt'))
model.eval()

# 推理
scores = np.zeros((len(vd), 100), dtype=np.float32)
batch_size = 500

for st in range(0, len(vd), batch_size):
    en = min(st + batch_size, len(vd))
    batch_cand = candidates[st:en]
    batch_src = vd.iloc[st:en, 0].values.astype(np.int64)

    # ★ Jittor 前向
    with jt.no_grad():
        output = model(
            jt.array(batch_src),
            jt.array(batch_cand)
        )
        # ★ 关键: Jittor.Var → NumPy
        scores[st:en] = output.numpy()

# 保存
np.save('data/dataset2/my_model_scores.npy', scores)

Jittor → NumPy 的桥梁只有一行:output.numpy()。 除此之外没有框架依赖。

PyTorch 也可以

# 完全一样的工作流,只是把 jt.array 换成 torch.from_numpy
import torch
# ...
with torch.no_grad():
    output = model(torch.from_numpy(batch_src), torch.from_numpy(batch_cand))
    scores[st:en] = output.cpu().numpy()

验证分数文件

sc = np.load('data/dataset2/my_model_scores.npy')
print(sc.shape)    # 应该是 (222783, 100)
print(sc.dtype)    # float32 或 float64
print(sc[0, 0])    # 第一个查询的真值分数
print(sc[0, 1:5])  # 前 4 个负样本的分数

1.7 第三步:评分与解读

单模型评分

python score_val.py \
    --val data/dataset2/ \
    --scores data/dataset2/my_model_scores.npy

你会看到的输出

验证基准: data/dataset2/val_hot.csv
查询数: 222,783
Cold-at-train' 真值: 30.0% (66,835/222,783)
已加载: my_model_scores  shape=(222783, 100)

===============================================================
  单模型评分
===============================================================

  [my_model_scores]
  ──────────────────────────────────────────────
  查询数: 222,783  (hot-true: 155,948, cold-true: 66,835)
  ──────────────────────────────────────────────
  MRR (all)       = 0.4598   ← 线上 D2 热分量估算器
  MRR (hot-true)  = 0.6123   (仅热真值查询)
  MRR (cold-true) = 0.1043   (冷真值查询, 随机基线≈0.052)
  ──────────────────────────────────────────────
  真值排名分位数:
    P10=1  P25=1  P50=3  P75=12  P90=42
  Top-1 Acc:  0.2750
  Top-3 Acc:  0.5550
  Top-10 Acc: 0.8660

每个指标怎么读

指标 含义 判断标准
MRR(all) 全部 val 查询的 MRR 最重要的数字。 比线上 D2 热分量略高
MRR(hot-true) 仅热真值查询 模型的热排序纯能力。> 0.55 算好
MRR(cold-true) 仅冷真值查询 ≈ 0.05 正常(随机),> 0.10 说明模型有冷启动能力
P50 Rank 真值排名中位数 < 3 优秀,3-10 一般,> 10 差
Top-1 Acc 真值排第 1 的比例 与 MRR 强相关
Top-10 Acc 真值在前 10 的比例 宽松指标。> 0.85 说明"至少不会完全跑偏"

多模型融合评分

如果你有多个模型的分数,想知道平均融合能不能涨分:

python score_val.py \
    --val data/dataset2/ \
    --scores model_a.npy model_b.npy model_c.npy \
    --blend

输出会多一段融合评分,告诉你简单平均后 MRR 是多少。

大规模评分(一个目录下的所有模型)

python score_val.py \
    --val data/dataset2/ \
    --score-dir data/dataset2/scores/

1.8 如何把分数变化翻译成线上预期

Val-hot 的 MRR 变化能可靠预测线上 D2 的方向和大致幅度。我们用一个经验公式:

Δ线上D2 ≈ 0.70 × ΔMRR(all)

具体例子(来自我们的实际提交记录):

改动 ΔMRR(all) val 预测 Δ线上 实际 Δ线上
+cand_freq 特征 +0.0187 +0.013 +0.0184 ✓
+pop 热度特征 +0.0384 +0.027 +0.0467 ✓ (低估)
+GNN 预训练 +0.0112 +0.008 +0.0071 ✓
SM 模型入融合 +0.0045 +0.003 +0.0032 ✓

保守估计:val ΔMRR > 0.003 才值得提交。 低于这个阈值可能是噪声。


1.9 常见问题与排错

Q1: 我没有 test.csv,只有 train.csv,怎么办?

如果竞赛平台暂时没有公开 test.csv:

  • 方案 A(推荐):向平台申请测试集文件。大部分竞赛都会在后期公开。
  • 方案 B(近似):用 np.unique(train['dst']) 作为候选池。但这会低估冷 dst 比例,导致 MRR 虚高。一定要在 README 中注明这个近似,并期待 test.csv 到手后重新构造。
  • 方案 C(模拟):假设冷 dst 比例为 50%,从全节点空间随机采样 50% 虚构冷节点作为候选池补充。但这只是模拟——精度取决于你的假设。

Q2: 我的模型输出分数怎么做成 [Q, 100] 格式?

确保列 0 始终是真值 dst。如果你用的是 val_hot.csv,列顺序就是:

col 0 = src
col 1 = time
col 2 = dst (真值)
col 3-101 = n0...n98 (99 个负样本)

所以 vd.iloc[:, 2:].values 就是你要的 [Q, 100],且第 0 列 = 真值 dst。

Q3: MRR(cold-true) 一直是 0.05 左右,是不是我的模型有问题?

不一定。 如果 val_hot 中的冷真值查询的真值 dst 在 train' 中完全没出现过,任何基于训练 embedding 的模型都对它无法区分。0.05 ≈ 随机。这是竞赛设计的固有限制。

如果 MRR(cold-true) > 0.08,你的模型可能有一些归纳能力(比如通过 src 的邻居模式推断新 dst)。这是好事。

Q4: 我的 MRR(all) 和线上差很多,为什么?

可能原因:

  1. 你的模型对冷 dst 的排序比随机差(线上受冷分量拖累更严重)
  2. 训练集/测试集的构造方式与 val-hot 的假设不同(比如组织者用了非均匀负采样)
  3. 时间跨度算错了(检查 test.csv 的 time 范围)
  4. 候选池不完整(检查 test.csv 的候选列是否全部读入)

调试方法:做一次"探针提交"——把冷候选分数全部置零,线上 D2 ≈ f_hot × MRR_hot。用这个反推 f_hot 和 cold MRR。

Q5: build_val.py 报 "test.csv 缺少必要列" 怎么办?

检查你的 test.csv 列名。标准格式是 src, time, dst, n0, n1, ..., n98。如果列名不同(比如 candidate_0 而不是 n0),修改 build_val.py 中 cand_cols 的提取逻辑:

# 如果你不想改源码,直接改列名即可
td.columns = ['src', 'time', 'dst'] + [f'n{i}' for i in range(99)]

Q6: 如何把 val-hot 推广到 D1?

修改时间跨度。D1 的 test 期通常和 D2 不同。查看 test.csv:

span = td['time'].max() - td['time'].min()

然后在 build_val.py 中使用这个 span 做切分。

Q7: 为什么不用 sklearn 的 train_test_split?

因为时序图数据有时间依赖——未来的边依赖于过去的边。随机切分(shuffle split)会让模型在训练时"看到未来"——这是最严重的数据泄漏。必须用时间切分。


第二部分:来自战场的经验

从 0.9357 到 1.24434,54 个 D2 实验,44 次提交,无数死胡同。 这些是我们希望前人告诉过我们的话。

2.1 三代验证体系的演进

第一代:天真随机负采样(存活 3 天后被推翻)

# 当时我们觉得这很合理
negatives = np.random.choice(np.unique(train['dst']), 99)  # ❌

推翻它的瞬间:PopTrack(零参数热度查询)在这个验证集上 MRR = 0.99,比我们训练了 40 个 epoch 的深度学习模型还高。提交线上:PopTrack = 0.50。差了一倍。

💡 如果你的零参数 baseline 在验证集上接近满分,你的验证集有 bug。

第二代:Hard Negative + 校准(存活 1 周)

用 src 历史邻居 + 热门加权采样构造"更难"的负样本,然后线性回归到线上分数。两个致命缺陷:(1) hard negative 分布 ≠ 评测真实分布;(2) 校准需要消耗宝贵的提交次数。

第三代:Val-Hot 基准(至今,已验证可靠)

核心洞察来自一次凌晨 2 点的数据探索:test.csv 里已经包含了评测的所有信息。 候选池、时间跨度、查询格式——全在 test.csv 里。我们只需要"模仿"它。


2.2 我们踩过的十个大坑

坑 1:训练负样本池 ≠ 评测候选池

99-neg InfoNCE 训练时用了 unique(train_dst) 做负样本池(~50K 全是热 dst)。val_hot MRR 崩到 0.1758。原因:模型从未在训练中见过冷 dst(占评测 54%),推理时冷负样本分数完全失控。

解法:训练负样本池 = test.csv 候选池(含冷 dst)。一行代码的改动,MRR 恢复正常。

坑 2:Val AP 0.96 以上不再反映 MRR

Massive 模型(384 维 hidden)Val AP = 0.9622(最高),但线上 MRR 比 Val AP = 0.9594 的小模型低了 0.0123。AP 和 MRR 在顶端脱钩——更大的模型在"容易样本"上更自信,但在"难样本"上反而退步。

坑 3:Jittor backward 图被消费两次

两个优化器各调用一次 optimizer.step(loss) → Jittor 的 backward 图在第一次就被消费了,第二个优化器拿到空梯度。修了一周才发现。教训:永远只用一个优化器。

坑 4:CRAFT 源码的 forward view bug

# 源码第 308-309 行
output = self.output_layer(output.view(-1, output.shape[-1]))
output = output.view(output.shape[0], output.shape[1], -1)  # ← 当 test_dst≠None 时维度错误
# 返回 [B*C, 1, 1] 而非 [B, C, 1]

3 天定位,1 行 monkey-patch 修复。开源代码也可能有 bug——怀疑一切。

坑 5:冷 DST 方向——7 种方法,8 次提交,全部失败

知识蒸馏、归纳嵌入、分数传播、分位数校准、伪正样本、冷负采样、冷正则化——没有一种方法能把冷 DST 排序提升到随机以上。 第三次独立验证(leak_detection.py)后永久关闭此方向。冷 DST 的候选是均匀噪声,信息论上不可区分。

这 8 次提交烧掉了我们几乎一整天的配额。

坑 6:D1 和 D2 是完全不同的任务

  • D1 (72.6% 重复边) = 记忆型。EdgeBank(查表)单模型 0.737。
  • D2 (2.2% 重复边, 54% 冷启动) = 推理型。EdgeBank 只有 0.18。

一套策略打两个数据集 → 全部失败。必须分叉处理。

坑 7:TwoTower 点积在冷节点上的结构性失败

src-tower embedding · cold-dst embedding = src · random_vector ≈ 0。冷 dst 的 embedding 是随机初始化的,点积天然为零。换 InfoNCE loss、换 GNN 预训练——都没用。这是结构问题,不是训练问题。

坑 8:零参数热度 ≈ 最强的深度学习模型

90 天静态热度(一行 bincount + exp 衰减)val MRR = 0.4467。我们训练了 40+ 个实验的 Cold-CRAFT = 0.4598。差距 0.013——CRAFT 的交叉注意力看不到"全局热度"这个最简单的信号。这个发现后来贡献了 +0.047 的线上提升。

💡 在你调第 50 个超参之前,先检查一个零参数统计量能不能打败你的模型。

坑 9:中文路径崩溃 Jittor 编译器

Jittor 1.3.9 的 JIT 编译器在处理含中文字符的 Windows 路径(C:\Users\穆再排尔\...)时崩溃。解决方案:所有项目路径纯 ASCII。

坑 10:提交是唯一的地面真理

Dual Path CRAFT 在本地 val 上 MRR = 0.90,提交线上 D2 = 0.28。本地的"好"不一定是线上的好。当本地和线上冲突时,相信线上,然后修本地验证。


2.3 离线评估的六条黄金法则

# 法则 违反的典型代价
1 候选池必须与评测分布一致 虚高 0.30-0.50 MRR
2 时间切分零泄漏(特征不能使用 val 期信息) 虚高 0.15-0.20 MRR
3 负采样与评测同分布(不要自己发明 harder negatives) 排行不可信
4 冷热分解评估(MRR_hot 和 MRR_cold 分别看) 不知道改进来自哪里
5 固定随机种子(保证可复现对比) 无法确认 A > B
6 至少跑一个零参数 baseline(PopTrack / EdgeBank) 不知道验证集本身有没有 bug

2.4 突破时间线

0.9357  起点 — Jittor 官方 baseline (含 bug)
1.0848  CRAFT-bip 架构 (二部图类型感知 + struct gate, +0.15)
1.1098  12d 结构特征 (+0.025)
1.1456  3 路集成 (CRAFT+Massive+DyGFormer, +0.036)
1.1748  D1 EdgeBank — 天花板 (4 次独立命中同一分数)
           ↓ 发现冷 DST 信息论不可解 ↓
1.2187  pop 热度特征 (零训练!+0.047) ← 最大的单次突破
1.2370  cand_freq (候选频次编码测试期热度, +0.018)
1.2443  cfτ60 时间分辨版 (+0.007)
         → 目标 1.40,在路上

最重要的是:两个最大突破(pop +0.047, cand_freq +0.018)都是在"放下模型、去看数据"时发现的。 不是新架构,不是更长的训练,而是"我们到底在评测什么"的理解深入了一层。


第三部分:代码与理想

—— 一名光荣的共青团员写给开源社区的话

互联网从诞生那天起,就带着一种朴素而伟大的精神:信息的自由流动、知识的无偿共享、协作的去中心化。

Tim Berners-Lee 放弃 WWW 专利,让万维网成为全人类的公共财产。Richard Stallman 发起 GNU 计划,用 Copyleft 对抗 Copyright。Linus Torvalds 把 Linux 内核扔到邮件列表里说"只是个爱好,不会太大"——然后全世界的工程师一起把它建成了互联网的基石。GitHub 上数亿个仓库,每一行 import numpy as np 的背后,都是无数人无偿劳动、开放共享的成果。

这就是互联网精神:因为开放,所以强大;因为共享,所以繁荣。


而当我以一名共青团员的身份思考这一切时,我发现:互联网精神与共产主义精神在底层逻辑上是辩证统一的。

共产主义的核心追求是什么?各尽所能,按需分配——每个人贡献自己的能力和智慧,每个人都能获得自己需要的东西。开源社区难道不是这样吗?全世界的开发者"各尽所能"——有人修 bug,有人写文档,有人做 code review,有人搭建 CI/CD——而所有人"按需分配"——任何人都可以免费使用 Linux、Python、PyTorch、Jittor 这些基础设施。

共产主义反对知识垄断和阶级固化。 开源运动反对专利壁垒和技术霸权。当一家公司把关键算法锁进专利保险柜时,它在做的事情是:让知识成为少数人的私有财产,拒绝全人类共享进步的果实。而当一名工程师把自己的代码以 MIT License 开源时,ta 在说:"这个解法我找到了,你可以拿去用,不需要付费,不需要署名,只需要让这个世界变得更好一点。"

这不正是"全心全意为人民服务"的技术表达吗?


习近平总书记说过:"青年兴则国家兴,青年强则国家强。"作为新时代的共青团员,我认为技术开源就是青年一代践行共产主义理想的具体行动。我们不只是在写代码——我们在用 pull request 的方式建设一个更平等的世界。


有人可能会问:竞赛代码开源了,别人不就能超过你了吗?

是的。这正是我们希望的。

如果我们的 val-hot 基准设计、我们的经验教训、我们的验证工具能帮助下一个参赛团队少踩几个坑,更快地做出更好的模型——那是我们的荣幸。竞争不是零和博弈。一个人的胜利是胜利,但所有人一起进步的胜利是更大的胜利。

水涨船高。 当中国 AI 竞赛的基线被开源工具抬高时,所有人都会受益——包括我们自己。因为我们会看到更强的对手、更聪明的解法、更深刻的分析。而这些反过来又会推动我们向前。


互联网精神说:信息想要自由。 共产主义精神说:各尽所能,按需分配。 共青团员说:把这两件事都做了。

这就是我们开源 OpenJittor 的全部理由。


⚡ 让每一次提交都有意义。让每一个后来者少走弯路。 ⚡

穆再排尔·穆合塔尔
机电学院 机械工程
光荣的共青团员
2026 年 7 月


文件结构

openjittor/
├── README.md              ← 你正在读的文件
├── build_val.py           ← 构造 val-hot 验证基准
├── score_val.py           ← MRR 评分与融合分析
├── example_workflow.py    ← 完整工作流示例 (Jittor 兼容)
└── requirements.txt       ← Python 依赖 (numpy, pandas, scipy)

依赖: 只需 NumPy + Pandas + SciPy。Jittor 框架仅在你的模型推理时需要——评分工具本身不依赖任何深度学习框架。

引用

@misc{openjittor2026,
  title     = {OpenJittor: Competition-Grade Local MRR Validation Toolkit},
  author    = {Muzaipaer·Muhetaer and Jittor Track-1 Team},
  year      = {2026},
  url       = {https://github.com/[username]/openjittor},
  note      = {第六届计图AI挑战赛赛道一参赛团队出品}
}

致谢

  • 清华大学 Jittor 团队 —— 维护了优秀的国产深度学习框架
  • CRAFT 论文作者 —— 提供了强大的时序图学习基线
  • 所有在 arXiv 上公开发表论文的研究者 —— 你们的成果是我们站立的肩膀
  • 每一个给开源项目提 issue、修 bug、写文档的陌生人 —— 你们让这个世界变得更好

Licensed under MIT. 拿去用吧。

About

Competition-grade local MRR validation toolkit for temporal graph link prediction. Works with Jittor framework. 第六届计图人工智能挑战赛赛道一本地MRR验证工具包,与计图框架兼容。

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages