12.4. TP Prédiction météo par IA#

Marc Buffat Dpt mécanique, UCB Lyon 1

meteonet

12.4.1. Objectifs#

  • prévision complète de la météo par apprentissage (machine learning)

  • comparaison et validation de la prédiction.

  • utilisation de la librairie keras de tensor-flow (neural network)

  • Réseaux de neurones récurrents (RNN)

Les réseaux de neurones récurrents (RNN) sont une classe de réseaux de neurones puissants pour modéliser des données de séquence. Schématiquement, une couche RNN utilise une boucle for pour itérer sur les pas de temps d’une séquence, tout en maintenant un état interne qui code pour des informations sur les pas de temps qu’il a vu jusqu’à présent.

L’objectif est d’avoir une prédiction de la méteo du jour en utilisant les mesures du jour précédent

  • prédiction de la température par heure

A la fin du notebook, vous devez utiliser la commende pour générer la version html de votre compte tendu:

 !genereTPhtml CompteRendu
%matplotlib inline
#algèbre linéaire
import numpy as np
import os, time
import pandas as pd
#représentation des résultats
import matplotlib.pyplot as plt
import seaborn as sns
from IPython.display import display,Markdown
def printmd(text):
    display(Markdown(text))
plt.rc('font', family='serif', size='18')
from validation.validation import info_etudiant, bib_validation, test_function
from validation.valide_markdown import test_markdown, test_code
try: NUMERO_ETUDIANT
except NameError: NUMERO_ETUDIANT = None 
if type(NUMERO_ETUDIANT) is not int :
    printmd("**ERREUR:** numéro d'étudiant non spécifié!!!")
    NOM, PRENOM, NUMERO_ETUDIANT = info_etudiant()
    #raise AssertionError("NUMERO_ETUDIANT non défini")
# parametres spécifiques
_uid_    = NUMERO_ETUDIANT
np.random.seed(_uid_)
_station_test_  = 69029001  # Lyon Bron aeroport
_stations_ = [69299001, 69028001, 1089001, 42005001, 73329001 ]
_station_train_ = _stations_[np.random.randint(len(_stations_))]
# mois d'etude
_station_train_ = 69028001  # Brindas
_mois_ = np.random.randint(1,13)
printmd("**Login étudiant {} {} uid={}**".format(NOM,PRENOM,_uid_))
printmd(f"Station méteo pour l'apprentissage: {_station_train_}")
printmd(f"Station méteo pour le test (Lyon Bron): {_station_test_}")
printmd(f"Etude pour les mois {_mois_} à {_mois_+1} de 2016")

ERREUR: numéro d’étudiant non spécifié!!!

Login étudiant Marc BUFFAT uid=137764122

Station méteo pour l’apprentissage: 69028001

Station méteo pour le test (Lyon Bron): 69029001

Etude pour les mois 3 à 4 de 2016

12.4.2. Configuration tensor flow sur serveur NVIDIA#

# init tensorflow
from validation.libIA_GPU import Init_tensorflowGPU

Init_tensorflowGPU(memlim = 2*1024, gpunum = NUMERO_ETUDIANT % 2)
LD_LIBRARY_PATH= /home/buffat/venvs/jupyter/lib/python3.10/site-packages/nvidia/cudnn/lib:/usr/local/cuda/lib64
2026-07-21 15:39:57.987808: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.
2026-07-21 15:39:57.992117: I external/local_xla/xla/tsl/cuda/cudart_stub.cc:32] Could not find cuda drivers on your machine, GPU will not be used.
2026-07-21 15:39:58.003346: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
E0000 00:00:1784641198.021714  446462 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
E0000 00:00:1784641198.026833  446462 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
W0000 00:00:1784641198.041077  446462 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.
W0000 00:00:1784641198.041094  446462 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.
W0000 00:00:1784641198.041097  446462 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.
W0000 00:00:1784641198.041099  446462 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.
2026-07-21 15:39:58.045794: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
Attention: no GPU available!
E0000 00:00:1784641199.996156  446462 cuda_executor.cc:1228] INTERNAL: CUDA Runtime error: Failed call to cudaGetRuntimeVersion: Error loading CUDA libraries. GPU will not be used.: Error loading CUDA libraries. GPU will not be used.
W0000 00:00:1784641200.002947  446462 gpu_device.cc:2341] Cannot dlopen some GPU libraries. Please make sure the missing libraries mentioned above are installed properly if you would like to use GPU. Follow the guide at https://www.tensorflow.org/install/gpu for how to download and setup the required libraries for your platform.
Skipping registering GPU devices...
#Machine learning
from sklearn import preprocessing
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM
from tensorflow.keras.layers import Dense
from sklearn.model_selection import train_test_split
from sklearn.preprocessing   import MinMaxScaler

12.4.3. Base de données meteo#

utilisation de la base de données météonet

12.4.3.1. structure des données des stations meteo (mesure / 5 minutes)#

Paramètres de métadonnées

  • number_sta : numéro de la station au sol

  • lat : latitude en degrés décimaux

  • lon : longitude en degrés décimaux

  • height_sta : hauteur de la station en mètres

Le paramètre date est un objet datetime au format “AAAA-MM-JJ HH:mm:ss”.

Paramètres météorologiques

dd : direction du vent en degrés
ff : vitesse du vent en m.s-1
precip : précipitations durant la période de référence en kg.m2
hu : humidité en %
td : température du point de rosée en Kelvin
t : température en Kelvin
psl : pression réduite au niveau de la mer en Pa

température du point de rosée: td (wikipedia)

Le point de rosée ou température de rosée est la température sous laquelle de la rosée se dépose naturellement. Plus techniquement, en dessous de cette température qui dépend de la pression et l’humidité ambiantes, la vapeur d’eau contenue dans l’air se condense sur les surfaces, par effet de saturation.

En météorologie, la température de rosée est parfois utilisée pour estimer le niveau de confort, comme une température ressentie. La température de rosée détermine en effet si la transpiration s’évaporera de la peau, causant ainsi un rafraîchissement de l’organisme. Contrairement à la température, qui varie généralement considérablement entre le jour et la nuit, les points de rosée varient plus lentement. Ainsi, bien que la température puisse chuter la nuit, une journée lourde est généralement suivie d’une nuit lourde.

précipitation: precip (wikipedia)

la dimension des données “precip” est kg/m^2 sur 5 min (une mesure toutes les 5 minutes).

On la convertit en mm d’eau par m^2 et par heure (en xiant par 12)

La quantité de pluie tombée se mesure en mm (millimètre). Un mm représente un litre d’eau tombé par mètre carré. Selon les situations, la pluie tombe avec différentes intensités.

  • pluie faible: < 2.5 mm / heure

  • pluie modérée < 7.5 mm / heure

  • pluie forte > 7.5 mm / heure

12.4.3.2. Lecture base de données#

Lecture de la base de test et d’apprentissage:

  • dans le répertoire /home/cours/DatabaseIA/data_meteonet/SE/ground_stations/

  • sous dossier et fichier csv:

    • annee/SE{annee}_{mois}.csv

    • 2016/SE2016_01.csv pour janvier 2016 (attention au format)

  • lire la base de données pour les 2 mois consécutifs donnés

  • création de

  • création de 2 dataset panda:

    • data_train

    • data_test

#importation de notre dataset sous panda
directory = "/data/DatabaseIA_cours/data_meteonet/SE/ground_stations/"
data = None
station_test  = _station_test_
station_train = _station_train_
printmd(f"## Base de données de test {station_test} et d'entrainement {station_train}")
data_test = data_train = None
### BEGIN SOLUTION
### END SOLUTION
if data_test is not None:
    data_test.info()
    data_train.info()

Base de données de test 69029001 et d’entrainement 69028001

12.4.3.3. Information sur les 2 stations méteo#

on a une base de données pour 2 stations météo :

  • la station de test qui est la station la plus proche de Lyon

  • une station d’apprentissage

En allant sur le site météo France (menu informations sur les stations)

déterminer les caractéristiques de ces 2 stations météo

On déterminera en particulier la distance entre les 2 stations météo

geodesique

12.4.3.3.1. calcul de la distance des stations méteo à Lyon 1 (géodésique)#

La géodésique est la trajectoire correspondant à la distance minimale entre deux points sur une surface. Dans le cas de la sphère, c’est un arc de grand cercle.Connaissant la position de deux points A et B sur une sphère, calculer la distance entre eux revient donc à calculer l’abscisse curviligne S (AB) sur le grand cercle passant par A et B.

geodesique

Si l’on considère deux points A et B sur la sphère, de latitudes \(\phi_A\) et \(\phi_B\) et de longitudes \(\lambda_A\) et \(\lambda_B\), alors la distance angulaire en radians entre A et B est donnée par la relation fondamentale de trigonométrie sphérique:

\[ S_{AB} = \arccos (\sin \phi_A \sin \phi_B + \cos \phi_A \cos \phi_B \cos(\lambda_B - \lambda_A)) \]

La distance S en mètres, s’obtient en multipliant \(S_{AB}\) par un rayon de la Terre conventionnel (6 378 137 mètres par exemple).

# calcul de la distance en km entre les 2 stations
dist_station = None

### BEGIN SOLUTION
### END SOLUTION

12.4.4. Nettoyage de la BD pour ces stations#

  • indexation des base de données avec la date

  • élimination des données inutiles: colonnes de position, hauteur, ou non utilisés vent, pression

    • “number_sta”, “lat”, “lon”, “height_sta”,”ff”,”dd”,”psl”

  • interpole les données dans les colonnes utilisées pour les données manquantes

    • “td” , “precip” , “hu”, “t”

def print_info(dataset,titre=""):
    """affiche information sur dataset"""
    N = len(dataset)
    display(dataset.info())
    print(f"Taille des donnees N={N} et % donnees manquantes. {titre}")
    display(dataset.isna().sum())
    display(dataset.describe(percentiles=[]))
    return
# nettoyage des 2 BD data_test et data_train

### BEGIN SOLUTION
### END SOLUTION

12.4.5. Préparation de la base de données#

A partir de ces 2 bases de données, nous allons maintenant construire les données pour l’apprentissage

On veut faire une prédiction sur la température

  • on sélectionne les données de température: colonne “t”

  • on normalise les données entre 0 et 1 en utilisant la même normalisation (basé sur la BD de training)

  • on ré-échantillonne la série pour avoir des données par heure (et non toutes les 5 minutes)

les 2 séries de données sont mises dans T_train et T_test

# colonne de donnees a analyser
col = 't'
T_train_norm = T_test_norm  = None
T_train = T_test = None
Tmin = None
Tmax = None
### BEGIN SOLUTION
### END SOLUTION

12.4.5.1. Tracés des 2 séries de températures#

sur la figure suivante,on trace les données pour vérifier la validité de l’approche

if T_train is not None:
    plt.figure(figsize=(14,6))
    ax = plt.gca()
    T_train.plot(ax=ax,label="training")
    T_test.plot(ax=ax,label="test")
    plt.title("Base de données de température adimen. ")
    plt.legend();

12.4.6. Création du modèle d’IA#

pour cet apprentissage en temps:

  • on veut prédire le temps du lendemain, connaissant le temps du jour

  • les données d’entrée sont donc des mesures de température sur 24h (une par heure)

  • en sortie on a l’évolution de la température sur les 24h suivantes (une par heure)

Il faut donc segmenter la base de données pour créer des segments de données de 24 valeurs de température consécutive, et des segments de résultats associées des 24 valeurs suivantes.

lstm_window

On note:

  • Nh le nombre de points d’histoire à considérer

  • Np le nombre de points à prédire

Effectuer cette segmentation des données pour construire les tableaux suivants

  - X_entrainement, Y_entrainement pour le traing
  - X_test, Y_test   pour le test
#Selection du nombre de points de donnés (histoire) et le nombre en prédiction
Nh = 24
Np = 24
if T_train is not None:
    print("taille BD:",T_train.shape, T_test.shape," et intervalles : ", Nh,Np)
X_entrainement= None; Y_entrainement = None
X_test= None;  Y_test = None
### BEGIN SOLUTION
### END SOLUTION
# visualisation des données sur 2 sequences la 1ere et la 50ième
if X_test is not None: print("dimension DB :",X_test.shape)
if X_entrainement is not None:
    plt.figure(figsize=(12,6))
    t = np.arange(X_entrainement.shape[0])
    for i in [0,49]:
        plt.scatter(t[i],X_entrainement[i,0,0])
        plt.plot(t[i:i+Nh],X_entrainement[i,:,0],label=f"seq {i}")
        plt.scatter(t[i+Nh:t[i+Nh+Np]],Y_entrainement[i,:],marker='x',c='k')
    plt.title(f"séquences de {Nh} valeurs")
    plt.legend();

12.4.7. Apprentissage#

utilisation d’un réseaux de neurones récurrents RNN de type LSTM avec la bibliothéque keras

# construction du modele LSTM
model = Sequential([
    LSTM(Nh),
    Dense(Np),
])
#Configuration du modèle(on minimise avec la méthode des moindres carrés)
model.compile(optimizer='adam', loss='mse')
if X_entrainement is not None:
    print("Taille de la base de données training:",X_entrainement.shape, Y_entrainement.shape)
    print("Taille de la base de données test    :",X_test.shape, Y_test.shape)
!nvidia-smi
Tue Jul 21 15:40:00 2026       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.157                Driver Version: 390.157                   |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce 605         Off  | 00000000:01:00.0 N/A |                  N/A |
| 40%   48C    P8    N/A /  N/A |    450MiB /   957MiB |     N/A      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0                    Not Supported                                       |
+-----------------------------------------------------------------------------+

12.4.7.1. paramètres d’apprentissage#

  • Epoch dans le contexte de l’entraînement d’un modèle, l’epoch est un terme utilisé pour référer à une itération où le modèle voit tout le training set pour mettre à jour ses coefficients.

  • mae Calcule la moyenne de la différence absolue entre les résultats et les prédictions.

  • loss Le but des fonctions de perte (loss) est de calculer la quantité qu’un modèle doit chercher à minimiser pendant l’apprentissage

  1. lancer l’apprentissage du modèle sur la base de training,

  2. on notera history le résultat de la fonction d’apprentissage (fit)

  3. puis faire la prédiction du la base de test

On notera YPred la prédiction sur la base de test. Pour comparer cette prédication, on remettra (avec la méthode reshape) le tableau Y_test sous la même forme que YPred

On pourra jouer sur la paramètre EPOQUE, nbre d’itération du modèle d’apprentissage en temps

#Lance l'entrainement du modèle
EPOQUE = 100
history = None
YPred = None
### BEGIN SOLUTION
### END SOLUTION
if history is not None:
    plt.title("convergence apprentissage")
    plt.plot(history.history['loss'], label='train')
    plt.legend();
# bilan de l'apprentissage
from sklearn.metrics import r2_score
if YPred is not None:
    print("R2 score {:.2f}".format(r2_score(Y_test, YPred)))
    print("model evaluate loss/mae")
    model.evaluate(X_test,Y_test)
    model.summary()

12.4.8. Affichage des résultats#

12.4.8.1. Transformation des résultats#

  • à partir des résultats sans dimension

  • conversion des résultats en °C dans

    • Thist pour les données X_test

    • Tpred pour les prédictions Ypred

    • Ttest pour les resultats Y_test

# renormalisation des resultats
Thist = None
Tpred = None
Ttest = None
### BEGIN SOLUTION
### END SOLUTION

12.4.8.2. Tracé des résultats#

def plot_multi_etape(historique, vrai_futur, prediction, titre):
    """fonction pour tracer une prédiction sur un jour fixé"""
    plt.figure(figsize=(12, 6))
    num_passe = np.arange(-len(historique),0)
    num_futur = np.arange(len(vrai_futur))
    plt.plot(num_passe, np.array(historique[:, 0]), label='Passé donné')
    plt.plot(num_futur, np.array(vrai_futur), label='Véritable futur')
    plt.plot(num_futur, np.array(prediction), '-ro', label='Futur prédis')
    plt.legend(loc='upper left')
    plt.title(titre)
    plt.xlabel("heures")
    plt.ylabel("T en °C");
    return
#Première figure: La prédiction sur le mois  
if Tpred is not None:
    plt.figure(figsize=(30,8))
    sns.set(rc={"lines.linewidth": 3})
    plt.plot(t,Tpred[:,0],color="green")
    sns.set(rc={"lines.linewidth": 3})
    plt.plot(t,Ttest[:,0],color="coral")
    plt.margins(x=0, y=0.5)
    plt.legend(["Mesure", "Prédiction"])
    plt.title("Prediction / mesure");
    plt.xlim([0,24*10])
    plt.ylim([-5,30]);
#Deuxième figure: Une prédiction particulière pour une date
if Tpred is not None:
    date = f"2016-{mois:2d}-05 00:00:00"
    cas  = T_test.index.get_loc(date) + Nh
    plot_multi_etape(Thist[cas], Ttest[cas], Tpred[cas], 
                 titre=f"Prediction température à Lyon 1 le {date}")
# idem mais pour le mois suivant
if Tpred is not None:
    date = f'2016-{mois+1:2d}-19 00:00:00'
    cas = T_test.index.get_loc(date) + Nh
    plot_multi_etape(Thist[cas], Ttest[cas], Tpred[cas],
                 titre=f"Prediction température à Lyon 1 le {date}")

12.4.9. Amélioration de la prédiction#

Dans les cellules suivantes, proposer et tester des améliorations de la prédiction

  • utilisation des données sur les 2 jours précédents

  • optimisation des paramètres

# tester les améliorations proposées
### BEGIN SOLUTION
### END SOLUTION

12.4.10. Analyse et conclusion#

  • dans le fichier CompteRendu.md écrire votre analyse et vos conclusions en markdown

  • puis générer la version html avec la commande suivante

# génération de la version html du CR
!genereTPhtml CompteRendu

12.4.11. FIN#