百度工程师浅析强化学习

news/2024/5/18 23:51:19 标签: PPO, 强化学习, RL

作者 | Jane

导读

本文主要介绍了强化学习(Reinforcement Learning,RL)的基本概念以及什么是RL强化学习让智能体通过与环境的交互来学习如何做出决策,以获得最大的累积奖励。文章还介绍了策略梯度(Policy Gradient,PG)和近端策略优化(PPO)等强化学习算法。

全文7099字,预计阅读时间18分钟。

RL_10">01 强化学习(Reinforcement Learning,RL

RL_12">1.1 基本概念&什么是RL

强化学习(reinforcement learning,RL)专注于让智能体(agent)通过与环境的交互来学习如何做出决策,以使其在不断变化且不确定的环境中获得最大的累积奖励。

这个过程可以用以下几个要素来描述:

智能体(Agent):智能体是执行动作并与环境进行交互的实体。它可以是一个机器人、一个虚拟角色、一个算法等。

环境(Environment):环境包括智能体所处的所有上下文,包括外部条件、状态变量和其他影响智能体的因素。

动作(Action):智能体可以在环境中执行的操作,这些操作可能会改变环境的状态。

状态(State):状态是描述环境的当前情况的信息。智能体的决策通常依赖于当前状态。

奖励(Reward):在每个时间步,智能体执行一个动作后会获得一个奖励信号,用于指示动作的好坏。目标是最大化累积奖励。

策略(Policy):策略定义了智能体如何根据当前状态选择动作。强化学习的目标之一是找到一个最优策略,使智能体能够获得最大的累积奖励。

价值函数(Value Function):价值函数衡量了在某个状态下执行某个策略能够获得的预期累积奖励。这个函数帮助智能体评估不同状态的重要性。

学习过程强化学习的核心是智能体通过尝试不同的动作并观察奖励信号来学习适应性策略。它可以使用不同的算法,如Q-learning、Deep Q-Networks(DQN)、Policy Gradient等。

图片

△图1 强化学习过程

其中状态(State)是对整个环境的完整描述,包括了所有对智能体的决策和行动有影响的信息。状态通常用来指导智能体的决策,因为它提供了有关环境当前状态的全部信息。

观测(Observation)则是对状态的一种抽象描述,它可能会忽略或省略一些状态信息。在实际问题中,有时候智能体无法直接观测到完整的状态,而只能获取到一部分信息,即观测。这可能是因为某些信息无法直接测量,或者因为为了简化问题而对状态进行了抽象。

因此,智能体基于它的观测来做出决策,而不是直接使用完整的状态。这就需要智能体具备从观测中学习有关状态的信息的能力,以便更好地进行决策和行动。

下面举一些可应用强化学习的示例:

1、在电子游戏中,摇杆是智能体,主机是环境,显示屏上的游戏画面(像素)是观测,击杀怪物得分则是奖励。

2、在阿法狗围棋对弈中,阿法狗是智能体,人类棋手是环境,棋局是观测,围棋的输赢则是奖励。

图片

强化学习中,智能体持续与环境进行交互,采取一系列的动作,从而形成一个状态和动作的序列,这个序列被称为轨迹(trajectory):

图片

一个交互序列被称为一个回合(episode),在每个回合中,智能体根据当前状态选择动作,与环境互动,并根据奖励信号进行学习和策略优化,以获得最大化累积奖励。

这里需要注意的是,环境对我们来说是黑盒,是不可控的,它在当前状态下跳转到下一状态是服从一定的分布的,也就是说它是有一定的随机性的,那么它产出的奖励也具有了一定的随机性。我们可控的是actor的policy,即可以通过学习,让policy在面对状态 s 时采取一个能让他获得最大奖励的action。所以实际上我们的目标是最大化期望的累积奖励(expected cumulative reward)

RL_60">1.2 RL和监督学习的区别

监督学习:

1. 数据独立同分布(i.i.d.): 监督学习假设样本是从一个未知分布中独立地采样得到的,数据之间没有时序关联。

2. 标签信息: 在监督学习中,每个样本都有正确的标签信息,即模型需要学习从输入到标签的映射关系。

3. 训练目标:监督学习的目标是使模型的预测尽可能地接近真实标签,通常通过最小化预测与真实标签之间的损失函数来实现。

4. 局限性: 监督学习在需要大量标记数据、难以获取所有可能状态的标签信息或者数据之间存在时序关联的情况下可能受限。

强化学习

1. 时序关联数据强化学习中智能体的观测是时序关联的,当前的决策会影响未来的观测和奖励。

2. 延迟奖励强化学习中智能体在做出决策后不会立即得到奖励,奖励可能在未来的时间步骤才会出现,这就需要智能体通过试错来学习哪些决策导致了最终的奖励。

3. 探索与学习强化学习中智能体需要在探索和利用之间找到平衡,以发现能够最大化长期累积奖励的有效策略。

4. 超越人类表现强化学习的一个优势在于它可以在某些情况下超越人类表现,因为智能体可以自主探索环境并发现人类难以想象的优化策略。

RL_82">1.3 RL算法类型

按不同类型的智能体方法划分:

1.基于价值的智能体(Value-Based Agents): 智能体显式地学习价值函数,然后从学习到的价值函数中隐式地推导出策略,例如Q-learning和Sarsa,这些算法在训练过程中更新状态-动作值函数(Q值),然后根据Q值选择最佳动作。

2.基于策略的智能体(Policy-Based Agents): 智能体直接学习策略,即在给定状态下选择每个动作的概率分布。策略梯度(Policy Gradient)方法是一种常见的基于策略的强化学习方法,它通过梯度下降或其他优化算法来更新策略参数,以最大化累积奖励。

3.演员-评论员智能体(Actor-Critic Agents): 这是一种综合了前两种方法的智能体。演员(Actor)负责学习策略,而评论员(Critic)负责学习价值函数。演员根据策略选择动作,评论员基于环境的反馈和学习的价值函数提供有关动作的评估。这种结合可以帮助解决价值和策略学习中的某些困难问题,并且在实际应用中表现出很好的性能。

按是否需要对环境建模来划分:

1.Model-Based 方法:需要对环境建模,包括状态转移函数(描述状态之间如何转换)和奖励函数(描述在每个状态下获得的奖励)。一旦模型被建立,智能体可以在模型中进行"心算",即通过模型进行推演,而无需真正地与环境进行交互。这使得智能体可以在模型上进行试错,寻找在真实环境中表现良好的策略。然而,这种方法的关键挑战在于如何准确地建立一个模型,因为环境可能非常复杂且不确定。

2.Model-Free 方法:不需要显式地构建环境模型,而是智能体直接与环境交互,通过尝试和观察来学习策略。这种方法通常需要更多的交互时间和数据,因为智能体需要通过实际的试验来逐步改进策略。Model-free 方法可以分为基于价值和基于策略的方法,前者关注学习状态或状态-动作的价值函数,后者则直接学习策略。

在实际应用中,"model-based"方法可能需要更少的交互次数来找到较优策略,因为它在模型上进行预测和规划。然而,这个模型的建立可能会面临挑战,尤其是在复杂和不确定的环境中。"model-free"方法则更加适用于无法准确建模的情况,但可能需要更多的实际交互来学习策略。选择使用哪种方法通常取决于问题的性质以及可用的数据和计算资源。

02 策略梯度(Policy Gradient,PG)

强化学习的3个核心组成部分:actor(policy、agent)、环境奖励函数。环境和奖励函数不是我们可以控制的,我们能做的是调整Actor的策略,使得它获得最大的奖励。策略一般记作 π \pi π,如果使用深度学习来做强化学习,策略就是一个network,其参数记作 θ \theta θ。其输入是观测(或states),输出是动作空间的一个概率分布 。(同一个states,同一个env,所采去的action也是可能不同的,所以这里是概率分布,action是具有一定的随机性的)。

图片

△图2 神经网络作为策略

奖励的稀疏性和延迟性?

如果把玩一场游戏看做一个回合(episode),在一个episode里,actor不断与环境进行交互,即在s1时采取了action a1,并获得奖励r1,环境接收a1后跳转到s2,actor看到s2后采取了action a2,并获得奖励r3,…,直至达到某个条件结束游戏。这一个episode里,把环境输出 s 和actor输出 a 以及奖励输出 r 的序列组合起来,就形成了一个轨迹(trajactory):

图片

图片

△图3 最大化奖励

计算某个轨迹发生的概率就是:

图片

目标是通过调整actor的参数 θ \theta θ,最大化整个轨迹中获得的total奖励: R ( τ ) = ∑ t = 1 T r t R(\tau)=\sum_{t=1}^T r^t R(τ)=t=1Trt

但是由于采取action的随机性,环境输出states的随机性,得到的reward也是有随机性的,所以在计算中我们需要最大化的不是某个回合的奖励,而是最大化期望奖励。(直观上理解:平均意义上的奖励最大化了,那么某个具体的回合他的奖励也不会小到哪儿去),所以目标就是最大化 R ( τ ) R(\tau) R(τ)的期望值

图片

最大化问题,可以使用梯度上升(gradient ascent)来求解,使用梯度更新公式来更新参数,这就是策略梯度算法。(核心就是采样数据,更新参数,再采样数据,再更新参数。。。)

图片

接下来就是计算 R θ ˉ \bar{R_{\theta}} Rθˉ的梯度

图片

再看下 R θ ˉ = ∑ τ R ( τ ) p θ ( τ ) = E τ ∼ p θ [ R ( τ ) ] \bar{R_{\theta}} =\sum_{\tau} R(\tau)p_{\theta}(\tau) = E_{\tau \sim p_{\theta}} [R(\tau)] Rθˉ=τR(τ)pθ(τ)=Eτpθ[R(τ)] 期望的这个公式的意义:从分布 p θ ( τ ) p_{\theta}(\tau) pθ(τ)采样一个轨迹 τ \tau τ,计算 R ( τ ) R(\tau) R(τ)的期望值,就是期望奖励(expected reward)。我们要最大化期望奖励。即相当于我们穷举所有的轨迹,每个轨迹对应一个概率。但这个期望无法计算,因为没法穷举所有的轨迹。如何解决呢?=>大数定理:做N次采样,用平均代替期望。

图片

注意, p ( s 1 ) p(s_1) p(s1) p ( s t + 1 ∣ s _ t , a _ t ) p(s_{t+1}|s\_t,a\_t) p(st+1s_t,a_t)来自环境,由环境决定, θ \theta θ与 无关,因此 ∇ log ⁡ p ( s 1 ) = 0 \nabla \log p(s_1)=0 logp(s1)=0 ∇ ∑ t = 1 T log ⁡ p ( s t + 1 ∣ s _ t , a _ t ) = 0 \nabla\sum_{t=1}^{T}\log p(s_{t+1}|s\_t,a\_t)=0 t=1Tlogp(st+1s_t,a_t)=0

2.1 2个问题

接下来探讨2个问题:

1.如果采取每个action的奖励都是正的,只是有大有小,会出现什么问题?

2.整个episode里每个(s, a)pair都使用了同一个total reward合适吗?这样公平吗?一场游戏输了那里面的每个step的操作都是失败的吗?一场游戏赢了,中间就不会有失误吗?

如何解决这些问题?

解决问题1:增加baseline

如果采取每个action的奖励都是正的,只是有大有小,会出现什么问题?

例如如果所有的 r >= 10,那么r=10就相对来说是『负』的。也就是说在采样中,没有被采样到的动作的概率会被下降,但该动作不一定是不好的动作,因此奖励最好是有正有负的,这可以增加baselinse解决:

图片

通过这种方法,当\(R(\tau)-b>0\)时即总奖励\(R(\tau)>b\),就让\((s,a)\)的概率上升;当\(R(\tau)<b\),即便\(R(\tau)\)是正的,但如果它值很小也是不好的,就让\((s,a)\)的概率下降。

b b b可通过对 τ \tau τ值取期望来进行设置, 即计算 τ \tau τ的平均值,令 b ≈ E [ R ( τ ) ] b \approx E[R(\tau)] bE[R(τ)]。实践中把训练期间的\(R(\tau)\)的值不断记下来然后计算平均值,用来设置b。

解决问题2:分配合适的分数

1、reward-to-go:未来的总回报

∇ log ⁡ p θ ( a t n ∣ s t n ) \nabla\log p_{\theta}(a_{t}^n|s_t^n) logpθ(atnstn)前面的 R ( τ ) − b R(\tau)-b R(τ)b看作是它的权重,表征了动作a的好坏。这个权重原先是整个轨迹上奖励的总和,现在改成从某个时刻 t t t开始,一直到游戏结束得到的所有奖励的总和。也就是说计算某个状态-动作对的奖励的时候,不再是把整场游戏得到的奖励全部加起来,而是只计算从这个动作执行以后得到的奖励。直观上也比较容易理解:因为这场游戏在执行这个动作之前发生的事情是与执行这个动作是没有关系的,所以在执行这个动作之前得到的奖励都不能算是这个动作的贡献。我们把执行这个动作以后发生的所有奖励加起来,才是这个动作真正的贡献。

图片

2、discount有折扣的:衰减的回报

进一步,我们在未来的奖励中引入折扣因子。为什么要把未来的奖励做一个折扣呢?因为虽然在某一时刻,执行某一个动作,会影响接下来所有的结果(有可能在某一时刻执行的动作,接下来得到的奖励都是这个动作的功劳),但在一般的情况下,时间拖得越长,该动作的影响力就越小。

图片

折扣因子 γ ∈ [ 0 , 1 ] \gamma \in [0,1] γ[0,1],一般设置为0.9或0.99。其中 G t = r t n + γ r t + 1 n + γ 2 r t + 2 n + γ 3 r t + 3 n + . . . + γ T n − t r T n n = r t n + γ G t + 1 G_t = r_t^n + \gamma r_{t+1}^n + \gamma^2 r_{t+2}^n + \gamma^3 r_{t+3}^n +...+ \gamma^{T_n-t} r_{T_n}^n =r_t^n+\gamma G_{t+1} Gt=rtn+γrt+1n+γ2rt+2n+γ3rt+3n+...+γTntrTnn=rtn+γGt+1

。从 G t G_t Gt的展开式可以看到未来的奖励在离t时刻越远时,折扣就越多,这样显然更合理。

2.2 重要性采样

策略梯度算法的缺点:

1.采样效率低:每次当前actor策略与环境交互收集到一批训练资料后,策略一更新,这批数据就不能再使用了,因为actor已经发生了变化,需要重新拿新的actor再次与环境与做交互,拿到新的一批训练资料。而与环境做交互是非常费时间的(相对于在GPU上作训练),整个过程可能大部分时间在做交互收集(采样)数据。

2.迭代步长过长,或步长/学习率难以确定,训练不稳定。

采样效率低的问题,考虑下能不能用另外一个策略 π θ ′ \pi_{\theta'} πθ和一个actor θ ′ \theta' θ与环境交互,用 θ ′ \theta' θ采样到的数据去训练 θ \theta θ,并且利用这些数据去让 θ \theta θ多次执行梯度上升,多次更新参数, 这样就能大幅提升效率。

如何实现这个过程呢?

图片

这个公式可以看做是先从 q q q里面从采样 x x x,再计算 f ( x ) p ( x ) q ( x ) f(x) \frac{p(x)}{q(x)} f(x)q(x)p(x),再取期望值。所以即便没法从 p p p 里面采样数据,但从 q q q里面采样数据,我们依然可以通过这种变换来计算从采样 p p p代入 x x x以后的期望值。因为是从 q q q采样数据,所以我们从 q q q采样出来的每一笔数据,都需要乘一个 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x)来修正这两个分布的差异,这个修正银子就叫做重要性权重(importance weight)。

对于用 actor θ \theta θ采样出 s t s_t st a t a_t at的状态-动作的对时,我们使用状态-动作对的优势(advantage): A θ ( s t , a t ) A_{\theta}(s_t,a_t) Aθ(st,at), 来表示累积奖励减去基线这一项,这一项代表的是在状态 s t s_t st采取动作 a t a_t at的好坏(有没有优势)。如果 A θ ( s t , a t ) A_{\theta}(s_t,a_t) Aθ(st,at)是正的,就要增大概率;如果是负的,就要减小概率。

那么回到上面策略梯度的采样效率的问题,就可以考虑,由一个 p θ ’ p_{\theta’} pθ也就是另一个actor(其实就是需要更新的actor的一个副本)去和环境做交互然后收集采样数据,再用这个数据去多次更新 p θ p_{\theta} pθ以提升采样效率。也就是说利用重要性采样,就可以把公式转化为:

图片

上式第3行利用条件概率公式展开,最后去掉 p θ ( s t ) p_{\theta}(s_t) pθ(st) p t h e t a ‘ ( s t ) p_{\\theta‘}(s_t) ptheta(st) 得到最终的简化的结果。所以根据梯度公式我们可以写出目标函数:

图片

重要性采样解决了采样效率的问题,但也可能会引入新问题。我们再来看下重要性采样的公式: E x ∼ p [ f ( x ) ] = E x ∼ q [ f ( x ) p ( x ) q ( x ) ] E_{x\sim p}[f(x)]=E_{x\sim q}[f(x)\frac{p(x)}{q(x)}] Exp[f(x)]=Exq[f(x)q(x)p(x)],虽然它们期望是相等的,但它们的方差如何呢?根据方差计算公式 V a r [ X ] = E [ X 2 ] − ( E [ X ] ) 2 {Var} [X]=E[X^2]-(E[X])^2 Var[X]=E[X2](E[X])2,分别计算出 f ( x ) f(x) f(x) f ( x ) p ( x ) q ( x ) f(x)\frac{p(x)}{q(x)} f(x)q(x)p(x)的方差:

图片

可以看到2个方差的第一项是不同的, Var ⁡ x ∼ q [ f ( x ) p ( x ) q ( x ) ] \operatorname{Var}_{x \sim q}\left[f(x) \frac{p(x)}{q(x)}\right] Varxq[f(x)q(x)p(x)]的第一项多乘了 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x),如果 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x)差距很大, p θ ( a t ∣ s t ) p_{\theta}\left(a_{t} | s_{t}\right) pθ(atst)的方差就会很大。也就是说如果 p θ ( a t ∣ s t ) p_{\theta}\left(a_{t} | s_{t}\right) pθ(atst) p θ ′ ( a t ∣ s t ) p_{\theta'}\left(a_{t} | s_{t}\right) pθ(atst) 相差太大,即这两个分布相差太多,重要性采样的结果就会不好。也就是说通过有限次的采样,如果 p ( x ) p(x) p(x) q ( x ) q(x) q(x)的差距过大,我们是无法保证这个期望的等式在采样数据上一定成立。

怎么避免它们相差太多呢?这就是TRPO和PPO要做的事情。

2.3 TRPO

**信任区域策略优化(trust region policy optimization,TRPO)**就是对 θ \theta θ θ ′ \theta' θ进行一个约束,让它们的 KL 散度小于 :

图片

虽然TRPO解决了分布差距不会很大的问题,但计算有难度(引入约束项,使用拉格朗日的对偶法处理后,采用共轭梯度法进行优化),因此我们一般使用在实现上更容易但性能差不多的PPO

PPO_224">03 近端策略优化(PPO

PPO1PPOpenalty_226">3.1 PPO1(近端策略优化惩罚(PPO-penalty))

PPO(ProximalPolicyOptimization) 解决TRPO计算量大的问题,不是把约束放在条件里,而是直接放到目标函数里:

图片

图片

其中惩罚项 β K L ( θ , θ k ) \beta \mathrm{KL}\left(\theta, \theta^{k}\right) βKL(θ,θk)称之为自适应KL散度(adaptive KL divergence), β \beta β可以动态调整,其调整策略为:

  • 如果 K L ( θ , θ k ) > K L m a x KL(\theta,\theta^k)>KL_{max} KL(θ,θk)>KLmax,则增加 β \beta β

  • 如果 K L ( θ , θ k ) < K L m i n KL(\theta,\theta^k)<KL_{min} KL(θ,θk)<KLmin,则减小 β \beta β

PPO2PPOclip_241">3.2 PPO2(近端策略优化裁剪(PPO-clip))

如果我们觉得计算 KL 散度很复杂,那么还有一个PPO2算法,PPO2 即近端策略优化裁剪。近端策略优化裁剪的目标函数里面没有 KL 散度,其要最大化的目标函数为

图片

  • 裁剪(clip)函数,裁剪函数是指,在括号里面有3项,如果第一项小于第二项,那就输出 1 − ε 1-\varepsilon 1ε ;第一项如果大于第三项,那就输出 1 + ε 1+\varepsilon 1+ε

  • ε \varepsilon ε是一个超参数,是我们要调整的,可以设置成 0.1 或 0.2 。

为什么这个式子就可以做到 p θ p_{\theta} pθ p θ ′ p_{\theta'} pθ差距不会太大呢?

图片

  • 如果 A > 0 A>0 A>0,也就是某一个 ( a t , s t ) (a_t,s_t) (at,st)是好的,则我们希望增大这个状态-动作pair的概率。也就是想让 p θ ( a t ∣ s t ) p_{\theta}(a_t∣s_t) pθ(atst)越大越好,但它与 p θ k ( a t ∣ s t ) p_{\theta^k}(a_t∣s_t) pθk(atst)的比值不能超过 1 + ε 1+\varepsilon 1+ε

  • 如果 A < 0 A<0 A<0,也就是某一个 ( a t , s t ) (a_t,s_t) (at,st)是不好的,那则我们希望把 p θ ( a t ∣ s t ) p_{\theta}(a_t∣s_t) pθ(atst)减小,当减到比值是 1 − ε 1-\varepsilon 1ε的时候停止。

通用这个clip的限制,我们不会让 p θ ( a t ∣ s t ) p_{\theta}(a_t∣s_t) pθ(atst) p θ k ( a t ∣ s t ) p_{\theta^k}(a_t∣s_t) pθk(atst)差距太大,并且实现这个其实也很简单,如下:

ratios = paddle.exp(cur_batch_log_probs - batch_log_probs.detach())
surr1 = ratios * A_k
surr2 = paddle.clip(ratios, 1-self.clip, 1-self.clip) * A_k
actor_loss = -paddle.minimum(surr1, surr2) # 负号,把最大化问题转化为最小化问题ratios = paddle.exp(cur_batch_log_probs - batch_log_probs.detach())
surr1 = ratios * A_k
surr2 = paddle.clip(ratios, 1-self.clip, 1-self.clip) * A_k
actor_loss = -paddle.minimum(surr1, surr2) # 负号,把最大化问题转化为最小化问题

04 总结

图片

——END——

参考资料:

[1]https://datawhalechina.github.io/easy-rl/#/

[2]https://speech.ee.ntu.edu.tw/~hylee/ml/2020-spring.php

推荐阅读:

浅谈统一权限管理服务的设计与开发

百度APP iOS端包体积50M优化实践(五) HEIC图片和无用类优化实践

百度知道上云与架构演进

百度APP iOS端包体积50M优化实践(四)代码优化

百度App启动性能优化实践篇


http://www.niftyadmin.cn/n/4961421.html

相关文章

RestFul的风格是什么

RestFul的风格是什么&#xff1f; 当我们谈论RESTful风格时&#xff0c;它指的是一种设计和构建网络应用程序的原则和约定。以下是RESTful风格的一些主要特点&#xff1a; 资源&#xff1a;将应用程序的功能封装为资源&#xff0c;每个资源都有一个唯一的标识符&#xff08;U…

SpringBoot读取Nacos配置文件

断点到ClientWorker类的getServerConfig方法&#xff0c;反向Debug。

算法-滑动窗口-串联所有单词的子串

算法-滑动窗口-串联所有单词的子串 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/substring-with-concatenation-of-all-words/ 1.2 题目描述 2 滑动窗口Hash表 2.1 解题思路 构建一个大小为串联子串的总长的滑动窗口为每个words中的子串创建一个hash表, <子…

Docker容器:docker consul的注册与发现及consul-template守护进程

文章目录 一.docker consul的注册与发现介绍1.什么是服务注册与发现2.什么是consul3.consul提供的一些关键特性4.数据流向 二.consul部署1.consul服务器&#xff08;192.168.198.12&#xff09;&#xff08;1&#xff09;建立 Consul 服务&#xff08;2&#xff09;查看集群信息…

257_C++_异步io的方式测试卡线程的问题,一旦线程卡住超过5秒,就会进行打印【boost::asio::io_context】

#if defined(N88SX) static std::once_flag flag; static void *work_thread(void *pvParm) {prctl(PR_SET_NAME,

softmax回归+损失函数+图片分类数据集

知识点积累 # mnist_train[0][0].shape&#xff0c;mnist_train[0] 表示 MNIST 训练集中的第一个样本&#xff0c;而 [0] 则表示取该样本的第一个元素。 # 在 MNIST 数据集中&#xff0c;每个样本由一对输入特征和标签组成。因此&#xff0c;mnist_train[0] 表示第一个样本&am…

Aspose.Tasks for .NET V23Crack

Aspose.Tasks for .NET V23Crack 改进了大型项目的内存占用。 添加了API&#xff0c;允许您在应用程序无法访问系统字体文件夹时指定用户的字体文件夹。 Aspose.Tasksfor.NET是处理MicrosoftProject文件的可靠的项目管理API。API支持在不依赖Microsoft Project的情况下读取、写…

java反序列化泛型中json对象

使用 jackson的objectMapper 来实现 import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMa…