diff --git a/bot/src/dqn/burnrl/environment.rs b/bot/src/dqn/burnrl/environment.rs index f8e5f21..dd75b73 100644 --- a/bot/src/dqn/burnrl/environment.rs +++ b/bot/src/dqn/burnrl/environment.rs @@ -91,7 +91,7 @@ impl Environment for TrictracEnvironment { type ActionType = TrictracAction; type RewardType = f32; - const MAX_STEPS: usize = 700; // Limite max pour éviter les parties infinies + const MAX_STEPS: usize = 1000; // Limite max pour éviter les parties infinies fn new(visualized: bool) -> Self { let mut game = GameState::new(false); @@ -179,9 +179,9 @@ impl Environment for TrictracEnvironment { // Récompense finale basée sur le résultat if let Some(winner_id) = self.game.determine_winner() { if winner_id == self.active_player_id { - reward += 100.0; // Victoire + reward += 50.0; // Victoire } else { - reward -= 50.0; // Défaite + reward -= 25.0; // Défaite } } } @@ -259,7 +259,7 @@ impl TrictracEnvironment { // } TrictracAction::Go => { // Continuer après avoir gagné un trou - reward += 0.4; + reward += 0.2; Some(GameEvent::Go { player_id: self.active_player_id, }) @@ -288,7 +288,7 @@ impl TrictracEnvironment { let checker_move1 = store::CheckerMove::new(from1, to1).unwrap_or_default(); let checker_move2 = store::CheckerMove::new(from2, to2).unwrap_or_default(); - reward += 0.4; + reward += 0.2; Some(GameEvent::Move { player_id: self.active_player_id, moves: (checker_move1, checker_move2), @@ -313,6 +313,8 @@ impl TrictracEnvironment { }; if self.game.validate(&dice_event) { self.game.consume(&dice_event); + let (points, adv_points) = self.game.dice_points; + reward += 0.3 * (points - adv_points) as f32; // Récompense proportionnelle aux points } } } else { @@ -356,7 +358,7 @@ impl TrictracEnvironment { }, } } - TurnStage::MarkAdvPoints | TurnStage::MarkPoints => { + TurnStage::MarkPoints => { let opponent_color = store::Color::Black; let dice_roll_count = self .game @@ -366,14 +368,31 @@ impl TrictracEnvironment { .dice_roll_count; let points_rules = PointsRules::new(&opponent_color, &self.game.board, self.game.dice); - let points = points_rules.get_points(dice_roll_count).0; - reward -= 0.3 * points as f32; // Récompense proportionnelle aux points + let (points, adv_points) = points_rules.get_points(dice_roll_count); + reward -= 0.3 * (points - adv_points) as f32; // Récompense proportionnelle aux points GameEvent::Mark { player_id: self.opponent_id, points, } } + TurnStage::MarkAdvPoints => { + let opponent_color = store::Color::Black; + let dice_roll_count = self + .game + .players + .get(&self.opponent_id) + .unwrap() + .dice_roll_count; + let points_rules = + PointsRules::new(&opponent_color, &self.game.board, self.game.dice); + let points = points_rules.get_points(dice_roll_count).1; + // pas de reward : déjà comptabilisé lors du tour de blanc + GameEvent::Mark { + player_id: self.opponent_id, + points, + } + } TurnStage::HoldOrGoChoice => { // Stratégie simple : toujours continuer GameEvent::Go { diff --git a/bot/src/dqn/burnrl/main.rs b/bot/src/dqn/burnrl/main.rs index fdaafc6..e7f9722 100644 --- a/bot/src/dqn/burnrl/main.rs +++ b/bot/src/dqn/burnrl/main.rs @@ -11,13 +11,13 @@ type Env = environment::TrictracEnvironment; fn main() { println!("> Entraînement"); let conf = dqn_model::DqnConfig { - num_episodes: 50, + num_episodes: 40, // memory_size: 8192, // must be set in dqn_model.rs with the MEMORY_SIZE constant - // max_steps: 700, // must be set in environment.rs with the MAX_STEPS constant + // max_steps: 1000, // must be set in environment.rs with the MAX_STEPS constant dense_size: 256, // neural network complexity eps_start: 0.9, // epsilon initial value (0.9 => more exploration) eps_end: 0.05, - eps_decay: 1000.0, + eps_decay: 3000.0, }; let agent = dqn_model::run::(&conf, false); //true); diff --git a/bot/src/dqn/simple/dqn_trainer.rs b/bot/src/dqn/simple/dqn_trainer.rs index c23b542..dedf382 100644 --- a/bot/src/dqn/simple/dqn_trainer.rs +++ b/bot/src/dqn/simple/dqn_trainer.rs @@ -357,8 +357,8 @@ impl TrictracEnv { &self.game_state.board, self.game_state.dice, ); - let points = points_rules.get_points(dice_roll_count).0; - reward -= 0.3 * points as f32; // Récompense proportionnelle aux points + let (points, adv_points) = points_rules.get_points(dice_roll_count); + reward -= 0.3 * (points - adv_points) as f32; // Récompense proportionnelle aux points GameEvent::Mark { player_id: self.opponent_player_id, diff --git a/bot/src/strategy/client.rs b/bot/src/strategy/client.rs index 4f7b84f..891f942 100644 --- a/bot/src/strategy/client.rs +++ b/bot/src/strategy/client.rs @@ -46,7 +46,14 @@ impl BotStrategy for ClientStrategy { } fn calculate_adv_points(&self) -> u8 { - self.calculate_points() + let dice_roll_count = self + .get_game() + .players + .get(&self.player_id) + .unwrap() + .dice_roll_count; + let points_rules = PointsRules::new(&Color::White, &self.game.board, self.game.dice); + points_rules.get_points(dice_roll_count).1 } fn choose_go(&self) -> bool { diff --git a/devenv.nix b/devenv.nix index d41dbe8..1b51c9d 100644 --- a/devenv.nix +++ b/devenv.nix @@ -13,6 +13,7 @@ # dev tools pkgs.samply # code profiler + pkgs.feedgnuplot # to visualize bots training results # for bevy pkgs.alsa-lib diff --git a/doc/refs/dqn-burn.md b/doc/refs/dqn-burn.md new file mode 100644 index 0000000..94cbf73 --- /dev/null +++ b/doc/refs/dqn-burn.md @@ -0,0 +1,56 @@ +# DQN avec burn-rl + +## Paramètre d'entraînement dans dqn/burnrl/dqn_model.rs + +Ces constantes sont des hyperparamètres, c'est-à-dire des réglages que l'on fixe avant l'entraînement et qui conditionnent la manière dont le modèle va apprendre. + +MEMORY_SIZE + +- Ce que c'est : La taille de la "mémoire de rejeu" (Replay Memory/Buffer). +- À quoi ça sert : L'agent interagit avec l'environnement (le jeu de TricTrac) et stocke ses expériences (un état, l'action prise, la récompense obtenue, et l'état suivant) dans cette mémoire. Pour s'entraîner, au + lieu d'utiliser uniquement la dernière expérience, il pioche un lot (batch) d'expériences aléatoires dans cette mémoire. +- Pourquoi c'est important : + 1. Décorrélation : Ça casse la corrélation entre les expériences successives, ce qui rend l'entraînement plus stable et efficace. + 2. Réutilisation : Une même expérience peut être utilisée plusieurs fois pour l'entraînement, ce qui améliore l'efficacité des données. +- Dans votre code : const MEMORY_SIZE: usize = 4096; signifie que l'agent gardera en mémoire les 4096 dernières transitions. + +DENSE_SIZE + +- Ce que c'est : La taille des couches cachées du réseau de neurones. "Dense" signifie que chaque neurone d'une couche est connecté à tous les neurones de la couche suivante. +- À quoi ça sert : C'est la "capacité de réflexion" de votre agent. Le réseau de neurones (ici, Net) prend l'état du jeu en entrée, le fait passer à travers des couches de calcul (de taille DENSE_SIZE), et sort une + estimation de la qualité de chaque action possible. +- Pourquoi c'est important : + - Une valeur trop petite : le modèle ne sera pas assez "intelligent" pour apprendre les stratégies complexes du TricTrac. + - Une valeur trop grande : l'entraînement sera plus lent et le modèle pourrait "sur-apprendre" (overfitting), c'est-à-dire devenir très bon sur les situations vues en entraînement mais incapable de généraliser + sur de nouvelles situations. +- Dans votre code : const DENSE_SIZE: usize = 128; définit que les couches cachées du réseau auront 128 neurones. + +EPS_START, EPS_END et EPS_DECAY + +Ces trois constantes gèrent la stratégie d'exploration de l'agent, appelée "epsilon-greedy". Le but est de trouver un équilibre entre : + +- L'Exploitation : Jouer le coup que le modèle pense être le meilleur. +- L'Exploration : Jouer un coup au hasard pour découvrir de nouvelles stratégies, potentiellement meilleures. + +epsilon (ε) est la probabilité de faire un choix aléatoire (explorer). + +- `EPS_START` (Epsilon de départ) : + + - Ce que c'est : La valeur d'epsilon au tout début de l'entraînement. + - Rôle : Au début, le modèle ne sait rien. Il est donc crucial qu'il explore beaucoup pour accumuler des expériences variées. Une valeur élevée (proche de 1.0) est typique. + - Dans votre code : const EPS_START: f64 = 0.9; signifie qu'au début, l'agent a 90% de chances de jouer un coup au hasard. + +- `EPS_END` (Epsilon final) : + + - Ce que c'est : La valeur minimale d'epsilon, atteinte après un certain nombre d'étapes. + - Rôle : Même après un long entraînement, on veut conserver une petite part d'exploration pour éviter que l'agent ne se fige dans une stratégie sous-optimale. + - Dans votre code : const EPS_END: f64 = 0.05; signifie qu'à la fin, l'agent explorera encore avec 5% de probabilité. + +- `EPS_DECAY` (Décroissance d'epsilon) : + - Ce que c'est : Contrôle la vitesse à laquelle epsilon passe de EPS_START à EPS_END. + - Rôle : C'est un facteur de "lissage" dans la formule de décroissance exponentielle. Plus cette valeur est élevée, plus la décroissance est lente, et donc plus l'agent passera de temps à explorer. + - Dans votre code : const EPS_DECAY: f64 = 1000.0; est utilisé dans la formule EPS_END + (EPS_START - EPS_END) \* f64::exp(-(step as f64) / EPS_DECAY); pour faire diminuer progressivement la valeur d'epsilon à + chaque étape (step) de l'entraînement. + +En résumé, ces constantes définissent l'architecture du "cerveau" de votre bot (DENSE*SIZE), sa mémoire à court terme (MEMORY_SIZE), et comment il apprend à équilibrer entre suivre sa stratégie et en découvrir de +nouvelles (EPS*\*). diff --git a/justfile b/justfile index e7d7222..c2d85a8 100644 --- a/justfile +++ b/justfile @@ -9,7 +9,8 @@ shell: runcli: RUST_LOG=info cargo run --bin=client_cli runclibots: - RUST_LOG=info cargo run --bin=client_cli -- --bot dqn,dummy + #RUST_LOG=info cargo run --bin=client_cli -- --bot dqn,dummy + RUST_LOG=info cargo run --bin=client_cli -- --bot dummy,dqn match: cargo build --release --bin=client_cli LD_LIBRARY_PATH=./target/release ./target/release/client_cli -- --bot dummy,dqn @@ -24,7 +25,8 @@ trainbot: #python ./store/python/trainModel.py # cargo run --bin=train_dqn # ok cargo build --release --bin=train_dqn_burn - LD_LIBRARY_PATH=./target/release ./target/release/train_dqn_burn + #LD_LIBRARY_PATH=./target/release ./target/release/train_dqn_burn + LD_LIBRARY_PATH=./target/release ./target/release/train_dqn_burn | tee >&2 | sed s/,//g | awk '{print $4}' | feedgnuplot --lines --points --unset grid # cargo run --bin=train_dqn_burn # utilise debug (why ?) debugtrainbot: cargo build --bin=train_dqn_burn