Salta al contenuto principale

Misurare l\

Ogni volta che addestri un modello di machine learning, esegui una pipeline ETL o fai girare un batch job notturno, consumi energia.

Cosa ne pensi di questo articolo?

Misurare l\
Green SoftwareFoto:Brett SaylesPexels

In Breve

Ogni volta che addestri un modello di machine learning, esegui una pipeline ETL o fai girare un batch job notturno, consumi energia.

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 EmissionsTracker e OfflineEmissionsTracker
  • 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 --privileged o 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)

  1. 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.
  2. 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.
  3. Model size ridotto (BERT-large → DistilBERT): -80% parametri, -60% tempo. Modelli distillati o più piccoli raggiungono spesso accuracy comparabili con una frazione del compute.
  4. 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.
  5. 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:

  1. Misura: Integra CodeCarbon in tutti i workflow. Definisci carbon budget. Crea baseline misurabili. Rendi le emissioni visibili quanto la accuracy.
  2. Ottimizza: Usa i dati per identificare i bottleneck carbonici. Location shifting, model size, batch size e mixed precision sono i leverà più efficaci.
  3. 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.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti