探索的大胜利——随机网络蒸馏(Random Network Distillation)

本文介绍了一种增强强化学习探索能力的方法——随机网络蒸馏(RND)。通过两个网络的竞争,RND能够自动生成内在奖励,激励智能体探索未知环境。文章详细展示了RND的工作原理及其在经典游戏上的应用。

探索的大胜利——随机网络蒸馏(Random Network Distillation)

写在前面

本项目是之前写过的一个项目项目——好奇心驱动的强化学习中鼓励探索机制(好奇心机制也是其中一种)的另一种形式,对好奇心还不是很熟悉的童鞋可以看看。

RND 有多强?

先来看一张 benchmark,环境为雅达利的游戏——蒙特祖马的复仇

可以看到一众经典算法包括 DQN、A3C、C51、ES 都考了零蛋,情况稍微好一点的算法一般是采用先进理论的算法(比如 UBE,不确定性贝尔曼方程及探索),又或者是一些分布式的训练框架(比如Ape-X,分布式优先级采样的 DQN 框架)

但是,我们的 RND 一骑绝尘,打通了所有关卡,还找到了所有隐藏房间(当然不知道训练了多久)。

RND 简介

我们的 RND 算法到底长啥样呢?这里先摆一张正经的伪代码(用于装杯)

简单来说,其实就是两个不同随机参数的网络,一个更新(称为 predictor)一个不更新(称为 target)。

target 满足的理论前提是对于不同的原始输入(比如图像),输出也要不同,也就是一对一的映射关系。

而 predictor 的目标,就是跟上 target 的脚步,如果 predictor 能跟上 target 的脚步(两个网络的输出很相似),说明 predictor 至少接受过与 target 一样的输入(也就是这个环境已经探索过了),此时内在的奖励就变少了。

举个例子,如果整张地图是一张被 target 盖住的刮刮乐,predictor 就会随着环境输入渐渐地把整个地图都刮开。

这里的内在奖励定义为 ∣ ∣ f ^ ( x ; θ ) − f ( x ) ∣ ∣ 2 ||\hat{f}(x;\theta)-f(x)||^2 f^(x;θ)f(x)2

我们一般称带小帽子的 f ^ \hat{f} f^为预测的值, θ \theta θ是参数,对应 predictor;剩下的 f f f自然就是 target 的输出。

举个例子,如果整张地图是一张被 target 盖住的刮刮乐,predictor 就会随着环境输入渐渐地把整个地图都刮开。

这里的内在奖励定义为 ∣ ∣ f ^ ( x ; θ ) − f ( x ) ∣ ∣ 2 ||\hat{f}(x;\theta)-f(x)||^2 f^(x;θ)f(x)2

我们一般称带小帽子的 f ^ \hat{f} f^为预测的值, θ \theta θ是参数,对应 predictor;剩下的 f f f自然就是 target 的输出。

RND 怎么用

了解好奇心机制的童鞋应该都知道,生成好奇心奖励的好奇心模块就是一个挂件,可以直接外接在强化学习的核心算法上。

所以,模型更新的时候记得收集一下 next state,做成一个列表。模型更新的时候按索引计算内在奖励,加在模型总损失里面就好了。

但是,一定要记得把 target 设置成 stop_gradient=True 噢!

真 · 完全体—— RND+PPO 玩马里奥

创建游戏环境

创建可供多线程的游戏环境

%%writefile MARIO/game_env.py
from __future__ import print_function
import gym_super_mario_bros
from gym.spaces import Box
from gym import Wrapper
from nes_py.wrappers import JoypadSpace
from gym_super_mario_bros.actions import SIMPLE_MOVEMENT, COMPLEX_MOVEMENT, RIGHT_ONLY
import cv2
import numpy as np
import subprocess as sp
import multiprocessing as mp


class Monitor:
    def __init__(self, width, height, saved_path):

        self.command = ["ffmpeg", "-y", "-f", "rawvideo", "-vcodec", "rawvideo", "-s", "{}X{}".format(width, height),
                        "-pix_fmt", "rgb24", "-r", "60", "-i", "-", "-an", "-vcodec", "mpeg4", saved_path]
        try:
            '''创建子进程
            '''
            self.pipe = sp.Popen(self.command, stdin=sp.PIPE, stderr=sp.PIPE)
        except FileNotFoundError:
            pass
    '''记录
    '''
    def record(self, image_array):
        self.pipe.stdin.write(image_array.tostring())


def process_frame(frame):
    if frame is not None:
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
        frame = cv2.resize(frame, (84, 84))[None, :, :] / 255.
        return frame
    else:
        return np.zeros((1, 84, 84))


class CustomReward(Wrapper):
    def __init__(self, env=None, monitor=None):
        super(CustomReward, self).__init__(env)
        self.observation_space = Box(low=0, high=255, shape=(1, 84, 84))
        self.curr_score = 0
        if monitor:
            self.monitor = monitor
        else:
            self.monitor = None

    def step(self, action):
        state, reward, done, info = self.env.step(action)
        if self.monitor:
            self.monitor.record(state)
        state = process_frame(state)
        reward += (info["score"] - self.curr_score) / 40.
        self.curr_score = info["score"]
        if done:
            if info["flag_get"]:
                reward += 50
                reward += info["time"]
            else:
                reward -= 50
        return state, reward / 10., done, info

    def reset(self):
        self.curr_score = 0
        return process_frame(self.env.reset())


class CustomSkipFrame(Wrapper):
    def __init__(self, env, skip=4):
        super(CustomSkipFrame, self).__init__(env)
        self.observation_space = Box(low=0, high=255, shape=(skip, 84, 84))
        self.skip = skip
        self.states = np.zeros((skip, 84, 84), dtype=np.float32)

    def step(self, action):
        total_reward = 0
        last_states = []
        for i in range(self.skip):
            state, reward, done, info = self.env.step(action)
            total_reward += reward
            if i >= self.s
在知识蒸馏(Knowledge Distillation, KD)过程中,学生模型的特征被随机 mask 的做法通常是为了增强模型的鲁棒性、防止过拟合,并模拟教师模型在推理过程中的不确定性。这种方法可以视为一种正则化策略,通过引入噪声来迫使学生模型学习更具泛化能力的表示。 ### 原因 1. **增强鲁棒性**:通过对输入特征或中间层输出进行随机 masking,可以提升学生模型对缺失信息的容忍度,使其在面对不完整或有噪声的数据时仍能保持较好的性能。 2. **防止过拟合**:masking 操作本质上是一种数据增强手段,它减少了学生模型对特定特征的依赖,从而避免其过度拟合训练集中的某些模式[^3]。 3. **模拟教师模型的行为**:在某些情况下,教师模型可能并不总是提供完全一致的 soft label,尤其是在存在背景噪声或标签分布不平衡的情况下。通过对学生模型的特征进行随机 masking,可以更好地模仿这种不确定性,使学生模型更贴近教师模型的学习目标[^1]。 ### 实现方法 1. **输入特征的随机 masking**: 在输入层对学生模型的特征向量进行部分 masking,通常是将一部分特征值设置为零或者替换为平均值。例如,在自然语言处理任务中,可以对词嵌入向量的一部分维度进行 masking: ```python import torch import torch.nn.functional as F def random_feature_masking(features, mask_prob=0.1): """ 对输入特征进行随机 masking。 参数: features (Tensor): 输入特征张量,形状为 [batch_size, feature_dim] mask_prob (float): 每个特征被 masking 的概率 返回: Tensor: 经过 masking 后的特征 """ mask = torch.rand(features.shape) < mask_prob masked_features = features.clone() masked_features[mask] = 0 # 或者使用平均值填充 return masked_features ``` 2. **中间层输出的 masking**: 在 Transformer 架构中,可以在 attention 层或前馈网络(FFN)的输出上应用 masking。例如,在 attention 头之间选择一部分进行 masking,以减少冗余计算并提升模型的注意力集中能力[^2]。 ```python class AttentionWithMasking(torch.nn.Module): def __init__(self, embed_dim, num_heads): super(AttentionWithMasking, self).__init__() self.multihead_attn = torch.nn.MultiheadAttention(embed_dim, num_heads) def forward(self, query, key, value, mask_ratio=0.2): # 对 key 和 value 进行随机 masking batch_size, seq_len, _ = key.size() mask = torch.rand((batch_size, seq_len)) < mask_ratio key[mask] = 0 value[mask] = 0 attn_output, _ = self.multihead_attn(query, key, value) return attn_output ``` 3. **损失函数中的 masking 效应**: 在定义损失函数时,也可以隐式地实现类似 masking 的效果。例如,在交叉熵损失中忽略某些预测位置,只关注未被 mask 的部分,这在序列到序列的任务中尤为常见(如 BERT 的 MLM 任务)。 ```python def masked_cross_entropy(logits, targets, ignore_index=-100): """ 计算带有忽略标记的交叉熵损失。 参数: logits (Tensor): 模型输出 logits,形状为 [batch_size, vocab_size] targets (Tensor): 目标标签,形状为 [batch_size] ignore_index (int): 需要忽略的索引值 返回: Tensor: 平均损失值 """ loss = F.cross_entropy(logits, targets, ignore_index=ignore_index) return loss ``` 综上所述,学生模型在知识蒸馏过程中采用特征随机 masking 是一种有效的策略,既能提升模型的泛化能力,又能更好地匹配教师模型的输出特性。 ---
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值