(8-6)基于神经网络的推荐模型:基于强化学习的推荐模型

8.6  基于强化学习的推荐模型

基于强化学习的推荐模型是一种利用强化学习算法来优化推荐系统性能的方法,它的目标是通过与用户的交互和反馈来学习推荐策略,以提供个性化的推荐结果。

8.6.1  基于强化学习推荐模型的构成

在基于强化学习的推荐模型中有三个主要的组成部分,分别是环境、代理和训练过程,这三个部分的具体说明如下。

  1. 环境(Environment):推荐环境是模型与用户进行交互的场景,它包括用户、商品和推荐系统的状态信息。环境定义了用户的行为空间和反馈机制。例如,在电子商务推荐系统中,环境可以表示为一组用户和商品的历史交互数据,以及用户对推荐结果的反馈。
  2. 代理(Agent):代理是强化学习模型,它通过与环境交互来学习推荐策略。代理基于当前的环境状态选择一个动作,该动作用于推荐系统以生成推荐结果。代理的目标是通过与环境的交互,使得长期累积的回报最大化。
  3. 训练过程:训练过程是使用强化学习算法来优化代理的推荐策略。训练过程包括多个回合(episodes),每个回合中代理与环境交互并根据获得的奖励更新策略参数。常见的强化学习算法包括Q-learning、Policy Gradient等。

在Python程序中,实现基于强化学习的推荐模型通常涉及使用深度学习框架(如TensorFlow、PyTorch)来构建代理网络和训练过程。代理网络可以是多层神经网络,接收环境状态作为输入,输出推荐动作。在训练过程中,根据奖励信号和优化目标,使用梯度下降等方法更新代理网络的参数。

总体来说,基于强化学习的推荐模型通过与用户的交互和反馈,通过学习最优的推荐策略来提供个性化的推荐结果。它可以适应不同的环境和用户行为,并具有良好的扩展性和泛化能力。

8.6.2  Q-learning算法

Q-learning是一种经典的强化学习算法,用于解决基于马尔可夫决策过程(MDP)的强化学习问题,它通过学习一个状态动作值函数(Q函数)来确定最佳策略。Q-learning的目标是学习一个最优的Q函数,使得在每个状态下选择能够最大化累积奖励的动作。Q函数表示在给定状态下,采取某个动作所能获得的预期累积奖励。

Q-learning算法的步骤如下:

1初始化Q函数:对于每个状态-动作对,初始化Q值为任意值(通常为0)。

(2)选择动作:根据当前状态,根据一定的策略(如ε-greedy策略)选择一个动作。

(3)执行动作并观察环境:执行选定的动作,并观察下一个状态和即时奖励。

(4)更新Q值:使用Bellman方程更新Q值,即通过当前奖励和未来状态的最大Q值来估计当前状态-动作对的Q值。

Q(s, a) = Q(s, a) + α * (r + γ * max(Q(s', a')) - Q(s, a))

其中,Q(s, a)是当前状态-动作对的Q值,α是学习率(用于控制更新幅度),r是即时奖励,γ是折扣因子(用于衡量未来奖励的重要性),s'是下一个状态,a'是下一个状态的动作。

(5)更新状态:将当前状态更新为下一个状态。

(6)重复步骤2-5,直到达到停止条件(如达到最大迭代次数或学习收敛)。

通过不断迭代更新Q值,最终可以得到一个最优的Q函数,从而获得最佳的策略。在训练过程中,可以使用ε-greedy策略来平衡探索和利用,以便更好地探索状态空间并逐渐收敛到最优策略。

例如下面是一个实现Q-learning算法的例子,功能是实现了一个简单的Q-learning算法来学习在一个具有3个状态和2个动作的环境中的最优策略

源码路径:daima\8\ce.py

import numpy as np

# 定义环境
# 状态空间的大小为3,动作空间的大小为2
num_states = 3
num_actions = 2

# 定义Q表格
Q = np.zeros((num_states, num_actions))

# 定义参数
gamma = 0.9  # 折扣因子
alpha = 0.1  # 学习率
num_episodes = 1000  # 迭代次数

# Q-learning算法
for episode in range(num_episodes):
    state = 0  # 初始状态
    done = False  # 是否达到终止状态

    while not done:
        # 选择动作
        action = np.argmax(Q[state])

        # 执行动作并观察下一个状态和奖励
        if action == 0:
            next_state = state + 1
            reward = 0
        else:
            next_state = state - 1
            reward = 1

        # 更新Q值
        Q[state, action] = Q[state, action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state, action])

        # 更新当前状态
        state = next_state

        # 判断是否达到终止状态
        if state == num_states - 1:
            done = True

# 打印学到的Q值
print("Learned Q-values:")
print(Q)

在上述代码中,首先定义了环境的状态空间大小为3,动作空间大小为2。接下来,创建了一个Q表格,用于存储状态动作对的Q值,并将所有Q值初始化为0。然后定义了一些算法参数,包括折扣因子、学习率和迭代次数。接下来是Q-learning算法的主要部分,通过循环迭代指定次数来更新Q值。在内部循环中,根据当前状态,选择具有最大Q值的动作作为当前的动作。然后执行选择的动作,并观察下一个状态和奖励。并根据Q-learning的更新规则,更新Q值。然后将当前状态更新为下一个状态,并判断是否达到终止状态,如果是则结束内部循环。最后完成所有的迭代后,并打印学习到的Q值。执行后会输出:

Learned Q-values:
[[0. 0.]
 [0. 0.]
 [0. 0.]]

8.6.3  深度Q网络算法介绍

深度Q网络算法(Deep Q-Network, DQN)是一种融合了深度学习和强化学习的方法,用于解决强化学习中的值函数近似问题。DQN是由DeepMind在2013年提出的,通过使用深度神经网络作为值函数的函数逼近器,能够处理高维、复杂的状态空间。

深度Q网络算法在解决许多强化学习问题中取得了显著的成功,包括Atari游戏和机器人控制等领域。它通过结合深度学习和强化学习的优势,使得智能体能够处理高维、复杂的状态空间,并学习到高质量的决策策略。深度Q网络算法的主要思想和步骤如下:

1定义深度Q网络:深度Q网络由一个深度神经网络构成,输入为状态,输出为每个动作的Q值。网络的参数用于近似值函数。

2构建经验回放缓冲区:为了提高样本利用率和训练稳定性,使用经验回放缓冲区来存储智能体与环境交互的经验元组(状态、动作、奖励、下一个状态)。

3初始化深度Q网络:随机初始化深度Q网络的参数。

4迭代训练过程:

  1. 选择动作:根据当前状态和深度Q网络预测的Q值,使用ε-greedy等策略选择动作。
  2. 执行动作并观察环境:将选定的动作应用于环境,并观察下一个状态和获得的奖励。
  3. 存储经验:将经验元组(当前状态、动作、奖励、下一个状态)存储到经验回放缓冲区中。
  4. 从经验回放缓冲区中随机采样一批经验元组。
  5. 计算目标Q值:对于采样的每个经验元组,使用深度Q网络预测的Q值计算目标Q值。
  6. 更新深度Q网络:使用均方误差(MSE)损失函数来更新深度Q网络的参数,使得预测的Q值接近目标Q值。
  7. 定期更新目标网络:为了增加算法的稳定性,定期(例如每隔一定步数)将当前深度Q网络的参数复制给目标网络。

(5)重复以上步骤,直到达到停止条件。

通过迭代训练,深度Q网络逐渐学习到状态和动作之间的Q值函数。该函数可以用于指导智能体在环境中做出最优决策。

需要注意的是,为了提高算法的稳定性,DQN还采用了一些技术,如目标网络、经验回放和渐进式更新等。

下面是一个深度Q网络算法实例:解决手推车问题(实现CartPole游戏)。手推车问题是一个简单的强化学习环境,其中智能体需要控制一个手推车来平衡一个竖立的杆子。智能体可以采取两个动作:向左推车或向右推车。目标是通过采取适当的动作使杆子保持平衡,同时防止手推车离开边界。如图8-3所示。

8-3  手推车问题

1. 项目介绍

本项目的实现文件是reinforcement_q_learning.py,首先定义了一个神经网络模型作为深度Q网络,用于估计每个动作的Q值。然后使用经验回放缓冲区来存储智能体与环境交互的经验,包括状态、动作、奖励和下一个状态。接下来,使用epsilon-greedy策略选择动作,并执行动作并观察环境的反馈。通过与环境的交互,智能体会收集经验并存储到经验回放缓冲区中。在训练过程中,从经验回放缓冲区中随机采样一批经验,然后计算目标Q值,使用均方误差损失函数更新深度Q网络的参数。通过反复进行这个迭代过程,深度Q网络逐渐学习到状态和动作之间的Q值函数。最终,训练好的深度Q网络可以用于在每个状态下选择最优的动作,从而使手推车保持平衡并获得高分。

本项目展示了如何使用PyTorch构建和训练深度Q网络,以解决强化学习问题。提供了一个简单但具有挑战性的环境,可以帮助理解和实践深度强化学习算法。

2. 具体实现

实例文件reinforcement_q_learning.py的具体实现流程如下所示。

源码路径:daima\11\reinforcement_q_learning.py

1)创建OpenAI Gym环境

创建一个名为"CartPole-v1"的OpenAI Gym环境,OpenAI Gym是一个开源的强化学习库,提供了一系列用于开发和比较强化学习算法的标准化环境。对应的实现代码如下所示:

env = gym.make("CartPole-v1")

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

plt.ion()

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

2回放记忆

将使用经验回放记忆来训练我们的 DQN,它存储智能体观察到的转换,使我们以后可以重用此数据。 通过从中随机采样,可以构建批量的转换相关,这样可以极大地稳定和改善DQN 训练程序。对应的实现代码如下所示:

Transition = namedtuple('Transition',
                        ('state', 'action', 'next_state', 'reward'))

class ReplayMemory(object):

    def __init__(self, capacity):
        self.memory = deque([], maxlen=capacity)

    def push(self, *args):
        """Save a transition"""
        self.memory.append(Transition(*args))

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)
  1. 首先,使用gym.make("CartPole-v1")创建了一个名为env的环境对象。该环境是CartPole问题的一个变体,其中一个杆子固定在一个可以左右移动的小车上。目标是通过左右移动小车,使得杆子保持平衡。
  2. 接下来,设置了Matplotlib的一些参数。首先检查当前是否在IPython环境中运行,如果是,则从IPython模块导入display。这是为了在IPython中实现动态图形显示。然后,代码调用plt.ion()来启用交互式绘图模式。
  3. 最后,使用torch.cuda.is_available()检查是否有可用的GPU。如果有可用的GPU,device将被设置为"cuda",否则将使用CPU。这是为了在训练模型时指定使用GPU还是CPU进行计算。

(3)DQN算法

定义一个名为DQN的神经网络模型类,用于实现深度 Q 网络(Deep Q-Network)。该模型的功能是接收观测作为输入,并输出对应于每个可能动作的 Q 值估计。在强化学习中,该模型用于学习和预测在给定观测下采取每个动作的价值。对应的实现代码如下所示:

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)

    def forward(self, x):
        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        return self.layer3(x)

4超参数和工具

定义一些超参数和辅助函数,以及初始化了模型、优化器、重放缓存和一些计数器。主要定义了如下所示的超参数:

  1. BATCH_SIZE:批量大小,用于训练模型时每次从重放缓存中采样的样本数量。
  2. GAMMA:折扣因子,用于计算Q值的累积奖励折扣。
  3. EPS_START和EPS_END:ε贪婪策略的起始和结束值。ε贪婪策略用于在探索和利用之间进行平衡,即以一定概率随机选择动作进行探索,以一定概率选择模型认为最优的动作进行利用。
  4. EPS_DECAY:ε贪婪策略的衰减速率,用于控制ε值随时间的衰减。
  5. TAU:目标网络(target network)更新时的软更新率。
  6. LR:优化器的学习率。

对应的实现代码如下所示:

BATCH_SIZE = 128
GAMMA = 0.99
EPS_START = 0.9
EPS_END = 0.05
EPS_DECAY = 1000
TAU = 0.005
LR = 1e-4

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)

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)
    steps_done += 1
    if sample > eps_threshold:
        with torch.no_grad():
            # t.max(1) will return the largest column value of each row.
            # second column on max result is index of where max element was
            # found, so we pick action with the larger expected reward.
            return policy_net(state).max(1)[1].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())
    # Take 100 episode averages and plot them too
    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)  # pause a bit so that plots are updated
    if is_ipython:
        if not show_result:
            display.display(plt.gcf())
            display.clear_output(wait=True)
        else:
            display.display(plt.gcf())

对上述代码的具体说明如下:

  1. 首先使用env.action_space.n获取了环境中可用的动作数量,并使用len(state)获取了状态观测的维度。这些信息用于初始化深度Q网络模型的输入和输出维度。
  2. 然后,创建了两个神经网络模型:policy_net和target_net,它们都基于DQN类,并将它们移动到适当的设备上(GPU或CPU)。target_net加载了policy_net的初始状态字典,以保持两个网络的初始参数相同。
  3. 接着,使用optim.AdamW定义了一个AdamW优化器,用于优化policy_net模型的参数。它将policy_net的参数和学习率传递给优化器的构造函数。然后创建了一个ReplayMemory对象,作为重放缓存,用于存储训练样本。
  4. 然后,定义了一个名为steps_done的全局计数器,用于跟踪选择动作的次数。
  5. 接下来,定义了一个名为select_action的函数,用于根据当前状态选择动作。该函数接受当前状态作为输入,并根据ε贪婪策略选择动作。如果随机数大于ε阈值,则选择模型预测的具有最大预期奖励的动作,否则随机选择一个动作。
  6. 然后,定义了一个名为episode_durations的空列表,用于存储每个回合的持续时间(游戏运行的帧数)。
  7. 最后,定义了一个名为plot_durations的辅助函数,用于绘制游戏回合持续时间的图表。

(5)优化深度 Q 网络(DQN)模型

定义函数optimize_model(),用于优化深度 Q 网络(DQN)模型。首先对一批进行采样,将所有张量连接为一个张量,计算Q(s[t], a[t])和V(s[t+1])= max[a] Q(s[t+1], a),并将其合并为我们的损失。 根据定义,如果s为终端状态,则设置V(s) = 0。 还使用目标网络来计算V(s[t+1]),以提高稳定性。 目标网络的权重大部分时间保持冻结状态,但经常更新以策略网络的权重。对应的实现代码如下所示:

def optimize_model():
    if len(memory) < BATCH_SIZE:
        return
    transitions = memory.sample(BATCH_SIZE)
    batch = Transition(*zip(*transitions))
    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)
    state_action_values = policy_net(state_batch).gather(1, action_batch)
    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)[0]
    expected_state_action_values = (next_state_values * GAMMA) + reward_batch
    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()

(6)执行深度 Q 网络(DQN)的训练循环

首先,重置环境并初始化state张量。 然后,采样一个动作并执行,观察下一个屏幕和奖励(总是 1),并一次优化我们的模型。 当剧集结束(我们的模型失败)时,我们重新开始循环。对应的实现代码如下所示:

if torch.cuda.is_available():
    num_episodes = 600
else:
    num_episodes = 50

for i_episode in range(num_episodes):
    # Initialize the environment and get it's state
    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)

        # Move to the next state
        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()

对上述代码的具体说明如下:

  1. 首先,检查是否有可用的 CUDA 设备来确定要运行的回合数(num_episodes)。如果有可用的 CUDA 设备,则设置 num_episodes 为 600,否则设置为 50。
  2. 然后,使用 for 循环迭代 num_episodes 次,进行模型训练。在每个循环的开始,代码初始化环境并获取初始状态。初始状态被转换为张量(state)并移动到设备上,并使用 unsqueeze(0) 将其转换为形状为 (1, n_observations) 的批次。
  3. 接下来,使用一个内嵌的 for 循环,不断选择动作、执行动作、更新状态,并将转换信息存储在重放缓存中。在每个时间步中,调用函数select_action()选择动作,并执行该动作以观察新的状态、奖励和终止标志。相关的值被转换为张量,并将奖励封装为一个形状为 (1,) 的张量。
  4. 然后,根据终止标志判断是否存在下一个状态,如果终止则设置 next_state 为 None,否则将新的观测转换为张量并移动到设备上。
  5. 接下来,将当前状态、动作、下一个状态和奖励存储在重放缓存中,以便后续的模型优化。
  6. 然后,调用函数optimize_model()执行一步模型优化,更新策略网络的参数。
  7. 接着,执行目标网络的软更新操作。它首先获取目标网络和策略网络的参数字典,并根据软更新率 TAU 更新目标网络的参数。更新后的参数字典再次加载到目标网络中。在每个回合结束时,将回合持续时间(t + 1)添加到 episode_durations 列表中,并调用 plot_durations 函数绘制回合持续时间的图表。如果回合已经结束,则跳出内嵌的 for 循环。
  8. 最后,代码打印出"Complete",调用函数plot_durations()绘制最终结果的图表,并关闭交互式绘图模式。最后,调用函数 plt.show() 显示图表。

执行后会绘制训练过程中的主要图表(Training...)和训练完成后的结果图表(Result)如图8-4所示

8-4  执行效果

本《基于神经网络的推荐模型》专题已完结:

(8-1)基于神经网络的推荐模型:深度推荐模型介绍-CSDN博客

(8-2)基于神经网络的推荐模型:基于多层感知器(MLP)的推荐模型-CSDN博客

(8-3)基于神经网络的推荐模型:基于卷积神经网络(CNN)的推荐模型-CSDN博客

(8-4)基于神经网络的推荐模型:基于循环神经网络(RNN)的推荐模型-CSDN博客

(8-5)基于神经网络的推荐模型:基于自注意力机制的推荐模型-CSDN博客

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值