Dans la continuité de la dernière fois, je voudrais expliquer le code. C'est la dernière fois. Othello-De la troisième ligne de "Implementation Deep Learning" (1) http://qiita.com/Kumapapa2012/items/cb89d73782ddda618c99 Cliquez ici pour les articles suivants. Othello-De la troisième ligne de "Implementation Deep Learning" (3) http://qiita.com/Kumapapa2012/items/3cc20a75c745dc91e826 Othello-De la troisième ligne de "Implementation Deep Learning" (4) [Fin] http://qiita.com/Kumapapa2012/items/9cec4e6d2c935d11f108
Le code source est ici. https://github.com/Kumapapa2012/Learning-Machine-Learning/tree/master/Reversi
Bien que je l'explique, ce n'est qu'un exemple du livre "Implementation Deep Learning" avec un jeu d'Othello. Pour cette raison, cet article expliquera uniquement les modifications de l'exemple et le code de l'Othello créé. Pour la signification et le rôle de chaque fichier de script, le déroulement des opérations et une explication détaillée de Deep Q-Leaning, consultez le livre "Implementation Deep Learning".
Cela fait environ 3 mois que j'ai commencé à étudier correctement, il peut donc y avoir des erreurs. Si vous avez des erreurs, des commentaires, des questions ou des questions, nous vous serions reconnaissants de bien vouloir commenter.
Avant d'exécuter chacun des scripts ci-dessous, l'environnement doit être configuré pour que l'exemple de "Implementation Deep Learning" puisse être exécuté. Le script doit être exécuté après être entré dans l'environnement Anaconda en exécutant une commande telle que la suivante, comme décrit dans le livre "Implementation Deep Learning".
. activate main (Ou la source active principale)
De plus, RL_Glue doit être démarré avant que chaque script puisse être exécuté. (Hors "Game_Reversi_Test.py").
agent.py C'est un «agent» qui effectue un apprentissage automatique. Usage:
python agent.py [--gpu <gpu id>] [--size <board size>] --gpu: ID GPU (si omis ou valeur négative, le processeur sera utilisé) --size: taille de la carte Othello (valeur par défaut 6 si omis)
Description:
C'est un agent qui mène l'apprentissage des matières en utilisant DQN.
La taille de la carte doit correspondre à ce qui a été spécifié dans environment.py au démarrage. [^ 1]
Grâce à RL_Glue, il reçoit le contenu suivant de environment.py sous forme de tableau unidimensionnel, DQN détermine le meilleur déplacement et le renvoie à RL_Glue.
couche | Contenu |
---|---|
0 | Position de votre cadre(Agent) |
1 | Position de la pièce de l'adversaire(Environment) |
2 | Position où vous pouvez placer le cadre |
3 | Position où l'adversaire peut placer la pièce |
L'agent continuera à placer des pièces tant qu'il est indiqué où il peut être placé dans la couche 2. Couche 2 S'il n'y a pas de place pour mettre votre pièce, l'agent passera.
Exemple) Dans les situations suivantes sur une carte 6x6:
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | -1 | 0 | 0 | 0 |
3 | 0 | 1 | 1 | 0 | 0 | 0 |
4 | 0 | 1 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
Le contenu d'entrée est le suivant
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | 0 | 0 | 0 | 0 |
3 | 0 | 1 | 1 | 0 | 0 | 0 |
4 | 0 | 1 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 | 1 | 0 | 0 |
2 | 0 | 0 | 0 | 1 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 1 | 0 | 1 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
En tant que couche d'entrée, le réseau neuronal a le même nombre d'éléments que les informations ci-dessus obtenues à partir de environment.py x le nombre d'actions pouvant être retracées pendant l'entraînement.
Cette fois également, nous avons changé la classe QNet
init '' '' pour avoir 8 couches cachées entièrement connectées.
La couche de sortie comporte un nombre de nœuds correspondant à la taille de la carte.
Parmi les nœuds de cette couche de sortie, celui avec la valeur la plus élevée est l'emplacement de la trame sélectionnée par l'agent.
En outre, il existe les différences suivantes par rapport aux livres.
valeur | Livres | Ce code | La description |
---|---|---|---|
self.n_rows | 3 | size(6 ou plus même) | Taille de la planche |
self.bdim | self.dim * 2 | self.dim * 4 | Taille des données d'entraînement |
self.capacity | 1 * 10**4 | 2 * 10**4 | Replay Memory retention number |
self.n_frames | 3 | 9 | Nombre d'actions remontant à l'apprentissage |
self.batch_size | 32 | 128 | Taille du lot au moment de l'apprentissage |
En ce qui concerne le contenu de la mise en œuvre, bien qu'il y ait quelques changements en fonction des changements de paramètres ci-dessus, le déroulement des opérations est le même que l'explication dans le livre.
environment.py C'est «l'environnement» qui est l'adversaire de l'agent. Usage:
python environment.py [--size <board size>] --size: taille de la carte Othello (valeur par défaut 6 si omis)
Description:
De l'exemple "environment.py" dans le livre "Implementation Deep Learning", la logique de la troisième ligne est supprimée, et la logique du jeu est implémentée dans "Game_Reversi.py".
Par conséquent, une instance de Game_Reversi est créée et initialisée comme suit.
environment.py
import Game_Reversi as game_base
#(Omission)
def __init__(self, size):
self.game= game_base.Game_Reversi(size,size)
#(Ce qui suit est omis)
Le plateau de jeu sera un carré de la taille spécifiée et sera conservé dans le g_board de la classe Game_Reversi sous la forme d'un tableau N x N Int. Chaque élément du tableau contient l'une des valeurs suivantes:
0 | Espace libre |
1 | Agent |
-1 | environnement |
En exprimant le joueur avec une valeur positive ou négative de 1, les actions et les jugements peuvent être exécutés simplement en inversant le signe, et le jeu peut être joué avec un minimum de code. Le statut du jeu décrit dans "agent.py" est créé par la fonction "build_map_from_game".
environment.py
def build_map_from_game(self):
map_data=[]
#0: carte actuelle(Agent=Position de 1 cadre)
board_data=(self.game.g_board.reshape(-1)== self.game.turn).astype(int)
map_data.extend(board_data)
#1: carte actuelle(Environment=-Position de 1 cadre)
board_data=(self.game.g_board.reshape(-1)==-self.game.turn).astype(int)
map_data.extend(board_data)
#2: Un endroit où l'agent peut être placé."turn"Agent et Environnement sont exprimés par le positif et le négatif de.
#S'il est positif, il devient un agent.
pos_available=np.zeros_like(self.game.g_board)
l_available=self.game.getPositionAvail(self.game.turn)
for avail in l_available:
pos_available[tuple(avail)]=1
map_data.extend(pos_available.reshape(-1).tolist())
#3: Lieu où l'environnement peut être placé
pos_available=np.zeros_like(self.game.g_board)
l_available=self.game.getPositionAvail(-self.game.turn)
for avail in l_available:
pos_available[tuple(avail)]=1
map_data.extend(pos_available.reshape(-1).tolist())
return map_data
Fondamentalement, l'état de la carte est créé et l'endroit où le cadre peut être placé est créé en modifiant le signe de virage, et un tableau unidimensionnel est créé. Au début du jeu, réinitialisez simplement le tableau et envoyez le contenu créé par la fonction ci-dessus.
environment.py
def env_start(self):
# plan:Initialisation de la carte Reversi
self.game.resetBoard()
#Créer des données cartographiques
self.map=self.build_map_from_game()
#(Omission)
#RL l'état de la carte_Passer à l'agent via la colle
observation = Observation()
observation.intArray = self.map
return observation
Après le début du jeu, l'agent spécifiera l'action (où placer la pièce) pour effectuer les actions suivantes.
Les actions de l'agent sont des valeurs entières. Pour une carte 6x6, c'est un entier de -1 ≤ a ≤ 35. -1 est le chemin. Utilisez la taille du tableau pour le convertir en un tuple de ligne et de colonne du tableau et transmettez-le à la méthode step de Game_Reversi pour effectuer l'action.
environment.py
if int_action_agent==-1 :
step_raw_col=(-1,-1)
else :
step_raw_col=(int_action_agent//self.n_cols,int_action_agent%self.n_cols)
#exécution des étapes
step_o, step_r, step_done = self.game.step(step_raw_col
Lorsque l'action est effectuée, le statut du plateau, les récompenses et les indicateurs indiquant si le jeu est réglé sont passés. À ce stade, le g_board de Game_Reversi a été mis à jour avec les éléments d'agent et les éléments d'environnement. Dans cet état, utilisez la fonction "build_map_from_game" pour créer un état de la carte.
Enfin, l'état du tableau, la récompense et le fait qu'il soit réglé ou non sont stockés dans l'instance rot de la classe Reward_observation_terminal de RL_Glue et renvoyés à RL_Glue.
environment.py
# (Omission)
rot = Reward_observation_terminal()
# build_map_from_game()Créez une carte avec.
self.map=self.build_map_from_game()
observation = Observation()
observation.intArray = self.map
rot.o = observation
# step_r est une récompense, étape_fait est de savoir s'il faut continuer ou non
rot.r=step_r
rot.terminal = step_done
# (Omission)
#S'il est réglé, l'agent de l'agent_end
#S'il n'y a pas de règlement, l'agent de l'agent_Continuer à l'étape
return rot
experiment.py C'est une «expérience» qui gère le jeu. Usage:
Description: Ce script n'a pas été modifié par rapport au contenu du livre "Implementation Deep Learning", je vais donc omettre l'explication.
Game_Reversi.py Implémentation du jeu Othello. Usage:
Description: Met en œuvre les règles du jeu Othello. Selon les règles officielles d'Othello, l'endroit où placer la pièce est limité à l'endroit où la pièce de l'adversaire peut être retournée. De plus, le pass est limité lorsqu'il n'y a pas de place pour mettre votre propre pièce. Ce jeu suit également ces règles.
Le plateau du jeu est représenté par le tableau Int de Numpy. Pour cette raison, la logique principale est implémentée à l'aide d'opérations de tableau. [^ 2]
Le déroulement des opérations est le suivant:
Code d'initialisation.
Game_Reversi.py
def __init__(self,n_rows,n_cols):
#Réinitialisation de la carte
self.n_rows = n_rows
self.n_cols = n_cols
self.g_board=np.zeros([self.n_rows,self.n_cols],dtype=np.int16)
#Othello met les 4 premières images au centre
self.g_board[self.n_rows//2-1,self.n_cols//2-1]=1
self.g_board[self.n_rows//2-1,self.n_cols//2]=-1
self.g_board[self.n_rows//2,self.n_cols//2-1]=-1
self.g_board[self.n_rows//2,self.n_cols//2]=1
Comme vous pouvez le voir, il est possible de gérer des planches non carrées, mais actuellement du côté environment.py, nous essayons de démarrer le jeu avec uniquement des carrés.
Pour placer une pièce et renvoyer la pièce de l'adversaire, vous devez d'abord spécifier l'endroit où vous pouvez placer la pièce. Ceci est fait avec isValidMove (). isValidMove () utilise une opération de tableau pour déterminer où placer une image. Par exemple, sur une carte 8x8, ● essaie de placer un cadre à l'emplacement X suivant (2,1).
Déterminez si vous pouvez placer un cadre à cet endroit selon le flux suivant. Phase1: Tout d'abord, il recherche les images adjacentes dans 8 directions à l'emplacement spécifié. Vérifiez s'il y a un cercle à l'endroit surligné en vert. S'il n'y a pas de ○, à ce stade, il est jugé que ● ne peut pas être placé à cet endroit et le processus se termine. Dans ce cas, il y a un cercle à l'endroit indiqué en rouge, alors passez à la phase 2.
Phase2: Recherchez ● dans la direction de ○ trouvé. La plage de recherche est surlignée en jaune. La recherche se poursuit dans la direction du cercle et se poursuit jusqu'à ce que vous trouviez un ●, un espace ou le bord du plateau. Au début, pour le cercle en (3,2) ci-dessous, un espace est trouvé en (5,4), donc ce cercle ne peut pas être retourné. Ensuite, environ ○ dans (2,2). Si vous recherchez vers la droite, vous trouverez ● à (2,5). À ce stade, vous pouvez mettre un ● à la position X (2,1).
Le code suivant les implémente.
Game_Reversi.py
#Vérifiez dans toutes les directions à partir de l'emplacement désigné.
#Fin s'il y a même une pièce de l'adversaire qui peut être retournée
for direction in ([-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]):
if not (0 <= (pos+direction)[0] < self.n_rows and 0 <= (pos+direction)[1] < self.n_cols ) :
#Ignorer le traitement hors de portée
continue
#
# Phase 1:La couleur du cadre adjacent est-elle l'opposé de la couleur spécifiée?
#
cpos = pos + direction
if (self.g_board[tuple(cpos)] == -c):
#
# Phase 2:Y a-t-il votre propre cadre là-bas?
#
while (0 <= cpos [0] < self.n_rows and 0 <= cpos [1] < self.n_cols):
if (self.g_board[tuple(cpos)] == 0):
#Il se termine parce que c'est un espace vide avant que le jugement ne soit rendu
break
elif (self.g_board[tuple(cpos)] == -c):
#Si vous avez votre propre cadre à l'avenir, vous pourrez peut-être le prendre.
#Continuez à rechercher votre propre cadre.
cpos = cpos+direction
continue
elif (self.g_board[tuple(cpos)] == c):
#Puisqu'au moins une image peut être renvoyée, la recherche se termine à ce stade.
result=True
break
else:
print("catastorophic failure!!! @ isValidMove")
exit()
Lancer isValidMove () sur tous les cadres vides vous donnera une liste des endroits où vous pouvez placer les cadres. Ceci est implémenté avec la fonction getPositionAvail ().
def getPositionAvail(self,c):
temp=np.vstack(np.where(self.g_board==0))
nullTiles=np.hstack((temp[0].reshape(-1,1),temp[1].reshape(-1,1)))
#IsValidMove pour les carrés sans cadres()
can_put=[]
for p_pos in nullTiles:
if self.isValidMove(p_pos[0],p_pos[1],c):
can_put.append(p_pos)
return can_put
L'agent et l'environnement choisissent tous deux l'endroit où placer la pièce dans la liste des endroits où cette pièce peut être placée. De plus, cette fonction peut également obtenir une liste des endroits où l'adversaire peut placer une image simplement en inversant le code de la image.
Retournez le cadre avec putStone (). Similaire à isValidMove, cette fonction recherche les trames dans 8 directions et exécute les phases 1 et 2. Si vous trouvez une pièce de la même couleur que la vôtre, retournez à l'endroit où vous avez placé la pièce et retournez-la. Dans l'exemple précédent, les cadres sont placés dans (2,1) et les mêmes cadres de couleur se trouvent dans (2,5), de sorte que les cadres sont de l'ordre de (2,4), (2,3), (2,2). Tourner. Ci-dessous le code.
Game_Reversi.py
for dir in ([-1,-1],[-1,0],[-1,1],[ 0,-1],[ 0,1],[ 1,-1],[ 1,0],[ 1,1]):
f_canFlip=False
cpos = pos + dir
#(Omission)
#Renvoie une image de la position cpos actuelle à la position spécifiée.
if f_canFlip :
cpos=cpos-dir #Première image à revenir
while np.array_equal(cpos,pos) == False:
board[tuple(cpos)] = c
numFlip = numFlip + 1
cpos=cpos-dir
Cette fonction renvoie le nombre d'images inversées. Si le 4ème argument est Vrai, le mode sera simulé et le contenu retourné ne sera pas reflété dans le jeu. Ceci est utilisé lorsque vous souhaitez vérifier à l'avance où placer les cadres et combien de cadres peuvent être retournés. Cette fonction peut également changer la couleur du cadre inversé simplement en inversant le signe.
L'agent utilise DQN pour déterminer où placer les pièces, tandis que l'environnement utilise getPosition () pour déterminer où placer les pièces. La logique de getPosition () détermine la force d'Othello. Dans ce code, l'endroit où placer le cadre est décidé par la logique suivante.
probabilité | endroit |
---|---|
90% | L'un des quatre coins |
80% | Position où vous pouvez obtenir le plus de cadres |
10% or 20% | Aléatoire(Quand une pièce peut être placée quelque part dans les quatre coins 10%Si vous ne pouvez pas le mettre 20%) |
Ci-dessous le code.
Game_Reversi.py
#Décidez si vous voulez le rendre aléatoire
t_rnd=np.random.random()
# 1.90 s'il y a des cornes%Allez-y avec une probabilité de
if cornerPos != []:
if t_rnd < 0.9:
returnPos= cornerPos[np.random.randint(0,len(cornerPos))]
# 2.Puis 80%Obtenez celui avec le plus grand nombre.
if returnPos==[]:
if maxPos != []:
if t_rnd < 0.8:
returnPos= maxPos
# 3.Aléatoire sinon décidé à ce stade(Après tout, 1,Il est possible que ce soit 2)
if returnPos==[]:
returnPos= can_put[np.random.randint(0,len(can_put))]
return returnPos
Avec ce qui précède, lorsque les actions à la fois de l'agent et de l'environnement sont terminées, le jugement final et le calcul de la récompense sont effectués. Le jugement final est effectué en trouvant un endroit où les deux cadres peuvent être placés. Le jeu se termine lorsqu'il n'y a pas de place pour mettre les deux. La récompense est de 0 pendant que le jeu est en cours, lorsque le jeu est terminé, le nombre des deux pièces est compté, et si elles sont identiques, cela signifie "dessiner" -0,5, s'il y a beaucoup d'agents, cela signifie "gagner" 1.0, et il y a peu d'agents. Par exemple, "perdre" vous donnera une récompense de -1,0. Le code est comme suit.
Game_Reversi.py
stonePos_agent = self.getPosition(self.turn)
stonePos_environment = self.getPosition(-self.turn)
if stonePos_agent==[] and stonePos_environment==[]:
done=True
if self.render : print("****************Finish****************")
if done :
# 2.Calcul de la compensation une fois terminé
num_agent=len(np.where(self.g_board==self.turn)[1])
num_envionment=len(np.where(self.g_board==-self.turn)[1])
if self.render : print("you:%i/environment:%i" % (num_agent,num_envionment))
#Jugement
if num_agent > num_envionment :
reward=1.0
if self.render : print("you win!")
elif num_agent < num_envionment :
reward=-1.0
if self.render : print("you lose!")
else :
reward=-0.5
if self.render : print("Draw!")
Game_Reversi_Test.py C'est pour tester le "Game_Reversi.py" ci-dessus. Vous permet de jouer contre des humains au nom d'agents. Usage:
python Game_Reversi_Test.py
Description: La taille de la carte est codée en dur à 8x8 comme indiqué ci-dessous, alors changez-la en conséquence.
Game_Reversi_Test.py
g=game.Game_Reversi(8,8)
Lorsqu'il est démarré, il attend l'entrée de l'utilisateur. Spécifiez la position pour placer le cadre avec un entier séparé par des virgules tel que "2,4". Comme pour la réaction à l'agent, placez la pièce à l'emplacement spécifié et placez votre propre pièce. Et encore une fois, il attendra l'entrée. Lorsque les deux parties ne peuvent pas placer une pièce à la fin, le jugement final et le score sont affichés et le processus se termine.
C'est tout pour l'explication. Nous sommes impatients de vous aider. Si vous avez des erreurs, des commentaires, des questions ou des questions, nous vous serions reconnaissants de bien vouloir commenter.
(Livre) Mise en œuvre Deep Learning
http://shop.ohmsha.co.jp/shopdetail/000000004775/
Règles d'Othello
http://www.othello.org/lesson/lesson/rule.html
(D'autres références seront publiées à une date ultérieure)
[^ 1]: Cependant, puisque agent_init reçoit des informations de RL_Glue, si vous l'utilisez, vous n'aurez peut-être pas besoin de cet argument? [^ 2]: Pour cette raison, je pense qu'il est possible d'utiliser Numpy (cupy) de Cuda. [^ 3]: Au fait, si la couleur du premier venu, c'est-à-dire l'agent (= 1) est noire selon la règle officielle, selon la règle fixée par la "Japan Othello Federation", la disposition des cadres initiaux de cette classe est inversée en noir et blanc. C'est vrai. Cependant, l'aspect pivote de 90 degrés et il n'y a pas de changement théorique, alors laissez-le tel quel.
Recommended Posts