强化学习_06_pytorch-PPO2实践(Humanoid-v4)

一、PPO优化

PPO的简介和实践可以看笔者之前的文章 强化学习_06_pytorch-PPO实践(Pendulum-v1)
针对之前的PPO做了主要以下优化:

-笔者-PPO笔者-PPO2ref
data collectone episodeseveral episode(one batch)
activationReLUTanh
adv-compute-compute adv as one serires
adv-normalizemini-batch normalizeservel envs-batch normalize影响PPO算法性能的10个关键技巧
Value Function Loss Clipping- L V = m a x [ ( V θ t − V t a r ) 2 , ( c l i p ( V θ t , V θ t − 1 − ϵ , V θ t − 1 + ϵ ) ) 2 ] L^{V}=max[(V_{\theta_t} - V_{tar})^2, (clip(V_{\theta_t}, V_{\theta_{t-1}}-\epsilon, V_{\theta_{t-1}}+\epsilon))^2] LV=max[(VθtVtar)2,(clip(Vθt,Vθt1ϵ,Vθt1+ϵ))2]The 37 Implementation Details of Proximal Policy Optimization
optimizeractor-opt & critic-optuse common opt
lossactor-loss-backward & critic-loss-backwardloss weight sum
paramate-init-1- hidden layer orthogonal initialization of weights 2 \sqrt{2} 2 ; 2- The policy output layer weights are initialized with the scale of 0.01; 3- The value output layer weights are initialized with the scale of 1.0The 37 Implementation Details of Proximal Policy Optimization
training envssingle gym envSyncVectorEnv

相比于PPO2_old.py 这次实现了上述的全部优化,

1.1 PPO2 代码

详细可见 Github: PPO2.py


class PPO:
    """
    PPO算法, 采用截断方式
    """
    def __init__(self,
                state_dim: int,
                actor_hidden_layers_dim: typ.List,
                critic_hidden_layers_dim: typ.List,
                action_dim: int,
                actor_lr: float,
                critic_lr: float,
                gamma: float,
                PPO_kwargs: typ.Dict,
                device: torch.device,
                reward_func: typ.Optional[typ.Callable]=None
                ):
        dist_type = PPO_kwargs.get('dist_type', 'beta')
        self.dist_type = dist_type
        self.actor = policyNet(state_dim, actor_hidden_layers_dim, action_dim, dist_type=dist_type).to(device)
        self.critic = valueNet(state_dim, critic_hidden_layers_dim).to(device)
        self.actor_lr = actor_lr
        self.critic_lr = critic_lr
        self.actor_opt = torch.optim.Adam(self.actor.parameters(), lr=actor_lr)
        self.critic_opt = torch.optim.Adam(self.critic.parameters(), lr=critic_lr)
        
        self.gamma = gamma
        self.lmbda = PPO_kwargs['lmbda']
        self.k_epochs = PPO_kwargs['k_epochs'] # 一条序列的数据用来训练的轮次
        self.eps = PPO_kwargs['eps'] # PPO中截断范围的参数
        self.sgd_batch_size = PPO_kwargs.get('sgd_batch_size', 512)
        self.minibatch_size = PPO_kwargs.get('minibatch_size', 128)
        self.action_bound = PPO_kwargs.get('action_bound', 1.0)
        self.action_low = torch.FloatTensor([-1 * self.action_bound]).to(device)
        self.action_high = torch.FloatTensor([self.action_bound]).to(device)
        if 'action_space' in PPO_kwargs:
            self.action_low = torch.FloatTensor(PPO_kwargs['action_space'].low).to(device)
            self.action_high = torch.FloatTensor(PPO_kwargs['action_space'].high).to(device)
        
        self.count = 0 
        self.device = device
        self.reward_func = reward_func
        self.min_batch_collate_func = partial(mini_batch, mini_batch_size=self.minibatch_size)

    def _action_fix(self, act):
        if self.dist_type == 'beta':
            # beta 0-1 -> low ~ high
            return act * (self.action_high - self.action_low) + self.action_low
        return act 
    
    def _action_return(self, act):
        if self.dist_type == 'beta':
            # low ~ high -> 0-1 
            act_out = (act - self.action_low) / (self.action_high - self.action_low)
            return act_out * 1 + 0
        return act 

    def policy(self, state):
        state = torch.FloatTensor(np.array([state])).to(self.device)
        action_dist = self.actor.get_dist(state, self.action_bound)
        action = action_dist.sample()
        action = self._action_fix(action)
        return action.cpu().detach().numpy()[0]

    def update(self, samples: deque):
        state, action, reward, next_state, done = zip(*samples)

        state = torch.FloatTensor(np.stack(state)).to(self.device)
        action = torch.FloatTensor(np.stack(action)).to(self.device)
        reward = torch.tensor(np.stack(reward)).view(-1, 1).to(self.device)
        if self.reward_func is not None:
            reward = self.reward_func(reward)

        next_state = torch.FloatTensor(np.stack(next_state)).to(self.device)
        done = torch.FloatTensor(np.stack(done)).view(-1, 1).to(self.device)
        
        old_v = self.critic(state)
        td_target = reward + self.gamma * self.critic(next_state) * (1 - done)
        td_delta = td_target - old_v
        advantage = compute_advantage(self.gamma, self.lmbda, td_delta, done).to(self.device)
        # recompute
        td_target = advantage + old_v
        # trick1: batch_normalize
        advantage = (advantage - torch.mean(advantage)) / (torch.std(advantage) + 1e-5)
        action_dists = self.actor.get_dist(state, self.action_bound)
        # 动作是正态分布
        old_log_probs = action_dists.log_prob(self._action_return(action))
        if len(old_log_probs.shape) == 2:
            old_log_probs = old_log_probs.sum(dim=1)
        d_set = memDataset(state, action, old_log_probs, advantage, td_target)
        train_loader = DataLoader(
            d_set,
            batch_size=self.sgd_batch_size,
            shuffle=True,
            drop_last=True,
            collate_fn=self.min_batch_collate_func
        )

        for _ in range(self.k_epochs):
            for state_, action_, old_log_prob, adv, td_v in train_loader:
                action_dists = self.actor.get_dist(state_, self.action_bound)
                log_prob = action_dists.log_prob(self._action_return(action_))
                if len(log_prob.shape) == 2:
                    log_prob = log_prob.sum(dim=1)
                # e(log(a/b))
                ratio = torch.exp(log_prob - old_log_prob.detach())
                surr1 = ratio * adv
                surr2 = torch.clamp(ratio, 1 - self.eps, 1 + self.eps) * adv

                actor_loss = torch.mean(-torch.min(surr1, surr2)).float()
                critic_loss = torch.mean(
                    F.mse_loss(self.critic(state_).float(), td_v.detach().float())
                ).float()
                self.actor_opt.zero_grad()
                self.critic_opt.zero_grad()
                actor_loss.backward()
                critic_loss.backward()
                torch.nn.utils.clip_grad_norm_(self.actor.parameters(), 0.5) 
                torch.nn.utils.clip_grad_norm_(self.critic.parameters(), 0.5) 
                self.actor_opt.step()
                self.critic_opt.step()

        return True

    def save_model(self, file_path):
        if not os.path.exists(file_path):
            os.makedirs(file_path)

        act_f = os.path.join(file_path, 'PPO_actor.ckpt')
        critic_f = os.path.join(file_path, 'PPO_critic.ckpt')
        torch.save(self.actor.state_dict(), act_f)
        torch.save(self.critic.state_dict(), critic_f)

    def load_model(self, file_path):
        act_f = os.path.join(file_path, 'PPO_actor.ckpt')
        critic_f = os.path.join(file_path, 'PPO_critic.ckpt')
        self.actor.load_state_dict(torch.load(act_f, map_location='cpu'))
        self.critic.load_state_dict(torch.load(critic_f, map_location='cpu'))
        self.actor.to(self.device)
        self.critic.to(self.device)
        self.actor_opt = torch.optim.Adam(self.actor.parameters(), lr=self.actor_lr)
        self.critic_opt = torch.optim.Adam(self.critic.parameters(), lr=self.critic_lr)

    def train(self):
        self.training = True
        self.actor.train()
        self.critic.train()

    def eval(self):
        self.training = False
        self.actor.eval()
        self.critic.eval()

1.2 ppo2_train

其实就是向量环境多个step进行一次ppo update
详细可见 Github: ppo2_train


def ppo2_train(envs, agent, cfg, 
                    wandb_flag=False, 
                    train_without_seed=False, 
                    step_lr_flag=False, 
                    step_lr_kwargs=None, 
                    test_ep_freq=100,
                    online_collect_nums=1024,
                    test_episode_count=3,
                    wandb_project_name="RL-train_on_policy",
                    add_max_step_reward_flag=False
                ):
    test_env = envs.envs[0]
    env_id = str(test_env).split('>')[0].split('<')[-1]
    if wandb_flag:
        wandb.login()
        cfg_dict = cfg.__dict__
        if step_lr_flag:
            cfg_dict['step_lr_flag'] = step_lr_flag
            cfg_dict['step_lr_kwargs'] = step_lr_kwargs

        algo = agent.__class__.__name__
        now_ = datetime.now().strftime('%Y%m%d__%H%M')
        wandb.init(
            project=wandb_project_name,
            name=f"{algo}__{env_id}__{now_}",
            config=cfg_dict,
            monitor_gym=True
        )
    mini_b = cfg.PPO_kwargs.get('minibatch_size', 12)
    if step_lr_flag:
        opt = agent.actor_opt if hasattr(agent, "actor_opt") else agent.opt
        schedule = StepLR(opt, step_size=step_lr_kwargs['step_size'], gamma=step_lr_kwargs['gamma'])

    tq_bar = tqdm(range(cfg.num_episode))
    rewards_list = []
    now_reward = 0
    recent_best_reward = -np.inf
    update_flag = False
    best_ep_reward = -np.inf
    buffer_ = replayBuffer(cfg.off_buffer_size)
    steps = 0
    rand_seed = np.random.randint(0, 9999)
    final_seed = rand_seed if train_without_seed else cfg.seed
    s, _ = envs.reset(seed=final_seed)
    for i in tq_bar:
        if update_flag:
            buffer_ = replayBuffer(cfg.off_buffer_size)

        tq_bar.set_description(f'Episode [ {i+1} / {cfg.num_episode} ](minibatch={mini_b})')    
        step_rewards = np.zeros(envs.num_envs)
        step_reward_mean = 0.0
        for step_i in range(cfg.off_buffer_size):
            a = agent.policy(s)
            n_s, r, terminated, truncated, infos = envs.step(a)
            done = np.logical_or(terminated, truncated)
            steps += 1
            mem_done = done 
            buffer_.add(s, a, r, n_s, mem_done)
            s = n_s
            step_rewards += r
            if (steps % test_ep_freq == 0) and (steps > cfg.off_buffer_size):
                freq_ep_reward = play(test_env, agent, cfg, episode_count=test_episode_count, play_without_seed=train_without_seed, render=False, ppo_train=True)
                
                if freq_ep_reward > best_ep_reward:
                    best_ep_reward = freq_ep_reward
                    # 模型保存
                    save_agent_model(agent, cfg, f"[ ep={i+1} ](freqBest) bestTestReward={best_ep_reward:.2f}")


            max_step_flag = (step_i == (cfg.off_buffer_size - 1)) and add_max_step_reward_flag
            if max_step_flag:
                step_reward_mean = step_rewards.mean()

            if (("final_info" in infos) or max_step_flag) and step_i >= 5:
                info_counts = 0.0001
                episode_rewards = 0
                for info in infos.get("final_info", dict()):
                    if info and "episode" in info:
                        # print(f"global_step={step_i}, episodic_return={info['episode']['r']}")
                        if isinstance(info["episode"]["r"], np.ndarray):
                            episode_rewards += info["episode"]["r"][0]
                        else:
                            episode_rewards += info["episode"]["r"]
                        info_counts += 1

            # if(steps % cfg.max_episode_steps == 0):
                rewards_list.append(max(episode_rewards/info_counts, step_reward_mean))
                # print(rewards_list[-10:])  0: in buffer_size step not get any point
                now_reward = np.mean(rewards_list[-10:])
                if max_step_flag:
                    step_reward_mean = 0.0

                if (now_reward > recent_best_reward):
                    # best 时也进行测试
                    test_ep_reward = play(test_env, agent, cfg, episode_count=test_episode_count, play_without_seed=train_without_seed, render=False, ppo_train=True)
                    if test_ep_reward > best_ep_reward:
                        best_ep_reward = test_ep_reward
                        # 模型保存
                        save_agent_model(agent, cfg, f"[ ep={i+1} ](recentBest) bestTestReward={best_ep_reward:.2f}")
                    recent_best_reward = now_reward
                
                tq_bar.set_postfix({
                    'lastMeanRewards': f'{now_reward:.2f}', 
                    'BEST': f'{recent_best_reward:.2f}',
                    "bestTestReward": f'{best_ep_reward:.2f}'
                })
                if wandb_flag:
                    log_dict = {
                        'lastMeanRewards': now_reward,
                        'BEST': recent_best_reward,
                        "episodeRewards": episode_rewards,
                        "bestTestReward": best_ep_reward
                    }
                    if step_lr_flag:
                        log_dict['actor_lr'] = opt.param_groups[0]['lr']
                    wandb.log(log_dict)

        update_flag = agent.update(buffer_.buffer, wandb=wandb if wandb_flag else None)
        if step_lr_flag:
            schedule.step()

    envs.close()
    if wandb_flag:
        wandb.finish()
    return agent

二、 Pytorch实践

2.1 智能体构建与训练

PPO2主要是收集n_envs * n_step的结果序列进行训练,针对Humanoid-v4,需要同时对多个环境进行游戏采样(num_envs = 128),同时环境的步数需要进行尝试(笔者尝试了[60, 64, 70, 75, 80, 100, 120, 159, 164)最终采用n_step=80。这里还有一个非常重要的是需要对环境进行NormalizeObservation

2.1.1 NormalizeObservation

下图是几个训练的比较好的未进行环境Normalize的BestTestReward VS 进行环境Normalize的BestTestReward。 所有1500分以上的均是环境Normalize的
在这里插入图片描述
NormalizeObservation的核心就是RunningMeanStd,每个step都对环境进行迭代

# update_mean_var_count_from_moments
    delta = batch_mean - mean
    tot_count = count + batch_count

    new_mean = mean + delta * batch_count / tot_count
    m_a = var * count
    m_b = batch_var * batch_count
    M2 = m_a + m_b + np.square(delta) * count * batch_count / tot_count
    new_var = M2 / tot_count
    new_count = tot_count

2.1.2 进行训练

详细可见 Github: test_ppo.Humanoid_v4_ppo2_test

env_name = 'Humanoid-v4'
num_envs = 128 #64
gym_env_desc(env_name)
print("gym.__version__ = ", gym.__version__ )
path_ = os.path.dirname(__file__)
norm_flag = True
reward_flag = False
envs = gym.vector.SyncVectorEnv(
    [make_env(env_name, obs_norm_trans_flag=norm_flag, reward_norm_trans_flag=reward_flag) for _ in range(num_envs)]
)
dist_type = 'beta'
cfg = Config(
    envs, 
    # 环境参数
    save_path=os.path.join(path_, "test_models" ,f'PPO_Humanoid-v4-{norm_flag}-1'), 
    seed=202405,
    # 网络参数
    actor_hidden_layers_dim=[128, 128, 128],
    critic_hidden_layers_dim=[128, 128, 128],
    # agent参数
    actor_lr=4.5e-4, 
    gamma=0.99,
    # 训练参数
    num_episode=3000, 
    off_buffer_size=80, # batch_size = off_buffer_size * num_env
    max_episode_steps=80,
    PPO_kwargs={
        'lmbda': 0.985, 
        'eps': 0.125,  
        'k_epochs': 3,
        'sgd_batch_size': 2048, # 1024, # 512,
        'minibatch_size': 1024,  # 512,  # 64,
        'action_space': envs.single_action_space,
        'act_type': 'tanh',
        'dist_type': dist_type,
        'critic_coef': 1,
        'max_grad_norm': 3.5, # 45.5
        'clip_vloss': True,
        # 'min_adv_norm': True,
        'anneal_lr': False, # not work
        'num_episode': 3000
    }
)
cfg.test_max_episode_steps = 300
cfg.num_envs = num_envs
minibatch_size = cfg.PPO_kwargs['minibatch_size']
max_grad_norm = cfg.PPO_kwargs['max_grad_norm']
agent = PPO2(
    state_dim=cfg.state_dim,
    actor_hidden_layers_dim=cfg.actor_hidden_layers_dim,
    critic_hidden_layers_dim=cfg.critic_hidden_layers_dim,
    action_dim=cfg.action_dim,
    actor_lr=cfg.actor_lr,
    critic_lr=cfg.critic_lr,
    gamma=cfg.gamma,
    PPO_kwargs=cfg.PPO_kwargs,
    device=cfg.device,
    reward_func=None
)
agent.train()
ppo2_train(envs, agent, cfg, wandb_flag=True, wandb_project_name=f"PPO2-{env_name}",
                train_without_seed=False, test_ep_freq=cfg.off_buffer_size * 10, 
                online_collect_nums=cfg.off_buffer_size,
                test_episode_count=10)
# save norm env
save_env(envs.envs[0], os.path.join(cfg.save_path, 'norm_env.pkl'))

2.2 训练出的智能体观测

最后将训练的最好的网络拿出来进行观察,这里需要注意:我们在训练的时候对环境进行了Normalize,所以在环境初始化的时候,需要将obs_rms (即 RunningMeanStd)中的mean, var, count进行初始化,然后再play

agent.load_model(cfg.save_path)
agent.eval()

with open(os.path.join(cfg.save_path, 'norm_env.pkl'), 'rb') as f:
    env = cloudpickle.load(f)

# p = '/home/scc/sccWork/myGitHub/RL/src/test/test_models/PPO_Humanoid-v4-True-2/norm_env.pkl'
# with open(p, 'rb') as f:
#     env = cloudpickle.load(f)

obs_rms = env.get_wrapper_attr('env').get_wrapper_attr("obs_rms")
env = make_env(env_name, obs_norm_trans_flag=norm_flag, render_mode='human')()
env.get_wrapper_attr('env').get_wrapper_attr("obs_rms").mean = obs_rms.mean
env.get_wrapper_attr('env').get_wrapper_attr("obs_rms").var = obs_rms.var
env.get_wrapper_attr('env').get_wrapper_attr("obs_rms").count = obs_rms.count
# env = make_env(env_name, obs_norm_trans_flag=norm_flag)()
# cfg.max_episode_steps = 1020 
play(env, agent, cfg, episode_count=3, play_without_seed=False, render=True)

在这里插入图片描述

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

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

相关文章

鸿蒙ArkTS声明式开发:跨平台支持列表【按键事件】

按键事件 按键事件指组件与键盘、遥控器等按键设备交互时触发的事件&#xff0c;适用于所有可获焦组件&#xff0c;例如Button。对于Text&#xff0c;Image等默认不可获焦的组件&#xff0c;可以设置focusable属性为true后使用按键事件。 说明&#xff1a; 开发前请熟悉鸿蒙开…

【STM32踩坑】HAL固件库版本过高导致烧录后无法运行问题

问题引入 目前STM32CUBEMX已经更新到了6.11版本&#xff0c;对应的固件库也一直在更新&#xff1b; 以STM32F1库为例&#xff0c;目前最新的库对应版本为1.8.5 但是我们会发现&#xff0c;如果直接使用1.8.5版本的固件库生成HAL源码后&#xff0c;烧录是可以烧录&#xff0c;但…

【IC】良率模型-yield model

缺陷密度Default Density(D0),表示单位面积的缺陷数D。 单位面积有M个部件,一个部件的平均失效率为: 一个面积为A的系统(芯片)良率: Possion模型: 当M趋于无穷时,系统良率为possion模型。 Murphy模型:(D~对称三角分布) 大芯片或大系统possion模型预估良率过于…

Python自动化测试进阶:性能测试与持续集成实践

Python自动化测试进阶包括性能测试和持续集成实践两个关键方面。以下是对这两个领域的简要介绍&#xff0c;并附带一些示例代码。 性能测试 性能测试是评估软件在特定条件下的性能表现的过程。对于Python应用程序&#xff0c;可以使用一些工具来进行性能测试&#xff0c;例如ps…

linux centos tomcat启动内存泄漏

tomcat启动内存泄漏 经过与开发沟通&#xff0c;结果是开发写了死循环&#xff0c;导致内存泄漏&#xff0c;上一次是开发少打了一个jar包&#xff0c;让开发查代码就行&#xff0c;重新更新代码

C++学习/复习6---内存管理(数据的位置/大小)/new、delete/内存相关面试题(malloc与new/内存泄漏)

一、内存中区域 1.不同数据的存储位置 2.数据占用内存大小 二、动态内存函数 三、new与delete 1.操作内置类型 2.操作自定义类型 四、operator new与operator delete 1.底层源码&#xff08;malloc/free&#xff09; 2.内置/自定义与构造和析构 3.举例 五、定位new表达式 1.举…

TiDB学习3:TiKV

目录 1. TiKV架构和作用 2. RocksDB 2.1 写入 2.2 查询 2.3 Column Families列簇 3. 分布式事务 3.1 事务流程 3.2 分布式事务流程 3.3 MVCC 4. Raft与Multi Raft 4.1 Raft日志复制 4.2 Raft Leader选举 5. TiKV- 读写 5.1 数据的写入 5.2 数据的读取ReadIndex …

教育大模型的发展现状、创新架构及应用展望

引言 从通用大模型到教育领域的专用大模型&#xff0c;是人工智能大模型技术深化发展的必然趋势。教育大模型不是在通用大模型基础上的微调和优化&#xff0c;而是以重构未来教育图景为目标、以开放算法模型架构为基础、以创新教育应用场景为核心的系统性变革。如何厘清教育大…

Linux汉化Jupyter Notebook

要在Linux系统中使Jupyter Notebook汉化&#xff0c;可以通过安装jupyterlab-language-pack-zh-CN扩展来实现。以下是具体步骤和示例代码&#xff1a; 打开终端。 执行以下命令以安装Jupyter Notebook的中文语言包&#xff1a; pip install jupyterlab-language-pack-zh-CN …

实现UI显示在最上面的功能

同学们肯定遇到过UI被遮挡的情况&#xff0c;那如何让UI显示在最前面呢&#xff0c;先看效果 原理:UI的排序方式是和unityHierarchy窗口的层级顺序有关的&#xff0c;排序在下就越后显示&#xff0c;所以按照这个理论&#xff0c;当我们鼠标指到UI的时候把层级设置到最下层就好…

香橙派 AIpro开发体验:使用YOLOV8对USB摄像头画面进行目标检测

香橙派 AIpro开发体验&#xff1a;使用YOLOV8对USB摄像头画面进行目标检测 前言一、香橙派AIpro硬件准备二、连接香橙派AIpro1. 通过网线连接路由器和香橙派AIpro2. 通过wifi连接香橙派AIpro3. 使用vscode 通过ssh连接香橙派AIpro 三、USB摄像头测试1. 配置ipynb远程开发环境1.…

超越连接:ZL-450边缘网关全面评测与应用案例

前言 在现代工业自动化和智能设备管理的背景下&#xff0c;对实时数据通信与设备监控的需求日益增加。ZL450边缘网关作为一款先进的串口通信解决方案&#xff0c;不仅满足了这些要求&#xff0c;还通过其多样的连接性和高效的数据处理能力&#xff0c;为企业带来了显著的效率提…

添加、修改和删除字典元素

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 由于字典是可变序列&#xff0c;所以可以随时在字典中添加“键-值对”。向字典中添加元素的语法格式如下&#xff1a; dictionary[key] value 参数…

为什么短剧突然爆火?背后究竟谁在为流量买单?

为什么短剧突然爆火&#xff1f;背后究竟谁在为流量买单&#xff1f; 文丨微三云营销总监胡佳东&#xff0c;点击上方“关注”&#xff0c;为你分享市场商业模式电商干货。 - 今年很多朋友交流的更多的商业热门话题就是“短剧”&#xff0c;目前我国拥有超10亿的短视频用户&a…

Android:将时间戳转换为本地时间格式

一、效果图 图1&#xff0c;中国的时间格式 图2&#xff0c;美国的时间格式 二、StringUtil.kt代码 import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* object StringUtil {fun formatTimestamp(currentTime: Long): String {var sdf Si…

【吊打面试官系列】Java高并发篇 - 什么是乐观锁和悲观锁?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是乐观锁和悲观锁?】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是乐观锁和悲观锁? 1、乐观锁&#xff1a; 就像它的名字一样&#xff0c;对于并发间操作产生的线程安全问题持乐观状态&#xff0c; 乐观锁认为竞争…

在没有足够测试数据的请情况下,如何验证前端页面的分页展示和渲染情况

问题描述&#xff1a;测试过程中&#xff0c;为了验证前端页面的展示效果及分页组件的展示情况&#xff0c;测试人员一般都会选择在数据库造数据&#xff0c;但遇到表格管理逻辑特别复杂的情况&#xff0c;可能会耗费大量的时间&#xff0c;此时我们可以选择使用工具模拟返回值…

【quarkus系列】构建可执行文件native image

目录 序言为什么选择 Quarkus Native Image&#xff1f;性能优势便捷的云原生部署 搭建项目构建可执行文件方式一&#xff1a;配置GraalVM方式二&#xff1a;容器运行错误示例构建过程分析 创建docker镜像基于可执行文件命令式构建基于dockerfile构建方式一&#xff1a;构建mic…

Geometry-Aware Attenuation Field Learning for Sparse-View CBCT Reconstruction

摘要&#xff1a; 锥形束CT&#xff08;Cone Beam Computed Tomography&#xff0c;CBCT&#xff09;是目前口腔医学中应用最广泛的一种成像方法&#xff0c;要重建一幅高质量的CBCT图像&#xff0c;需要进行数百次的X线投影&#xff08;即&#xff0c;衰减场&#xff09;在传…

三分钟“手撕”顺序表与ArrayList

前言&#xff1a; 实现顺序表的代码放开头&#xff0c;供大家更好的查阅&#xff0c;每个方法都有代码的实现。 其次我会讲解Java自带的ArrayList的实例&#xff0c;扩容机制ArrayList使用方法&#xff0c;遍历以及它的优缺点。 目录 一、自己实现的顺序表 二、Java的ArrayLi…