强化学习+优化:如何做得更好?

2024-02-20

我正在学习如何使用强化学习进行优化。我选择的问题是最大匹配 https://en.wikipedia.org/wiki/Maximum_cardinality_matching在二分图中,因为我可以轻松计算出真正的最优值。

回想一下,图中的匹配是边的子集,其中没有两条边入射到同一节点/顶点上。目标是找到最大的此类子集。

我在下面展示了完整的代码,但首先让我解释一下其中的部分内容。

num_variables = 1000
g = ig.Graph.Random_Bipartite(num_variables, num_variables, p=3/num_variables)
g_matching = g.maximum_bipartite_matching()
print("Matching size", len([v for v in g_matching.matching if v < num_variables and v != -1]))

这将生成一个随机二部图,两组节点中每组都有 1000 个节点。然后它打印出真正最大匹配的大小。

在下面的代码中,self.agent_pos是一个数组,表示当前找到的匹配项。它的长度是原图中的边数,索引处有一个1i如果边缘i包含在内,否则为 0。self.matching是增长匹配中的边集。self.matching_nodes是增长匹配中的节点集,用于检查是否可以添加特定边。

import igraph as ig
from tqdm import tqdm
import numpy as np
import gym
from gym import spaces

from stable_baselines3 import PPO
from stable_baselines3.common.env_util import make_vec_env

class MaxMatchEnv(gym.Env):
    metadata = {'render.modes': ['console']}
    def __init__(self, array_length=10):
        super(MaxMatchEnv, self).__init__()
        # Size of the 1D-grid
        self.array_length = array_length
        self.agent_pos = [0]*array_length
        self.action_space = spaces.Discrete(array_length)
        self.observation_space = spaces.Box(low=0, high=1, shape=(array_length,), dtype=np.uint8)
        self.matching = set()  # set of edges
        self.matching_nodes = set() # set of node ids (ints)
        self.matching_size = len([v for v in g_matching.matching if v < num_variables and v != -1])
        self.best_found = 0
        
    def reset(self):
        # Initialize the array to have random values
        self.time = 0
        #print(self.agent_pos)
        self.agent_pos = [0]*self.array_length
        self.matching = set()
        self.matching_nodes = set()
        return np.array(self.agent_pos)
    
        
    def step(self, action):
        self.time += 1 
        reward = 0
        edge = g.es[action]
        if not(edge.source in self.matching_nodes or edge.target in self.matching_nodes):
            self.matching.add(edge)
            self.matching_nodes.add(edge.source)
            self.matching_nodes.add(edge.target)
            self.agent_pos[action] = 1
            if sum(self.agent_pos) > self.best_found:
                self.best_found = sum(self.agent_pos)
                print("New max", self.best_found)
            reward = 1
        elif self.agent_pos[action] == 1:
            #print("Removing edge", action)
            self.matching_nodes.remove(edge.source)
            self.matching_nodes.remove(edge.target)
            self.matching.remove(edge)
            self.agent_pos[action] = 0
            reward = -1
        done = sum(self.agent_pos) == self.matching_size
        info = {}
        return np.array(self.agent_pos), reward, done, info

    def render(self, mode='console'):
        print(sum(self.agent_pos))

    def close(self):
        pass


if __name__ == '__main__':
 
    num_variables = 1000
    g = ig.Graph.Random_Bipartite(num_variables, num_variables, p=3/num_variables)
    g_matching = g.maximum_bipartite_matching()
    print("Matching size", len([v for v in g_matching.matching if v < num_variables and v != -1]))

    env = make_vec_env(lambda: MaxMatchEnv(array_length=len(g.es)), n_envs=12)

    model = PPO('MlpPolicy', env, verbose=1).learn(10000000)

这存在很多问题,但最主要的问题是它优化得不好。这段代码给出了刚刚超过 550 的结果,然后在真正的最佳值超过 900 的地方停止改进(它是由代码在开始时打印出来的)。

主要问题是:

如何才能做得更好,从而达到更好的匹配?

一个附属问题是,如何打印迄今为止找到的最佳匹配?我尝试使用 self.best_found 来保持最佳分数不起作用,因为它似乎会定期重置。

没有帮助的改变

  • 将 PPO 更改为 DQN 只会产生微小的差异。
  • 我尝试更改代码以便done1000 步后为 True。

变化如下:

if self.time == 1000:
    done = True
else:
    done = False

添加后print(max(env.get_attr("best_found")))代替print("New max", self.best_found)此更改为done完全没有显示出任何优势。


要打印您可以使用的每个环境的最大值get_attr方法来自稳定的基线。更多信息在他们的官方文档 https://stable-baselines.readthedocs.io/en/master/guide/vec_envs.html#stable_baselines.common.vec_env.DummyVecEnv.get_attr.

例如,下面的行将打印 12 个环境中每个环境的最大值,然后打印所有环境的最大值。

print(env.get_attr("best_found"))
print(max(env.get_attr("best_found")))

至于为什么它不收敛,可能是由于选择了错误的奖励,尽管看看你的奖励选择似乎是明智的。我在您的代码中添加了调试打印,以查看某些步骤是否导致done = True,但环境似乎永远不会达到那种状态。我认为对于模型的学习来说,有多个动作序列会导致一个状态done = True,这意味着模型将经历一个情节的结束。我没有详细研究您的代码中的问题,但也许这些信息可以帮助调试您的问题。

如果我们将问题与其他环境进行比较,例如CartPole https://gym.openai.com/envs/CartPole-v1/,我们的剧集以done = True这有助于模型学习更好的策略(在您的情况下,您可以限制每个情节的操作量,而不是永远运行同一情节)。这可以帮助模型避免陷入局部最优,因为你给它在新的情节中“重试”的机会。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

强化学习+优化:如何做得更好? 的相关文章

随机推荐