【动手学强化学习】part7-Actor-Critic算法

news2024/11/5 8:49:53

阐述、总结【动手学强化学习】章节内容的学习情况,复现并理解代码。

文章目录

  • 一、算法背景
    • 1.1 算法目标
    • 1.2 存在问题
    • 1.3 解决方法
  • 二、Actor-Critic算法
    • 2.1 必要说明
      • · 优势函数
    • 2.2 伪代码
      • · 算法流程简述
    • 2.3 算法代码
    • 2.4 运行结果
      • · 结果分析
    • 2.5 算法流程说明
      • · 初始化参数
      • · 初始化环境
      • · 初始化网络
      • · 采样episode
      • · 价值更新
      • · 策略更新
  • 三、疑问
  • 四、总结


一、算法背景

1.1 算法目标

给定“黑盒”环境,求解最优policy。

1.2 存在问题

前面介绍了DQN算法和REINFORCE算法,其中基于值函数的方法只学习一个价值函数,而基于策略的方法只学习一个策略函数。那么,一个很自然的问题是,有没有什么方法既学习价值函数,又学习策略函数呢?

1.3 解决方法

构建value_net网络进行价值函数的拟合,构建policy_net网络进行策略函数的拟合。

二、Actor-Critic算法

  • 🌟算法类型
    环境依赖:❌model-based ✅model-free
    价值估计:✅non-incremental ❌incremental
    价值表征:❌tabular representation ✅function representation
    学习方式:✅on-policy ❌off-policy
    策略表征:❌value-based ✅policy-based
    · non-incremental:(采样一个完整的episode后才进行网络更新,而不是像TD采样一个(s,a,r,s’)就更新Q值)
    · function representation:(value_net(critic)网络用于学习价值函数)
    · on-policy:(采样episode和优化的policy都是policy_net(actor))
    · policy-based:(Actor-Critic 算法本质上是基于策略的算法,因为这一系列算法的目标都是优化一个带参数的策略,只是会额外学习价值函数,从而帮助策略函数更好地学习)

2.1 必要说明

· 优势函数

REINFORCE 通过蒙特卡洛采样的方法对策略梯度的估计是无偏的,但是方差非常大,在policy_net网络更新过程中,损失函数(交叉熵)的设定采用 q π θ ( s t , a t ) q_{\piθ}(s_t,a_t) qπθ(st,at) 作为指导,该值是通过episode采样后蒙特卡洛法估计的。
在这个基础上,还可以使用其它函数作为指导,例如:
A π θ ( s t , a t ) = q π θ ( s t , a t ) − v π θ ( s t ) A_{\piθ}(s_t,a_t)=q_{\piθ}(s_t,a_t)-v_{\piθ}(s_t) Aπθ(st,at)=qπθ(st,at)vπθ(st)
有贝尔曼公式中定义:
v π ( s ) a = ∑ a π ( a ∣ s ) a q π ( s , a ) {v_\pi(s)}_{a}=\sum_a{\pi(a|s)}_{a}{q_\pi(s,a)} vπ(s)a=aπ(as)aqπ(s,a)
v π θ ( s t ) v_{\piθ}(s_t) vπθ(st)可看作为 q π θ ( s t , a t ) q_{\piθ}(s_t,a_t) qπθ(st,at) 的加权平均,若某个q(s,a)优于v(s),则代表这个q(s,a)肯定是在“平均线”以上的。我们将上式称之为“优势函数”

为什么要引入优势函数?答:加入了baseline(基线),即 v π θ ( s t ) v_{\piθ}(s_t) vπθ(st) 后,可以使得训练结果方差更小。

又根据q(s,a)的定义,可将优势函数转换为:
q π ( s , a ) = ∑ r p ( r ∣ s , a ) r + γ ∑ s ′ p ( s ′ ∣ s , a ) v π ( s ′ ) = r + γ v ( s ′ ) q_\pi(s,a)=\sum_rp(r|s,a)r+\gamma\sum_{s'}p(s'|s,a)v_\pi(s')=r+\gamma v(s') qπ(s,a)=rp(rs,a)r+γsp(ss,a)vπ(s)=r+γv(s)
A π θ ( s t , a t ) = r t + γ v π θ ( s t + 1 ) − v π θ ( s t ) A_{\piθ}(s_t,a_t)=r_t+\gamma v_{\pi_\theta}(s_{t+1})-v_{\pi_\theta}(s_t) Aπθ(st,at)=rt+γvπθ(st+1)vπθ(st)
而在actor-critic算法中,**通过训练value_net(critic)网络去估计 v π θ ( s t ) v_{\piθ}(s_t) vπθ(st)值。**从而采用优势函数去指导policy_net网络更新,即交叉熵的真值设定为优势函数

2.2 伪代码

在这里插入图片描述

· 算法流程简述

①初始化网络:初始化value_net和policy_net网络模型。
②采样episode:设定周期数num_episodes,循环迭代采样episode,对于单个episode而言,根据policy_net获取当前state的action,再与环境交互env.step,获得(s,a,r,s’,done)样本,并直至terminal state(done为True),将样本存储在字典中。
③价值更新:计算TD_target,计算损失函数(均方误差),对value_net进行梯度清零+反向传播+参数更新。
④策略更新:计算TD_delta(优势函数),计算损失函数(交叉熵),对policy_net进行梯度清零+反向传播+参数更新。
⑤终止判断:根据已产生episode个数是否达到num_episodes,判断算法是否终止,并输出policy。

2.3 算法代码

import gym
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
import rl_utils


class PolicyNet(torch.nn.Module):
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(PolicyNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return F.softmax(self.fc2(x), dim=1)


class PolicyNet(torch.nn.Module):
    '''策略网络是4-128-2的,输入为state,输出为action的归一化概率'''
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(PolicyNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return F.softmax(self.fc2(x), dim=1)


class ValueNet(torch.nn.Module):
    '''价值网络是4-128-1的,输入为state,输出为state value:v(s)的估计值'''
    def __init__(self, state_dim, hidden_dim):
        super(ValueNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, 1)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return self.fc2(x)


class ActorCritic:
    def __init__(self, state_dim, hidden_dim, action_dim, actor_lr, critic_lr,
                 gamma, device):
        # 策略网络
        self.actor = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
        self.critic = ValueNet(state_dim, hidden_dim).to(device)  # 价值网络
        # 策略网络优化器
        self.actor_optimizer = torch.optim.Adam(self.actor.parameters(),
                                                lr=actor_lr)
        self.critic_optimizer = torch.optim.Adam(self.critic.parameters(),
                                                 lr=critic_lr)  # 价值网络优化器
        self.gamma = gamma
        self.device = device

    def take_action(self, state):
        state = torch.tensor([state], dtype=torch.float).to(self.device)
        probs = self.actor(state)
        action_dist = torch.distributions.Categorical(probs)
        action = action_dist.sample()
        return action.item()

    def update(self, transition_dict):
        states = torch.tensor(transition_dict['states'],
                              dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).view(-1, 1).to(
            self.device)
        rewards = torch.tensor(transition_dict['rewards'],
                               dtype=torch.float).view(-1, 1).to(self.device)
        next_states = torch.tensor(transition_dict['next_states'],
                                   dtype=torch.float).to(self.device)
        dones = torch.tensor(transition_dict['dones'],
                             dtype=torch.float).view(-1, 1).to(self.device)

        # 时序差分目标
        td_target = rewards + self.gamma * self.critic(next_states) * (1 -
                                                                       dones)
        td_delta = td_target - self.critic(states)  # 时序差分残差
        log_probs = torch.log(self.actor(states).gather(1, actions))
        actor_loss = torch.mean(-log_probs * td_delta.detach())
        # 均方误差损失函数
        critic_loss = torch.mean(
            F.mse_loss(self.critic(states), td_target.detach()))
        self.actor_optimizer.zero_grad()
        self.critic_optimizer.zero_grad()
        actor_loss.backward()  # 计算策略网络的梯度
        critic_loss.backward()  # 计算价值网络的梯度
        self.actor_optimizer.step()  # 更新策略网络的参数
        self.critic_optimizer.step()  # 更新价值网络的参数


actor_lr = 1e-3
critic_lr = 1e-2
num_episodes = 1000
hidden_dim = 128
gamma = 0.98
device = torch.device("cuda") if torch.cuda.is_available() else torch.device(
    "cpu")

env_name = 'CartPole-v0'
env = gym.make(env_name)
env.seed(0)
torch.manual_seed(0)
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
agent = ActorCritic(state_dim, hidden_dim, action_dim, actor_lr, critic_lr,
                    gamma, device)

return_list = rl_utils.train_on_policy_agent(env, agent, num_episodes)


episodes_list = list(range(len(return_list)))
plt.plot(episodes_list, return_list)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Actor-Critic on {}'.format(env_name))
plt.show()

mv_return = rl_utils.moving_average(return_list, 9)
plt.plot(episodes_list, mv_return)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Actor-Critic on {}'.format(env_name))
plt.show()

2.4 运行结果

Iteration 0: 100%|██████████| 100/100 [00:01<00:00, 62.50it/s, episode=100, return=19.000]
Iteration 1: 100%|██████████| 100/100 [00:03<00:00, 32.51it/s, episode=200, return=58.600]
Iteration 2: 100%|██████████| 100/100 [00:04<00:00, 20.90it/s, episode=300, return=94.000]
Iteration 3: 100%|██████████| 100/100 [00:08<00:00, 11.72it/s, episode=400, return=187.100]
Iteration 4: 100%|██████████| 100/100 [00:10<00:00,  9.66it/s, episode=500, return=155.900]
Iteration 5: 100%|██████████| 100/100 [00:11<00:00,  8.53it/s, episode=600, return=196.000]
Iteration 6: 100%|██████████| 100/100 [00:12<00:00,  8.01it/s, episode=700, return=200.000]
Iteration 7: 100%|██████████| 100/100 [00:12<00:00,  7.82it/s, episode=800, return=200.000]
Iteration 8: 100%|██████████| 100/100 [00:14<00:00,  7.09it/s, episode=900, return=195.500]
Iteration 9: 100%|██████████| 100/100 [00:12<00:00,  8.16it/s, episode=1000, return=200.000]

在这里插入图片描述
在这里插入图片描述

· 结果分析

抖动情况相比 REINFORCE 算法有了一定程度的改进,这说明优势函数的引入减小了方差。

2.5 算法流程说明

· 初始化参数

actor_lr = 1e-3
critic_lr = 1e-2
num_episodes = 1000
hidden_dim = 128
gamma = 0.98
device = torch.device("cuda") if torch.cuda.is_available() else torch.device(
    "cpu")

· 初始化环境

env_name = 'CartPole-v0'
env = gym.make(env_name)
env.seed(0)
torch.manual_seed(0)

· 初始化网络

state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
agent = ActorCritic(state_dim, hidden_dim, action_dim, actor_lr, critic_lr,
                    gamma, device)
...
    def __init__(self, state_dim, hidden_dim, action_dim, actor_lr, critic_lr,
                 gamma, device):
        # 策略网络
        self.actor = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
        self.critic = ValueNet(state_dim, hidden_dim).to(device)  # 价值网络
        # 策略网络优化器
        self.actor_optimizer = torch.optim.Adam(self.actor.parameters(),
                                                lr=actor_lr)
        self.critic_optimizer = torch.optim.Adam(self.critic.parameters(),
                                                 lr=critic_lr)  # 价值网络优化器
        self.gamma = gamma
        self.device = device
...
class PolicyNet(torch.nn.Module):
    '''策略网络是4-128-2的,输入为state,输出为action的归一化概率'''
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(PolicyNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return F.softmax(self.fc2(x), dim=1)


class ValueNet(torch.nn.Module):
    '''价值网络是4-128-1的,输入为state,输出为state value:v(s)的估计值'''
    def __init__(self, state_dim, hidden_dim):
        super(ValueNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, 1)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return self.fc2(x)

policy_net(actor)设定为4-128-2的全连接层网络,输入为state=4维,输出为action概率=2维,其定义与REINFORCE算法中相同:
y = s o f t m a x ( f c 2 ( r e l u ( f c 1 ( x ) ) ) ) , x = s t a t e s y=softmax\left(fc_2\left(relu\left(fc_1\left(x\right)\right)\right)\right),x=states y=softmax(fc2(relu(fc1(x)))),x=states
value_net(critic)设定为4-128-1的全连接层网络,输入维state=4维,输出为state value=1维,其定义与DQN算法中类似:
y = f c 2 ( r e l u ( f c 1 ( x ) ) ) , x = s t a t e s y=fc_2(relu(fc_1(x))),x=states y=fc2(relu(fc1(x))),x=states
两个网络均采用Adam作为优化器。

· 采样episode

return_list = rl_utils.train_on_policy_agent(env, agent, num_episodes)
...
for i_episode in range(int(num_episodes/10)):
                episode_return = 0
                transition_dict = {'states': [], 'actions': [], 'next_states': [], 'rewards': [], 'dones': []}
                state = env.reset()
                done = False
                while not done:
                    action = agent.take_action(state)
                    next_state, reward, done, _ = env.step(action)
                    transition_dict['states'].append(state)
                    transition_dict['actions'].append(action)
                    transition_dict['next_states'].append(next_state)
                    transition_dict['rewards'].append(reward)
                    transition_dict['dones'].append(done)
                    state = next_state
                    episode_return += reward
                return_list.append(episode_return)
...
    def take_action(self, state):
        state = torch.tensor([state], dtype=torch.float).to(self.device)
        probs = self.actor(state)
        action_dist = torch.distributions.Categorical(probs)
        action = action_dist.sample()
        return action.item()

①获取初始state:state = env.reset()
②根据policy_net的输出采取action:agent.take_action(state)
③与环境交互,得到(s,a,r,s’,done):env.step(action)
④将样本添加至episode:transition_dict
⑤统计episode即时奖励累加值:episode_return += reward

· 价值更新

agent.update(transition_dict)
...
		states = torch.tensor(transition_dict['states'],
                              dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).view(-1, 1).to(
            self.device)
        rewards = torch.tensor(transition_dict['rewards'],
                               dtype=torch.float).view(-1, 1).to(self.device)
        next_states = torch.tensor(transition_dict['next_states'],
                                   dtype=torch.float).to(self.device)
        dones = torch.tensor(transition_dict['dones'],
                             dtype=torch.float).view(-1, 1).to(self.device)

        # 时序差分目标
        td_target = rewards + self.gamma * self.critic(next_states) * (1 - dones)
		# 均方误差损失函数
        critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))
        self.critic_optimizer.zero_grad()
		...
        critic_loss.backward()  # 计算价值网络的梯度
		...	
        self.critic_optimizer.step()  # 更新价值网络的参数

①将采样完的单个episode转换为tensor张量
②计算TD_target:td_target = rewards + self.gamma * self.critic(next_states) * (1 - dones)
③计算损失函数(均方误差):critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))
④value_net网络训练更新

· 策略更新

        # 时序差分目标
        td_target = rewards + self.gamma * self.critic(next_states) * (1 -dones)
        td_delta = td_target - self.critic(states)  # 时序差分残差
        log_probs = torch.log(self.actor(states).gather(1, actions))
        #交叉熵损失函数
        actor_loss = torch.mean(-log_probs * td_delta.detach())

        self.actor_optimizer.zero_grad()
		...
        actor_loss.backward()  # 计算策略网络的梯度
		...
        self.actor_optimizer.step()  # 更新策略网络的参数

①将采样完的单个episode转换为tensor张量
②计算优势函数:td_delta = td_target - self.critic(states)
③计算损失函数(交叉熵):actor_loss = torch.mean(-log_probs * td_delta.detach())
④policy_net网络训练更新

三、疑问

暂无

四、总结

  • Actor-Critic算法算是DQN与REINFORCE算法的结合,集成了值函数近似和策略梯度下降方法
  • ActorCritic 是囊括一系列算法的整体架构,目前很多高效的前沿算法都属于 Actor-Critic 算法

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL【二】

查询列 SELECT [ALL | DISTINCT ] * | 列名1[,……列名n] FROM 表名; 查询所有选课学生的学号&#xff0c;结果去除重复值 select distinct sno from sc; 选择行 查询满足条件的数据集 SELECT 字段列表 FROM 表名 WHERE 查询条件 查询不属于数学系或外国语系的学生全部信息 …

ElasticSearch - Bucket Selector使用指南

文章目录 官方文档Bucket Selector1. 定义2. 工作原理3. 使用场景与示例使用场景官方案例示例2 4. 注意事项5. 总结 官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html Bucket Selector https://www.elastic.co/guide/en/…

“死鱼眼”,不存在的,一个提词小技巧,拯救的眼神——将内容说给用户,而非读给用户!

视频录制时&#xff0c;死鱼眼问题常见 即便内容再好&#xff0c;眼神死板也会减分 痛点真痛&#xff1a;拍视频时容易紧张 面对镜头&#xff0c;许多人难免紧张 神情僵硬&#xff0c;眼神无光&#xff0c;甚至忘词 这不仅影响表现&#xff0c;还让人难以专注 忘我场景&#x…

PyQt5实战——多脚本集合包,UI以及工程布局(二)

个人博客&#xff1a;苏三有春的博客 系列往期&#xff1a; PyQt5实战——多脚本集合包&#xff0c;前言与环境配置&#xff08;一&#xff09; 布局 2.1 UI页面布局 整体框架分为分为三个部分&#xff0c;垂直分布。 第一个部分为功能选择按钮&#xff08;如UTF-8转换&#…

《Python网络安全项目实战》项目2 Python基础练习_总复习(1)

《Python网络安全项目实战》项目2 Python基础练习 总复习&#xff08;1&#xff09; 班级&#xff1a; 姓名&#xff1a; 实训成绩&#xff1a; 任务单成绩 &#xff1a; 输入用户名密码并将其输出打印。 userName _____________________ passWord ______________________ #输…

数组排序简介-基数排序(Radix Sort)

基本思想 将整数按位数切割成不同的数字&#xff0c;然后从低位开始&#xff0c;依次到高位&#xff0c;逐位进行排序&#xff0c;从而达到排序的目的。 算法步骤 基数排序算法可以采用「最低位优先法&#xff08;Least Significant Digit First&#xff09;」或者「最高位优先…

LangChain学习之路

何谓 LangChain&#xff1f;释放大语言模型潜能的利器 作为一种专为开发基于语言模型的应用而设计的框架&#xff0c;通过LangChain&#xff0c;我们不仅可以通过API调用如 ChatGPT、GPT-4、Llama 2 等大型语言模型&#xff0c;还可以实现更高级的功能。 我们相信&#xff0c…

二:Linux学习笔记(第一阶段)-- Linux命令

目录 Linux注意事项&#xff1a; Linux目录 Linux系统基础命令 1. 文件和目录操作 2. 文件查看和编辑 3. 文件权限和所有权 4. 系统信息 5. 网络命令 6. 文件查找 7. 压缩和解压缩 8. 系统管理 Linux注意事项&#xff1a; 严格区分大小写一切皆文件windows下的程序不…

嵌入式硬件重点(四)常用信号处理、放大电路、运算放大器(运放)基础篇

引言&#xff1a;在嵌入式硬件设计中&#xff0c;信号处理和放大电路是至关重要的组成部分。它们不仅影响系统的性能&#xff0c;还直接关系到数据的准确性和可靠性。随着嵌入式系统的广泛应用&#xff0c;对各种传感器和外部设备的信号进行有效处理显得尤为重要。 运算放大器&…

3D Gaussian Splatting代码详解(二):模型构建

3 模型构建 gaussians GaussianModel(dataset.sh_degree) 3.1 初始化函数 __init__ 构造函数 构造函数 __init__ 的主要作用是初始化 3D 高斯模型的各项参数和激活函数&#xff0c;用于生成 3D 空间中的高斯表示。 初始化球谐函数的参数&#xff1a; self.active_sh_degre…

自由学习记录(18)

动画事件的碰撞器触发 Physics 类的常用方法 RaycastHit hit; if (Physics.Raycast(origin, direction, out hit, maxDistance)) {Debug.Log("Hit: " hit.collider.name); } Physics.Raycast&#xff1a;从指定点向某个方向发射射线&#xff0c;检测是否与碰撞体…

[FE] React 初窥门径(四):React 组件的加载过程(render 阶段)

1. 回顾 前几篇文章中&#xff0c;我们采用了 VSCode 插件 CodeTour 来记录代码的执行过程&#xff0c; 并把相关的数据 .tour/ 放到了 github: thzt/react-tour 中。 截止到本文为之&#xff0c;我们总共记录了这些 code-tour&#xff0c; .tour/ ├── 2. 构建过程.tour ├─…

java毕业设计之基于Bootstrap的常州地方旅游管理系统的设计与实现(springboot)

项目简介 基于Bootstrap的常州地方旅游管理系统的设计与实现有下功能&#xff1a; 基于Bootstrap的常州地方旅游管理系统的设计与实现的主要使用者分为用户功能模块和管理员功能模块两大部分&#xff0c;用户可查看景点信息、景点资讯等&#xff0c;注册登录后可进行景点订票…

单链表OJ题(3):合并两个有序链表、链表分割、链表的回文结构

目录 一、合并两个有序链表 二、链表分割 三、链表的回文结构 u解题的总体思路&#xff1a; 合并两个有序链表&#xff1a;首先创建新链表的头节点&#xff08;哨兵位&#xff1a;本质上是占位子&#xff09;&#xff0c;为了减少一些判断情况&#xff0c;简化操作。然后我们…

为数据集而生的 SQL 控制台

随着数据集的使用量急剧增加&#xff0c;Hugging Face 社区已经变成了众多数据集默认存放的仓库。每月&#xff0c;海量数据集被上传到社区&#xff0c;这些数据集亟需有效的查询、过滤和发现。 Dataset Monthly Creations 每个月在 Hugging Face Hub 创建的数据集 我们现在非常…

简易了解Pytorch中的@ 和 * 运算符(附Demo)

目录 1. 基本知识2. 3. * 1. 基本知识 在 PyTorch 中&#xff0c; 和 * 运算符用于不同类型的数学运算&#xff0c;具体是矩阵乘法和逐元素乘法 基本知识 运算符功能适用场景示例矩阵乘法&#xff08;或点乘&#xff09;用于执行线性代数中的矩阵乘法C A B&#xff0c;其中…

JavaScript知识点梳理及案例实践

1. Date对象 创建Date对象 //方法1&#xff1a;不指定参数 var nowd1new Date(); console.log(nowd1.toLocaleString( )); //方法2&#xff1a;参数为日期字符串 var d2new Date("2004/3/20 11:12"); console.log(d2.toLocaleString( )); var d3new Date("04/…

推荐一款Windows维护和修复工具包:RepairKit

RepairKit是一个综合性的Java开发的Windows修复和维护工具包。该工具包旨在为用户提供一个专用的系统修复工具&#xff0c;并快速访问一些操作系统功能及其他附带的电脑维护软件。 RepairKit 提供了一个简单有效的解决方案&#xff0c;用于维护PC的顺畅运行。它包括自动修复/清…

cocos开发QA

目录 TS相关foreach循环中使用return循环延迟动态获取类属性 Cocos相关属性检查器添加Enum属性实现不规则点击区域使用cc.RevoluteJoint的enable激活组件无效本地存储以及相关问题JSON.stringify(map)返回{}数据加密客户端复制文本使用客户端方法热更新LabelOutline.color is d…

大数据新视界 -- 大数据大厂之 Impala 性能优化:数据存储分区的艺术与实践(下)(2/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…