Skip to content

Commit 069b5ca

Browse files
doc: Add technical report on CNVD severity trainer improvements
Concise summary of all changes, experiments, and results from the work prompted by the external analysis in issue #19. Related to #19 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5b2866c commit 069b5ca

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

docs/cnvd-severity-improvements.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# CNVD Severity Classifier: Improvements Report
2+
3+
Technical report on changes made to the CNVD severity trainer (`classify_severity_cnvd.py`) following the independent analysis in [VulnTrain#19](https://github.com/vulnerability-lookup/VulnTrain/issues/19).
4+
5+
## Context
6+
7+
An external review of the [CIRCL/vulnerability-severity-classification-chinese-macbert-base](https://huggingface.co/CIRCL/vulnerability-severity-classification-chinese-macbert-base) model identified several issues: data leakage in the train/test split, poor Low-class recall, keyword dependency, and a suboptimal published checkpoint. The reported headline accuracy of 77.83% was inflated by ~1.7pp due to leakage.
8+
9+
## Changes
10+
11+
### Data leakage fix
12+
13+
**Problem:** CNVD reuses boilerplate descriptions across different vulnerability IDs. The original `train_test_split` split by row index, allowing 1,587 identical descriptions (15.6% of the test set) to appear in both splits.
14+
15+
**Fix:** New `deduplicate_split()` function groups all entries by description text and assigns entire groups to one split. No description appears in both train and test.
16+
17+
**Impact:** The old model evaluated on the deduplicated test set scores 85.2% (inflated — it was trained on data overlapping this test set). A model retrained on the deduplicated split scores 76.8%, matching the independently measured unleaked accuracy of 76.6%.
18+
19+
### Class weighting experiments
20+
21+
**Problem:** The Low class (~9% of data) had only ~41% recall on unleaked data, with 60% of Low entries misclassified as Medium.
22+
23+
Four loss strategies were tested on the deduplicated split:
24+
25+
| Mode | Low recall | Medium recall | High recall | Overall acc |
26+
|------|-----------|---------------|-------------|-------------|
27+
| Uniform (none) | 0.4099 | 0.8165 | 0.7809 | 0.7677 |
28+
| Sqrt-dampened | 0.4902 | 0.7481 | 0.8056 | 0.7457 |
29+
| Balanced | 0.6084 | 0.7024 | 0.8099 | 0.7323 |
30+
| Focal (gamma=2) | 0.6328 | 0.6441 | 0.8349 | 0.7110 |
31+
32+
**Conclusion:** Every weighting strategy that improved Low recall caused disproportionate Medium recall loss. The Low/Medium vocabulary overlap in CNVD descriptions makes this a data-level limitation, not a loss-function problem. The trainer defaults to uniform loss.
33+
34+
A `--class-weights` flag (`none`, `sqrt`, `balanced`, `focal`) was added for future experimentation.
35+
36+
### Per-class metrics
37+
38+
`compute_metrics` now reports precision, recall, and F1 per class (Low/Medium/High) alongside overall accuracy and macro F1 at each evaluation epoch.
39+
40+
### Best model checkpoint selection
41+
42+
- `metric_for_best_model` set to `accuracy` (was defaulting to `eval_loss`)
43+
- `save_total_limit` increased from 2 to 3 to prevent the best checkpoint from being pruned
44+
45+
### CodeCarbon tracker scoping
46+
47+
The `@track_emissions` decorator wrapped the entire `train()` function, including `push_to_hub()`. The codecarbon background thread never stopped during the upload. Replaced with an explicit `EmissionsTracker` start/stop scoped to `trainer.train()` only. Also removed `push_to_hub=True` from `TrainingArguments` (it caused `trainer.train()` to upload internally before returning). The same fix was applied to `classify_severity.py`.
48+
49+
### Dynamic model card
50+
51+
The model card is now a template (`model_card_cnvd_severity.md`) populated with actual eval metrics from `trainer.evaluate()` after each training run. Documents per-class metrics, training configuration, and known limitations.
52+
53+
### Known limitations documented
54+
55+
- Low severity recall (~41%)
56+
- Keyword dependency (accuracy drops from ~89% to ~55% on atypical-severity entries)
57+
- Negation blindness
58+
- CVE overlap (81% of CNVD entries have a CVE equivalent)
59+
60+
## Commits
61+
62+
| Commit | Description |
63+
|--------|-------------|
64+
| `6352273` | Data leakage fix, class-weighted loss, per-class metrics, best model selection |
65+
| `b1679fb` | CNVD severity model comparison validator |
66+
| `1fdee05` | Sqrt-dampened class weights |
67+
| `a81f90d` | Scoped codecarbon tracker (CNVD trainer) |
68+
| `65d3d88` | Removed push_to_hub from TrainingArguments |
69+
| `b7a2d6d` | Scoped codecarbon tracker (severity trainer) |
70+
| `ed2c230` | `--class-weights` flag (none/sqrt/balanced) |
71+
| `f1cd426` | Focal loss option |
72+
| `9fa0f86` | Default to uniform loss |
73+
| `7920361` | Static model card |
74+
| `5b2866c` | Dynamic model card from eval metrics |
75+
76+
## References
77+
78+
- Issue: [VulnTrain#19](https://github.com/vulnerability-lookup/VulnTrain/issues/19)
79+
- Model: [CIRCL/vulnerability-severity-classification-chinese-macbert-base](https://huggingface.co/CIRCL/vulnerability-severity-classification-chinese-macbert-base)
80+
- Dataset: [CIRCL/Vulnerability-CNVD](https://huggingface.co/datasets/CIRCL/Vulnerability-CNVD)
81+
- External validation: [eromang/researches/CNVD-Dataset-Validation](https://github.com/eromang/researches/tree/main/CNVD-Dataset-Validation)

0 commit comments

Comments
 (0)