30分钟吃掉DQN算法

DQN还是PPO?强化学习工业落地的三大决策准则 强化学习算法选型本质上是工程约束下的最优解问题。从基础概念看,DQN属于值函数方法,依赖离散动作枚举与经验回放;策略梯度类算法(如PPO、SAC)则直接优化策略分布,天然适配连续动作与在线学习。其技术价值体现在更高的样本利用效率、更强的时序一致性鲁棒性,以及可预测的低延迟推理性能。典型应用场景包括物流路径规划、金融高频做市、IoT设备控制等对动作空间连续性、推理实时性和部署稳定性有严苛要求的工业系统。当面临动作空间爆炸、reward稀疏或SLA≤20ms等挑战时,策略梯度往往成为不可替代的工程选择。 阅读详情

9ef2a3782ebcdf75fca0c11c24483fd5.png

表格型方法存储的状态数量有限,当面对围棋或机器人控制这类有数不清的状态的环境时,表格型方法在存储和查找效率上都受局限,DQN的提出解决了这一局限,使用神经网络来近似替代Q表格。

本质上DQN还是一个Q-learning算法,更新方式一致。为了更好的探索环境,同样的也采用epsilon-greedy方法训练。

在Q-learning的基础上,DQN提出了两个技巧使得Q网络的更新迭代更稳定。

  • 经验回放(Experience Replay): 使用一个经验池存储多条经验s,a,r,s',再从中随机抽取一批数据送去训练。

  • 固定目标(Fixed Q-Target): 复制一个和原来Q网络结构一样的Target-Q网络,用于计算Q目标值。

公众号算法美食屋后台回复关键词:torchkeras,获取本文notebook源码~

不了解强化学习的同学,推荐先阅读:Q-learning解决悬崖问题

一,准备环境

gym是一个常用的强化学习测试环境,可以用make创建环境。

env具有reset,step,render几个方法。

倒立摆问题

环境设计如下:

倒立摆问题环境的状态是无限的,用一个4维的向量表示state.

4个维度分别代表如下含义

  • cart位置:-2.4 ~ 2.4

  • cart速度:-inf ~ inf

  • pole角度:-0.5 ~ 0.5 (radian)

  • pole角速度:-inf ~ inf

智能体设计如下:

智能体的action有两种,可能的取值2种:

  • 0,向左

  • 1,向右

奖励设计如下:

每维持一个步骤,奖励+1,到达200个步骤,游戏结束。

所以最高得分为200分。

倒立摆问题希望训练一个智能体能够尽可能地维持倒立摆的平衡。

import gym 
import numpy as np 
import pandas as pd 
import time
import matplotlib
import matplotlib.pyplot as plt
from IPython import display

print("gym.__version__=",gym.__version__)


%matplotlib inline

#可视化函数:
def show_state(env, step, info=''):
    plt.figure(num=10086,dpi=100)
    plt.clf()
    plt.imshow(env.render())
    plt.title("step: %d %s" % (step, info))
    plt.axis('off')
    display.clear_output(wait=True)
    display.display(plt.gcf())
    plt.close()
    

env = gym.make('CartPole-v1',render_mode="rgb_array") # CartPole-v0: 预期最后一次评估总分 > 180(最大值是200)
env.reset()
action_dim = env.action_space.n   # CartPole-v0: 2
obs_shape = env.observation_space.shape   # CartPole-v0: (4,)
gym.__version__= 0.26.2
env.reset()
done = False
step = 0
while not done:
    
    action = np.random.randint(0, 1)
    state,reward,done,truncated,info = env.step(action)
    step+=1
    print(state,reward)
    time.sleep(1.0)
    #env.render() 
    show_state(env,step=step)
    #print('step {}: action {}, state {}, reward {}, done {}, truncated {}, info {}'.format(\
    #        step, action, state, reward, done, truncated,info))
    
display.clear_output(wait=True)

0600aa2e49580d2568fae93d51fc8b93.gif

可以看到,没有训练智能体之前,我们采取随机动作的话,只维持了10步,倒立摆就因为倾斜角度超出范围而导致游戏结束。😭

二,定义Agent

DQN的核心思想为使用一个神经网络来近似替代Q表格。

Model: 模型结构, 负责拟合函数 Q(s,a)。主要实现forward方法。

Agent:智能体,负责学习并和环境交互, 输入输出是numpy.array形式。有sample(单步采样), predict(单步预测), 有predict_batch(批量预测), compute_loss(计算损失), sync_target(参数同步)等方法。

import torch 
from torch import nn
import torch.nn.functional as F
import copy 

class Model(nn.Module):
    def __init__(self, obs_dim, action_dim):
        
        # 3层全连接网络
        super(Model, self).__init__()
        self.obs_dim = obs_dim
        self.action_dim = action_dim 
        self.fc1 = nn.Linear(obs_dim,32)
        self.fc2 = nn.Linear(32,16)
        self.fc3 = nn.Linear(16,action_dim)

    def forward(self, obs):
        # 输入state,输出所有action对应的Q,[Q(s,a1), Q(s,a2), Q(s,a3)...]
        x = self.fc1(obs)
        x = torch.tanh(x)
        x = self.fc2(x)
        x = torch.tanh(x)
        Q = self.fc3(x)
        return Q
    
model = Model(4,2)
model_target = copy.deepcopy(model)

model.eval()
model.forward(torch.tensor([[0.2,0.1,0.2,0.0],[0.3,0.5,0.2,0.6]]))

model_target.eval() 
model_target.forward(torch.tensor([[0.2,0.1,0.2,0.0],[0.3,0.5,0.2,0.6]]))
tensor([[-0.1148,  0.0068],
        [-0.1311,  0.0315]], grad_fn=<AddmmBackward0>)
import torch 
from torch import nn 
import copy 

class DQNAgent(nn.Module):
    def __init__(self, model, 
        gamma=0.9,
        e_greed=0.1,
        e_greed_decrement=0.001
        ):
        super().__init__()
        
        self.model = model
        self.target_model = copy.deepcopy(model)
  
        self.gamma = gamma # reward 的衰减因子,一般取 0.9 到 0.999 不等
        
        self.e_greed = e_greed  # 有一定概率随机选取动作,探索
        self.e_greed_decrement = e_greed_decrement  # 随着训练逐步收敛,探索的程度慢慢降低
        
        self.global_step = 0
        self.update_target_steps = 200 # 每隔200个training steps再把model的参数复制到target_model中
        
        
    def forward(self,obs):
        return self.model(obs)
    
    @torch.no_grad()
    def predict_batch(self, obs):
        """ 使用self.model网络来获取 [Q(s,a1),Q(s,a2),...]
        """
        self.model.eval()
        return self.forward(obs)
    
    
    #单步骤采样    
    def sample(self, obs):
        sample = np.random.rand()  # 产生0~1之间的小数
        if sample < self.e_greed:
            action = np.random.randint(self.model.action_dim)  # 探索:每个动作都有概率被选择
        else:
            action = self.predict(obs)  # 选择最优动作
        self.e_greed = max(
            0.01, self.e_greed - self.e_greed_decrement)  # 随着训练逐步收敛,探索的程度慢慢降低
        return action
    
    #单步骤预测   
    def predict(self, obs):  # 选择最优动作
        obs = np.expand_dims(obs, axis=0)
        tensor = torch.tensor(obs,dtype=torch.float32).to(self.model.fc1.weight.device)
        pred_Q = self.predict_batch(tensor)
        action = torch.argmax(pred_Q,1,keepdim=True).cpu().numpy()  
        action = np.squeeze(action)
        return action
    
    
    def sync_target(self):
        """ 把 self.model 的模型参数值同步到 self.target_model
        """
        self.target_model.load_state_dict(self.model.state_dict())
    

    def compute_loss(self, obs, action, reward, next_obs, done):
        
        # 每隔200个training steps同步一次model和target_model的参数
        if self.global_step % self.update_target_steps == 0:
            self.sync_target()
        self.global_step += 1
        
        
        # 从target_model中获取 max Q' 的值,用于计算target_Q
        self.target_model.eval()
        next_pred_value = self.target_model(next_obs)
        best_value = torch.max(next_pred_value, dim = 1,keepdim=True).values 
        target = reward.reshape((-1,1)) + (
            torch.tensor(1.0) - done.reshape(-1,1)) * self.gamma * best_value
        
        #print("best_value",best_value.shape)
        #print("target",target.shape)

        # 获取Q预测值
        self.model.train()
        pred_value = self.model(obs)  
        action_onehot = F.one_hot(action.reshape(-1),
                num_classes = self.model.action_dim).float()
        prediction = torch.sum(pred_value*action_onehot,dim= 1,keepdim=True)
        
        #print("pred_value",pred_value.shape)
        #print("action_onehot",action_onehot.shape)
        #print("prediction",prediction.shape)
        
        # 计算 Q(s,a) 与 target_Q的均方差,得到loss
        loss = F.smooth_l1_loss(target,prediction)
        return loss
agent = DQNAgent(model,gamma=0.9,e_greed=0.1,
                 e_greed_decrement=0.001)
agent.predict_batch(torch.tensor([[2.0,3.0,4.0,2.0],[1.0,2.0,3.0,4.0]]))
tensor([[-0.1596, -0.0481],
        [-0.0927,  0.0318]])
loss = agent.compute_loss(torch.tensor([[2.0,3.0,4.0,2.0],[1.0,2.0,3.0,4.0],[1.0,2.0,3.0,4.0]]),
          torch.tensor([[1],[0],[0]]),
          torch.tensor([[1.0],[1.0],[1.0]]),
         torch.tensor([[2.0,3.0,0.4,2.0],[1.0,2.0,3.0,4.0],[1.0,2.0,3.0,4.0]]),
         torch.tensor(0.9))
print(loss)
tensor(0.5757, grad_fn=<SmoothL1LossBackward0>)

三,训练Agent

import random
import collections
import numpy as np

LEARN_FREQ = 5 # 训练频率,不需要每一个step都learn,攒一些新增经验后再learn,提高效率
MEMORY_SIZE = 2048    # replay memory的大小,越大越占用内存
MEMORY_WARMUP_SIZE = 512  # replay_memory 里需要预存一些经验数据,再开启训练
BATCH_SIZE = 128   # 每次给agent learn的数据数量,从replay memory随机里sample一批数据出来
#经验回放
class ReplayMemory(object):
    def __init__(self, max_size):
        self.buffer = collections.deque(maxlen=max_size)

    # 增加一条经验到经验池中
    def append(self, exp):
        self.buffer.append(exp)

    # 从经验池中选取N条经验出来
    def sample(self, batch_size):
        mini_batch = random.sample(self.buffer, batch_size)
        obs_batch, action_batch, reward_batch, next_obs_batch, done_batch = [], [], [], [], []

        for experience in mini_batch:
            s, a, r, s_p, done = experience
            obs_batch.append(s)
            action_batch.append(a)
            reward_batch.append(r)
            next_obs_batch.append(s_p)
            done_batch.append(done)

        return np.array(obs_batch).astype('float32'), \
            np.array(action_batch).astype('int64'), np.array(reward_batch).astype('float32'),\
            np.array(next_obs_batch).astype('float32'), np.array(done_batch).astype('float32')

    def __len__(self):
        return len(self.buffer)
from torch.utils.data import IterableDataset,DataLoader  
class MyDataset(IterableDataset):
    def __init__(self,env,agent,rpm,stage='train',size=200):
        self.env = env
        self.agent = agent 
        self.rpm = rpm if stage=='train' else None
        self.stage = stage
        self.size = size 
        
    def __iter__(self):
        obs,info = self.env.reset() # 重置环境, 重新开一局(即开始新的一个episode)
        step = 0
        batch_reward_true = [] #记录真实的reward
        while True:
            step += 1
            action = self.agent.sample(obs) 
            next_obs, reward, done, _, _ = self.env.step(action) # 与环境进行一个交互
            batch_reward_true.append(reward)
            
            if self.stage=='train':
                self.rpm.append((obs, action, reward, next_obs, float(done)))
                if (len(rpm) > MEMORY_WARMUP_SIZE) and (step % LEARN_FREQ == 0):
                    #yield batch_obs, batch_action, batch_reward, batch_next_obs,batch_done
                    yield self.rpm.sample(BATCH_SIZE),sum(batch_reward_true)
                    batch_reward_true.clear()
            
            else:
                obs_batch = np.array([obs]).astype('float32')
                action_batch = np.array([action]).astype('int64')
                reward_batch = np.array([reward]).astype('float32')
                next_obs_batch = np.array([next_obs]).astype('float32')
                done_batch = np.array([float(done)]).astype('float32')
                batch_data = obs_batch,action_batch,reward_batch,next_obs_batch,done_batch
                yield batch_data,sum(batch_reward_true)
                batch_reward_true.clear()
            
    
            if self.stage =='train':
                next_action = self.agent.sample(next_obs) # 训练阶段使用探索策略
            else:
                next_action = self.agent.predict(next_obs) # 验证阶段使用模型预测结果
 
            action = next_action
            obs = next_obs   

            if done:
                if self.stage=='train' and len(self.rpm)<MEMORY_WARMUP_SIZE: #确保训练一次
                    yield self.rpm.sample(len(self.rpm)),sum(batch_reward_true)
                    batch_reward_true.clear()
                    break
                else:
                    break
    def __len__(self):
        return self.size 
    

env = gym.make('CartPole-v1') 
rpm = ReplayMemory(MEMORY_SIZE)

ds_train = MyDataset(env,agent,rpm,stage='train',size=1000)
ds_val = MyDataset(env,agent,rpm,stage='val',size=200)
#ReplayMemory预存数据
while len(ds_train.rpm)<MEMORY_WARMUP_SIZE:
    for data in ds_train:
        print(len(ds_train.rpm))
13
47
167
272
511
521
def collate_fn(batch):
    samples,rewards = [x[0] for x in batch],[x[-1] for x in batch] 
    samples = [torch.from_numpy(np.concatenate([x[j] for x in samples])) for j in range(5)] 
    rewards = torch.from_numpy(np.array([sum(rewards)]).astype('float32'))
    return samples,rewards 

dl_train = DataLoader(ds_train,batch_size=1,collate_fn=collate_fn)
dl_val = DataLoader(ds_val,batch_size=1,collate_fn=collate_fn)
for batch in dl_train:
    break
import sys,datetime
from tqdm import tqdm
import numpy as np
from accelerate import Accelerator
from torchkeras import KerasModel
import pandas as pd 

from copy import deepcopy

class StepRunner:
    def __init__(self, net, loss_fn, accelerator=None, stage = "train", metrics_dict = None, 
                 optimizer = None, lr_scheduler = None
                 ):
        self.net,self.loss_fn,self.metrics_dict,self.stage = net,loss_fn,metrics_dict,stage
        self.optimizer,self.lr_scheduler = optimizer,lr_scheduler
        self.accelerator = accelerator if accelerator is not None else Accelerator()
    
    def __call__(self, batch):
        
        samples,reward = batch
        #torch_data = ([torch.from_numpy(x) for x in batch_data])
        loss = self.net.compute_loss(*samples)
        
        #backward()
        if self.optimizer is not None and self.stage=="train":
            self.accelerator.backward(loss)
            if self.accelerator.sync_gradients:
                self.accelerator.clip_grad_norm_(self.net.parameters(), 1.0)
            self.optimizer.step()
            if self.lr_scheduler is not None:
                self.lr_scheduler.step()
            self.optimizer.zero_grad()
                
            
        #losses (or plain metric)
        step_losses = {self.stage+'_reward':reward.item(), 
                       self.stage+'_loss':loss.item()}
        
        #metrics (stateful metric)
        step_metrics = {}
        if self.stage=="train":
            if self.optimizer is not None:
                step_metrics['lr'] = self.optimizer.state_dict()['param_groups'][0]['lr']
            else:
                step_metrics['lr'] = 0.0
        return step_losses,step_metrics
    

class EpochRunner:
    def __init__(self,steprunner,quiet=False):
        self.steprunner = steprunner
        self.stage = steprunner.stage
        self.accelerator = steprunner.accelerator
        self.net = steprunner.net
        self.quiet = quiet
        
    def __call__(self,dataloader):
        dataloader.agent = self.net 
        n = dataloader.size  if hasattr(dataloader,'size') else len(dataloader)
        loop = tqdm(enumerate(dataloader,start=1), 
                    total=n,
                    file=sys.stdout,
                    disable=not self.accelerator.is_local_main_process or self.quiet,
                    ncols=100
                   )
        epoch_losses = {}
        for step, batch in loop: 
            if step<n:
                step_losses,step_metrics = self.steprunner(batch)   
                step_log = dict(step_losses,**step_metrics)
                for k,v in step_losses.items():
                    epoch_losses[k] = epoch_losses.get(k,0.0)+v
                loop.set_postfix(**step_log) 
            else:
                break
            
        epoch_metrics = step_metrics
        epoch_metrics.update({self.stage+"_"+name:metric_fn.compute().item() 
                         for name,metric_fn in self.steprunner.metrics_dict.items()})
        epoch_losses = {k:v for k,v in epoch_losses.items()}
        epoch_log = dict(epoch_losses,**epoch_metrics)
        loop.set_postfix(**epoch_log)

        for name,metric_fn in self.steprunner.metrics_dict.items():
            metric_fn.reset()
            
        return epoch_log
    
KerasModel.StepRunner = StepRunner
KerasModel.EpochRunner = EpochRunner
keras_model = KerasModel(net= agent,loss_fn=None,
        optimizer=torch.optim.Adam(agent.model.parameters(),lr=1e-2))

dfhistory = keras_model.fit(train_data = dl_train,
    val_data=dl_val,
    epochs=600,
    ckpt_path='checkpoint.pt',
    patience=100,
    monitor='val_reward',
    mode='max',
    callbacks=None,
    plot= True,
    cpu=True)

3aad2955bb0a7f63ec3220feeb1dd432.gif

四,评估Agent

# 评估 agent, 跑 3 次,总reward求平均
def evaluate(env, agent, render=False):
    eval_reward = []
    for i in range(2):
        obs,info = env.reset()
        episode_reward = 0
        step=0
        while step<300:
            action = agent.predict(obs)  # 预测动作,只选最优动作
            obs, reward, done, _, _ = env.step(action)
            episode_reward += reward
            if render:
                show_state(env,step,info='reward='+str(episode_reward))
            if done:
                break
            step+=1
        eval_reward.append(episode_reward)
    return np.mean(eval_reward)
#直观显示动画
env = gym.make('CartPole-v1',render_mode="rgb_array") 

evaluate(env, agent, render=True)

4b454eda96489d798e15c51ff1a1a60c.gif可以看到,训练完成之后,我们的agent已经变得非常的智能了,能够维持倒立摆的平衡超过200s。🤗

288.5

五,保存Agent

torch.save(agent.state_dict(),'dqn_agent.pt')

万水千山总是情,点个在看行不行?🤗🤗

abfb51255c9799ef2b6dd28b33857442.png

本文notebook源码,以及更多有趣范例,可在公众号算法美食屋后台回复关键词:torchkeras,获取~

深度强化学习在加密交易中的实盘落地:对抗回测过拟合 深度强化学习(DRL)作为一种面向序贯决策的建模范式,正逐步替代传统技术指标类量化策略,其核心价值在于构建能适应市场结构迁移的动态交易策略。在加密货币等高波动、低延迟、流动性不稳定的场景中,DRL模型极易陷入回测过拟合——即在历史数据上表现优异,却无法泛化至新行情。本文从状态空间设计、动作空间建模与奖励函数重构三方面出发,系统阐述如何通过可解释特征工程、环境扰动注入与跨期泛化验证等工程手段,提升模型鲁棒性与实盘适应性。特别聚焦于PPO算法在连续仓位控制、移动止损协同与风控内生化方面的实践优势,为量化工程师和 阅读详情

相关推荐

量化策略本质:从价格预测到资金行为建模

股票市场预测在工程实践中并非直接拟合价格序列,而应回归金融本质——建模市场参与者的集体决策逻辑。其底层原理在于:价格本身服从随机游走,但资金流动、杠杆变化、外资动向等行为痕迹具备更强的时序稳定性与可解释性。这种‘决策信号建模’范式显著提升模型鲁棒性,规避传统LSTM端到端拟合导致的过拟合防控失效与实盘衰减问题。典型应用场景包括行业轮动识别、主力资金监测、波动率突变归因等高频交易决策支持。本文聚焦机器学习在真实量化系统中的落地逻辑,以可验证、可迭代、可风控的方式重构时序建模方法论。

326

Pytorch踩坑记:赋值、浅拷贝、深拷贝三者的区别以及model.state_dict()和model.load_state_dict()的坑点

之前一直不太搞明白浅拷贝和赋值、深拷贝到底有什么区别,直到被pytorch的model.state_dict()给坑了 今天在和实验室同学讨论联邦学习框架代码的时候,终于明白了他们之间的区别,这里做个记录。.........

捡起一束光的博客 2万+

【信息科学与工程学】计算机科学与自动化——第三百零五篇 数据中心 Scale-Up、Scale-Out、Scale-Across 20

GPU 训练中的各种算子融合技巧、梯度压缩与稀疏通信的高级变体、混合专家模型的负载均衡与通信优化、长序列模型的注意力机制优化、模型量化与蒸馏的联合优化、分布式训练中的容错与恢复、以及一些新兴硬件特性的建模(如 HBM3、GDDR7、CXL 3.0 内存语义、PCIe Gen6 PAM4 信令等)。每条仍然包含四个方程式子节。

weixin_49199313的博客 355

dqn在训练过程中loss越来越大_用DQN算法玩FlappyBird

DQN算法可以用于解决离散的动作问题,而FlappyBird的操作正好是离散的。FlappyBird的游戏状态一般可以通过图像加卷积神经网络(CNN)来进行强化学习。但是通过图像分析会比较麻烦,因为每次只输入一帧图像可能是不够的,因为一帧的图像分析不出小鸟的速度信息,所以通常需要几帧图像一起送入CNN分析。这样的方法麻烦又复杂,仔细观察一下游戏。FlappyBird游戏发现有三个信息对于小鸟能够顺...

weixin_34214135的博客 755

遗传算法+神经网络训练智能贪吃蛇:从原理到代码实战

在机器学习领域,神经网络与遗传算法都是经典技术,它们不依赖梯度信息即可优化模型参数。传统神经网络训练多依赖反向传播,而遗传算法通过模拟“优胜劣汰”的进化规律,能有效解决奖励稀疏、延迟反馈的决策问题。本文从基础概念出发,解析遗传算法如何通过适应度评估、选择、交叉与变异迭代优化神经网络的权重,并给出一个完整实战案例:训练一条贪吃蛇自动吃食。项目不使用任何梯度计算,仅依靠“进化”就能让蛇学会导航与避障。文章涵盖游戏环境设计、感知向量输入、网络结构、适应度函数调整等关键细节,并提供可复现的源码结构,适合希望快速入门

weixin_33868027的博客 390

Xcelium XRUN实用指南:从入门到高效仿真

本文是Xcelium XRUN仿真工具的实用指南,从基础入门到高效应用全面解析。XRUN作为Cadence Xcelium的统一命令行入口,能自动化编译、精细和仿真流程,大幅提升数字芯片与FPGA验证效率。文章详细介绍了最简命令、波形调试、增量编译、多核并行仿真等核心功能,并提供了管理复杂项目、性能调优与深度调试的进阶技巧,帮助工程师快速上手并精通这一高效仿真工具。

weixin_29238147的博客 742

pytorch 状态字典:state_dict

pytorch 中的 state_dict 是一个简单的python的字典对象,将每一层与它的对应参数建立映射关系.(如model的每一层的weights及偏置等等) (注意,只有那些参数可以训练的layer才会被保存到模型的state_dict中,如卷积层,线性层等等) 优化器对象Optimizer也有一个state_dict,它包含了优化器的状态以及被使用的超参数(如lr, momentu...

wzg2016的博客 3万+

保姆级教程:用Ansys Workbench找出BGA焊点最容易坏的位置(附热循环+振动分析)

本文提供了一份详细的Ansys Workbench教程,指导如何通过热循环和振动分析找出BGA焊点最容易失效的位置。从模型搭建、材料定义到网格划分和载荷设置,全面解析了BGA焊点可靠性分析的全流程,帮助工程师提前预防焊点开裂问题,提升电子封装设计的可靠性。

weixin_30983965的博客 420

pytorch加载和保存模型

方法一(推荐): 第一种方法也是官方推荐的方法,只保存和恢复模型中的参数。 保存 torch.save(the_model.state_dict(), PATH) 恢复 the_model = TheModelClass(*args, **kwargs) the_model.load_state_dict(torch.load(PATH)) 使用这种方法,我们需要自己导入模...

weixin_36670529的博客 1285

ChatGPT Windows桌面版:从安全下载到高效配置的全流程指南

WebView封装技术是将网页应用转化为独立桌面程序的核心方法,它通过嵌入浏览器内核实现跨平台功能复用。其原理在于利用Electron或Tauri等框架,将Web内容打包进原生应用外壳,同时注入系统级API调用能力。这项技术的工程价值在于显著提升了Web应用的系统集成度和用户体验,使其能够支持全局快捷键、独立进程和本地数据存储等原生特性。在AI助手深度集成工作流的应用场景中,这种封装模式解决了浏览器多标签页切换繁琐、会话管理不便等痛点。本文以ChatGPT桌面客户端为例,详细解析了基于开源项目的安全下载路径

weixin_30437847的博客 488

AD(十四)调用已有的PCB元件封装库

软件:Altium Designer 16 需要资料:已有的PCB原理图文件(.PCBDOC)或者PCB库文件(.PCBLIB)(来源文中细说) 调用已有的PCB元件封装库(一)从已有的PCB原理图文件(*.PCBDOC)中调用1、PCB原理图文件来源2、打开已有的PCB原理图文件(*.PCBDOC)3、生成PCB封装库4、复制封装到工程文件下的PCB库文件(*.PCBLIB)5、重名问题(二)从已有的PCB库文件(*.PCBLIB)中调用1、PCB库文件(*.PCBLIB)的来源2、打开已有的PCB库 (

Curnane0_0的博客 5084

Pytorch model.load_state_dict()

1. 问题描述: 当运行到model.load_state_dict(torch.load(trained_model.pth)["state_dict"])时报错: Missing key(s) in state_dict: "conv1.weight", "bn1.weight", "bn1.bias", "bn1.running_mean", "bn1.running_var"... Un...

zhangjipinggom的博客 2万+

高速特征金字塔多角度旋转模板匹配(工业零部件缺陷检测C++ Opencv版)

博主在性能较好的笔记本上测试算法全部执行效果大约为45ms,在一般配置的笔记本上博主在性能较好的笔记本上测试算法全部执行效果大约为130ms。下图即为实际检测效果。本文最后附有完整代码:Qt版和MFC版本(可直接运行)。...

认真努力,做一只会飞的毛毛虫。 5547

AI球球大作战:Go-Bigger多智能体决策智能挑战赛

比赛网页:https://www.datafountain.cn/competitions/549 repo:https://github.com/opendilab/Gobigger-Explore/ 入门篇 赛题说明 本次竞赛采用 Go-Bigger 作为游戏环境。Go-Bigger 是一款多人组队竞技游戏。更多细节请参考 Go-Bigger 文档。在游戏中,每支竞赛参赛队伍控制游戏中一支队伍(每支队伍由多个玩家组成)。竞赛参赛队伍需要通过提交智能体的方式,来对游戏中的某个队伍及其所包含的玩家进行控制,

小星AI 2374

手把手教你学Simulink--基于新能源与储能系统的场景实例:压缩空气储能(CAES)系统热力学建模

本文介绍了使用Simulink进行压缩空气储能(CAES)系统热力学建模的详细步骤。系统模型包含压缩机、储气装置、膨胀机、热交换器和控制器等核心模块,通过MATLAB函数计算各部件参数,并设置仿真条件。文章详细说明了从模型创建到性能评估的完整流程,包括压缩/膨胀过程的能量转换、温度压力变化等关键指标分析。该建模方法可验证设计方案的有效性,并为系统优化提供参考,适用于新能源与储能领域的研究与应用。

MHD0815的博客 9190

PreScan快速入门到精通第三十九讲基于车道线识别传感器的车道保持辅助算法Demo讲解

车道保持辅助系统使用一个前置的摄像头,一般安装在车内后视镜附近,用来检测车辆前方的车道线,并监测车辆在其车道上的位置。在有电动助力转向EPS的车辆中,它轻轻地,但明显地反转向,以保持车辆在车道上。在简单车辆动力学的Simulink模型中,一个25%的油门开度常数被设定为输入,作为车辆行驶的纵向动力。首先是近似的角度α,它是传感器航向(=车辆航向)与右侧车道车道线的切线的近似角。一个配备了车道线传感器和简单动力学模型的车辆(MAZDA),将在没有任何规定轨迹的情况下跟随道路车道线行驶。3D浏览器中的多视图。

小明师兄的博客 1843

Q-Learning工程实战:从Q表构建到边缘部署的硬核拆解

Q-Learning作为强化学习最基础的价值迭代算法,其核心在于贝尔曼方程驱动下的状态-动作价值估计。理解其更新原理(TD误差计算、max操作语义)是掌握更复杂算法如DQN、SAC的前提;而技术价值正体现在可解释性、低资源开销与物理世界对齐能力上——这使其成为嵌入式AI、工业边缘控制与教学穿透场景的首选范式。本文聚焦表格型Q-Learning在真实硬件中的落地挑战:涵盖状态离散化的物理意义切分、乐观初始化与自适应学习率设计、经验缓存机制对稀疏奖励的响应优化,以及Q表内存布局、HIL验证等工程细节。所有方案均

cuizhu0832的博客 408

抑制电磁干扰(EMI)的实用电路技术

抑制电磁干扰(EMI)的实用电路技术 qtFK_EMC_EMI 【导读】本系列文章的第 1 部分至第 4 部分详细介绍了开关电源稳压器引起的传导发射和辐射发射,包括噪声产生机制、测量要求、频率范围、适用的测试限值、传播模式和寄生效应。在第 5 部分中,我将基于这一理论基础介绍抑制电磁干扰 (EMI) 的实用电路技术。 一般来说,电路原理图和印刷电路板 (PCB) 对于实现出色的 EMI 性能至关重要。第 3 部分重点强调通过谨慎的元器件选型和 PCB 布局尽量减小“功率回路”寄生电感的重要性。电源转换

学海无涯的专栏 1196

OpenClaw实战指南:RAG+多智能体+DevOps深度集成

RAG(检索增强生成)是一种将外部知识库与大语言模型动态结合的技术,其核心在于提升模型回答的准确性与时效性;多智能体系统则通过角色分工、状态协同与上下文共享,实现复杂任务的自动化拆解与闭环执行。二者在DevOps场景中形成关键技术合力:RAG提供可审计的知识记忆,多智能体构建可编排的执行手脚,共同支撑CI/CD自动卡点、故障主动巡检、测试用例生成等高价值工程实践。本文聚焦真实产线落地经验,详解RAG分层投喂、元标签治理、多智能体终止机制与共享上下文总线等关键技术决策,助力团队跨越‘能跑’到‘稳用’的关键跃迁

abxlep7702的博客 397

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Q-Learning入门:从试错决策到工业落地的强化学习基石

Q-Learning是一种基于价值反馈的无模型强化学习算法,其核心是通过试错交互构建状态-动作价值映射(Q表),解决延迟奖励下的序贯决策问题。它不依赖环境动力学先验,仅需感知状态、执行动作、接收标量奖励即可迭代优化策略,因而成为机器人控制、智能调度、故障预测等场景的首选入门范式。相比深度强化学习,Q-learning以可解释性、低资源消耗和冷启动友好性见长,特别适合中小规模离散状态空间下的业务决策建模。本文聚焦工程落地视角,解析Q表设计原理、TD更新机制、状态/动作的业务语义化构建方法,以及ε-greedy

weixin_30660027的博客 415

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

pytorch load state dict_源码详解Pytorch的state_dict和load_state_dict

在 Pytorch 中一种模型保存和加载的方式如下:# save model.state_dict()其实返回的是一个OrderDict,存储了网络结构的名字和对应的参数,下面看看源代码如何实现的。state_dict# torch.nn.modules.module.py 可以看到state_dict函数中遍历了4中元素,分别是_paramters,_buffers,_modules和_stat...

weixin_39649490的博客 1330

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 314

把 KV Cache 压到 FP4:BitDecoding-FP4 的算法-系统协同设计复盘

长上下文推理卡在KV Cache的显存与带宽上,而现有FP4方案要么精度崩、要么速度废。这篇文章复盘BitDecoding-FP4如何用三个算法补丁加一个系统重构,在B300上把服务吞吐提升2.4–3.0倍,精度几乎不掉。

helloagent的博客 310

【算法拾光】链表 哈希表|K个一组翻转链表 + 两数之和

K个一组翻转链表需分段反转,借助哑节点与前驱指针维护连接,避免断链;两数之和利用哈希表实现O(1)查找,空间换时间,高效定位目标组合。前者考察链表指针操作,后者聚焦映射关系构建,均为面试高频题。

wuqingyigexiaoba的博客 330

排序算法深度笔记(下):非比较类排序的线性突破与工程边界

排序算法时间复杂度额外空间复杂度稳定性是否基于比较选择排序O(N^2)(O(1))不稳定是冒泡排序O(N^2)(O(1))稳定是插入排序O(N^2)(O(1))稳定是归并排序O(N\log N)(O(N))稳定是快速排序O(N\log N)O(log N)不稳定是堆排序O(N\log N)O(1)不稳定是计数排序(O(N + M))O(M))稳定否基数排序O(N)稳定否桶排序平均O(N) / 最坏O(N^2)

2401_87069538的博客 601

优选算法:分治-归并排序

交易逆序对的总数

Wangshijie1015的博客 146
上一篇: Q-learning解决悬崖问题
下一篇: 用BERT做命名实体识别任务
算法美食屋
博客等级 码龄9年 2万+粉丝 154原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值