11.3、信赖域策略优化算法TRPO强化学习-运用实践

基于LunarLander登陆器的TRPO强化学习(含PYTHON工程)

TRPO强化学习算法主要分为3个部分,分别介绍其理论、细节、实现

本文主要介绍TRPO的理论和代码的对应、实践

TRPO系列(TRPO是真的复杂,全部理解花费了我半个月的时间嘞,希望也能帮助大家理解一下):
11.1、信赖域策略优化算法TRPO强化学习-从理论到实践
11.2、信赖域策略优化算法TRPO强化学习-约束优化求解
11.3、信赖域策略优化算法TRPO强化学习-运用实践

其他算法:
07、基于LunarLander登陆器的DQN强化学习案例(含PYTHON工程)

08、基于LunarLander登陆器的DDQN强化学习(含PYTHON工程)

09、基于LunarLander登陆器的Dueling DQN强化学习(含PYTHON工程)

10、基于LunarLander登陆器的Dueling DDQN强化学习(含PYTHON工程)

11、基于LunarLander登陆器的A2C强化学习(含PYTHON工程)

参考:
【TRPO系列讲解】(五)TRPO_理论推导篇

目录

    • 基于LunarLander登陆器的TRPO强化学习(含PYTHON工程)
    • 1、替代优势函数的计算
    • 2、替代优势函数导数的计算
    • 3、海森向量积的计算
    • 4、共轭梯度法求解 H − 1 g {{H^{ - 1}}g} H1g
    • 5、计算策略网络的更新步长
    • 6、更新步长回溯衰减
    • 7、基于LunarLander登陆器的TRPO强化学习(含PYTHON工程)

1、替代优势函数的计算

信赖域策略优化算法TRPO强化学习-从理论到实践中介绍了TRPO的核心函数:替代优势函数。
要优化的式子是:
max ⁡ θ L θ o l d ( θ ) = max ⁡ θ E s ∼ ρ θ o l d , a ∼ θ o l d [ π θ ( a ∣ s ) π θ o l d ( a ∣ s ) A θ o l d ( s , a ) ] s u b j e c t    t o E s ∼ ρ θ o l d [ D K L ( π θ o l d ( ⋅ ∣ s ) ∥ π θ ( ⋅ ∣ s ) ) ] ≤ δ \begin{array}{l} {\max _\theta }{L_{{\theta _{old}}}}(\theta ) ={\max _\theta }E{_{s\sim{\rho _{{\theta _{old}}}},a\sim\theta_{old} }}\left[ {\frac{{{\pi _\theta }(a\mid s)}}{{{\pi _{{\theta _{old}}}}(a\mid s)}}{A_{{\theta _{old}}(s,a)}}} \right]\\ subject\;to\quad {E_{s\sim{\rho _{{\theta _{old}}}}}}\left[ {{D_{KL}}({\pi _{{\theta _{old}}}}( \cdot \mid s)\parallel {\pi _\theta }( \cdot \mid s))} \right] \le \delta \end{array} maxθLθold(θ)=maxθEsρθold,aθold[πθold(as)πθ(as)Aθold(s,a)]subjecttoEsρθold[DKL(πθold(s)πθ(s))]δ

替代优势函数 L θ o l d ( θ ) {L_{{\theta _{old}}}}(\theta ) Lθold(θ)的表达式为:
L θ o l d ( θ ) = E s ∼ ρ θ o l d , a ∼ θ o l d [ π θ ( a ∣ s ) π θ o l d ( a ∣ s ) A θ o l d ( s , a ) ] {L_{{\theta _{old}}}}(\theta ) =E{_{s\sim{\rho _{{\theta _{old}}}},a\sim\theta_{old} }}\left[ {\frac{{{\pi _\theta }(a\mid s)}}{{{\pi _{{\theta _{old}}}}(a\mid s)}}{A_{{\theta _{old}}(s,a)}}} \right] Lθold(θ)=Esρθold,aθold[πθold(as)πθ(as)Aθold(s,a)]
其中:
A π ( s , a ) = Q π ( s , a ) − V π ( s ) A_\pi(s,a)=Q_\pi(s,a)-V_\pi(s) Aπ(s,a)=Qπ(s,a)Vπ(s)

对应核心代码(考虑到熵实际上是额外的改进了):

	# 计算新旧动作概率之比,即当前策略与旧策略在给定状态下的动作选择概率之比
	prob_ratio = action_prob / old_action_prob  # pi(a|s) / pi_old(a|s)
	# 计算替代损失。这个损失主要由两部分组成:1) 基于新旧策略的动作概率之比的收益;2) entropy正则化项,由self.ent_coeff控制权重。
	loss = tf.reduce_mean(prob_ratio * advantage) + self.ent_coeff * entropy

状态价值函数V可以通过批评者网络得到,但是Q函数在TRPO中属于未知的,在此使用 U t U_t Ut近似,其原因如下:

Q(s,a)和Ut关系:Q(s,a)是Ut的期望,期望可以理解为求积分,实际上Q是对Ut把所有t之后的时刻(t+1、t+2等等)当作变量求积分得到的。因此Q(s,a)可以直观反应当前状态s下执行各个动作的好坏。
Q π ( s t , a t ) = E [ U t ∣ s t , a t ] Q_\pi(s_t,{a_t})=\mathbb{E}[U_t\mid s_t,{a_t}] Qπ(st,at)=E[Utst,at]

由此替代优势函数的计算的代码表达如下(代码里面叫surrogate_loss,其实是一样的):

# 计算状态价值函数
Vs = self.value_model(obs).numpy().flatten()
# 计算优势函数,Gs就是Ut
advantage = Gs - Vs
# 直接标准化
advantage = (advantage - advantage.mean())/(advantage.std() + 1e-8)
actions_one_hot = tf.one_hot(actions, self.envs[0].action_space.n, dtype="float64")
# 计算其替代函数L的损失
policy_loss = surrogate_loss()
def surrogate_loss(theta=None):
	# 如果theta为None,则使用self.model作为模型,否则使用self.tmp_model并为其赋值
	if theta is None:
		model = self.model
	else:
		model = self.tmp_model
		assign_vars(self.tmp_model, theta)
	# 使用模型对obs进行预测,得到logits
	logits = model(obs)
	# 对logits应用softmax函数,得到action的概率分布
	action_prob = tf.nn.softmax(logits)
	# 计算每个动作的概率之和,这里假设actions_one_hot是动作的one-hot编码
	action_prob = tf.reduce_sum(actions_one_hot * action_prob, axis=1)
	# 使用原始模型对obs进行预测,得到旧的logits
	old_logits = self.model(obs)
	# 对旧的logits应用softmax函数,得到旧的动作概率分布
	old_action_prob = tf.nn.softmax(old_logits)
	# 计算每个动作的旧概率之和,并加上一个小的常数以避免除以0的错误
	old_action_prob = tf.reduce_sum(actions_one_hot * old_action_prob, axis=1).numpy() + 1e-8
	# 计算新旧动作概率之比,即当前策略与旧策略在给定状态下的动作选择概率之比
	prob_ratio = action_prob / old_action_prob  # pi(a|s) / pi_old(a|s)
	# 计算替代损失。这个损失主要由两部分组成:1) 基于新旧策略的动作概率之比的收益;2) entropy正则化项,由self.ent_coeff控制权重。
	loss = tf.reduce_mean(prob_ratio * advantage) + self.ent_coeff * entropy
	# 返回计算得到的损失值。
	return loss

2、替代优势函数导数的计算

信赖域策略优化算法TRPO强化学习-约束优化求解的第2部分中,替代优势函数的导数在更新中扮演着十分重要的作用,需要进行求解:
g T = ∇ θ L θ o l d ( θ ) ∣ θ = θ o l d {g^T} = {\nabla _\theta }{L_{{\theta _{old}}}}(\theta ){|_{\theta = {\theta _{old}}}} gT=θLθold(θ)θ=θold
θ ′ = 2 δ g T H − 1 g H − 1 g + θ \theta^{\prime}=\sqrt{\frac{2\delta}{g^{T}H^{-1}g}}H^{-1}g+\theta θ=gTH1g2δ H1g+θ

其对应的代码为:

# 计算替代优势函数的导数g
policy_gradient = flatgrad(surrogate_loss, self.model.trainable_variables).numpy()

其中,flatgrad是求导的函数,在代码中会多次用到:

# Makes gradient of function loss_fn wrt var_list and
# flattens it to have a 1-D vector 计算梯度,并将其扁平化
def flatgrad(loss_fn, var_list):
	with tf.GradientTape() as t:
		loss = loss_fn()
	grads = t.gradient(loss, var_list, unconnected_gradients=tf.UnconnectedGradients.ZERO)
	return tf.concat([tf.reshape(g, [-1]) for g in grads], axis=0)

3、海森向量积的计算

对于TRPO的不等式约束,我们对其使用二阶泰勒展开(证明参考自然梯度Natural Policy):
D K L ρ o l d ‾ ( π o l d , π n e w ) → 1 2 ( θ − θ o l d ) T A ( θ o l d ) ( θ − θ o l d ) H = A ( θ o l d ) = ∇ θ 2 D K L ρ o l d ‾ ( π o l d , π n e w ) \begin{aligned}\overline{D_{KL}^{\rho_{old}}}(\pi_{old},\pi_{new})\to\frac12(\theta-\theta_{old})^TA(\theta_{old})(\theta-\theta_{old})\\\\H=A(\theta_{old})=\nabla_\theta^2\overline{D_{KL}^{\rho_{old}}}(\pi_{old},\pi_{new})\end{aligned} DKLρold(πold,πnew)21(θθold)TA(θold)(θθold)H=A(θold)=θ2DKLρold(πold,πnew)

此外,我们需要使用共轭梯度法求解 H − 1 g {{H^{ - 1}}g} H1g矩阵,这等价于求解线性方程 H x = g Hx=g Hx=g,需要频繁使用到海森矩阵H和向量x的乘积。而且,海森矩阵需要对KL散度求二阶导得到,这是一个相对复杂的过程。【TRPO系列讲解】(六)TRPO_求解实现篇的第12min介绍了一种简单的运算加速方式,就是先对KL散度求一阶导数,然后乘以向量x,然后再对相乘后的式子求导
H i j = ∂ ∂ θ j ∂ D K L ∂ θ i y i = ∂ ∂ θ ∑ j ∂ D K L ∂ θ j x j y k = ∑ j H k j x j = ∑ j ∂ ∂ θ j ∂ D K L ∂ θ k x j = ∂ ∂ θ k ∑ j ∂ D K L ∂ θ j x j \begin{aligned} &H_{ij}=\frac{\partial}{\partial\theta_{j}}\frac{\partial D_{KL}}{\partial\theta_{i}} \\ &y_{i}=\frac{\partial}{\partial\theta}\sum_{j}\frac{\partial D_{KL}}{\partial\theta_{j}}x_{j} \\ &y_{k}=\sum_{j}H_{kj}x_{j} =\sum_{j}\frac{\partial}{\partial\theta_{j}}\frac{\partial D_{KL}}{\partial\theta_{k}}x_{j} \\ &=\frac{\partial}{\partial\theta_{k}}\sum_{j}\frac{\partial D_{KL}}{\partial\theta_{j}}x_{j} \end{aligned} Hij=θjθiDKLyi=θjθjDKLxjyk=jHkjxj=jθjθkDKLxj=θkjθjDKLxj

对应代码如下:

# 计算Hessian向量积
def hessian_vector_product(p):
	# 此处的p实际输入的是x
	def hvp_fn():
		# 使用flatgrad函数计算kl_fn损失函数关于模型可训练变量的梯度,得到一个扁平化的梯度数组kl_grad_vector
		kl_grad_vector = flatgrad(kl_fn, self.model.trainable_variables)
		# 计算梯度向量与给定向量p的点积,并将结果存储在grad_vector_product中
		grad_vector_product = tf.reduce_sum(kl_grad_vector * p)
		# 返回grad_vector_product的值
		return grad_vector_product

	# 使用flatgrad函数计算hvp_fn内部函数关于模型可训练变量的梯度,得到一个扁平化的梯度数组fisher_vector_product
	fisher_vector_product = flatgrad(hvp_fn, self.model.trainable_variables).numpy()
	# 返回fisher_vector_product的值,并加上cg_damping与向量p的乘积?
	return fisher_vector_product + (self.cg_damping * p)

4、共轭梯度法求解 H − 1 g {{H^{ - 1}}g} H1g

信赖域策略优化算法TRPO强化学习-约束优化求解的第4部分提到了,我们需要使用共轭梯度法求解 H − 1 g {{H^{ - 1}}g} H1g矩阵,这等价于求解线性方程 H x = g Hx=g Hx=g,需要频繁使用到海森矩阵H和向量x的乘积,这实际上是残差的计算。

其中policy_gradient 是对替代优势函数的导数,也就是g。hessian_vector_product函数作为conjugate_grad函数的第一个输入Ax,之后再调用Ax§计算海森矩阵和向量p的乘积。(至始至终都没有单独计算过海森矩阵的值,都是计算的海森矩阵和向量的乘积

对共轭梯度法不了解的参考我的另一个博客:最速下降法、梯度下降法、共轭梯度法—理论分析与实践

代码中step_direction 其实就是要计算的 H − 1 g {{H^{ - 1}}g} H1g矩阵。

# 计算替代优势函数的导数g
policy_gradient = flatgrad(surrogate_loss, self.model.trainable_variables).numpy()
# 使用共轭梯度法
step_direction = conjugate_grad(hessian_vector_product, policy_gradient)
		# 共轭梯度法
		def conjugate_grad(Ax, b):
			"""
			Conjugate gradient algorithm
			(see https://en.wikipedia.org/wiki/Conjugate_gradient_method)
			"""
			x = np.zeros_like(b)
			r = b.copy() # Note: should be 'b - Ax(x)', but for x=0, Ax(x)=0. Change if doing warm start.
			p = r.copy()
			old_p = p.copy()
			r_dot_old = np.dot(r,r)
			for _ in range(self.cg_iters):
				z = Ax(p)
				alpha = r_dot_old / (np.dot(p, z) + 1e-8)
				old_x = x
				x += alpha * p
				r -= alpha * z
				r_dot_new = np.dot(r,r)
				beta = r_dot_new / (r_dot_old + 1e-8)
				r_dot_old = r_dot_new
				if r_dot_old < self.residual_tol:
					break
				old_p = p.copy()
				p = r + beta * p
				if np.isnan(x).any():
					print("x is nan")
					print("z", np.isnan(z))
					print("old_x", np.isnan(old_x))
					print("kl_fn", np.isnan(kl_fn()))
			return x

5、计算策略网络的更新步长

信赖域策略优化算法TRPO强化学习-约束优化求解的第3部分提到了,在TRPO算法中,其更新公式为:
θ ′ = 2 δ g T H − 1 g H − 1 g + θ \theta^{\prime}=\sqrt{\frac{2\delta}{g^{T}H^{-1}g}}H^{-1}g+\theta θ=gTH1g2δ H1g+θ

H − 1 g {{H^{ - 1}}g} H1g的数值我们可以通过共轭梯度法进行求解,那我们该如何计算更新的参数呢?

# 计算替代优势函数的导数g
policy_gradient = flatgrad(surrogate_loss, self.model.trainable_variables).numpy()
# 使用共轭梯度法,step_direction 是H-1*g
step_direction = conjugate_grad(hessian_vector_product, policy_gradient)
# 中间变量,计算的是
shs = .5 * step_direction.dot(hessian_vector_product(step_direction).T)

lm = np.sqrt(shs / self.delta) + 1e-8
fullstep = step_direction / lm

根据如上的代码:
s h s = ( 1 2 H − 1 g ) ( H H − 1 g ) T = 1 2 g T H − 1 g l m = g T H − 1 g 2 δ f u l l s t e p = H − 1 g g T H − 1 g 2 δ = 2 δ g T H − 1 g H − 1 g \begin{array}{l} shs = \left( {\frac{1}{2}{H^{ - 1}}g} \right){\left( {H{H^{ - 1}}g} \right)^T} = \frac{1}{2}{g^T}{H^{ - 1}}g\\ lm = \sqrt {\frac{{{g^T}{H^{ - 1}}g}}{{2\delta }}} \\ fullstep = \frac{{{H^{ - 1}}g}}{{\sqrt {\frac{{{g^T}{H^{ - 1}}g}}{{2\delta }}} }} = \sqrt {\frac{{2\delta }}{{{g^T}{H^{ - 1}}g}}} {H^{ - 1}}g \end{array} shs=(21H1g)(HH1g)T=21gTH1glm=2δgTH1g fullstep=2δgTH1g H1g=gTH1g2δ H1g

和更新数据一致。

6、更新步长回溯衰减

但是,信赖域策略优化算法TRPO强化学习-约束优化求解的第5部分提到了,TRPO使用了太多的近似,理论的方程并不一定是最好的,那我们我们该如何做呢?我们可以选择小于 α \alpha α的学习率多次尝试(如 0. 9 1 α 0.9^1\alpha 0.91α 0. 9 2 α 0.9^2\alpha 0.92α 0. 9 3 α 0.9^3\alpha 0.93α 0. 9 4 α 0.9^4\alpha 0.94α…),直到找到一个能够提升替代优势 L θ o l d ( θ ) {L_{{\theta _{old}}}}(\theta ) Lθold(θ)的学习率,这个过程被称为LineSearch:

	# 在给定方向上找到函数的局部最小值,最优化算法
	def linesearch(x, fullstep):
		# fval = surrogate_loss(x)
		for (_n_backtracks, stepfrac) in enumerate(self.backtrack_coeff**np.arange(self.backtrack_iters)):
			xnew = x + stepfrac * fullstep
			newfval = surrogate_loss(xnew)
			kl_div = kl_fn(xnew)
			if np.isnan(kl_div):
				print("kl is nan")
				print("xnew", np.isnan(xnew))
				print("x", np.isnan(x))
				print("stepfrac", np.isnan(stepfrac))
				print("fullstep",  np.isnan(fullstep))
			if kl_div <= self.delta and newfval >= 0:
				print("Linesearch worked at ", _n_backtracks)
				return xnew
			if _n_backtracks == self.backtrack_iters - 1:
				print("Linesearch failed.", kl_div, newfval)
		return x

7、基于LunarLander登陆器的TRPO强化学习(含PYTHON工程)

基于TensorFlow 2.10

主要代码来自github上的一位小哥,但是原来的效果太差了:
在这里插入图片描述
我稍微修改优化了一下,下面的两张图第一个是每次运行的得分,第二张为均值平滑后的曲线,因为使用了wandb,实际上运行了大约300个episode,作者原来的因该是运行500个episode的结果嘞(LR是价值网络的学习率,因为使用了自然梯度法,策略网络的学习率无需手动设置)。优化后基本能跑个200多分,算是还可以吧。
在这里插入图片描述
工程可以从最上方链接下载,喜欢就点个赞吧。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++算法学习心得五.二叉树(3)

1.合并二叉树&#xff08;617题&#xff09; 题目要求&#xff1a; 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们…

红日靶场5

目录 前言 外网渗透 信息收集 1、arp探测 2、nmap 3、nikto 4、whatweb 5、gobuster 6、dirsearch 漏洞探测 ThinKPHP漏洞 漏洞利用 get shell 内网渗透 信息收集 CS 启动&#xff01; CS连接 CS信息收集 1、hashdump 2、猕猴桃 3、端口扫描 MSF启动&…

解决“百度网盘启动缓慢”问题

最近在使用百度网盘&#xff0c;双击桌面的《百度网盘》图标&#xff0c;发现有等好几分钟&#xff0c;软件才会启动。百度网盘启动太慢了&#xff0c;后面发现百度网盘&#xff0c;使用dll注入技术&#xff0c;附加到很多不相干的进程里&#xff0c;比如附加explorer进程、附加…

构建基于RHEL8系列(CentOS8,AlmaLinux8,RockyLinux8等)的支持63个常见模块的PHP8.1.20的RPM包

本文适用&#xff1a;rhel8系列&#xff0c;或同类系统(CentOS8,AlmaLinux8,RockyLinux8等) 文档形成时期&#xff1a;2023年 因系统版本不同&#xff0c;构建部署应略有差异&#xff0c;但本文未做细分&#xff0c;对稍有经验者应不存在明显障碍。 因软件世界之复杂和个人能力…

Spark原理——逻辑执行图

逻辑执行图 明确逻辑计划的边界 在 Action 调用之前&#xff0c;会生成一系列的RDD,这些RDD之间的关系&#xff0c;其实就是整个逻辑计划 val conf new SparkConf().setMaster("local[6]").setAppName("wordCount_source") val sc new SparkContext(conf)v…

vue前端开发自学练习,Props数据传递-类型校验,默认值的设置!

vue前端开发自学练习,Props数据传递-类型校验,默认值的设置&#xff01; 实际上&#xff0c;vue开发框架的时候&#xff0c;充分考虑到了前端开发人员可能会遇到的各种各样的情况&#xff0c;比如大家经常遇到的&#xff0c;数据类型的校验&#xff0c;再比如&#xff0c;默认…

Web安全测试基础

SQL注入 当下最常用的一个攻击手段&#xff0c;就是通过SQL命令插入到Web表单中或页面请求查询字符串中&#xff0c;最终达到欺骗服务器执行恶意的SQL语句的目的&#xff0c;SQL注入一旦成功&#xff0c;轻则直接绕开服务器验证&#xff0c;直接登录成功&#xff0c;重则将服务…

php多小区智慧物业管理系统源码带文字安装教程

多小区智慧物业管理系统源码带文字安装教程 运行环境 服务器宝塔面板 PHP 7.0 Mysql 5.5及以上版本 Linux Centos7以上 统计分析以小区为单位&#xff0c;统计如下数据&#xff1a;小区总栋数、小区总户数、小区总人数、 小区租户数量、小区每月收费金额统计、小区车位统计、小…

专业130+总400+哈尔滨工业大学803信号与系统和数字逻辑电路考研经验哈工大,电子信息,信息与通信工程,信通

今年专业课803信号与系统和数字逻辑130总分400如愿考上哈尔滨工业大学电子信息&#xff08;信息与通信工程-信通&#xff09;&#xff0c;总结了一些各门课程复习心得&#xff0c;希望对大家复习有帮助。 数学一 资料选择&#xff1a; ①高数&#xff1a;张宇强化班 ②线性…

基于JavaWeb+BS架构+SpringBoot+Vue+Hadoop的物品租赁系统的设计与实现

基于JavaWebBS架构SpringBootVueHadoop的物品租赁系统的设计与实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 目  录 I 1绪 论 1 1.1开发背景 1 1.2开发目的与意义 1 1.2.1开发目…

【Redis】Redis 进阶

文章目录 1. BigKey1.1 MoreKey1.2 BigKey 2. 缓存双写一致性更新策略2.1 读缓存数据2.2 数据库和缓存一致性的更新策略2.3 canal 实现双写一致性 3. 进阶应用3.1 统计应用3.2 hyperloglog3.3 GEO3.4 bitmap 4. 布隆过滤器5. Redis 经典问题5.1 缓存预热5.2 缓存穿透5.3 缓存击…

C++ 类 对象

C 在 C 语言的基础上增加了面向对象编程&#xff0c;C 支持面向对象程序设计。类是 C 的核心特性&#xff0c;通常被称为用户定义的类型。 类用于指定对象的形式&#xff0c;是一种用户自定义的数据类型&#xff0c;它是一种封装了数据和函数的组合。类中的数据称为成员变量&a…

Anaconda定制Python编程并打包

本文主要介绍如何使用Anaconda定制一个Python编程环境并打包&#xff0c;方便编程环境迁移。 文章参考 谢作如 邱奕盛两位老师的《为信息科技教学定制一个Python编程环境》 * 开发不同的项目需要不同的库&#xff08;甚至不同版本&#xff09;&#xff0c;把所有的库安装到一…

Jenkins基础篇--添加用户和用户权限设置

添加用户 点击系统管理&#xff0c;点击管理用户&#xff0c;然后点击创建用户&#xff08;Create User&#xff09; 用户权限管理 点击系统管理&#xff0c;点击全局安全配置&#xff0c;找到授权策略&#xff0c;选择安全矩阵&#xff0c;配置好用户权限后&#xff0c;点击…

计算机网络-VLAN间通信

之前复习了VLAN的概念以及几个接口类型。VLAN在二层可以实现广播域的划分&#xff0c;VLAN间可以实现二层通信&#xff0c;但是不能实现三层通信&#xff0c;需要借助其它方式。 一、概述 实际网络部署中一般会将不同IP地址段划分到不同的VLAN。同VLAN且同网段的PC之间可直接进…

【Unity】Joystick Pack摇杆插件实现锁四向操作

Joystick Pack ​ 简介&#xff1a;一款Unity摇杆插件&#xff0c;非常轻量化 ​ 摇杆移动类型&#xff1a;圆形、横向、竖向 ​ 摇杆类型&#xff1a; Joystick描述Fixed固定位置Floating浮动操纵杆从用户触碰的地方开始&#xff0c;一直固定到触碰被释放。Dynamic动态操纵…

【设计模式】01-前言

23 Design Patterns implemented by C. 从本文开始&#xff0c;一系列的文章将揭开设计模式的神秘面纱。本篇博文是参考了《设计模式-可复用面向对象软件的基础》这本书&#xff0c;由于该书的引言 写的太好了&#xff0c;所以本文基本是对原书的摘抄。 0.前言 评估一个面向对…

孩子用什么样的灯对眼睛没有伤害?分享最合适孩子的护眼台灯

为人父母以后&#xff0c;孩子健康成长一定是摆放在首位的&#xff0c;随着孩子慢慢长大&#xff0c;步入更高的年级&#xff0c;作业课程也在随之增多。不少孩子哪怕夜色已经降临&#xff0c;仍就伏案在桌子上完成没有做完的功课&#xff0c;作为父母的我们不得不担心孩子的视…

Unity 工具 之 Azure 微软连续语音识别ASR的简单整理

Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 目录 Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 五、关键脚本 一、简单介绍 Unity 工具类&#xff0c;自己整理的一些游戏开发可能用到的模块&#x…

书客、明基、好视力护眼台灯大比拼,哪款更胜一筹?

在现代生活中&#xff0c;我们经常面对着各种电子屏幕&#xff0c;给眼睛造成了一定的压力&#xff0c;时间一长&#xff0c;会发现眼睛很疲劳。很多家长仔细观察&#xff0c;当孩子长时间处在不合适地灯光下玩耍、学习&#xff0c;会发现他们有揉眼的动作&#xff0c;这就是不…