Pomiar śladu węglowego Twojego kodu za pomocą CodeCarbon
Za każdym razem, gdy szkolisz model uczenia maszynowego, uruchamiasz potok ETL lub uruchamiasz zadanie wsadowe at night, you consume energy. Ta energia ma rzeczywisty koszt emisji dwutlenku węgla, który zależy od tego, gdzie i kiedy biegasz kod. The ICT sector as a whole is responsible for the 2-4% globalnych emisji CO₂, co stanowi udział porównywalny z udziałem całego światowego przemysłu lotniczego. Ale W przeciwieństwie do lotu, emisje oprogramowania są prawie zawsze niewidoczne.
Szkolenie GPT-3 przyniosło ok 552 ton CO₂co odpowiada 300 podróżom podróż samolotem w obie strony Nowy Jork-San Francisco. Dostrajanie BERT-large na procesorze graficznym A100 przez 10 godzin Niemcy (miks energetyczny ~381 gCO₂/kWh) emitują ok 1,2 kg CO₂. To samo szkolenie przeniesione do Francji (energia jądrowa, ~56 gCO₂/kWh) produkuje tylko 177 gramów. Różnica jest prawie 7-krotna dla tego samego kodu.
KodWęgiel to narzędzie typu open source, które umożliwia zmierzenie tych emisji i porównywalne. Za pomocą kilku linijek Pythona możesz śledzić ślad węglowy dowolnego procesu obliczeniowych, od pojedynczego eksperymentu ML do rurociągu produkcyjnego, i zintegrować te metryki w przepływach pracy MLflow, raportach ESG i potokach CI/CD.
Czego się nauczysz
- Wewnętrzna architektura CodeCarbon: jak mierzy energię i oblicza emisję CO₂
- Konfiguracja za pomocą
EmissionsTrackereOfflineEmissionsTracker - Śledzenie podczas uczenia modeli TensorFlow i PyTorch
- Pomiar GPU za pomocą CUDA i NVML dla sprzętu NVIDIA
- Konfiguracja dla różnych krajów europejskich i intensywność emisji dwutlenku węgla w sieci
- Integracja z MLflow dla emisji jako wskaźników eksperymentu
- Pulpit nawigacyjny z matplotlib i plotly do wizualizacji trendów
- Cloud Carbon Footprint w celu oszacowania emisji na platformach AWS, GCP i Azure
- Porównanie narzędzi: CodeCarbon vs Eco2AI vs CarbonTracker vs ML CO2 Impact
- Prawdziwe studium przypadku: optymalizacja treningu NLP przy 96% redukcji emisji
Seria Green Software: 10 artykułów dotyczących tworzenia zrównoważonego oprogramowania
| # | Przedmiot | Temat | Państwo |
|---|---|---|---|
| 1 | Zasady Fundacji Zielonego Oprogramowania | GSF, SCI, 8 podstawowych zasad | Dostępny |
| 2 | Pomiar śladu węglowego za pomocą CodeCarbon | Pomiar, śledzenie, deska rozdzielcza | Aktualny artykuł |
| 3 | Zintegruj API Climatiq | Obliczenia Protokołu GHG, Zakres 1-3 | Dostępny |
| 4 | Pakiet SDK uwzględniający emisję dwutlenku węgla | Zmiana czasu, zmiana lokalizacji | Dostępny |
| 5 | Zakres 1, 2 i 3: Modelowanie danych ESG | Struktura danych, obliczenia, agregacja | Dostępny |
| 6 | GreenOps z Kubernetesem | Planowanie i infrastruktura uwzględniające emisję dwutlenku węgla | Dostępny |
| 7 | Zakres rurociągu 3 Łańcuch wartości | Dane dostawcy, ścieżka audytu | Dostępny |
| 8 | Raportowanie ESG i API CSRD | Zgodność europejska, automatyzacja | Dostępny |
| 9 | Zrównoważone wzorce architektoniczne | Pamięć masowa, pamięć podręczna, partia uwzględniająca węgiel | Dostępny |
| 10 | Sztuczna inteligencja i węgiel: ślad szkoleniowy ML | Zielona AI, optymalizacja szkoleń | Dostępny |
CodeCarbon: architektura i mechanizm operacyjny
CodeCarbon to biblioteka Pythona typu open source opracowana we współpracy pomiędzy Instytut AI Mila Quebec, Uniwersytet Carnegie Mellon, Comet.ml i BCG GAMMA. Projekt narodził się w 2020 roku z myślą o wykonaniu pomiaru emisji kodów dostępne dla każdego analityka danych, bez konieczności stosowania dedykowanego sprzętu sprzętowego.
Zasada działania opiera się na podstawowym wzorze:
Centralna formuła CodeCarbon
Wyemitowany CO₂ [kg] = zużyta energia [kWh] × emisyjność sieci [kgCO₂/kWh]
Zużyta energia jest mierzona składnik po elemencie (GPU + CPU + RAM), natomiast intensywność emisji dwutlenku węgla jest pobierana z baz danych aktualizowanych według kraju/regionu lub dostarczane ręcznie dla środowisk offline.
Wewnętrzny rurociąg pomiarowy
CodeCarbon co tydzień pobiera próbki zużycia energii 15 sekund (konfigurowalne) poprzez rurociąg wielopoziomowy. Przy każdym próbkowaniu mierzy chwilową moc procesora, karty graficznej i pamięci RAM, całkuj w czasie, aby uzyskać kWh, i pomnóż przez współczynnik emisji kraju.
Hierarchia pomiaru energii
| Część | Metoda pomiaru | Precyzja | Dostępność |
|---|---|---|---|
| Procesory graficzne NVIDIA | NVML przez pynvml (biblioteka zarządzania NVIDIA) | Wysoka (pomiar bezpośredni) | Tylko procesory graficzne NVIDIA ze sterownikami |
| Procesor Intela | RAPL (limit średniej mocy roboczej) poprzez pyRAPL | Wysoka (czujniki sprzętowe) | Linux z dostępem MSR, Intel |
| Procesory AMD/ARM | Szacowany TDP + procent wykorzystania | Średnia (oszacowanie) | Wszystkie systemy |
| BARAN | Wzór empiryczny: 3 W na 8 GB | Niski-Średni (oszacowanie) | Wszystkie systemy |
| Procesory graficzne AMD | ROCm SMI (jeśli jest dostępny) | Wysoka (pomiar bezpośredni) | Karta graficzna AMD z ROCm |
Intensywność emisji dwutlenku węgla według kraju
Aby przeliczyć kWh na kgCO₂, CodeCarbon korzysta z hierarchicznej bazy danych współczynników emisji. W trybie online pobieraj dane poprzez geolokalizację IP. W trybie offline, korzysta z wewnętrznej bazy danych zawierającej średnie roczne wartości dla ponad 240 krajów.
Intensywność emisji dwutlenku węgla według krajów europejskich (2024)
| Wieś | gCO₂/kWh | Główny miks energetyczny | Kod ISO |
|---|---|---|---|
| Norwegia | ~28 | Hydroelektrownia (90%) | ANI |
| Francja | ~56 | Energia jądrowa (70%) | MIĘDZY |
| Szwecja | ~45 | Energia jądrowa + wodna | SWE |
| Hiszpania | ~191 | Odnawialne źródła energii + gaz | DO POTĘGI |
| Włochy | ~233 | Gaz ziemny + odnawialne źródła energii | IT |
| Niemcy | ~381 | Gaz + Węgiel + Wiatr | DEU |
| Polska | ~640 | Węgiel (70%) | POL |
Konfiguracja i instalacja CodeCarbon
Instalacja CodeCarbon wymaga Pythona 3.7 lub nowszego i jest dostępna na PyPI i Conda. Do śledzenia GPU potrzebne są sterowniki NVIDIA i pakiet pynvml.
Instalacja
# 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')
"
Plik konfiguracyjny .codecarbon.config
CodeCarbon obsługuje plik konfiguracyjny INI obejmujący cały projekt, co pozwala uniknąć powtórzeń
parametry w każdym skrypcie. Utwórz plik .codecarbon.config w katalogu głównym projektu:
# .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: trzy tryby użytkowania
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: dla środowisk bez Internetu
W klastrach HPC, kontenerach produkcyjnych lub maszynach bez dostępu do Internetu użyj
OfflineEmissionsTracker. Kod kraju ISO staje się obowiązkowy.
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
)
Śledzenie emisji w projektach uczenia maszynowego
Podstawowym przypadkiem użycia CodeCarbon jest śledzenie podczas uczenia modeli uczenia maszynowego. Zobaczmy praktyczne przykłady z PyTorch i TensorFlow/Keras.
Szkolenie PyTorch z pełnym śledzeniem emisji dwutlenku węgla
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")
Wywołanie zwrotne Keras dla 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()
Śledzenie GPU: Precyzyjny pomiar dzięki NVIDIA NVML
W przypadku obciążeń intensywnie korzystających z procesora graficznego CodeCarbon używa pynvml, powiązanie Pythona z Biblioteka zarządzania NVIDIA (NVML) do pomiaru bezpośredniego zużycia energii w watach. Jest to najbardziej precyzyjna dostępna metoda, charakteryzująca się rozdzielczością 1–5 watów.
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()
Ograniczenia śledzenia GPU
- Tylko NVIDIA z CUDA: Procesory graficzne AMD wymagają ROCm; Intel Arc nie jest obsługiwany
- Nieuprzywilejowane kontenery Docker: NVML może nie mieć dostępu do sterowników;
dodaj flagę
--privilegedlub zamontować/dev/nvidia* - Maszyny wirtualne w chmurze: Na instancjach EC2/GCE/Azure NVML mogą nie ujawniać rzeczywistego zużycia; użyj Cloud Carbon Footprint do oszacowania chmury
- RAPL na procesorze: W systemie Linux wymaga dostępu do
/sys/class/powercap/; w systemach macOS i Windows użyj szacunkowego TDP - Ogólna dokładność: Ostatnie badania (2025) wskazują, że narzędzia szacowania mogą mieć błędy do 40%; używaj danych jako rzędu wielkości, a nie miary bezwzględnej
Integracja z MLflow: Emisje jako wskaźniki eksperymentu
Integracja CodeCarbon z MLflow umożliwia rejestrowanie emisji CO₂ wraz ze wskaźnikami modelu (dokładność, strata, F1). Kryterium wyboru modelu staje się ślad węglowy dokładnie tak, jak przewidywana wydajność.
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-emisje-sdk: Integracja natywna
Dataroots opracowało dedykowany pakiet rozszerzający MLflow o natywną obsługę CodeCarbon,
zapewnienie zajęć EmissionsTrackerMlflow który automatycznie rejestruje emisję
w cyklu życia przebiegu 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
Panel kontrolny i wizualizacja emisji
CodeCarbon generuje plik CSV ze wszystkimi pomiarami. Możesz tworzyć niestandardowe dashboardy z plotly lub matplotlib do analizy trendów, porównywania eksperymentów i przygotowywania raportów ESG.
Struktura wyjściowego pliku CSV
Główne pola CodeCarbon CSV
| Pole | Jednostka | Opis |
|---|---|---|
timestamp |
ISO8601 | Moment pomiarowy |
project_name |
- | Nazwa projektu |
duration |
towary drugiej jakości | Całkowity czas śledzenia |
emissions |
kgCO₂ekw | Całkowita emisja |
cpu_energy |
kWh | Energia procesora |
gpu_energy |
kWh | Energia GPU |
ram_energy |
kWh | Energia RAM |
energy_consumed |
kWh | Całkowita energia (procesor + karta graficzna + RAM) |
carbon_intensity |
kgCO₂/kWh | Zastosowana intensywność emisji dwutlenku węgla |
country_name |
- | Kraj wykonania |
gpu_model |
- | Wykryto model procesora graficznego |
Pulpit nawigacyjny z raportem Plotly i statycznym za pomocą 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)
Ślad węglowy w chmurze: emisje obciążenia chmury
CodeCarbon mierzy lokalne emisje na maszynie, na której działa kod. W przypadku rozproszonych obciążeń w chmurze potrzebujesz narzędzia uzupełniającego: Ślad węglowy chmur (CCF), projekt open source pierwotnie opracowany przez ThinkWorks.
CodeCarbon a ślad węglowy w chmurze
| Charakterystyczny | KodWęgiel | Ślad węglowy w chmurze |
|---|---|---|
| Główny cel | Kod lokalny/lokalny | Obciążenie w chmurze publicznej |
| Źródło danych | Sprzęt bezpośredni (NVML, RAPL) | Dostawcy chmury API rozliczeń |
| Dostawcy chmury | Dowolny sprzęt | AWS, GCP, Azure |
| W czasie rzeczywistym | Tak (15-30 sekund) | Nie (opóźnione dane rozliczeniowe) |
| Szczegółowość | Na proces, na procesor graficzny | Na konto, na region, na usługę |
| Ucieleśniony węgiel (Zakres 3) | No | Tak (szacunki produkcyjne) |
| Panele internetowe | CSV + niestandardowe | Dołączony panel Reaguj |
# 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
Porównanie narzędzi: CodeCarbon vs alternatywy
Krajobraz narzędzi do pomiaru śladu węglowego w kodzie szybko się rozwija. Oto porównanie techniczne najczęściej używanych opcji w 2025 roku.
Pełne porównanie
| Instrument | GPU | Nieaktywny | MLflow | Dokładność | Konserwacja |
|---|---|---|---|---|---|
| KodWęgiel | NVIDIA (NVML) | Sì | Za pośrednictwem SDK | Średnio-wysoki | Aktywny (2025) |
| Eco2AI | Ograniczony | Sì | No | Przeciętny | 2023 |
| Śledzenie węgla | Sì | Częściowy | No | Przeciętny | 2022 |
| Wpływ ML CO2 | Szacunek | Nie dotyczy | No | Niski (oszacowanie) | Narzędzia internetowe |
| PyRAPL | No | Sì | No | Wysoka (procesor Intel) | 2021 |
| Pakiet SDK uwzględniający emisję dwutlenku węgla | Nie dotyczy | No | No | Nie dotyczy (przesunięcie) | Aktywny (GSF) |
Wytyczne dotyczące wyboru
- KodWęgiel: Wiodący wybór w zakresie szkoleń ML w języku Python na sprzęcie lokalnym lub lokalnym. Doskonała obsługa procesorów graficznych NVIDIA, integracja z MLflow, duża społeczność.
- Ślad węglowy w chmurze: Do analizy kosztów emisji gazów cieplarnianych w infrastrukturze chmurowej. Uzupełnienie CodeCarbon, a nie zamiennik.
- Kalkulator wpływu ML CO2: Narzędzie internetowe umożliwiające szybką wycenę przed rozpoczęciem szkolenia. Przydatne na etapie planowania przy wyborze sprzętu i regionu chmury.
- Pakiet SDK uwzględniający emisję dwutlenku węgla (GSF): Nie mierzy emisji, ale minimalizuje je na podstawie czasu/lokalizacji przesunięcie. Użyj go razem z CodeCarbon, aby uzyskać kompletną strategię.
Integracja CI/CD z akcjami GitHub
Systematyczny pomiar staje się naprawdę przydatny, gdy jest zautomatyzowany. Oto jak zintegrować CodeCarbon z przepływem pracy GitHub Actions z egzekwowaniem budżetu emisji dwutlenku węgla.
# .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) |`
});
Budżet emisji dwutlenku węgla i SCI (intensywność emisji dwutlenku węgla przez oprogramowanie)
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
Prawdziwe studium przypadku: Optymalizacja szkolenia NLP za pomocą CodeCarbon
Zespół NLP trenował model klasyfikacji nastrojów na podstawie recenzji produktów po włosku. Celem było osiągnięcie ponad 90% dokładności. Przed wprowadzeniem CodeCarbon, proces szkoleniowy był całkowicie nieprzejrzysty z punktu widzenia śladu węglowego.
Sytuacja wyjściowa (linia bazowa)
Konfiguracja podstawowa
| Parametr | Wartość bazowa | Zoptymalizowana wartość |
|---|---|---|
| Model | BERT-duży-bez obudowy (parametry 340M) | DistilBERT wielojęzyczny (parametry 66M) |
| Sprzęt komputerowy | 1xNVIDIA A100 80 GB | 1xNVIDIA A100 80 GB |
| Region centrum danych | Niemcy (381 gCO₂/kWh) | Francja (56 gCO₂/kWh) |
| Epoki | 20 (bez wcześniejszego zatrzymania) | Wczesne zatrzymanie, zbieżność w epoce 9 |
| Rozmiar partii | 16 (A100 zużyte w 30%) | 128 (A100 wykorzystano w 92%) |
| Mieszana precyzja | Nie (FP32) | Tak (16PR) |
| Dokładność Val | 91,2% | 90,7% |
| Czas trwania szkolenia | ~4,5 godziny | ~36 minut |
Wyniki zmierzone za pomocą CodeCarbon
Porównanie śladu węglowego: poziom bazowy i zoptymalizowany
| Metryczny | Linie bazowe | Zoptymalizowany | Zmniejszenie |
|---|---|---|---|
| Emitowany CO2 | 1723 kgCO₂ekw | 0,061 kg CO₂ekw | -96,5% |
| Zużyta energia | 4,52 kWh | 1,09 kWh | -75,9% |
| Czas trwania szkolenia | 4,5 godziny | 36 minut | -86,7% |
| Dokładność Val | 91,2% | 90,7% | -0,5% (dopuszczalne) |
| Wykorzystanie GPU | ~30% | ~92% | +62 s |
| Szacunkowy koszt chmury | ~13,50 dolarów | ~1,80 dolara | -86,7% |
Redukcja 96,5% emisji Dowodzi tego dokładność wynosząca zaledwie -0,5%. w większości przypadków ślad węglowy oprogramowania ML może być drastyczny zmniejszona bez utraty jakości modelu. Najważniejsza lekcja: zmierzyć przed optymalizacją.
5 interwencji mających na celu redukcję emisji ML (w kolejności wpływu)
- Zmiana lokalizacji (Niemcy → Francja): główny wkład -85% emisji. Intensywność emisji dwutlenku węgla w sieci elektrycznej jest pojedynczym czynnikiem o największym wpływie. Francja (energia jądrowa, ~56 gCO₂/kWh) vs Niemcy (~381 gCO₂/kWh) dają różnicę 7x, a wszystko inne jest równe.
- Wcześniejsze zatrzymanie: -55% średniego czasu trwania. Większość szkoleń ML ma charakter zbieżny znacznie wcześniej niż skonfigurowana liczba epok. Wczesne zatrzymanie się z cierpliwością 3-5 eliminuje niepotrzebne epoki.
- Zmniejszony rozmiar modelu (BERT-large → DistilBERT): -80% parametrów, -60% czasu. Modele destylowane lub mniejsze często osiągają porównywalną dokładność przy ułamku obliczeń.
- Mieszana precyzja FP16: -30-50% energii GPU. Na karcie graficznej NVIDIA A100, RTX 3090+ i FP16 Jest przyspieszany sprzętowo i prawie o połowę zmniejsza zużycie operacji mnożenia macierzy.
- Optymalna wielkość partii: -75% czasu trwania dla tych samych epok. Optymalna wielkość partii dla dostępnej pamięci VRAM maksymalizuje wykorzystanie procesora graficznego i skraca całkowity czas szkolenia.
Najlepsze praktyki dotyczące systematycznego śledzenia emisji dwutlenku węgla
Zalecana struktura projektu
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
Konwencja nazewnictwa i konfiguracja na podstawie zmiennych środowiskowych
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()
Anty-wzorce, których należy unikać
- Nie śledź przetwarzania wstępnego: Tokenizacja i powiększanie danych może pochłonąć Znaczący procesor. Zawsze uwzględniaj przetwarzanie wstępne w swoim śledzeniu, aby uzyskać pełny zasięg.
- Porównaj przebiegi w różnych krajach: Emisje CO₂ nie są porównywalne bezpośrednio pomiędzy trasami w krajach o różnej intensywności emisji dwutlenku węgla. Normalizuj według kWh lub użyj ustalony kraj dla wszystkich wskaźników referencyjnych.
- Nie wersjonuj .codecarbon.config: Konfiguracja modułu śledzącego musi być wersjonowane w repozytorium, aby zapewnić powtarzalność pomiarów między programistami.
-
Zatrzymaj moduł śledzący w przypadku wyjątku: Zawsze używaj bloku try/finally
lub menedżer kontekstu, aby to zapewnić
tracker.stop()jest również wezwany w przypadku awarii, zachowując częściowe dane. - Traktuj CSV jako ostateczny punkt końcowy: CSV jest punktem wyjścia. Twórz potoki analityczne, aby wydobywać przydatne informacje i porównywać eksperymenty.
Rachunkowość emisji dwutlenku węgla i raportowanie ESG z danymi CodeCarbon
Emisje mierzone przez CodeCarbon są przydatne nie tylko do optymalizacji kodu: reprezentują cenne dane do celów raportowania i zapewniania zgodności z przepisami ESG Dyrektywa CSRD (dyrektywa w sprawie raportowania zrównoważonego rozwoju korporacyjnego), co narzuca duże europejskie przedsiębiorstwa mają obowiązek raportowania emisji cyfrowych począwszy od lat 2025–2026. W tym kontekście CodeCarbon staje się narzędziem do zarządzania, a także narzędziem programistycznym.
Ramy protokołu GHG (protokół gazów cieplarnianych) klasyfikują emisje w trzech zakresach: Zakres 1 (emisje bezpośrednie, np. generatory diesla), Zakres 2 (zakupiona energia elektryczna, która uwzględnia emisję CodeCarbon) e Zakres 3 (łańcuch wartości, np. dostawca chmury i produkt sprzętowy). CodeCarbon obejmuje przede wszystkim emisję Zakresu 2 związaną z obciążeniami obliczeniowymi.
Agregacja danych dla kwartalnych raportów ESG
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"
)
Cele redukcyjne i cele oparte na nauce (SBTi)
Firmy, które przyłączają się do inicjatywy Science Based Targets (SBTi), zobowiązują się do ograniczania emisji zgodnie z celami klimatycznymi Porozumienia paryskiego. W przypadku emisji ICT, zazwyczaj oznacza to roczną redukcję o 4-7%. CodeCarbon może być używany do automatycznego śledzenia postępów w realizacji tych celów.
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 e' 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 i CSRD: co musisz wiedzieć
Dyrektywa CSRD (dyrektywa w sprawie raportowania zrównoważonego rozwoju przedsiębiorstw) nakłada na przedsiębiorstwa europejskie obowiązek zatrudniających powyżej 500 pracowników (od 2025 r.), a następnie do zatrudniających powyżej 250 pracowników (od 2026 r.) raportowanie emisji gazów cieplarnianych zgodnie ze standardami ESRS (European Sustainability Reporting Standards). W przypadku wydań cyfrowych:
- ESRS E1 (zmiana klimatu): Wymaga ujawnienia emisji z zakresu 1, 2 i 3 z metodologią Protokołu GHG. CodeCarbon obejmuje zakres 2 operacji obliczeniowych.
- Materialność: Jeżeli emisje z ICT są istotne (znaczące w porównaniu do całości spółki), należy zgłosić. W przypadku firm technologicznych dzieje się tak prawie zawsze.
- Ścieżki audytu: Pliki CSV CodeCarbon zapewniają szczegółową ścieżkę audytu do weryfikacji raportowanie stron trzecich, niezbędny wymóg wiarygodności raportu ESG.
- Pewność: CSRD wymaga ograniczonej (od 2025 r.) i rozsądnej (od 2028 r.) pewności. Jakość danych CodeCarbon (pomiar bezpośredni a oszacowanie) jest kluczowa.
Wnioski: Pomiar to pierwszy krok
CodeCarbon przekształcił śledzenie emisji gazów cieplarnianych w kodzie z ćwiczenia akademickiego w praktykę inżynieria konkretna i dostępna. Mając mniej niż 10 linii Pythona, każdy analityk danych o Inżynier ML może zacząć mierzyć i określać ilościowo wpływ swojej pracy na środowisko.
Studium przypadku wykazało redukcję 96,5% emisji CO₂ z dokładnością zaledwie -0,5%. Nie jest to przypadek wyjątkowy: jest to norma, gdy zaczynamy od niezoptymalizowanej konfiguracji. Większość szkoleń ML ma ogromne marże optymalizacji emisji dwutlenku węgla, które pozostają niewidoczne do momentu rozpoczęcia pomiarów.
Droga do bardziej zrównoważonego oprogramowania składa się z trzech etapów:
- Mierzyć: Zintegruj CodeCarbon ze wszystkimi przepływami pracy. Zdefiniuj budżet węglowy. Twórz mierzalne wartości bazowe. Spraw, aby emisje były tak widoczne, jak dokładność.
- Być optymistą: Wykorzystaj dane, aby zidentyfikować wąskie gardła związane z emisją dwutlenku węgla. Zmiana lokalizacji, Rozmiar modelu, wielkość partii i mieszana precyzja to najskuteczniejsze dźwignie.
- Zapobiegać: Zintegruj śledzenie emisji dwutlenku węgla z CI/CD. Zdefiniuj budżet węglowy według rodzaju eksperymentu. Użyj pakietu SDK Carbon Aware, aby zautomatyzować przesunięcie lokalizacji/czasu.
W następnym artykule z serii będziemy się nad tym zastanawiać API Climatiqa, narzędzie uzupełniający, który umożliwia obliczenie emisji Protokołu GHG (Zakres 1, 2 i 3) poprzez REST API, integrujące obliczenia z backendem aplikacji do automatycznego raportowania ESG.
Zasoby i przydatne linki
Kontynuacja serii zielonego oprogramowania
- Artykuł 1: Zasady Fundacji Zielonego Oprogramowania — GSF, SCI, 8 zasad
- Artykuł 2 (bieżący): Pomiar śladu węglowego za pomocą CodeCarbon
- Artykuł 3: Zintegruj obliczenia Climatiq API — GHG Protocol z backendem
- Artykuł 4: Pakiet SDK dotyczący emisji dwutlenku węgla – Przesunięcie czasu i lokalizacja
- Artykuł 6: GreenOps z Kubernetesem – Infrastruktura świadoma emisji dwutlenku węgla
- Artykuł 10: Sztuczna inteligencja i węgiel – Pomiar i ograniczanie śladu szkoleń w zakresie uczenia maszynowego







