MAML-RL Pytorch 代码解读 (15) – maml_rl/episode.py
文章目录
- MAML-RL Pytorch 代码解读 (15) -- maml_rl/episode.py
- 基本介绍
- 源码链接
- 文件路径
- `import` 包
- `BatchEpisodes()` 类
基本介绍
在网上看到的元学习 MAML 的代码大多是跟图像相关的,强化学习这边的代码比较少。
因为自己的思路跟 MAML-RL 相关,所以打算读一些源码。
MAML 的原始代码是基于 tensorflow 的,在 Github 上找到了基于 Pytorch 源码包,学习这个包。
文件路径
./maml_rl/episode.py
import
包
import numpy as np
import torch
import torch.nn.functional as F
BatchEpisodes()
类
class BatchEpisodes:
#### 主要是对一批任务跑完之后,存在的很多(数量=batch_size)的回合信息做处理的类。首先初始化batch_size也就是批处理的大;设置折扣率gamma=0.95;设置运算设备默认是'cpu'。
def __init__(self, batch_size, gamma=0.95, device='cpu'):
self.batch_size = batch_size
self.gamma = gamma
self.device = device
#### 以列表的数据结构存储数据,每个列表里面包含batch_size个空列表。
# [[], [],...batch_size of []]
self._observations_list = [[] for _ in range(batch_size)]
self._actions_list = [[] for _ in range(batch_size)]
self._rewards_list = [[] for _ in range(batch_size)]
self._mask_list = []
#### 默认设置观测信息、动作信息、奖励信息、回报信息(_returns)和掩码(_mask)。
self._observations = None
self._actions = None
self._rewards = None
self._returns = None
self._mask = None
@property
def observations(self):
#### 如果self._observations标记是有"None",则从self._observations_list的第一个子列表的第一个元素中获得状态大小。len(self)未知是啥,可能是受property装饰器的影响。生成了一个大小是[len(self),self.batch_size,observation_shape]的数据池。
if self._observations is None:
observation_shape = self._observations_list[0][0].shape
observations = np.zeros((len(self), self.batch_size)
+ observation_shape, dtype=np.float32)
#### 获得每个子集里面回合的长度,将列表里面第i个子集的任务的观测信息存储在observations池中
for i in range(self.batch_size):
length = len(self._observations_list[i])
observations[:length, i] = np.stack(self._observations_list[i], axis=0)
#### 转换成张量数据结构。
self._observations = torch.from_numpy(observations).to(self.device)
return self._observations
@property
def actions(self):
#### 如果self._actions标记是有"None",则从self._actions_list的第一个子列表的第一个元素中获得动作大小。len(self)未知是啥,可能是受property装饰器的影响。生成了一个大小是[len(self),self.batch_size,action_shape]的数据池。
if self._actions is None:
action_shape = self._actions_list[0][0].shape
actions = np.zeros((len(self), self.batch_size)
+ action_shape, dtype=np.float32)
#### 获得每个子集里面回合的长度,将列表里面第i个子集的任务的动作信息存储在actions池中
for i in range(self.batch_size):
length = len(self._actions_list[i])
actions[:length, i] = np.stack(self._actions_list[i], axis=0)
#### 转换成张量数据结构。
self._actions = torch.from_numpy(actions).to(self.device)
return self._actions
@property
def rewards(self):
#### 如果self._rewards标记是有"None",则生成了一个大小是[len(self),self.batch_size]的数据池。因为奖励函数是标量。
if self._rewards is None:
rewards = np.zeros((len(self), self.batch_size), dtype=np.float32)
#### 获得每个子集里面回合的长度,将列表里面第i个子集的任务的奖励信息存储在rewards池中
for i in range(self.batch_size):
length = len(self._rewards_list[i])
rewards[:length, i] = np.stack(self._rewards_list[i], axis=0)
#### 转换成张量数据结构。
self._rewards = torch.from_numpy(rewards).to(self.device)
return self._rewards
@property
def returns(self):
#### 如果self._returns标记是有"None",则生成了一个大小是[len(self),self.batch_size]的数据池。因为回报函数是标量。将rewards从torch的张量结构转变为numpy的数据结构,将mask从torch的张量结构转变为numpy的数据结构。
if self._returns is None:
return_ = np.zeros(self.batch_size, dtype=np.float32)
returns = np.zeros((len(self), self.batch_size), dtype=np.float32)
rewards = self.rewards.cpu().numpy()
mask = self.mask.cpu().numpy()
#### 对这批任务里面的所有位置都计算回报,记录到returns的numpy数组当中。
for i in range(len(self) - 1, -1, -1):
return_ = self.gamma * return_ + rewards[i] * mask[i]
returns[i] = return_
#### 最后再将numpy结构转变成了torch结构
self._returns = torch.from_numpy(returns).to(self.device)
return self._returns
@property
def mask(self):
#### 如果self._mask标记是有"None",则生成了一个大小是[len(self),self.batch_size]的数据池。因为掩码是标量。对每个掩码都做做处理,设置成数值1.0。转换成张量数据结构。
if self._mask is None:
mask = np.zeros((len(self), self.batch_size), dtype=np.float32)
for i in range(self.batch_size):
length = len(self._actions_list[i])
mask[:length, i] = 1.0
self._mask = torch.from_numpy(mask).to(self.device)
return self._mask
def gae(self, values, tau=1.0):
#### 这个库用于处理很长的episode时,后面的状态的估计处理。
"""
:param values: [200, 20, 1], tensor
:param tau:
:return:
"""
# Add an additional 0 at the end of values for
# the estimation at the end of the episode
#### torch.nn.functional.pad是PyTorch内置的矩阵填充函数,第一个参数input:需要进行填充的Tensor数据;第二个参数pad:进行pad的元组(注意这里版本变了以后建议用列表类型),元组中元素个数小于等于input维度的2倍。如:input是2维的,则pad可以最多有4个元素;input是5维的,pad可以最多有10个元素。
values = values.squeeze(2).detach() # [200, 20]
values = F.pad(values * self.mask, (0, 0, 0, 1)) # [201, 20]
#### 计算价值误差。设置一个advantages数组,计算每次乘以折扣率和tau数值的个gae数据。最后返回advantage数据。
deltas = self.rewards + self.gamma * values[1:] - values[:-1] # [200, 20]
advantages = torch.zeros_like(deltas).float() # [200, 20]
gae = torch.zeros_like(deltas[0]).float() # [20]
for i in range(len(self) - 1, -1, -1):
gae = gae * self.gamma * tau + deltas[i]
advantages[i] = gae
return advantages
#### 将观测信息、动作信息、奖励信息和批号捆绑起来,传入各自的列表中。
def append(self, observations, actions, rewards, batch_ids):
for observation, action, reward, batch_id in zip(observations, actions, rewards, batch_ids):
if batch_id is None:
continue
self._observations_list[batch_id].append(observation.astype(np.float32))
self._actions_list[batch_id].append(action.astype(np.float32))
self._rewards_list[batch_id].append(reward.astype(np.float32))
#### 将长度属性设置奖励列表的长度最大值。
def __len__(self):
return max(map(len, self._rewards_list))