【动手学强化学习】part6-策略梯度算法

阐述、总结【动手学强化学习】章节内容的学习情况,复现并理解代码。

文章目录

  • 一、算法背景
    • 1.1 算法目标
    • 1.2 存在问题
    • 1.3 解决方法
  • 二、REINFORCE算法
    • 2.1 必要说明
      • softmax()函数
      • 交叉熵
      • 策略更新思想
    • 2.2 伪代码
      • 算法流程简述
    • 2.3 算法代码
    • 2.4 运行结果
    • 2.5 算法流程说明
      • 初始化参数
      • 初始化“车杆”环境
      • 初始化policy_net网络模型
      • episode采样
      • 价值更新
      • 策略更新
  • 三、疑问
    • 3.1 为什么要采用softmax作为policy_net的输出?
    • 3.2 为什么REINFORCE算法比DQN算法波动更大?
    • 3.3 策略更新的步骤具体在算法哪一块?
  • 四、总结


一、算法背景

1.1 算法目标

给定“黑盒”模型,求解最优policy

1.2 存在问题

动态规划、时序差分-TD、值函数近似算法中策略的表示都是基于价值(value-based)的,即算法目标是学习值函数,然后根据值函数导出一个策略,学习过程中并不存在一个显式的策略。

1.3 解决方法

采用基于策略(policy-based)算法,直接显式地学习一个目标策略。
可以理解为,之前policy的表示是“表格式(tabular)”的,即存在一个“ π − t a b l e : π ( a ∣ s ) {\pi}-table:{\pi}(a|s) πtable:π(as) ”,现在通过“函数拟合”去学习一个关于policy的函数,即 π θ ( a ∣ s ) {\pi}_θ(a|s) πθ(as),参数为θ

二、REINFORCE算法

  • 🌟算法类型
    环境依赖:❌model-based ✅model-free
    价值估计:✅non-incremental ❌incremental(采用蒙特卡洛方法估计)
    价值表征:✅tabular representation ❌function representation(采用蒙特卡洛方法估计)
    学习方式:✅on-policy ❌off-policy
    策略表征:❌value-based ✅policy-based(通过函数拟合去计算 π θ ( a ∣ s ) {\pi}_θ(a|s) πθ(as) 概率)

2.1 必要说明

softmax()函数

softmax函数是一种常用的激活函数,主要用于将一个向量转换为概率分布,使得向量中的每个元素都被映射到 [0, 1] 区间内,并且所有元素的和为 1。
对于一个向量 z = [ z 1 , z 2 , … , z n ] \mathbf{z}=[z_1,z_2,\ldots,z_n] z=[z1,z2,,zn] ,softmax函数的定义为:
s o f t m a x ( z ) i = e z i ∑ j = 1 n e z j \mathrm{softmax}(\mathbf{z})_i=\frac{e^{z_i}}{\sum_{j=1}^ne^{z_j}} softmax(z)i=j=1nezjezi

  • 特点
    ①归一化:softmax 函数将向量的每个元素转换为一个介于 0 和 1 之间的值,并且所有元素的和为 1。这使得输出可以被解释为概率分布。
    ** 在这里就体现为在state状态下与环境交互时,action的使用概率,即 π θ ( a ∣ s ) {\pi}_θ(a|s) πθ(as)
    ②强调最大值:softmax 函数会放大较大的值并抑制较小的值。这意味着在多分类问题中,模型更倾向于选择具有最高输出值的类别。
    ** 在这里就体现为最优policy中action的选择。

    ③平滑性:softmax 函数是平滑的,这意味着它在所有点上都是可导的,这在训练神经网络时非常有用,因为可以通过梯度下降等优化算法来更新模型参数。

交叉熵

当采用softmax函数作为神经网络的输出时,一般采用交叉熵来衡量两个概率的区别,即真值与神经网络输出概率的区别,因此交叉熵可以作为神经网络的损失函数。参考【动手学深度学习】part9-softmax回归

  • 交叉熵定义为:
    假设模型的输出为 z,真实标签为 y,则整个过程可以表示为:
    p = s o f t m a x ( z ) L = H ( y , p ) = − ∑ i = 1 n y i log ⁡ ( p i ) \begin{aligned}\mathbf{p}&=\mathrm{softmax}(\mathbf{z})\\\\L=H(\mathbf{y},\mathbf{p})&=-\sum_{i=1}^ny_i\log(p_i)\end{aligned} pL=H(y,p)=softmax(z)=i=1nyilog(pi)

策略更新思想

将policy_net的目标函数定义为最大化state value,即:
J ( θ ) = E [ ∑ t = 0 ∞ γ t R t + 1 ] = E s 0 [ V π θ ( s 0 ) ] J(\theta)=\mathbb{E}\left[\sum_{t=0}^\infty\gamma^tR_{t+1}\right]=\mathbb{E}_{s_0}[V^{\pi_\theta}(s_0)] J(θ)=E[t=0γtRt+1]=Es0[Vπθ(s0)]
V π θ ( s 0 ) = ∑ a ∇ θ π ( a ∣ s , θ ) q π ( s , a ) V^{\pi_\theta}(s_0)=\sum_a\nabla_\theta\pi(a|s,\theta)q_\pi(s,a) Vπθ(s0)=aθπ(as,θ)qπ(s,a)
为了求解最优policy使得state value最优,借鉴“梯度下降”(参考:【算法学习】优化算法-小批量随机梯度下降mini-batch SGD)的思想,求解 J ( θ ) J(\theta) J(θ)的梯度,即:
∇ θ J = ∑ s d ( s ) ∑ a ∇ θ π ( a ∣ s , θ ) q π ( s , a ) = ∑ s d ( s ) ∑ a π ( a ∣ s , θ ) ∇ θ ln ⁡ π ( a ∣ s , θ ) q π ( s , a ) = E S ∼ d [ ∑ a π ( a ∣ S , θ ) ∇ θ ln ⁡ π ( a ∣ S , θ ) q π ( S , a ) ] = E S ∼ d , A ∼ π [ ∇ θ ln ⁡ π ( A ∣ S , θ ) q π ( S , A ) ] ≐ E [ ∇ θ ln ⁡ π ( A ∣ S , θ ) q π ( S , A ) ] \begin{aligned} \nabla_{\theta}J& =\sum_sd(s)\sum_a\nabla_\theta\pi(a|s,\theta)q_\pi(s,a) \\ &=\sum_{s}d(s)\sum_{a}\pi(a|s,\theta)\nabla_{\theta}\ln\pi(a|s,\theta)q_{\pi}(s,a) \\ &=\mathbb{E}_{S\sim d}\left[\sum_{a}\pi(a|S,\theta)\nabla_{\theta}\ln\pi(a|S,\theta)q_{\pi}(S,a)\right] \\ &=\mathbb{E}_{S\sim d,A\sim\pi}\big[\nabla_\theta\ln\pi(A|S,\theta)q_\pi(S,A)\big] \\ &\doteq\mathbb{E}\big[\nabla_\theta\ln\pi(A|S,\theta)q_\pi(S,A)\big] \end{aligned} θJ=sd(s)aθπ(as,θ)qπ(s,a)=sd(s)aπ(as,θ)θlnπ(as,θ)qπ(s,a)=ESd[aπ(aS,θ)θlnπ(aS,θ)qπ(S,a)]=ESd,Aπ[θlnπ(AS,θ)qπ(S,A)]E[θlnπ(AS,θ)qπ(S,A)]
因此,policy_net的参数更新可以设置为:
θ t + 1 = θ t + α ∇ θ J ( θ ) = θ t + α E [ ∇ θ ln ⁡ π ( A ∣ S , θ t ) q π ( S , A ) ] \begin{aligned} \theta_{t+1}& =\theta_t+\alpha\nabla_\theta J(\theta) \\ &=\theta_t+\alpha\mathbb{E}\Big[\nabla_\theta\ln\pi(A|S,\theta_t)q_\pi(S,A)\Big] \end{aligned} θt+1=θt+αθJ(θ)=θt+αE[θlnπ(AS,θt)qπ(S,A)]
采用“随机近似(stochastic approximation)”去估计梯度,即:
θ t + 1 = θ t + α ∇ θ ln ⁡ π ( a t ∣ s t , θ t ) q π ( s t , a t ) \theta_{t+1}=\theta_t+\alpha\nabla_\theta\ln\pi(a_t|s_t,\theta_t)q_\pi(s_t,a_t) θt+1=θt+αθlnπ(atst,θt)qπ(st,at)

2.2 伪代码

在这里插入图片描述

算法流程简述

①初始化policy_net:构建policy_net网络模型。
②采样episode:设定周期数num_episodes,循环迭代采样episode,根据 π θ ( a ∣ s ) {\pi}_θ(a|s) πθ(as) 采样(s,a,r,s’,done)直至terminal state,即done为true,得到一条episode,即(s,a,r,s’)链。
③价值更新:依据蒙特卡洛方法,基于episode采用first-visit更新对应q(s,a)值。
④策略更新:设定损失函数为交叉熵,基于“梯度下降”方法更新policy_net网络参数。
⑤终止判断:根据已产生episode个数是否达到num_episodes,判断算法是否终止,并输出policy。

2.3 算法代码

import gym
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm
import rl_utilsclass PolicyNet(torch.nn.Module):def __init__(self, state_dim, hidden_dim, action_dim):super(PolicyNet, self).__init__()self.fc1 = torch.nn.Linear(state_dim, hidden_dim)self.fc2 = torch.nn.Linear(hidden_dim, action_dim)def forward(self, x):x = F.relu(self.fc1(x))return F.softmax(self.fc2(x), dim=1)class REINFORCE:def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma,device):self.policy_net = PolicyNet(state_dim, hidden_dim,action_dim).to(device)self.optimizer = torch.optim.Adam(self.policy_net.parameters(),lr=learning_rate)  # 使用Adam优化器self.gamma = gamma  # 折扣因子self.device = devicedef take_action(self, state):  # 🌟根据动作概率分布随机采样,不再是根据Q值去选取action了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 = 0self.optimizer.zero_grad()for i in reversed(range(len(reward_list))):  # 从最后一步算起reward = 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.gamma * G + rewardloss = -log_prob * G  # 每一步的损失函数loss.backward()  # 反向传播计算梯度self.optimizer.step()  # 梯度下降,更新policy_net参数learning_rate = 1e-3
num_episodes = 1000
hidden_dim = 128
gamma = 0.98
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")env_name = "CartPole-v0"
env = gym.make(env_name)
env.seed(0)
torch.manual_seed(0)
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
agent = REINFORCE(state_dim, hidden_dim, action_dim, learning_rate, gamma,device)return_list = []
for i in range(10):with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:for i_episode in range(int(num_episodes / 10)):episode_return = 0transition_dict = {'states': [],'actions': [],'next_states': [],'rewards': [],'dones': []}state = env.reset()done = Falsewhile not done:action = agent.take_action(state)next_state, reward, done, _ = env.step(action)transition_dict['states'].append(state)transition_dict['actions'].append(action)transition_dict['next_states'].append(next_state)transition_dict['rewards'].append(reward)transition_dict['dones'].append(done)state = next_stateepisode_return += rewardreturn_list.append(episode_return)agent.update(transition_dict)if (i_episode + 1) % 10 == 0:pbar.set_postfix({'episode':'%d' % (num_episodes / 10 * i + i_episode + 1),'return':'%.3f' % np.mean(return_list[-10:])})pbar.update(1)episodes_list = list(range(len(return_list)))
plt.plot(episodes_list, return_list)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('REINFORCE on {}'.format(env_name))
plt.show()mv_return = rl_utils.moving_average(return_list, 9)
plt.plot(episodes_list, mv_return)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('REINFORCE on {}'.format(env_name))
plt.show()

2.4 运行结果

Iteration 0: 100%|██████████| 100/100 [00:09<00:00, 10.01it/s, episode=100, return=28.200]
Iteration 1: 100%|██████████| 100/100 [00:09<00:00, 10.21it/s, episode=200, return=78.300]
Iteration 2: 100%|██████████| 100/100 [00:14<00:00,  6.77it/s, episode=300, return=146.000]
Iteration 3: 100%|██████████| 100/100 [00:18<00:00,  5.30it/s, episode=400, return=160.100]
Iteration 4: 100%|██████████| 100/100 [00:24<00:00,  4.02it/s, episode=500, return=199.400]
Iteration 5: 100%|██████████| 100/100 [00:25<00:00,  3.91it/s, episode=600, return=200.000]
Iteration 6: 100%|██████████| 100/100 [00:25<00:00,  3.96it/s, episode=700, return=183.700]
Iteration 7: 100%|██████████| 100/100 [00:26<00:00,  3.79it/s, episode=800, return=187.000]
Iteration 8: 100%|██████████| 100/100 [00:23<00:00,  4.22it/s, episode=900, return=193.200]
Iteration 9: 100%|██████████| 100/100 [00:25<00:00,  3.88it/s, episode=1000, return=180.700]

在这里插入图片描述
在这里插入图片描述

  • 结果分析
    对比DQN算法,得到的episode_return波动更大,但能获取到最优的性能,得益于蒙特卡洛方法,如:
    Iteration 5: 100%|██████████| 100/100 [00:25<00:00, 3.91it/s, episode=600, return=200.000]
    (carpole的最佳奖励分数为200)

2.5 算法流程说明

初始化参数

learning_rate = 1e-3
num_episodes = 1000
hidden_dim = 128
gamma = 0.98
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")

初始化“车杆”环境

env_name = "CartPole-v0"
env = gym.make(env_name)
env.seed(0)
torch.manual_seed(0)

初始化policy_net网络模型

state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
agent = REINFORCE(state_dim, hidden_dim, action_dim, learning_rate, gamma,device)
...
class PolicyNet(torch.nn.Module):def __init__(self, state_dim, hidden_dim, action_dim):super(PolicyNet, self).__init__()self.fc1 = torch.nn.Linear(state_dim, hidden_dim)self.fc2 = torch.nn.Linear(hidden_dim, action_dim)def forward(self, x):x = F.relu(self.fc1(x))return F.softmax(self.fc2(x), dim=1)

设置为4-128-2的全连接网络,输入为state=4维,输出为action的选取概率=2维,网络模型可表示为:
y = s o f t m a x ( f c 2 ( r e l u ( f c 1 ( x ) ) ) ) , x = s t a t e s y=softmax\left(fc_2\left(relu\bigl(fc_1(x)\bigr)\right)\right),x=states y=softmax(fc2(relu(fc1(x)))),x=states

episode采样

        for i_episode in range(int(num_episodes / 10)):episode_return = 0transition_dict = {'states': [],'actions': [],'next_states': [],'rewards': [],'dones': []}state = env.reset()done = Falsewhile not done: #采样单个episode直至terminal state,即done=trueaction = agent.take_action(state)next_state, reward, done, _ = env.step(action)transition_dict['states'].append(state)transition_dict['actions'].append(action)transition_dict['next_states'].append(next_state)transition_dict['rewards'].append(reward)transition_dict['dones'].append(done)state = next_stateepisode_return += rewardreturn_list.append(episode_return)
...def take_action(self, state):  # 🌟根据动作概率分布随机采样,不再是根据Q值去选取action了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()

①获取初始state:state = env.reset()
②根据policy_net的输出采取action:agent.take_action(state)
③与环境交互,得到(s,a,r,s’,done):env.step(action)
④将样本添加至episode:transition_dict
⑤统计episode即时奖励累加值:episode_return += reward

价值更新

agent.update(transition_dict)   #训练
...def update(self, transition_dict):reward_list = transition_dict['rewards']state_list = transition_dict['states']action_list = transition_dict['actions']G = 0self.optimizer.zero_grad()for i in reversed(range(len(reward_list))):  # 从最后一步算起reward = 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.gamma * G + reward

采样完一个episode后,倒序累加discounted reward:G,以🌟fist-visit方式来估计多个q(s,a),这里的G就用来估计q(s,a),以实现:
reversed(range(len(reward_list)))+G = self.gamma * G + reward
q ( s t , a t ) = ∑ k = t + 1 T γ k − t − 1 r k q({s_{t},a_{t}})=\sum_{k=t+1}^{T}\gamma^{k-t-1}r_{k} q(st,at)=k=t+1Tγkt1rk

策略更新

agent.update(transition_dict)   #训练
...self.optimizer.zero_grad()...log_prob = torch.log(self.policy_net(state).gather(1, action))G = self.gamma * G + rewardloss = -log_prob * G  # 每一步的损失函数loss.backward()  # 反向传播计算梯度self.optimizer.step()  # 梯度下降,更新policy_net参数

计算交叉熵损失函数:loss = -log_prob * G,对应上交叉熵的定义:
L = H ( y , p ) = − ∑ i = 1 n y i log ⁡ ( p i ) L=H(\mathbf{y},\mathbf{p})=-\sum_{i=1}^ny_i\log(p_i) L=H(y,p)=i=1nyilog(pi)
其中,G对应“真值”,log_prob 对应网络模型输出概率的对数。这里的G等同于episode在初始的env.state后采取action获得的累计折扣奖励,等同于就是在估计q(s,a)。

随后采用Adam优化器进行:梯度清零+反向传播+参数更新

三、疑问

3.1 为什么要采用softmax作为policy_net的输出?

要保证 π θ ( a ∣ s ) {\pi}_θ(a|s) πθ(as) 中每个action的概率为正数,并且概率在(0,1)之间取值。

3.2 为什么REINFORCE算法比DQN算法波动更大?

借助蒙特卡洛方法采样轨迹来估计动作价值,这种做法的一大优点是可以得到无偏的梯度。但是,正是因为使用了蒙特卡洛方法, REINFORCE 算法的梯度估计的方差很大,可能会造成一定程度上的不稳定

3.3 策略更新的步骤具体在算法哪一块?

算法示例中没有显式地去计算policy_net的梯度,而是在Adam optimizer中进行的梯度计算,具体代码为反向传播的这一步:loss.backward() # 反向传播计算梯度
在策略更新中,采用梯度下降的方式去更新policy_net的参数,推导出:
θ t + 1 = θ t + α ∇ θ ln ⁡ π ( a t ∣ s t , θ t ) q π ( s t , a t ) \theta_{t+1}=\theta_t+\alpha\nabla_\theta\ln\pi(a_t|s_t,\theta_t)q_\pi(s_t,a_t) θt+1=θt+αθlnπ(atst,θt)qπ(st,at)
在设置损失函数时,由于神经网络采用了softmax函数作为输出,因此设定交叉熵为损失函数。那么就引出一个问题:在多分类问题中,交叉熵中的真值 y i y_i yi 表示的是真实分类的标签,而在REINFORCE算法中构建的policy_net网络输出为action的概率,没有对应的真值,即在特定的state下应该采取的action。
于是根据“梯度下降”中policy_net参数的更新方式,选取 q π ( s t , a t ) q_\pi(s_t,a_t) qπ(st,at)为指导,即在计算交叉熵时,将 y i y_i yi设定为 q π ( s t , a t ) q_\pi(s_t,a_t) qπ(st,at)

四、总结

  • REINFORCE算法采用policy_net网络去显示地表征policy,采用蒙特卡洛的方法去估计q(s,a),智能体根据当前策略直接和环境交互,通过采样得到的轨迹数据直接计算出策略参数的梯度,进而更新当前策略,使其向最大化策略期望回报的目标靠近。

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

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

相关文章

单片机内存管理和启动文件

一、常见存储器介绍 FLASH又称为闪存&#xff0c;不仅具备电子可擦除可编程(EEPROM)的性能&#xff0c;还不会断电丢失数据同时可以快速读取数据&#xff0c;U盘和MP3里用的就是这种存储器。在以前的嵌入式芯片中&#xff0c;存储设备一直使用ROM(EPROM)&#xff0c;随着技术的…

Python画图3个小案例之“一起看流星雨”、“爱心跳动”、“烟花绚丽”

源码如下&#xff1a; import turtle # 导入turtle库&#xff0c;用于图形绘制 import random # 导入random库&#xff0c;生成随机数 import math # 导入math库&#xff0c;进行数学计算turtle.setup(1.0, 1.0) # 设置窗口大小为屏幕大小 turtle.title("流星雨动画&…

SQL-lab靶场less1-4

说明&#xff1a;部分内容来源于网络&#xff0c;如有侵权联系删除 前情提要&#xff1a;搭建sql-lab本地靶场的时候发现一些致命的报错&#xff1a; 这个程序只能在php 5.x上运行&#xff0c;在php 7及更高版本上&#xff0c;函数“mysql_query”和一些相关函数被删除&#xf…

AutoGLM:智谱AI的创新,让手机成为你的生活全能助手

目录 引言一、AutoGLM&#xff1a;开启AI的Phone Use时代二、技术核心&#xff1a;AI从“语言理解”到“执行操作”三、实际应用案例&#xff1a;AutoGLM的智能力量1. 智能生活管理&#x1f34e;2. 社交网络的智能互动&#x1f351;3. 办公自动化&#x1f352;4. 电子商务的购物…

ceph补充介绍

SDS-ceph ceph介绍 crushmap 1、crush算法通过计算数据存储位置来确定如何存储和检索&#xff0c;授权客户端直接连接osd 2、对象通过算法被切分成数据片&#xff0c;分布在不同的osd上 3、提供很多种的bucket&#xff0c;最小的节点是osd # 结构 osd (or device) host #主…

Scrapy源码解析:DownloadHandlers设计与解析

1、源码解析 代码路径&#xff1a;scrapy/core/downloader/__init__.py 详细代码解析&#xff0c;请看代码注释 """Download handlers for different schemes"""import logging from typing import TYPE_CHECKING, Any, Callable, Dict, Gener…

如何解决docker镜像下载失败问题

经常用docker的朋友都知道&#xff0c;docker hub的镜像仓库经常访问不通 rootiZwz97kfjnf78copv1ae65Z:~# docker pull ubuntu:18.04 Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.…

探索 ONLYOFFICE:开源办公套件的魅力

文章目录 引言一、ONLYOFFICE 产品介绍与历史1.1 ONLUOFFICE 介绍1.2 ONLYOFFICE发展历史 二、ONLYOFFICE 的核心功能2.1 文档处理2.2 演示文稿 三、ONLYOFFICE 部署与安装四、ONLYOFFICE 产品优势和挑战五、ONLYOFFICE 案例分析六、ONLYOFFICE 的未来发展七、全文总结 引言 在…

FlaskFastAPIgunicornunicorn并发调用

Flask VS. FastAPI Flask和FastAPI是Python中两种流行的Web框架&#xff0c;它们各自具有不同的特点和适用场景。以下是它们之间的一些主要区别&#xff1a; 1. 框架类型 Flask&#xff1a;Flask是一个轻量级的微框架&#xff0c;适合构建小型到中型的Web应用。它灵活且易于扩展…

第2章 JSP基础

JavaWeb程序设计-T2(JSP基础) 一、JSP概述 1、JSP概念 JSP(Java Server Page)是sun公司倡导建立的一种动态网页标准。 用于开发动态网页(将后端开发语言嵌入带前端中【将java嵌入到HTML中】) 2、JSP工作原理 JSP就是将传统Java代码嵌入到html页面代码中,由Web服务器进…

Unix 中文件权限设置

在 Unix 和类 Unix 系统中&#xff0c;文件权限是通过八进制数表示的&#xff0c;这些数字代表不同的权限组合。以下是一些常见的八进制数及其对应的权限设置&#xff1a; 1. **0644**&#xff1a; - 所有者&#xff08;owner&#xff09;&#xff1a;读&#xff08;read&a…

【小白学机器学习28】 统计学脉络+ 总体+ 随机抽样方法

目录 参考书&#xff0c;学习书 0 统计学知识大致脉络 1 个体---抽样---整体 1.1 关于个体---抽样---整体&#xff0c;这个三段式关系 1.2 要明白&#xff0c;自然界的整体/母体是不可能被全部认识的 1.2.1 不要较真&#xff0c;如果是人为定义的一个整体&#xff0c;是可…

《Python游戏编程入门》注-第4章5

2.3 实现开始游戏的功能 当显示图1所示的游戏启动界面后&#xff0c;根据提示点击“确定”按键&#xff0c;则可以开始游戏。也就是要完成键盘监听的功能&#xff0c;当游戏程序监听到玩家点击了“确定”按键后&#xff0c;开始游戏。 在《Python游戏编程入门注-第4章2》中介…

mysql中的锁理解

1.共享锁&#xff0c;排他锁&#xff0c;也叫读锁和写锁 共享锁(S锁)(读锁)&#xff1a;事务在读取记录的时候获取共享锁&#xff0c;允许其它事务同时获取共享锁。 排他锁(X锁)(写锁)&#xff1a;事务在修改记录的时候获取排他锁&#xff0c;只允许一个事务获取排他锁&#x…

【C++】位图详解(一文彻底搞懂位图的使用方法与底层原理)

目录 1.位图的概念 2.位图的使用方法 定义与创建 设置和清除 位访问和检查 转换为其他格式 3.位图的使用场景 1.快速的查找某个数据是否在一个集合中 2.排序去重 3.求两个集合的交集和并集 4.位图的底层实现 私有成员定义与初始化 set和reset的实现 前面的博客我们…

补齐:相交链表:扣160

梦重新开始的地方 – 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。图示两个链表在节点 c1 开始相交&#xff1a; 示例&#xff1a; 何解&#xff1f; 暴力&…

Python:入门基础

目录 常量和表达式 变量 变量的语法 变量的类型 动态类型特性 注释的使用 输入和输出 通过控制台输出 通过控制台输入 运算符 算术运算符 关系运算符 逻辑运算符 赋值运算符 常量和表达式 print是Python中的一个内置函数&#xff0c;使用print函数可以将数据打印…

手动搭建koa+ts项目框架(node开发配置环境变量)

文章目录 一、安装所需依赖二、设置package.json三、定义ts &#xff08;可选&#xff09;四、配置环境变量文件五、引入变量文件总结如有启发&#xff0c;可点赞收藏哟~ 一、安装所需依赖 pnpm add dotenv二、设置package.json 先配置脚本设置对应环境变量NODE_ENV {"…

吞吐量最高飙升20倍!破解强化学习训练部署难题

**强化学习&#xff08;RL&#xff09;对大模型复杂推理能力提升有关键作用&#xff0c;然而&#xff0c;RL 复杂的计算流程以及现有系统局限性&#xff0c;也给训练和部署带来了挑战。近日&#xff0c;字节跳动豆包大模型团队与香港大学联合提出 HybridFlow&#xff08;开源项…

Unity 插件编译版本.net 4.0

项目中用到了Google.ProtocolBuffersLite.dll 这个动态链接库&#xff0c;在升级完Unity版本后出现了 ”Unity targets .NET 4.x and is marked as compatible with editor, Editor can only use assemblies targeting .NET 3.5 or lower“ 的问题。 解决方法&#xff1a; 1、…