Stanford斯坦福 CS 224R: 深度强化学习 (3)

2024-05-26 22:12

本文主要是介绍Stanford斯坦福 CS 224R: 深度强化学习 (3),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于模型的强化学习

强化学习(RL)旨在让智能体通过与环境互动来学习最优策略,从而最大化累积奖励。传统的强化学习方法如Q-learning、策略梯度等,通过大量的试错来学习值函数或策略,样本效率较低。而基于模型的强化学习(MBRL)则利用对环境的预测模型来加速学习过程,大大提高了样本利用率。本章我们将系统地介绍MBRL的基本原理、核心算法、实现技巧以及代表性应用。

1. 采样优化入门

在探讨MBRL之前,我们先来回顾一下优化问题的两大类解法:基于梯度的方法和基于采样的方法。

1.1 基于梯度的优化

基于梯度的优化通过目标函数对参数的梯度信息来指导参数更新。给定参数 θ \theta θ 和损失函数 L ( θ ) L(\theta) L(θ),更新步骤为:

θ ← θ − α ∇ θ L ( θ ) \theta \leftarrow \theta - \alpha \nabla_{\theta}L(\theta) θθαθL(θ)

其中 α \alpha α 为学习率。这类方法在深度学习中被广泛应用,优点是适用于高维参数空间,收敛速度快;缺点是需要损失函数可导,对优化问题的形式有一定要求。

1.2 基于采样的优化

基于采样的优化则不依赖梯度信息,而是通过在参数空间中采样并评估样本的性能来搜索最优解。以交叉熵方法(CEM)为例:

  1. 从某个分布(如高斯分布)中采样一组参数 { θ i ∼ p ( θ ) } i = 1 N \{\theta^i \sim p(\theta)\}_{i=1}^N {θip(θ)}i=1N
  2. 评估各个样本的损失 { L ( θ i ) } i = 1 N \{L(\theta^i)\}_{i=1}^N {L(θi)}i=1N,选出表现最好的前k个样本
  3. 用均值和方差来更新采样分布 p ( θ ) p(\theta) p(θ)
  4. 重复以上过程直至找到最优解

基于采样的优化的优点是:

  • 易于并行化
  • 不要求目标函数可导
  • 适用于复杂的优化形式

缺点是:

  • 难以处理高维参数空间
  • 收敛速度较慢

在MBRL中,基于采样的优化被广泛用于规划(planning)任务。例如我们可以通过在动作空间中采样来搜索最优动作序列。

2. 基于模型的强化学习

MBRL的核心思想是学习一个对环境的预测模型,用它来推断各种行为的后果,从而指导策略学习。相比无模型RL,MBRL能更有效地利用样本,实现更高效、更稳定的学习。

2.1 如何学习一个好的动力学模型?

学习环境模型本质上是一个监督学习问题。我们希望学习一个函数 f ϕ ( s , a ) f_{\phi}(s,a) fϕ(s,a),使其能较好地预测在状态 s s s 下执行动作 a a a 后,环境转移到状态 s ′ s' s 的结果。即:

min ⁡ ϕ ∑ ( s , a , s ′ ) ∈ D ∥ f ϕ ( s , a ) − s ′ ∥ 2 \min_{\phi} \sum_{(s,a,s')\in\mathcal{D}} \| f_{\phi}(s,a) - s'\|^2 ϕmin(s,a,s)Dfϕ(s,a)s2

其中 D = { ( s , a , s ′ ) i } \mathcal{D}=\{(s,a,s')_i\} D={(s,a,s)i} 为收集到的转移数据集。

根据任务的特点,我们可以采用不同的建模方式,例如:

  • 机器人控制:输入为状态和动作,输出为下一状态
  • 视频预测:输入为之前的图像序列,输出为未来的图像
  • 物理引擎:在已知物理定律的基础上,学习一些未知参数(如摩擦系数)
  • 对话系统:用语言模型来预测对话的后续发展
  • 金融市场:股票趋势预测模型

可见,模型的形式依赖于具体问题,但万变不离其宗,都是在拟合状态转移函数 p ( s ′ ∣ s , a ) p(s'|s,a) p(ss,a) 。接下来的问题是,如何有效利用学到的模型?

2.2 如何利用学到的动力学模型?

2.2.1 使用模型进行规划

规划(planning)是指在给定模型的情况下,寻找最优动作序列 { a t ∗ } \{a^*_t\} {at} 以最大化未来累积奖励。形式化地,规划问题可以表示为:

max ⁡ a t : t + H ∑ t ′ = t t + H r ( s t ′ , a t ′ ) \max_{a_{t:t+H}} \sum^{t+H}_{t'=t} r(s_{t'}, a_{t'}) at:t+Hmaxt=tt+Hr(st,at)

其中 H H H 为规划视界, r ( s , a ) r(s,a) r(s,a) 为奖励函数。这个问题可以通过基于梯度的优化或基于采样的优化来求解。

基于梯度的规划

基于梯度的规划通过反向传播梯度来优化动作序列。主要步骤如下:

  1. 用当前策略(如随机策略)采集数据 D = { ( s , a , s ′ ) i } \mathcal{D}=\{(s,a,s')_i\} D={(s,a,s)i}
  2. 训练模型 f ϕ f_{\phi} fϕ 来最小化预测误差 ∑ i ∥ f ϕ ( s i , a i ) − s i ′ ∥ 2 \sum_{i} \| f_{\phi}(s_i,a_i) - s'_i \|^2 ifϕ(si,ai)si2
  3. 通过反向传播优化动作序列 { a t ∗ } = arg ⁡ max ⁡ a t : t + H ∑ t ′ = t t + H r ( s t ′ , a t ′ ) \{a^*_t\} = \arg\max_{a_{t:t+H}} \sum^{t+H}_{t'=t} r(s_{t'}, a_{t'}) {at}=argmaxat:t+Ht=tt+Hr(st,at)

这里第3步通过梯度上升来优化目标函数。模型 f ϕ f_{\phi} fϕ 充当了从动作到状态的映射,使我们能够计算目标函数对动作的梯度。

基于采样的规划

基于采样的规划则通过在动作空间中随机采样,并利用模型评估轨迹质量,来搜索最优动作序列。主要步骤如下:

  1. 用当前策略采集数据 D = { ( s , a , s ′ ) i } \mathcal{D}=\{(s,a,s')_i\} D={(s,a,s)i}
  2. 训练模型 f ϕ f_{\phi} fϕ 来最小化预测误差
  3. 通过采样搜索最优动作序列:
    • 随机采样N组长度为H的动作序列 { A t : t + H i } i = 1 N \{A^i_{t:t+H}\}^N_{i=1} {At:t+Hi}i=1N
    • 利用 f ϕ f_{\phi} fϕ 预测各动作序列对应的状态轨迹,并估算累积奖励
    • 选择奖励最高的动作序列 A ∗ = arg ⁡ max ⁡ A i ∑ t ′ = t t + H r ( s t ′ i , a t ′ i ) A^* = \arg\max_{A^i} \sum^{t+H}_{t'=t} r(s^i_{t'}, a^i_{t'}) A=argmaxAit=tt+Hr(sti,ati)

这里第3步本质上是一个启发式搜索,通过模型模拟来评估候选动作序列的优劣。这种方法的好处是易于实现、可并行化、适用于任意形式的环境模型。

对于连续动作空间,一种更高效的采样策略是交叉熵方法(CEM):

  1. 从高斯分布 N ( μ , Σ ) \mathcal{N}(\mu, \Sigma) N(μ,Σ) 中采样N个动作序列 { A i } i = 1 N \{A^i\}^N_{i=1} {Ai}i=1N
  2. 评估各动作序列的累积奖励 J ( A i ) = ∑ t ′ = t t + H r ( s t ′ i , a t ′ i ) J(A^i) = \sum^{t+H}_{t'=t} r(s^i_{t'}, a^i_{t'}) J(Ai)=t=tt+Hr(sti,ati)
  3. 选出奖励最高的前k个动作序列 { A i j } j = 1 k \{A^{i_j}\}^k_{j=1} {Aij}j=1k
  4. { A i j } j = 1 k \{A^{i_j}\}^k_{j=1} {Aij}j=1k 来更新高斯分布的均值和方差
  5. 重复以上过程,直至分布收敛

CEM相比随机采样,能更快地集中到高奖励区域进行采样,减少搜索代价。

规划算法总结

方法优点缺点
基于梯度适用高维动作空间需模型可导
随机采样简单,可并行采样效率低
交叉熵法采样高效依赖初始分布

以上规划算法都是开环(open-loop)控制,即先规划一个动作序列,然后不更新地执行到底。这对模型精度要求很高,否则容易导致误差累积。一种常用的改进是模型预测控制(model predictive control),即每次只执行规划出的前几步动作,然后重新规划。通过反复规划来及时修正误差,使控制更加鲁棒

2.2.2 使用模型指导策略学习

规划的一个局限是需要精确的奖励函数,且很难处理长期回报。一种更通用的做法是利用模型来辅助策略(policy)或值函数(value function)的学习。我们可以用学到的模型来增强真实的环境交互数据。具体来说,训练过程为:

  1. 用当前策略 π \pi π 在真实环境中采样数据 D e n v \mathcal{D}_{env} Denv
  2. 训练模型 f ϕ f_{\phi} fϕ 来拟合状态转移函数
  3. f ϕ f_{\phi} fϕ D e n v \mathcal{D}_{env} Denv 中的各个状态出发,模拟生成数据 D m o d e l \mathcal{D}_{model} Dmodel
  4. D e n v \mathcal{D}_{env} Denv D m o d e l \mathcal{D}_{model} Dmodel 合并为 D R L \mathcal{D}_{RL} DRL
  5. D R L \mathcal{D}_{RL} DRL 来训练新策略 π ′ \pi' π (或值函数 V V V)
  6. 重复以上过程,即可迭代优化策略

这个过程可以与任意的无模型RL算法相结合,关键是用模型模拟数据来加速策略学习。通过对 D e n v \mathcal{D}_{env} Denv 中的各个状态seed模拟轨迹,我们能更充分地利用采集到的真实转移数据。相比单纯地想象完整轨迹,这种做法能更好地覆盖状态空间。

3. 案例分析:用MBRL实现机器人灵巧操控

DeepMind在2019年提出的一个工作,展示了用MBRL让一只仿真五指机械手学会各种灵巧操控,如旋转棒、翻转刀等。尽管已经过去两年,但这个结果至今仍是五指灵巧操控领域最激动人心的成果之一。让我们来剖析一下他们是如何做到的。

3.1 问题定义

状态空间: 机械手和物体的位置
动作空间: 24维,控制5个手指的关节角度
模型结构: 3个全连接网络组成的集成模型,每个网络有2个大小为500的隐藏层
奖励函数: 跟踪物体轨迹的误差 + 惩罚掉落
规划算法: 修改版的CEM,引入时间平滑正则化和更softer的加权平均

整体训练过程为:交替地用CEM规划采集大约30条轨迹数据,并用新数据更新集成模型,如此反复迭代。

3.2 仿真实验

他们首先在MuJoCo物理引擎中构建仿真环境,测试不同RL算法的表现:

  • 无模型RL:
    • SAC:一种actor-critic算法
    • NPG:一种策略梯度算法
  • 有模型RL:
    • PDDM:本文提出的MBRL算法
    • MBPO:利用模型数据增强的RL算法
    • PETS:基于CEM的MBRL算法
    • Nagabandi et al.:基于随机采样的MBRL,不使用集成

从结果可以看出,MBRL算法的采样效率明显高于无模型RL,其中PDDM的表现最佳。进一步的消融实验表明:

  • 模型容量要足够大,但太大也会过拟合
  • 集成模型至少需要3个成员
  • 规划视界是个重要的权衡:太短则短视,太长则不可靠
  • CEM的改进策略是关键,尤其是temporally-correlated探索

可见,PDDM之所以性能卓越,缘于以下几点优化:

  • 大容量集成模型,增强了预测鲁棒性
  • 适度的规划视界,权衡了可靠性和远见性
  • 改进的CEM采样,引入了探索的时间相关性

3.3 实物实验

为了验证算法在实物场景中的有效性,他们搭建了一套机械手系统,包括一只24关节的ShadowHand和一套PhaseSpace运动捕捉系统。

实物训练面临两大挑战:

  • 采样成本高,需要样本高效的算法
  • 很难完全复现初始状态,增加了训练难度

他们先在仿真环境中预训练策略,再迁移到实物上微调。同时为了自动化采集数据,他们额外加了一只机械臂用于在每个回合将物体重置到初始位置。最终,系统在4小时内就学会了花式转棒的技能,展现了MBRL的强大潜力。

4. 代码实战

下面我们通过一个简单的例子来演示MBRL的实现。考虑倒立摆(Inverted Pendulum)环境,状态为摆角 θ \theta θ 和角速度 θ ˙ \dot{\theta} θ˙,动作为施加在摆基座的水平力。我们的目标是学习一个策略,使摆尽快达到并保持直立平衡。

首先导入需要的库:

import numpy as np
import torch
import gym
import matplotlib.pyplot as plt

然后定义一个神经网络作为环境模型:

class Model(torch.nn.Module):def __init__(self, state_dim, action_dim):super().__init__()self.fc1 = torch.nn.Linear(state_dim + action_dim, 256)self.fc2 = torch.nn.Linear(256, 256)self.fc3 = torch.nn.Linear(256, state_dim)def forward(self, state, action):x = torch.cat([state, action], dim=-1)x = torch.relu(self.fc1(x))  x = torch.relu(self.fc2(x))next_state = self.fc3(x)return next_state

模型输入当前状态和动作,输出预测的下一状态。接下来我们定义MBRL算法的主要组件:

class MBRL:def __init__(self, env, model, n_plan, n_iter, n_batch=128):self.env = env  self.model = modelself.n_plan = n_plan  # 规划视界self.n_iter = n_iter  # 总迭代次数self.n_batch = n_batch

其中env为强化学习环境,model为环境模型,n_plan为规划视界,n_iter为总迭代次数,n_batch为批大小。接下来实现数据采集函数:

    def collect_data(self, n_samples):states, actions, rewards, next_states = [], [], [], []state = self.env.reset()for _ in range(n_samples):action = self.env.action_space.sample()next_state, reward, done, _ = self.env.step(action)states.append(state)actions.append(action)rewards.append(reward)next_states.append(next_state)state = next_state if not done else self.env.reset()states = torch.tensor(states, dtype=torch.float32)actions = torch.tensor(actions, dtype=torch.float32)rewards = torch.tensor(rewards, dtype=torch.float32)next_states = torch.tensor(next_states, dtype=torch.float32) return states, actions, rewards, next_states  

这个函数使用随机策略采集指定数量的转移数据,并返回状态、动作、奖励和下一状态的张量。有了真实数据,我们就可以训练模型了:

    def train_model(self, states, actions, next_states):dataset = torch.utils.data.TensorDataset(states, actions, next_states)dataloader = torch.utils.data.DataLoader(dataset, batch_size=self.n_batch, shuffle=True)criterion = torch.nn.MSELoss()optimizer = torch.optim.Adam(self.model.parameters())for epoch in range(5):for batch in dataloader:state, action, next_state = batchpred_next_state = self.model(state, action)loss = criterion(pred_next_state, next_state)optimizer.zero_grad()loss.backward()optimizer.step()

模型训练采用均方损失和Adam优化器,训练5个epoch,每个epoch遍历整个数据集。接下来我们实现基于CEM的规划器:

    def plan(self, state):best_reward = -np.infbest_action_seq = Nonefor _ in range(100):  # CEM迭代次数action_seqs = np.random.uniform(-2, 2, size=(128, self.n_plan))rewards = np.zeros(128)for t in range(self.n_plan):actions = torch.tensor(action_seqs[:, t], dtype=torch.float32)next_states = self.model(state, actions)rewards += self.env.get_reward(state.numpy(), actions.numpy())state = next_stateselite_idxs = rewards.argsort()[-10:]  # 选取前10个最优轨迹  elite_action_seqs = action_seqs[elite_idxs]if rewards.max() > best_reward:best_reward = rewards.max()best_action_seq = action_seqs[rewards.argmax()]action_seqs = elite_action_seqs.mean(axis=0) + 0.5 * elite_action_seqs.std(axis=0) * np.random.randn(*elite_action_seqs.shape)return best_action_seq[0]  # 返回第一个动作

规划器从均匀分布中采样128条动作序列,长度为n_plan。然后用模型模拟各条序列对应的状态轨迹,并估算累积奖励。接着选出奖励最高的10条轨迹,用它们的均值和方差来更新采样分布。迭代若干次后,返回奖励最高的动作序列的第一个动作。

最后,我们定义主循环,交替地采集数据、训练模型和规划动作:

    def run(self):for i in range(self.n_iter):states, actions, _, next_states = self.collect_data(1000)self.train_model(states, actions, next_states)state = self.env.reset()for t in range(200): action = self.plan(torch.tensor(state, dtype=torch.float32))next_state, _, done, _ = self.env.step(action)state = next_stateif done:breakprint(f"Iteration {i}: lasted {t} steps")

主循环共执行n_iter次,每次采集1000条数据,并用新数据训练模型。然后用规划器与环境交互200步,记录并打印生存时长。

下面我们创建环境、模型和MBRL对象,启动训练:

env = gym.make("InvertedPendulum-v2")
model = Model(state_dim=env.observation_space.shape[0], action_dim=env.action_space.shape[0])
mbrl = MBRL(env, model, n_plan=20, n_iter=10)mbrl.run() 

运行这段代码,你将看到倒立摆在几次迭代后就能很快达到平衡位置并长时间维持。这充分展示了MBRL的高效性和可行性。当然,这只是一个简单示例,实际应用还需考虑更多技术细节,如模型集成、数据缓冲、策略约束等。感兴趣的读者可以进一步阅读MBRL的前沿文献。

5. 展望

MBRL利用环境模型来提高样本利用率和策略质量,是一种颇具前景的强化学习范式。近年来,随着深度学习的发展,MBRL在高维观察空间和连续动作空间上取得了长足的进步。一些代表性工作包括:

  • ME-TRPO[Kurutach et al., 2018]:集成模型+TRPO策略优化
  • SLBO[Luo et al., 2018]:基于模型的数据增强
  • PETS[Chua et al., 2018]:集成模型+CEM规划
  • POPLIN[Wang and Ba, 2019]:集成模型辅助TRPO策略优化
  • POLO[Lowrey et al., 2019]:策略优化结合离线数据
  • DMPO[Luo et al., 2019]:决策时集成规划

这些工作极大地拓展了MBRL的应用范围和实用价值。但MBRL还存在不少挑战:

  • 如何学习更准确、更鲁棒的环境模型?
  • 如何设计更高效、更可靠的规划和探索机制?
  • 如何在模型训练和策略优化之间权衡计算资源?
  • 如何处理复杂环境中的长期因果关系和延迟奖励?
  • 如何提高模型和策略的泛化性和适应性?

这需要RL、DL、规划、控制等领域的协同创新。展望未来,MBRL有望与以下方向深度融合:

  • 元学习:提高模型和策略的快速适应能力
  • 迁移学习:实现跨任务、跨域的知识复用
  • 因果推断:建模环境中的因果结构,实现更可解释、更稳定的策略学习
  • 多智能体学习:解决多个智能体互动时的博弈和协作问题

MBRL代表了AI研究的一个重要趋势,即用知识引导数据驱动的学习。通过模型这种先验知识,我们可以更高效、更可靠、更可解释地学习决策。这不仅能造福强化学习本身,也为构建大模型、训练通用 AI 系统带来了新的思路。

Q&A

我用一个简单的例子来解释无模型(model-free)和有模型(model-based)强化学习的区别。

假设你要训练一个机器人学习走路,目标是走得尽可能快。环境的状态是机器人的关节角度和角速度,动作是施加在关节上的力矩。

无模型RL:

  1. 机器人尝试随机动作,观察状态变化和获得的奖励(与走得快慢相关)。
  2. 根据这些数据,直接学习一个策略函数,告诉机器人在每个状态下应该采取什么动作。
  3. 不断重复 1-2,使策略逐渐优化,最终学会快速行走。
# Model-free RL伪代码
for episode in range(n_episodes):state = env.reset() for step in range(max_steps):action = policy(state)  # 策略根据状态直接产生动作next_state, reward = env.step(action)update_policy(state, action, reward, next_state)  # 更新策略state = next_state

这种方法的特点是:

  • 直接学习策略函数,没有对环境建模
  • 需要大量的试错和样本数据
  • 更新方向完全依赖当前策略采集的数据

有模型RL:

  1. 机器人尝试随机动作,观察状态变化,学习一个环境模型(输入当前状态+动作,预测下一状态)。
  2. 用学到的模型进行虚拟试错:在头脑中预演不同动作序列的结果,选出一个最优的方案。
  3. 令机器人执行这个方案并更新环境模型,不断重复 2-3,使策略和模型共同优化。
# Model-based RL伪代码
model = initialize_model()
for episode in range(n_episodes):state = env.reset()for step in range(max_steps): action_plan = plan_with_model(model, state)  # 用模型预演,选取最优动作序列for planned_step in range(plan_horizon):action = action_plan[planned_step] next_state, reward = env.step(action)model.update(state, action, next_state)  # 更新环境模型state = next_state

这种方法的特点是:

  • 显式地学习环境模型,揭示了状态转移规律
  • 通过模型预演来选取动作,减少真实环境中的试错
  • 可利用模型产生额外的虚拟数据,加速策略学习

无模型RL就像在一个陌生的城市摸索前行,只能根据已走过的路来决定下一步走哪。而有模型RL则像拿到了一张地图,可以提前规划路线,少走弯路。

什么时候用model-based更好?

  • 当环境状态转移规律较简单、容易建模时
  • 当真实环境交互代价较高时(如机器人、自动驾驶)
  • 当任务涉及多步规划、长期因果推理时

之前的无模型方法还有用吗?

  • 当环境状态转移复杂、难以准确建模时
  • 当大量采集真实数据的成本可以接受时
  • 对于反应性的决策任务,无需多步规划时

model-free和model-based是两种不同的思路,各有优劣,应根据具体任务来选择。近年来,也有许多工作尝试将两者结合,取长补短。比如用无模型RL预训练,再用模型fine-tune;或者在model-based RL框架下引入无模型数据增强等。

这篇关于Stanford斯坦福 CS 224R: 深度强化学习 (3)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1005730

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

五大特性引领创新! 深度操作系统 deepin 25 Preview预览版发布

《五大特性引领创新!深度操作系统deepin25Preview预览版发布》今日,深度操作系统正式推出deepin25Preview版本,该版本集成了五大核心特性:磐石系统、全新DDE、Tr... 深度操作系统今日发布了 deepin 25 Preview,新版本囊括五大特性:磐石系统、全新 DDE、Tree

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static