24/11/13 算法笔记<强化学习> DQN算法

DQN算法的主要特点包括:

  1. 神经网络代替Q表:在传统的Q学习中,需要维护一个Q表来存储每个状态-动作对的Q值。而在DQN中,使用神经网络来近似这些Q值,这使得算法能够处理具有大量状态和动作的问题。

  2. 经验回放(Experience Replay):DQN使用经验回放来存储智能体的交互经验(状态、动作、奖励、下一个状态),并从中随机抽取样本进行训练,这有助于打破数据之间的时间相关性,提高学习效率。

  3. 目标网络(Target Network):DQN维护两个神经网络,一个是用于预测Q值的评估网络(Evaluation Network),另一个是用于生成目标Q值的目标网络(Target Network)。目标网络的参数会定期更新,以保持稳定,这有助于减少训练过程中的不稳定性。

  4. ϵ-贪心策略(ε-greedy Strategy):DQN使用ϵ-贪心策略来平衡探索和利用。在这种策略下,智能体以一定的概率ϵ随机选择动作,以探索环境;以1-ϵ的概率选择当前最优动作,以利用已知信息。

下面我们来看一个简单的DQN代码:

import gym
import numpy as np
import random
import torch
import torch.nn as nn
import torch.optim as optim# 定义DQN网络结构
class DQN(nn.Module):def __init__(self, input_size, output_size):super(DQN, self).__init__()self.fc1 = nn.Linear(input_size, 24)self.fc2 = nn.Linear(24, 24)self.fc3 = nn.Linear(24, output_size)def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))x = self.fc3(x)return x# 经验回放
class ReplayMemory:def __init__(self, capacity):self.capacity = capacityself.memory = []self.position = 0def push(self, state, action, reward, next_state, done):if len(self.memory) < self.capacity:self.memory.append(None)self.memory[self.position] = (state, action, reward, next_state, done)self.position = (self.position + 1) % self.capacitydef sample(self, batch_size):return random.sample(self.memory, batch_size)def can_provide_sample(self, batch_size):return len(self.memory) >= batch_size# DQN Agent
class DQNAgent:def __init__(self, input_size, output_size, epsilon_start, epsilon_end, epsilon_decay, batch_size, gamma):self.gamma = gammaself.epsilon_start = epsilon_startself.epsilon_end = epsilon_endself.epsilon_decay = epsilon_decayself.batch_size = batch_sizeself.model = DQN(input_size, output_size)self.optimizer = optim.Adam(self.model.parameters())self.memory = ReplayMemory(10000)self.epsilon = epsilon_startdef select_action(self, state):if np.random.rand() <= self.epsilon:return random.randrange(env.action_space.n)state = torch.from_numpy(state).float().unsqueeze(0)action_values = self.model(state)return np.argmax(action_values.cpu().data.numpy())def optimize_model(self):if self.memory.can_provide_sample(self.batch_size):transitions = self.memory.sample(self.batch_size)batch = self._create_batch(transitions)self._train_step(batch)def _create_batch(self, transitions):batch = {'states': torch.cat([s for s, a, r, ss, d in transitions]),'actions': torch.cat([a for s, a, r, ss, d in transitions]),'reward': torch.cat([r for s, a, r, ss, d in transitions]),'next_states': torch.cat([ss for s, a, r, ss, d in transitions]),'done': torch.cat([d for s, a, r, ss, d in transitions])}return batchdef _train_step(self, batch):self.optimizer.zero_grad()states = batch['states']actions = batch['actions']rewards = batch['reward']next_states = batch['next_states']done = batch['done']Q_values = self.model(states)next_Q_values = self.model(next_states)max_next_Q_values = next_Q_values.max(1)[0]expected_Q_values = rewards + (1 - done) * self.gamma * max_next_Q_valuesloss = (Q_values[range(self.batch_size), actions] - expected_Q_values).pow(2).mean()loss.backward()self.optimizer.step()# 设置环境和参数
env = gym.make('CartPole-v1')
input_size = env.observation_space.shape[0]
output_size = env.action_space.n
epsilon_start = 1.0
epsilon_end = 0.1
epsilon_decay = 0.995
batch_size = 64
gamma = 0.99agent = DQNAgent(input_size, output_size, epsilon_start, epsilon_end, epsilon_decay, batch_size, gamma)# 训练过程
num_episodes = 1000
for episode in range(num_episodes):state = env.reset()state = torch.from_numpy(state).float()for t in range(500):action = agent.select_action(state.numpy())next_state, reward, done, _ = env.step(action)next_state = torch.from_numpy(next_state).float()agent.memory.push(state.numpy(), action, reward, next_state.numpy(), done)state = next_stateif done:breakif len(agent.memory.memory) > agent.batch_size:agent.optimize_model()agent.epsilon = max(agent.epsilon_end, agent.epsilon * agent.epsilon_decay)

我们来分析一下每段代码

1.导入库

import gym
import numpy as np
import random
import torch
import torch.nn as nn
import torch.optim as optim

gym用于创建和管理环境,numpy用于数值计算,random用于生成随机数,torch是PyTorch库,用于构建和训练神经网络,torch.nn用于定义神经网络层,torch.optim用于优化网络参数。

2.定义DQN网络结构

class DQN(nn.Module):def __init__(self, input_size, output_size):super(DQN, self).__init__()self.fc1 = nn.Linear(input_size, 24)self.fc2 = nn.Linear(24, 24)self.fc3 = nn.Linear(24, output_size)def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))x = self.fc3(x)return x

这里定义了一个简单的三层全连接神经网络,作为DQN的Q网络。它接受环境状态作为输入,输出每个可能动作的Q值。

3.定义经验回放

class ReplayMemory:def __init__(self, capacity):self.capacity = capacityself.memory = [] #初始化为空列表,用于存储经验元组self.position = 0 #用于追踪下一个要写入经验的位置。def push(self, state, action, reward, next_state, done):if len(self.memory) < self.capacity: #如果当前存储的经验数量小于 capacity,则扩展 memory 列表。self.memory.append(None)self.memory[self.position] = (state, action, reward, next_state, done)self.position = (self.position + 1) % self.capacity   #capacity 是一个参数,它定义了回放缓冲区(replay buffer)的最大存储容量。def sample(self, batch_size):return random.sample(self.memory, batch_size)def can_provide_sample(self, batch_size):return len(self.memory) >= batch_size

体验回放是一种重要的技术,它允许智能体(agent)存储过去的经验,并在后续的训练过程中随机抽取这些经验来更新其策略。

4.定义DQN智能体

class DQNAgent:def __init__(self, input_size, output_size, epsilon_start, epsilon_end, epsilon_decay, batch_size, gamma):self.gamma = gamma                 #折扣因子,用于计算未来奖励的现值。self.epsilon_start = epsilon_start #用于epsilon贪心策略的参数,控制随机选择动作的概率从 epsilon_start 衰减到 epsilon_end。self.epsilon_end = epsilon_end   self.epsilon_decay = epsilon_decayself.batch_size = batch_size       #每次从回放缓冲区中抽取的经验批次大小。self.model = DQN(input_size, output_size)self.optimizer = optim.Adam(self.model.parameters())self.memory = ReplayMemory(10000)self.epsilon = epsilon_start       #当前epsilon值,用于epsilon贪心策略。#根据当前状态选择一个动作,它使用ϵ-贪心策略在探索和利用之间进行平衡。def select_action(self, state):if np.random.rand() <= self.epsilon:return random.randrange(env.action_space.n)state = torch.from_numpy(state).float().unsqueeze(0)action_values = self.model(state)     #模型对当前状态的估计价值,选择最大价值的动作。return np.argmax(action_values.cpu().data.numpy())#从经验回放中随机抽取一批样本,然后使用这些样本来更新神经网络的权重。def optimize_model(self):if self.memory.can_provide_sample(self.batch_size):transitions = self.memory.sample(self.batch_size)batch = self._create_batch(transitions)self._train_step(batch)#用于将抽取的样本整理成一个批次,并按类型分组,以便进行批量训练。def _create_batch(self, transitions):batch = {'states': torch.cat([s for s, a, r, ss, d in transitions]),'actions': torch.cat([a for s, a, r, ss, d in transitions]),'reward': torch.cat([r for s, a, r, ss, d in transitions]),'next_states': torch.cat([ss for s, a, r, ss, d in transitions]),'done': torch.cat([d for s, a, r, ss, d in transitions])}return batch#执行一次训练步骤,它计算损失并更新网络权重。def _train_step(self, batch):self.optimizer.zero_grad()states = batch['states']actions = batch['actions']rewards = batch['reward']next_states = batch['next_states']done = batch['done']Q_values = self.model(states)next_Q_values = self.model(next_states)max_next_Q_values = next_Q_values.max(1)[0] expected_Q_values = rewards + (1 - done) * self.gamma * max_next_Q_valuesloss = (Q_values[range(self.batch_size), actions] - expected_Q_values).pow(2).mean()               #计算损失loss.backward()self.optimizer.step()     #方法使用在 loss.backward() 中计算出的梯度来更新模型的参数。

5.设置环境和参数

env = gym.make('CartPole-v1')
input_size = env.observation_space.shape[0]
output_size = env.action_space.n
epsilon_start = 1.0
epsilon_end = 0.1
epsilon_decay = 0.995
batch_size = 64
gamma = 0.99

这部分代码设置了环境和DQN算法的参数。env是Gym环境,input_sizeoutput_size分别是状态和动作的数量,epsilon_startepsilon_endepsilon_decay是ϵ-贪心策略的参数,batch_size是每次训练的样本数量,gamma是折扣因子。

6.训练过程

agent = DQNAgent(input_size, output_size, epsilon_start, epsilon_end, epsilon_decay, batch_size, gamma)num_episodes = 1000
for episode in range(num_episodes):state = env.reset()state = torch.from_numpy(state).float()for t in range(500):action = agent.select_action(state.numpy())next_state, reward, done, _ = env.step(action)next_state = torch.from_numpy(next_state).float()agent.memory.push(state.numpy(), action, reward, next_state.numpy(), done)state = next_stateif done:breakif len(agent.memory.memory) > agent.batch_size:agent.optimize_model()agent.epsilon = max(agent.epsilon_end, agent.epsilon * agent.epsilon_decay)

这部分代码实现了DQN的训练过程。对于每一集(episode),智能体与环境交互,选择动作,执行动作,然后观察下一个状态和奖励。这些经验被存储在经验回放中。当经验回放中的样本数量足够时,智能体从经验回放中抽取样本并更新其神经网络。随着时间的推移,ϵ值逐渐减小,使得智能体更多地利用其学到的知识而不是随机探索。

我们来看一下智能交通灯比赛里面的dqn算法

def learn(self, list_sample_data):t_data = list_sample_dataobs = torch.tensor(np.array([frame.obs for frame in t_data]), dtype=torch.float32).to(self.device)action = (torch.LongTensor(np.array([frame.act if not np.any(np.isinf(frame.act)) else 0 for frame in t_data])).long().to(self.model.device))rew = torch.tensor(np.array([frame.rew for frame in t_data]), device=self.model.device)_obs = torch.tensor(np.array([frame._obs for frame in t_data]), dtype=torch.float32).to(self.device)not_done = torch.tensor(np.array([frame.done for frame in t_data]),dtype=torch.float32,device=self.device,)# Main implementation of the multi-head output DQN algorithm# 多头输出dqn算法的主要实现self.model.eval()with torch.no_grad():# Calculate the target Q-values for each head# 计算各个头的目标q值q_targets = []for head_idx in range(self.num_head):q_targets_head = (rew[:, head_idx].unsqueeze(1)+ self._gamma * (self.model(_obs)[0][head_idx]).max(1)[0].unsqueeze(1) * not_done[:, None])q_targets.append(q_targets_head)q_targets = torch.cat(q_targets, dim=1)# Calculate the Q-values for each head# 计算各个头的q值self.model.train()q_values = []for head_idx in range(self.num_head):q_values_head = self.model(obs)[0][head_idx].gather(1, action[:, head_idx + 1].unsqueeze(1))q_values.append(q_values_head)q_values = torch.cat(q_values, dim=1)self.optim.zero_grad()loss = F.mse_loss(q_targets.float(), q_values.float())loss.backward()model_grad_norm = torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0).item()self.optim.step()self.train_step += 1value_loss = loss.detach().item()target_q_value = q_targets.mean().detach().item()q_value = q_values.mean().detach().item()# Periodically report monitoring# 按照间隔上报监控now = time.time()if now - self.last_report_monitor_time >= 60:monitor_data = {"value_loss": value_loss,"target_q_value": target_q_value,"q_value": q_value,"model_grad_norm": model_grad_norm,}self.monitor.put_data({os.getpid(): monitor_data})self.logger.info(f"value_loss: {value_loss}, target_q_value: {target_q_value},\q_value: {q_value},\model_grad_norm: {model_grad_norm}")self.last_report_monitor_time = now

提问:为什么在计算Q值的时候我下一个动作还没做,就能计算它的最大Q值?

答:

在强化学习中,计算下一个状态 s′s′ 的Q值确实是在智能体实际采取行动之前进行的。这是通过使用智能体的当前策略来预测或估计Q值实现的。具体来说,智能体使用其学习到的模型(例如,在DQN中是深度神经网络)来预测或估计下一个状态 s′s′ 的Q值。这个过程不需要智能体实际进入下一个状态或在该状态下采取任何行动。

让我们分段解释每段代码

1.数据准备

t_data = list_sample_dataobs = torch.tensor(np.array([frame.obs for frame in t_data]), dtype=torch.float32).to(self.device)
action = (torch.LongTensor(np.array([frame.act if not np.any(np.isinf(frame.act)) else 0 for frame in t_data])).long().to(self.model.device)
)
rew = torch.tensor(np.array([frame.rew for frame in t_data]), device=self.model.device)
_obs = torch.tensor(np.array([frame._obs for frame in t_data]), dtype=torch.float32).to(self.device)
not_done = torch.tensor(np.array([frame.done for frame in t_data]),dtype=torch.float32,device=self.device,
)
  • t_data 是传入的样本数据列表。
  • obs 是观察数据,转换为PyTorch张量并移动到指定设备(如GPU)。
  • action 是行动数据,如果行动值中有无穷大,则替换为0,然后转换为长整型张量并移动到模型的设备。
  • rew 是奖励数据,直接转换为张量并移动到模型的设备。
  • _obs 是下一个观察数据,转换为张量并移动到指定设备。
  • not_done 表示该episode是否未结束,转换为张量并移动到指定设备。

2.目标 Q 值计算

self.model.eval()with torch.no_grad():q_targets = []for head_idx in range(self.num_head):q_targets_head = (rew[:, head_idx].unsqueeze(1)+ self._gamma * (self.model(_obs)[0][head_idx]).max(1)[0].unsqueeze(1) * not_done[:, None])q_targets.append(q_targets_head)q_targets = torch.cat(q_targets, dim=1)
  • 将模型设置为评估模式。
  • 使用torch.no_grad()来避免计算梯度,节省内存和计算资源。
  • 遍历每个头,计算目标Q值。目标Q值由即时奖励加上折扣因子乘以下一个状态的最大Q值(对于未结束的episode)组成。
  • 将所有头的目标Q值拼接在一起。

3.Q值计算

self.model.train()
q_values = []
for head_idx in range(self.num_head):q_values_head = self.model(obs)[0][head_idx].gather(1, action[:, head_idx + 1].unsqueeze(1))q_values.append(q_values_head)
q_values = torch.cat(q_values, dim=1)

4.损失计算和优化

self.optim.zero_grad()
loss = F.mse_loss(q_targets.float(), q_values.float())
loss.backward()
#这是PyTorch提供的一个函数,用于裁剪梯度。
model_grad_norm = torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0).item()
self.optim.step()
self.train_step += 1
  • 清零优化器的梯度。
  • 计算目标Q值和预测Q值之间的均方误差损失。
  • 进行反向传播。
  • 使用梯度裁剪来限制梯度的大小,防止梯度爆炸。
  • 更新模型参数。
  • 增加训练步数。

5.监控和报告

value_loss = loss.detach().item()
target_q_value = q_targets.mean().detach().item()
q_value = q_values.mean().detach().item()now = time.time()
if now - self.last_report_monitor_time >= 60:monitor_data = {"value_loss": value_loss,"target_q_value": target_q_value,"q_value": q_value,"model_grad_norm": model_grad_norm,}self.monitor.put_data({os.getpid(): monitor_data})self.logger.info(f"value_loss: {value_loss}, target_q_value: {target_q_value},\q_value: {q_value},\model_grad_norm: {model_grad_norm}")self.last_report_monitor_time = now
  • 计算并记录损失、目标Q值、Q值和模型梯度范数。
  • 如果达到报告间隔(60秒),则记录监控数据并更新最后报告时间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470573.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Blender进阶:图像纹理节点和映射节点

13 图像纹理节点 13.1 图像纹理节点 图像纹理节点&#xff0c;用于加载一张贴图 加载图片后&#xff0c;可以从图片上取得一个像素点。 输入&#xff1a;一个坐标矢量 输出&#xff1a;该坐标的像素颜色 演示&#xff1a;使用合并xyz节点来指定坐标。。 13.2 多种贴图 一…

MYSQL 库,表 基本操作

相关的两个编码集(简单了解即可) 1.数据库编码集 :对将要存储的数据进行编码 2.数据库校验集:对将要执行的操作&#xff08;增删查改&#xff09;数据是对数据编码的校验&#xff0c;本质也是一种读取数据库中数据库采用的一种编码格式。 总结&#xff1a;数据库无论对数据做…

万字长文分析函数式编程

目录 一.认识函数式编程 一、函数式编程的定义 二、函数式编程的思想 三、函数式编程的特点 四、函数式编程的应用 二.Lambda表达式 三.Stream流 3.1 创建流对象 3.2 注意事项 3.3 Stream流的中间操作 filter map distinct sorted limit skip flatMap 3.4 St…

DOM 规范 — MutationObserver 接口

前言 最近在重学 JavaScript 中&#xff0c;再一次接触到了 MutationObserver 内容&#xff0c;接着联想到了 Vue 源码中有使用过这个接口&#xff0c;因此觉得有必要对 MutationObserver 接口进行相关了解和学习。 下面是 vue 源码中关于 MutationObserver 接口使用的代码&a…

灰狼优化算法

一、简介 1.1 灰狼优化算法-Grey Wolf Optimizer 通过模拟灰狼群体捕食行为&#xff0c;基于狼群群体协 作的机制来达到优化的目的。&#xff27;&#xff37;&#xff2f;算法具有结构简单、需 要调节的参数少、容易实现等特点&#xff0c;其中存在能够自适应调整 的收敛因子…

AI 写作(五)核心技术之文本摘要:分类与应用(5/10)

一、文本摘要&#xff1a;AI 写作的关键技术 文本摘要在 AI 写作中扮演着至关重要的角色。在当今信息爆炸的时代&#xff0c;人们每天都被大量的文本信息所包围&#xff0c;如何快速有效地获取关键信息成为了一个迫切的需求。文本摘要技术正是为了解决这个问题而诞生的&#x…

【 ElementUI 组件Steps 步骤条使用新手详细教程】

本文介绍如何使用 ElementUI 组件库中的步骤条组件完成分步表单设计。 效果图&#xff1a; 基础用法​ 简单的步骤条。 设置 active 属性&#xff0c;接受一个 Number&#xff0c;表明步骤的 index&#xff0c;从 0 开始。 需要定宽的步骤条时&#xff0c;设置 space 属性即…

尽量通俗易懂地概述.Net U nity跨语言/跨平台相关知识

本文参考来自唐老狮,Unity3D高级编程:主程手记,ai等途径 仅作学习笔记交流分享 目录 1. .Net是什么? 2. .Net框架的核心要点? 跨语言和跨平台 .Net x Unity跨平台发展史 Net Framework 2002 Unity跨平台之 Mono 2004 Unity跨平台之 IL2CPP 2015 二者区别 .NET Core …

基于yolov8、yolov5的番茄成熟度检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;番茄成熟度检测在农业生产及质量控制中起着至关重要的作用&#xff0c;不仅能帮助农民及时采摘成熟的番茄&#xff0c;还为自动化农业监测提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的番茄成熟度检测模型&#xff0c;该模型使用了…

应用程序部署(IIS的相关使用,sql server的相关使用)

数据服务程序&#xff08;API&#xff09;部署 1、修改配置文件 打开部署包中的web.config配置文件&#xff0c;确认数据库登录名和密码正确 修改ip为电脑IP&#xff08;winR输入cmd&#xff0c;输入ipconfig&#xff0c;IPv4对应的就是本机IP&#xff09; 2、打开IIS&#x…

网页版五子棋——对战模块(服务器端开发②)

前一篇文章&#xff1a;网页版五子棋——对战模块&#xff08;服务器端开发①&#xff09;-CSDN博客 项目源代码&#xff1a;Java: 利用Java解题与实现部分功能及小项目的代码集合 - Gitee.com 目录 前言 一、创建并注册 GameAPI 类 1.创建 GameAPI 类 2.注册 GameAPI 类 …

STM32单片机WIFI语音识别智能衣柜除湿消毒照明

实践制作DIY- GC0196-WIFI语音识别智能衣柜 一、功能说明&#xff1a; 基于STM32单片机设计-WIFI语音识别智能衣柜 二、功能介绍&#xff1a; STM32F103C系列最小系统板LCD1602显示器ULN2003控制的步进电机&#xff08;柜门开关&#xff09;5V加热片直流风扇紫外消毒灯DHT11…

网络远程操控

1.给两个设备配上ip地址让他们能通 2.开启远程管理功能&#xff0c;打开telnet 3.创建远程管理的账号和密码&#xff0c;账号权限 输入system-view进入视图&#xff0c;不敲这个命令不能进行配置 配好ip后进入AR1ping一下AR2的ip看看通不通&#xff0c;接着进入AR2开启telnet权…

【go从零单排】Timer、Epoch 时间函数

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;time.Timer 是一个用于在指定时间后执行操作的计时器。…

鸿蒙自定义UI组件导出使用

上期讲解了在Entry入口写了一个系统的下拉列表组件&#xff0c;如果我们想要封装一个可供复用的组件供团队其他人使用&#xff0c;那么需要掌握一下自定义组件的写法&#xff1a; 1、自定义可导入组件 - export 声明模块 如果要定义一个在外部可使用的组件 , 需要再定义组件…

Web大学生网页作业成品——婚礼婚纱网页设计与实现(HTML+CSS)(6个页面)

&#x1f389;&#x1f389;&#x1f389; 常见网页设计作业题材有**汽车、环保、明星、文化、国家、抗疫、景点、人物、体育、植物、公益、图书、节日、游戏、商城、旅游、家乡、学校、电影、动漫、非遗、动物、个人、企业、美食、婚纱、其他**等网页设计题目, 可满足大学生网…

时序数据库TimescaleDB安装部署以及常见使用

文章目录 一、时序数据库二、TimescaleDB部署1、repository yum仓库配置2、yum在线安装3、插件配置4、TimescaleDB使用登录pg创建插件使用超表 一、时序数据库 什么是时序数据库&#xff1f;顾名思义&#xff0c;用于处理按照时间变化顺序的数据的数据库即为时序数据库&#x…

Matlab: 生成对抗网络,使用Datastore结构输入mat格式数据

使用matlab的生成对抗网络&#xff08;Generative Adversarial Network&#xff0c;GAN&#xff09;以及条件CGAN时&#xff0c;案例中 的生成器的输入为图像&#xff0c;改为.mat格式输入遇到的问题。解决方法 官方资源 训练条件生成对抗网络 (CGAN)- MATLAB & Simulink-…

Linux kernel 堆溢出利用方法(二)

前言 本文我们通过我们的老朋友heap_bof来讲解Linux kernel中off-by-null的利用手法。在通过讲解另一道相对来说比较困难的kernel off-by-null docker escape来深入了解这种漏洞的利用手法。&#xff08;没了解过docker逃逸的朋友也可以看懂&#xff0c;毕竟有了root权限后&a…

设计模式:工厂方法模式和策略模式

工厂方法模式 什么是开闭原则? 开闭原则是扩展开发,对修改关闭 简单工厂(不是设计模式而是一种编程的习惯) 有三个角色 抽象产品:定义了产品的规范,描述了产品的特性和功能.具体产品:实现或者继承抽象产品的子类具体工厂:提供了创建产品的方法,调用者通过该方法获取产品 实…