将强化学习重新引入 RLHF

我们很高兴在 TRL 中介绍 RLOO (REINFORCE Leave One-Out) 训练器。作为一种替代 PPO 的方法,RLOO 是一种新的在线 RLHF 训练算法,旨在使其更易于访问和实施。特别是, RLOO 需要的 GPU 内存更少,并且达到收敛所需的挂钟时间也更短。如下面的图表所示:

  1. 🤑根据模型大小,RLOO 使用的 vRAM 比 PPO 少大约 50-70%;

  2. 🚀对于 1B 参数模型,RLOO 的运行速度比 PPO 快 2 倍,对于 6.9B 参数模型,RLOO 的运行速度比 PPO 快 3 倍

  3. 🔥在响应胜率 (由 GPT4 判断) 方面,RLOO 与 PPO 相当,并且始终优于 DPO 等流行的离线方法。

通过 RLOO,我们将强化学习重新引入 RLHF,使社区能够更轻松地探索在线 RL 方法。这令人兴奋,因为越来越多的研究表明,在线 RL 比 DPO 等离线方法更有效 (https://arxiv.org/abs/2402.04792, https://arxiv.org/abs/2405.08448)。

efd3918d3cd8b4c0c7012086cd852c1f.png

d9521f374da627a7da9af0f26521e4e1.png

这篇博客将解释 RLOO 训练器的背后的动机,它是如何工作的,以及如何在 TRL 中使用它。

动机

PPO 是一种有效的在线 RLHF 训练算法,用于训练最先进的模型,如 GPT-4。然而,由于其对 GPU 内存的高要求,PPO 在实际使用中可能相当具有挑战性。特别是,PPO 需要将模型的 4 个副本加载到内存中: 1) 策略模型,2) 参考策略模型,3) 奖励模型,以及 4) 价值模型,如下面的图所示。PPO 还有许多微妙的实现细节,这些细节可能很难正确把握 (Engstrom 等人; 2020,Huang 等人 2022)。

  • Engstrom 等人; 2020https://openreview.net/forum?id=r1etN1rtPB

  • Huang 等人 2022https://iclr-blog-track.github.io/2022/03/25/ppo-implementation-details/

d60d6d6ff3bb3b755abe997f57520f8b.png

在 Cohere 的一篇新论文中,Ahmadian 等人 (2024)重新审视了 RLHF 训练的基础,并提出了一种更简洁的方法,称为 RLOO,这是一种新的在线训练算法。RLOO 只需要将模型的 3 个副本加载到内存中: 1) 策略模型,2) 参考策略模型,以及 3) 奖励模型,如上图所示。

  • Ahmadian 等人 (2024)https://cohere.com/research/papers/back-to-basics-revisiting-reinforce-style-optimization-for-learning-from-human-feedback-in-llms-2024-02-23

重要的是,RLOO 需要更少的内存,这意味着它更容易:

  1. 在不出现 OOMs (内存不足错误) 的情况下运行

  2. 能够加载更大的批量大小

  3. 运行更高效且更快。

此外,RLOO 将整个补全 token 作为单一动作进行建模,如下图所示。在下一节中,我们将通过代码片段进一步详细介绍。

83c0da9f568d2e5f256bbe1a0f477567.png

RLOO 是如何工作的

RLOO 和 PPO 有几个共同的步骤:

  1. 策略模型会生成一些补全 token ,并获取当前策略和参考策略下的每个 token 的对数概率。

  2. 然后我们计算每个 token 的 KL 惩罚,作为当前策略和参考策略下对数概率的差异。

  3. 接着我们从奖励模型中获得整个补全的得分。

从这里开始,常规的 PPO 和 RLOO 在方法上有所不同。RLOO 有几个关键想法。首先,它将 整个模型补全 视为单一动作,而常规 PPO 将 每个补全 token 视为单独的动作。通常,只有 EOS token 获得真正的奖励,这非常稀疏。常规 PPO 会将奖励归因于 EOS token,而 RLOO 会将 EOS 奖励归因于整个补全,如下所示。

from torch import Tensor
response = Tensor([4., 5., 6.])
per_token_logprobs = Tensor([-12.3, -8.3, -2.3])
reference_per_token_logprobs = Tensor([-11.3, -8.4, -2.0])
kl = per_token_logprobs - reference_per_token_logprobs
score_from_rm = 1.0
print(f"{kl=}") # kl=tensor([-1.0000, 0.1000, -0.3000])
per_token_reward = kl.clone()
per_token_reward[-1] += score_from_rm # assume last token is the EOS token
print(f"{per_token_reward=}") # per_token_reward=tensor([-1.0000, 0.1000, 0.7000])
print(f"{score_from_rm=}") # score_from_rm=1.0
print("#### Modeling each token as an action")
for action, reward in zip(response, per_token_reward):
    print(f"{action=}, {reward=}")
# action=tensor(4.), reward=tensor(-1.)
# action=tensor(5.), reward=tensor(0.1000)
# action=tensor(6.), reward=tensor(0.7000)
print("#### Modeling the entire response as an action")
entire_generation_reward = per_token_reward.sum()
print(f"action='entire completion', reward={entire_generation_reward}")
# action='entire completion', reward=-0.2000 (-1 + 0.1 + 0.7)

其次,RLOO 使用 REINFORCE 损失,它基本上将 (奖励 - 基线) 与动作的对数概率相乘。在这里,我们突出了每个 token 的 REINFORCE 损失与整个补全的 REINFORCE 损失之间的区别。请注意,对于 PPO 的损失,我们还需要基于价值模型和广义优势估计 (GAE)来计算优势。

  • 广义优势估计 (GAE)https://arxiv.org/abs/1506.02438

from torch import Tensor
response = Tensor([4., 5., 6.])
per_token_logprobs = Tensor([-12.3, -8.3, -2.3])
reference_per_token_logprobs = Tensor([-11.3, -8.4, -2.0])
kl = per_token_logprobs - reference_per_token_logprobs
score_from_rm = 1.0
print(f"{kl=}") # kl=tensor([-1.0000, 0.1000, -0.3000])
per_token_reward = kl.clone()
per_token_reward[-1] += score_from_rm # assume last token is the EOS token
print(f"{per_token_reward=}") # per_token_reward=tensor([-1.0000, 0.1000, 0.7000])
print(f"{score_from_rm=}") # score_from_rm=1.0
print("#### Modeling each token as an action")
for action, reward in zip(response, per_token_reward):
    print(f"{action=}, {reward=}")
# action=tensor(4.), reward=tensor(-1.)
# action=tensor(5.), reward=tensor(0.1000)
# action=tensor(6.), reward=tensor(0.7000)
print("#### Modeling the entire response as an action")
entire_generation_reward = per_token_reward.sum()
print(f"action='entire completion', reward={entire_generation_reward}")
# action='entire completion', reward=-0.2000 (-1 + 0.1 + 0.7)
baseline = Tensor([0.2, 0.3, 0.4]) # dummy baseline
print("#### Modeling each token as an action")
advantage = per_token_reward - baseline
per_token_reinforce_loss = per_token_logprobs * advantage
print(f"{advantage=}") # advantage=tensor([-1.2000, -0.2000, 0.3000])
print(f"{per_token_reinforce_loss=}") # per_token_reinforce_loss=tensor([14.7600, 1.6600, -0.6900])
print(f"{per_token_reinforce_loss.mean()=}") # per_token_reinforce_loss.mean()=tensor(5.2433)

print("#### Modeling the entire response as an action")
advantage = entire_generation_reward - baseline.sum()
reinforce_loss = per_token_logprobs.sum()* advantage
print(f"{advantage=}") # advantage=tensor(-1.1000)
print(f"{reinforce_loss=}") # reinforce_loss=tensor(25.1900)

第三,RLOO 聪明地计算基线。注意我们上面使用了一个虚拟基线。在实际操作中,RLOO 使用批次中所有其他样本的奖励作为基线。下面是一个有 3 个提示和每个提示 4 个补全的例子。我们通过平均同一提示的所有其他补全的奖励来计算每个补全的基线。

import torch
local_batch_size = 3
rloo_k = 4

rlhf_reward = torch.tensor([
    1, 2, 3, # first rlhf reward for three prompts
    2, 3, 4, # second rlhf reward for three prompts
    5, 6, 7, # third rlhf reward for three prompts
    8, 9, 10, # fourth rlhf reward for three prompts
]).float() # here we have 3 prompts which have 4 completions each

# slow impl
baseline = (rlhf_reward.sum(0) - rlhf_reward) / (rloo_k - 1)
advantages = torch.zeros_like(rlhf_reward)
for i in range(0, len(advantages), local_batch_size):
    other_response_rlhf_rewards = []
    for j in range(0, len(advantages), local_batch_size):
        if i != j:
            other_response_rlhf_rewards.append(rlhf_reward[j : j + local_batch_size])
    advantages[i : i + local_batch_size] = rlhf_reward[i : i + local_batch_size] - torch.stack(
        other_response_rlhf_rewards
    ).mean(0)
assert (1 - (2 + 5 + 8) / 3 - advantages[0].item()) < 1e-6
assert (6 - (3 + 2 + 9) / 3 - advantages[7].item()) < 1e-6

# vectorized impl
rlhf_reward = rlhf_reward.reshape(rloo_k, local_batch_size)
baseline = (rlhf_reward.sum(0) - rlhf_reward) / (rloo_k - 1)
vec_advantages = rlhf_reward - baseline
torch.testing.assert_close(vec_advantages.flatten(), advantages)

向 Arash Ahmadian 致谢,他提供了上述优势计算的向量化实现。

开始使用 TRL 的 RLOO

要开始使用 RLOO,你可以通过 pip install --upgrade trl 安装 TRL 的最新版本,并导入 RLOOTrainer。下面是一个展示一些高级 API 使用的简短代码片段。你可以随时查阅这些文档:

  • https://hf.co/docs/trl/main/en/rloo_trainer

  • https://hf.co/docs/trl/main/en/ppov2_trainer

from transformers import (
    AutoModelForCausalLM,
    AutoModelForSequenceClassification,
    AutoTokenizer,
)

from trl.trainer.rloo_trainer import RLOOConfig, RLOOTrainer
from trl.trainer.utils import SIMPLE_QUERY_CHAT_TEMPLATE

base_model_name = "EleutherAI/pythia-1b-deduped"
tokenizer = AutoTokenizer.from_pretrained(base_model_name, padding_side="left")
tokenizer.add_special_tokens({"pad_token": "[PAD]"})
if tokenizer.chat_template is None:
    tokenizer.chat_template = SIMPLE_QUERY_CHAT_TEMPLATE
reward_model = AutoModelForSequenceClassification.from_pretrained(base_model_name, num_labels=1)
ref_policy = AutoModelForCausalLM.from_pretrained(base_model_name)
policy = AutoModelForCausalLM.from_pretrained(base_model_name)

train_dataset = ... # make sure to have columns "input_ids"
eval_dataset = ...

trainer = RLOOTrainer(
    config=RLOOConfig(
        per_device_train_batch_size=1,
        gradient_accumulation_steps=64,
        total_episodes=30000,
    ),
    tokenizer=tokenizer,
    policy=policy,
    ref_policy=ref_policy,
    reward_model=reward_model,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset,
)
trainer.train()

这是一个跟踪权重和偏差实验的例子:

  • 案例地址https://wandb.ai/huggingface/trl/runs/dd2o3g35

e5872e2096619f3d17be2e9ea679479c.png

在编写 RLOO 和 PPOv2 实现时,我们强调使模型开发的透明度更容易提升。特别是,我们已经增强了文档,包括对记录指标的解释以及阅读和调试这些指标的操作指南。例如,我们建议在训练期间密切监控 objective/rlhf_reward,这是 RLHF 训练的最终目标。

e45a55e6fd3bcf4ea1419240b9dbf464.png 3ce58b42711874d00825c9f429d9f975.png

为了帮助可视化训练进度,我们定期记录模型的一些示例补全。这里是一个补全的例子。在一个权重和偏差跟踪运行的示例中,它看起来像下面这样,允许你看到模型在不同训练阶段的响应。默认情况下,我们在训练期间生成 –num_sample_generations 10,但你可以自定义生成的数量。

  • 示例地址https://wandb.ai/huggingface/trl/runs/dd2o3g35

7b41d8cc1f9d2289272b347cd0304e0e.gif

我们如何在 TRL 中实现 RLOO 训练器

我们基于新的实验性 PPOv2Trainer 实现了 RLOO 训练器,后者又是基于 https://arxiv.org/abs/2403.17031。有趣的是,我们实现的 RLOO 训练器仍然使用 PPO 损失。这是因为 REINFORCE 的损失是 PPO 的一个特例 (https://arxiv.org/abs/2205.09123)。请注意,即使对数概率明确出现在 REINFORCE 损失中,它也隐含在 PPO 损失中。眼见为实,所以让我们用一个简单的例子来证明这一点。

import torch.nn.functional as F
from torch import LongTensor, Tensor, gather, no_grad

action = LongTensor([1])
advantage = Tensor([1.0])
logits = Tensor([[1.0, 2.0, 1.0, 1.0]])
logits.requires_grad = True
all_logprob = F.log_softmax(logits, dim=-1)
with no_grad():
    old_logprob = gather(all_logprob, 1, action.unsqueeze(-1)).squeeze(-1)
logprob = gather(all_logprob, 1, action.unsqueeze(-1)).squeeze(-1)
ratio = (logprob - old_logprob).exp()
ppo_loss = (ratio * advantage).mean() # [πθ(at | st) / πθ_old(at | st)* At]
# when the πθ and πθ_old are the same, the ratio is 1, and PPO's clipping has no effect
ppo_loss.backward()
print(f"{logits.grad=}") # tensor([[-0.1749, 0.5246, -0.1749, -0.1749]])
logits2 = Tensor([[1.0, 2.0, 1.0, 1.0]])
logits2.requires_grad = True
all_logprob2 = F.log_softmax(logits2, dim=-1)
logprob2 = gather(all_logprob2, 1, action.unsqueeze(-1)).squeeze(-1)
reinforce_loss = logprob2 * advantage # [log πθ(at | st)* At]
reinforce_loss.mean().backward()
print(f"{logits2.grad=}") # tensor([[-0.1749, 0.5246, -0.1749, -0.1749]])

实验

为了验证 RLOO 实现的有效性,我们在 Pythia 1B 和 6.9B 模型上进行了实验,并在这里发布了训练后的检查点:

  • 检查点链接https://hf.co/collections/vwxyzjn/rloo-ppov2-tl-dr-summarize-checkpoints-66679a3bfd95ddf66c97420d

我们从Huang 等人,2024直接获取 SFT / RM 模型。为了评估,我们使用 vLLM 加载检查点,并使用 GPT4 作为评判模型来评估生成的 TL;DR 与参考 TL;DR 的对比。我们还查看了 GPU 内存使用情况和运行时间,正如博客开头所示的图表。要重现我们的工作,请随时查看我们文档中的命令:

  • Huang 等人,2024https://arxiv.org/abs/2403.17031

  • https://hf.co/docs/trl/main/en/rloo_trainer#benchmark-experiments

  • https://hf.co/docs/trl/main/en/rloo_trainer#benchmark-experiments

关键结果如下:

  • 🚀高性能 RLOO 检查点: 使用 GPT4 作为评判模型,6.9B 检查点获得了 78.7% (k=2) 的偏好率,这甚至超过了原始论文中报告的最佳性能 77.9% (k=4) 和 74.2 (k=2)。这是一个很好的迹象,表明我们的 RLOO 训练按预期工作。

    论文地址https://arxiv.org/abs/2402.14740

  • RLOO 1B 检查点的胜率为 40.1%,而 SFT 检查点的胜率为 21.3%。这是一个很好的迹象,表明 RLOO 训练按预期工作。

  • 🤑 减少 GPU 内存并运行更快: RLOO 训练使用更少的内存并运行更快,使其成为在线 RL 训练中非常有用的算法。

数值稳定性: 黑暗面

尽管 RLOO 在性能和计算效率方面有优势,但我们想要强调一些数值问题。具体来说,生成过程中获得的响应对数概率与 bf16 下训练前向传递期间获得的对数概率在数值上略有不同。这给 PPO 和 RLOO 都带来了问题,但对于 RLOO 来说,问题更严重,如下所述。

例如,假设我们正在为两个序列生成 10 个 token。在 fp32 精度下,输出如下所示,其中 ratio = (forward_logprob - generation_logprob).exp() ,这是 PPO 用来裁剪的。在第一个周期和第一个小批量中,比率应该是完全相同的,因为模型还没有进行任何更新:

generation_logprob=tensor([[ -0.1527, -0.2258, -3.5535, -3.4805, -0.0519,
             -2.3097, -2.0275, -0.4597, -0.1687, -0.0000],
        [ -0.1527, -0.2258, -5.2855, -0.1686, -8.4760,
             -4.3118, -1.0368, -0.8274, -1.6342, -2.6128]],
       device='cuda:0')
forward_logprob=tensor([[-0.1527, -0.2258, -3.5535, -3.4805, -0.0519, -2.3097, -2.0275, -0.4597,
         -0.1687],
        [-0.1527, -0.2258, -5.2855, -0.1686, -8.4760, -4.3118, -1.0368, -0.8274,
         -1.6342]], device='cuda:0', grad_fn=<SqueezeBackward1>)
ratio=tensor([[1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000],
        [1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000]],
       device='cuda:0', grad_fn=<ExpBackward0>)
ratio.mean()=0.9999998211860657
ratio.std()=6.592738373001339e-06
ratio.max()=1.0000133514404297
ratio.min()=0.9999887943267822

然而,在 bf16 精度下,我们得到结果:

generation_logprob=tensor([[ -0.1426, -0.1904, -3.5938, -3.4688, -0.0618,
             -2.3906, -2.0781, -0.4375, -0.1562, -0.0000],
        [ -0.1426, -0.1904, -5.2812, -0.1641, -8.5625,
             -4.2812, -1.0078, -0.8398, -1.5781, -2.5781]],
       device='cuda:0', dtype=torch.bfloat16)
forward_logprob=tensor([[-0.1445, -0.1670, -3.5938, -3.5156, -0.0554, -2.2969, -1.9688, -0.5273,
         -0.1953],
        [-0.1445, -0.1670, -5.2812, -0.1533, -8.5625, -4.3125, -1.0000, -0.7852,
         -1.6641]], device='cuda:0', dtype=torch.bfloat16,
       grad_fn=<SqueezeBackward1>)
ratio=tensor([[1.0000, 0.9766, 1.0000, 1.0469, 0.9922, 0.9102, 0.8945, 1.0938, 1.0391],
        [1.0000, 0.9766, 1.0000, 0.9883, 1.0000, 1.0312, 0.9922, 0.9453, 1.0859]],
       device='cuda:0', dtype=torch.bfloat16, grad_fn=<ExpBackward0>)
ratio.mean()=1.0
ratio.std()=0.051025390625
ratio.max()=1.09375
ratio.min()=0.89453125

和在 fp16 精度下,我们得到的结果

generation_logprob=tensor([[ -0.1486, -0.2212, -3.5586, -3.4688, -0.0526,
             -2.3105, -2.0254, -0.4629, -0.1677, -0.0000],
        [ -0.1486, -0.2212, -5.2852, -0.1681, -8.4844,
             -4.3008, -1.0322, -0.8286, -1.6348, -2.6074]],
       device='cuda:0', dtype=torch.float16)
forward_logprob=tensor([[-0.1486, -0.2212, -3.5586, -3.4805, -0.0529, -2.3066, -2.0332, -0.4629,
         -0.1676],
        [-0.1486, -0.2212, -5.2852, -0.1682, -8.4766, -4.3008, -1.0322, -0.8281,
         -1.6299]], device='cuda:0', dtype=torch.float16,
       grad_fn=<SqueezeBackward1>)
ratio=tensor([[1.0000, 1.0000, 1.0000, 1.0117, 1.0000, 0.9961, 1.0078, 1.0000, 1.0000],
        [1.0000, 1.0000, 1.0000, 1.0000, 0.9922, 1.0000, 1.0000, 0.9995, 0.9951]],
       device='cuda:0', dtype=torch.float16, grad_fn=<ExpBackward0>)
ratio.mean()=1.0
ratio.std()=0.00418853759765625
ratio.max()=1.01171875
ratio.min()=0.9921875

请注意, bf16 的比率由于某种原因非常不稳定。当比率变得很大时,PPO 的裁剪系数 = 0.2 开始发挥作用, 取消 那些比率大于 1.2 或小于 0.8 的 token 的梯度。对于 RLOO,这个问题更为极端,因为我们看到的是 (forward_logprob.sum(1) - generation_logprob.sum(1)).exp() = [ 1.0625, 12.1875] ,这意味着整个第二个序列的梯度被取消了。

在实际操作中,我们注意到 PPO 取消了大约 3% 的批次数据的梯度,而 RLOO 取消了大约 20-40% 的批次数据。从理论上讲,当不使用小批量时,RLOO 应该取消 0 %的批次数据。重要的是,我们观察到,一旦我们增加了在生成新批次之前的梯度步骤数 (通过 num_ppo_epochs 和 num_mini_batches),RLOO 的裁剪比率并没有显著变化; 这提供了实证证据,表明裁剪比率确实是由于 bf16 的数值问题,而不是因为行为和最新策略有很大不同,正如论文中所定位的。

要了解有关最新问题更新的更多信息,请查看GitHub issue: #31267。

  • Issue #31267 链接https://github.com/huggingface/transformers/issues/31267

结论

TRL 中引入的 RLOO (REINFORCE Leave One-Out) 训练器是在线 RLHF 训练中一个令人兴奋的算法,它提供了一个比 PPO 更易访问和高效的替代方案。通过减少 GPU 内存使用和简化训练过程,RLOO 使得可以使用更大的批量大小和更快的训练时间。我们的实验表明,RLOO 在响应胜率方面与 PPO 竞争,并且优于 DPO 检查点,使其成为有效的在线 RLHF 的有力工具。查看我们的文档来开始使用吧!

  • https://hf.co/docs/trl/main/en/rloo_trainer

  • https://hf.co/docs/trl/main/en/ppov2_trainer

致谢和感谢

我们要感谢 Lewis Tunstall, Sara Hooker, Omar Sanseviero 和 Leandro Von Werra 对这篇博客提供的宝贵反馈。


原文链接: https://hf.co/blog/putting_rl_back_in_rlhf_with_rloo

原文作者: Shengyi Costa Huang, Arash Ahmadian

译者: innovation64

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

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

相关文章

01 Shell编程规范与变量

1、Shell脚本概述 在一些复杂的Linux维护工作中&#xff0c;大量的重复性的输入和交互操作不仅费力费时&#xff0c;而且容易出错&#xff0c;而编写一个恰到好处的Shell脚本程序&#xff0c;可以批量处理、自动化地完成一系列维护任务&#xff0c;大大减轻管理员的负担。 Sh…

【CT】LeetCode手撕—54. 螺旋矩阵

目录 题目1- 思路2- 实现⭐54. 螺旋矩阵——题解思路 3- ACM实现 题目 原题连接&#xff1a;92. 反转链表 II 1- 思路 模式识别&#xff1a;螺旋矩阵 ——> 用四个指针来顺时针遍历 2- 实现 ⭐54. 螺旋矩阵——题解思路 class Solution {public List<Integer> spir…

RuoYi Swagger请求401

问题描述&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 使用ruoyi-vue分离版&#xff0c;访问swagger&#xff0c;发现接口都调用失败&#xff1a;401 解决方案&#xff1a; 最终解决问题如下步骤&#xff1a; 1、 调用swagger中的接口&#xff0c;报错&a…

QT MQTT (二)编译与集成

一、QT MQTT 提供 MQTT 客户端服务的 Qt 专用库基于标准化发布 / 订阅协议&#xff0c;用于在设备和组件之间可靠地共享数据。MQTT 是为保证状态正确性、满足高安全标准和交换最小数据而设计的协议&#xff0c;因此被广泛应用于各种分布式系统和物联网解决方案中。 Qt开发MQT…

修改源码,打patch包,线上环境不生效

1.首先看修改的源码文件是否正确 在node_modules中&#xff0c;找对应的包&#xff0c;然后查看包中package.json 的main和module。如果用require引入&#xff0c;则修改lib下面的组件&#xff0c;如果是import引入则修改es下面的文件 main 对应commonjs引入方式的程序入口文件…

WPF 数据分组显示

WPF 数据分组显示 效果展示&#xff1a; Student类&#xff1a; public class Student {public string Name { get; set; }public string Class { get; set; }public int Age { get; set; } }MainWindow.xaml.cs public partial class MainWindow : Window {private Observ…

数据结构-图的基本概念

图的定义 图时由非空的顶点集合和一个描述顶点之间关系的集合组成。可以定义为&#xff1a; ​​​​​​​ ​​​​​​​ ​​​​​​​ G表示一个图&#xff0c;V表示点集&#xff0c;E表示边集。集合E的每一个二元组都包含两个值和&#xff0c;表示…

超薄续航,加量不加厚,这款手机甩某果几条街!

据微博知名数码博主爆料&#xff0c;一加即将推出的Ace3 Pro将搭载革命性的“冰川电池”&#xff0c;这是一款6100mAh容量的先进电池&#xff0c;比传统5000mAh电池薄0.49毫米&#xff0c;仅5.51毫米厚&#xff0c;且支持100W快充&#xff0c;可在30分钟内充满。 &#xff08;比…

设计软件有哪些?粒子插件篇,渲染100邀请码1a12

设计师常常需要设计特效&#xff0c;而粒子系统是必不可少的&#xff0c;这次我们简单介绍一些粒子插件。 1、ComplexFresnel ComplexFresnel插件是一款用于计算机图形渲染中的增强型菲涅尔效应模拟工具。它扩展了传统的菲涅尔效应模型&#xff0c;考虑了更多的光学参数&…

R语言——绘图与数据可视化

1、练习将25个点的符号绘制出来&#xff0c;然后用rainbow()返回25个颜色&#xff0c;后5个符号形状的背景颜色用蓝色填充&#xff0c;图的标题为"符号图"&#xff0c;x轴标题为符号索引&#xff0c;y轴标题为符号形状。 2、根据员工的销售业绩画饼状图&#xff0c;添…

【AI】AI在创造还是毁掉音乐?

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

嵌入式学习真的这么烧钱吗?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;在我的学习过程中身边有不…

Java宝藏实验资源库(4)对象数组

一、实验目的 学习面向对象程序设计的方法。学习建立对象数组的方法。 学习在数组中存储和处理对象。 二、实验内容、过程及结果 **10.7 (Game: ATM machine) Use the Account class created in Programming Exer cise 9.7 to simulate an ATM machine. Create ten accou…

MAC地址解析工具:ARP命令

网络中每台设备都有一个唯一的网络标识&#xff0c;这个地址叫MAC地址或网卡地址&#xff0c;由网络设备制造商生产时写在硬件内部。形象地说&#xff0c;MAC地址就如同身份证上的身份证号码&#xff0c;具有唯一性。 无论是局域网&#xff0c;还是广域网中的计算机之间进行通信…

Windows系统下安装RabbitMQ详细步骤

声明&#xff1a;原文参考链接出自&#xff1a; 如何在Windows系统下安装RabbitMQ_rabbitmq windows安装-CSDN博客 https://zhuanlan.zhihu.com/p/693160757 一、RabbitMQ安装软件资源准备 因为RabbitMQ是Erlang语言开发的&#xff0c;因此安装Erlang环境在进行安装RbbitMQ的…

小程序大作为|小程序开发详细流程,新手也能轻松掌握

随着移动互联网的快速发展&#xff0c;小程序作为一种轻量级应用&#xff0c;因其无需下载安装、即点即用、用完即走的特点&#xff0c;受到了广大用户的青睐。那么开发小程序都有哪些开发流程呢&#xff1f;可以用哪种方式开发&#xff1f;选择合适的开发方式&#xff0c;一起…

java连接mysql报错

1.背景&#xff0c;直接升级操作系统从centos-》国产化操作系统&#xff0c;mysql也升级到5.7.44 2&#xff0c;报错 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server. Attempted reconn…

如何使用 ArcGIS Pro 和 Landsat 8 影像计算叶绿素指数和全球环境监测指数

GIS 工具和技术的出现极大地帮助了识别、量化和解决问题。GIS 还通过研究可能的情况并实施预防方案提供了一种主动的解决方案。多年来&#xff0c;GIS 通过电信和网络服务、事故/事件分析、城市规划、交通规划、环境影响评估、洪水损失估计、自然资源管理、环境健康和安全、植被…

【STM32-DAP 仿真器】

STM32-DAP 仿真器 ■ STM32-DAP仿真器介绍■ STM32-DAP仿真特点■ STM32-DAP仿真器实物图■ STM32-DAP高速 DAP 仿真器实物图■ STM32-DAP高速无线调试器 实物图■ STM32-DAP高速无线调试器示意图■ STM32-DAP高速无线调试器接线图■ STM32-DAP高速无线调试器接收端示意图 ■ S…

oracle开放某些视图给特定用户,查询报视图不存在问题

以sysdba身份登录到Oracle数据库。 创建新用户。例如&#xff0c;创建一个名为new_user的用户&#xff0c;密码为password&#xff1a; CREATE USER new_user IDENTIFIED BY password;为新用户分配表空间和临时表空间。例如&#xff0c;将表空间users和临时表空间temp分配给新…