返回代码索引

base_runner.py

训练器/buffer 初始化及公共流程

原文件 · 203 行 · 8644 字节

runner/shared/base_runner.py
SHA-256: 089753fc171381140f5c2d34b98becd9072a8686e8f804eb928199c7d91c8be7

1import os2import numpy as np3import torch4from tensorboardX import SummaryWriter5from envs.macf.env_wrappers import DummyVecEnv6from utils.obs_utils import flat_obs_space, flat_state_space78def _t2n(x):9    """Convert torch tensor to a numpy array."""10    return x.detach().cpu().numpy()111213class Runner(object):14    """15    Base class for training recurrent policies.16    :param config: (dict) Config dictionary containing parameters for training.17    """18    def __init__(self, config):1920        self.config = config21        self.use_gat = config.use_gat22        self.envs: DummyVecEnv = config.envs23        self.eval_envs = config.eval_envs24        self.device = config.device25        self.num_agents = config.num_agents26        # if config.__contains__("render_envs"):27        #     self.render_envs = config['render_envs']       2829        # parameters30        self.env_name = config.env_name31        self.algorithm_name = config.algorithm_name32        self.experiment_name = config.experiment_name33        self.use_centralized_V = config.use_centralized_V34        self.use_obs_instead_of_state = config.use_obs_instead_of_state35        self.num_env_steps = config.num_env_steps36        self.episode_length = config.episode_length37        self.n_rollout_threads = config.n_rollout_threads38        self.n_eval_rollout_threads = config.n_eval_rollout_threads39        # self.n_render_rollout_threads = config.n_render_rollout_threads40        self.use_linear_lr_decay = config.use_linear_lr_decay41        self.hidden_size = config.hidden_size42        self.use_render = config.use_render43        self.recurrent_N = config.recurrent_N4445        # interval46        self.save_interval = config.save_interval47        self.use_eval = config.use_eval48        self.eval_interval = config.eval_interval49        self.eval_episodes = config.eval_episodes50        self.eval_seed = config.eval_seed51        self.log_interval = config.log_interval52        self.log_info_interval = config.log_info_interval5354        # dir55        self.model_dir = config.model_dir5657        self.run_dir = config.run_dir58        self.log_dir = str(getattr(config, "eval_output_dir", self.run_dir))59        self.tb_dir = str(getattr(config, "tb_dir", self.run_dir / 'tb'))60        if not os.path.exists(self.log_dir):61            os.makedirs(self.log_dir)62        if not os.path.exists(self.tb_dir):63            os.makedirs(self.tb_dir)64        self.writter = SummaryWriter(self.tb_dir)65        self.save_dir = str(self.run_dir / 'models')66        if not os.path.exists(self.save_dir):67            os.makedirs(self.save_dir)6869        if self.use_gat:70            from algorithms.algorithm.ga_rmappo import GARMAPPO as TrainAlgo71            from algorithms.algorithm.gaRMAPPOPolicy import GAMAPPOPolicy as Policy72            from utils.shared_gat_buffer import SharedGatReplayBuffer as Buffer73        else:74            from algorithms.algorithm.r_mappo import RMAPPO as TrainAlgo75            from algorithms.algorithm.rMAPPOPolicy import RMAPPOPolicy as Policy76            from utils.shared_buffer import SharedReplayBuffer as Buffer7778        obs_space = self.envs.observation_space[0]       # Dict space79        share_observation_space = self.envs.share_observation_space[0] if self.use_centralized_V else self.envs.observation_space[0]8081        # Spaces provide dimensions; HATT keeps Dict observations until its actor,82        # while standard MAPPO and legacy GAT flatten/fuse them inside the policy.83        obs_box = flat_obs_space(obs_space)84        share_box = flat_state_space(share_observation_space) if hasattr(share_observation_space, 'spaces') else share_observation_space85        self.policy = Policy(config,86                            obs_box,87                            share_box,88                            self.envs.action_space[0],89                            device = self.device)9091        if self.model_dir is not None:92            self.restore()9394        # algorithm95        self.trainer = TrainAlgo(config, self.policy, device = self.device)9697        # buffer — 传入 Dict space,buffer 内部处理 dict obs98        self.buffer = Buffer(config,99                             self.num_agents,100                             obs_space,101                             share_observation_space,102                             self.envs.action_space[0])103104    def run(self):105        """Collect training data, perform training updates, and evaluate policy."""106        raise NotImplementedError107108    def warmup(self):109        """Collect warmup pre-training data."""110        raise NotImplementedError111112    def collect(self, step):113        """Collect rollouts for training."""114        raise NotImplementedError115116    def insert(self, data):117        """118        Insert data into buffer.119        :param data: (Tuple) data to insert into training buffer.120        """121        raise NotImplementedError122    123    @torch.no_grad()124    def compute(self):125        """Calculate returns for the collected data."""126        self.trainer.prep_rollout()127        next_share_obs = {128            k: np.concatenate(v[-1]) for k, v in self.buffer.share_obs.items()129        } if isinstance(self.buffer.share_obs, dict) else np.concatenate(self.buffer.share_obs[-1])130        next_values = self.trainer.policy.get_values(131            next_share_obs,132            np.concatenate(self.buffer.rnn_states_critic[-1]),133            np.concatenate(self.buffer.masks[-1]),134        )135        next_values = np.array(np.split(_t2n(next_values), self.n_rollout_threads))136        self.buffer.compute_returns(next_values, self.trainer.value_normalizer)137    138    def train(self):139        """Train policies with data in buffer. """140        self.trainer.prep_training()141        train_infos = self.trainer.train(self.buffer)      142        self.buffer.after_update()143        return train_infos144145    def save(self, tag=None):146        """Save policy's actor and critic networks.147148        Args:149            tag: optional suffix appended to the filename (e.g. ``"ep000500"``150                 produces ``actor_ep000500.pt``).  When *None* (the default)151                 the canonical ``actor.pt`` / ``critic.pt`` files are written.152        """153        actor_suffix = f"_{tag}" if tag else ""154        critic_suffix = f"_{tag}" if tag else ""155        actor_name = f"actor{actor_suffix}.pt"156        critic_name = f"critic{critic_suffix}.pt"157        valuenorm_name = f"valuenorm{critic_suffix}.pt"158159        if hasattr(self.trainer.policy, "get_actor_checkpoint"):160            actor_state = self.trainer.policy.get_actor_checkpoint()161        else:162            actor_state = self.trainer.policy.actor.state_dict()163        torch.save(actor_state, os.path.join(str(self.save_dir), actor_name))164        policy_critic = self.trainer.policy.critic165        torch.save(policy_critic.state_dict(), os.path.join(str(self.save_dir), critic_name))166        value_normalizer = self.trainer.value_normalizer167        if value_normalizer is not None:168            torch.save(169                value_normalizer.state_dict(),170                os.path.join(str(self.save_dir), valuenorm_name),171            )172173    def restore(self):174        """Restore policy's networks from a saved model."""175        policy_actor_state_dict = torch.load(176            str(self.model_dir) + '/actor.pt', map_location=self.device177        )178        if hasattr(self.policy, "load_actor_checkpoint"):179            self.policy.load_actor_checkpoint(policy_actor_state_dict)180        else:181            self.policy.actor.load_state_dict(policy_actor_state_dict)182        if not self.config.use_render:183            policy_critic_state_dict = torch.load(str(self.model_dir) + '/critic.pt')184            self.policy.critic.load_state_dict(policy_critic_state_dict)185 186    def log_train(self, train_infos, total_num_steps):187        """188        Log training info.189        :param train_infos: (dict) information about training update.190        :param total_num_steps: (int) total number of training env steps.191        """192        for k, v in train_infos.items():193            self.writter.add_scalar(k, v, total_num_steps)194195    def log_env(self, env_infos, total_num_steps):196        """197        Log env info.198        :param env_infos: (dict) information about env state.199        :param total_num_steps: (int) total number of training env steps.200        """201        for k, v in env_infos.items():202            if len(v)>0:203                self.writter.add_scalar(k, np.mean(v), total_num_steps)