IA et Énergie : Optimiser la Consommation avec le Prédictif
Les factures d’énergie ont augmenté de 40 à 80 % pour les entreprises françaises entre 2021 et 2024. Dans ce contexte, l’optimisation énergétique n’est plus un sujet RSE — c’est une urgence de compétitivité. Une étude McKinsey de 2024 estime que l’IA prédictive peut réduire la consommation énergétique des bâtiments commerciaux de 15 à 30 % et de l’industrie de 10 à 20 %, sans investissement majeur en infrastructure. Voici comment mettre cette technologie au service de votre PME.
Comprendre les Leviers de l’Optimisation Énergétique par IA
L’IA n’agit pas sur la physique des équipements — elle agit sur le timing et le niveau de leur utilisation. Les trois leviers principaux sont :
- Prédiction de la demande : anticiper le besoin énergétique pour éviter les pics
- Optimisation du pilotage : ajuster en temps réel les consignes CVC, éclairage, process
- Détection d’anomalies : repérer les équipements dégradés qui consomment inutilement
Modéliser la Consommation Énergétique
Collecte des Données — Le Socle de Tout
Un bon modèle prédictif nécessite des données historiques riches. Les compteurs communicants (Linky pour l’électricité, compteurs gaz/eau connectés) fournissent des relevés au pas de 30 minutes — c’est votre point de départ.
import pandas as pd
import numpy as np
from pathlib import Path
def charger_donnees_energie(fichier_csv: str) -> pd.DataFrame:
"""Charger et préparer les données Linky ou compteur industriel."""
df = pd.read_csv(fichier_csv, parse_dates=["horodatage"])
df = df.sort_values("horodatage").reset_index(drop=True)
# Compléter les créneaux manquants (Linky peut avoir des lacunes)
index_complet = pd.date_range(
start=df["horodatage"].min(),
end=df["horodatage"].max(),
freq="30T"
)
df = df.set_index("horodatage").reindex(index_complet)
df["consommation_kwh"] = df["consommation_kwh"].interpolate(method="time")
df.index.name = "horodatage"
return df.reset_index()
def enrichir_features_temporelles(df: pd.DataFrame) -> pd.DataFrame:
"""Ajouter les variables temporelles et météo."""
df = df.copy()
ts = df["horodatage"]
df["heure"] = ts.dt.hour
df["jour_semaine"] = ts.dt.dayofweek
df["mois"] = ts.dt.month
df["est_weekend"] = (df["jour_semaine"] >= 5).astype(int)
df["est_ferie"] = ts.dt.date.apply(est_jour_ferie_fr).astype(int) # Calendrier jours fériés FR
df["trimestre"] = ts.dt.quarter
# Variables cycliques pour capturer la périodicité
df["heure_sin"] = np.sin(2 * np.pi * df["heure"] / 24)
df["heure_cos"] = np.cos(2 * np.pi * df["heure"] / 24)
df["mois_sin"] = np.sin(2 * np.pi * df["mois"] / 12)
df["mois_cos"] = np.cos(2 * np.pi * df["mois"] / 12)
# Lag features (consommation passée = signal fort)
df["conso_lag_1h"] = df["consommation_kwh"].shift(2) # 1h avant
df["conso_lag_24h"] = df["consommation_kwh"].shift(48) # 24h avant
df["conso_lag_1sem"] = df["consommation_kwh"].shift(336) # 1 semaine avant
# Moyenne glissante
df["conso_moy_7j"] = df["consommation_kwh"].rolling(336, min_periods=1).mean()
return df.dropna() Intégrer les Données Météo
La température extérieure est le premier prédicteur de consommation énergétique dans les bâtiments tertiaires (CVC représente 40 à 60 % de la consommation).
import httpx
from datetime import datetime, timedelta
def recuperer_meteo_historique(
latitude: float,
longitude: float,
date_debut: str,
date_fin: str
) -> pd.DataFrame:
"""API Open-Meteo — gratuite jusqu'à 10 000 appels/jour."""
url = "https://archive-api.open-meteo.com/v1/archive"
params = {
"latitude": latitude,
"longitude": longitude,
"start_date": date_debut,
"end_date": date_fin,
"hourly": "temperature_2m,relative_humidity_2m,wind_speed_10m,cloud_cover,direct_radiation",
"timezone": "Europe/Paris"
}
response = httpx.get(url, params=params, timeout=30)
data = response.json()
df_meteo = pd.DataFrame({
"horodatage": pd.to_datetime(data["hourly"]["time"]),
"temperature_c": data["hourly"]["temperature_2m"],
"humidite_pct": data["hourly"]["relative_humidity_2m"],
"vent_kmh": data["hourly"]["wind_speed_10m"],
"couverture_nuageuse": data["hourly"]["cloud_cover"],
"rayonnement_wm2": data["hourly"]["direct_radiation"]
})
# Degree Days de Chauffe (DJC) — indicateur clé pour chauffage
df_meteo["djc"] = (18 - df_meteo["temperature_c"]).clip(lower=0)
# Degree Days de Refroidissement (DJR)
df_meteo["djr"] = (df_meteo["temperature_c"] - 26).clip(lower=0)
return df_meteo Prédire la Consommation — Modèle LSTM
Les séries temporelles de consommation présentent des motifs multi-échelles (heure, jour, semaine, saison) que les réseaux LSTM capturent mieux que les modèles classiques.
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
class DatasetConsommation(Dataset):
def __init__(self, df: pd.DataFrame, sequence_len: int = 48, horizon: int = 24):
"""
sequence_len : historique en entrée (48 = 24h à pas 30 min)
horizon : horizon de prédiction (24 = 12h)
"""
self.X = []
self.y = []
features = df.drop("consommation_kwh", axis=1).values
target = df["consommation_kwh"].values
for i in range(len(df) - sequence_len - horizon):
self.X.append(features[i:i+sequence_len])
self.y.append(target[i+sequence_len:i+sequence_len+horizon])
self.X = torch.FloatTensor(np.array(self.X))
self.y = torch.FloatTensor(np.array(self.y))
def __len__(self): return len(self.X)
def __getitem__(self, idx): return self.X[idx], self.y[idx]
class LSTMConsommation(nn.Module):
def __init__(self, n_features: int, hidden_size: int = 128, horizon: int = 24):
super().__init__()
self.lstm = nn.LSTM(
input_size=n_features,
hidden_size=hidden_size,
num_layers=2,
dropout=0.2,
batch_first=True
)
self.attention = nn.MultiheadAttention(hidden_size, num_heads=4, batch_first=True)
self.fc = nn.Sequential(
nn.Linear(hidden_size, 64),
nn.ReLU(),
nn.Dropout(0.1),
nn.Linear(64, horizon)
)
def forward(self, x):
lstm_out, _ = self.lstm(x)
attn_out, _ = self.attention(lstm_out, lstm_out, lstm_out)
# Prendre le dernier pas de temps
return self.fc(attn_out[:, -1, :])
# Entraînement
n_features = X_train.shape[2]
model = LSTMConsommation(n_features=n_features, hidden_size=128, horizon=24)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)
criterion = nn.HuberLoss() # Robuste aux outliers
for epoch in range(50):
model.train()
for X_batch, y_batch in loader_train:
optimizer.zero_grad()
pred = model(X_batch)
loss = criterion(pred, y_batch)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step() Optimisation CVC — Smart Building
Le Problème du Préchauffage Optimal
Dans un bâtiment de bureaux, le chauffage doit atteindre la consigne à 8h00. Mais le temps de montée en température dépend de la température extérieure, de l’inertie thermique du bâtiment et de l’occupation prévue. L’IA calcule l’heure de démarrage optimale chaque jour.
from scipy.optimize import minimize_scalar
import numpy as np
class OptimiseurCVC:
def __init__(self, modele_thermique, meteo_prevision):
self.modele = modele_thermique
self.meteo = meteo_prevision
def calculer_heure_demarrage(
self,
temperature_actuelle: float,
consigne_confort: float,
heure_occupation: int = 8
) -> int:
"""
Retourne l'heure optimale de démarrage du chauffage (0-7h).
Minimise la consommation tout en garantissant T_confort à T_occupation.
"""
def cout_total(heure_start):
# Simuler la montée en température
temp_finale = self.modele.simuler(
temperature_actuelle,
heure_start,
heure_occupation,
self.meteo.temperature_prevue
)
# Pénalité si consigne non atteinte
penalite = max(0, consigne_confort - temp_finale) * 1000
# Coût énergie = durée × puissance × tarif heure creuse/pleine
tarif = self.meteo.tarif_heure(heure_start)
duree_h = heure_occupation - heure_start
cout_energie = duree_h * 10 * tarif # 10 kW de puissance exemple
return cout_energie + penalite
# Optimiser entre 4h et 7h30
result = minimize_scalar(cout_total, bounds=(4, 7.5), method="bounded")
return result.x
def ajuster_consigne_temps_reel(
self,
temperature_actuelle: float,
occupation_prevue: float, # 0 à 1
heure_actuelle: int
) -> float:
"""Consigne CVC dynamique selon l'occupation réelle."""
# Sans occupation → mode Hors-Gel (8°C)
if occupation_prevue < 0.1:
return 8.0
# Occupation partielle → consigne réduite
elif occupation_prevue < 0.5:
return 18.0
# Pleine occupation → consigne confort
else:
return 20.0 Détection des Équipements Énergivores
Un équipement défectueux (compresseur avec fuite de frigorigène, moteur avec roulement usé) peut consommer 20 à 50 % de plus que la normale. La détection d’anomalie le signale avant la panne.
from sklearn.ensemble import IsolationForest
import pandas as pd
class DetecteurEquipementEnergivore:
def __init__(self, contamination: float = 0.05):
self.modele = IsolationForest(
n_estimators=200,
contamination=contamination, # 5% de points anormaux attendus
random_state=42
)
self.seuil_alerte = None
def entrainer(self, df_normal: pd.DataFrame, cols_features: list):
"""Entraîner sur des données de fonctionnement normal."""
X = df_normal[cols_features].values
self.modele.fit(X)
scores = -self.modele.score_samples(X)
self.seuil_alerte = np.percentile(scores, 95)
print(f"Seuil d'anomalie calibré : {self.seuil_alerte:.3f}")
def detecter(self, df_courant: pd.DataFrame, cols_features: list) -> pd.DataFrame:
X = df_courant[cols_features].values
scores = -self.modele.score_samples(X)
df_courant = df_courant.copy()
df_courant["score_anomalie"] = scores
df_courant["est_anomalie"] = scores > self.seuil_alerte
anomalies = df_courant[df_courant["est_anomalie"]]
if len(anomalies) > 0:
print(f"⚠️ {len(anomalies)} créneaux anormaux détectés !")
print(f"Consommation anormale moyenne : {anomalies['consommation_kwh'].mean():.1f} kWh")
return df_courant
# Usage
detecteur = DetecteurEquipementEnergivore()
detecteur.entrainer(df_normal, ["consommation_kwh", "temperature_c", "occupation"])
# Analyser la semaine courante
df_analyse = detecteur.detecter(df_semaine_courante, ["consommation_kwh", "temperature_c", "occupation"]) Green AI — Réduire l’Empreinte Carbone de l’IA Elle-Même
L’IA consomme elle-même de l’énergie. Un modèle GPT-4 consomme environ 10 Wh par requête. À l’échelle d’une organisation faisant 10 000 requêtes par jour, cela représente 36 MWh/an — équivalent à la consommation annuelle d’un appartement. Quelques bonnes pratiques :
| Optimisation | Économie estimée |
|---|---|
| Utiliser Haiku/Mistral 7B au lieu de GPT-4 quand possible | 80-95 % de l’énergie par requête |
| Mettre en cache les réponses identiques | 30-60 % du volume de requêtes |
| Quantifier les modèles locaux (INT8) | 50-70 % énergie inference |
| Entraîner en heures creuses (énergie renouvelable) | 20-40 % émissions CO2 |
| Utiliser des régions cloud à énergie verte | 50-90 % émissions CO2 |
import anthropic
import hashlib
import json
from pathlib import Path
class CacheRequetesLLM:
"""Cache disque pour éviter les appels LLM redondants."""
def __init__(self, dossier_cache: str = ".llm_cache"):
self.dossier = Path(dossier_cache)
self.dossier.mkdir(exist_ok=True)
self.client = anthropic.Anthropic()
self.hits = 0
self.misses = 0
def _cle_cache(self, prompt: str, modele: str) -> str:
return hashlib.sha256(f"{modele}:{prompt}".encode()).hexdigest()
def generer(self, prompt: str, modele: str = "claude-3-5-haiku-20241022") -> str:
cle = self._cle_cache(prompt, modele)
fichier_cache = self.dossier / f"{cle}.json"
if fichier_cache.exists():
self.hits += 1
return json.loads(fichier_cache.read_text())["reponse"]
self.misses += 1
response = self.client.messages.create(
model=modele,
max_tokens=512,
messages=[{"role": "user", "content": prompt}]
)
reponse = response.content[0].text
fichier_cache.write_text(json.dumps({"reponse": reponse, "modele": modele}))
return reponse
def stats(self):
total = self.hits + self.misses
print(f"Taux de cache : {self.hits/total:.0%} ({self.hits} hits / {self.misses} misses)")
print(f"Économie estimée : {self.hits * 0.01:.2f} € (à 0,01€/requête)") ROI — Chiffrer les Gains pour une PME
Exemple : Immeuble de Bureaux 3 000 m²
| Action IA | Économie estimée | Investissement | Retour |
|---|---|---|---|
| Prédiction CVC + pilotage optimal | 18 000 €/an | 8 000 € | 5 mois |
| Détection équipements énergivores | 6 000 €/an | 3 000 € | 6 mois |
| Optimisation éclairage (présence) | 4 000 €/an | 2 500 € | 7 mois |
| Total | 28 000 €/an | 13 500 € | 6 mois |
Exemple : Atelier Industriel 500 kW installés
Un process industriel avec 500 kW de puissance installée et 4 000 heures/an de fonctionnement consomme environ 2 000 MWh/an. À 0,15 €/kWh, la facture annuelle atteint 300 000 €. Une réduction de 15 % par optimisation IA = 45 000 €/an d’économie.
Conclusion
L’IA prédictive appliquée à l’énergie est l’un des retours sur investissement les plus rapides dans le domaine de l’intelligence artificielle : les modèles s’appuient sur des données déjà disponibles (compteurs communicants, capteurs existants), les gains sont mesurables immédiatement, et les retours sur investissement s’atteignent en 6 à 12 mois dans la plupart des cas.
La démarche recommandée : commencer par la modélisation de la consommation sur 12 mois d’historique, identifier les 3 postes les plus énergivores, déployer un premier modèle prédictif sur ces postes, mesurer les gains sur 3 mois, puis étendre.
Vos factures d’énergie explosent et vous cherchez des leviers d’action concrets ? Contactez-nous à contact@brio-novia.eu — nous réalisons un audit énergétique augmenté par IA et identifions avec vous les économies atteignables dès les 6 prochains mois.