DQN和DDQN(进阶版)

article/2025/7/20 22:55:55

来源:
*《第五章 深度强化学习 Q网络》.ppt --周炜星、谢文杰

一、前言

Q表格、Q网络与策略函数
在这里插入图片描述
在这里插入图片描述

Q表格是有限的离散的,而神经网络可以是无限的。

在这里插入图片描述

对于动作有限的智能体来说,使用Q网络获得当下状态的对于每个动作的 状态-动作值 。那么 arg max ⁡ Q ( a , s ; θ ) = a b e s t \argmax Q(a,s; \theta) =a_{best} argmaxQ(a,s;θ)=abest ,那么我们对当前的状态s,会有一个最佳的选择 a b e s t a_{best} abest ,选择的依据是策略 θ \theta θ. 我们的目标是获得最优的策略 θ ∗ \theta^* θ.即优化 θ \theta θ,显然神经网络可以通过梯度下降的方法获得最优的策略 θ ∗ \theta^* θ.

二、Q-learning

DQN 算法全称是 Deep Q Network,基于经典强化学习算法 Q-learning 演化而来,Q-learning 作为强化学习的重要算法,有
着悠久的历史,在强化学习发展和应用过程中发挥了重要作用.在 Q-learning 算法中,状态-动作值函数 Q(s, a) 的更新公式为:
在这里插入图片描述

  1. Q-learning是借助于 Q-Table 的, 不存在 策略隐形表达(即关于 θ \theta θ的参数)
  2. 其MDP chain为: S → a / r S ′ → a ′ / r ′ S ′ ′ → ⋯ S \stackrel{a/r}→S'\stackrel{a'/r'}→S'' \to \cdots Sa/rSa/rS′′ 状态S在动作a下变为状态S’,并获得及时奖励r;……

2.1 代码示例

import numpy as np
import random# 定义网格世界环境
class GridWorld:def __init__(self, rows, cols, start, goal):self.rows = rowsself.cols = colsself.start = startself.goal = goalself.state = startdef reset(self):self.state = self.startreturn self.statedef step(self, action):x, y = self.stateif action == 0:  # 上x = max(x - 1, 0)elif action == 1:  # 下x = min(x + 1, self.rows - 1)elif action == 2:  # 左y = max(y - 1, 0)elif action == 3:  # 右y = min(y + 1, self.cols - 1)self.state = (x, y)reward = -1  # 每一步的惩罚done = self.state == self.goalif done:reward = 100  # 到达目标的奖励return self.state, reward, done# Q-learning 算法
class QLearning:def __init__(self, env, alpha=0.1, gamma=0.99, epsilon=0.1, episodes=1000):self.env = envself.alpha = alpha  # 学习率self.gamma = gamma  # 折扣因子self.epsilon = epsilon  # 探索率self.episodes = episodesself.q_table = np.zeros((env.rows, env.cols, 4))  # Q 表 ,初始为全0def choose_action(self, state):if random.uniform(0, 1) < self.epsilon:return random.choice([0, 1, 2, 3])  # 探索else:return np.argmax(self.q_table[state])  # 利用def train(self):for episode in range(self.episodes):state = self.env.reset()done = Falsewhile not done:action = self.choose_action(state)  #根据当前的状态,按照epsion策略选择动作(可能随机,可能最佳)next_state, reward, done = self.env.step(action)best_next_action = np.argmax(self.q_table[next_state]) #获得next_state根据Q表格最好的动作td_target = reward + self.gamma * self.q_table[next_state][best_next_action]#根据 next_state 和 best_next_action 获得未来的收益,再加上即使奖励reward,即当前的状态和动作的价值td_error = td_target - self.q_table[state][action]self.q_table[state][action] += self.alpha * td_errorstate = next_stateif (episode + 1) % 100 == 0:print(f"Episode {episode + 1}/{self.episodes} completed")def test(self):state = self.env.reset()done = Falsepath = [state]while not done:action = np.argmax(self.q_table[state])state, _, done = self.env.step(action)path.append(state)print("Path taken:", path)# 主程序
if __name__ == "__main__":# 定义网格世界rows, cols = 5, 5start = (0, 0)goal = (4, 4)env = GridWorld(rows, cols, start, goal)# 初始化 Q-learningq_learning = QLearning(env, alpha=0.1, gamma=0.99, epsilon=0.1, episodes=1000)# 训练q_learning.train()# 测试q_learning.test()

三、DQN

深度强化学习算法中值函数或策略函数一般使用深度神经网络逼近或近似,用 参数化的状态-动作值函数 Q(s, a; θ) 逼近 Q(s, a),可如下表示
在这里插入图片描述
深度强化学习算法在更新过程中不直接对状态-动作值函数Q(s, a; θ) 的数值进行更新,而是更新近似状态-动作值函数Q(s, a; θ) 的深度神经网络模型参数 θ,可表示为
在这里插入图片描述

3.1 Experience Replay 经验回放

经典的 DQN 算法中有一个关键技术,叫经验回放。智能体在与环境交互过程中获得的经验数据会保存在 经验池(Replay Buffer)之中。经验池中的数据存放形式如下:
在这里插入图片描述
经验池存储满后,我们可以将旧的经验样本数据剔除,保存新的经验样本数据.

from collections import deque
# 创建一个最大长度为 5000 的 deque
dq = deque(maxlen=5000)
# 向 deque 中添加元素
for i in range(5005):dq.append([单一经验条(见Eq.5)])

经验存储

self.replay_buffer = deque(maxlen=buffer_size) ##预先定义的存储池,下为存储命令
def store_experience(self, state, action, reward, next_state, done):self.replay_buffer.append((state, action, reward, next_state, done))

批量采样

batch = random.sample(self.replay_buffer, self.batch_size)
###上面是抽样,下面是数据格式转化
states, actions, rewards, next_states, dones = zip(*batch)states = torch.tensor(states, dtype=torch.float32)
actions = torch.tensor(actions, dtype=torch.int64)
rewards = torch.tensor(rewards, dtype=torch.float32)
next_states = torch.tensor(next_states, dtype=torch.float32)
dones = torch.tensor(dones, dtype=torch.float32)

3.2 目标网络

目标:我们要更新 Q ( s , a ; θ ) Q(s,a;\theta) Q(s,a;θ)中的参数 θ \theta θ,那么具体怎么更新呢?
已知:

  1. Q-learning的更新公式 (Eq.1 ),有当前的状态-动作值Q(s,a)(Q表格中的值与真正的值有误差的)随着 迭代关系 逐渐接近 真正的状态-动作值
    r + γ max ⁡ a ′ Q ( s ′ , a ′ ) r +\gamma \max_{a'} Q(s',a') r+γmaxaQ(s,a) =当下奖励+未来的增益
    G a i n t = r + γ G a i n t + 1 Gain_t =r +\gamma Gain_{t+1} Gaint=r+γGaint+1
    在Q-learing中其实默认了一种策略,就是选择next_state中best_next_action.即选择使下一个状态收益最大的动作。
    由于Q-表格 是有限的,随着迭代次数的增加,所以状态是会大量重复出现,且(状态-动作值)会逐渐有区分,所以会有
    在这里插入图片描述
  2. DQN算法根据Q-learn改编过来的。即有目标如下:
    Q ( s , a ; θ ) → ( r + γ max ⁡ a ′ Q ( s ′ , a ′ ; θ ∗ ) ) Q(s,a;\theta) \to \quad (r+\gamma \max_{a'}Q(s',a';\theta^*)) Q(s,aθ)(r+γamaxQ(s,a;θ))
    且有 θ \theta θ是当下的策略, θ ∗ \theta^* θ是最终的最佳策略。那么显然,当我们处于 θ \theta θ的策略的时候(起点),我们无法直接找到 θ ∗ \theta^* θ(终点).为什么不能直接找到?(因为可能不收敛)

测试:
假设有一个DQN网络已经定义好了(如3.3)。那么智能体有 Q-网络(即状态-动作值函数网络), 优化器, 损失函数.如下所示:

class DQN_Agent:def __init__(self,)###省略了很多self.q_network = DQN(self.state_dim, self.action_dim) #在线网络,Q网络self.optimizer = optim.Adam(self.q_network.parameters(), lr=self.lr)#优化器self.loss_fn = nn.MSELoss() #损失函数

没有目标网络直接优化:

Q t a r g e t = R t + γ ⋅ max ⁡ Q ( n e x t s t a t e , a c t i o n ) Q_{target}=R^t+ \gamma \cdot \max Q(nextstate ,action) Qtarget=Rt+γmaxQ(nextstate,action)
Q c a l c u l a t e = Q ( c u r r e n t s t a t e ) Q_{calculate} =Q(currentstate) Qcalculate=Q(currentstate)

###没有目标网络的时候,计算Q_target是用相同的Q网络计算的
q_values = self.q_network(states) #Q(current_state)
next_q_values = self.q_network(next_states) #Q(next_curente)
q_target = q_values.clone()for i in range(self.batch_size):if dones[i] == 1:q_target[i, actions[i]] = rewards[i]else:q_target[i, actions[i]] = rewards[i] + self.gamma * torch.max(next_q_values[i])loss = self.loss_fn(q_values, q_target)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

❓问题:Q 网络的更新可能会导致目标值(q_target)和预测值(q_values)之间的相关性过高。这种高相关性会导致训练过程不稳定,甚至发散
✋解决:我们在中间增加了一个目标网络Target network作为中点(过渡点)。
目标网络的作用
目标网络是一个固定频率更新的网络,它的参数不是每次训练都更新,而是定期从主网络(也称为在线网络)复制过来。这样做的目的是为了降低目标值和预测值之间的相关性,从而提高训练的稳定性

  1. 目标网络 同原来的神经网络(被称为 行为网络behavior network) 的结构完全相同.因此,Q网络(在线网络,动作-状态值函数网络)初始化。
    初始阶段:根据设定的 结构 和随机初始化参数 θ \theta θ. 见方法一中,参数结构分为三层:输入层self.fc1(设定的是全连接网络),隐藏层self.fc2(设定的是全连接),输出层self.fc3(设定的是全链接网络)。而方法二中,我们增加新的结构(即卷积网络),也固定了 θ \theta θ的参数的随机初始化(高斯分布)。
  2. 初始阶段目标网络同Q网络相同。只是更新频率和更新方式不同!!! Q-网络是通过 优化器(根据梯度下降法)每次迭代更新一次参数,Target-网络 更新速度慢于前者,且是通过直接复制参数的方法更新!
### 目标网络 初始的复制过程
self.q_network = DQN(self.state_dim[0], self.action_dim)  # 在线网络
self.target_network = DQN(self.state_dim[0], self.action_dim)  # 目标网络,结构复制
self.target_network.load_state_dict(self.q_network.state_dict())  # 初始化目标网络,参数复制self.target_network.eval()  # 设置为目标网络模式
self.optimizer = optim.Adam(self.q_network.parameters(), lr=self.lr)
self.loss_fn = nn.MSELoss()
self.target_update_frequency = target_update_frequency  # 目标网络更新频率
self.update_count = 0  # 更新计数器####中间省略了
#####更新频率 ,q_values = self.q_network(states)  # 使用在线网络计算 Q 值
next_q_values = self.target_network(next_states)  # 使用目标网络计算下一个状态的 Q 值 
####值得提醒的是,这里用的是taget_network,而不是Q_network,与上一段代码不同
q_target = q_values.clone()for i in range(self.batch_size):if dones[i] == 1:q_target[i, actions[i]] = rewards[i]else:q_target[i, actions[i]] = rewards[i] + self.gamma * torch.max(next_q_values[i])loss = self.loss_fn(q_values, q_target)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step() ###这一小部分是优化器对self.q_network的更新,即每轮训练就会更新一次Q-newrok# 更新目标网络
self.update_count += 1
if self.update_count % self.target_update_frequency == 0:self.target_network.load_state_dict(self.q_network.state_dict())##### 目标网络的更新的步幅是self.target_update_frequency ,且不是通过优化器更新的,而是直接对Q-network参数的复制

3.3 DQN的网络定义案例

## 方法一
class DQN(nn.Module):def __init__(self, input_dim, output_dim):super(DQN, self).__init__()self.fc1 = nn.Linear(input_dim, 128)self.fc2 = nn.Linear(128, 128)self.fc3 = nn.Linear(128, output_dim)def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))x = self.fc3(x)return x
### 方法二:使用高斯分布对参数进行随机初始化
# 定义DQN网络
class DQN(nn.Module):def __init__(self, input_channels, output_dim):super(DQN, self).__init__()# 第一层:卷积层self.conv1 = nn.Conv2d(input_channels, 16, kernel_size=3, stride=1, padding=1)self.fc1 = nn.Linear(16 * 8 * 8, 128)  # 将卷积层的输出展平self.fc2 = nn.Linear(128, 128)self.fc3 = nn.Linear(128, output_dim)self.initialize_weights()def forward(self, x):x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))x = self.fc3(x)return xdef initialize_weights(self):# 使用高斯分布初始化权重for layer in self.modules():if isinstance(layer, nn.Linear):nn.init.normal_(layer.weight, mean=0.0, std=0.1)  # 高斯分布,均值为0,标准差为0.1if layer.bias is not None:nn.init.constant_(layer.bias, 0.0)  # 偏置初始化为0

3.4 环境交互

在与环境交互过程中,智能体采用 ϵ-贪心策略生成轨迹,ϵ-贪心策略表示为
在这里插入图片描述
以代码为例

###选择动作的策略
def choose_action(self, state):if random.random() < self.epsilon:return random.randint(0, self.action_dim - 1) #随机选择else:state = torch.tensor(state, dtype=torch.float32).unsqueeze(0)  # 添加批次维度q_values = self.q_network(state)return torch.argmax(q_values).item() ##目标网络值最大的动作

3.5 损失函数

损失函数 值得是 预测值和实际值之间的误差
实际值(或者说 TD目标值),根据公式定义来计算。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其损失函数代码和网络的更新 见3.3 。

3.6 伪代码

在这里插入图片描述

四、DDQN

附录一:DQN案例代码

A.1 智能体

目标网络的引入:self.target_network 是目标网络,它的参数定期从在线网络(self.q_network)复制过来。在 __init__ 方法中,目标网络的参数初始化为在线网络的参数。
目标网络的更新:在 replay 方法中,使用目标网络计算下一个状态的 Q 值(next_q_values)。每次调用 replay 方法时,更新计数器 self.update_count 增加 1。当 self.update_count 达到目标网络更新频率(self.target_update_frequency)时,将在线网络的参数复制到目标网络。
目标网络的作用:目标网络的参数更新频率较低,这使得目标值(q_target)相对稳定,从而减少了目标值和预测值之间的相关性,提高了训练的稳定性。
class DQN_Agent:def __init__(self, env, lr=0.001, gamma=0.99, epsilon=1.0, epsilon_decay=0.995, min_epsilon=0.01, buffer_size=10000, batch_size=32, target_update_frequency=1000):self.env = envself.lr = lrself.gamma = gammaself.epsilon = epsilonself.epsilon_decay = epsilon_decayself.min_epsilon = min_epsilonself.buffer_size = buffer_sizeself.batch_size = batch_sizeself.replay_buffer = deque(maxlen=buffer_size)self.state_dim = (1, 8, 8)  # 输入维度:1 个通道,8x8 的图像self.action_dim = 4  # 动作维度 (上, 下, 左, 右)self.q_network = DQN(self.state_dim[0], self.action_dim)  # 在线网络self.target_network = DQN(self.state_dim[0], self.action_dim)  # 目标网络self.target_network.load_state_dict(self.q_network.state_dict())  # 初始化目标网络self.target_network.eval()  # 设置为目标网络模式self.optimizer = optim.Adam(self.q_network.parameters(), lr=self.lr)self.loss_fn = nn.MSELoss()self.target_update_frequency = target_update_frequency  # 目标网络更新频率self.update_count = 0  # 更新计数器def choose_action(self, state):if random.random() < self.epsilon:return random.randint(0, self.action_dim - 1)else:state = torch.tensor(state, dtype=torch.float32).unsqueeze(0)  # 添加批次维度q_values = self.q_network(state)return torch.argmax(q_values).item()def store_experience(self, state, action, reward, next_state, done):self.replay_buffer.append((state, action, reward, next_state, done))def train(self, episodes):for episode in range(episodes):state = self.env.reset()done = Falsewhile not done:action = self.choose_action(state)next_state, reward, done = self.env.step(action)self.store_experience(state, action, reward, next_state, done)state = next_stateif len(self.replay_buffer) > self.batch_size:self.replay()self.epsilon = max(self.min_epsilon, self.epsilon * self.epsilon_decay)if (episode + 1) % 100 == 0:print(f"Episode {episode + 1}/{episodes} completed, epsilon: {self.epsilon:.4f}")def replay(self):batch = random.sample(self.replay_buffer, self.batch_size)states, actions, rewards, next_states, dones = zip(*batch)states = torch.tensor(states, dtype=torch.float32)actions = torch.tensor(actions, dtype=torch.int64)rewards = torch.tensor(rewards, dtype=torch.float32)next_states = torch.tensor(next_states, dtype=torch.float32)dones = torch.tensor(dones, dtype=torch.float32)q_values = self.q_network(states)  # 使用在线网络计算 Q 值next_q_values = self.target_network(next_states)  # 使用目标网络计算下一个状态的 Q 值q_target = q_values.clone()for i in range(self.batch_size):if dones[i] == 1:q_target[i, actions[i]] = rewards[i]else:q_target[i, actions[i]] = rewards[i] + self.gamma * torch.max(next_q_values[i])loss = self.loss_fn(q_values, q_target)self.optimizer.zero_grad()loss.backward()self.optimizer.step()# 更新目标网络self.update_count += 1if self.update_count % self.target_update_frequency == 0:self.target_network.load_state_dict(self.q_network.state_dict())def test(self):state = self.env.reset()done = Falsepath = [state]while not done:state = torch.tensor(state, dtype=torch.float32).unsqueeze(0)  # 添加批次维度q_values = self.q_network(state)action = torch.argmax(q_values).item()state, _, done = self.env.step(action)path.append(state)print("Path taken:", path)

A.2 网络结构

输入层:输入是一个 8x8 的灰度图像,通道数为 1。输入维度为 (batch_size, 1, 8, 8)。
第一层:卷积层:使用 nn.Conv2d,输入通道为 1,输出通道为 16,卷积核大小为 3x3,步长为 1,填充为 1。输出维度为 (batch_size, 16, 8, 8)。应用 ReLU 激活函数。
第二层:全连接层:将卷积层的输出展平为一维张量,维度为 (batch_size, 16 * 8 * 8)。使用 nn.Linear,输入维度为 16 * 8 * 8,输出维度为 128。应用 ReLU 激活函数。
第三层:全连接层:使用 nn.Linear,输入维度为 128,输出维度为 128。应用 ReLU 激活函数。
输出层:使用 nn.Linear,输入维度为 128,输出维度为 output_dim(动作数量,这里是 4)。输出层不使用激活函数,直接输出 Q 值。
class DQN(nn.Module):def __init__(self, input_channels, output_dim):super(DQN, self).__init__()# 第一层:卷积层self.conv1 = nn.Conv2d(input_channels, 16, kernel_size=3, stride=1, padding=1)# 第二层:全连接层self.fc1 = nn.Linear(16 * 8 * 8, 128)  # 将卷积层的输出展平# 第三层:全连接层self.fc2 = nn.Linear(128, 128)# 第四层:输出层self.fc3 = nn.Linear(128, output_dim)self.initialize_weights()def forward(self, x):x = torch.relu(self.conv1(x))  # 第一层卷积后应用 ReLUx = x.view(x.size(0), -1)  # 展平x = torch.relu(self.fc1(x))  # 第一层全连接后应用 ReLUx = torch.relu(self.fc2(x))  # 第二层全连接后应用 ReLUx = self.fc3(x)  # 输出层return xdef initialize_weights(self):# 使用高斯分布初始化权重for layer in self.modules():if isinstance(layer, nn.Conv2d) or isinstance(layer, nn.Linear):nn.init.normal_(layer.weight, mean=0.0, std=0.1)  # 高斯分布,均值为0,标准差为0.1if layer.bias is not None:nn.init.constant_(layer.bias, 0.0)  # 偏置初始化为0

A.3 环境

环境结构说明:

网格世界:网格世界是一个 8x8 的网格,智能体可以在其中向上、下、左、右移动。智能体的目标是从起点 start 到达终点 goal。
状态表示:智能体的状态是一个二维坐标 (x, y)。状态被转换为一个 8x8 的灰度图像,智能体的位置用 1 表示,其余位置用 0 表示。
奖励机制:每一步的奖励为 -1。到达目标位置的奖励为 100。
输入维度:环境的输入是一个 8x8 的灰度图像,通道数为 1。输入维度为 (1, 8, 8)。
class GridWorld:def __init__(self, rows, cols, start, goal):self.rows = rowsself.cols = colsself.start = startself.goal = goalself.state = startdef reset(self):self.state = self.startreturn self.state_to_image(self.state)def step(self, action):x, y = self.stateif action == 0:  # 上x = max(x - 1, 0)elif action == 1:  # 下x = min(x + 1, self.rows - 1)elif action == 2:  # 左y = max(y - 1, 0)elif action == 3:  # 右y = min(y + 1, self.cols - 1)self.state = (x, y)reward = -1  # 每一步的惩罚done = self.state == self.goalif done:reward = 100  # 到达目标的奖励return self.state_to_image(self.state), reward, donedef state_to_image(self, state):# 将状态转换为 8x8 的灰度图像img = np.zeros((8, 8), dtype=np.float32)img[state[0], state[1]] = 1.0  # 将智能体的位置设置为 1return img

http://www.hkcw.cn/article/rutYyxxjsk.shtml

相关文章

新视讯影视官网入口,影视动漫在线播放网站

新视讯影视是一个免费为广大追剧迷提供在线播放服务的影视平台&#xff0c;深受众多影视爱好者的喜爱。它涵盖了大量免费的VIP电视剧资源、最新上映的大片、好看的综艺节目以及动漫视频&#xff0c;是一个播放速度快、资源多的免费影视网站。用户无需注册或登录&#xff0c;即可…

张家界溶洞垃圾已清运2.7吨 排污事件引发关注

近日,有网友反映张家界市慈利县一处天然溶洞遭到人为排污,导致溶洞被污染。相关话题引发了广泛关注。据慈利县融媒体中心6月1日发布的最新视频,经过7天的努力,杨家坡溶洞内的垃圾已清理打捞出2.7吨。相关视频显示,溶洞内垃圾正在被装袋并通过吊机吊出,旁边已经摆放着大量…

杭州机场迎首批入境旅客 免签新政促便利

6月1日下午,杭州口岸迎来了首批享受免签新政的南美洲旅客。为便利中外人员往来,中方决定扩大免签国家范围,自2025年6月1日起至2026年5月31日,巴西、阿根廷、智利、秘鲁、乌拉圭五个国家持普通护照的人员来华经商、旅游观光、探亲访友、交流访问或过境不超过30天,可免办签证…

余承东为何“炮轰”友商 华为销量不及小米

华为终端BG董事长余承东在2025未来汽车先行者大会上提到,有一家公司仅凭一款车型就取得了巨大成功。尽管该公司的产品质量和智能驾驶能力并不出色,但凭借强大的品牌影响力和流量支持,依然实现了热销。余承东表示,华为的产品在质量、体验和性能方面都优于这家公司,但在销量…

患者服临床试验抗癌药致重症肺炎 临床研究用药疑云

在重庆39度的高温天气里,刘呈富推着轮椅艰难地爬坡过坎,汗流浃背,呼吸短促。坐在轮椅中的李忠美怀里抱着30多斤重的制氧机,拖着枕头状氧气袋,同样疲惫不堪。李忠美是一位45岁的宫颈癌患者,抗癌已有12年。2023年她的病情再次复发,医生推荐她使用一种名为卡度尼利单抗的注…

190头猪高速中暑晕厥消防员浇水救援 高温下的生命接力

近日,一段发生在山东泰安的救援视频在网络上广为流传,既让人忍俊不禁又感动不已。在地面温度高达43℃的酷热天气下,一辆运输生猪的货车行驶在高速公路上,车厢内的190头猪因高温中暑晕厥,情况危急。消防员迅速赶到现场展开了一场特殊的救援行动。货车司机当时心急如焚,这些…

WEBSTORM前端 —— 第3章:移动 Web —— 第5节:响应式网页

目录 一、媒体查询 1.媒体查询 2.媒体查询 – 书写顺序 3.案例 – 左侧隐藏 4.媒体查询 – 完整写法&#xff08;了解&#xff09; 5.媒体查询 – 外部CSS 二、Bootstrap 1.Bootstrap – 简介 2.Bootstrap – 栅格系统 3.Bootstrap – 全局样式 4.Bootstrap – 组件&…

dify本地部署的怎么更新新版本

首先拉取源码到你的dify的docker下面 然后解压 在原来的dify下面拉取docke-compose up -d&#xff08;生死在此一举&#xff09; 把volmes文件夹替换掉 暂停掉docker和服务 systemctl stop docker docker compose down 之后拉取失败可能是你doker的源有问题替换一下就可以成…

韩国人到底吃不吃得起肉 统计数据揭示真相

关于韩国人吃不吃得起肉的问题,可以通过韩国国家统计局KOSIS的门户网站查看相关统计数据。在该网站上搜索“고기(肉)”,可以找到2022-2023年的《首尔市民食品调查》数据,发布日期为2024年7月18日。数据显示,首尔居民每两周吃一次肉的比例为20.1%,每周吃1到3次肉的比例为…

斯维托丽娜晋级法网女单八强 胜利瞬间庆祝

6月1日,在巴黎举行的2025法国网球公开赛女子单打第四轮比赛中,乌克兰选手斯维托丽娜以2比1战胜意大利选手保利尼,成功晋级八强。比赛中,斯维托丽娜展现了出色的状态,多次在得分后庆祝,并在关键时刻回球和发球表现出色。责任编辑:zhangxiaohua

“战火”不断,各方发声 车市竞争白热化

端午小长假第二天,多家车企公布了5月份销量或交付量数据,不少企业同比出现翻倍增长。行业竞争激烈,工信部和中汽协纷纷发声,或将为产业带来一些变化。6月1日,多家车企发布了5月销售或交付量数据。比亚迪新能源车销量遥遥领先,达到38.25万辆,同比增长15.27%。吉利紧随其后…

大熊猫“顶流”花花人气爆棚 排队两小时只为一睹真容

六一儿童节正值端午假期,在四川成都大熊猫繁育研究基地,游客络绎不绝。基地内的“顶流女明星”花花人气爆棚。6月1日下午,参观大熊猫花花的游客排出了长长的队伍,保安工作人员提醒排队时间约两个小时,但能否看到花花也要碰运气,因为大熊猫有可能会自己回到室内,并不是排…

云南西藏等地需关注次生灾害 强降雨持续袭击

昨天,南方地区出现较强降雨,主要集中在安徽、湖北、广西、云南等地。其中,广西桂林和柳州、贵州黔南、云南德宏等地出现了大暴雨。今天是端午假期的最后一天,南方地区的雨区继续南移。长江中下游一带的降雨逐渐减弱并结束,而华南及云南、西藏东南部等地将出现较强降雨。华…

AI精准挖掘SEO关键词策略

内容概要 在数字化营销竞争加剧的背景下&#xff0c;AI技术正深度重构SEO关键词挖掘的核心逻辑。传统依赖人工经验与工具筛选的模式&#xff0c;逐渐被基于自然语言处理&#xff08;NLP&#xff09;的语义分析技术取代&#xff0c;使关键词研究从单一词频统计转向用户意图预测…

谢晋复盘U16国少夺冠历程 齐心协力创佳绩

在呼和浩特国际足球锦标赛中,中国U16男足以5-2战胜澳大利亚队,最终以2胜1平的成绩夺得冠军。赛后,打入最后一球的谢晋回顾了夺冠的过程。谢晋表示,在对阵越南的比赛中,球队以2-2战平,最后有些体力不支。为了打好这场比赛,全队齐心协力,共同创造了这场胜利。教练在赛前详…

章子怡晒女儿儿子 母爱满满共度时光

中年女星离婚并不意味着世界末日,相反,她们感到开心和快乐。如果在一起不快乐,没有必要勉强。42岁的陈妍希带着儿子在大理庆生,估计之后还会带孩子一起欢度六一。她看起来状态很不错,还开直播和粉丝一起庆祝生日,有儿子陪伴在身边让她非常开心。章子怡离婚后也晒出多张与…

假期余额不足1天!北京气温开始明显走高 防暑降温模式开启

今天是端午假期的最后一天,北京市气象台预报显示,白天晴转多云,西部北部有分散性阵雨或雷阵雨。北风从一级逐渐增强到三四级,阵风可达六七级,最高气温31℃。夜间天气转为多云到晴,北风三级左右减弱至一级,最低气温15℃。受冷空气影响,白天北风自西向东逐渐加大,需注意…

女子刚要进厕所一车辆撞破墙冲进来 邻居家四轮车误撞

据‬荔枝‬新闻‬6月‬1日‬报道‬。5月29日,黑龙江牡丹江,女子刚要进入厕所,一辆‬四轮车突然把围墙撞倒。当事人接受采访称,是邻居家的四轮车,拐弯方向盘没打过去,无人受伤。声明:取材网络、谨慎鉴别责任编辑:zx0001

地磁暴来了 黑龙江多地现粉色极光 梦幻粉紫映夜空

6月2日,多位网友在黑龙江省密山市、佳木斯市等地拍摄到了罕见的粉色极光。天空被渲染成梦幻般的粉紫色,景象如梦如幻,宛若仙境。其中一位视频发布者表示,他在佳木斯市郊区福胜村江边拍到了粉色极光。从晚上9时左右开始拍摄,一直持续到次日凌晨1时,使用的是延时摄影技术。…

北京:预警!爆发!今明两天或将出现地磁暴与极光

北京时间5月31日7时45分左右,太阳活动区14100爆发耀斑,软X射线流量快速上升,并在8时5分达到M8.1级中等耀斑强度。中国气象局国家空间天气监测预警中心预计未来三天可能发生地磁暴。2日左右,我国北部有机会出现较为明显的极光,部分地区甚至可能出现红绿复合极光。地磁暴是太…