【大模型理论篇】强化学习RL与大模型智能体

1. 背景介绍        

        大模型商业化,解决某类实际的业务问题,仅靠大模型本身其实存在很大的局限性,很多场景下不足以完全胜任真实业务需求【1】。

        例如,如果给大模型发出指令:“过去5年间,隐私计算和数据要素流通市场的增长趋势如何?陆续出台的数据安全政策对其有何影响?是否可以提供过去5年内数据价值流通市场逐年发展的图表?”仅凭大模型自身的能力,可能无法完整解答如此复杂的问题。虽然引入外部知识库的RAG(检索增强生成)模式可能部分弥补这个不足,但完整回答仍需要额外操作。首先,需要将问题拆解为多个子问题,其次需通过特定工具和流程逐步解决,以最终获得所需答案。一个可能的解决方案是开发一个能够访问最新环境政策文献、市场报告及公开数据库的大模型智能体,以获取关于隐私计算市场增长及其政策影响的信息。此外,这个大模型智能体还应配备“数据分析”工具,以利用收集到的数据生成直观图表,清晰展示过去5年隐私计算市场的趋势。智能体需要具备解决方案的规划和记忆模块的设计,有助于智能体追踪操作流程、监控和评估整体进展。

2. 基础知识

        在介绍大模型智能体之前,还是让我们先回顾一下强化学习的基本概念,大模型的智能体与传统强化学习智能体在概念上还是存在一定的关联。

2.1 基础术语

        强化学习(Reinforcement Learning, RL)是机器学习中的关键范式,通过优化累积奖励来指导代理与环境的交互。与依赖标注数据的监督学习和在无标注数据中寻找模式的无监督学习不同,RL专注于通过试错方式从直接反馈中学习。如果场景是缺乏明确指示但自主决策是关键的,那么比较适合采用强化学习。

        强化学习中涉及的关键术语和概念解释:

智能体(Agent): 在强化学习(RL)中,智能体(也称代理)是核心所在,负责在环境中导航。智能体具备感知环境状态的能力,通过交互采取行动,以实现预定目标。

环境(Environment): 环境指智能体与之交互的外部系统,构成了智能体操作的背景。环境通常建模为马尔可夫决策过程(MDP),包括状态、动作、转移概率和奖励。

动作(Actions): 动作指智能体在环境中可采取的潜在行动或决策。智能体的目标是选择有助于实现目标的动作。

奖励(Rewards): 奖励是环境给智能体的反馈机制,指示所采取动作的价值。无论是即时的还是延迟的,奖励在智能体的学习过程中非常关键。

策略(Policies): 策略代表智能体的行为策略,决定了在不同环境状态下采取的行动。这些策略可以是确定性的或随机性的,随着时间推移,它们会演变为最大化累积奖励的策略。

2.2 强化算法与技术

2.2.1 Q-Learning

Q学习(Q-Learning)是强化学习(RL)中的基本算法,专注于估计特定状态下动作的价值。通过迭代更新,智能体不断调整其Q值,逐渐收敛到最优策略。Q-Learning 是一种强化学习中的无模型(model-free)算法,它通过学习状态-动作对的价值函数(即 Q 值)来指导代理选择动作,从而最大化累积奖励。Q-Learning 的核心思想是在不同状态下评估每个可能的动作,并通过迭代更新 Q 值来逐渐逼近最优策略。

Q-Learning 的基本概念

  1. 状态 (State, s): 智能体在环境中的当前情况或位置。

  2. 动作 (Action, a): 智能体在当前状态下可以执行的操作。

  3. 奖励 (Reward, r): 智能体执行某个动作后从环境中获得的反馈,用来衡量动作的好坏。

  4. Q 值 (Q-Value, Q(s, a)): 表示在状态 s 下选择动作 a 后的预期累积奖励。Q 值是一个状态-动作对的价值函数。

  5. 折扣因子 (Discount Factor, γ): 用于衡量未来奖励的重要性,通常取值在 [0, 1] 之间。γ 越接近 1,表示未来奖励的重要性越大。

  6. 学习率 (Learning Rate, α): 决定新信息对更新 Q 值的影响程度。学习率越高,表示新信息的重要性越大。

Q-Learning 的更新公式

Q-Learning 通过以下更新公式来迭代 Q 值:

Q(s, a) \leftarrow Q(s, a) + \alpha \left[ r + \gamma \max_{a'} Q(s', a') - Q(s, a) \right]

  • 当前状态 (s): 智能体所处的状态。
  • 当前动作 (a): 智能体在状态 s 下选择的动作。
  • 奖励 (r): 智能体执行动作 a 后从环境中获得的即时奖励。
  • 下一状态 (s'): 智能体执行动作 a 后转移到的新状态。
  • 最优未来 Q 值 (\max_{a'} Q(s', a')): 在下一状态 s' 中所有可能动作的 Q 值中的最大值。
  • α: 学习率,用于控制旧 Q 值和新信息之间的平衡。
  • γ: 折扣因子,用于权衡当前奖励与未来奖励的关系。

Q-Learning 的工作流程

  1. 初始化 Q 值表: 对所有状态-动作对的 Q 值初始化(通常为零)。

  2. 观察当前状态 s

  3. 选择动作 a: 通过某种策略(如 ε-贪婪策略),基于当前 Q 值表选择一个动作。

  4. 执行动作 a,并观察获得的奖励 r 和下一个状态 s'。

  5. 更新 Q 值表: 根据更新公式计算并更新 Q 值。

  6. 转移到下一个状态 s',并重复上述步骤,直到达到终止条件。

  7. 策略提取: 最终的策略可以通过选择在每个状态下具有最大 Q 值的动作来确定。

Q-Learning 的特点

  • 无模型 (Model-Free): Q-Learning 不需要知道环境的模型(如转移概率),只需要通过与环境的交互来学习最优策略。

  • 离线学习 (Off-Policy): Q-Learning 是一种离线学习算法,即它的更新与实际采取的策略无关,可以利用不同的策略进行学习。

以下代码展示了Q学习算法在简化网格环境中的学习过程【2】,相对来说比较容易理解。

import numpy as np

# 定义环境
n_states = 16  # 网格世界中的状态数量
n_actions = 4  # 可能的动作数量(上、下、左、右)
goal_state = 15  # 目标状态

# 用零初始化 Q 表
Q_table = np.zeros((n_states, n_actions))

# 定义参数
learning_rate = 0.8  # 学习率
discount_factor = 0.95  # 折扣因子
exploration_prob = 0.2  # 探索概率
epochs = 1000  # 训练轮数

# Q 学习算法
for epoch in range(epochs):
    current_state = np.random.randint(0, n_states)  # 从一个随机状态开始

    while current_state!= goal_state:
        # 用 ε-贪心策略选择动作
        if np.random.rand() < exploration_prob:
            action = np.random.randint(0, n_actions)  # 探索,随机选择一个动作
        else:
            action = np.argmax(Q_table[current_state])  # 利用,选择当前状态下 Q 值最大的动作

        # 模拟环境(移动到下一个状态)
        # 为了简单起见,直接移动到下一个状态
        next_state = (current_state + 1) % n_states

        # 定义一个简单的奖励函数(如果到达目标状态为 1,否则为 0)
        reward = 1 if next_state == goal_state else 0

        # 使用 Q 学习的更新规则更新 Q 值
        Q_table[current_state, action] += learning_rate * (reward + discount_factor * np.max(Q_table[next_state]) - Q_table[current_state, action])

        current_state = next_state  # 移动到下一个状态

# 训练后,Q 表代表了学习到的 Q 值
print("学习到的 Q 表:")
print(Q_table)

2.2.2 DQN

深度Q网络(Deep Q-Networks, DQN)通过引入深度学习技术,利用神经网络来近似Q值。这一结合使RL算法能够处理高维状态空间,并在复杂环境中取得显著性能。

有了前述的Q-Learning的背景知识,理解DQN会相对容易。

Deep Q-Network(深度Q网络,简称DQN)是一种结合了Q学习和深度神经网络的强化学习算法。它由DeepMind在2015年提出,并因其在玩Atari游戏中达到接近人类水平的表现而广为人知。

DQN关键概念

  1. Q学习

    Q学习是一种基于策略外(off-policy)的强化学习算法,用于通过学习状态-动作对的价值来找到最优的动作选择策略。Q值代表了在给定状态和动作下,期望的未来奖励。
  2. 深度神经网络

    在DQN中,使用深度神经网络来近似Q值函数。网络以状态作为输入,并输出所有可能动作的Q值。
  3. 经验回放

    DQN采用了一种称为经验回放的技术,将经验(状态、动作、奖励、下一状态)存储在一个回放记忆中。在训练过程中,从这个记忆中随机抽取小批量经验进行训练,以打破连续经验之间的相关性并提高训练的稳定性。
  4. 目标网络

    为了稳定训练,DQN使用了一个目标网络,它是Q网络的一个拷贝,更新频率较低。训练主网络时,目标网络用于生成Q值目标。
  5. 损失函数

    DQN通过最小化Q网络预测的Q值与目标Q值之间的差异来进行训练,目标Q值由目标网络计算得出。

DQN算法概述

  1. 初始化一个固定大小的回放记忆。
  2. 初始化Q网络,权重为随机值。
  3. 对每个回合:
    • 从一个初始状态开始。
    • 对每一步:
      • 以概率ε(epsilon)选择一个随机动作(探索),或选择Q值最高的动作(利用)。
      • 执行该动作并观察奖励和下一个状态。
      • 将经验存储在回放记忆中。
      • 从回放记忆中随机抽取一个小批量的经验。
      • 使用目标网络计算目标Q值。
      • 通过最小化预测的Q值与目标Q值之间的损失来更新Q网络。
    • 定期使用Q网络的权重更新目标网络。

        这里,我们参考【3】展示实际的一个DQN学习代码示例“倒立摆”任务,在倒立摆问题中,任务是通过推动小车向左或向右来平衡一根竖直的杆。

        当智能体观察环境中倒立杆的当前状态并选择一个动作时,环境会转换到一个新状态,并返回一个奖励,该奖励表明该动作的后果。在这个任务中,每一个增量时间步的奖励为 + 1,如果杆子倾斜得太厉害或者手推车从中心移动超过 2.4 个单位,环境就会终止。这意味着表现更好的场景将持续更长时间,积累更大的回报。倒立摆任务的设计使得智能体的输入是代表环境状态(位置、速度等)的 4 个实数值。直接使用这 4 个输入而不进行任何缩放,并将它们通过一个具有 2 个输出(坐或者右)的小型全连接网络,每个动作对应一个输出。这个网络被训练为在给定输入状态的情况下预测每个动作的预期值。然后选择具有最高预期值的动作。

        整个训练过程会持续600 episode,可以观察倒立杆稳定的持续时间,从学习的最终结果来看,模型还是很有效果的,持续时间duration从一开始的比较短,慢慢震荡向上增长,最终基本实现了倒立摆的持续长时间平衡。

import gymnasium as gym
import math
import random
import matplotlib
import matplotlib.pyplot as plt
from collections import namedtuple, deque
from itertools import count

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

# 创建“CartPole-v1”环境
env = gym.make("CartPole-v1")

# 设置matplotlib
is_ipython = 'inline' in matplotlib.get_backend()
if is_ipython:
    from IPython import display

plt.ion()  # 开启交互模式

# 如果需要使用GPU
device = torch.device(
    "cuda" if torch.cuda.is_available() else
    "mps" if torch.backends.mps.is_available() else
    "cpu"
)

# 定义Transition命名元组,包含状态、动作、下一个状态和奖励
Transition = namedtuple('Transition',
                        ('state', 'action', 'next_state', 'reward'))

# 定义经验回放存储类
class ReplayMemory(object):

    def __init__(self, capacity):
        # 初始化一个双端队列,最大容量为capacity
        self.memory = deque([], maxlen=capacity)

    def push(self, *args):
        """保存一个转换(transition)"""
        self.memory.append(Transition(*args))

    def sample(self, batch_size):
        # 随机抽取batch_size大小的样本
        return random.sample(self.memory, batch_size)

    def __len__(self):
        # 返回当前存储的样本数量
        return len(self.memory)

# 定义深度Q网络(DQN)模型
class DQN(nn.Module):

    def __init__(self, n_observations, n_actions):
        super(DQN, self).__init__()
        # 定义三层全连接网络
        self.layer1 = nn.Linear(n_observations, 128)
        self.layer2 = nn.Linear(128, 128)
        self.layer3 = nn.Linear(128, n_actions)

    # 前向传播函数,输入x,输出各动作的Q值
    def forward(self, x):
        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        return self.layer3(x)

# 设置超参数
BATCH_SIZE = 128  # 从回放存储中采样的转换数量
GAMMA = 0.99  # 折扣因子
EPS_START = 0.9  # epsilon的初始值
EPS_END = 0.05  # epsilon的最终值
EPS_DECAY = 1000  # epsilon的指数衰减率,越大衰减越慢
TAU = 0.005  # 目标网络的更新率
LR = 1e-4  # AdamW优化器的学习率

# 获取动作数量
n_actions = env.action_space.n
# 获取状态观测数量
state, info = env.reset()
n_observations = len(state)

# 初始化策略网络和目标网络
policy_net = DQN(n_observations, n_actions).to(device)
target_net = DQN(n_observations, n_actions).to(device)
target_net.load_state_dict(policy_net.state_dict())  # 目标网络初始权重与策略网络相同

# 定义优化器和经验回放存储
optimizer = optim.AdamW(policy_net.parameters(), lr=LR, amsgrad=True)
memory = ReplayMemory(10000)  # 经验回放存储容量为10000

steps_done = 0  # 记录执行的步数

# 选择动作的函数
def select_action(state):
    global steps_done
    sample = random.random()
    eps_threshold = EPS_END + (EPS_START - EPS_END) * \
        math.exp(-1. * steps_done / EPS_DECAY)  # 计算当前epsilon值
    steps_done += 1
    if sample > eps_threshold:
        with torch.no_grad():
            # t.max(1) 返回每一行最大值的索引
            # 我们选择期望奖励更大的动作
            return policy_net(state).max(1).indices.view(1, 1)
    else:
        # 否则随机选择一个动作
        return torch.tensor([[env.action_space.sample()]], device=device, dtype=torch.long)

episode_durations = []  # 记录每个回合的持续时间

# 绘制持续时间的函数
def plot_durations(show_result=False):
    plt.figure(1)
    durations_t = torch.tensor(episode_durations, dtype=torch.float)
    if show_result:
        plt.title('Result')
    else:
        plt.clf()
        plt.title('Training...')
    plt.xlabel('Episode')
    plt.ylabel('Duration')
    plt.plot(durations_t.numpy())
    # 绘制100回合的平均持续时间
    if len(durations_t) >= 100:
        means = durations_t.unfold(0, 100, 1).mean(1).view(-1)
        means = torch.cat((torch.zeros(99), means))
        plt.plot(means.numpy())

    plt.pause(0.001)  # 暂停以便更新图像
    if is_ipython:
        if not show_result:
            display.display(plt.gcf())
            display.clear_output(wait=True)
        else:
            display.display(plt.gcf())

# 优化模型的函数
def optimize_model():
    if len(memory) < BATCH_SIZE:
        return
    transitions = memory.sample(BATCH_SIZE)  # 从回放存储中采样一个batch
    # 转置batch,将数组的batch转换为batch的数组
    batch = Transition(*zip(*transitions))

    # 计算非终止状态的掩码,并将batch元素连接起来
    non_final_mask = torch.tensor(tuple(map(lambda s: s is not None,
                                          batch.next_state)), device=device, dtype=torch.bool)
    non_final_next_states = torch.cat([s for s in batch.next_state
                                                if s is not None])
    state_batch = torch.cat(batch.state)
    action_batch = torch.cat(batch.action)
    reward_batch = torch.cat(batch.reward)

    # 计算Q(s_t, a),模型计算Q(s_t),然后选择采取的动作列
    state_action_values = policy_net(state_batch).gather(1, action_batch)

    # 计算所有下一个状态的V(s_{t+1})
    # 非终止状态的动作期望值基于“旧的”目标网络计算,选择最大奖励
    next_state_values = torch.zeros(BATCH_SIZE, device=device)
    with torch.no_grad():
        next_state_values[non_final_mask] = target_net(non_final_next_states).max(1).values
    # 计算预期的Q值
    expected_state_action_values = (next_state_values * GAMMA) + reward_batch

    # 计算Huber损失
    criterion = nn.SmoothL1Loss()
    loss = criterion(state_action_values, expected_state_action_values.unsqueeze(1))

    # 优化模型
    optimizer.zero_grad()
    loss.backward()
    # 梯度裁剪
    torch.nn.utils.clip_grad_value_(policy_net.parameters(), 100)
    optimizer.step()

# 设置训练回合数
if torch.cuda.is_available() or torch.backends.mps.is_available():
    num_episodes = 600
else:
    num_episodes = 50

# 开始训练回合
for i_episode in range(num_episodes):
    # 初始化环境并获取状态
    state, info = env.reset()
    state = torch.tensor(state, dtype=torch.float32, device=device).unsqueeze(0)
    for t in count():
        action = select_action(state)
        observation, reward, terminated, truncated, _ = env.step(action.item())
        reward = torch.tensor([reward], device=device)
        done = terminated or truncated

        if terminated:
            next_state = None
        else:
            next_state = torch.tensor(observation, dtype=torch.float32, device=device).unsqueeze(0)

        # 将转换存储到回放存储中
        memory.push(state, action, next_state, reward)

        # 转移到下一个状态
        state = next_state

        # 执行一次优化步骤(在策略网络上)
        optimize_model()

        # 软更新目标网络的权重
        # θ′ ← τ θ + (1 −τ )θ′
        target_net_state_dict = target_net.state_dict()
        policy_net_state_dict = policy_net.state_dict()
        for key in policy_net_state_dict:
            target_net_state_dict[key] = policy_net_state_dict[key]*TAU + target_net_state_dict[key]*(1-TAU)
        target_net.load_state_dict(target_net_state_dict)

        if done:
            episode_durations.append(t + 1)
            plot_durations()
            break

print('Complete')
plot_durations(show_result=True)
plt.ioff()
plt.show()

2.2.3 Policy Gradient Methods

策略梯度方法(Policy Gradient Methods)提供了一种不同于基于价值的方法(如Q学习)的替代方案。它们不估计Q值,而是通过梯度上升直接优化代理的策略。

        同样的,还是以倒立摆为例,来对比说明策略梯度方法的原理,和Q学习的差异在哪里。

        在倒立摆问题中,任务是通过推动小车向左或向右来平衡一根竖直的杆。每个状态下的观察值是一个长度为4的向量(描述了杆的物理属性、小车的位置、小车的速度、杆的角度和杆的旋转速度)。每一步可能的动作集合是二进制的——向左或向右移动小车。这个问题的奖励函数也非常简单:每保持杆直立的一步就获得+1的奖励。一个回合在获得200的奖励时结束(如果我们能保持杆平衡这么久,问题就被定义为已解决),或者当杆倾斜到失去平衡时结束。

         既然我们知道了观察和动作空间的维度,可以设计一个策略,该策略将输入观察值并产生动作的概率。一般来说,参数化的策略\pi_{\theta}是一个神经网络,其中 \theta 表示网络的可学习权重。在我们的例子中,为了简单期间,不使用复杂的神经网络策略。而是使用简单的逻辑回归来参数化左右移动的概率。此外,将使用这种简单的策略形式手动推导策略梯度更新规则的梯度。

        设 x表示长度为4的观察向量。由于倒立摆问题是完全可观察的,观察和状态是可以互换的概念。设 \pi_{\theta}(0|x) = \frac{1}{1 + e^{-\theta \cdot x}} = \frac{e^{\theta \cdot x}}{1 + e^{\theta \cdot x}}​ 是动作0(向左移动小车)的概率,那么 \pi_{\theta}(1|x) = 1 - \pi_{\theta}(0|x) = \frac{1}{1 + e^{\theta \cdot x}}。因此,策略由一个长度为四的向量 \theta 参数化。

        为了应用策略梯度更新,需要推导 \nabla_{\theta} \log \pi_{\theta}(a|x):

\nabla_{\theta} \log \pi_{\theta}(0|x) = \nabla_{\theta} (\theta \cdot x - \log(1 + e^{\theta \cdot x})) = x - \frac{x e^{\theta \cdot x}}{1 + e^{\theta \cdot x}} = x - x \pi_{\theta}(0|x)

        以及

\nabla_{\theta} \log \pi_{\theta}(1|x) = \nabla_{\theta} (-\log(1 + e^{\theta \cdot x})) = -\frac{x e^{\theta \cdot x}}{1 + e^{\theta \cdot x}} = -x \pi_{\theta}(0|x)

        有了上述推导,接下来我们就可以开始写代码【4】了:

import numpy as np
import gym

# 用于保存和加载已训练策略的额外导入
from gym.wrappers.monitor import Monitor, load_results


class LogisticPolicy:

    def __init__(self, θ, α, γ):
        # 初始化参数 θ,学习率 α 和折扣因子 γ

        self.θ = θ
        self.α = α
        self.γ = γ

    def logistic(self, y):
        # 定义逻辑函数

        return 1/(1 + np.exp(-y))

    def probs(self, x):
        # 返回两个动作的概率

        y = x @ self.θ
        prob0 = self.logistic(y)

        return np.array([prob0, 1-prob0])

    def act(self, x):
        # 根据概率比例选择一个动作

        probs = self.probs(x)
        action = np.random.choice([0, 1], p=probs)

        return action, probs[action]

    def grad_log_p(self, x):
        # 计算对数概率的梯度

        y = x @ self.θ
        grad_log_p0 = x - x*self.logistic(y)
        grad_log_p1 = - x*self.logistic(y)

        return grad_log_p0, grad_log_p1

    def grad_log_p_dot_rewards(self, grad_log_p, actions, discounted_rewards):
        # 将每个回合中各个动作的梯度与未来的奖励点积

        return grad_log_p.T @ discounted_rewards

    def discount_rewards(self, rewards):
        # 计算时间调整后的折扣奖励

        discounted_rewards = np.zeros(len(rewards))
        cumulative_rewards = 0
        for i in reversed(range(0, len(rewards))):
            cumulative_rewards = cumulative_rewards * self.γ + rewards[i]
            discounted_rewards[i] = cumulative_rewards

        return discounted_rewards

    def update(self, rewards, obs, actions):
        # 计算所有观察中的每个动作的梯度
        grad_log_p = np.array([self.grad_log_p(ob)[action] for ob, action in zip(obs, actions)])

        assert grad_log_p.shape == (len(obs), 4)

        # 计算时间调整后的折扣奖励
        discounted_rewards = self.discount_rewards(rewards)

        # 梯度乘以奖励
        dot = self.grad_log_p_dot_rewards(grad_log_p, actions, discounted_rewards)

        # 参数的梯度上升
        self.θ += self.α*dot


def run_episode(env, policy, render=False):

    observation = env.reset()
    totalreward = 0

    observations = []
    actions = []
    rewards = []
    probs = []

    done = False

    while not done:
        if render:
            env.render()

        observations.append(observation)

        action, prob = policy.act(observation)
        observation, reward, done, info = env.step(action)

        totalreward += reward
        rewards.append(reward)
        actions.append(action)
        probs.append(prob)

    return totalreward, np.array(rewards), np.array(observations), np.array(actions), np.array(probs)


def train(θ, α, γ, Policy, MAX_EPISODES=1000, seed=None, evaluate=False):

    # 初始化环境和策略
    env = gym.make('CartPole-v0')
    if seed is not None:
        env.seed(seed)
    episode_rewards = []
    policy = Policy(θ, α, γ)

    # 训练直到达到最大回合数
    for i in range(MAX_EPISODES):

        # 运行一个回合
        total_reward, rewards, observations, actions, probs = run_episode(env, policy)

        # 记录回合奖励
        episode_rewards.append(total_reward)

        # 更新策略
        policy.update(rewards, observations, actions)
        print("回合: " + str(i) + " 得分: " + str(total_reward) + " ", end="\r", flush=False)

    # 训练结束后进行评估 - 使用最后训练的策略评估100个回合
    if evaluate:
        env = Monitor(env, 'pg_cartpole/', video_callable=False, force=True)
        for _ in range(100):
            run_episode(env, policy, render=False)
        env.env.close()

    return episode_rewards, policy


# 为了可重复性
GLOBAL_SEED = 0
np.random.seed(GLOBAL_SEED)

episode_rewards, policy = train(θ=np.random.rand(4),
                                α=0.002,
                                γ=0.99,
                                Policy=LogisticPolicy,
                                MAX_EPISODES=2000,
                                seed=GLOBAL_SEED,
                                evaluate=True)


import matplotlib.pyplot as plt
plt.plot(episode_rewards)

# plt.imshow(episode_rewards, cmap='gray')
plt.savefig(f'generated_image_rl1.png')

        从结果来看,倒立摆的持续时间也是震荡上升,通过policy gradient也能学出来。

2.3 强化学习的特点

马尔可夫决策过程(MDPs) 马尔可夫决策过程(MDPs)是建模强化学习(RL)问题的数学基础。捕捉环境的动态,包括状态、动作、转移概率和奖励。MDPs遵循马尔可夫性质,即未来的状态仅由当前状态和动作决定。

强化学习与其他机器学习范式的对比 强化学习(RL)区别于监督学习和无监督学习,专注于动态环境中的序列决策。监督学习从标注数据中学习,无监督学习在无标注数据中发现模式,而RL则从与环境的交互中获得的反馈中学习。其通过试错学习最优策略的能力,适用于无法提供明确指导的场景。

3. 大模型智能体

        上述章节对强化学习的概念、特点、常用算法都做了介绍和回顾,相信读者对强化学习有了一定的理解,接下来我们将基于这样的知识背景,来介绍大模型智能体技术【5, 6】。不过话说在前面,大模型智能体技术还处于发展过程中,这里仅仅是谈及一些现有的理解。

3.1 什么是大模型智能体

        大模型智能体是一个超越简单文本生成的AI系统。它使用大语言模型(LLM)作为其核心计算引擎,使其能够进行对话、执行任务、推理,并展现一定的自主性。其中,利用提示语工程,编码智能体的身份、指令、授权和上下文,这些提示指导大模型智能体并设置其回复和行为的风格。        

3.2 大模型智能体的关键组成部分

        【7】给出了大模型智能体的几种架构。

  1. 核心LLM LLM智能体的基础是其构建的大语言模型。这个神经网络通过使用大量数据集进行训练,能够生成和理解简单的文本。LLM的规模和设计决定了智能体的初始能力和限制。

  2. 提示 激活和引导LLM功能的提示也是至关重要的。通过精心设计的提示,赋予智能体身份、专业知识、行为和目标。提示提供了预定义的模板,这些模板结合了重要的指令、上下文和参数,以引出所需的代理响应。对话智能体需要在提示中编码角色以采用不同的语气。提示通过明确目标、提供相关信息和框定指令来帮助面向任务的智能体。

  3. 记忆 智能体的记忆非常重要,它提供了时间框架,并存储了与特定用户或任务相关的详细信息。智能体使用两种主要类型的记忆来提高其性能:

    (1)短期记忆能力,它是大模型的核心,确保不会忘记正在进行的对话或最近的行动。该智能体的短期记忆充当了一个变化的上下文窗口,帮助它理解当前的对话。

    (2)长期记忆,将外部数据库与LLM结合起来,增强LLM记住大量数据的能力。这种增强使智能体的记忆扩展到包括信息、对话和其他内容的更长时间内的信息。通过加入这种长期记忆形式,智能体可以利用积累的知识和见解。

    综合起来,这些信息为智能体提供了对过去的理解以及对当前用户的上下文知识。提高智能体在执行复杂程序时的可靠性和技能。智能体的记忆使得对话更加个性化,能够建立更深层次的联系。

  4. 知识 在LLM智能体中,记忆和知识这两个不同的方面在塑造其能力方面起着相当重要的作用。记忆涉及在时间框架内保留用户互动和特定任务相关的详细信息,而知识则代表可以跨不同用户和任务应用的广泛专业知识。知识丰富并扩展了模型固有参数中包含的基础。知识又细分为专业知识、常识知识、程序性知识等。特别是程序性知识,为智能体提供了完成特定任务所需的实际技能和方法。无论是理解复杂的工作流程、运用分析技术还是参与创造性过程,程序性知识都使智能体能够提供实用的指导和解决方案。

  5. 工具集成 工具集成使代理能够通过使用其他服务和API来执行任务,而不仅仅依赖语言生成。例如,智能体可以使用像代码解释器这样的代码执行工具来执行提示中提到的软程序。

        综上所述,大模型智能体主要涵盖5大模块。当然真正实施,还会涉及到策略或者planning。

3.3 智能体类型

        由于面向任务的差异性,当前逐步衍生出两类比较主流的大模型智能体架构。有一些开源的框架陆续推出,可以关注一下。

  1. Web智能体(Web LLM Agent) Web智能体目标是进行有效的网页导航,处理信息收集和通信。WebAgent将任务指令分解为子任务,以直接为网页导航生成可执行的Python程序。

  2. 工具智能体(Tool LLM Agent) 工具智能体可能会更广泛,利用外部工具来增强其问题解决能力。可有效与各种工具,特别是API进行交互,以执行复杂任务。

案例:

        AutoGPT【8】的核心概念是无需用户干预即可自动执行常规任务。在收到用户的目标提示后,AutoGPT将执行必要的步骤来实现该目标。AutoGPT框架基于智能体技术,使用在线访问来在没有人工干预的情况下完成任务。

        CensusGPT【9】解决了人口普查数据难以获取的问题。只需提出一个问题,CensusGPT 就会以表格数据和可视化的形式给出答案。CensusGPT 构建在开源项目 TextSQL 之上,该项目通过使用大模型将查询转换为 SQL,使用户能够用自然语言与任何数据集交互。

3.4 如何实现大模型智能体

3.4.1 关键步骤

  1. 数据收集
    收集大量且多样化的文本语料库,这些数据与任务领域相关。该语料库将用于训练语言模型,可以用于后续的模型训练或者微调,一般来说,用领域相关知识调整后的模型效果肯定会比通用大模型更契合业务需求。

  2. 数据预处理
    对收集到的文本数据进行清理和预处理,去除噪音、不一致的格式和多余的信息。将文本进行标记化处理,以将其分解为更易于模型训练的小块。数据处理和高质量保证是重中之重,关于怎么处理和需要处理哪些内容,可以参考文章《关于LLaMA 3.1 405B以及小模型的崛起》中提到的LLaMA3的训练过程。

  3. 训练语言模型
    利用预处理后的数据集训练语言模型。这里提到的训练更多的是全参调整,不过如果你用的是超大规模参数的话,代价相对较大。在训练过程中,将文本序列输入语言模型,并优化其参数,以学习数据中的统计关系和模式。

  4. 微调
    为了提高性能并使预训练的语言模型适应您的特定用例,可以针对更具体的任务或领域进行微调。模型需要在特定任务的数据集上进行训练,同时保留其之前的知识。有的时候你微调之后,可能会导致大模型通用能力下降明显,所以这块需要特别注意,看看是否满足你的需求,根据实际要求进行调整训练策略。

  5. 评估与迭代
    使用适当的指标(如困惑度或准确性)评估LLM代理的性能,并对模型进行必要的修订。通过反复迭代训练和微调过程,逐步提高智能体的能力。

  6. 部署与集成
    大模型智能体的性能达到满意水平,将其部署到生产环境中,或将其集成到您想要的平台或应用程序中。提供与代理通信所需的API或接口。

  7. 持续迭代
    定期更新和重新训练大模型智能体,以确保其拥有最新的知识。通过这样做,智能体可以保持长期的相关性和实用性。其实在之前的业务中,都会涉及到利用用户的反馈数据来调优模型,或者补充更多高质量的数据,保持模型的持续进化。

3.4.2 通用的生成式Agent架构【10】

        生成式智能体架构,智能体能够感知其环境,所有感知到的内容都会保存在一个全面的记录中,称为记忆流。基于这些感知,架构会检索相关的记忆,并利用这些检索到的记忆来确定下一步行动。这些检索到的记忆还会用于制定长期计划和生成更高层次的反思,所有这些内容都会被输入到记忆流中,以便未来使用。

3.4.3 大模型智能体架构

        基于通用的生成式智能体架构,落到大模型智能体场景中,可能的形式如下【11, 12】:

关于其中提到的Planning【11】,也就是策略,展开说一下:

        一个复杂的任务通常涉及许多步骤。智能体需要了解这些步骤并提前进行规划。首先是任务拆解,通常也就是我们提到的基于链式思维进行分解复杂任务。该技术指导模型“逐步思考”,以利用更多的测试计算资源将困难任务分解为更小、更简单的步骤。将大任务转化为多个可管理的小任务。另外,后续提出的思维树则扩展了链式思维,通过在每一步探索多个推理可能性。它首先将问题分解为多个思维步骤,并在每个步骤生成多个思维,形成树状结构。搜索过程可以是广度优先搜索(BFS)或深度优先搜索(DFS),每个状态由分类器(通过提示)或多数投票进行评估。

        自我反思是允许智能体通过完善过去的行动决策和纠正先前错误来迭代改进的关键方面。在现实世界的任务中,试错是不可避免的,自我反思在此过程中发挥了重要作用,其实也就是强化学习中的试错,通过收益来判断之前的状态-动作是否合适。大模型中,ReAct【13】通过将行动空间扩展为任务特定的离散动作和语言空间的组合,整合了大模型的推理和行动。

        ReAct的提示模板包含了LLM思考的明确步骤,大致格式如下:

  • 思考:...
  • 行动:...
  • 观察:... (重复多次)

        Reflexion【14】是一个为代理配备动态记忆和自我反思能力的框架,以提高推理能力。Reflexion采用了标准的强化学习设置,其中奖励模型提供了简单的二元奖励,行动空间遵循ReAct中的设置,任务特定的行动空间通过语言增强以实现复杂的推理步骤。在每次行动后,代理计算一个启发式 ,并可以根据自我反思的结果选择是否重置环境以开始新的试验。

        这里给出一些启发式思考,关于大模型智能体的规划模块,是一个非常关键且持续演进的领域,需要保持思考、探索、实践。

4. 参考材料

【1】基于大模型的智能体

【2】Q-Learning

【3】Reinforcement Learning (DQN) Tutorial

【4】Reinforcement learning with policy gradients in pure Python

【5】LLM reinforcement learning: What is Essential in 2024

【6】What is LLM Agent? Ultimate Guide to LLM Agent

【7】BOLAA: BENCHMARKING AND ORCHESTRATING LLM-AUGMENTED AUTONOMOUS AGENTS【8】auto-gpt

【9】census-gpt

【10】Generative Agents: Interactive Simulacra of Human Behavior

【11】LLM Powered Autonomous Agents

【12】The Future of Data Labeling: Embracing Agents

【13】ReAct: Synergizing Reasoning and Acting in Language Models

【14】Reflexion: Language Agents with Verbal Reinforcement Learning

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值