本实验选择的比赛为 2021/11/22 - 2022/1/20 进行的 “RLChina 智能体挑战赛 - 辛丑年冬赛季”,该竞赛由 RLChina 联合及第平台共同举办,使用及第平台的 “奥林匹克 跑步运动” 作为比赛科目
总结一下关键信息
本题有如下特点
部分可观测:agent 只能感知局部信息,全局信息丢失,这会给状态空间的设计带来很大的困难。下面两个 agent 全局位置不同,但是观测是相同的,agent 就无法区分这两个状态
泛化性挑战:测试时会出现未见地图,这是本题的最大难点,因为 RL 算法本身不具备这样的泛化性设计
Note: RL 考虑的泛化性是在同一张地图的任意位置出发都能完成任务
局部观测赋予 agent 一定的地图泛化能力,如果直接用原始观测或从中提取特征作为状态,则 agent 学到的是从这个 25*25 的观测特征分布到动作的映射,在所有地图的生成满足某个生成规则,训练地图中包含所有地图构成要素,且有一定多样性的前提下,可能可以进行一定的泛化。坏消息是,各个地图间区别很大
各个地图中包含的元素太多且差异很大,再加上部分观测导致的信息丢失,这个 25*25 观测的特征空间变得更大,相比而言训练地图数量就显得太少,这样直接学出的观测特征分布到动作的映射很容易过拟合,因此最好能考虑联合历史交互数据考虑构造新状态特征(比如用 RNN)。另外,状态和奖励中没有和能量有关的表征,在训练地图上通过强化学出来的分配策略往往过于激进,几乎无法泛化,可以考虑硬编码能量分配规则
稀疏奖励:原始环境中仅在取胜或平局达到终点时可以得到 1 的奖励,非常稀疏,需要考虑根据观测信息构造内部奖励
环境本身是确定性转移,随机性来自和对手交互,这个对抗其实不是很强,超过或落后对手一定距离即可看作没有对手存在
由于起点位置固定,使用前几帧就能判断是那一张训练训练地图!这样有可能硬编码一些动作
总结一下:部分观测 + 地图间差异大 = 极端的 POMDP 问题 = 信息缺失严重,特征工程难做
Note: 关键是 agent 位置、速度、能量等重要信息缺失
Note:由于本实验选题为强化学习,而冠军方法是一个纯规则方法(不涉及任何机器学习内容),这和《数据科学与应用实验课概述》ppt 中假定的监督学习类竞赛有很大不同,所以本报告不会完全按照上述 PPT 所规定之格式撰写
先看一下比赛结果
前五名中,第一第二都是纯规则方法,而三到五名都是机器学习方法,他们使用的方法 + tricks 概括如下
注意到一共出现了三类方法,模仿学习和强化学习都可以算到机器学习的范畴,而前两名的规则类方法硬要说的话仅仅就是做了一个很复杂的数据预处理而已
一个强化学习比赛却被规则类 agent 夺魁,这是值得思考的一件事,从学术的角度看,这体现了强化学习方法的泛化性问题
考虑到本课程性质,本报告会综合分析前五名的方法,这样不但能包含一些机器学习的相关内容,还可以针对序列决策任务,对 learning-based 和 rule-based 方法的性质做一些比较
注意到使用强化学习的第三名和第五名在模型选择问题上都不约而同地选择了 PPO 算法,故本节讨论 PPO 算法的优势。先看一下各类方法存在的问题
解决 Policy gradient 方法问题的一个思路是直接换一种优化方法,以回避上面的更新过程,比如改用置信域优化方法
- 置信域优化是一种经典优化方法,其出发点是:如果对目标函数 J ( θ ) J(\theta) J(θ) 进行优化过于困难,不妨在 θ \theta θ 的当前值 θ n o w \theta_{now} θnow 附近构造一个局部范围内和 J ( θ ) J(\theta) J(θ) 十分相似的替代函数 L ( θ ∣ θ n o w ) L(\theta|\theta_{now}) L(θ∣θnow),通过在这个局部范围内最优化 L ( θ ∣ θ n o w ) L(\theta|\theta_{now}) L(θ∣θnow) 来更新一次 θ \theta θ 值,反复迭代上述过程直到收敛。注意到这里的置信域半径控制着每一轮迭代中 θ \theta θ 变化的上限,我们通常会让这个半径随优化过程不断减小来避免 overstep,其一步更新和优化过程示意图如下
- 注意到每一轮迭代中,我们都在构造并求解一个小的约束优化问题,这样就不要做 θ ← θ + α g \theta \leftarrow \theta + \alpha g θ←θ+αg 形式的更新了,更重要的是,我们可以更自由地设计置信域约束条件,从而对每轮迭代中 π ( a ∣ s ; θ ) \pi(a|s;\theta) π(a∣s;θ) 的变化进行直接控制
把置信域优化方法和 policy based 方法相结合就得到了 TRPO 方法,下面做一些公式推导。记住我们的优化目标是最大化 J ( θ ) = E S [ V π ( a ∣ s ; θ ) ( S ) ] J(\theta) = \mathbb{E}_S[V_{\pi(a|s;\theta)}(S)] J(θ)=ES[Vπ(a∣s;θ)(S)],为了构造 L ( θ ∣ θ n o w ) L(\theta|\theta_{now}) L(θ∣θnow),把 θ n o w \theta_{now} θnow 引入 V π ( s ) V_\pi(s) Vπ(s) 中
V π ( s ) = ∑ a π ( a ∣ s ; θ ) ⋅ Q π ( s , a ) = ∑ a π ( a ∣ s ; θ n o w ) ⋅ π ( a ∣ s ; θ ) π ( a ∣ s ; θ n o w ) ⋅ Q π ( s , a ) = E A ∼ π ( a ∣ s ; θ n o w ) [ π ( a ∣ s ; θ ) π ( a ∣ s ; θ n o w ) ⋅ Q π ( s , a ) ] \begin{aligned} V_\pi(s) &= \sum_a \pi(a|s;\theta)·Q_\pi(s,a) \\ &= \sum_a \pi(a|s;\theta_{now})·\frac{\pi(a|s;\theta)}{\pi(a|s;\theta_{now})}·Q_\pi(s,a) \\ &= \mathbb{E}_{A\sim \pi(a|s;\theta_{now})} \big[\frac{\pi(a|s;\theta)}{\pi(a|s;\theta_{now})}·Q_\pi(s,a) \big] \end{aligned} Vπ(s)=a∑π(a∣s;θ)⋅Qπ(s,a)=a∑π(a∣s;θnow)⋅π(a∣s;θnow)π(a∣s;θ)⋅Qπ(s,a)=EA∼π(a∣s;θnow)[π(a∣s;θnow)π(a∣s;θ)⋅Qπ(s,a)]这样优化目标就变成
J ( θ ) = E S [ V π ( a ∣ s ; θ ) ( S ) ] = E S [ E A [ π ( a ∣ s ; θ ) π ( a ∣ s ; θ n o w ) ⋅ Q π ( s , a ) ] ] = E S , A [ π ( a ∣ s ; θ ) π ( a ∣ s ; θ n o w ) ⋅ Q π ( s , a ) ] \begin{aligned} J(\theta) &= \mathbb{E}_S[V_{\pi(a|s;\theta)}(S)] \\ &= \mathbb{E}_S\big[\mathbb{E}_A \big[\frac{\pi(a|s;\theta)}{\pi(a|s;\theta_{now})}·Q_\pi(s,a) \big]\big] \\ &= \mathbb{E}_{S,A} \big[\frac{\pi(a|s;\theta)}{\pi(a|s;\theta_{now})}·Q_\pi(s,a) \big] \end{aligned} J(θ)=ES[Vπ(a∣s;θ)(S)]=ES[EA[π(a∣s;θnow)π(a∣s;θ)⋅Qπ(s,a)]]=ES,A[π(a∣s;θnow)π(a∣s;θ)⋅Qπ(s,a)]
注意到这个原始优化目标中有两个不好处理的点
解决两个问题后构造出的替代目标函数 L ( θ ∣ θ n o w ) L(\theta|\theta_{now}) L(θ∣θnow) 如下:
L ( θ ∣ θ n o w ) = 1 n ∑ i = 1 n π ( a i ∣ s i ; θ ) π ( a i ∣ s i ; θ n o w ) ⋅ g i L(\theta|\theta_{now}) = \frac{1}{n}\sum_{i=1}^n \frac{\pi(a_i|s_i;\theta)}{\pi(a_i|s_i;\theta_{now})}·g_i L(θ∣θnow)=n1i=1∑nπ(ai∣si;θnow)π(ai∣si;θ)⋅gi再加上置信域约束,每轮迭代中构造出的约束优化问题为
θ n e w ← arg max θ L ( θ ∣ θ n o w ) s.t. θ ∈ N ( θ n o w ) \theta_{new} \leftarrow \arg\max_\theta L(\theta|\theta_{now}) \space\space\space\space\space\space\space \text{s.t.} \space\space\theta \in \mathcal{N}(\theta_{now}) θnew←argθmaxL(θ∣θnow) s.t. θ∈N(θnow)为了避免策略的 overstep 问题,直接把更新前后的策略差异的约束作为 θ \theta θ 的约束条件,即
θ ∈ N ( θ n o w ) ⇒ 1 n ∑ i = 1 n KL [ π ( a ∣ s ; θ n o w ∣ ∣ π ( a ∣ s ; θ ) ) ] < △ \theta \in \mathcal{N}(\theta_{now}) \space\space\space \Rightarrow\space\space \frac{1}{n}\sum_{i=1}^n \text{KL}\big[\pi(a|s;\theta_{now} || \pi(a|s;\theta)) \big] < \triangle θ∈N(θnow) ⇒ n1i=1∑nKL[π(a∣s;θnow∣∣π(a∣s;θ))]<△持续迭代求解上述约束优化问题直至策略收敛的方法就是 TRPO,注意我们不再需要手动设置梯度更新步长 α \alpha α 了,且能有效控制每轮迭代中策略的变化程度,这就很好地解决了策略网络优化时的 overstep 问题,另外 TRPO 对超参数(如置信域半径)也不敏感,即使超参设置较差,性能也不会下降很多。但是,TRPO 也引入了新问题:上面这个约束优化问题求解起来非常困难,编程实现也很繁琐
PPO 巧妙地将 TRPO 中的约束优化问题转换为了一个无约束优化问题,其优化目标如下
J P P O θ k ( θ ) ≈ ∑ ( s t , a t ) min ( π ( a t ∣ s t ; θ ) π ( a t ∣ s t ; θ k ) A θ k ( s t , a t ) , clip ( π ( a t ∣ s t ; θ ) π ( a t ∣ s t ; θ k ) , 1 − ε , 1 + ε ) A θ k ( s t , a t ) ) \begin{aligned} J_{P P O}^{\theta^{k}}(\theta) \approx \sum_{\left(s_{t}, a_{t}\right)} \min &\left(\frac{\pi\left(a_{t} | s_{t};\theta\right)}{\pi\left(a_{t} | s_{t};{\theta^{k}}\right)} A^{\theta^{k}}\left(s_{t}, a_{t}\right)\right.,\\ &\left.\operatorname{clip}\left(\frac{\pi\left(a_{t} | s_{t};\theta\right)}{\pi\left(a_{t} | s_{t};{\theta^{k}}\right)}, 1-\varepsilon, 1+\varepsilon\right) A^{\theta^{k}}\left(s_{t}, a_{t}\right)\right) \end{aligned} JPPOθk(θ)≈(st,at)∑min(π(at∣st;θk)π(at∣st;θ)Aθk(st,at),clip(π(at∣st;θk)π(at∣st;θ),1−ε,1+ε)Aθk(st,at))其中主要有两点比较重要
引入了优势函数 A θ k ( s t , a t ) = Q π ( a ∣ s ; θ k ) ( s t , a t ) − V π ( a ∣ s ; θ k ) ( s t ) A^{\theta^{k}}\left(s_{t}, a_{t}\right) = Q_{\pi(a|s;\theta^k)}(s_t,a_t) - V_{\pi(a|s;\theta^k)}(s_t) Aθk(st,at)=Qπ(a∣s;θk)(st,at)−Vπ(a∣s;θk)(st) ,优势函数代表在状态 s t s_t st 处执行动作 a t a_t at 带来了多大的好处,其关键性质是
E a ∼ π θ [ A π θ ( s , a ) ] = E a ∼ π θ [ Q π θ ( s , a ) ] Var ( A π θ ( s , a ) ) < Var ( Q π θ ( s , a ) \mathbb{E}_{a\sim{\pi^\theta}}[A_{\pi^\theta}(s,a)] = \mathbb{E}_{a\sim{\pi^\theta}}[Q_{\pi^\theta}(s,a)] \\ \text{Var}(A_{\pi^\theta}(s,a)) < \text{Var}(Q_{\pi^\theta}(s,a) Ea∼πθ[Aπθ(s,a)]=Ea∼πθ[Qπθ(s,a)]Var(Aπθ(s,a))<Var(Qπθ(s,a) 优势函数 A A A 相比状态动作价值函数 Q Q Q 具有相同的期望和更小的方差,在 policy gradient 类方法中涉及到 Q Q Q 函数的地方几乎都可以直接替换为 A A A 函数,这是一个常用 trick
特殊设计的目标函数,这个乍看起来比较复杂,但其实上就是在给优势函数 A θ k A^{\theta^{k}} Aθk 选择一个系数 min ( π ( a t ∣ s t ; θ ) π ( a t ∣ s t ; θ k ) , clip ( π ( a t ∣ s t ; θ ) π ( a t ∣ s t ; θ k ) , 1 − ε , 1 + ε ) ) \min \left(\frac{\pi\left(a_{t} | s_{t};\theta\right)}{\pi\left(a_{t} | s_{t};{\theta^{k}}\right)}, \operatorname{clip}\big(\frac{\pi\left(a_{t} | s_{t};\theta\right)}{\pi\left(a_{t} | s_{t};{\theta^{k}}\right)}, 1-\varepsilon, 1+\varepsilon\big)\right) min(π(at∣st;θk)π(at∣st;θ),clip(π(at∣st;θk)π(at∣st;θ),1−ε,1+ε)),其中后者就是把前者裁剪到 [ 1 − ϵ , 1 + ϵ ] [1-\epsilon,1+\epsilon] [1−ϵ,1+ϵ] 而已,直接将两个系数的曲线如下画出来
其中蓝色绿色虚线是 π ( a t ∣ s t ; θ ) π ( a t ∣ s t ; θ k ) \frac{\pi\left(a_{t} | s_{t};\theta\right)}{\pi\left(a_{t} | s_{t};{\theta^{k}}\right)} π(at∣st;θk)π(at∣st;θ),蓝色虚线是 clip ( π ( a t ∣ s t ; θ ) π ( a t ∣ s t ; θ k ) , 1 − ε , 1 + ε ) \operatorname{clip}\big(\frac{\pi\left(a_{t} | s_{t};\theta\right)}{\pi\left(a_{t} | s_{t};{\theta^{k}}\right)}, 1-\varepsilon, 1+\varepsilon\big) clip(π(at∣st;θk)π(at∣st;θ),1−ε,1+ε),红色实线是优势函数 A ( s t , a t ) A(s_t,a_t) A(st,at) 不同取值时 min \min min 操作选出的系数。以左图为例分析, A ( s t , a t ) > 0 A(s_t,a_t)>0 A(st,at)>0 意味着状态 s t s_t st 处动作 a t a_t at 带来了好处,所以为了鼓励 a t a_t at 出现系数应尽量大,但是不要超过 1 + ϵ 1+\epsilon 1+ϵ (就是说 s t s_t st 处选择 a t a_t at 的概率不要比现在高超过 1 + ϵ 1+\epsilon 1+ϵ 倍 ),以免策略网络出现 overstep,而系数小于 1 时说明网络还处于欠拟合状态,没有训练好,这时就不用限制了;右图显示的 A ( s t , a t ) < 0 A(s_t,a_t)<0 A(st,at)<0 情况也同理
可见,PPO 巧妙地将 TRPO 的约束变形成一个 clip \text{clip} clip 加一个 min \min min 操作,不但消除了约束条件,甚至连 KL \text{KL} KL 散度都不用算了,PPO 在保留 TRPO 训练稳定,调参简单的基础上,进一步简化了算法实现
顺带一提,在 PPO 的原始论文中使用了两个 trick,这两个都是 “插件” 形式的 trick,可以和各种 RL 方法组合
总的来看,PPO 几乎是目前解连续控制问题的主流 model-free DRL 方法中训练最稳定,调参最简单的
- Note:就好像做 CV 时我们首先考虑 CNN 一样,PPO/TD3/SAC 是现在解连续控制强化学习问题的首选方法,它们都是 Policy Gradient 方法,其中
- TD3:在 DPG 的基础上增加了很多 trick 来缓解价值高估问题。其问题在于超参数很多,调参困难,相比而言 PPO 没有那么多参数
- SAC:在策略网络的训练目标中增加策略熵项,从而更好地平衡探索和利用。其问题在于非常依赖 Reward Function,相比而言 PPO 即使用较差的 Reward function 也能训练,而且 PPO 的网络结构要简单一点(不过事实上 SAC 是目前 SOTA 的 model-free 方法)
- 相比而言,PPO 在 “性能” 和 “实现难度 (编程/调参/奖励函数设计)” 间取得了最好的平衡,考虑到比赛时间的紧迫性,PPO 成为大部分走 RL 路线队伍的首选也是意料之中的
使用的 tricks:
分析:在难以收集大量数据的情况下,仅仅做了一个最简单的 BC 就能拿到第 4 名,这个还是有点出乎意料,不过仔细思考一下可以发现,这个环境中 IL 其实取巧了
表现较好的 Learning-based 方法基本遵循以下思路
本次比赛中 Learning-based 方法的统一问题在于
Rule-based 方法说白了就是人工设计一套规则,将观测输入直接映射到输出动作。看到红灯停车,看到绿灯起步就是一种规则系统
放到机器学习的语境下,这大概可以相当于结合先验知识做了一个很强的特征工程(或者说数据预处理),直接替代整个学习过程
- 以图像处理分类问题为例
- 普通监督学习先做特征工程提取图像特征,然后跑监督学习算法学习从特征到预测标记的映射;
- Rule-based 直接手工设计一个分类规则作为从原始图像到预测标记的映射(PCA 降维人脸识别大概可以算这一卦的)
- 如果是放在本赛题这样的强化学习背景中
- 普通强化学习方法在每一步都对原始观测做特征工程提取特征作为状态,通过大量交互建立起各个状态的价值估计,再依赖价值估计不断优化策略,最终得到从状态到动作的映射
- Rule-based 方法直接手工设计一个规则作为从原始观测到动作的映射
本节仅对冠军方案进行分析说明,第二名的规则方法不提
深入分析 agent 的状态信息,特别是因 POMDP 而缺失的部分
如果能获取自身的位置,就能估计一切
发现获取 agent 的绝对位置是重中之重,作者注意到每一帧旋转角度最多 30 度运动距离有限,这样两帧之间就会有很大重叠,直接 for 循环暴力匹配一定范围内的整数位移,就能得到 agent 每一帧的位移信息,进而可以直接积分得到绝对位置
更进一步,如果能得到每一帧的位移,那么我们其实可以把每一帧的观测都拼接起来还原当前的地图,从而方便地估计位置、速度、能量等关键状态信息,还可以对箭头方向、道路边缘、终点线等各种信息进行识别,这种情况下
冠军方案思路
地图拼接效果:下面展示了绿色 agent 在 map8 和 map11 运动过程中的地图重建情况
拼接正方向:上面两张最右侧图像都是游戏画面逆时针旋转 90 度所得,画面中的绿色和紫色方框代表两个 agent 的观测信息,注意原始观测是 agent 当前位置正前(上)方的一个正方形区域。作者把 agent 初始朝向转 180 度作为拼接地图的正方向,也就是说 agent 初始指向在构造的 global map 中总是垂直向下的,上面两个地图中 agent 都是在游戏画面水平向左起步,所以原始地图的左方变成了构造 global map 的下方,像上面那样将原始游戏画面逆时针旋转 90 度即可对齐
agent 尺寸和观测尺寸:超分后观测矩阵边长 50 像素,根据游戏画面比例,agent 示意圆形半径为 5 像素
地图尺寸:各个地图尺寸不同,为了保证地图总能拼接完整,global map 尺寸为边长 2000 像素的正方形,这远远大于所有测试地图
坐标系:程序涉及两个坐标系,如下所示
其中浅蓝色代表 global map,绿色圆和方框代表初始时刻 agent 及其观测的位置
角度:程序中所有角度都是测算的目标向量和 x 轴负方向的夹角,并且限制在 [-180,180] 范围内,因此初始时刻 agent 的绝对角度为 180 度或 -180 度,以此为初始值对 agent 每个动作的选择角度积分,即可得到任意时刻 agent 的绝对角度,这对于拼接地图非常重要
箭头指向:作者假设地图中箭头只可能有四个指向,程序中用东南西北表示,在 global map 中表示如下
wrap_obs
函数:输入为 shape = (25,25)
的原始观测
global_map[:,:,0]
这样取出相应的观测标记切片进行分析add_arrows
函数:输入为包装后观测的第 0 维度(arrow标记)切片,记为 img
img[i,j]> 0.9
就认为找到了一个箭头像素ll
箭头在 k
箭头所指方向(意味着已经按 k
箭头指向走向并看到了下一个箭头),则设置 k
箭头为 “已经过箭头”,否则将其记为 “有效箭头”get_edges
函数:输入为当前重建的 global map
global_map[:,:,1]
找出这个切片的所有探索边界点,存入 edge_point_list
,这些点都是 agent 可以前往的(没有被墙挡住)past_x
和 past_y
记录相邻的上一个 bfs 点的坐标,这样对与任意一个点,都能通过反复查询这两个数组找出其 bfs 路径edge_point_list
中的每一个点,步进值 3 做 bfs,把 edge_point_list
切分为若干长度不超过 20 的 bfs 路径,并且排除掉那些长度小于等于 4 的路径。这样找出的路径都是探索边界上没有被墙壁阻挡的边,是 agent 可以前往的候选目标(如 2.1.2 节中间图像所示),将他们加入 edge_list
。每一条边的 “中点” 定义为组成该边的所有点坐标的均值edge_list
中的每一条可行边,检查目前找出的所有箭头,每一个距离小于 70 且指向该边中点的有效箭头加 1 分,同时考虑 agent 去往该边中心位置的转向角扣分(转向角越小扣分越少,细节请看复现代码)和行进距离扣分(距离该边越近扣分越少,细节请看复现代码),选出一个最优的可去边past_x
和 past_y
反向找出从 agent 当前位置去向目标点的 bfs 路径(如2.1.2 节中间图像所示),并返回past_x
和 past_y
找出从 agent 当前位置去向该标记点的 bfs 路径返回get_action
函数:输入为 2.2.4 节中找出的 target_bfs_path
power = 200 if self.v <= 2.01 else 200 / self.v * 2
,其中 self.v
是利用 global map 差分计算的绝对速度。这里控制 power * speed ≤ 400 \text{power * speed} \leq 400 power * speed≤400 避免能量耗尽target_bfs_path
的最后一个点(最靠近目标边中点的那个点)设为目标点向 agent 当前位置连直线,逐像素遍历,检查中间是否有墙壁,如果有墙壁阻挡,就按 bfs 顺序回退,直到找到没有阻挡的点作为 “可行目标点”,计算从当前位置去往该点的转向角度 anglepower
和 angle
fix_action
函数:输入为 2.2.5 节中生成的转向角度 angle
self.angle
(也是和 x 轴负方向的夹角)做差,标准化到 [ − 180 , 180 ] [-180,180] [−180,180],记为 a
angle
在最大转向角度内(30),而过去一段时间的累计转向角度 a
超过最大转向角度,且速度较快,就进行补偿先看文件夹
再看根目录下的几个文件
体会冠军队方案的编程技巧:这个代码写得非常糟糕,堪称屎山,其具有以下问题
a b c d
这样的变量,理解困难针对这些问题,我使用以下手段阅读
说实话,我没感受到作者用了什么编程技巧,这种规则类方法不像机器学习那种代码有一定的框架和套路,就是一个纯粹的像素级别的逻辑处理,这种情况下确实也很难用一些技巧了,就全部都是业务逻辑。在有限的比赛时间内,写成这样乱七八槽也能理解。硬要说的话,我深刻地感受到了写注释和文档的重要性,并且对于不规范的编程习惯的厌恶更上一层楼
我选择 RL 题目原本是想学习一下怎么编写 DRL 的代码的,没想到第一名却不是 RL 方法,阅读和复现的过程中,我感觉这全部都是 dirty work,就好像在写一个巨复杂的 CSP 模拟题,完全没有通用性,在浪费大量时间后,终于怀着极大的痛苦把他搞完了。做这件事让我感到筋疲力尽,唯一的好处就是阅读代码能力可能提高了一点…
怀着对这个冠军代码的极大恶意,我复现时特别注意变量名规范,并且使用 python 3 新引入的函数注释方法,仿照 request 开源库的格式,给每一个函数都写了非常详细的文档说明,还对可能产生困惑的地方使用中文进行补充注释,例如
def update_map(self, wrap_obs, pos_x, pos_y) -> None:
""" Splice current wrapped observation to update the global map
:param wrap_obs: wrapped observations, shape = (50,50,4)
:param pos_x: the x center coord of current observation center
:param pos_y: the y center coord of current observation center
:return: None
:note: The part that has been spliced won't be overwritten
"""
# 观测中心的中心坐标,向上取整
x = int(pos_x + 0.5)
y = int(pos_y + 0.5)
# 遍历 wrap_obs 中所有像素,拼接到 self.global_map 上
for i in range(scaled_size):
for j in range(scaled_size):
absolute_x = to_absolute_coord(x + i - scaled_size // 2)
absolute_y = to_absolute_coord(y + j - scaled_size // 2)
# 这个条件限制已经拼接过的部分不要被覆盖
if wrap_obs[i, j, 3] >= 0 and self.global_map[absolute_x, absolute_y, 3] >= 150:
self.global_map[absolute_x, absolute_y] = wrap_obs[i, j]
现在一切都非常清晰了
对于逻辑层面,我复现过程中基本没有改动,因为这些逻辑本身也没什么可以改的,非要换一种写法的话,就是 “为了复现而复现” 了
做性能评估时也遇到了很多麻烦,因为及第平台上这个比赛科目的在线测评系统一直开放,所以我一开始是打算用它来测评的,但是遇到两个问题
考虑到上述问题,我决定直接写个本地测评脚本,让我复现的 agent 和原始冠军 agent 进行对抗,这样只要胜率保持在 50% 即可说明复现成功。测试时每张地图对抗 100 局,然后交换出发位置再对抗 100 局,11 张地图一共对抗 2200 局,用时 15 小时左右,我复现的 agent 胜率如下
map1 | map2 | map3 | map4 | map5 | map6 | map7 | map8 | map9 | map10 | map11 | |
---|---|---|---|---|---|---|---|---|---|---|---|
绿色位置出发 | 100% 平 | 100% 胜 | 100% 胜 | 100% 负 | 100% 胜 | 100% 胜 | 100% 负 | 100% 胜 | 100% 胜 | 100% 负 | 100% 负 |
紫色位置出发 | 100% 平 | 100% 负 | 100% 负 | 100% 胜 | 100% 负 | 100% 负 | 100% 胜 | 100% 负 | 100% 负 | 100% 胜 | 100% 胜 |
综合胜率 | 50% | 50% | 50% | 50% | 50% | 50% | 50% | 50% | 50% | 50% | 50% |
实验说明复现 agent 具有和原始冠军 agent 完全一致的性能
经过测试发现,由于两个 agent 的行为策略完全相同,导致胜负的唯一因素就是出发时的位置,第一个弯道走在内线的 agent 必胜
我对 11 张地图上的比赛各制作了一张 gif(三倍速播放),放在根目录的 Experience 文件夹中,由于使用固定策略且二者规则一致,事实上所有实验都是高度相似的,gif 中的两个 agent 的轨迹都可以看做复现 agent 从相应位置出发的运动轨迹
get_edges
函数里,寻找目标边的标准设计不是很好。作者在这里综合考虑了箭头、距离和转向角度三个因素,但是我没看出来作者在平衡这几项时的依据是什么,感觉就是一点点尝试最后修正得到了一些还可以的超参数,这个设定在 map3 这里就出现了问题,另外在 map6 的第一道门槛处也是如此get_edges
函数很难设计,冠军方法在面对超宽地图时应该会表现不佳,因为这时 agent 的观测中经常没有围墙,各个方向都是可行边,get_edges
受到的压力就会更大,一旦它给出错误的指向,agent 就会绕很多弯路;反之,冠军方法在面对窄地图时表现应该比较好,因为这时可行边数量较少,get_edges
更容易指出最优边get_displacement
函数是在有限的区域内做暴力搜索进行匹配的,如果 agent 出现剧烈碰撞,瞬时出现很大的位移,该函数识别的位移数据就可能出错,导致拼接的 global map 中出现断裂,这些断裂又会干扰 get_edges
函数的判断。不过从试验来看这种剧烈碰撞几乎不会出现add_arrows
运行出错。不过由于 get_edges
方法不只考虑了箭头,从试验结果看这一点小错误影响不大虽然 3.1 节中指出了一些问题,但是他们在冠军方案这种重建地图的框架下几乎是无解的,特别是 get_edges
函数中选取目标边的规则设计是一个开放性问题,在逐步构建地图的过程中,我们无法获取去向终点最优路径的任何绝对准确的信息(map7中的箭头也可能导致一直在外侧转圈),也就只能像作者这样根据直觉设计一个基础规则,再利用已知地图不断尝试来优化它了
但是另一方面,我们可以尝试将 Rule-based 方法和 Learning-based 方法相结合。注意到我们可以根据前几帧的观测直接判断出当前所在的地图是哪一张训练地图,那么或许可以尝试以下方法
这个方法的唯一问题在于对手可能会和我们发生碰撞,导致重放动作序列无法还原对应的最优轨迹。不过不用太担心,我们在 0.1.2 节就分析过:这个环境的对抗不是很强,超过或落后对手一定距离即可看作没有对手存在,RL 学到的能量分配策略都很激进,很容易和对手拉开差距从而避免对抗。同时,我们还可以对动作重放过程进行监控,一旦偏移最优轨迹太多,就还原到这里的冠军方法
由于时间原因没有进行实验,但是理论上讲,上述方法应该能大幅提高 agent 的性能,至少不会比现在更差