The Environmental Cost of AI
AI's growing capabilities come with a growing environmental footprint. Understanding and managing this cost is an emerging responsibility for AI practitioners.
Staggering Numbers
| Model / Task | Estimated CO₂ equivalent | Comparison |
|---|---|---|
| GPT-3 training | ~552 tonnes CO₂e | ~110 round-trip flights NYC→SF |
| GPT-4 training (est.) | ~5,000–15,000 tonnes CO₂e | Small country's monthly emissions |
| BERT-large training | ~1.5 tonnes CO₂e | One transatlantic flight |
| Average inference call (GPT-4) | ~0.001–0.01 kg CO₂e | Leaving a light on for 30 seconds |
| Annual AI inference (global) | Growing rapidly — comparable to aviation industry |
Training is a one-time cost; inference at scale dominates lifetime emissions.
Where the Energy Goes
AI Carbon Footprint
├─ Training: hardware (GPU/TPU), cooling, networking (one-time, but huge)
├─ Inference: production serving, data center overhead (continuous)
├─ Data collection and processing: transfer, storage, preprocessing
└─ Development iterations: experiments, hyperparameter searches, ablations
Jevons Paradox: efficiency improvements often increase total consumption — cheaper inference → more AI use → more total energy.
Carbon Intensity Varies by Location
The same computation can have 10–50× different carbon impact depending on where it runs:
| Location | Grid Carbon Intensity (gCO₂/kWh) |
|---|---|
| Iceland (geothermal) | ~10–30 |
| Norway (hydro) | ~15–30 |
| France (nuclear) | ~55–70 |
| Germany (mixed) | ~350–430 |
| US (average) | ~380–430 |
| India (coal-heavy) | ~700–800 |
| Poland (coal-heavy) | ~750–850 |
Green AI practice: schedule batch training jobs to run during hours of low carbon intensity, or in low-carbon data center regions.
Measuring AI Energy Consumption
Key Metrics
- = energy consumed by hardware (kWh)
- = Power Usage Effectiveness (data center overhead: cooling, UPS, lighting); typically 1.1–1.5
- = grid carbon intensity (kg CO₂/kWh) at the location
FLOPs (Floating Point Operations) can estimate energy:
But FLOP estimates alone are insufficient — real-world hardware utilization, memory bandwidth, and data movement also contribute significantly.
Model Efficiency Scaling
A key insight from the Chinchilla and related papers: larger models run on cheaper hardware with more tokens are more efficient than smaller models on expensive hardware. For inference:
- Model size → VRAM requirement → hardware cost
- Inference latency → throughput → cost per request
- Quantization can reduce both with minimal accuracy loss
Carbon Reporting Standards
- GHG Protocol (Scope 1, 2, 3): Scope 2 = purchased electricity for training; Scope 3 = supply chain emissions (hardware manufacturing)
- ISO 14001: Environmental Management System — complementary to ISO 42001
- EU Corporate Sustainability Reporting Directive (CSRD): large companies must report AI-related emissions from 2025+
- ML-specific: MLPerf, ML CO₂ Impact calculator, CodeCarbon library
Model Compression Techniques
1. Quantization
Reduce numerical precision of weights:
| Precision | Bits per weight | Memory reduction | Accuracy loss |
|---|---|---|---|
| FP32 | 32 | 1× (baseline) | None |
| FP16 | 16 | 2× | Negligible (<0.1%) |
| INT8 | 8 | 4× | Small (0.1–1%) |
| INT4 | 4 | 8× | Moderate (1–3%) |
| INT1 | 1 | 32× | Significant |
Post-training quantization (PTQ): quantize after training — no retraining needed Quantization-aware training (QAT): simulate quantization during training — better accuracy
2. Pruning
Remove weights or neurons with small magnitude:
- Unstructured pruning: remove individual weights (creates sparse matrices) — hard to accelerate without specialized hardware
- Structured pruning: remove entire filters/heads/layers — directly reduces computation
- Iterative magnitude pruning: train → prune 10% weakest weights → retrain → repeat
50–90% of weights can often be pruned with <1% accuracy loss.
3. Knowledge Distillation
Train a small student model to mimic a large teacher model:
- : teacher and student logits
- : temperature (higher = softer probability distribution = more information)
- The student learns from the soft labels (probability distribution over classes) which contain more information than hard labels
Examples: DistilBERT (40% smaller, 97% performance), TinyBERT, MobileBERT
4. Efficient Architectures
| Architecture | Key Innovation | Speedup |
|---|---|---|
| MobileNet v3 | Depthwise separable convolutions | 5–10× vs ResNet |
| EfficientNet | Compound scaling (depth + width + resolution) | Best accuracy/FLOP tradeoff |
| DistilBERT | Distilled from BERT-base, 6 layers | 60% faster |
| Flash Attention | Memory-efficient exact attention | 2–4× training speedup |
| Mixture of Experts (MoE) | Activate only a subset of parameters per token | Larger model, same inference cost |
# pip install codecarbon torch transformers
# ─── 1. Carbon Tracking with CodeCarbon ──────────────────────
from codecarbon import EmissionsTracker, OfflineEmissionsTracker
import time
# Online tracker (auto-detects location and uses live grid intensity)
tracker = EmissionsTracker(
project_name="loan_model_training",
output_dir="./carbon_reports",
save_to_file=True,
log_level="warning",
country_iso_code="USA", # fallback if geo-detection fails
)
tracker.start()
# Simulate a training workload
import numpy as np
X = np.random.randn(10000, 50)
y = (X[:, 0] > 0).astype(int)
from sklearn.ensemble import GradientBoostingClassifier
model = GradientBoostingClassifier(n_estimators=200, max_depth=5)
model.fit(X, y)
time.sleep(0.5) # simulate additional work
emissions = tracker.stop()
print(f"\nCarbon Tracking Report:")
print(f" CO₂ equivalent: {emissions * 1000:.4f} g CO₂e")
print(f" Energy consumed: approximately proportional to training time")
print(f" Report saved to: ./carbon_reports/emissions.csv")
# Offline tracker for environments without internet
offline_tracker = OfflineEmissionsTracker(
project_name="inference_benchmark",
country_iso_code="DEU", # Germany — higher carbon intensity
country_2letter_iso_code="DE",
)
# ─── 2. Model Efficiency Comparison ──────────────────────────
import torch
import torch.nn as nn
import time
class LargeModel(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(512, 2048), nn.ReLU(), nn.Dropout(0.2),
nn.Linear(2048, 2048), nn.ReLU(), nn.Dropout(0.2),
nn.Linear(2048, 1024), nn.ReLU(),
nn.Linear(1024, 10),
)
def forward(self, x): return self.net(x)
class SmallModel(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(512, 256), nn.ReLU(),
nn.Linear(256, 128), nn.ReLU(),
nn.Linear(128, 10),
)
def forward(self, x): return self.net(x)
def count_parameters(model):
return sum(p.numel() for p in model.parameters())
def benchmark_inference(model, n_samples=1000, batch_size=64, n_runs=5):
x = torch.randn(batch_size, 512)
times = []
model.eval()
with torch.no_grad():
for _ in range(n_runs):
start = time.time()
for _ in range(n_samples // batch_size):
_ = model(x)
times.append(time.time() - start)
return np.mean(times)
large = LargeModel()
small = SmallModel()
large_params = count_parameters(large)
small_params = count_parameters(small)
large_time = benchmark_inference(large)
small_time = benchmark_inference(small)
print(f"\nModel Efficiency Comparison:")
print(f" Large model: {large_params:,} params, {large_time*1000:.1f}ms/1000 inferences")
print(f" Small model: {small_params:,} params, {small_time*1000:.1f}ms/1000 inferences")
print(f" Parameter reduction: {large_params/small_params:.1f}×")
print(f" Speed improvement: {large_time/small_time:.1f}×")
# ─── 3. Post-Training Quantization ───────────────────────────
# FP32 → INT8 with PyTorch dynamic quantization
quantized_model = torch.quantization.quantize_dynamic(
large,
{nn.Linear}, # quantize Linear layers
dtype=torch.qint8,
)
def get_model_size_mb(model):
import io
buf = io.BytesIO()
torch.save(model.state_dict(), buf)
return buf.tell() / 1024 / 1024
original_size = get_model_size_mb(large)
quantized_size = get_model_size_mb(quantized_model)
quant_time = benchmark_inference(quantized_model)
print(f"\nQuantization (FP32 → INT8):")
print(f" Original size: {original_size:.2f} MB")
print(f" Quantized size: {quantized_size:.2f} MB ({100*(1-quantized_size/original_size):.0f}% smaller)")
print(f" Speed change: {large_time/quant_time:.2f}× {'faster' if quant_time < large_time else 'slower'}")
# ─── 4. Magnitude Pruning ─────────────────────────────────────
import torch.nn.utils.prune as prune
prunable_model = LargeModel()
# Apply unstructured L1 pruning (remove 50% of weights in each linear layer)
for name, module in prunable_model.named_modules():
if isinstance(module, nn.Linear):
prune.l1_unstructured(module, name='weight', amount=0.5)
prune.remove(module, 'weight') # make pruning permanent
# Count remaining non-zero parameters
total_params = sum(p.numel() for p in prunable_model.parameters())
nonzero_params = sum((p != 0).sum().item() for p in prunable_model.parameters())
sparsity = 1 - nonzero_params / total_params
print(f"\nMagnitude Pruning (50% per layer):")
print(f" Total parameters: {total_params:,}")
print(f" Non-zero parameters: {nonzero_params:,}")
print(f" Sparsity: {100*sparsity:.1f}%")
# ─── 5. Knowledge Distillation ───────────────────────────────
import torch.optim as optim
import torch.nn.functional as F
teacher = LargeModel()
student = SmallModel()
teacher.eval()
optimizer = optim.Adam(student.parameters(), lr=1e-3)
# Distillation training loop
temperature = 4.0
alpha = 0.7 # weight for distillation loss vs hard label loss
X_train = torch.randn(500, 512)
y_hard = torch.randint(0, 10, (500,))
dataset = torch.utils.data.TensorDataset(X_train, y_hard)
loader = torch.utils.data.DataLoader(dataset, batch_size=64, shuffle=True)
print(f"\nKnowledge Distillation (T={temperature}, α={alpha}):")
for epoch in range(5):
student.train()
total_loss = 0
for bx, by in loader:
optimizer.zero_grad()
with torch.no_grad():
teacher_logits = teacher(bx)
student_logits = student(bx)
# Hard label loss (cross-entropy with ground truth)
hard_loss = F.cross_entropy(student_logits, by)
# Soft label loss (KL divergence with temperature-softened teacher)
soft_teacher = F.softmax(teacher_logits / temperature, dim=-1)
soft_student = F.log_softmax(student_logits / temperature, dim=-1)
distill_loss = F.kl_div(soft_student, soft_teacher, reduction='batchmean') * (temperature ** 2)
loss = (1 - alpha) * hard_loss + alpha * distill_loss
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f" Epoch {epoch+1}: loss={total_loss/len(loader):.4f}")
print(f"\nStudent model: {count_parameters(student):,} params ({count_parameters(student)/count_parameters(teacher)*100:.0f}% of teacher)")
# ─── 6. Green AI Report ──────────────────────────────────────
def green_ai_report(project_name, model_type, params, training_co2_g,
inference_calls_per_day, inference_co2_per_call_ug):
daily_inference_co2 = inference_calls_per_day * inference_co2_per_call_ug / 1e6
annual_co2_kg = (training_co2_g / 1000) + (daily_inference_co2 * 365)
trees_needed = annual_co2_kg / 21 # average tree absorbs ~21 kg CO₂/year
print(f"\n{'='*50}")
print(f"GREEN AI REPORT: {project_name}")
print(f"{'='*50}")
print(f" Model type: {model_type}")
print(f" Parameters: {params:,}")
print(f" Training CO₂: {training_co2_g:.1f}g CO₂e")
print(f" Inference CO₂: {inference_co2_per_call_ug:.0f}μg CO₂e per call")
print(f" Daily inference CO₂: {daily_inference_co2*1000:.1f}g CO₂e")
print(f" Annual total CO₂: {annual_co2_kg:.2f}kg CO₂e")
print(f" Trees to offset: {trees_needed:.1f} trees/year")
print(f" Reduction strategies: quantization, distillation, efficient serving")
green_ai_report("LoanScoreAI v3.2", "XGBoost (1000 estimators)",
params=1_000_000, training_co2_g=45.2,
inference_calls_per_day=50_000, inference_co2_per_call_ug=120)Sustainable AI Development Practices
Compute-Aware Experimentation
Before running large experiments:
- Pilot on small scale: verify approach on 1% of data/compute
- Early stopping: don't train to convergence when validation shows no improvement
- Hyperparameter search efficiency: use Bayesian optimization (Optuna, W&B Sweeps) instead of grid search — finds good configs with 5–10× fewer runs
- Pre-trained models: fine-tune rather than train from scratch — 100–1000× less compute
Infrastructure Choices
| Choice | Carbon Impact |
|---|---|
| Cloud region | Choose low-carbon regions (e.g., Oregon, Iowa for AWS; Northern Europe for Azure/GCP) |
| Carbon-aware scheduling | Use ElectricityMaps API to run batch jobs when grid is cleanest |
| Spot/preemptible instances | Use idle capacity; reduces effective carbon by filling otherwise unused compute |
| Right-sizing | Match GPU/memory to actual need; avoid over-provisioning |
| Model serving | Batch requests; use quantized models; scale to zero when idle |
Reporting and Accountability
ISO 42001 does not yet have a specific control for environmental impact, but leading organizations align with:
- GHG Protocol Scope 2: report electricity-based emissions from AI compute
- Science Based Targets initiative (SBTi): commit to net-zero AI emissions aligned with 1.5°C pathways
- Green Software Foundation Principles: energy efficiency, hardware efficiency, carbon awareness
The Performance-Efficiency Trade-off
There is no universal right answer — the decision depends on context:
| Application | Justifiable High Compute | Not Justifiable |
|---|---|---|
| Medical AI saving lives | Yes — performance paramount | Excessive experimentation for marginal gains |
| Content recommendation | Moderate — balance with revenue | Training 100B model for 0.1% CTR improvement |
| Research frontier | Yes — advancing knowledge | Duplicating experiments already published |
| Production inference | No — efficiency is priority | Running FP32 when INT8 gives same quality |
Knowledge check
A team trains two models with equivalent accuracy: Model A (BERT-large fine-tuned, 340M params) and Model B (DistilBERT fine-tuned, 66M params). For a production system serving 10 million requests per day, which is the more responsible choice?
Summary
- Training large models emits significant CO₂: GPT-3 training produced ~552 tonnes CO₂e; at scale, inference dominates lifetime emissions
- Carbon intensity varies 50× by location: running compute in Iceland vs Poland makes a 25–50× difference in carbon impact
- Quantization (FP32→INT8) reduces model size 4× with minimal accuracy loss; pruning removes 50–90% of weights; knowledge distillation trains a small student to match a large teacher
- CodeCarbon provides easy-to-integrate carbon tracking for any Python ML workload
- Green AI practices: use pre-trained models, early stopping, Bayesian hyperparameter search, carbon-aware scheduling, and right-sized infrastructure
- Sustainability reporting will become mandatory under EU CSRD for large companies — start tracking now
Next: Testing, Auditing & Data Hygiene — the operational playbook for maintaining responsible AI in practice.