语言模型|第二章|强化学习

一、序论

强化学习是一种机器学习方法,它通过智能体(agent)在与环境的交互中学习如何采取行动以最大化某种累积奖励。强化学习的核心思想是基于试错,智能体通过不断尝试不同的行动,观察结果,并根据结果调整策略,以逐步优化其行为。

强化学习的基本组成部分包括:

  1. 智能体(Agent):执行动作并从环境中接收反馈的实体。
  2. 环境(Environment):智能体所处的世界,它对智能体的动作做出反应并提供反馈。
  3. 状态(State):环境在某一时刻的描述,通常用一组特征表示。
  4. 动作(Action):智能体在某一状态下可以采取的操作。
  5. 奖励(Reward):环境对智能体动作的即时反馈,通常是一个数值,表示动作的好坏。
  6. 策略(Policy):智能体根据当前状态选择动作的规则或函数。
  7. 价值函数(Value Function):评估某一状态或状态-动作对长期累积奖励的期望值。
  8. 模型(Model):对环境的模拟,用于预测状态转移和奖励。

以围棋界大名鼎鼎的深蓝和Alpha go为例,背后的算法就是智能体,棋盘就是环境。每一次落子就是一个动作,落子完后就是一个状态。奖励就是赢或输,但围棋很难一开始就能判断输赢,所以奖励也会在探索的过程中更新。模型就是对整个棋盘、包括规则进行建模。由于我们很难在游戏结束前就对某一状态或动作进行价值判定,因此需要一个评估函数,这就是价值函数。

强化学习在许多领域都有广泛应用,如游戏AI、机器人控制、自动驾驶、推荐系统等。它能够处理复杂的决策问题,尤其是在环境动态变化且奖励信号稀疏的情况下,强化学习能够通过探索和利用的平衡,逐步学习到最优策略。大模型微调就会应用到人类反馈强化学习------RLHF。通过设定奖惩规则,让模型的输出更具有某种风格、更适用于某个领域。

二、强化学习的几种思想

强化学习的目标就是训练一个智能体,如这个老鼠,能够在任意状态(位置)都能找到最优策略(路线),以实现最大累计回报(如快速找到出口或吃到零食)。训练的过程中,老鼠前进一步就是在探索,遇到墙壁反馈就是负,需要回退。反之就是正,可以继续向下探索。如前所述,在彻底赢或输之前,每一个动作对应的反馈都不是最终的。如何评估某个状态下某个动作的价值,进而指导智能体做出正确的决策,下面是一些前人提出的想法。

1、蒙特卡洛

蒙特卡洛方法本质上是一种基于随机采样的数值计算方法,通常用于全局估计。通过采样完整的轨迹(episode)来估计价值函数(ValueFunction)或策略(Policy)。原理如下:

采样轨迹:从初始状态开始,根据当前策略与环境交互,生成完整的轨迹(episode)。

估计价值函数:使用轨迹的累积回报(Return)来估计状态或状态-动作对的价值。

策略改进:根据估计的价值函数,使用策略改进方法(如ε-greedy策略)来更新策略。

2、动态规划

动态规划方法通过利用环境的动态模型(状态转移概率和奖励函数)来计算最优策略。核心在于每一步求一个当前子问题的max,然后逐层往上迭代。缺点是需要环境已知,无需探索就知道每个动作对应的反馈。原理如下:

策略评估:根据当前策略,计算状态或状态-动作对的价值函数。

策略改进:根据价值函数,使用贪心策略或其他策略改进方法来更新策略。

迭代优化:通过反复进行策略评估和策略改进,逐步优化策略,直到收敛到最优策略。

关于动态规划解决实际问题的案例以及代码请移步我的另一篇总结:

动态规划思想及应用_动态规划投资分配-CSDN博客

3、时序差分

时序差分方法结合了蒙特卡洛方法和动态规划方法的特点,通过采样部分轨迹来估计价值函数或策略。原理如下:

采样部分轨迹:从初始状态开始,根据当前策略与环境交互,生成部分轨迹。

估计价值函数:使用时序差分更新公式(如TD(0)、TD(λ))来估计状态或状态-动作对的价值。

策略改进:根据估计的价值函数,使用策略改进方法(如ε-greedy策略)来更新策略。

4、关键概念与差别分析

无论是哪种思路,都涉及到了价值函数和策略。

(1)状态价值函数:v=f(s),表示从该状态s开始,遵循当前策略所能获得的期望累积回报。

(2)状态-动作价值函数:v=f(s, a),表示在状态s下采取动作a,并遵循当前策略所能获得的期望累积回报。

(3)确定性策略:π(s) = a,在确定性策略中,每个状态s对应一个确定的动作a。这意味着在状态s下,智能体总是选择动作a。

(4)随机性策略:π(a|s),在随机性策略中,每个状态s对应一个动作的概率分布,表示为π(a|s)。这意味着在状态s下,智能体根据概率分布选择动作a。比如在走迷宫游戏中,π(向上|(x, y)) = 0.7表示在位置(x, y)下,智能体有70%的概率选择向上移动。

蒙特卡洛不需要环境的动态模型,直接通过采样轨迹来学习。但需要完整的轨迹才能进行价值估计,适用于回合制任务。完整轨迹指的是,基于当前策略(如右移动),一直走到出口或走不动为止。然后才根据结果更新价值函数和策略。因此,可以看到,这类算法的反馈即使在后期也存在较大振荡。(我的理解是,前999步累积了很高的一个正反馈,最后一步是死胡同,一下全部变成负反馈了。)使用该思想的算法有DQN、Reinforce。动态规划需要环境的动态模型,即状态转移概率和奖励函数。需要计算所有状态和动作的值,计算复杂度较高。在动态规划中通常用于求解马尔可夫决策过程。时序差分不需要完整的轨迹,适用于连续任务。由于使用部分轨迹进行更新,估计结果的方差较低(振荡小)。代表算法PPO。

三、强化学习的几种实现方法

以一个游戏为例,展示几种强化学习的区别。模型需要学习如何移动下面的黑色物体从而使木棍不倒。这是一个动态交互的过程,非常适合使用强化学习来完成任务。

1、DQN(value-base,变种:DoubleDQN,DuelingDQN

DQN的核心思想是将深度神经网络(Deep Neural Network, DNN)与Q-learning算法结合,以处理高维状态空间的问题。传统的Q-learning算法适用于离散状态和动作空间,但当状态空间非常大时(例如图像输入),传统的表格方法(Q-table)变得不可行。DQN通过使用深度神经网络来近似Q函数,从而解决了这个问题。

(1)DQN大致过程:

① 初始化

  • 初始化主网络和目标网络的参数。
  • 初始化经验回放缓冲区。

② 交互与采样

  • 智能体在环境中执行动作,并记录经验(状态、动作、奖励、下一个状态)到经验回放缓冲区。
  • 从经验回放缓冲区中随机采样一批经验用于训练。

③ 计算目标Q值

  • 使用目标网络计算下一个状态的最大Q值:max Q(s', a'; θ-),其中θ-是目标网络的参数。
  • 计算目标Q值:y = r + γ * max Q(s', a'; θ-),其中r是当前状态的奖励,γ是折扣因子。

④ 更新主网络

  • 使用目标Q值和主网络的预测Q值计算损失函数:L = (y - Q(s, a; θ))^2
  • 使用梯度下降法更新主网络的参数θ

⑤ 更新目标网络

  • 每隔一定步数,将主网络的参数θ复制到目标网络的参数θ-

核心代码:

python 复制代码
class DQN:
    def __init__(self, state_dim, hidden_dim, action_dim, lr, gama, epsilon, target_update, device, dqn_type='DQN'):
        self.action_dim = action_dim
        # 主网络和目标网络的结构是相同的
        # 目标网络用于计算目标Q值,以减少Q值更新的波动性,提高训练的稳定性。
        self.q_net = Qnet(state_dim, hidden_dim, action_dim).to(device)
        self.target_q_net = Qnet(state_dim, hidden_dim, action_dim).to(device)
        self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=lr)
        self.gama = gama
        self.epsilon = epsilon
        self.target_update = target_update
        self.count = 0
        self.device = device
        self.dqn_type = dqn_type

    def take_action(self, state):
        # 以ε的概率随机选择一个动作,可以探索新的动作
        if np.random.random() < self.epsilon:
            action = np.random.randint(self.action_dim)
        else:
            # 以1-ε的概率选择当前价值最高的动作,平衡探索与利用
            # 也可以只选择当前价值最高的动作,前提是当前的价值函数估计比较准确
            state = torch.tensor(state, dtype=torch.float).to(self.device)
            action = self.q_net(state).argmax().item()
        return action

    def update(self, transition_dict):
        states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).view(-1, 1).to(self.device)
        rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).view(-1, 1).to(self.device)
        next_states = torch.tensor(transition_dict['next_states'], dtype=torch.float).to(self.device)
        dones = torch.tensor(transition_dict['dones'], dtype=torch.float).view(-1, 1).to(self.device)
        q_values = self.q_net(states).gather(1, actions)
        max_next_q = self.target_q_net(next_states).max(1)[0].view(-1, 1)
        q_target = rewards + self.gama * max_next_q * (1-dones)
        dqn_loss = torch.mean(F.mse_loss(q_values, q_target))
        self.optimizer.zero_grad()
        dqn_loss.backward()
        self.optimizer.step()
        if self.count % self.target_update == 0:
            # 传递网络参数,相当于目标网络的参数是主网络参数的延迟更新版本
            self.target_q_net.load_state_dict(self.q_net.state_dict())
        self.count += 1
(2)DoubleDQN

DoubleDQN是对DQN的一种改进,旨在解决DQN中的过估计(overestimation)问题。在DQN中,目标Q值的计算方式为y=r+γ*maxQ(s',a';θ-),其中maxQ(s',a';θ-)表示在下一个状态s'下,使用目标网络参数θ-计算的最大Q值。由于max操作的存在,DQN容易高估下一个状态的Q值,尤其是在训练初期,Q值估计不准确时。这种高估可能导致次优策略,因为智能体可能会选择被高估的动作。

DoubleDQN通过引入两个独立的网络来计算目标Q值,从而减少过估计。具体说,DoubleDQN使用主网络(q_net)来选择动作,使用目标网络(tartget_q_net)来评估该动作的Q值。这也就是double的由来。

python 复制代码
if self.dqn_type == 'DoubleDQN':
    # 使用主网络的Q函数获取最大Q对应的动作
    max_a = self.q_net(next_states).argmax(1).view(-1, 1)
    # 使用目标网络评估Q值
    max_next_q = self.target_q_net(next_states).gather(1, max_a).view(-1, 1)
else:
    max_next_q = self.target_q_net(next_states).max(1)[0].view(-1, 1)
q_target = rewards + self.gama * max_next_q * (1 - dones)
(4)DuelingDQN

相对普通的DQN就是把神经网络由Qnet替换为VAnet,也就是替换价值函数

Q(s,a)=V(s)+A(s,a)

通过将Q值分解为状态值和优势值,DuelingDQN能够更好地表示状态的内在价值,从而提高对状态的区分能力。在某些情况下,状态值的变化比动作值的变化更为重要,DuelingDQN能够更好地捕捉这种变化。DuelingDQN通过优势值函数来表示动作的相对优势,从而提高对动作的区分能力。在动作空间较大的情况下,DuelingDQN能够更准确地选择最优动作。代码如下:

python 复制代码
class Qnet(nn.Module):
    def __init__(self, state_dim, hidden_dim, action_dim):
        super().__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.fc2 = nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return self.fc2(x)


class VAnet(nn.Module):
    def __init__(self, state_dim, hidden_dim, action_dim):
        super().__init__()
        self.fc1 = nn.Linear(state_dim, hidden_dim)
        self.V = nn.Linear(hidden_dim, 1)
        self.A = nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        if len(x.shape) == 1:
            x = torch.unsqueeze(x, 0)
        x = F.relu(self.fc1(x))
        A = self.A(x)
        V = self.V(x)
        Q = V + A - A.mean(1).view(-1, 1)
        return Q

2、Reinforce(policy_base)

REINFORCE算法是一种基于策略梯度的强化学习算法,通过直接优化策略函数来学习最优策略,适用于连续或离散的动作空间。具体来说,REINFORCE算法通过计算策略的梯度,并使用梯度上升法来更新策略参数,从而最大化期望回报。这个策略函数就是告诉你,在某个状态下最推荐某一个动作(概率最高的)。

算法优点是直接优化策略函数,适用于连续或离散的动作空间。原理简单,易于理解和实现。缺点是容易受到高方差问题的影响,导致训练不稳定。需要大量的样本进行训练,样本效率较低。

算法的流程可以概括为以下几个步骤:

初始化 -初始化策略函数的参数θ

交互与采样 -智能体在环境中执行动作,并记录轨迹(状态、动作、奖励)。-从轨迹中计算每个时间步的累积回报Gt

计算策略梯度 -使用轨迹数据计算策略梯度:∇θJ(θ)=∑t=0T∇θlogπ(at|st;θ)Gt

更新策略参数 -使用梯度上升法更新策略参数θθ←θ+α∇θJ(θ),其中α是学习率。

重复步骤2-4-重复步骤2-4,直到策略收敛或达到预定的迭代次数。

核心代码如下:

python 复制代码
class REINFORCE:
    def __init__(self, state_dim, hidden_dim, action_dim, lr, gama, device):
        self.policy_net = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
        self.optimizer = torch.optim.Adam(self.policy_net.parameters(), lr=lr)
        self.gama = gama
        self.device = device

    def take_action(self, state):
        state = torch.tensor([state], dtype=torch.float).to(self.device)
        # 经过网络后会输出每个类别(动作)的概率
        probs = self.policy_net(state)
        # 创建一个分类分布对象
        action_dist = torch.distributions.Categorical(probs)
        # 依据概率进行采样(多次采样后的概率满足提供的概率,但一次采样并不一定会选择概率较高的)
        action = action_dist.sample()
        return action.item()

    def update(self, transition_dict):
        reward_list = transition_dict['rewards']
        state_list = transition_dict['states']
        action_list = transition_dict['actions']
        G = 0
        self.optimizer.zero_grad()
        # 采样到底,然后回填奖励,所以是逆序
        for i in reversed(range(len(reward_list))):
            r = reward_list[i]
            state = torch.tensor([state_list[i]], dtype=torch.float).to(self.device)
            action = torch.tensor([action_list[i]]).view(-1, 1).to(self.device)
            log_prob = torch.log(self.policy_net(state).gather(1, action))
            # 累计回报
            G = self.gama * G + r
            # 交叉熵
            loss = -log_prob * G
            loss.backward()
        self.optimizer.step()

3、PPO(Actor-Critic,结合value和policy)

PPO(Proximal Policy Optimization,近端策略优化),旨在通过一种稳定且高效的方式来优化策略,避免传统策略梯度方法中可能出现的策略更新过大或不稳定的问题。

PPO算法的步骤如下:

① 收集数据

使用当前策略与环境交互,收集一批轨迹数据。每个轨迹包含状态、动作、奖励和下一个状态等信息。

② 计算优势函数

使用收集到的轨迹数据计算优势函数。优势函数可以通过多种方法计算,例如使用广义优势估计(GAE)。

③ 计算裁剪目标函数

使用当前策略和收集到的数据计算裁剪目标函数。

④ 更新策略

使用裁剪目标函数进行策略更新。通常使用梯度上升方法来最大化裁剪目标函数。

⑤ 重复步骤

重复上述步骤,直到策略收敛或达到预定的迭代次数。

核心代码如下:

python 复制代码
class PPO:
    def __init__(self, state_dim, hidden_dim, action_dim, actor_lr, critic_lr, lmbda, epochs, eps, gama, device):
        # 学习策略
        self.actor = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
        # 学习价值
        self.critic = ValueNet(state_dim, hidden_dim).to(device)
        self.opt_actor = torch.optim.Adam(self.actor.parameters(), lr=actor_lr)
        self.opt_critic = torch.optim.Adam(self.critic.parameters(), lr=critic_lr)
        self.gama = gama
        self.lmbda = lmbda
        self.epochs = epochs
        self.eps = eps
        self.device = device

    def take_action(self, state):
        state = torch.tensor([state], dtype=torch.float).to(self.device)
        probs = self.actor(state)
        action_dict = torch.distributions.Categorical(probs)
        action = action_dict.sample()
        return action.item()

    def update(self, transition_dict):
        states = torch.tensor(transition_dict['states'], dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).view(-1, 1).to(self.device)
        rewards = torch.tensor(transition_dict['rewards'], dtype=torch.float).view(-1, 1).to(self.device)
        next_states = torch.tensor(transition_dict['next_states'], dtype=torch.float).to(self.device)
        dones = torch.tensor(transition_dict['dones'], dtype=torch.float).view(-1, 1).to(self.device)

        td_target = rewards + self.gama * self.critic(next_states) * (1 - dones)
        td_delta = td_target - self.critic(states)
        advantage = compute_advantage(self.gama, self.lmbda, td_delta.cpu()).to(self.device)
        old_log_probs = torch.log(self.actor(states).gather(1, actions)).detach()

        for _ in range(self.epochs):
            log_probs = torch.log(self.actor(states).gather(1, actions))
            ratio = torch.exp(log_probs - old_log_probs)
            surr1 = ratio * advantage
            # ppo的截断体现在这里, 限制梯度的范围
            surr2 = torch.clamp(ratio, 1-self.eps, 1+self.eps) * advantage
            actor_loss = torch.mean(-torch.min(surr1, surr2))
            critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))
            self.opt_actor.zero_grad()
            self.opt_critic.zero_grad()
            actor_loss.backward()
            critic_loss.backward()
            self.opt_actor.step()
            self.opt_critic.step()

通过对比训练过程的反馈值趋势可知,PPO的效率很高,episode为100时就能达到较高的奖励。

四、人类反馈强化学习RLHF

RLHF(Reinforcement Learning from Human Feedback)是一种结合了人类反馈的强化学习方法。与传统的强化学习算法不同,RLHF通过引入人类专家或用户的反馈来增强学习过程,从而提高学习效率和策略质量。人类反馈可以是显式的(如评分、标签)或隐式的(如示范、纠正)。

以上知识都是掌握大模型所必须的基础,也是私有化部署所需要用到的技术。在GPT3.5以前,大模型并没有引起什么大的关注。主要就是它的回答太像机器人。而在3.5以后,我们突然发现,这个智能体居然能像人类一样同我们交流,机器的痕迹已经很淡了。虽然它回复的内容总体没有变化。另外,大模型一般只适用于通用场景,具体业务场景都需要给一些数据、示例来进行微调,机器人回复的语气也可以根据实际场景进行调整。这些都会涉及强化学习。

3种强化学习实现代码都放在了网盘里,代码包含关键点的注释。请扫码或关注同名公众号后,在资源获取栏选择【语言模型】获取。后续还会更新大模型微调的代码。

相关推荐
YSGZJJ1 小时前
股指期货的套保策略如何精准选择和规避风险?
人工智能·区块链
无脑敲代码,bug漫天飞1 小时前
COR 损失函数
人工智能·机器学习
HPC_fac130520678162 小时前
以科学计算为切入点:剖析英伟达服务器过热难题
服务器·人工智能·深度学习·机器学习·计算机视觉·数据挖掘·gpu算力
小陈phd4 小时前
OpenCV从入门到精通实战(九)——基于dlib的疲劳监测 ear计算
人工智能·opencv·计算机视觉
Guofu_Liao5 小时前
大语言模型---LoRA简介;LoRA的优势;LoRA训练步骤;总结
人工智能·语言模型·自然语言处理·矩阵·llama
ZHOU_WUYI9 小时前
3.langchain中的prompt模板 (few shot examples in chat models)
人工智能·langchain·prompt
如若1239 小时前
主要用于图像的颜色提取、替换以及区域修改
人工智能·opencv·计算机视觉
老艾的AI世界10 小时前
AI翻唱神器,一键用你喜欢的歌手翻唱他人的曲目(附下载链接)
人工智能·深度学习·神经网络·机器学习·ai·ai翻唱·ai唱歌·ai歌曲
DK2215110 小时前
机器学习系列----关联分析
人工智能·机器学习
Robot25110 小时前
Figure 02迎重大升级!!人形机器人独角兽[Figure AI]商业化加速
人工智能·机器人·微信公众平台