Misurare l'Impronta Carbonica del Codice con CodeCarbon
Ogni volta che addestri un modello di machine learning, esegui una pipeline ETL o fai girare un batch job notturno, consumi energia. Quell'energia ha un costo carbonico reale, che dipende da dove e quando esegui il codice. Il settore ICT nel suo complesso è responsabile del 2-4% delle emissioni globali di CO₂, una quota paragonabile a quella dell'intera industria aeronautica mondiale. Ma a differenza di un volo, le emissioni del software sono quasi sempre invisibili.
Il training di GPT-3 ha prodotto circa 552 tonnellate di CO₂, equivalenti a 300 viaggi andata e ritorno New York-San Francisco in aereo. Un fine-tuning di BERT-large su una GPU A100 per 10 ore in Germania (mix energetico a ~381 gCO₂/kWh) emette circa 1,2 kg di CO₂. Lo stesso training spostato in Francia (nucleare, ~56 gCO₂/kWh) produce solo 177 grammi. La differenza è di quasi 7 volte, a parità di codice.
CodeCarbon è lo strumento open-source che rende queste emissioni misurabili e confrontabili. Con poche righe di Python puoi tracciare il carbon footprint di qualsiasi processo computazionale, dal singolo esperimento ML alla pipeline di produzione, e integrare queste metriche nei tuoi workflow MLflow, nei report ESG e nelle pipeline CI/CD.
Cosa Imparerai
- Architettura interna di CodeCarbon: come misura energia e calcola le emissioni CO₂
- Setup con
EmissionsTrackereOfflineEmissionsTracker - Tracking durante il training di modelli TensorFlow e PyTorch
- Misurazione GPU con CUDA e NVML per hardware NVIDIA
- Configurazione per diversi paesi e grid carbon intensity europei
- Integrazione con MLflow per emissioni come metriche di esperimento
- Dashboard con matplotlib e plotly per visualizzazione trend
- Cloud Carbon Footprint per stimare emissioni su AWS, GCP e Azure
- Confronto strumenti: CodeCarbon vs Eco2AI vs CarbonTracker vs ML CO2 Impact
- Caso studio reale: ottimizzazione training NLP con riduzione del 96% delle emissioni
Serie Green Software: 10 Articoli per Sviluppare Software Sostenibile
| # | Articolo | Argomento | Stato |
|---|---|---|---|
| 1 | Principi Green Software Foundation | GSF, SCI, 8 principi fondamentali | Disponibile |
| 2 | Misurare l'Impronta Carbonica con CodeCarbon | Misurazione, tracking, dashboard | Articolo corrente |
| 3 | Integrare Climatiq API | Calcoli GHG Protocol, Scope 1-3 | Disponibile |
| 4 | Carbon Aware SDK | Time shifting, location shifting | Disponibile |
| 5 | Scope 1, 2 e 3: Data Modeling ESG | Struttura dati, calcoli, aggregazione | Disponibile |
| 6 | GreenOps con Kubernetes | Scheduling carbon-aware, infrastruttura | Disponibile |
| 7 | Pipeline Scope 3 Value Chain | Supplier data, audit trail | Disponibile |
| 8 | API ESG Reporting e CSRD | Compliance europea, automazione | Disponibile |
| 9 | Pattern Architetturali Sostenibili | Storage, cache, batch carbon-aware | Disponibile |
| 10 | AI e Carbonio: ML Training Footprint | Green AI, ottimizzazione training | Disponibile |
CodeCarbon: Architettura e Meccanismo di Funzionamento
CodeCarbon è una libreria Python open-source sviluppata da una collaborazione tra Mila Quebec AI Institute, Carnegie Mellon University, Comet.ml e BCG GAMMA. Il progetto nasce nel 2020 con l'obiettivo di rendere la misurazione delle emissioni del codice accessibile a qualsiasi data scientist, senza richiedere strumentazione hardware dedicata.
Il principio di funzionamento si basa su una formula fondamentale:
Formula Centrale di CodeCarbon
CO₂ emessa [kg] = Energia consumata [kWh] × Intensità carbonica della rete [kgCO₂/kWh]
L'energia consumata viene misurata componente per componente (GPU + CPU + RAM), mentre l'intensità carbonica viene recuperata da database aggiornati per paese/regione o fornita manualmente per ambienti offline.
Pipeline di Misurazione Interna
CodeCarbon campiona il consumo energetico ogni 15 secondi (configurabile) attraverso una pipeline a più livelli. A ogni campionamento misura la potenza istantanea di CPU, GPU e RAM, integra nel tempo per ottenere i kWh, e moltiplica per il fattore di emissione del paese.
Gerarchia di Misurazione Energetica
| Componente | Metodo di Misurazione | Precisione | Disponibilità |
|---|---|---|---|
| GPU NVIDIA | NVML via pynvml (NVIDIA Management Library) | Alta (misura diretta) | Solo GPU NVIDIA con driver |
| CPU Intel | RAPL (Running Average Power Limit) via pyRAPL | Alta (sensori hardware) | Linux con accesso MSR, Intel |
| CPU AMD/ARM | TDP stimato + percentuale di utilizzo | Media (stima) | Tutti i sistemi |
| RAM | Formula empirica: 3W per 8GB | Bassa-Media (stima) | Tutti i sistemi |
| GPU AMD | ROCm SMI (quando disponibile) | Alta (misura diretta) | GPU AMD con ROCm |
Carbon Intensity per Paese
Per tradurre kWh in kgCO₂, CodeCarbon utilizza un database gerarchico di fattori di emissione. In modalità online, recupera dati tramite geolocalizzazione IP. In modalità offline, usa un database interno con valori medi annuali per 240+ paesi.
Carbon Intensity per Paese Europeo (2024)
| Paese | gCO₂/kWh | Mix Energetico Prevalente | Codice ISO |
|---|---|---|---|
| Norvegia | ~28 | Idroelettrico (90%) | NOR |
| Francia | ~56 | Nucleare (70%) | FRA |
| Svezia | ~45 | Nucleare + Idro | SWE |
| Spagna | ~191 | Rinnovabili + Gas | ESP |
| Italia | ~233 | Gas naturale + Rinnovabili | ITA |
| Germania | ~381 | Gas + Carbone + Vento | DEU |
| Polonia | ~640 | Carbone (70%) | POL |
Setup e Installazione di CodeCarbon
L'installazione di CodeCarbon richiede Python 3.7+ ed è disponibile su PyPI e conda. Per il tracking GPU è necessario avere i driver NVIDIA e il pacchetto pynvml.
Installazione
# Installazione base
pip install codecarbon
# Con dipendenze per visualizzazione dashboard
pip install codecarbon[viz]
# Per ambienti conda
conda install -c conda-forge codecarbon
# Verifica installazione
python -c "from codecarbon import EmissionsTracker; print('CodeCarbon OK')"
# Controlla quale hardware viene rilevato
python -c "
import pynvml
try:
pynvml.nvmlInit()
count = pynvml.nvmlDeviceGetCount()
for i in range(count):
h = pynvml.nvmlDeviceGetHandleByIndex(i)
print(f'GPU {i}: ', pynvml.nvmlDeviceGetName(h))
pynvml.nvmlShutdown()
except Exception as e:
print(f'GPU non rilevata: {e}')
print('CodeCarbon usera stima TDP per CPU')
"
File di Configurazione .codecarbon.config
CodeCarbon supporta un file di configurazione INI a livello di progetto che evita di ripetere
i parametri in ogni script. Crea un file .codecarbon.config nella root del progetto:
# .codecarbon.config
[codecarbon]
# Modalità: online (default) o offline
mode = online
# Paese ISO 3166-1 alpha-3 per carbon intensity
country_iso_code = ITA
# Intervallo di campionamento in secondi (default: 15)
measure_power_secs = 15
# File di output CSV
output_file = emissions.csv
# Directory output
output_dir = ./carbon_reports
# Log level: DEBUG, INFO, WARNING, ERROR
log_level = INFO
# Salva su CodeCarbon cloud dashboard (richiede API key)
save_to_api = false
# Modalità di tracking
tracking_mode = process
EmissionsTracker: Tre Modalità di Utilizzo
from codecarbon import EmissionsTracker
from codecarbon import track_emissions
import time
# ===== MODALITA 1: Context Manager (raccomandato) =====
with EmissionsTracker(
project_name="my-ml-project",
output_dir="./carbon_reports",
country_iso_code="ITA",
log_level="INFO"
) as tracker:
print("Training in corso...")
time.sleep(5) # Simula training
print(f"Emissioni: {tracker.final_emissions:.6f} kgCO2eq")
print(f"Energia: {tracker.final_emissions_data.energy_consumed:.4f} kWh")
# ===== MODALITA 2: Start/Stop Espliciti =====
tracker = EmissionsTracker(
project_name="batch-job",
output_file="batch_emissions.csv",
measure_power_secs=10
)
tracker.start()
print("Preprocessing...")
time.sleep(3)
tracker.flush() # Checkpoint senza fermare il tracking
print("Training...")
time.sleep(5)
emissions = tracker.stop()
print(f"CO2 emessa: {emissions:.6f} kgCO2eq")
# ===== MODALITA 3: Decorator =====
@track_emissions(
project_name="inference-pipeline",
country_iso_code="FRA",
output_dir="./carbon_reports"
)
def run_inference(model, data):
return model.predict(data)
OfflineEmissionsTracker: Per Ambienti Senza Internet
In cluster HPC, container di produzione o macchine senza accesso a internet, usa
OfflineEmissionsTracker. Il country code ISO diventa obbligatorio.
from codecarbon import OfflineEmissionsTracker
tracker = OfflineEmissionsTracker(
country_iso_code="ITA", # Obbligatorio in modalità offline
project_name="hpc-training",
output_dir="/results/carbon",
measure_power_secs=30, # Meno frequente per ridurre overhead
log_level="WARNING"
)
tracker.start()
try:
run_training_pipeline()
except Exception as e:
print(f"Errore: {e}")
raise
finally:
# Chiama sempre stop() anche in caso di errore
emissions = tracker.stop()
if emissions:
print(f"[Carbon] {emissions:.4f} kgCO2eq")
# Override carbon intensity manuale (es: data center con energia 100% rinnovabile)
tracker_custom = OfflineEmissionsTracker(
country_iso_code="ITA",
# Specifica intensità carbonica del tuo fornitore (es: 20 gCO2/kWh)
# mediante variabile ambiente CODECARBON_CARBON_INTENSITY=0.020
measure_power_secs=15
)
Tracking Emissioni in Progetti Machine Learning
Il caso d'uso principale di CodeCarbon è il tracking durante l'addestramento di modelli ML. Vediamo esempi pratici con PyTorch e TensorFlow/Keras.
Training PyTorch con Carbon Tracking Completo
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from codecarbon import EmissionsTracker
import numpy as np
import json
from datetime import datetime
class TextClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, num_classes):
super(TextClassifier, self).__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.lstm = nn.LSTM(embed_dim, 128, batch_first=True, bidirectional=True)
self.classifier = nn.Sequential(
nn.Linear(256, 64),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(64, num_classes)
)
def forward(self, x):
embedded = self.embedding(x)
lstm_out, _ = self.lstm(embedded)
pooled = lstm_out.mean(dim=1)
return self.classifier(pooled)
def train_with_carbon_tracking(model, train_loader, val_loader, epochs=10, country="ITA"):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
optimizer = optim.AdamW(model.parameters(), lr=2e-4, weight_decay=0.01)
criterion = nn.CrossEntropyLoss()
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
training_history = []
tracker = EmissionsTracker(
project_name=f"text-classifier-{datetime.now().strftime('%Y%m%d_%H%M')}",
output_dir="./carbon_reports",
country_iso_code=country,
log_level="WARNING",
measure_power_secs=15
)
tracker.start()
gpu_name = torch.cuda.get_device_name(0) if torch.cuda.is_available() else "N/A"
print(f"Dispositivo: {device} | GPU: {gpu_name} | Paese: {country}")
for epoch in range(epochs):
# Training
model.train()
train_loss, correct, total = 0.0, 0, 0
for X, y in train_loader:
X, y = X.to(device), y.to(device)
optimizer.zero_grad()
out = model(X)
loss = criterion(out, y)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
train_loss += loss.item()
correct += out.argmax(1).eq(y).sum().item()
total += y.size(0)
# Validation
model.eval()
val_correct, val_total = 0, 0
with torch.no_grad():
for X, y in val_loader:
X, y = X.to(device), y.to(device)
out = model(X)
val_correct += out.argmax(1).eq(y).sum().item()
val_total += y.size(0)
scheduler.step()
stats = {
"epoch": epoch + 1,
"train_acc": 100. * correct / total,
"val_acc": 100. * val_correct / val_total,
"train_loss": train_loss / len(train_loader),
}
training_history.append(stats)
print(f"Epoch {epoch+1}/{epochs} | "
f"Train: {stats['train_acc']:.2f}% | Val: {stats['val_acc']:.2f}%")
total_emissions = tracker.stop()
em_data = tracker.final_emissions_data
final_val_acc = training_history[-1]["val_acc"]
print(f"\n=== CARBON REPORT ===")
print(f"CO2 totale: {total_emissions:.6f} kgCO2eq")
print(f"Per epoch: {total_emissions / epochs * 1000:.2f} gCO2eq")
if em_data:
print(f"Energia: {em_data.energy_consumed:.4f} kWh")
print(f"Val accuracy: {final_val_acc:.2f}%")
if total_emissions:
print(f"Efficienza: {final_val_acc / (total_emissions * 1000):.2f} acc/gCO2")
results = {
"model": "TextClassifier-BiLSTM",
"epochs": epochs,
"final_val_accuracy": final_val_acc,
"total_co2_kg": total_emissions,
"country": country,
"training_history": training_history
}
with open("./carbon_reports/training_results.json", "w") as f:
json.dump(results, f, indent=2)
return results
if __name__ == "__main__":
X = torch.randint(0, 1000, (2000, 128))
y = torch.randint(0, 3, (2000,))
dataset = TensorDataset(X, y)
train_ds, val_ds = torch.utils.data.random_split(dataset, [1600, 400])
train_loader = DataLoader(train_ds, batch_size=32, shuffle=True)
val_loader = DataLoader(val_ds, batch_size=64)
model = TextClassifier(vocab_size=1000, embed_dim=64, num_classes=3)
train_with_carbon_tracking(model, train_loader, val_loader, epochs=5, country="ITA")
Callback Keras per TensorFlow
import tensorflow as tf
from tensorflow import keras
from codecarbon import EmissionsTracker
import numpy as np
class CarbonTrackingCallback(keras.callbacks.Callback):
"""Callback Keras che integra CodeCarbon per il reporting per epoch."""
def __init__(self, tracker: EmissionsTracker):
super().__init__()
self.tracker = tracker
def on_epoch_end(self, epoch, logs=None):
logs = logs or {}
val_acc = logs.get('val_accuracy', 0)
print(f"\n [Carbon] Epoch {epoch+1} | Val accuracy: {val_acc:.4f}")
def train_keras_with_carbon():
vocab_size, max_len = 10000, 200
model = keras.Sequential([
keras.layers.Embedding(vocab_size, 128, input_length=max_len),
keras.layers.Bidirectional(keras.layers.LSTM(64, return_sequences=True)),
keras.layers.GlobalMaxPooling1D(),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dropout(0.3),
keras.layers.Dense(1, activation='sigmoid')
])
model.compile(
optimizer=keras.optimizers.Adam(3e-4),
loss='binary_crossentropy',
metrics=['accuracy']
)
X_train = np.random.randint(0, vocab_size, (8000, max_len))
y_train = np.random.randint(0, 2, (8000,))
X_val = np.random.randint(0, vocab_size, (2000, max_len))
y_val = np.random.randint(0, 2, (2000,))
with EmissionsTracker(
project_name="keras-sentiment",
output_dir="./carbon_reports",
country_iso_code="ITA",
log_level="WARNING"
) as tracker:
history = model.fit(
X_train, y_train,
validation_data=(X_val, y_val),
epochs=10,
batch_size=64,
callbacks=[
CarbonTrackingCallback(tracker),
keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),
keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=2)
],
verbose=1
)
print(f"\nEmissioni totali: {tracker.final_emissions:.6f} kgCO2eq")
print(f"Val accuracy: {max(history.history['val_accuracy']):.4f}")
return model, tracker.final_emissions
train_keras_with_carbon()
GPU Tracking: Misurazione Precisa con NVIDIA NVML
Per i workload GPU-intensive, CodeCarbon usa pynvml, il binding Python della NVIDIA Management Library (NVML), per misurare il consumo di potenza diretto in watt. Questo è il metodo più preciso disponibile, con una risoluzione di 1-5 watt.
import pynvml
from codecarbon import EmissionsTracker
import torch
def inspect_gpu_power():
"""Ispezione diretta del consumo GPU con NVML (come fa CodeCarbon internamente)."""
pynvml.nvmlInit()
count = pynvml.nvmlDeviceGetCount()
print(f"GPU rilevate: {count}")
for i in range(count):
h = pynvml.nvmlDeviceGetHandleByIndex(i)
name = pynvml.nvmlDeviceGetName(h)
power_w = pynvml.nvmlDeviceGetPowerUsage(h) / 1000 # mW -> W
limit_w = pynvml.nvmlDeviceGetEnforcedPowerLimit(h) / 1000
mem = pynvml.nvmlDeviceGetMemoryInfo(h)
print(f" GPU {i}: {name}")
print(f" Consumo: {power_w:.1f}W / {limit_w:.0f}W (TDP)")
print(f" VRAM: {mem.used / 1e9:.1f}GB / {mem.total / 1e9:.1f}GB")
pynvml.nvmlShutdown()
def train_on_gpu_with_tracking():
"""Training su GPU con tracking dettagliato del consumo."""
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
tracker = EmissionsTracker(
project_name="gpu-intensive-run",
output_dir="./carbon_reports",
country_iso_code="ITA",
gpu_ids=[0], # Monitora solo GPU 0
measure_power_secs=5, # Alta frequenza per run brevi
log_level="DEBUG" # Mostra dettagli misurazione
)
tracker.start()
print(f"Dispositivo: {device}")
# Workload GPU intensivo: matrix multiplication
for step in range(100):
a = torch.randn(256, 1024, device=device)
b = torch.randn(1024, 1024, device=device)
c = torch.matmul(a, b)
loss = c.mean()
loss.backward()
if step % 25 == 0:
print(f"Step {step}/100 | Loss: {loss.item():.4f}")
emissions = tracker.stop()
data = tracker.final_emissions_data
print(f"\n=== GPU CARBON REPORT ===")
print(f"Emissioni: {emissions:.6f} kgCO2eq")
if data:
print(f"Energia: {data.energy_consumed:.4f} kWh")
return emissions
inspect_gpu_power()
train_on_gpu_with_tracking()
Limitazioni del Tracking GPU
- Solo NVIDIA con CUDA: GPU AMD richiedono ROCm; Intel Arc non supportata
- Container Docker non privilegiati: NVML potrebbe non avere accesso ai driver;
aggiungi il flag
--privilegedo monta/dev/nvidia* - Cloud VM: Su istanze EC2/GCE/Azure NVML potrebbe non esporre il consumo reale; usa Cloud Carbon Footprint per stime cloud
- RAPL su CPU: Su Linux richiede accesso a
/sys/class/powercap/; su macOS e Windows usa stima TDP - Precisione complessiva: Ricerca recente (2025) indica che gli strumenti di stima possono avere errori fino al 40%; usa i dati come ordine di grandezza, non come misura assoluta
Integrazione con MLflow: Emissioni come Metriche di Esperimento
Integrare CodeCarbon con MLflow permette di loggare le emissioni CO₂ accanto alle metriche del modello (accuracy, loss, F1). Il carbon footprint diventa un criterio di selezione del modello esattamente come la performance predittiva.
import mlflow
import mlflow.pytorch
from codecarbon import EmissionsTracker
import torch
import torch.nn as nn
from datetime import datetime
def train_with_mlflow_carbon(
model_config: dict,
train_loader,
val_loader,
country: str = "ITA",
experiment_name: str = "carbon-aware-experiments"
):
"""Training con logging doppio: MLflow per metriche, CodeCarbon per CO2."""
mlflow.set_experiment(experiment_name)
with mlflow.start_run(run_name=f"run-{datetime.now().strftime('%H%M%S')}") as run:
# Log iperparametri
mlflow.log_params({
"model_type": model_config.get("type", "unknown"),
"learning_rate": model_config.get("lr", 1e-3),
"batch_size": model_config.get("batch_size", 32),
"epochs": model_config.get("epochs", 10),
"country": country
})
# Avvia tracking CO2
tracker = EmissionsTracker(
project_name=f"mlflow-{run.info.run_id[:8]}",
output_dir="./carbon_reports",
country_iso_code=country,
log_level="WARNING"
)
tracker.start()
# Training loop
model = build_model(model_config)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
opt = torch.optim.Adam(model.parameters(), lr=model_config.get("lr", 1e-3))
crit = nn.CrossEntropyLoss()
epochs = model_config.get("epochs", 10)
best_val_acc = 0.0
for epoch in range(epochs):
# Train
model.train()
train_loss, correct, total = 0.0, 0, 0
for X, y in train_loader:
X, y = X.to(device), y.to(device)
opt.zero_grad()
out = model(X)
loss = crit(out, y)
loss.backward()
opt.step()
train_loss += loss.item()
correct += out.argmax(1).eq(y).sum().item()
total += y.size(0)
# Validation
model.eval()
val_c, val_t = 0, 0
with torch.no_grad():
for X, y in val_loader:
X, y = X.to(device), y.to(device)
val_c += model(X).argmax(1).eq(y).sum().item()
val_t += y.size(0)
train_acc = correct / total
val_acc = val_c / val_t
best_val_acc = max(best_val_acc, val_acc)
mlflow.log_metrics({
"train_accuracy": train_acc,
"val_accuracy": val_acc,
"train_loss": train_loss / len(train_loader),
}, step=epoch)
# Log metriche CO2 al termine del training
total_emissions = tracker.stop()
em_data = tracker.final_emissions_data
carbon_metrics = {
"co2_kg": total_emissions or 0.0,
"best_val_accuracy": best_val_acc,
"carbon_efficiency": best_val_acc / (total_emissions * 1000) if total_emissions else 0.0,
}
if em_data:
carbon_metrics["energy_kwh"] = em_data.energy_consumed or 0.0
mlflow.log_metrics(carbon_metrics)
mlflow.set_tags({
"carbon_tracked": "true",
"country": country,
"hardware": "gpu" if torch.cuda.is_available() else "cpu"
})
mlflow.pytorch.log_model(model, "model")
print(f"\n=== Run {run.info.run_id} ===")
print(f"Best Val Acc: {best_val_acc:.4f}")
print(f"CO2 emessa: {total_emissions:.6f} kgCO2eq")
print(f"Carbon Efficiency: {carbon_metrics['carbon_efficiency']:.2f} acc/gCO2")
return {
"run_id": run.info.run_id,
"best_val_acc": best_val_acc,
"co2_kg": total_emissions,
"carbon_efficiency": carbon_metrics["carbon_efficiency"]
}
def build_model(config: dict) -> nn.Module:
h = config.get("hidden_size", 128)
return nn.Sequential(
nn.Linear(config.get("input_size", 784), h),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(h, config.get("num_classes", 10))
)
mlflow-emissions-sdk: Integrazione Nativa
Dataroots ha sviluppato un pacchetto dedicato che estende MLflow con supporto nativo a CodeCarbon,
fornendo la classe EmissionsTrackerMlflow che logga automaticamente le emissioni
nel lifecycle di un run MLflow.
pip install mlflow-emissions-sdk
from mlflow_emissions_sdk.tracker import EmissionsTrackerMlflow
import mlflow
with mlflow.start_run():
with EmissionsTrackerMlflow(country_iso_code="ITA") as tracker:
# Le emissioni vengono loggato automaticamente su MLflow
run_training()
# Metriche disponibili nel run MLflow dopo il context manager
Dashboard e Visualizzazione delle Emissioni
CodeCarbon genera un file CSV con tutte le misurazioni. Puoi costruire dashboard personalizzate con plotly o matplotlib per analizzare trend, confrontare esperimenti e preparare report ESG.
Struttura del CSV di Output
Campi Principali del CSV CodeCarbon
| Campo | Unità | Descrizione |
|---|---|---|
timestamp |
ISO 8601 | Momento di misurazione |
project_name |
- | Nome progetto |
duration |
secondi | Durata totale tracking |
emissions |
kgCO₂eq | Emissioni totali |
cpu_energy |
kWh | Energia CPU |
gpu_energy |
kWh | Energia GPU |
ram_energy |
kWh | Energia RAM |
energy_consumed |
kWh | Energia totale (CPU+GPU+RAM) |
carbon_intensity |
kgCO₂/kWh | Intensità carbonica usata |
country_name |
- | Paese di esecuzione |
gpu_model |
- | Modello GPU rilevato |
Dashboard con Plotly e Report Statico con Matplotlib
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import matplotlib.pyplot as plt
def load_emissions_data(csv_path: str = "./carbon_reports/emissions.csv") -> pd.DataFrame:
df = pd.read_csv(csv_path)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['emissions_g'] = df['emissions'] * 1000 # kgCO2 -> gCO2
df['duration_min'] = df['duration'] / 60
return df
def create_plotly_dashboard(df: pd.DataFrame, output_path: str = "./carbon_dashboard.html"):
fig = make_subplots(
rows=2, cols=2,
subplot_titles=[
"Emissioni CO2 per Run (gCO2eq)",
"Breakdown Energetico Medio",
"Emissioni per Progetto (Totale)",
"CO2 Cumulativa nel Tempo"
],
specs=[
[{"type": "bar"}, {"type": "pie"}],
[{"type": "bar"}, {"type": "scatter"}]
]
)
# 1. Bar chart emissioni per run
fig.add_trace(
go.Bar(x=df['project_name'], y=df['emissions_g'],
name="CO2 (g)", marker_color='#e74c3c',
text=df['emissions_g'].round(3), textposition='auto'),
row=1, col=1
)
# 2. Pie chart breakdown energetico
breakdown = {
'CPU': df['cpu_energy'].mean(),
'GPU': df['gpu_energy'].fillna(0).mean(),
'RAM': df['ram_energy'].mean()
}
fig.add_trace(
go.Pie(labels=list(breakdown.keys()), values=list(breakdown.values()),
marker_colors=['#3498db', '#e67e22', '#2ecc71']),
row=1, col=2
)
# 3. Emissioni aggregate per progetto
proj = df.groupby('project_name')['emissions_g'].sum().reset_index()
fig.add_trace(
go.Bar(x=proj['project_name'], y=proj['emissions_g'],
marker_color='#9b59b6'),
row=2, col=1
)
# 4. Trend cumulativo
df_s = df.sort_values('timestamp')
fig.add_trace(
go.Scatter(x=df_s['timestamp'], y=df_s['emissions_g'].cumsum(),
mode='lines+markers', line=dict(color='#e74c3c', width=2),
fill='tozeroy', fillcolor='rgba(231,76,60,0.1)'),
row=2, col=2
)
fig.update_layout(
title="CodeCarbon Dashboard: Carbon Footprint del Progetto",
height=700, showlegend=False, template="plotly_dark"
)
fig.write_html(output_path)
print(f"Dashboard salvata in: {output_path}")
return fig
def create_matplotlib_report(df: pd.DataFrame, output_dir: str = "./carbon_reports"):
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle("Carbon Footprint Report - CodeCarbon", fontsize=16, fontweight='bold')
colors = ['#27ae60', '#2ecc71', '#a8e6cf', '#dcedc1']
# 1. Emissioni per progetto
proj = df.groupby('project_name')['emissions_g'].sum()
axes[0,0].bar(range(len(proj)), proj.values, color=colors[0])
axes[0,0].set_xticks(range(len(proj)))
axes[0,0].set_xticklabels(proj.index, rotation=30, ha='right', fontsize=9)
axes[0,0].set_ylabel("CO2 emessa (gCO2eq)")
axes[0,0].set_title("Emissioni per Progetto")
# 2. Breakdown energetico
ev = [df['cpu_energy'].mean()*1000, df['gpu_energy'].fillna(0).mean()*1000,
df['ram_energy'].mean()*1000]
axes[0,1].pie(ev, labels=['CPU','GPU','RAM'], autopct='%1.1f%%', colors=colors)
axes[0,1].set_title("Breakdown Energetico Medio")
# 3. Emissioni vs durata
axes[1,0].scatter(df['duration_min'], df['emissions_g'],
c=colors[0], alpha=0.7, s=80, edgecolors='white')
axes[1,0].set_xlabel("Durata run (minuti)")
axes[1,0].set_ylabel("Emissioni (gCO2eq)")
axes[1,0].set_title("Emissioni vs Durata Run")
# 4. Cumulativo
cumulative = df.sort_values('timestamp')['emissions_g'].cumsum()
axes[1,1].fill_between(range(len(cumulative)), cumulative, alpha=0.3, color=colors[0])
axes[1,1].plot(range(len(cumulative)), cumulative, color=colors[0], linewidth=2)
axes[1,1].set_xlabel("Numero run")
axes[1,1].set_ylabel("CO2 Cumulativa (gCO2eq)")
axes[1,1].set_title("Andamento Emissioni Cumulativo")
plt.tight_layout()
plt.savefig(f"{output_dir}/carbon_report.png", dpi=150, bbox_inches='tight')
plt.savefig(f"{output_dir}/carbon_report.pdf", bbox_inches='tight')
print("Report salvato in carbon_report.png e .pdf")
return fig
if __name__ == "__main__":
df = load_emissions_data("./carbon_reports/emissions.csv")
create_plotly_dashboard(df)
create_matplotlib_report(df)
Cloud Carbon Footprint: Emissioni dei Workload Cloud
CodeCarbon misura le emissioni locali sulla macchina dove gira il codice. Per workload distribuiti su cloud è necessario uno strumento complementare: Cloud Carbon Footprint (CCF), progetto open-source originariamente sviluppato da ThoughtWorks.
CodeCarbon vs Cloud Carbon Footprint
| Caratteristica | CodeCarbon | Cloud Carbon Footprint |
|---|---|---|
| Target principale | Codice locale / on-premise | Workload su cloud pubblico |
| Fonte dati | Hardware diretto (NVML, RAPL) | Billing API cloud providers |
| Cloud provider | Qualsiasi hardware | AWS, GCP, Azure |
| Real-time | Sì (15-30 sec) | No (dati billing in ritardo) |
| Granularità | Per-process, per-GPU | Per-account, per-region, per-service |
| Embodied carbon (Scope 3) | No | Sì (stima manufacturing) |
| Dashboard web | CSV + custom | React dashboard inclusa |
# Setup Cloud Carbon Footprint
git clone https://github.com/cloud-carbon-footprint/cloud-carbon-footprint.git
cd cloud-carbon-footprint
# Crea file .env da template
cp packages/cli/.env.template packages/cli/.env
# Configurazione AWS (aggiungi in .env):
# AWS_TARGET_ACCOUNT_ID=123456789012
# AWS_REGIONS=eu-west-1,eu-central-1
# AWS_USE_BILLING_DATA=true
# Configurazione GCP (aggiungi in .env):
# GCP_PROJECT_ID=my-project
# GCP_BIG_QUERY_TABLE=billing_export.gcp_billing_export_v1_*
npm install
# Stima emissioni dell'ultimo mese
npx ts-node packages/cli/src/index.ts \
--startDate 2025-02-01 \
--endDate 2025-02-28 \
--groupBy month \
--cloudProviderToSeed AWS
# Output in emissions.json con breakdown per servizio e regione
Confronto Strumenti: CodeCarbon vs Alternative
Il panorama degli strumenti di misurazione del carbon footprint del codice è in rapida evoluzione. Ecco un confronto tecnico tra le opzioni più usate nel 2025.
Confronto Completo
| Strumento | GPU | Offline | MLflow | Accuratezza | Manutenzione |
|---|---|---|---|---|---|
| CodeCarbon | NVIDIA (NVML) | Sì | Via SDK | Media-Alta | Attiva (2025) |
| Eco2AI | Limitato | Sì | No | Media | 2023 |
| CarbonTracker | Sì | Parziale | No | Media | 2022 |
| ML CO2 Impact | Stima | N/A | No | Bassa (stima) | Web tool |
| PyRAPL | No | Sì | No | Alta (CPU Intel) | 2021 |
| Carbon Aware SDK | N/A | No | No | N/A (shifting) | Attiva (GSF) |
Linee Guida per la Scelta
- CodeCarbon: Scelta principale per training ML in Python su hardware locale o on-premise. Eccellente supporto GPU NVIDIA, integrazione MLflow, ampia community.
- Cloud Carbon Footprint: Per analisi dei costi carbonici di infrastruttura cloud. Complementare a CodeCarbon, non sostitutivo.
- ML CO2 Impact Calculator: Strumento web per stime rapide prima di avviare training. Utile in fase di pianificazione per scegliere hardware e regione cloud.
- Carbon Aware SDK (GSF): Non misura emissioni ma le minimizza tramite time/location shifting. Usalo insieme a CodeCarbon per una strategia completa.
Integrazione in CI/CD con GitHub Actions
La misurazione sistematica diventa veramente utile quando è automatizzata. Ecco come integrare CodeCarbon in un workflow GitHub Actions con carbon budget enforcement.
# .github/workflows/carbon-tracking.yml
name: Carbon Footprint Tracking
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
carbon-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install codecarbon pandas
pip install -r requirements.txt
- name: Run training with carbon tracking
run: |
python scripts/train.py \
--country ITA \
--output-dir ./carbon_reports \
--epochs 5
env:
CODECARBON_LOG_LEVEL: WARNING
- name: Enforce carbon budget
run: |
python - <<'PYEOF'
import pandas as pd
import sys
df = pd.read_csv('./carbon_reports/emissions.csv')
total_g = df['emissions'].sum() * 1000
BUDGET_G = 100.0 # Budget massimo per run CI
print(f"Emissioni: {total_g:.2f}g | Budget: {BUDGET_G}g")
if total_g > BUDGET_G:
print(f"BUDGET SUPERATO: {total_g:.2f}g > {BUDGET_G}g")
sys.exit(1)
print(f"OK: {total_g/BUDGET_G*100:.1f}% del budget usato")
PYEOF
- name: Upload carbon reports
uses: actions/upload-artifact@v4
if: always()
with:
name: carbon-reports-${{ github.run_id }}
path: ./carbon_reports/
retention-days: 90
- name: Comment PR with carbon summary
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const csv = fs.readFileSync('./carbon_reports/emissions.csv', 'utf8');
const lines = csv.trim().split('\n');
const headers = lines[0].split(',');
const last = lines[lines.length - 1].split(',');
const idx = headers.indexOf('emissions');
const g = parseFloat(last[idx]) * 1000;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `### Carbon Report\n| Metrica | Valore |\n|---|---|\n| CO2 | ${g.toFixed(3)} gCO2eq |\n| Paese | ITA (233 gCO2/kWh) |`
});
Carbon Budget e SCI (Software Carbon Intensity)
from codecarbon import EmissionsTracker
from dataclasses import dataclass
@dataclass
class CarbonBudget:
project_name: str
max_co2_per_run_g: float
max_co2_monthly_kg: float
def check_run(self, emissions_kg: float) -> bool:
g = emissions_kg * 1000
if g > self.max_co2_per_run_g:
print(f"BUDGET SUPERATO: {g:.2f}g > {self.max_co2_per_run_g}g")
return False
pct = g / self.max_co2_per_run_g * 100
print(f"Budget: {pct:.1f}% usato ({g:.2f}g / {self.max_co2_per_run_g}g)")
return True
BUDGETS = {
"research": CarbonBudget("research", max_co2_per_run_g=500, max_co2_monthly_kg=10),
"production": CarbonBudget("production", max_co2_per_run_g=50, max_co2_monthly_kg=2),
"ci": CarbonBudget("ci", max_co2_per_run_g=100, max_co2_monthly_kg=5),
}
def calculate_sci(emissions_kg: float, energy_kwh: float,
functional_unit: float, unit_desc: str = "prediction") -> dict:
"""
Calcola SCI (Software Carbon Intensity) secondo la specifica GSF:
SCI = CO2eq / R (functional unit)
"""
sci = emissions_kg / max(functional_unit, 1e-9)
return {
"sci": sci,
"sci_unit": f"kgCO2eq/{unit_desc}",
"co2_kg": emissions_kg,
"energy_kwh": energy_kwh,
"equiv_km_car": emissions_kg * 4.44, # ~225 gCO2/km
"equiv_iphone_charges": int(emissions_kg / 0.000008), # ~8 Wh per ricarica
}
def run_with_budget(training_fn, budget_name="production", country="ITA"):
budget = BUDGETS.get(budget_name, BUDGETS["research"])
with EmissionsTracker(
project_name=budget.project_name,
country_iso_code=country,
output_dir="./carbon_reports"
) as tracker:
result = training_fn()
em_kg = tracker.final_emissions
en_kwh = tracker.final_emissions_data.energy_consumed if tracker.final_emissions_data else 0
budget.check_run(em_kg)
n_preds = result.get("num_predictions", 1)
sci = calculate_sci(em_kg, en_kwh, n_preds)
print(f"SCI: {sci['sci']*1e6:.2f} microgCO2eq/prediction")
print(f"Equivalente a {sci['equiv_km_car']:.3f} km in auto")
return sci
Caso Studio Reale: Ottimizzazione Training NLP con CodeCarbon
Un team di NLP stava addestrando un modello di classificazione del sentiment su recensioni di prodotti in italiano. L'obiettivo era raggiungere il 90%+ di accuracy. Prima di introdurre CodeCarbon, il processo di training era completamente opaco dal punto di vista del carbon footprint.
Situazione Iniziale (Baseline)
Configurazione Baseline
| Parametro | Valore Baseline | Valore Ottimizzato |
|---|---|---|
| Modello | BERT-large-uncased (340M parametri) | DistilBERT multilingual (66M parametri) |
| Hardware | 1x NVIDIA A100 80GB | 1x NVIDIA A100 80GB |
| Regione data center | Germania (381 gCO₂/kWh) | Francia (56 gCO₂/kWh) |
| Epochs | 20 (senza early stopping) | Early stopping, convergenza a epoch 9 |
| Batch size | 16 (A100 usata al 30%) | 128 (A100 usata al 92%) |
| Mixed precision | No (FP32) | Sì (FP16) |
| Val accuracy | 91.2% | 90.7% |
| Durata training | ~4.5 ore | ~36 minuti |
Risultati Misurati con CodeCarbon
Confronto Carbon Footprint: Baseline vs Ottimizzato
| Metrica | Baseline | Ottimizzato | Riduzione |
|---|---|---|---|
| CO2 emessa | 1.723 kgCO₂eq | 0.061 kgCO₂eq | -96.5% |
| Energia consumata | 4.52 kWh | 1.09 kWh | -75.9% |
| Durata training | 4.5 ore | 36 minuti | -86.7% |
| Val Accuracy | 91.2% | 90.7% | -0.5% (accettabile) |
| GPU Utilization | ~30% | ~92% | +62 pp |
| Costo cloud stimato | ~$13.50 | ~$1.80 | -86.7% |
La riduzione del 96.5% delle emissioni con solo -0.5% di accuracy dimostra che nella maggior parte dei casi il carbon footprint del software ML può essere drasticamente ridotto senza sacrificare la qualità del modello. La lezione più importante: misurare prima di ottimizzare.
I 5 Interventi per Ridurre le Emissioni ML (in ordine di impatto)
- Location shifting (Germania → Francia): contributo principale -85% emissioni. La carbon intensity del grid elettrico è il singolo fattore più impattante. Francia (nucleare, ~56 gCO₂/kWh) vs Germania (~381 gCO₂/kWh) produce una differenza di 7x a parità di tutto il resto.
- Early stopping: -55% durata media. La maggioranza dei training ML converge molto prima del numero di epochs configurato. Early stopping con patience 3-5 elimina epochs inutili.
- Model size ridotto (BERT-large → DistilBERT): -80% parametri, -60% tempo. Modelli distillati o più piccoli raggiungono spesso accuracy comparabili con una frazione del compute.
- Mixed precision FP16: -30-50% energia GPU. Su GPU NVIDIA A100, RTX 3090+ il FP16 è hardware-accelerato e quasi dimezza il consumo su operazioni di matrix multiplication.
- Batch size ottimale: -75% durata a parità di epochs. Un batch size ottimale per la VRAM disponibile massimizza l'utilizzo GPU e riduce il tempo totale di training.
Best Practices per il Carbon Tracking Sistematico
Struttura di Progetto Raccomandata
project/
├── .codecarbon.config # Configurazione globale
├── carbon_reports/ # Output CodeCarbon
│ ├── emissions.csv # Dati grezzi
│ ├── carbon_report.png # Report grafico
│ └── unified_report.json # Report locale + cloud
├── scripts/
│ ├── train.py # Training con EmissionsTracker
│ ├── evaluate.py # Evaluation con tracker
│ └── carbon_report.py # Genera dashboard
├── .github/workflows/
│ └── carbon-tracking.yml # CI/CD con carbon budget
└── Makefile
# make train - training con tracking
# make carbon-check - verifica budget
# make carbon-report - genera dashboard
Naming Convention e Configurazione da Environment Variables
import os
from datetime import datetime
from codecarbon import EmissionsTracker
def create_tracker(model: str, exp_type: str, dataset: str, country: str = None) -> EmissionsTracker:
"""
Naming convention: {model}-{type}-{dataset}-{timestamp}
Es: distilbert-finetuning-imdb-it-20250309_1430
"""
ts = datetime.now().strftime('%Y%m%d_%H%M')
project_name = f"{model}-{exp_type}-{dataset}-{ts}"
country_code = country or os.environ.get('CODECARBON_COUNTRY', 'ITA')
return EmissionsTracker(
project_name = project_name,
country_iso_code= country_code,
output_dir = os.environ.get('CODECARBON_OUTPUT_DIR', './carbon_reports'),
log_level = os.environ.get('CODECARBON_LOG_LEVEL', 'WARNING'),
measure_power_secs = int(os.environ.get('CODECARBON_INTERVAL', '15'))
)
# Utilizzo
with create_tracker("distilbert", "finetuning", "imdb-it", country="FRA"):
fine_tune_model()
Anti-Pattern da Evitare
- Non tracciare il preprocessing: Tokenization e data augmentation possono consumare CPU significativa. Includi sempre il preprocessing nel tracking per avere il footprint completo.
- Confrontare run tra paesi diversi: Le emissioni CO₂ non sono confrontabili direttamente tra run in paesi con carbon intensity diverse. Normalizza per kWh oppure usa un paese fisso per tutti i benchmark.
- Non versionare .codecarbon.config: La configurazione del tracker deve essere versionata nel repo per garantire riproducibilità delle misurazioni tra sviluppatori.
-
Fermare il tracker in caso di eccezione: Usa sempre un blocco try/finally
o il context manager per garantire che
tracker.stop()venga chiamato anche in caso di crash, preservando i dati parziali. - Trattare il CSV come endpoint finale: Il CSV è il punto di partenza. Costruisci pipeline di analisi per estrarre insight actionable e confrontare esperimenti.
Carbon Accounting e Reporting ESG con i Dati CodeCarbon
Le emissioni misurate da CodeCarbon non sono utili solo per ottimizzare il codice: rappresentano dati preziosi per il reporting ESG e la compliance alla Direttiva CSRD (Corporate Sustainability Reporting Directive), che impone alle grandi aziende europee di rendicontare le emissioni digitali a partire dal 2025-2026. In questo contesto, CodeCarbon diventa uno strumento di governance oltre che di sviluppo.
Il framework GHG Protocol (Greenhouse Gas Protocol) classifica le emissioni in tre scope: Scope 1 (emissioni dirette, es: generatori diesel), Scope 2 (energia elettrica acquistata, dove rientrano le emissioni di CodeCarbon) e Scope 3 (catena del valore, es: cloud vendor e hardware prodotto). CodeCarbon copre principalmente le emissioni di Scope 2 relative ai workload computazionali.
Aggregazione Dati per Report ESG Trimestrale
import pandas as pd
import json
from datetime import datetime, timedelta
from pathlib import Path
def generate_esg_report(
emissions_csv: str = "./carbon_reports/emissions.csv",
report_period: str = "Q1-2025",
company_name: str = "MyTechCompany SRL",
output_path: str = "./esg_report.json"
) -> dict:
"""
Genera un report ESG strutturato dai dati CodeCarbon.
Compatibile con CSRD / GHG Protocol Scope 2.
"""
df = pd.read_csv(emissions_csv)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['emissions_g'] = df['emissions'] * 1000
# Statistiche aggregate
total_co2_kg = df['emissions'].sum()
total_energy_kwh = df['energy_consumed'].sum()
num_experiments = len(df)
avg_co2_per_run_g = df['emissions_g'].mean()
max_co2_run_g = df['emissions_g'].max()
# Breakdown per progetto
project_breakdown = (
df.groupby('project_name')
.agg(
total_co2_kg=('emissions', 'sum'),
total_energy_kwh=('energy_consumed', 'sum'),
num_runs=('emissions', 'count'),
avg_duration_min=('duration', lambda x: (x / 60).mean())
)
.reset_index()
.to_dict(orient='records')
)
# Equivalenze per comunicazione executive
equiv_km_car = total_co2_kg * 4.44 # km in auto media (225 gCO2/km)
equiv_flights = total_co2_kg / 255 # volo Roma-Milano ~255 gCO2 per passeggero
equiv_trees_days = total_co2_kg / 0.022 # giorni assorbimento di un albero (~22 gCO2/giorno)
# Intensità carbonica media pesata
if total_energy_kwh > 0:
weighted_intensity = (df['emissions'] * 1000 / df['energy_consumed']).mean()
else:
weighted_intensity = 0.0
report = {
"report_metadata": {
"generated_at": datetime.now().isoformat(),
"report_period": report_period,
"company": company_name,
"standard": "GHG Protocol - Scope 2 (Market-based)",
"tool": "CodeCarbon v2.x",
"boundary": "Computational workloads (ML training, batch jobs)"
},
"summary": {
"total_co2_kg": round(total_co2_kg, 4),
"total_co2_tco2eq": round(total_co2_kg / 1000, 6),
"total_energy_kwh": round(total_energy_kwh, 4),
"num_tracked_runs": num_experiments,
"avg_co2_per_run_g": round(avg_co2_per_run_g, 3),
"max_co2_single_run_g": round(max_co2_run_g, 3),
"avg_carbon_intensity": round(weighted_intensity, 4)
},
"equivalences": {
"km_car_equivalent": round(equiv_km_car, 1),
"flights_equivalent": round(equiv_flights, 2),
"tree_absorption_days": round(equiv_trees_days, 1)
},
"project_breakdown": project_breakdown,
"scope_classification": {
"scope": 2,
"category": "Purchased electricity for owned operations",
"methodology": "Activity-based (hardware power measurement)",
"data_quality": "High (direct measurement via NVML/RAPL)"
}
}
with open(output_path, 'w') as f:
json.dump(report, f, indent=2, default=str)
print(f"=== Report ESG {report_period} ===")
print(f"CO2 totale: {total_co2_kg:.4f} kgCO2eq")
print(f"Energia: {total_energy_kwh:.4f} kWh")
print(f"Equivalente a: {equiv_km_car:.1f} km in auto")
print(f"Run tracciati: {num_experiments}")
print(f"Report in: {output_path}")
return report
# Genera report per Q1 2025
report = generate_esg_report(
emissions_csv="./carbon_reports/emissions.csv",
report_period="Q1-2025",
company_name="Acme AI SRL",
output_path="./esg_carbon_q1_2025.json"
)
Obiettivi di Riduzione e Science-Based Targets (SBTi)
Le aziende che aderiscono alla Science Based Targets initiative (SBTi) si impegnano a ridurre le emissioni in linea con gli obiettivi climatici dell'Accordo di Parigi. Per le emissioni ICT, questo significa tipicamente una riduzione annuale del 4-7%. CodeCarbon può essere usato per tracciare automaticamente il progresso verso questi target.
import pandas as pd
from datetime import datetime
def track_reduction_progress(
emissions_csv: str,
baseline_year: int,
target_reduction_pct: float = 5.0, # 5% riduzione annuale
) -> dict:
"""
Traccia il progresso verso gli obiettivi di riduzione SBTi.
"""
df = pd.read_csv(emissions_csv)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['year'] = df['timestamp'].dt.year
annual_emissions = df.groupby('year')['emissions'].sum().to_dict()
baseline_emissions = annual_emissions.get(baseline_year, 0)
current_year = datetime.now().year
current_emissions = annual_emissions.get(current_year, 0)
years_elapsed = current_year - baseline_year
target_reduction_factor = (1 - target_reduction_pct / 100) ** years_elapsed
target_emissions = baseline_emissions * target_reduction_factor
actual_reduction_pct = 0.0
on_track = False
if baseline_emissions > 0:
actual_reduction_pct = (baseline_emissions - current_emissions) / baseline_emissions * 100
on_track = current_emissions <= target_emissions
return {
"baseline_year": baseline_year,
"baseline_emissions_kg": round(baseline_emissions, 4),
"current_year": current_year,
"current_emissions_kg": round(current_emissions, 4),
"target_emissions_kg": round(target_emissions, 4),
"target_annual_reduction_pct": target_reduction_pct,
"actual_reduction_pct": round(actual_reduction_pct, 2),
"on_track": on_track,
"annual_history": {str(k): round(v, 4) for k, v in annual_emissions.items()}
}
# Utilizzo: verifica se il team è in linea con gli obiettivi SBTi
progress = track_reduction_progress(
emissions_csv="./carbon_reports/emissions.csv",
baseline_year=2024,
target_reduction_pct=5.0
)
status = "IN LINEA" if progress['on_track'] else "SOTTO TARGET"
print(f"Obiettivi SBTi: {status}")
print(f"Riduzione attuale: {progress['actual_reduction_pct']:.1f}%")
print(f"Target annuale: {progress['target_annual_reduction_pct']:.1f}%")
CodeCarbon e CSRD: Cosa Devi Sapere
La Direttiva CSRD (Corporate Sustainability Reporting Directive) richiede alle aziende europee con oltre 500 dipendenti (dal 2025) e successivamente a quelle con oltre 250 dipendenti (dal 2026) di rendicontare le emissioni GHG secondo standard ESRS (European Sustainability Reporting Standards). Per le emissioni digitali:
- ESRS E1 (Climate change): Richiede disclosure di emissioni Scope 1, 2 e 3 con metodologia GHG Protocol. CodeCarbon copre Scope 2 delle operazioni computazionali.
- Materialità: Se le emissioni ICT sono materiali (significative rispetto al totale aziendale), devono essere rendicontate. Per aziende tech, questo è quasi sempre il caso.
- Audit trail: I CSV di CodeCarbon forniscono un audit trail granulare per la verifica di terze parti, un requisito essenziale per la credibilità del report ESG.
- Assurance: La CSRD richiede assurance limitata (dal 2025) e ragionevole (dal 2028). La qualità dei dati di CodeCarbon (misurazione diretta vs stima) è determinante.
Conclusioni: La Misura È il Primo Passo
CodeCarbon ha trasformato il carbon tracking del codice da un esercizio accademico a una pratica ingegneristica concreta e accessibile. Con meno di 10 righe di Python, qualsiasi data scientist o ML engineer può iniziare a misurare e quantificare l'impatto ambientale del proprio lavoro.
Il caso studio ha dimostrato una riduzione del 96.5% delle emissioni CO₂ con solo -0.5% di accuracy. Questo non è un caso eccezionale: è la norma quando si parte da una configurazione non ottimizzata. La maggior parte dei training ML ha margini enormi di ottimizzazione carbonica che rimangono invisibili finché non si inizia a misurare.
Il percorso verso un software più sostenibile segue tre fasi:
- Misura: Integra CodeCarbon in tutti i workflow. Definisci carbon budget. Crea baseline misurabili. Rendi le emissioni visibili quanto la accuracy.
- Ottimizza: Usa i dati per identificare i bottleneck carbonici. Location shifting, model size, batch size e mixed precision sono i leverà più efficaci.
- Previeni: Integra il carbon tracking in CI/CD. Definisci carbon budget per tipo di esperimento. Usa il Carbon Aware SDK per automatizzare il location/time shifting.
Nel prossimo articolo della serie esploreremo Climatiq API, uno strumento complementare che permette di calcolare le emissioni GHG Protocol (Scope 1, 2 e 3) tramite REST API, integrando i calcoli nel backend delle applicazioni per report ESG automatizzati.
Risorse e Link Utili
Continua la Serie Green Software
- Articolo 1: Principi Green Software Foundation - GSF, SCI, 8 principi
- Articolo 2 (corrente): Misurare l'Impronta Carbonica con CodeCarbon
- Articolo 3: Integrare Climatiq API - Calcoli GHG Protocol nel backend
- Articolo 4: Carbon Aware SDK - Time shifting e location shifting
- Articolo 6: GreenOps con Kubernetes - Infrastruttura carbon-aware
- Articolo 10: AI e Carbonio - Misurare e ridurre il footprint del ML Training

Commenti
Caricamento commenti...
Accedi per commentare