用AI解释AI - 《格斗之王AI》代码解读

目录

一、说明

 二、AI的解释

==train.py部分==

1. os.makedirs

 2. SubprocVecEnv([make_env( )...])

 3. env = retro.make()

4. gym.Wrapper 

5. env = Monitor(env) 

 6. PPO()参数

 7. n_epochs和total_timesteps的关系

8. PPO算法采样流程 

9. Linear scheduler 

10.  CheckpointCallback

11. ep_len_mean、approx_kl等指标解释

==street_fighter_custom_wrapper.py部分==

12. gym.spaces.Box()

13. obs形状(200,256,3)

==test.py部分==

14 .custom_objects 


一、说明

该项目针对B站UP主 林亦LYi 的作品 格斗之王!AI写出来的AI竟然这么强!进行代码解读。

因为我们之前对此接触较少,所以我们借助AI的力量进行解读。在理解的基础上,做了一些整理。

林亦LYi项目代码:GitHub - linyiLYi/street-fighter-ai: This is an AI agent for Street Fighter II Champion Edition.

此外,针对项目代码的视频解释,《格斗之王AI》代码逐行解读_哔哩哔哩_bilibili。 

 二、AI的解释

基于AI生成,可能对理解代码有帮助。(复制答案的过程中,数字标号有误,可忽略)

==train.py部分==

1. os.makedirs

问:os.makedirs(LOG_DIR, exist_ok=True)什么意思
答:os.makedirs(LOG_DIR, exist_ok=True) 是一个用于创建目录的 Python 代码片段。
os.makedirs() 是一个函数,用于递归创建多层目录。它接受两个参数:
LOG_DIR:要创建的目录的路径。这是一个表示目录路径的字符串变量。
exist_ok=True:一个可选的参数,表示如果目录已经存在是否忽略错误。如果设置为 True,则在目录已存在的情况下不会引发异常;如果设置为 False(默认值),则在目录已存在时会引发 FileExistsError 异常。

 2. SubprocVecEnv([make_env( )...])

问:解释下env = SubprocVecEnv([make_env(game, state="Champion.Level12.RyuVsBison", seed=i) for i in range(NUM_ENV)])
答:这段代码使用 OpenAI Gym 的 SubprocVecEnv 类创建了一个并行化的环境,以便在多个并行的游戏环境中进行训练或评估。

下面是代码中的关键部分的解释:

1. `make_env(game, state="Champion.Level12.RyuVsBison", seed=i)`:这是一个自定义函数 `make_env`,用于创建游戏环境。它接受三个参数:游戏名称、游戏状态和随机种子。在这里,使用游戏名称和特定的游戏状态来创建游戏环境。

2. `NUM_ENV`:这是一个表示要创建的并行环境数量的变量。

3. `[make_env(game, state="Champion.Level12.RyuVsBison", seed=i) for i in range(NUM_ENV)]`:这是一个列表推导式,用于创建多个游戏环境。在这里,使用 `make_env` 函数创建了 `NUM_ENV` 个游戏环境,并将它们放入一个列表中。

4. `SubprocVecEnv([...])`:这是使用 SubprocVecEnv 类创建并行环境的部分。`SubprocVecEnv` 是 Gym 提供的一种环境类,它可以在多个并行进程中运行多个环境实例。它接受一个包含游戏环境的列表作为参数,并返回一个并行化的环境对象。

综合起来,这段代码的目的是使用 SubprocVecEnv 创建多个并行运行的游戏环境,每个环境都使用相同的游戏名称和特定的游戏状态。这样可以在训练或评估过程中同时在多个环境中进行并行操作,以提高效率和性能。

 3. env = retro.make()

问:解释下        env = retro.make(
            game=game, 
            state=state, 
            use_restricted_actions=retro.Actions.FILTERED, 
            obs_type=retro.Observations.IMAGE    
        )

答:这段代码使用 OpenAI Retro 库创建了一个游戏环境。

下面是代码中的关键部分的解释:

1. `retro.make()`:这是 Retro 库中的一个函数,用于创建游戏环境。它接受几个参数来指定游戏名称、游戏状态和其他选项。

1. `game`:这是一个参数,表示游戏的名称。它指定了要创建的游戏环境所对应的游戏。

1. `state`:这是一个参数,表示游戏的状态。游戏状态是游戏的一个特定快照或初始状态。通过指定游戏状态,可以从特定的游戏进度或关卡开始。

1. `use_restricted_actions`:这是一个参数,用于指定游戏环境是否使用受限制的动作集。在 Retro 中,游戏通常提供了完整的动作空间,但有些动作可能是不必要或无效的。通过设置 `use_restricted_actions=retro.Actions.FILTERED`,可以使用经过筛选的动作集来减少动作空间的大小。

1. `obs_type`:这是一个参数,用于指定观测的类型。在这里,`obs_type=retro.Observations.IMAGE` 表示观测的类型为图像。这意味着游戏环境将返回图像作为观测结果。

综合起来,这段代码的目的是使用 Retro 库创建一个特定游戏和状态的游戏环境。它还指定了使用受限制的动作集和返回图像观测结果。这样,您可以在游戏环境中进行训练、评估或其他与游戏交互相关的操作。

4. gym.Wrapper 

问:介绍下gym.Wrapper,什么情况可以继承使用它?
答:`gym.Wrapper` 是 Gym 库中的一个基类,用于创建自定义的环境包装器。环境包装器是一种通过包装现有环境对象来修改或扩展其功能的方法。通过继承 `gym.Wrapper` 类并实现自定义的方法,可以对环境进行灵活的修改和定制。

`gym.Wrapper` 类的主要目的是允许开发者在不修改原始环境代码的情况下对环境进行预处理、后处理或功能增强。以下是一些情况下可以继承并使用 `gym.Wrapper` 的例子:

1. **预处理和后处理**:您可以使用 `gym.Wrapper` 对环境的观测、奖励或动作进行预处理或后处理。例如,您可以对观测进行缩放、归一化或降维,对奖励进行调整或裁剪,或者对动作进行转换。

1. **记录和日志**:您可以在 `gym.Wrapper` 中添加代码来记录环境的状态、动作、奖励等信息,并生成日志或记录文件。这对于调试、分析和可视化训练过程非常有用。

1. **修改环境行为**:您可以通过继承 `gym.Wrapper` 并重写相关方法来修改环境的行为。例如,您可以更改环境的重置逻辑,修改动作空间或观测空间的定义,或者添加额外的约束或限制。

1. **增加额外功能**:您可以通过继承 `gym.Wrapper` 并添加自定义方法和属性来增加额外的功能。这可以包括与环境交互的特定行为、特殊的数据收集或处理,或其他自定义需求。

通过继承 `gym.Wrapper` 类,您可以灵活地修改和扩展 Gym 环境,以满足特定的需求和目标。它提供了一种可重复使用和模块化的方式来定制环境行为,同时保持与 Gym 环境的兼容性和一致性。

5. env = Monitor(env) 

问:解释env = Monitor(env)
答:`Monitor(env)` 是 Gym 库中的一个环境包装器(wrapper),用于监视和记录环境的交互过程。

下面是对这段代码的解释:

- `Monitor` 是 Gym 提供的一个环境包装器类,用于跟踪和记录环境的状态、动作和奖励等信息。
- `env` 是一个 Gym 环境对象,表示要被包装和监视的原始环境。
- `Monitor(env)` 创建了一个 `Monitor` 的实例对象,并将原始环境 `env` 作为参数传递给它。

通过将环境包装在 `Monitor` 中,您可以实现以下功能:

1. **记录交互信息**:`Monitor` 会记录每个时间步的观测、动作和奖励等信息。这可以用于后续分析、可视化或回放环境交互过程。

1. **统计信息**:`Monitor` 会计算并记录环境的统计信息,如累积奖励、步数和完成的任务数等。这对于训练过程的监控和性能评估很有用。

1. **生成日志和记录文件**:`Monitor` 可以将交互信息和统计信息写入日志文件或记录文件中,以便后续分析和查看。它提供了灵活的配置选项,可以指定日志文件的位置、格式和频率等。

综上所述,`env = Monitor(env)` 将原始环境 `env` 包装在 `Monitor` 中,使得我们能够轻松地记录和分析环境的交互过程,以及获取有关环境性能的统计数据。

 6. PPO()参数

问:解释下from stable_baselines3 import PPO
    model = PPO(
        "CnnPolicy", 
        env,
        device="cuda", 
        verbose=1,
        n_steps=512,
        batch_size=512,
        n_epochs=4,
        gamma=0.94,
        learning_rate=lr_schedule,
        clip_range=clip_range_schedule,
        tensorboard_log="logs"
    )
答:这段代码使用 Stable Baselines3 库中的 Proximal Policy Optimization(PPO)算法创建了一个模型对象。

下面是代码中的关键部分的解释:

1. `from stable_baselines3 import PPO`:这行代码导入了 Stable Baselines3 库中的 PPO 类,用于实现 PPO 算法。

1. `model = PPO("CnnPolicy", env, device="cuda", verbose=1, n_steps=512, batch_size=512, n_epochs=4, gamma=0.94, learning_rate=lr_schedule, clip_range=clip_range_schedule, tensorboard_log="logs")`:这行代码创建了一个 PPO 模型对象,并将其赋值给 `model` 变量。

   - `"CnnPolicy"`:这是指定模型的策略网络类型。在这里,使用的是卷积神经网络(CNN)作为策略网络。

   - `env`:这是 Gym 环境对象,表示模型将在该环境中进行训练或评估。

   - `device="cuda"`:这是指定模型在哪个设备上运行。在这里,模型将在 CUDA 支持的 GPU 上运行。

   - `verbose=1`:这是指定训练过程中输出的详细程度。在这里,设置为 1 表示输出额外的训练信息。

   - `n_steps=512`:这是指定每次采集样本时的步数。在 PPO 中,每次采集一批样本用于更新策略。
    【编者注:The number of steps to run for each environment per update (i.e. rollout buffer size is n_steps * n_envs where n_envs is number of environment copies running in parallel)
每个环境每次更新要运行的步数(即滚出缓冲区大小为n_steps * n_envs,其中n_envs是并行运行的环境副本的数量)】
   - `batch_size=512`:这是指定每次更新策略时的样本批次大小。
    【注:Minibatch size】
   - `n_epochs=4`:这是指定每次更新策略时的迭代次数。
    【注:Number of epoch when optimizing the surrogate loss 
        优化代理损失时的历元数,
        我查看了train()部分的源代码,应该就是训练的epochs迭代轮数。这里要注意和后面的learn()中参数total_timesteps结合使用,模型会达到较小的一个值后停止训练】
   - `gamma=0.94`:这是指定折扣因子(discount factor),用于计算累积奖励的折扣累加。

   - `learning_rate=lr_schedule`:这是指定学习率(learning rate),可以是一个常数或学习率调度器(learning rate schedule)。

   - `clip_range=clip_range_schedule`:这是指定 PPO 中的策略优化剪切范围(clipping range),可以是一个常数或剪切范围调度器。

   - `tensorboard_log="logs"`:这是指定保存 TensorBoard 日志的路径,用于可视化训练过程中的指标和曲线。

综合起来,这段代码创建了一个 PPO 模型对象,配置了模型的策略网络类型、环境、设备、训练参数等。这个模型可以用于在指定环境中进行训练、评估和使用强化学习技术解决问题。

 7. n_epochs和total_timesteps的关系

问:在stable_baselines3库PPO算法中,参数n_epochs和total_timesteps的关系是什么?
答:这两个参数在训练过程中的关系可以理解为:

n_epochs 决定了每次参数更新时的迭代次数,即在每个批次上执行的优化步数。
total_timesteps 决定了训练过程中采样和使用的总的环境交互步数。
在训练过程中,PPO 算法会迭代执行多个批次的参数更新,直到达到指定的总步数 total_timesteps。
在每个批次中,模型会使用 n_epochs 指定的迭代次数来执行参数更新。
因此,总的训练步数 total_timesteps 可以看作是 n_epochs 的一个上界,即训练过程中可能的最大迭代次数。

8. PPO算法采样流程 

问:在stable_baselines3库PPO算法中,环境采样次数设置为n_steps后,采样流程是什么样的?
答:在 Stable Baselines3 库的 PPO 算法中,设置环境采样次数为 `n_steps` 后,采样流程如下:

1. 初始化环境的初始状态。

1. 在每个采样过程中,执行以下循环:

   a. 根据当前状态选择一个动作,并将动作应用于环境。

   b. 观察环境的下一个状态、奖励和是否终止等信息。

   c. 将观察到的状态、动作、奖励等信息添加到经验缓存中。

   d. 如果采样次数达到或超过 `n_steps`,或者环境进入终止状态,终止循环。

   e. 将当前状态更新为下一个状态。

9. Linear scheduler 

问:介绍下Linear scheduler
答:Linear scheduler(线性调度器)是一种常见的调度器类型,用于在训练过程中动态地调整某个参数的值。它基于线性插值的原理,在训练的不同阶段逐渐改变参数的值。

线性调度器通常具有以下几个关键属性:

起始值(initial value):线性调度器的参数起始值是在训练开始时设置的初始值。

结束值(final value):线性调度器的参数结束值是在训练结束时期望的最终值。

总步数(total steps):线性调度器在训练过程中会线性地改变参数的值,总步数定义了从起始值到结束值所经过的步数。
current_value = initial_value + (final_value - initial_value) * (current_step / total_steps)

10.  CheckpointCallback

问:解释CheckpointCallback
答:CheckpointCallback 是 Stable Baselines3 库中的一个回调函数,用于在训练过程中保存模型的检查点(checkpoint)。
CheckpointCallback 具有以下几个主要功能:

指定保存频率:可以设置一个保存频率,例如每隔固定的训练步数或每个训练轮次,来触发保存模型的检查点操作。

指定保存目录:可以指定保存模型检查点的目录路径,以便将模型参数和相关信息保存到指定的目录中。

自动保存最佳模型:除了按照指定的频率保存模型检查点外,CheckpointCallback 还可以根据训练过程中的指标,自动保存最佳的模型检查点。例如,可以根据验证集的性能指标(如平均回报)来选择保存具有最佳性能的模型

11. ep_len_mean、approx_kl等指标解释

问:请介绍PPO算法中下面这些日志是什么意思?
-------------------------------------------
| rollout/                |               |
|    ep_len_mean          | 195           |
|    ep_rew_mean          | -0.0277       |
| time/                   |               |
|    fps                  | 139           |
|    iterations           | 19            |
|    time_elapsed         | 69            |
|    total_timesteps      | 9728          |
| train/                  |               |
|    approx_kl            | 1.2374949e-07 |
|    clip_fraction        | 0             |
|    clip_range           | 0.0348        |
|    entropy_loss         | -8.32         |
|    explained_variance   | -1.37         |
|    learning_rate        | 2.19e-05      |
|    loss                 | 0.000431      |
|    n_updates            | 72            |
|    policy_gradient_loss | -3.46e-05     |
|    value_loss           | 0.00103       |
-------------------------------------------
答:这些日志是PPO(Proximal Policy Optimization)算法中的训练指标和统计信息。下面是每个日志的解释:

- `rollout/ep_len_mean`: 每个回合(episode)的平均步长(即环境交互的步数)为195步。
- `rollout/ep_rew_mean`: 每个回合的平均奖励为-0.0277。这个值可能是一个负数,具体含义取决于具体的环境和奖励函数的设计。
- `time/fps`: 训练过程中的平均帧率为139帧/秒。
- `time/iterations`: 已经进行了19次迭代。
- `time/time_elapsed`: 训练已经经过的时间为69单位(具体时间单位未提供)。
- `time/total_timesteps`: 已经执行的总步数为9728步。
- `train/approx_kl`: 近似克隆(Approximate KL)的值为1.2374949e-07。这是一个用于衡量新策略与旧策略之间差异的指标。较小的值表示新策略与旧策略之间的差异较小。
- `train/clip_fraction`: 裁剪比例为0。这是在PPO算法中用于限制策略更新幅度的一种技术,如果这个值不为0,说明对策略进行了一定的裁剪。
- `train/clip_range`: 裁剪范围为0.0348。这是在裁剪过程中使用的一个参数,用于限制策略更新的幅度。
- `train/entropy_loss`: 策略的熵损失为-8.32。熵是用于衡量策略的不确定性,较大的熵表示策略更加随机。
- `train/explained_variance`: 解释方差为-1.37。这是一个用于衡量价值函数拟合优度的指标,较高的值表示价值函数对真实奖励的解释能力更强。
- `train/learning_rate`: 学习率为2.19e-05。这是在训练过程中用于更新策略和价值函数的学习率。
- `train/loss`: 总体损失为0.000431。这是训练过程中的目标函数的值,用于衡量策略和价值函数的优化程度。
- `train/n_updates`: 已经进行了72次参数更新。
- `train/policy_gradient_loss`: 策略梯度损失为-3.46e-05。这是PPO算法中用于优化策略的损失函数的值。
- `train/value_loss`: 价值函数的损失为0.00103。这是PPO算法中用于优化价值函数的损失函数的值。

这些日志记录了PPO算法在训练过程中的关键指标和统计信息,用于监控算法的性能和进展。

==street_fighter_custom_wrapper.py部分==

12. gym.spaces.Box()

问:解释gym.spaces.Box(low=0, high=255, shape=(100, 128, 3), dtype=np.uint8)
答:`gym.spaces.Box` 是 OpenAI Gym 库中的一个类,用于定义一个连续的数值空间。在这个特定的例子中,`gym.spaces.Box` 定义了一个三维的盒子空间,具有以下属性:

- `low=0`:表示每个元素的最小值是0。
- `high=255`:表示每个元素的最大值是255。
- `shape=(100, 128, 3)`:表示盒子的形状是一个三维数组,其中第一个维度的大小为100,第二个维度的大小为128,第三个维度的大小为3。
- `dtype=np.uint8`:表示盒子中元素的数据类型是无符号8位整数。

换句话说,这个盒子空间表示一个形状为100x128x3的三维数组,其中每个元素的取值范围是0到255之间的整数。这种表示方式常用于图像处理任务,其中图像的每个像素值都可以表示为0到255之间的灰度值或颜色通道值。

编者注:其实就是100x128的彩色图像 

13. obs形状(200,256,3)

问:self.observation_space = gym.spaces.Box(low=0, high=255, shape=(100, 128, 3), dtype=np.uint8),为什么obs, _reward, _done, info = self.env.step(action)后,obs的shape是(200,256,3)呢?
答:根据您提供的信息,您在游戏环境中设置了 `self.observation_space` 的形状为 `(100, 128, 3)`,但在执行 `self.env.step(action)` 后,返回的 `obs` 形状为 `(200, 256, 3)`。

这个形状变化很可能是因为游戏环境在生成观测时对图像进行了缩放或调整操作。游戏环境通常会将原始的游戏画面渲染到一个显示屏幕上,而显示屏幕的分辨率可能与您设置的观测空间的形状不同。

在这种情况下,游戏环境可能会执行以下操作:

1. 图像缩放:游戏环境将原始游戏画面缩放到适应显示屏幕分辨率的大小。这可能导致形状的变化。

1. 填充或裁剪:游戏环境可能对图像进行填充或裁剪操作,以适应显示屏幕的宽高比。这也可能导致形状的变化。

要解决这个问题,您可以查看游戏环境代码中与观测生成和图像处理相关的部分。尤其是检查是否有对图像进行缩放、填充或裁剪的操作,以及调整形状的代码。您可能需要调整这些操作,使得生成的观测与您期望的形状一致。

==test.py部分==

14 .custom_objects 

问:解释一下PPO.load(...,custom_objects = {'observation_space': env.observation_space, 'action_space': env.action_space})
答:`PPO.load(..., custom_objects={'observation_space': env.observation_space, 'action_space': env.action_space})` 是一种在加载PPO模型时使用的参数设置。这个参数用于指定自定义对象,其中包括观测空间(`observation_space`)和动作空间(`action_space`)的信息。

在PPO.load函数中,`custom_objects`参数允许你传递一个字典,其中键是自定义对象的名称,值是实际的对象。在这种情况下,我们为`custom_objects`提供了两个键值对:

- `'observation_space': env.observation_space`:这表示观测空间的自定义对象。`env.observation_space`是你的环境对象的观测空间属性,它描述了模型接收的观测数据的特征和范围。

- `'action_space': env.action_space`:这表示动作空间的自定义对象。`env.action_space`是你的环境对象的动作空间属性,它描述了模型可以选择的动作的特征和范围。

通过将这些自定义对象传递给`PPO.load`函数,它可以在加载模型时使用这些对象来验证新环境的观测空间和动作空间是否与原始训练环境匹配。

这样做的目的是确保在加载模型时,模型能够正确地验证新环境,并与观测空间和动作空间保持一致。这对于确保模型在新环境中的有效性和正确性非常重要。

你可能感兴趣的:(人工智能)