井字游戏的策略梯度不起作用

人工智能 神经网络 强化学习 政策梯度 火炬
2021-11-14 06:50:32

我想在井字游戏上实施策略梯度。我尝试将适用于任何环境的代码(例如 CartPole-v0)用于我的井字游戏。但这不是学习。没有错误。只是结果太糟糕了。

RandomPlayer(“玩家 X”)vs PolicyAgent(“玩家 O”)

在此处输入图像描述

所以可以看到策略代理在 500 场战斗后没有学习。每场战斗包括与随机玩家的 100 场比赛。一起 500 * 100 场比赛。

有人可以告诉我代码中的问题或错误。我想不明白。或者我需要改进的地方。会很棒的。

这也是一个做同样的项目,我想做,但成功了。 https://github.com/fcarsten/tic-tac-toe/blob/master/tic_tac_toe/DirectPolicyAgent.py 我没有得到我所做的不同。

代码:

套餐:

import torch
import torch as T
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

import numpy as np
import gym
from gym import wrappers

神经网络:

class PolicyNetwork(nn.Module):
    def __init__(self, lr, input_dims, fc1_dims, fc2_dims, n_actions):
        super(PolicyNetwork, self).__init__()
        self.input_dims = input_dims
        self.lr = lr
        self.fc1_dims = fc1_dims
        self.fc2_dims = fc2_dims
        self.n_actions = n_actions

        self.fc1 = nn.Linear(self.input_dims, self.fc1_dims)
        self.fc2 = nn.Linear(self.fc1_dims, self.fc2_dims)
        self.fc3 = nn.Linear(self.fc2_dims, self.n_actions)

        self.optimizer = optim.Adam(self.parameters(), lr=lr)

    def forward(self, observation):
        state = T.Tensor(observation)
        x = F.relu(self.fc1(state))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

保单代理:

class PolicyAgent:
    def __init__(self, player_name):
        self.name = player_name
        self.value = PLAYER[self.name]

    def board_to_input(self, board):
        input_ = np.array([0] * 27)
        for i, val in enumerate(board):
            if val == self.value:
                input_[i] = 1  
            if val == self.value * -1:
                input_[i+9] = 1
            if val == 0:
                input_[i+18] = 1
        return np.reshape(input_, (1,-1))


    def start(self, learning_rate=0.001, gamma=0.1):
        self.lr = learning_rate
        self.gamma = gamma
        self.all_moves = list(range(0,9))
        self.policy = PolicyNetwork(self.lr, 27, 243, 91, 9)
        self.reward_memory = []
        self.action_memory = []

    def turn(self, board, availableMoves):
        state = self.board_to_input(board.copy())
        prob = F.softmax(self.policy.forward(state))
        action_probs = torch.distributions.categorical.Categorical(prob)
        action = action_probs.sample()

        while action.item() not in availableMoves:
            state = self.board_to_input(board.copy())
            prob = F.softmax(self.policy.forward(state))
            action_probs = torch.distributions.categorical.Categorical(prob)
            action = action_probs.sample()

        log_probs = action_probs.log_prob(action)
        self.action_memory.append(log_probs)

        self.reward_memory.append(0)
        return action.item()

    def learn(self, result):
        if result == 0:
            reward = 0.5
        elif result == self.value:
            reward = 1.0
        else:
            reward = 0

        self.reward_memory.append(reward)
        #print(self.reward_memory)

        self.policy.optimizer.zero_grad()
        #G = np.zeros_like(self.action_memory, dtype=np.float64)
        G = np.zeros_like(self.reward_memory, dtype=np.float64)


        #running_add = reward
        #for t in reversed(range(0, len(self.action_memory))):
        #    G[t] = running_add
        #    running_add = running_add * self.gamma

        #'''
        running_add = 0
        for t in reversed(range(0, len(self.reward_memory))):
            if self.reward_memory[t] != 0:
                running_add = 0
            running_add = running_add * self.gamma + self.reward_memory[t]
            G[t] = running_add
        for t in range(len(self.reward_memory)):
            G_sum = 0
            discount = 1
            for k in range(t, len(self.reward_memory)):
                G_sum += self.reward_memory[k] * discount
                discount *= self.gamma
            G[t] = G_sum
        mean = np.mean(G)
        std = np.std(G) if np.std(G) > 0 else 1
        G = (G-mean)/std
        #'''

        G = T.tensor(G, dtype=T.float)

        loss = 0
        for g, logprob in zip(G, self.action_memory):
            loss += -g * logprob

        loss.backward()
        self.policy.optimizer.step()

        self.reward_memory = []
        self.action_memory = []
1个回答

一些建议:

  1. 您有一个循环,其中忽略了 RL 代理的非法移动换句话说,当代理进行非法移动时,它不会受到惩罚,也不会有任何 +/- 奖励。在我的程序中,我将非法移动视为输掉比赛。

  2. 尝试玩一些“预动作”以使游戏更容易。例如我从这个开始(现在是让玩家'X'移动):

    在此处输入图像描述

    然后你可能会更快地看到收敛。从一个非常简单的案例开始总是一个好主意。

  3. 请注意,最佳值不是“完美分数”。如果你赢=2,输=-2,平=1,那么玩家'X'在上述游戏中的最佳分数是1.5,而不是2.0。(你可以检查数学)。

    我编写了一个小型 Python 程序来计算 RL 代理与随机对手比赛的最佳分数(假设两个玩家都不会做出非法动作)。看到这个答案

  4. 对我来说,看到融合需要超过 50K 游戏。它更像是 1-2M 游戏。更正:随着更多的线索,我发现策略梯度似乎无法收敛到最佳值,但它确实随着时间的推移而改善,达到次优分数。)

  5. 你的神经网络可能有点太小了。

备注:这是一个很好的问题,对人工智能也很重要,因为井字游戏是一种适合基于逻辑的代理的游戏,重要的是要了解深度学习在这种“逻辑”领域的表现如何。我自己的研究专注于将逻辑结构与深度学习相结合。

我的代码在这里: https://github.com/Cyber​​netic1/policy-gradient