Compare commits
2 Commits
9a393972eb
...
2495e9365e
| Author | SHA1 | Date | |
|---|---|---|---|
| 2495e9365e | |||
| 7ba4287399 |
@ -21,6 +21,7 @@ Le script :
|
|||||||
## Lecture rapide des résultats (validation)
|
## Lecture rapide des résultats (validation)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
- Température : le gradient boosting est meilleur que la forêt sur le très court terme (MAE ≈0,13 à +10 min), mais reste derrière les modèles linéaires du chapitre 9 (MAE ≈0,14 à +60 min avec Ridge). La sous‑utilisation des données d’apprentissage (1/10) pèse sur la performance.
|
- Température : le gradient boosting est meilleur que la forêt sur le très court terme (MAE ≈0,13 à +10 min), mais reste derrière les modèles linéaires du chapitre 9 (MAE ≈0,14 à +60 min avec Ridge). La sous‑utilisation des données d’apprentissage (1/10) pèse sur la performance.
|
||||||
@ -29,4 +30,7 @@ Le script :
|
|||||||
|
|
||||||
## Conclusion provisoire
|
## Conclusion provisoire
|
||||||
|
|
||||||
Ces modèles non linéaires apportent de la flexibilité mais, avec un apprentissage allégé pour tenir le temps de calcul, ils ne battent pas les baselines ni les modèles linéaires sur les horizons courts. Pour progresser, il faudra soit élargir l’échantillon d’apprentissage (temps de calcul plus long), soit régler finement les hyperparamètres, soit enrichir les features (ou combiner les deux), tout en vérifiant que le gain justifie l’effort.
|
Ces modèles non linéaires apportent de la flexibilité mais, avec un apprentissage allégé pour tenir le temps de calcul, ils ne battent pas les baselines ni les modèles linéaires sur les horizons courts. Pour progresser, il faudra soit élargir l’échantillon d’apprentissage (temps de calcul plus long), soit régler finement les hyperparamètres, soit enrichir les features (ou combiner les deux).
|
||||||
|
|
||||||
|
À ce stade, les modèles non-linéaires "naïfs" que l'on a implémenté ici travaillent pendant plusieurs minutes et ne sont pas capables de faire mieux que les modèles vus précédemment.
|
||||||
|
Je doute donc qu'il soit pertinent de creuser le sujet, mais cela a aiguisé ma curiosité pour des modèles existants, pré-entraînés, tels que Chronos, d'Amazon.
|
||||||
|
|||||||
BIN
docs/11 - Modèle Chronos/figures/chronos_errors_combined.png
Normal file
BIN
docs/11 - Modèle Chronos/figures/chronos_errors_combined.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
BIN
docs/11 - Modèle Chronos/figures/chronos_multi_errors_rain.png
Normal file
BIN
docs/11 - Modèle Chronos/figures/chronos_multi_errors_rain.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
@ -2,33 +2,67 @@
|
|||||||
|
|
||||||
Objectif : tester un modèle de prévision généraliste récent (**Chronos-2**, Amazon) en zéro-shot sur notre station. On resample la température à l’heure, on coupe les dernières 96 h pour évaluer la prévision, et on compare le forecast à l’observé.
|
Objectif : tester un modèle de prévision généraliste récent (**Chronos-2**, Amazon) en zéro-shot sur notre station. On resample la température à l’heure, on coupe les dernières 96 h pour évaluer la prévision, et on compare le forecast à l’observé.
|
||||||
|
|
||||||
### Mise en route
|
```shell
|
||||||
|
python "docs/11 - Modèle Chronos/scripts/run_chronos.py"
|
||||||
|
```
|
||||||
|
|
||||||
Installez les dépendances (`pip install -r requirements.txt`), puis lancez `run_chronos.py`. Le script lit `data/weather_minutely.csv`, resample la température en pas horaire, prend les 336 dernières heures en contexte et les 96 suivantes en cible. Il charge `amazon/chronos-t5-small` par défaut (modifiable via `CHRONOS_MODEL`), génère 20 échantillons dont on prend la moyenne, calcule MAE/RMSE, sauvegarde les CSV `chronos_forecast_<model>.csv` et `chronos_errors_<model>.csv`, et produit les figures associées dans `docs/11 - Modèle Chronos/figures/`.
|
### Résultats comparés
|
||||||
|
|
||||||
Pour comparer plusieurs tailles, lancez `run_chronos.py` avec différents `CHRONOS_MODEL` (mini/small/base), puis `compare_chronos.py` agrège les CSV et trace la comparaison.
|
Nous avons utilisé les variables d’environnement suivantes : `CHRONOS_CONTEXT` (336 h), `CHRONOS_HORIZON` (96 h), `CHRONOS_RESAMPLE` (`1h`), `CHRONOS_SAMPLES` (20).
|
||||||
|
|
||||||
### Paramètres
|
|
||||||
|
|
||||||
Modifiables via variables d’environnement : `CHRONOS_MODEL` (défaut `amazon/chronos-t5-small`), `CHRONOS_CONTEXT` (336 h), `CHRONOS_HORIZON` (96 h), `CHRONOS_RESAMPLE` (`1h`), `CHRONOS_SAMPLES` (20).
|
|
||||||
|
|
||||||
### Résultats comparés (mêmes données, horizon 96 h)
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Sur la même fenêtre de validation locale, nous avons testé trois tailles : `chronos-t5-mini`, `chronos-t5-small` et `chronos-t5-base`. Le modèle **small** est ressorti devant (MAE ≈ 3,68 °C, RMSE ≈ 4,53 °C), les versions mini et base étant derrière (MAE ≈ 4,18–4,24 °C, RMSE ≈ 5,3–5,6 °C). Autrement dit, monter en taille n’a pas amélioré la prévision à 96 h sur ces données locales ; la version small offre le meilleur compromis précision/poids.
|
Sur la même fenêtre de validation locale, nous avons testé trois tailles (`CHRONOS_MODEL`) : `chronos-t5-mini`, `chronos-t5-small` et `chronos-t5-base`. Le modèle **small** semble le plus précis (MAE ≈ 3,68 °C, RMSE ≈ 4,53 °C), les versions mini et base étant derrière (MAE ≈ 4,18–4,24 °C, RMSE ≈ 5,3–5,6 °C). Autrement dit, monter en taille n’a pas amélioré la prévision à 96 h sur ces données locales ; la version `small` offre le meilleur compromis précision/poids.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python "docs/11 - Modèle Chronos/scripts/compare_chronos.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### chronos-t5-mini
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
#### chronos-t5-small
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Le modèle suit un peu mieux la courbe de référence que les autres, mais une forte amplitude existe.
|
||||||
|
|
||||||
|
#### chronos-t5-base
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Lecture et portée
|
### Lecture et portée
|
||||||
|
|
||||||
Pour coller à nos horizons cibles, `run_chronos_multi.py` évalue Chronos-small sur 1 h, 6 h et 24 h pour la température, le vent et la pluie (horaire uniquement ; l’horizon 10 minutes n’est pas couvert par Chronos qui est pré-entraîné en pas horaire). Les figures `chronos_multi_temperature.png`, `chronos_multi_wind_speed.png` et `chronos_multi_rain_rate.png` illustrent où le modèle est le plus fiable : à 1 h, la température reste sous ~1,3 °C de MAE et le vent sous ~0,6 (unités du jeu) ; à 6 h, l’erreur grimpe modérément (≈2 °C temp., ≈3 km/h vent) ; à 24 h, elle dépasse 4–6 (°C/ km/h). Sur la pluie, le F1 reste nul à 1 h/6 h et ne monte qu’à ~0,15 à 24 h, signe que le modèle “foundation” horaire ne capture pas bien les occurrences locales rares. Les figures individuelles (`chronos_forecast_<model>.png`, `chronos_errors_<model>.png`) permettent de voir la trajectoire prédit vs observé et l’erreur par horizon.
|
Pour coller à nos horizons cibles, `run_chronos_multi.py` évalue Chronos-small sur 1 h, 6 h et 24 h pour la température, le vent et la pluie (horaire uniquement ; l’horizon 10 minutes n’est pas couvert par Chronos qui est pré-entraîné en pas horaire). Les figures `chronos_multi_temperature.png`, `chronos_multi_wind_speed.png` et `chronos_multi_rain_rate.png` illustrent où le modèle est le plus fiable : à 1 h, la température reste sous ~1,3 °C de MAE et le vent sous ~0,6 km/h ; à 6 h, l’erreur grimpe modérément (≈2 °C temp., ≈3 km/h vent) ; à 24 h, elle dépasse 4–6 (°C/ km/h). Sur la pluie, le F1 reste nul à 1 h/6 h et ne monte qu’à ~0,15 à 24 h, signe que le modèle “foundation” horaire ne capture pas bien les occurrences locales rares.
|
||||||
|
|
||||||
Au total, Chronos-small fournit un signal exploitable sur la température et un peu sur le vent pour des horizons courts à intermédiaires, mais reste faible sur la pluie et se dégrade nettement au-delà de 24 h. Une calibration locale, davantage de contexte ou une cible adaptée (pluie binaire calibrée) seraient nécessaires pour en faire un outil de prévision robuste sur toutes les variables.
|
```shell
|
||||||
|
python "docs/11 - Modèle Chronos/scripts/run_chronos_multi.py"
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```shell
|
||||||
|
python "docs/11 - Modèle Chronos/scripts/plot_chronos_errors_combined.py"
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Les figures individuelles permettent de voir la trajectoire prédite vs observée (dans la section précédente) et l’erreur par horizon (ci-dessous).
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python "docs/11 - Modèle Chronos/scripts/run_chronos_multi_errors.py"
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Au final, Chronos-small fournit un signal exploitable sur la température et un peu sur le vent pour des horizons courts à intermédiaires, mais reste faible sur la pluie et se dégrade nettement au-delà de 24 h. Une calibration locale, davantage de contexte ou une cible adaptée (pluie binaire calibrée) seraient nécessaires pour en faire un outil de prévision robuste sur toutes les variables.
|
||||||
|
|
||||||
### Réglages prudents (contexte 288 h, horizon limité à 64 h, 100 échantillons)
|
### Réglages prudents (contexte 288 h, horizon limité à 64 h, 100 échantillons)
|
||||||
|
|
||||||
@ -40,7 +74,11 @@ Au total, Chronos-small fournit un signal exploitable sur la température et un
|
|||||||
|
|
||||||
Avec `run_chronos_tuned.py`, on réduit le contexte (288 h) et l’horizon maximum (64 h) tout en augmentant les échantillons (100). Sur la même fenêtre locale, la température s’améliore nettement : MAE ~0,75 °C à 1 h, ~1,27 °C à 6 h, ~3,40 °C à 24 h (vs 1,33/2,02/4,84 auparavant). Le vent progresse surtout à 24 h (≈2,39 contre ~6,38 auparavant), même si le 1 h est moins bon que la première passe. La pluie reste instable : le F1 peut atteindre 0,22–0,28 à 24–48 h mais les scores courts sont peu fiables. Limiter l’horizon à 64 h, raccourcir le contexte et lisser par davantage d’échantillons améliorent donc la température et le vent, mais ne suffisent pas à rendre la pluie prédictible.
|
Avec `run_chronos_tuned.py`, on réduit le contexte (288 h) et l’horizon maximum (64 h) tout en augmentant les échantillons (100). Sur la même fenêtre locale, la température s’améliore nettement : MAE ~0,75 °C à 1 h, ~1,27 °C à 6 h, ~3,40 °C à 24 h (vs 1,33/2,02/4,84 auparavant). Le vent progresse surtout à 24 h (≈2,39 contre ~6,38 auparavant), même si le 1 h est moins bon que la première passe. La pluie reste instable : le F1 peut atteindre 0,22–0,28 à 24–48 h mais les scores courts sont peu fiables. Limiter l’horizon à 64 h, raccourcir le contexte et lisser par davantage d’échantillons améliorent donc la température et le vent, mais ne suffisent pas à rendre la pluie prédictible.
|
||||||
|
|
||||||
### Dernier essai “comme en vrai” : prévision sur les 6 dernières heures
|
### Dernier essai en conditions réelles : prévision sur les 6 dernières heures
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python "docs/11 - Modèle Chronos/scripts/run_chronos_holdout6.py"
|
||||||
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
# scripts/plot_chronos_errors_combined.py
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
DOC_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
DATA_DIR = DOC_DIR / "data"
|
||||||
|
FIG_DIR = DOC_DIR / "figures"
|
||||||
|
|
||||||
|
|
||||||
|
def load_errors() -> pd.DataFrame:
|
||||||
|
pattern = re.compile(r"chronos_forecast_(amazon__chronos-t5-[a-z]+)\.csv")
|
||||||
|
records = []
|
||||||
|
for csv in DATA_DIR.glob("chronos_forecast_*.csv"):
|
||||||
|
m = pattern.match(csv.name)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
model = m.group(1).replace("__", "/")
|
||||||
|
df = pd.read_csv(csv)
|
||||||
|
if not {"y_true", "y_pred"}.issubset(df.columns):
|
||||||
|
continue
|
||||||
|
err = (df["y_pred"] - df["y_true"]).abs()
|
||||||
|
for i, v in enumerate(err, start=1):
|
||||||
|
records.append({"model": model, "horizon_h": i, "abs_error": v})
|
||||||
|
return pd.DataFrame(records)
|
||||||
|
|
||||||
|
|
||||||
|
def plot_errors(df: pd.DataFrame, output_path: Path) -> None:
|
||||||
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
fig, ax = plt.subplots(figsize=(8, 4))
|
||||||
|
for model, sub in df.groupby("model"):
|
||||||
|
sub_sorted = sub.sort_values("horizon_h")
|
||||||
|
ax.plot(sub_sorted["horizon_h"], sub_sorted["abs_error"], label=model, linewidth=2)
|
||||||
|
ax.set_xlabel("Horizon (heures)")
|
||||||
|
ax.set_ylabel("Erreur absolue (°C)")
|
||||||
|
ax.set_title("Chronos T5 – erreur absolue vs horizon")
|
||||||
|
ax.grid(True, linestyle=":", alpha=0.4)
|
||||||
|
ax.legend()
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig(output_path, dpi=150)
|
||||||
|
plt.close(fig)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
df = load_errors()
|
||||||
|
if df.empty:
|
||||||
|
raise SystemExit("Aucun fichier chronos_forecast_*.csv trouvé ou colonnes manquantes.")
|
||||||
|
plot_errors(df, FIG_DIR / "chronos_errors_combined.png")
|
||||||
|
print("✔ Figure : figures/chronos_errors_combined.png")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
# scripts/plot_chronos_multi_errors.py
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
DOC_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
DATA_DIR = DOC_DIR / "data"
|
||||||
|
FIG_DIR = DOC_DIR / "figures"
|
||||||
|
|
||||||
|
|
||||||
|
def _plot_temp_wind(df: pd.DataFrame, output: Path) -> None:
|
||||||
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
fig, ax = plt.subplots(figsize=(6, 4))
|
||||||
|
for target in ("temperature", "wind_speed"):
|
||||||
|
sub = df[(df["target"] == target) & (df["kind"] == "reg")].sort_values("horizon_h")
|
||||||
|
ax.plot(sub["horizon_h"], sub["mae"], marker="o", label=f"{target} – MAE")
|
||||||
|
ax.plot(sub["horizon_h"], sub["rmse"], marker="s", label=f"{target} – RMSE", linestyle="--")
|
||||||
|
ax.set_xlabel("Horizon (heures)")
|
||||||
|
ax.set_ylabel("Erreur")
|
||||||
|
ax.set_title("Chronos small – erreurs température / vent")
|
||||||
|
ax.grid(True, linestyle=":", alpha=0.4)
|
||||||
|
ax.legend()
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig(output, dpi=150)
|
||||||
|
plt.close(fig)
|
||||||
|
|
||||||
|
|
||||||
|
def _plot_rain(df: pd.DataFrame, output: Path) -> None:
|
||||||
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
sub = df[(df["target"] == "rain_rate") & (df["kind"] == "cls")].sort_values("horizon_h")
|
||||||
|
fig, ax1 = plt.subplots(figsize=(6, 4))
|
||||||
|
ax1.plot(sub["horizon_h"], sub["f1"], marker="o", color="tab:blue", label="F1")
|
||||||
|
ax1.set_ylabel("F1", color="tab:blue")
|
||||||
|
ax1.tick_params(axis="y", labelcolor="tab:blue")
|
||||||
|
|
||||||
|
ax2 = ax1.twinx()
|
||||||
|
ax2.plot(sub["horizon_h"], sub["brier"], marker="s", color="tab:red", linestyle="--", label="Brier")
|
||||||
|
ax2.set_ylabel("Brier", color="tab:red")
|
||||||
|
ax2.tick_params(axis="y", labelcolor="tab:red")
|
||||||
|
|
||||||
|
ax1.set_xlabel("Horizon (heures)")
|
||||||
|
ax1.set_title("Chronos small – pluie (F1/Brier)")
|
||||||
|
ax1.grid(True, linestyle=":", alpha=0.4)
|
||||||
|
|
||||||
|
# Combine legends
|
||||||
|
handles, labels = [], []
|
||||||
|
for ax in (ax1, ax2):
|
||||||
|
h, l = ax.get_legend_handles_labels()
|
||||||
|
handles += h
|
||||||
|
labels += l
|
||||||
|
ax1.legend(handles, labels, loc="upper right")
|
||||||
|
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig(output, dpi=150)
|
||||||
|
plt.close(fig)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
metrics_path = DATA_DIR / "chronos_multi_metrics.csv"
|
||||||
|
if not metrics_path.exists():
|
||||||
|
raise SystemExit("chronos_multi_metrics.csv introuvable. Lancez run_chronos_multi.py d'abord.")
|
||||||
|
df = pd.read_csv(metrics_path)
|
||||||
|
_plot_temp_wind(df, FIG_DIR / "chronos_multi_errors_temp_wind.png")
|
||||||
|
_plot_rain(df, FIG_DIR / "chronos_multi_errors_rain.png")
|
||||||
|
print("✔ Figures : chronos_multi_errors_temp_wind.png, chronos_multi_errors_rain.png")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
x
Reference in New Issue
Block a user