如何泛化到新项目、新语言、新CWE与未来时间?

第76题:如何泛化到新项目、新语言、新CWE与未来时间?

在这里插入图片描述

1. 核心回答

我会把漏洞检测模型的泛化能力拆成四个独立维度:

  1. Cross-Project Generalization:新项目;
  2. Cross-Language Generalization:新编程语言;
  3. Unseen-CWE Generalization:训练阶段未见的漏洞类型;
  4. Temporal Generalization:未来时间出现的新代码和新漏洞。

这四种泛化都属于 Distribution Shift,但产生分布变化的原因不同,因此需要分别设计训练策略和测试集。

整体原则是:

减少模型对项目、语言、CWE标签和时间特征的表面记忆

尽量学习漏洞形成机制

针对每一个外推维度建立独立 OOD 测试集

报告相对于同分布测试的性能衰减

如果只在随机划分的测试集上获得较高 F1,仍然不能证明模型能够泛化到真实的新项目和未来漏洞。


2. 为什么漏洞检测容易出现泛化问题

假设训练数据为:

Dtrain∼Ptrain(X,Y) D_{\text{train}}\sim P_{\text{train}}(X,Y) DtrainPtrain(X,Y)

真实部署环境为:

Ddeploy∼Pdeploy(X,Y) D_{\text{deploy}}\sim P_{\text{deploy}}(X,Y) DdeployPdeploy(X,Y)

理想情况下两者接近。

实际漏洞检测中,经常存在:

Ptrain(X,Y)≠Pdeploy(X,Y) P_{\text{train}}(X,Y) \neq P_{\text{deploy}}(X,Y) Ptrain(X,Y)=Pdeploy(X,Y)

例如:

  • 不同项目具有不同 API、命名习惯和代码结构;
  • 不同语言具有不同语法、类型系统和运行时;
  • 不同 CWE 对应不同漏洞形成机制;
  • 软件框架、依赖和漏洞模式会随时间变化。

已有跨项目漏洞检测研究明确观察到:模型在同项目或随机划分数据上的性能,迁移到未见项目后可能明显下降。

因此我不会把一次随机 Train/Test Split 当作泛化能力证明。


3. 新项目:Cross-Project Generalization

3.1 泛化困难来自哪里

例如训练数据主要来自:

  • Linux;
  • Chromium;
  • FFmpeg;

随后部署到一个从未出现过的新项目。

模型可能已经学到:

  • 特定函数名;
  • 项目特有 API;
  • 文件路径;
  • 代码风格;
  • 项目模板;
  • 重复代码片段;

这些都可能与漏洞标签相关,却不属于真正的漏洞机制。

这种现象会形成 Project Shortcut。


3.2 训练阶段怎么提高跨项目泛化

第一,增加训练项目的多样性。

训练集尽量覆盖:

  • 不同项目;
  • 不同开发团队;
  • 不同代码规模;
  • 不同应用领域;
  • 不同框架。

第二,清理项目特有捷径。

例如检查模型是否依赖:

  • repository name;
  • file path;
  • commit message;
  • CVE编号;
  • issue description;
  • 漏洞修复后的标签信息。

第三,进行跨项目去重。

如果项目 A 和项目 B 中存在 fork、复制代码或高度相似函数,需要进行 clone / near-duplicate 检查。

否则:

Train Project A

和:

Test Project B

表面上属于两个项目,实际上仍然可能包含相同代码。

第四,优先学习代码语义和漏洞机制。

例如利用:

  • 控制流;
  • 数据流;
  • source-sink关系;
  • def-use关系;
  • 调用关系;

减少对项目名称和表面 token 的依赖。


4. 新项目应该怎样测试

最直接的方法是 Leave-One-Project-Out。

假设有:

P1,P2,…,Pn P_1,P_2,\ldots,P_n P1,P2,,Pn

个项目。

选择:

Pn P_n Pn

作为测试项目。

训练阶段只能使用:

P1,…,Pn−1 P_1,\ldots,P_{n-1} P1,,Pn1

即:

$$
D_{\text{train}}

\bigcup_{i=1}^{n-1}P_i
$$

Dtest=Pn D_{\text{test}}=P_n Dtest=Pn

然后轮换测试不同项目。

工程实现上也可以使用类似 GroupKFold 的非重叠 group 划分原则,让同一个项目只存在于训练侧或测试侧。

最终报告:

  • 每个项目的 Precision;
  • Recall;
  • F1;
  • MCC;
  • PR-AUC;
  • 平均值;
  • 方差。

这样可以发现模型是否只在某几个项目上有效。


5. 新语言:Cross-Language Generalization

5.1 新语言为什么更困难

假设:

$$
D_{\text{train}}

{\text{C/C++}}
$$

测试:

$$
D_{\text{test}}

{\text{Java}}
$$

此时变化的不只是 token。

还包括:

  • 语法结构;
  • 类型系统;
  • 内存模型;
  • API;
  • 异常处理;
  • 对象模型;
  • 常见漏洞模式。

已有跨语言漏洞检测研究也把语言之间的 semantic gap 和 distribution discrepancy 作为主要问题。


5.2 怎么提高跨语言泛化

第一,可以使用覆盖多种编程语言的代码预训练模型作为基础表示。

第二,可以增加语言无关的结构信息,例如:

  • AST;
  • Control Flow Graph;
  • Data Flow Graph;
  • Code Property Graph;
  • source-sink关系。

例如:

C/C++:

strcpy(dst, src)

Java:

Runtime.exec(input)

具体 API 完全不同,但漏洞判断都可以进一步抽象为:

untrusted source

insufficient validation

sensitive sink

模型如果能够学习这一层机制,跨语言迁移通常更有依据。

第三,可以使用多语言联合训练。

例如:

$$
D_{\text{train}}

D_C
\cup
D_{C++}
\cup
D_{Java}
\cup
D_{Python}
$$

通过训练数据多样性降低模型绑定某一种语言的风险。

如果目标语言没有标签,还可以研究 Domain Adaptation 或 Zero-Shot Transfer。


6. 新语言应该怎样测试

可以使用 Leave-One-Language-Out。

例如训练:

C + C++ + Java

测试:

Python

然后轮换:

C + C++ + Python → Java

这种实验能够直接回答:

模型是否能够识别训练阶段完全没有见过的编程语言中的漏洞?

需要同时报告:

  1. 同语言性能;
  2. 跨语言性能;
  3. 两者之间的性能下降。

例如:

$$
\Delta_{\text{language}}

F1_{\text{ID}}

F1_{\text{Cross-Language}}
$$

Δlanguage\Delta_{\text{language}}Δlanguage 越大,说明模型越依赖语言特有模式。


7. 新CWE:Unseen-CWE Generalization

这里必须先区分任务。

7.1 如果任务是漏洞二分类

任务为:

f(x)→{Vulnerable,Safe} f(x)\rightarrow \{\text{Vulnerable},\text{Safe}\} f(x){Vulnerable,Safe}

这时可以直接做 Leave-One-CWE-Out。

例如训练集中包含:

  • CWE-79;
  • CWE-89;
  • CWE-125;
  • CWE-787;

测试阶段完整保留:

  • CWE-416 Use After Free。

训练阶段完全不能出现 CWE-416 的漏洞样本。

然后测试:

模型能否把 CWE-416 样本识别为 Vulnerable?

这是真正意义上的 unseen-CWE vulnerability detection。


8. 新CWE为什么可能泛化

CWE 本身具有层级结构。

MITRE CWE 中,不同 Weakness 的抽象程度不同。

例如较高层的 Class 通常更加抽象,可以跨语言或技术描述弱点;更低层的 Variant 更接近特定语言或技术。

因此模型可以尝试学习较高层的漏洞机制,例如:

输入不可信

缺少边界检查

危险内存访问

而非只学习:

CWE-787

这个标签本身。

训练时可以进一步利用:

  • CWE hierarchy;
  • CWE description;
  • source-sink模式;
  • control/data-flow;
  • vulnerability mechanism;

建立更抽象的漏洞表示。

这样能够提高向未见具体 CWE 迁移的可能性。


9. 如果任务是预测具体CWE

任务变成:

f(x)→CWEi f(x)\rightarrow CWE_i f(x)CWEi

情况会发生变化。

如果:

CWEnew∉Ytrain CWE_{new}\notin Y_{\text{train}} CWEnew/Ytrain

普通 closed-set classifier 的类别空间中根本没有:

CWEnew CWE_{new} CWEnew

因此它无法正常预测这个新类别。

此时需要改成 Open-Set 或 Zero-Shot CWE Classification,例如:

  1. 输出 Unknown
  2. 先预测 CWE 的上层类别;
  3. 将代码表示与 CWE textual description 进行语义匹配;
  4. 使用层级分类;
  5. 使用检索或生成方式给出候选 CWE。

所以“未见 CWE 泛化”需要先明确:

检测未知类型的漏洞

和:

准确给出未知 CWE 编号

属于两个不同任务。


10. 未来时间:Temporal Generalization

这是最接近真实部署的一种测试。

假设时间点为:

t t t

模型只能使用:

T≤t T\leq t Tt

时已经存在并已经公开的信息。

然后预测:

T>t T>t T>t

的数据。

例如:

2022及以前 → Train

2023 → Validation

2024 → Test

训练阶段不能使用 2024 年之后才公开的:

  • CVE 标签;
  • 修复 commit;
  • issue;
  • 漏洞说明;
  • 补丁信息。

11. 为什么不能随机切分未来漏洞数据

漏洞标签具有明显的时间属性。

一个函数在 2022 年真实存在漏洞,但这个漏洞可能到 2024 年才被发现。

如果今天构造完整数据集,然后随机切分:

Train / Test

模型训练阶段可能获得 2022 年当时实际上尚不可知的信息。

这属于 Temporal Leakage。

已有软件漏洞预测研究专门指出,真实训练设置应只使用训练时间点当时已经发现的漏洞标签。

因此未来泛化测试应该采用 Forward-Chaining:

D≤t→Dt+1 D_{\leq t} \rightarrow D_{t+1} DtDt+1

然后:

D≤t+1→Dt+2 D_{\leq t+1} \rightarrow D_{t+2} Dt+1Dt+2

逐时间窗口向前评估。


12. 未来时间怎么保持泛化能力

时间分布会持续变化,因此模型部署后需要持续监控。

可以记录:

Metric(t) Metric(t) Metric(t)

随时间的变化。

如果发现:

Metric(t+1)<Metric(t) Metric(t+1)<Metric(t) Metric(t+1)<Metric(t)

并持续下降,需要分析是否出现:

  • 新框架;
  • 新语言特性;
  • 新 CWE;
  • 新 API;
  • 新代码风格;
  • 新漏洞利用模式。

此时可以采用:

  • 定期重新训练;
  • Continual Learning;
  • Replay;
  • 参数高效微调;
  • 新样本增量学习。

但更新模型后仍然必须保留历史测试集,检查新模型是否出现 catastrophic forgetting。


13. 四轴泛化测试矩阵

最终我会建立如下测试矩阵:

泛化维度TrainTest主要问题
In-Distribution已见项目/语言/CWE/时间同分布样本基础能力
Cross-ProjectProjects A/B/C未见 Project D项目迁移
Cross-LanguageC/C++/Java未见 Python语言迁移
Unseen-CWECWE A/B/C未见 CWE D漏洞机制迁移
TemporalT≤tT\leq tTtT>tT>tT>t未来泛化

这样才能把“泛化能力”从一个模糊概念转化成四个可测实验。


14. 怎么量化泛化能力下降

首先计算同分布性能:

MID M_{\text{ID}} MID

然后分别计算:

Mproject M_{\text{project}} Mproject

Mlanguage M_{\text{language}} Mlanguage

MCWE M_{\text{CWE}} MCWE

Mtime M_{\text{time}} Mtime

定义绝对性能下降:

$$
\Delta_i

M_{\text{ID}}

M_i
$$

还可以定义性能保持率:

$$
R_i

\frac{M_i}{M_{\text{ID}}}
$$

例如:

F1ID=0.85 F1_{\text{ID}}=0.85 F1ID=0.85

F1Cross-Project=0.68 F1_{\text{Cross-Project}}=0.68 F1Cross-Project=0.68

则:

$$
\Delta_{\text{project}}

0.17
$$

性能保持率:

$$
R_{\text{project}}

\frac{0.68}{0.85}

0.80
$$

也就是保留了约 80% 的同分布性能。

这样比单独说:

“Cross-Project F1 是 0.68”

更容易判断泛化损失到底有多严重。


15. 还应该报告哪些结果

不能只报告总体 F1。

至少还应该报告:

  • Precision;
  • Recall;
  • PR-AUC;
  • MCC;
  • 每个项目结果;
  • 每种语言结果;
  • 每类 CWE 结果;
  • 每个时间窗口结果;
  • 多 seed 均值和方差;
  • 置信区间;
  • 错误案例。

对于安全检测,还应该特别关注:

Recallvulnerability Recall_{\text{vulnerability}} Recallvulnerability

因为模型在 OOD 条件下可能出现明显漏报。

如果输出概率,还应该检查 Calibration。

例如模型预测:

P(vulnerable)=0.9 P(\text{vulnerable})=0.9 P(vulnerable)=0.9

在新项目上是否仍然接近真实的 90% 命中概率。

分布发生变化后,模型可能仍然非常自信,但预测已经错误。


16. 泛化失败后如何处理

真正部署时,需要承认模型存在适用范围。

可以根据:

  • OOD score;
  • confidence;
  • ensemble disagreement;
  • retrieval evidence;
  • 静态分析结果;

建立低置信检测机制。

例如:

Confidence<τ Confidence < \tau Confidence<τ

时:

模型自动判定

转换为:

模型给候选结果 → 静态分析/工具验证 → 人工复核

对于明显超出训练分布的新语言、新框架或新 CWE,也可以直接进入人工升级流程。

这样可以避免模型在未知领域中产生高置信错误。


17. 面试时可以压缩成下面这段

我会把泛化能力拆成四个轴:新项目、新语言、新 CWE 和未来时间,并分别建立 OOD 测试。

新项目采用 Leave-One-Project-Out,保证测试项目从未进入训练;新语言采用 Leave-One-Language-Out,重点验证模型是否学习了跨语言的代码语义和数据流机制;新 CWE 如果是漏洞二分类,就做 Leave-One-CWE-Out,如果要求预测具体的未见 CWE,则需要开放集、层级分类或基于 CWE 描述的零样本方法;未来时间采用严格的 temporal split,只允许模型使用预测时间点以前已经公开的数据和漏洞标签。

训练阶段我会通过多项目、多语言和多 CWE 数据提高分布覆盖,并尽量使用控制流、数据流、source-sink 等更接近漏洞机制的特征,减少项目名、函数名、CVE 标签等表面捷径。

评测时除了报告 Precision、Recall、F1、PR-AUC 和 MCC,我还会计算相对于同分布测试的性能衰减:

Δi=MID−Mi \Delta_i=M_{\text{ID}}-M_i Δi=MIDMi

同时分析不同项目、语言、CWE 和时间窗口中的失败模式与置信度校准。

只有模型在这些独立 OOD 测试中仍然保持可接受的 Recall 和 Precision,才能说明它具有实际泛化能力。


18. 来源

  1. MITRE, Common Weakness Enumeration (CWE):CWE 是软件和硬件弱点的分类体系,并具有 Class、Base、Variant 等不同抽象层级。
  2. scikit-learn, GroupKFold:提供 group 不重叠的训练/测试划分方式,可用于实现项目级、语言级等分组隔离实验。
  3. scikit-learn, TimeSeriesSplit:强调时间有序数据应保持训练在过去、测试在未来,可用于构造 forward temporal evaluation。
  4. Safdar et al., Data and context matter: towards generalizing AI-based software vulnerability detection, International Journal of Information Security, 2026:讨论漏洞检测模型在 unseen codebases 上的明显泛化问题,并研究跨数据集性能下降。
  5. A zero-shot framework for cross-project vulnerability detection in source code, Empirical Software Engineering, 2025:指出不同项目之间的 coding style 和 feature distribution 差异会造成 cross-project generalization 问题。
  6. Multi-source cross-domain vulnerability detection based on code pre-trained model, Information and Software Technology, 2025:研究 cross-project 和 cross-language 漏洞检测中的 domain discrepancy。
  7. Jimenez et al.相关研究总结于 Learning from what we know: How to perform vulnerability prediction using noisy historical data, Empirical Software Engineering:指出真实时间条件下只能使用当时已经发现的漏洞标签,避免未来信息进入历史训练集。
  8. scikit-learn, Probability Calibration:可使用 reliability diagram、Brier score 等方法检查模型输出概率的校准情况。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白羊丨

开始面试题与解析

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值