
- Cette fois, le jeu "Snake" est sélectionné.
- Une bibliothèque pour le réseau Go a été créée.
- On retrouve le principe de l'apprentissage, en fonction de la «profondeur» de la mémoire.
- Un serveur est écrit pour le jeu entre développeurs.
L'essence du jeu
Beaucoup de gens se souviennent peut-être du jeu "Snake", qui était une application standard sur les téléphones Nokia. Son essence est la suivante: un serpent se déplace à travers le champ, qui diminue s'il ne trouve pas de nourriture, ou augmente s'il en trouve. Si un serpent s'écrase dans un obstacle, il meurt.
J'ai un peu changé les règles: le serpent ne meurt pas s'il tombe en panne, mais s'arrête tout simplement, continuant à diminuer. De plus, le serpent peut être divisé en deux. Si le serpent a une cellule dans le corps et qu'elle n'a pas pu trouver de nourriture en 10 mouvements, alors elle meurt, se transformant en nourriture.
Nous allons entraîner le bot qui contrôle le serpent. Si le serpent se sépare, alors le bot aura un autre serpent en contrôle, qui à son tour peut également se diviser.
L'expérience avec les serpents du cyber biologiste Mikhail Tsarkov est prise comme base.
Réseau de neurones
Dans le cadre de la tâche, une bibliothèque pour le réseau de neurones a été écrite dans le langage Go. En étudiant le travail du réseau neuronal, j'utilise le journal vidéo
foo52ru et le livre de Tariq Rashid - Créer un réseau neuronal.
La fonction
CreateLayer(L []int)
crée un réseau neuronal avec le nombre de couches requis et leur taille. Sur chaque couche, sauf la dernière, un neurone de déplacement est ajouté. Nous alimentons les données de la première couche et nous obtenons le résultat de la dernière couche.
Un exemple:
CreateLayer([]int{9, 57, 3, 1})
Ici, nous avons créé un réseau neuronal avec neuf entrées. Deux couches cachées de 57 et 3 neurones et un neurone pour obtenir le résultat. Les neurones de déplacement sont automatiquement ajoutés par le plus à ceux que nous avons définis.
La bibliothèque vous permet de:
- Soumettez les données à l'entrée réseau.
- Obtenez le résultat en accédant à la dernière couche.
- Demandez les bonnes réponses et organisez une formation en ajustant le poids des liens.
Les pondérations initiales des obligations sont données par des valeurs aléatoires proches de zéro. Pour l'activation, nous avons utilisé la fonction logistique.
Formation de bot
Le bot reçoit un champ carré 9x9 à l'entrée, au milieu duquel se trouve la tête d'un serpent. En conséquence, notre réseau de neurones aura 81 entrées. L'ordre des cellules alimentant l'entrée n'a pas d'importance. Pendant la formation, le réseau «le découvrira» lui-même, où se trouve ce qui se trouve.
Pour indiquer les obstacles et autres serpents, j'ai utilisé des valeurs de -1 à 0 (non inclus). Les cellules vides ont été désignées avec une valeur de 0,01 et la nourriture 0,99.
En sortie du réseau neuronal, 5 neurones ont été utilisés pour des actions:
- se déplacer à gauche le long de l'axe X;
- à droite;
- en haut de l'axe y;
- vers le bas;
- divisé en deux.
Le mouvement du bot a été déterminé par le neurone, qui a la plus grande valeur en sortie.
Étape 0. Randomiseur
Tout d'abord, un randomiseur de bot a été créé. J'appelle donc un bot qui marche au hasard. Il est nécessaire de vérifier l'efficacité du réseau neuronal. Avec une formation appropriée, un réseau de neurones devrait facilement le battre.
Étape 1. Apprendre sans mémoire
Après chaque mouvement, nous ajustons les poids des liaisons pour le neurone de sortie qui a indiqué la valeur la plus élevée. Nous ne touchons pas aux autres neurones de sortie.
Les valeurs suivantes ont été données pour la formation:
- nourriture trouvée: 0.99
- fait un mouvement dans n'importe quelle direction: 0,5
- perdu une cellule du corps sans trouver de nourriture (10 mouvements sont donnés pour cela): 0,2
- reste immobile (a heurté un obstacle ou est coincé): 0,1
- immobile, ayant une cellule du corps: 0,01
Après une telle formation, les bots ont rapidement commencé à battre le randomiseur, et je me suis fixé la tâche: créer des bots qui les battront.
Test A / B
Pour accomplir cette tâche, un programme a été créé qui divise les serpents en deux parties, selon la configuration du réseau neuronal. Sur le terrain, 20 serpents de chaque configuration ont été produits.
Tous les serpents contrôlés par un bot avaient le même réseau neuronal. Plus il y avait de serpents dans sa gestion et plus souvent ils étaient confrontés à des tâches différentes, plus la formation avait lieu rapidement. Si, par exemple, un serpent a appris à éviter les blocages ou à se diviser en deux lorsqu'il a atteint une impasse, alors automatiquement tous les serpents de ce bot ont acquis ces compétences.
En modifiant la configuration du réseau neuronal, vous pouvez obtenir de bons résultats, mais cela ne suffit pas. Pour améliorer encore l'algorithme, j'ai décidé d'utiliser la mémoire pour plusieurs mouvements.
Étape 2. Apprendre avec la mémoire
Pour chaque bot, j'ai créé une mémoire pour 8 mouvements. L'état du terrain et le mouvement suggéré par le bot ont été enregistrés en mémoire. Après cela, j'ai ajusté les poids pour les huit États qui ont précédé le déménagement. Pour cela, j'ai utilisé un seul facteur de correction, indépendant de la profondeur de voyage. Ainsi, chaque mouvement a conduit à l'ajustement des poids non pas une, mais huit.
Comme prévu, les bots de mémoire ont commencé à battre rapidement les bots qui se sont entraînés sans mémoire.
Étape 3. Diminution du coefficient de correction en fonction de la profondeur de la mémoire
Ensuite, j'ai essayé de réduire le facteur de correction, en fonction de la profondeur de la mémoire. Pour le dernier mouvement effectué, le coefficient d'ajustement des poids le plus élevé a été établi. Dans le cours qui l'a précédé, le facteur de correction a diminué et ainsi de suite tout au long de la mémoire.

Une diminution linéaire du coefficient de correction en fonction de la profondeur de la mémoire a conduit au fait que de nouveaux bots ont commencé à battre ceux qui utilisaient un seul coefficient.
Ensuite, j'ai essayé d'utiliser la réduction logarithmique du facteur de correction. Le coefficient a diminué de moitié, selon la profondeur de la mémoire pour chaque mouvement. Ainsi, les mouvements qui ont été effectués "il y a longtemps" ont eu un impact significativement moindre sur l'apprentissage que les mouvements "frais".
Les bots avec une réduction logarithmique du coefficient de correction ont commencé à vaincre les bots avec une relation linéaire.
Serveur pour les bots
Il s'est avéré que l'amélioration du niveau de «pompage» des robots peut être infinie. Et j'ai décidé de créer un serveur où les développeurs pourraient rivaliser (quel que soit le langage de programmation) en écrivant un algorithme efficace pour Snakes.
Protocole
Pour l'autorisation, vous devez envoyer une demande GET au répertoire «game» et spécifier un nom d'utilisateur, par exemple:
.../game/?user=masterdak
Au lieu de "...", vous devez spécifier l'adresse du site et le port sur lequel le serveur est déployé.
Ensuite, le serveur émettra une réponse au format JSON indiquant la session:
{"answer":"Hellow, masterdak!","session":"f4f559d1d2ed97e0616023fb4a84f984"}
Après cela, vous pouvez demander une carte et les coordonnées du serpent sur le terrain, en ajoutant une session à la demande:
.../game/?user=masterdak&session=f4f559d1d2ed97e0616023fb4a84f984
Le serveur affichera quelque chose comme ceci:
{ "answer": "Sent game data.", "data": { "area": [ ["... ..."] ], "snakes": [ { "num": 0, "body": [ { "x": 19, "y": 24 }, { "x": 19, "y": 24 }, { "x": 19, "y": 24 } ], "energe": 4, "dead": false } ] } }
Le champ de
zone indiquera l'état du terrain de jeu avec les valeurs suivantes:
0
Cela sera suivi d'un tableau avec des serpents qui sont sous votre contrôle.
Le corps du serpent est dans la rangée de
corps . Comme vous pouvez le voir, tout le corps du serpent (y compris la tête - la première cellule) au début est à la même position «x»: 19, «y»: 24. Cela est dû au fait qu'au début du jeu, les serpents sortent du trou, qui est défini par une cellule sur le terrain . De plus, les coordonnées du corps et de la tête seront différentes.
Les structures suivantes (un exemple dans Go) définissent toutes les options de réponse du serveur:
type respData struct { Answer string Session string Data struct { Area [][]int Snakes []struct { Num int Body []Cell Energe int Dead bool } } } type Cell struct { X int Y int }
Ensuite, vous devez envoyer le mouvement effectué par le serpent en ajoutant un
mouvement à la demande GET, par exemple:
...&move=u
u - signifie commande vers le haut;
d - vers le bas;
l - à gauche;
r - à droite;
/ - réduire de moitié.
La commande pour plusieurs serpents (par exemple, pour sept) ressemblera à ceci:
...&move=ud/urld
Un personnage - une équipe. La réponse doit contenir une commande pour tous les serpents sous votre contrôle. Sinon, certains serpents peuvent ne pas recevoir de commande et continueront l'ancienne action.
Le champ est mis à jour à des intervalles de 150 ms. Si aucune commande n'est reçue dans les 60 secondes, le serveur ferme la connexion.
Les références
Afin d'éviter l'habraeffet, pour ceux qui seront intéressés de voir, envoyez-moi un message. En réponse, j'enverrai l'adresse IP de mon serveur. Ou vous pouvez déployer votre serveur en utilisant le code source du programme.
Je ne suis ni spécialiste de la programmation ni des réseaux de neurones. Par conséquent, je peux faire des bévues. Je diffuse le code «tel quel». Je serais heureux si des développeurs plus expérimentés montreraient les erreurs commises.
- Bibliothèque du réseau de neurones avec le jeu "Tic Tac Toe"
- Snake Master - Serveur
- Snake Master - Bot
- Snakeworld2
UPDTéléchargez temporairement
l'adresse IP du serveur . Maintenant, un seul randomiseur de bot (SnakeBot0) y est lancé. J'espère que le serveur ne plante pas si vite.