Skip to content

Commit 3d11217

Browse files
fix: [training] Push CodeCarbon emissions report to the Hub
The emissions.csv was written to the CWD (CodeCarbon's default output_dir), which trainer.push_to_hub() never uploads, so the report stopped reaching the Hub. Point the tracker at model_save_dir and push the report explicitly via a shared push_emissions_report() helper. Also convert generation_description.py and cwe_guesser_patches.py from the @track_emissions decorator to an explicit EmissionsTracker: the decorator only finalizes emissions.csv after train() returns, i.e. after push_to_hub() has already run, so the report could never be included. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5ce8432 commit 3d11217

5 files changed

Lines changed: 87 additions & 7 deletions

File tree

vulntrain/trainers/classify_severity.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
TrainingArguments,
1717
)
1818

19+
from vulntrain.utils import push_emissions_report
20+
1921
"""
2022
Automatically classify new vulnerabilities based on their descriptions,
2123
even if they don't have CVSS scores.
@@ -225,7 +227,15 @@ def tokenize_function(elem):
225227
# Train model
226228
tracker = None
227229
if codecarbon:
228-
tracker = EmissionsTracker(project_name="VulnTrain", allow_multiple_runs=True)
230+
# Save emissions data inside the model directory so it gets pushed to
231+
# the Hub together with the model (default output_dir is the CWD, which
232+
# is never uploaded).
233+
tracker = EmissionsTracker(
234+
project_name="VulnTrain",
235+
output_dir=model_save_dir,
236+
output_file="emissions.csv",
237+
allow_multiple_runs=True,
238+
)
229239
tracker.start()
230240
try:
231241
trainer.train()
@@ -239,6 +249,9 @@ def tokenize_function(elem):
239249
trainer.push_to_hub()
240250
tokenizer.push_to_hub(repo_id)
241251

252+
if tracker is not None and push_emissions_report(model_save_dir, repo_id):
253+
logger.info(f"Emissions report pushed to {repo_id}")
254+
242255

243256
def main():
244257
parser = argparse.ArgumentParser(

vulntrain/trainers/classify_severity_cnvd.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
TrainingArguments,
2020
)
2121

22+
from vulntrain.utils import push_emissions_report
23+
2224
# Logging setup
2325
logging.basicConfig(level=logging.INFO)
2426
logger = logging.getLogger(__name__)
@@ -311,7 +313,15 @@ def tokenize_function(examples):
311313
else:
312314
trainer = Trainer(**trainer_kwargs)
313315

314-
tracker = EmissionsTracker(project_name="VulnTrain", allow_multiple_runs=True)
316+
# Save emissions data inside the model directory so it gets pushed to the
317+
# Hub together with the model (default output_dir is the CWD, which is never
318+
# uploaded).
319+
tracker = EmissionsTracker(
320+
project_name="VulnTrain",
321+
output_dir=model_save_dir,
322+
output_file="emissions.csv",
323+
allow_multiple_runs=True,
324+
)
315325
tracker.start()
316326
try:
317327
trainer.train()
@@ -323,6 +333,9 @@ def tokenize_function(examples):
323333
trainer.push_to_hub()
324334
tokenizer.push_to_hub(repo_id)
325335

336+
if push_emissions_report(model_save_dir, repo_id):
337+
logger.info(f"Emissions report pushed to {repo_id}")
338+
326339
if not push_card:
327340
return
328341

vulntrain/trainers/cwe_guesser_patches.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import evaluate
1111
import numpy as np
1212
import torch
13-
from codecarbon import track_emissions
13+
from codecarbon import EmissionsTracker
1414
from datasets import load_dataset
1515
from sklearn.metrics import accuracy_score, f1_score
1616
from transformers import (
@@ -21,6 +21,8 @@
2121
TrainingArguments,
2222
)
2323

24+
from vulntrain.utils import push_emissions_report
25+
2426
accuracy = evaluate.load("accuracy")
2527
f1 = evaluate.load("f1", config_name="macro")
2628

@@ -62,7 +64,6 @@ def compute_loss(
6264
return (loss, outputs) if return_outputs else loss
6365

6466

65-
@track_emissions(project_name="VulnTrain", allow_multiple_runs=True)
6667
def train(base_model, dataset_id, repo_id, model_save_dir="./vulnerability-classify"):
6768
dataset = load_dataset(dataset_id)
6869
dataset = dataset["train"].filter(lambda x: x.get("cwe") and len(x["cwe"]) > 0)
@@ -179,9 +180,20 @@ def tokenize_function(examples):
179180
class_weights=class_weights,
180181
)
181182

183+
# Save emissions data inside the model directory so it gets pushed to the
184+
# Hub together with the model (default output_dir is the CWD, which is never
185+
# uploaded).
186+
tracker = EmissionsTracker(
187+
project_name="VulnTrain",
188+
output_dir=model_save_dir,
189+
output_file="emissions.csv",
190+
allow_multiple_runs=True,
191+
)
192+
tracker.start()
182193
try:
183194
trainer.train()
184195
finally:
196+
tracker.stop()
185197
model.save_pretrained(model_save_dir)
186198
tokenizer.save_pretrained(model_save_dir)
187199

@@ -200,6 +212,9 @@ def tokenize_function(examples):
200212
trainer.push_to_hub(repo_id)
201213
tokenizer.push_to_hub(repo_id)
202214

215+
if push_emissions_report(model_save_dir, repo_id):
216+
logger.info(f"Emissions report pushed to {repo_id}")
217+
203218

204219
def main():
205220
parser = argparse.ArgumentParser(

vulntrain/trainers/generation_description.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pathlib import Path
44

55
import torch
6-
from codecarbon import track_emissions
6+
from codecarbon import EmissionsTracker
77
from datasets import load_dataset
88
from transformers import (
99
AutoModelForCausalLM,
@@ -14,6 +14,8 @@
1414
TrainingArguments,
1515
)
1616

17+
from vulntrain.utils import push_emissions_report
18+
1719
"""
1820
Create a text generation model for descriptions of vulnerabilities.
1921
@@ -38,7 +40,6 @@ def tokenize_function(examples):
3840
return tokenized_datasets.train_test_split(test_size=0.2)
3941

4042

41-
@track_emissions(project_name="VulnTrain", allow_multiple_runs=True)
4243
def train(
4344
base_model, dataset_id, repo_id, model_save_dir="./vulnerability-description"
4445
):
@@ -89,15 +90,29 @@ def train(
8990
data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False),
9091
)
9192

93+
# Save emissions data inside the model directory so it gets pushed to the
94+
# Hub together with the model (default output_dir is the CWD, which is never
95+
# uploaded).
96+
tracker = EmissionsTracker(
97+
project_name="VulnTrain",
98+
output_dir=model_save_dir,
99+
output_file="emissions.csv",
100+
allow_multiple_runs=True,
101+
)
102+
tracker.start()
92103
try:
93104
trainer.train()
94105
finally:
106+
tracker.stop()
95107
model.save_pretrained(model_save_dir)
96108
tokenizer.save_pretrained(model_save_dir)
97109

98110
trainer.push_to_hub()
99111
tokenizer.push_to_hub(repo_id)
100112

113+
if push_emissions_report(model_save_dir, repo_id):
114+
print(f"Emissions report pushed to {repo_id}")
115+
101116

102117
def main():
103118
parser = argparse.ArgumentParser(

vulntrain/utils.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import re
33

44
import cvss
5-
65
from markdown_it import MarkdownIt
76
from nltk.tokenize import sent_tokenize
87

@@ -206,3 +205,28 @@ def extract_cpe(branches):
206205

207206
# Print unique CPEs
208207
return sorted(set(cpe_list))
208+
209+
210+
def push_emissions_report(model_save_dir: str, repo_id: str) -> bool:
211+
"""Upload the CodeCarbon ``emissions.csv`` report to the Hugging Face Hub.
212+
213+
``trainer.push_to_hub()`` uploads ``model_save_dir`` but ignores files it
214+
does not recognise, so the emissions report is pushed explicitly.
215+
216+
Returns ``True`` if the report existed and was uploaded.
217+
"""
218+
from pathlib import Path
219+
220+
emissions_file = Path(model_save_dir) / "emissions.csv"
221+
if not emissions_file.exists():
222+
return False
223+
224+
from huggingface_hub import HfApi
225+
226+
HfApi().upload_file(
227+
path_or_fileobj=str(emissions_file),
228+
path_in_repo="emissions.csv",
229+
repo_id=repo_id,
230+
commit_message="Add CodeCarbon emissions report",
231+
)
232+
return True

0 commit comments

Comments
 (0)