强化学习-价值学习算法
Sarsa
理论解释
Sarsa是基于时序差分算法的,它的公式非常简单且易理解,不像策略梯度算法那样需要复杂的推导过程。
Sarsa的核心函数是 Q ( s , a ) Q(s, a) Q(s,a),它的含义是在状态 s s s下执行 a a a,在后续轨迹中获取的期望总奖励。时序差分算法的核心思想,就是用当前获得的奖励加上下一个状态的价值估计来作为当前状态的价值估计,在Sarsa算法中体现为如下公式:
Q ( s t , a t ) ← r t + γ ⋅ Q ( s t + 1 , a t + 1 ) Q(s_t, a_t) \leftarrow r_t + \gamma \cdot Q(s_{t+1}, a_{t+1}) Q(st,at)←rt+γ⋅Q(st+1,at+1)
在这里,我们使用神经网络来拟合 Q ( s , a ) Q(s, a) Q(s,a),在选取动作时采用 ϵ \epsilon ϵ-greedy策略,即有 ϵ \epsilon ϵ的概率随机选取一个动作, 1 − ϵ 1 - \epsilon 1−ϵ的概率选取 Q ( s , a ) Q(s, a) Q(s,a)最大的动作。
按照此策略我们在状态 s t s_t st时选取动作 a t a_t at,此时环境会返回状态 s t + 1 s_{t+1} st+1,则再按照此策略选取动作 a t + 1 a_{t+1} at+1,然后按照上述的公式来更新 Q ( s , a ) Q(s, a) Q(s,a)参数。由于这里我们使用神经网络来拟合参数,所以我们更新的方式是计算loss值,然后进行梯度下降。如下面所示,其中 l o s s f n loss_{fn} lossfn是指根据现有值和目标值来计算loss值的函数,在代码中采取的MSE均方误差函数。
q v a l u e = Q ( s , a ) q_{value} = Q(s, a) qvalue=Q(s,a)
q t a r g e t = r t + γ ⋅ Q ( s t + 1 , a t + 1 ) q_{target} = r_t + \gamma \cdot Q(s_{t+1}, a_{t+1}) qtarget=rt+γ⋅Q(st+1,at+1)
l o s s = l o s s f n ( q v a l u e , q t a r g e t ) loss = loss_{fn}(q_{value}, q_{target}) loss=lossfn(qvalue,qtarget)
代码
环境为python3.12,各依赖包均为最新版。
import random
import gymnasium as gym
import torch
import torch.nn as nn
from torch import tensorclass QNet(torch.nn.Module):def __init__(self, action_state_dim, hidden_dim):"""网络的输入由action和state连接而成,网络的输出是长度为1的向量,代表 q value。action用one-hot向量表示,例如动作空间为A = {0, 1, 2}时,向量(1, 0, 0)和(0, 1, 0)分别代表动作a = 0和动作a = 1。"""super(QNet, self).__init__()# 一个线性层 + 激活函数 + 一个线性层self.network = nn.Sequential(nn.Linear(action_state_dim, hidden_dim),nn.ReLU(),nn.Linear(hidden_dim, 1),)def forward(self, x):x = self.network(x)return xclass Agent:def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma, device, epsilon):# 策略网络self.action_value_net = QNet(state_dim + action_dim, hidden_dim).to(device)# 创建优化器,优化器的作用是根据每个参数的梯度来更新参数self.optimizer = torch.optim.Adam(self.action_value_net.parameters(), lr=learning_rate)# 折扣因子self.gamma = gamma# 进行神经网络计算的设备self.device = device# 探索策略,有epsilon的概率随机选取动作self.epsilon = epsilon# 状态维度self.state_dim = state_dim# 动作维度self.action_dim = action_dim# 损失函数,根据当前值和目标值来计算得出损失值self.loss_fn = nn.MSELoss()def take_action(self, state):# 随机探索if random.random() < self.epsilon:return random.choice(range(self.action_dim))# 生成一个对角线矩阵,矩阵的每一行元素代表一个动作actions = torch.eye(self.action_dim).to(self.device)# 对state进行复制,actions中有多少个动作,就state复制为多少行state = tensor(state, dtype=torch.float).to(self.device)states = state.unsqueeze(0).repeat(actions.shape[0], 1)# 连接actions和states矩阵,得到的action_states可以看做是一个batch的动作状态向量action_states = torch.cat((actions, states), dim=1)# 将一个batch的动作状态向量输入到Q网络中,得到一组Q值# 注意q_values的形状是(batch_size, 1),我们将它转换成一维向量q_values = self.action_value_net(action_states).view(-1)# 获取最大Q值对应的下标,下标的值就是采取的最优动作max_value, max_index = torch.max(q_values, dim=0)return max_index.item()def update(self, transition):# 取出相关数据reward = torch.tensor(transition['reward']).to(self.device)state = torch.tensor(transition['state']).to(self.device)next_state = torch.tensor(transition['next_state']).to(self.device)terminated = transition['terminated']# 将数字action转换成one-hot action向量action = torch.zeros(self.action_dim, dtype=torch.float).to(self.device)action[transition['action']] = 1.# 将数字next_action转换成one-hot next_action向量next_action = torch.zeros(self.action_dim, dtype=torch.float).to(self.device)next_action[transition['next_action']] = 1.# 连接action和state向量action_state = torch.cat((action, state), dim=0)next_action_state = torch.cat((next_action, next_state), dim=0)# 获取Q值q_value = self.action_value_net(action_state)[0]# 计算目标Q值。一定要注意如果terminated为true,说明执行action后游戏就终止了# 那么next_state和next_action是无意义的,它们的Q值应该为0# 通过将Q值乘以(1. - float(terminated))的方式,来使其在终止时为0q_target = reward + self.action_value_net(next_action_state)[0] * self.gamma \* (1. - float(terminated))# 计算损失值,第一个参数为当前Q值,第二个参数为目标Q值loss = self.loss_fn(q_value, q_target)# 更新参数self.optimizer.zero_grad()loss.backward()self.optimizer.step()if __name__ == '__main__':# 更新网络参数的学习率learning_rate = 1e-3# 训练轮次num_episodes = 1000# 隐藏层神经元数量hidden_dim = 128# 计算累计奖励时的折扣率gamma = 0.98epsilon = 0.2# 如果存在cuda就用cuda,否则用cpudevice = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")env = gym.make('CartPole-v1')# 获取状态维度,为4state_dim = env.observation_space.shape[0]# 获取离散动作数量,为2action_dim = env.action_space.n# 强化学习智能体agent = Agent(state_dim, hidden_dim, action_dim, learning_rate, gamma, device, epsilon)for episode in range(num_episodes):# transition含义是,在state执行action后,环境返回reward、next_state、terminated# 根据next_state,继续采取next_action作为下一动作transition = {'state': None,'action': None,'next_state': None,'next_action': None,'reward': None,'terminated': None}# 统计信息,游戏结束时获得的总奖励sum_reward = 0# reset返回的是一个元组,第一个元素是初始state值,第二个元素是一个字典state = env.reset()[0]# 游戏终止信号terminated = Falseaction = agent.take_action(state)while not terminated:next_state, reward, terminated, _, _ = env.step(action)next_action = agent.take_action(next_state)# 为transition中添加当前的状态、动作等信息transition['state'] = statetransition['action'] = actiontransition['next_state'] = next_statetransition['reward'] = rewardtransition['next_action'] = next_actiontransition['terminated'] = terminated# 一定确保这里会学习到terminated为true的那一步agent.update(transition)sum_reward += reward# 进入下一状态state = next_stateaction = next_action# 每10轮打印一次统计信息if episode % 10 == 0:print(f"Episode: {episode}, Reward: {sum_reward}")
DQN
理论解释
DQN全程Deep Q Learning,与Sarsa算法十分类似,依然是使用时序差分算法来优化 Q ( s , a ) Q(s, a) Q(s,a)函数。不过DQN的 Q ( s , a ) Q(s, a) Q(s,a)函数含义和优化方式与Sarsa略有不同。
DQN中 Q ( s , a ) Q(s, a) Q(s,a)的含义是在状态 s s s执行动作 a a a后,在后续的轨迹中所能获得的最大累积奖励,为了作区分也有人把DQN的 Q ( s , a ) Q(s, a) Q(s,a)表示为 Q ⋆ ( s , a ) Q^\star(s, a) Q⋆(s,a),本文就不在作区分表示了。
DQN中 Q ( s , a ) Q(s, a) Q(s,a)的时序差分优化过程如下,其中 A A A是动作空间:
Q ( s t , a t ) ← r t + γ ⋅ max a ′ ∈ A Q ( s t + 1 , a ′ ) Q(s_t, a_t) \leftarrow r_t + \gamma \cdot \max\limits_{a' \in A} Q(s_{t+1}, a') Q(st,at)←rt+γ⋅a′∈AmaxQ(st+1,a′)
使用神经网络来拟合 Q ( s , a ) Q(s, a) Q(s,a),在选取动作时依然采用 ϵ \epsilon ϵ-greedy策略。按照此策略我们在状态 s t s_t st时选取动作 a t a_t at,此时环境会返回状态 s t + 1 s_{t+1} st+1,然后遍历所有的动作,选取 Q ( s t + 1 , a ′ ) Q(s_{t+1}, a') Q(st+1,a′)最大的动作 a ′ a' a′,然后计算loss值。
q v a l u e = Q ( s , a ) q_{value} = Q(s, a) qvalue=Q(s,a)
q t a r g e t = r t + γ ⋅ max a ′ ∈ A Q ( s t + 1 , a ′ ) q_{target} = r_t + \gamma \cdot \max\limits_{a' \in A} Q(s_{t+1}, a') qtarget=rt+γ⋅a′∈AmaxQ(st+1,a′)
l o s s = l o s s f n ( q v a l u e , q t a r g e t ) loss = loss_{fn}(q_{value}, q_{target}) loss=lossfn(qvalue,qtarget)
与Sarsa相同,损失函数的计算方式依然选择MSE均方误差。
代码
环境为python3.12,各依赖包均为最新版。
实现代码与Sarsa基本相同,仅有两处做了修改,修改位置已在代码中注释。
import random
import gymnasium as gym
import torch
import torch.nn as nn
from torch import tensorclass QNet(torch.nn.Module):def __init__(self, action_state_dim, hidden_dim):"""网络的输入由action和state连接而成,网络的输出是长度为1的向量,代表 q value。action用one-hot向量表示,例如动作空间为A = {0, 1, 2}时,向量(1, 0, 0)和(0, 1, 0)分别代表动作a = 0和动作a = 1。"""super(QNet, self).__init__()# 一个线性层 + 激活函数 + 一个线性层self.network = nn.Sequential(nn.Linear(action_state_dim, hidden_dim),nn.ReLU(),nn.Linear(hidden_dim, 1),)def forward(self, x):x = self.network(x)return xclass Agent:def __init__(self, state_dim, hidden_dim, action_dim, learning_rate, gamma, device, epsilon):# 策略网络self.action_value_net = QNet(state_dim + action_dim, hidden_dim).to(device)# 创建优化器,优化器的作用是根据每个参数的梯度来更新参数self.optimizer = torch.optim.Adam(self.action_value_net.parameters(), lr=learning_rate)# 折扣因子self.gamma = gamma# 进行神经网络计算的设备self.device = device# 探索策略,有epsilon的概率随机选取动作self.epsilon = epsilon# 状态维度self.state_dim = state_dim# 动作维度self.action_dim = action_dim# 损失函数,根据当前值和目标值来计算得出损失值self.loss_fn = nn.MSELoss()def take_action(self, state):# 随机探索if random.random() < self.epsilon:return random.choice(range(self.action_dim))# 生成一个对角线矩阵,矩阵的每一行元素代表一个动作actions = torch.eye(self.action_dim).to(self.device)# 对state进行复制,actions中有多少个动作,就state复制为多少行state = tensor(state, dtype=torch.float).to(self.device)states = state.unsqueeze(0).repeat(actions.shape[0], 1)# 连接actions和states矩阵,得到的action_states可以看做是一个batch的动作状态向量action_states = torch.cat((actions, states), dim=1)# 将一个batch的动作状态向量输入到Q网络中,得到一组Q值# 注意q_values的形状是(batch_size, 1),我们将它转换成一维向量q_values = self.action_value_net(action_states).view(-1)# 获取最大Q值对应的下标,下标的值就是采取的最优动作max_value, max_index = torch.max(q_values, dim=0)return max_index.item()def update(self, transition):# 取出相关数据reward = torch.tensor(transition['reward']).to(self.device)state = torch.tensor(transition['state']).to(self.device)next_state = torch.tensor(transition['next_state']).to(self.device)terminated = transition['terminated']# 将数字action转换成one-hot action向量action = torch.zeros(self.action_dim, dtype=torch.float).to(self.device)action[transition['action']] = 1.# 连接action和state向量action_state = torch.cat((action, state), dim=0)# 获取Q值q_value = self.action_value_net(action_state)[0]"""与Sarsa算法主要不同的地方,在于q_target的计算方式:类似于take_action函数中的内容,这里需要把所有动作都进行one-hot操作,与状态连接并输入到网络中,获取所有动作的q_value中最大的值,作为计算q_target的一部分。"""next_actions = torch.eye(self.action_dim).to(self.device)next_states = next_state.unsqueeze(0).repeat(next_actions.shape[0], 1)next_action_states = torch.cat((next_actions, next_states), dim=1)q_target = reward + torch.max(self.action_value_net(next_action_states)) \* self.gamma * (1. - float(terminated))# 计算损失值,第一个参数为当前Q值,第二个参数为目标Q值loss = self.loss_fn(q_value, q_target)# 更新参数self.optimizer.zero_grad()loss.backward()self.optimizer.step()if __name__ == '__main__':# 更新网络参数的学习率learning_rate = 1e-3# 训练轮次num_episodes = 1000# 隐藏层神经元数量hidden_dim = 128# 计算累计奖励时的折扣率gamma = 0.98epsilon = 0.2# 如果存在cuda就用cuda,否则用cpudevice = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")env = gym.make('CartPole-v1')# 获取状态维度,为4state_dim = env.observation_space.shape[0]# 获取离散动作数量,为2action_dim = env.action_space.n# 强化学习智能体agent = Agent(state_dim, hidden_dim, action_dim, learning_rate, gamma, device, epsilon)for episode in range(num_episodes):# transition含义是,在state执行action后,环境返回reward、next_state、terminated# 根据next_state,继续采取next_action作为下一动作transition = {'state': None,'action': None,'next_state': None,'next_action': None,'reward': None,'terminated': None}# 统计信息,游戏结束时获得的总奖励sum_reward = 0# reset返回的是一个元组,第一个元素是初始state值,第二个元素是一个字典state = env.reset()[0]# 游戏终止信号terminated = Falsewhile not terminated:"""与Sarsa算法略有不同的地方,这里不需要再获取next_action"""action = agent.take_action(state)next_state, reward, terminated, _, _ = env.step(action)# 为transition中添加当前的状态、动作等信息transition['state'] = statetransition['action'] = actiontransition['next_state'] = next_statetransition['reward'] = rewardtransition['terminated'] = terminated# 一定确保这里会学习到terminated为true的那一步agent.update(transition)sum_reward += reward# 进入下一状态state = next_state# 每10轮打印一次统计信息if episode % 10 == 0:print(f"Episode: {episode}, Reward: {sum_reward}")
相关文章:
强化学习-价值学习算法
Sarsa 理论解释 Sarsa是基于时序差分算法的,它的公式非常简单且易理解,不像策略梯度算法那样需要复杂的推导过程。 Sarsa的核心函数是 Q ( s , a ) Q(s, a) Q(s,a),它的含义是在状态 s s s下执行 a a a,在后续轨迹中获取的期望…...
Apache Struts RCE (CVE-2024-53677)
前言 对目前的Apache Struts RCE (CVE-2024-53677)的poc进行总结,由于只能单个ip验证,所以自己更改一下代码,实现:多线程读取url验证并保存,更改为中文解释 免责声明 请勿利用文章内的相关技术从事非法测试…...
23. AI-大语言模型-DeepSeek
文章目录 前言一、DeepSeek是什么1. 简介2. 产品版本3. 特征4. 地址链接5. 三种访问方式1. 网页端和APP2. DeepSeek API 二、DeepSeek可以做什么1. 应用场景2. 文本生成1. 文本创作2. 摘要与改写3. 结构化生成 3. 自然语言理解与分析1. 语义分析2. 文本分类3. 知识推理 4. 编程…...
verilog基础知识
一,Verilog和VHDL区别 全世界高层次数字系统设计领域中,应用Verilog和VHDL的比率是80%和20%;这两种语言都是用于数字电路系统设计的硬件描述语言, 而且都已经是 IEEE 的标准。 VHDL 是美国军方组织开发的,VHDL1987年成为标准;Verilog 是由一个公司的私有财产转化而来,…...
负载测试工具有哪些?
Apache JMeter Apache JMeter 是一款开源的性能测试工具,主要用于对 Web 应用程序进行功能、负载和压力测试。JMeter 支持多种协议和技术,包括 HTTP, HTTPS, FTP 和 WebSocket 等。通过模拟大量并发用户访问来评估应用程序的表现1。 jmeter -n -t testp…...
项目中分库分表的分布式ID如何生成
分库分表与分布式ID生成在Java项目中的应用 在大规模的分布式系统中,数据库表和数据量的增大可能会导致单个数据库或单个表的性能瓶颈。为了解决这个问题,我们通常使用分库分表来进行数据的水平切分和垂直切分。同时,在分布式环境中…...
用xarray解析高程数据时,Python报错:FileNotFoundError: [Errno 2] No such file or directory
问题: 用xarray解析高程数据时,Python报错:FileNotFoundError: [Errno 2] No such file or directory。 但是该文件时存在的,用panoply工具也是可以正常打开查看。 产生原因: 文件路径中存在中文,导致出现此问题。 …...
标准SαS分布的模拟脉冲噪声
标准SαS分布(Standard SαS Distribution)是一种用于描述脉冲噪声的统计分布,常用于模拟具有重尾特性的脉冲噪声信号。SαS分布是从稳定分布(Stable Distribution)中派生出来的一类分布,具有灵活的形状&am…...
基于STM32设计的自动追光系统(系统资料)
基于STM32设计的自动追光系统 摘要:基于STM32设计的自动追光系统主要由光敏采集电路、单片机核心板电路和步进电机控制电路构成。通过光敏电阻采集环境光强信息,经 STM32 单片机处理后,控制步进电机转动,实现对光源的自动追踪。该系统具有响应速度快、追踪精度较高等优点,…...
成人床垫更新关于 SOR/2016-183 和《纺织品贴标和广告法规》的合规
成人床垫更新关于 SOR/2016-183 和《纺织品贴标和广告法规》的合规性声明 加拿大站成人床垫法规SOR/2016-183是强制性的 。为确保买家安全并遵守相关法规,亚马逊要求所有在加拿大销售的成人床垫必须符合《床垫法规》规定的安全标准,包括SOR/2016-183。此…...
11.编写前端内容|vscode链接Linux|html|css|js(C++)
vscode链接服务器 安装VScode插件 Chinese (Simplified) (简体中⽂) Language Pack for Visual Studio CodeOpen in BrowserRemote SSH 在命令行输入 remote-ssh接着输入 打开配置文件,已经配置好主机 点击远程资源管理器可以找到 右键链接 输入密码 …...
Unity3D 基于 GPU 动画和 Compute Shader 的大批量动画渲染详解
引言 在现代游戏开发中,渲染大量动画角色是一个常见的需求,尤其是在大规模战斗场景、开放世界游戏或 VR/AR 应用中。传统的 CPU 动画计算和渲染方式在面对大批量角色时,往往会遇到性能瓶颈。为了优化性能,开发者可以利用 GPU 的强…...
网工项目实践2.6 广域网需求分析及方案制定
本专栏持续更新,整一个专栏为一个大型复杂网络工程项目。阅读本文章之前务必先看《本专栏必读》。 全网拓扑展示 一.广域网互联方式 1.专线 优点 稳定 独享。绝对安全。可靠性高,带宽高,完全取决于终端接口。 缺点: 费用高。建设时间长。难…...
大模型相关学习
知识科普 为什么不直接使用网页版 DeepSeek? 我们的需求:绝对的隐私保护和个性化知识库构建。场景:若希望大模型能根据企业规章制度来回答问题,一般需上传企业规章制度的附件,但仍可能面临问题。 数据隐私问题:联网使…...
vue2自定义useVModel函数
父组件: <template> <div>父组件数据名字:<input v-model"person.name">父组件数据年龄:<input v-model"person.age"><son v-model"person"></son> </div> </t…...
基于Java(JSP)+MySQL设计与实现的 MVC 鲜花订购系统
基于MVC的鲜花订购系统的设计与实现 摘 要 摘 要:鲜花订购系统与网络相结合,给用户提供更加周到和人性化的服务。网站模式为MVC模式,基于MySql数据库,采用Jsp,Session绘画跟踪、JavaScript等技术,实现了普通用户可以浏览、查看鲜…...
YOLOv11-ultralytics-8.3.67部分代码阅读笔记-dataset.py
dataset.py ultralytics\data\dataset.py 目录 dataset.py 1.所需的库和模块 2.class YOLODataset(BaseDataset): 3.class YOLOMultiModalDataset(YOLODataset): 4.class GroundingDataset(YOLODataset): 5.class YOLOConcatDataset(ConcatDataset): 6.class Sema…...
网络原理-
文章目录 协议应用层传输层网络层 数据链路层 协议 在网络通信中,协议是非常重要的概念.协议就是一种约定. 在网络通信过程中,对协议进行了分层 接下来就按照顺序向大家介绍每一种核心的协议. 应用层 应用层是咱们程序员打交道最多的一层协议.应用层里有很多现成的协议,但…...
解码 NLP:从萌芽到蓬勃的技术蜕变之旅
内容概况: 主要讲述NLP专栏的内容和NLP的发展及其在现代生活中的广泛应用。课程强调实践为主、理论为辅的学习方法,并通过多个生活场景展示了NLP技术的实际应用,如对话机器人、搜索引擎、翻译软件、电商推荐和智能客服等。 这边我就不多做自我…...
Word中的文档信息域
Word中的文档信息域 DocProperty包含文档信息的多个属性, 也可以自定义属性. 查看文档预定义的自定义属性 【文件】→【信息】→【属性】→【高级属性】 参考链接 WORD中文档属性域DocProperty的应用-CSDN博客 第06套 Word_哔哩哔哩_bilibili...
.NET周刊【2月第2期 2025-02-09】
国内文章 开箱即用的.NET MAUI组件库 V-Control 发布了! https://www.cnblogs.com/jevonsflash/p/18701494 文章介绍了V-Control,一个适用于.NET MAUI的组件库。作者计划将其开源,强调.NET MAUI是生产力强的跨平台移动开发工具。V-Control提供多种组件…...
MySQL六大日志的功能介绍。
前言 首先,MySQL的日志应该包括二进制日志(Binary Log)、错误日志(Error Log)、查询日志(General Query Log)、慢查询日志(Slow Query Log)、重做日志(Redo …...
java机器学习计算指标动态阈值
java机器学习计算指标动态阈值 最近听到有的人说要做机器学习就一定要学Python,我想他们掌握的知道还不够系统全面。本文作者以动态阈值需求场景给大家介绍几种常用Java实现的机器学习库,包括使用开源库如Weka或Deeplearning4j(DL4J…...
Note25021902_TIA Portal V18 WinCC BCA Ed 需要.NET 3.5 SP1
TIA Portal V18 WinCC BCA Ed 需要.NET 3.5 SP1 在安装TIA Portal V18时,遇到TIA Portal V18 WinCC BCA Ed 需要.NET 3.5 SP1. 请在此PC上中启用.NET 3.5 SP1; 检索: 电脑上如何启用 .NET 3.5 SP1 参考资料1: https://baijiahao.…...
CHARMM-GUI EnzyDocker: 一个基于网络的用于酶中多个反应状态的蛋白质 - 配体对接的计算平台
❝ "CHARMM-GUI EnzyDocker for Protein−Ligand Docking of Multiple Reactive States along a Reaction Coordinate in Enzymes"介绍了 CHARMM-GUI EnzyDocker,这是一个基于网络的计算平台,旨在简化和加速 EnzyDock 对接模拟的设置过程&…...
阅读论文笔记《Translating Embeddings for Modeling Multi-relational Data》
目录 一、模型核心原理剖析二、实验设计与数据集选择三、实验结果深度解读(一)链接预测实验(二)关系分类实验(三)链接预测示例(四)泛化实验 四、模型优缺点总结(一&#…...
实在智能与宇树科技、云深科技一同获评浙江省“人工智能服务商”、 “数智优品”等荣誉
近日,浙江省经信厅正式公布《2024 年浙江省人工智能应用场景、应用标杆企业、人工智能服务商及 “数智优品” 名单》。 实在智能获评浙江省“人工智能服务商”,核心产品 “实在 Agent 智能体” 入选 “数智优品”。一同获此殊荣的还有宇树科技、云深处科…...
跳表(Skip List)详解
一、什么是跳表? 跳表是一种基于有序链表的高效数据结构,通过建立多级索引实现快速查询。它在平均情况下支持O(log n)时间复杂度的搜索、插入和删除操作,性能接近平衡树,但实现更为简单。 二、核心原理 1. 层级结构 底层为完整…...
图片粘贴上传实现
图片上传 html demo 直接粘贴本地运行查看效果即可,有看不懂的直接喂给 deepseek 会解释的很清晰 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"…...
【Linux常用命令-不断更新】
在 Linux 系统中,查看总剩余内存常用方法。 方法 1:使用 free 命令 free 是一个常用的命令,用于显示系统的总内存、已用内存、空闲内存和交换内存。 free -h-h 参数表示以易读的格式(如 GB、MB)显示内存信息。输出示…...
