Skip to content
SDB
Responsible AI

Chapter 08 · intermediate · 26 min

Sustainability & Green AI

Carbon footprint of AI, energy-efficient training, model compression, and measuring environmental impact

Subhendu Datta BhowmikAI Tutorials

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 / TaskEstimated CO₂ equivalentComparison
GPT-3 training~552 tonnes CO₂e~110 round-trip flights NYC→SF
GPT-4 training (est.)~5,000–15,000 tonnes CO₂eSmall country's monthly emissions
BERT-large training~1.5 tonnes CO₂eOne transatlantic flight
Average inference call (GPT-4)~0.001–0.01 kg CO₂eLeaving 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:

LocationGrid 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

CO2e=E×PUE×I\text{CO}_2\text{e} = E \times PUE \times I

  • EE = energy consumed by hardware (kWh)
  • PUEPUE = Power Usage Effectiveness (data center overhead: cooling, UPS, lighting); typically 1.1–1.5
  • II = grid carbon intensity (kg CO₂/kWh) at the location

FLOPs (Floating Point Operations) can estimate energy: EFLOPshardware efficiency (FLOP/J)E \approx \frac{\text{FLOPs}}{\text{hardware efficiency (FLOP/J)}}

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:

PrecisionBits per weightMemory reductionAccuracy loss
FP32321× (baseline)None
FP1616Negligible (<0.1%)
INT88Small (0.1–1%)
INT44Moderate (1–3%)
INT1132×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:

LKD=(1α)LCE(y,y^s)+αLKL(σ(zt/T),σ(zs/T))\mathcal{L}_{KD} = (1-\alpha) \mathcal{L}_{CE}(y, \hat{y}_s) + \alpha \mathcal{L}_{KL}(\sigma(z_t/T), \sigma(z_s/T))

  • zt,zsz_t, z_s: teacher and student logits
  • TT: 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

ArchitectureKey InnovationSpeedup
MobileNet v3Depthwise separable convolutions5–10× vs ResNet
EfficientNetCompound scaling (depth + width + resolution)Best accuracy/FLOP tradeoff
DistilBERTDistilled from BERT-base, 6 layers60% faster
Flash AttentionMemory-efficient exact attention2–4× training speedup
Mixture of Experts (MoE)Activate only a subset of parameters per tokenLarger model, same inference cost
Carbon Tracking, Quantization, Pruning, and Knowledge Distillationpython
# 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:

  1. Pilot on small scale: verify approach on 1% of data/compute
  2. Early stopping: don't train to convergence when validation shows no improvement
  3. Hyperparameter search efficiency: use Bayesian optimization (Optuna, W&B Sweeps) instead of grid search — finds good configs with 5–10× fewer runs
  4. Pre-trained models: fine-tune rather than train from scratch — 100–1000× less compute

Infrastructure Choices

ChoiceCarbon Impact
Cloud regionChoose low-carbon regions (e.g., Oregon, Iowa for AWS; Northern Europe for Azure/GCP)
Carbon-aware schedulingUse ElectricityMaps API to run batch jobs when grid is cleanest
Spot/preemptible instancesUse idle capacity; reduces effective carbon by filling otherwise unused compute
Right-sizingMatch GPU/memory to actual need; avoid over-provisioning
Model servingBatch 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:

ApplicationJustifiable High ComputeNot Justifiable
Medical AI saving livesYes — performance paramountExcessive experimentation for marginal gains
Content recommendationModerate — balance with revenueTraining 100B model for 0.1% CTR improvement
Research frontierYes — advancing knowledgeDuplicating experiments already published
Production inferenceNo — efficiency is priorityRunning 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.

Responsible AI