Si je fais jouer deux moteurs les uns contre les autres avec les mêmes couleurs, le même jeu résultera-t-il à chaque fois? Sinon, d'où vient le caractère aléatoire du jeu du moteur? (Négliger le livre d'ouverture, où si je ne me trompe pas, le livre peut dire au moteur de choisir entre deux mouvements au hasard car ils sont tout aussi bons.)
Je suppose qu'il y a de l'aléatoire car dans le match Alphazero vs Stockfish, nous n'avons pas eu le même jeu plusieurs fois de suite. Mais je ne comprends pas pourquoi. Vraisemblablement, la seule façon de le faire est de faire en sorte que le moteur joue un mouvement inférieur parfois, ce qui ressemble à du seppuku.
Réponses:
Concernant le match AlphaZero vs Stockfish, cette question a déjà été abordée ici par SmallChess .
AlphaZero mis à part (qui utilise une routine Monte Carlo 1 spécialisée dans son exploration des lignes de jeu), qui est conçu pour être non déterministe par construction, pour les moteurs d'échecs habituels basés sur l'heuristique, tels que Stockfish et autres (bien qu'il existe d'autres moteurs qui ont des routines basées sur MC, AFAIK Rybka avait une telle fonctionnalité), la source de l'aléatoire n'est généralement qu'une conséquence des aspects techniques de la mise en œuvre, plutôt que l'aléatoire intentionnel étant introduit de manière algorithmique dans la prise de décision du moteur. De manière abstraite, une des raisons à cela est le fait que les moteurs ne fonctionnent pas de manière purement séquentielle (exécutant une tâche après l'autre). Au lieu de cela, pour rendre les moteurs plus efficaces, ils effectuent des recherches parallèles dans diverses branches de l'arborescence des mouvements possibles. Ils le font via ce qu'on appelle le multi-threading (ou -processing mais c'est un peu différent). Ainsi, plusieurs threads des CPU sont simultanémentexécuter des opérations pour rechercher dans l'arbre (et mettre en cache les évaluations des positions visitées), alors imaginez que chaque thread se voit attribuer un sous-arbre. Le problème avec ce type d'implémentation est que l'exécution globale des threads devient fortement dépendante de toutes sortes de conditions (temps d'attente, swaps de RAM, ...), donc au final une variante principale peut être choisie sans avoir autorisé toutes les autres discussions pour terminer leur recherche.
Cela se produit en effet souvent parce que le moteur est configuré pour prendre une décision dans un certain laps de temps, donc la gestion du temps modifie le comportement. Vous pouvez également revenir sur cette affirmation en disant: connaître l'algorithme et implémenter des routines de thread déterministes ne sont pas suffisants pour prédire de manière fiable l'état du programme après tout instant t. Bien sûr, si l'on permet toujours à tous les threads de terminer leur recherche, et qu'il n'y a pas eu de problèmes de concurrence pendant cette exécution (par exemple, un thread essayant d'accéder à un certain cache qui n'est pas accessible), alors le comportement sera en effet entièrement reproductible étant donné que tout le reste est le même 2 .
1 : Avec le fait que grâce à une formation supplémentaire (par exemple, l'auto-jeu), son réseau de neurones continue d'évoluer (paramètres réajustés), ou si vous voulez, sa fonction d'évaluation n'a pas de définition constante et fixe (contrairement aux moteurs basés sur l'heuristique ).
2 : Même alors, comme vous l'avez dit, au niveau d'ouverture, avec un livre d'ouverture, il y a parfois des décisions aléatoires intentionnelles prises par le moteur quant à la variante à choisir. De même, en dehors de la phase d'ouverture, il peut y avoir des moments où plusieurs variations ont des évaluations presque égales (dans la résolution choisie pour l'Eval), puis en fonction du design, il peut finir par en choisir une au hasard. Enfin, au niveau des paramètres du moteur, vous devez également être prudent, par exemple la profondeur de recherche et les temps de réflexion choisis pour chaque moteur (et s'ils peuvent calculer davantage pendant les temps de réflexion les uns des autres).
la source
Merci à @Phonon de couvrir en détail mes réponses précédentes. Je voudrais ajouter un autre point: le contrôle du temps .
Le seul contrôle temporel déterministe est le nombre de nœuds , mais cela est rare. Le contrôle du temps beaucoup plus courant - le nombre fixe de secondes ou le temps de jeu ne sont généralement pas déterministes.
Essayons un exemple. Exécutez stockfish sur votre terminal. Type:
Cette commande demande au moteur d'effectuer un mouvement après 20 secondes. Mes résultats:
Le mouvement était de 1.Nf3. Ensuite, j'ai tué mon Stockfish, j'en ai commencé un nouveau. Encore une fois, 20 secondes. J'ai eu:
C'est 1.d4! Même position, les deux 20 secondes de recherche!
Est-ce que tu vois? Les deux 20 secondes pour le déménagement, mais en raison des fluctuations du système d'exploitation Linux, ma deuxième exécution a eu une recherche plus approfondie (26185280> 24325860).
Veuillez noter que cette petite expérience n'était même pas multithread (nombre de threads = 1). Le multithread rendrait les choses encore plus non déterministes.
Stockfish a eu une minute par coup dans le match Google AlphaZero. Le nombre de discussions était de 64. Les décisions de Stockfish dans le match ne pouvaient pas être déterministes.
la source