深度强化学习-DQN算法原理与代码

2023-05-16

DQN算法是DeepMind团队提出的一种深度强化学习算法,在许多电动游戏中达到人类玩家甚至超越人类玩家的水准,本文就带领大家了解一下这个算法,论文和代码的链接见下方。

论文:Human-level control through deep reinforcement learning | Nature

代码:https://github.com/indigoLovee/DQN

喜欢的话可以点个star呢。

1 DQN算法简介

Q-learning算法采用一个Q-tabel来记录每个状态下的动作值,当状态空间或动作空间较大时,需要的存储空间也会较大。如果状态空间或动作空间连续,则该算法无法使用。因此,Q-learning算法只能用于解决离散低维状态空间和动作空间类问题。DQN算法的核心就是用一个人工神经网络q(s,a;\omega ),s\in S,a\in A来代替Q-tabel,即动作价值函数。网络的输入为状态信息,输出为每个动作的价值,因此DQN算法可以用来解决连续状态空间和离散动作空间问题,无法解决连续动作空间类问题。针对连续动作空间类问题,后面blog会慢慢介绍。

2 DQN算法原理

DQN算法是一种off-policy算法,当同时出现异策、自益和函数近似时,无法保证收敛性,容易出现训练不稳定或训练困难等问题。针对这些问题,研究人员主要从以下两个方面进行了改进。

(1)经验回放:将经验(当前状态s_{t}、动作a_{t}、即时奖励r_{t+1}、下个状态s_{t+1}、回合状态done)存放在经验池中,并按照一定的规则采样。

(2)目标网络:修改网络的更新方式,例如不把刚学习到的网络权重马上用于后续的自益过程。

2.1 经验回放

经验回放就是一种让经验概率分布变得稳定的技术,可以提高训练的稳定性。经验回放主要有“存储”和“回放”两大关键步骤:

存储:将经验以(s_{t},a_{t},r_{t+1},s_{t+1},done)形式存储在经验池中。

回放:按照某种规则从经验池中采样一条或多条经验数据。

存储的角度来看,经验回放可以分为集中式回放和分布式回放:

集中式回放:智能体在一个环境中运行,把经验统一存储在经验池中。

分布式回放:多个智能体同时在多个环境中运行,并将经验统一存储在经验池中。由于多个智能体同时生成经验,所以能够使用更多资源的同时更快地收集经验。

采样的角度来看,经验回放可以分为均匀回放和优先回放:

均匀回放:等概率从经验池中采样经验。

优先回放:为经验池中每条经验指定一个优先级,在采样经验时更倾向于选择优先级更高的经验。一般的做法是,如果某条经验(例如经验i)的优先级为p_{i},那么选取该经验的概率为:

p_{i}=\frac{p_{i}}{\sum p_{k}}

优先回放可以具体参照这篇论文:优先经验回放

经验回放的优点:

1.在训练Q网络时,可以打破数据之间的相关性,使得数据满足独立同分布,从而减小参数更新的方差,提高收敛速度。

2.能够重复使用经验,数据利用率高,对于数据获取困难的情况尤其有用。

经验回放的缺点:

无法应用于回合更新和多步学习算法。但是将经验回放应用于Q学习,就规避了这个缺点。

代码中采用集中式均匀回放,具体如下:

import numpy as np


class ReplayBuffer:
    def __init__(self, state_dim, action_dim, max_size, batch_size):
        self.mem_size = max_size
        self.batch_size = batch_size
        self.mem_cnt = 0

        self.state_memory = np.zeros((self.mem_size, state_dim))
        self.action_memory = np.zeros((self.mem_size, ))
        self.reward_memory = np.zeros((self.mem_size, ))
        self.next_state_memory = np.zeros((self.mem_size, state_dim))
        self.terminal_memory = np.zeros((self.mem_size, ), dtype=np.bool)

    def store_transition(self, state, action, reward, state_, done):
        mem_idx = self.mem_cnt % self.mem_size

        self.state_memory[mem_idx] = state
        self.action_memory[mem_idx] = action
        self.reward_memory[mem_idx] = reward
        self.next_state_memory[mem_idx] = state_
        self.terminal_memory[mem_idx] = done

        self.mem_cnt += 1

    def sample_buffer(self):
        mem_len = min(self.mem_size, self.mem_cnt)

        batch = np.random.choice(mem_len, self.batch_size, replace=True)

        states = self.state_memory[batch]
        actions = self.action_memory[batch]
        rewards = self.reward_memory[batch]
        states_ = self.next_state_memory[batch]
        terminals = self.terminal_memory[batch]

        return states, actions, rewards, states_, terminals

    def ready(self):
        return self.mem_cnt > self.batch_size

2.2 目标网络

对于基于自益的Q学习,动作价值估计和权重w有关。当权重变化时,动作价值的估计也会发生变化。在学习的过程中,动作价值试图追逐一个变化的回报,容易出现不稳定的情况。

目标网络是在原有的神经网络之外重新搭建一个结构完全相同的网络。原先的网络称为评估网络,新构建的网络称为目标网络。在学习过程中,使用目标网络进行自益得到回报的评估值,作为学习目标。在更新过程中,只更新评估网络的权重,而不更新目标网络的权重。这样,更新权重时针对的目标不会在每次迭代都发生变化,是一个固定的目标。在更新一定次数后,再将评估网络的权重复制给目标网络,进而进行下一批更新,这样目标网络也能得到更新。由于在目标网络没有变化的一段时间内回报的估计是相对固定的,因此目标网络的引入增加了学习的稳定性。

目标网络的更新方式:

上述在一段时间内固定目标网络,一定次数后将评估网络权重复制给目标网络的更新方式为硬更新(hard update),即

w_{t}\leftarrow w_{e}

其中w_{t}表示目标网络权重,w_{e}表示评估网络权重。

另外一种常用的更新方式为软更新(soft update),即引入一个学习率\tau,将旧的目标网络参数和新的评估网络参数直接做加权平均后的值赋值给目标网络

w_{t}\leftarrow \tau w_{e}+\left ( 1-\tau \right )w_{t}

学习率\tau\in \left ( 0,1 \right )

3 DQN算法伪代码

DQN算法的实现代码为:

import torch as T
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
from buffer import ReplayBuffer

device = T.device("cuda:0" if T.cuda.is_available() else "cpu")


class DeepQNetwork(nn.Module):
    def __init__(self, alpha, state_dim, action_dim, fc1_dim, fc2_dim):
        super(DeepQNetwork, self).__init__()

        self.fc1 = nn.Linear(state_dim, fc1_dim)
        self.fc2 = nn.Linear(fc1_dim, fc2_dim)
        self.q = nn.Linear(fc2_dim, action_dim)

        self.optimizer = optim.Adam(self.parameters(), lr=alpha)
        self.to(device)

    def forward(self, state):
        x = T.relu(self.fc1(state))
        x = T.relu(self.fc2(x))

        q = self.q(x)

        return q

    def save_checkpoint(self, checkpoint_file):
        T.save(self.state_dict(), checkpoint_file, _use_new_zipfile_serialization=False)

    def load_checkpoint(self, checkpoint_file):
        self.load_state_dict(T.load(checkpoint_file))


class DQN:
    def __init__(self, alpha, state_dim, action_dim, fc1_dim, fc2_dim, ckpt_dir,
                 gamma=0.99, tau=0.005, epsilon=1.0, eps_end=0.01, eps_dec=5e-4,
                 max_size=1000000, batch_size=256):
        self.tau = tau
        self.gamma = gamma
        self.epsilon = epsilon
        self.eps_min = eps_end
        self.eps_dec = eps_dec
        self.batch_size = batch_size
        self.action_space = [i for i in range(action_dim)]
        self.checkpoint_dir = ckpt_dir

        self.q_eval = DeepQNetwork(alpha=alpha, state_dim=state_dim, action_dim=action_dim,
                                   fc1_dim=fc1_dim, fc2_dim=fc2_dim)
        self.q_target = DeepQNetwork(alpha=alpha, state_dim=state_dim, action_dim=action_dim,
                                     fc1_dim=fc1_dim, fc2_dim=fc2_dim)

        self.memory = ReplayBuffer(state_dim=state_dim, action_dim=action_dim,
                                   max_size=max_size, batch_size=batch_size)

        self.update_network_parameters(tau=1.0)

    def update_network_parameters(self, tau=None):
        if tau is None:
            tau = self.tau

        for q_target_params, q_eval_params in zip(self.q_target.parameters(), self.q_eval.parameters()):
            q_target_params.data.copy_(tau * q_eval_params + (1 - tau) * q_target_params)

    def remember(self, state, action, reward, state_, done):
        self.memory.store_transition(state, action, reward, state_, done)

    def choose_action(self, observation, isTrain=True):
        state = T.tensor([observation], dtype=T.float).to(device)
        actions = self.q_eval.forward(state)
        action = T.argmax(actions).item()

        if (np.random.random() < self.epsilon) and isTrain:
            action = np.random.choice(self.action_space)

        return action

    def learn(self):
        if not self.memory.ready():
            return

        states, actions, rewards, next_states, terminals = self.memory.sample_buffer()
        batch_idx = np.arange(self.batch_size)

        states_tensor = T.tensor(states, dtype=T.float).to(device)
        rewards_tensor = T.tensor(rewards, dtype=T.float).to(device)
        next_states_tensor = T.tensor(next_states, dtype=T.float).to(device)
        terminals_tensor = T.tensor(terminals).to(device)

        with T.no_grad():
            q_ = self.q_target.forward(next_states_tensor)
            q_[terminals_tensor] = 0.0
            target = rewards_tensor + self.gamma * T.max(q_, dim=-1)[0]
        q = self.q_eval.forward(states_tensor)[batch_idx, actions]

        loss = F.mse_loss(q, target.detach())
        self.q_eval.optimizer.zero_grad()
        loss.backward()
        self.q_eval.optimizer.step()

        self.update_network_parameters()
        self.epsilon = self.epsilon - self.eps_dec if self.epsilon > self.eps_min else self.eps_min

    def save_models(self, episode):
        self.q_eval.save_checkpoint(self.checkpoint_dir + 'Q_eval/DQN_q_eval_{}.pth'.format(episode))
        print('Saving Q_eval network successfully!')
        self.q_target.save_checkpoint(self.checkpoint_dir + 'Q_target/DQN_Q_target_{}.pth'.format(episode))
        print('Saving Q_target network successfully!')

    def load_models(self, episode):
        self.q_eval.load_checkpoint(self.checkpoint_dir + 'Q_eval/DQN_q_eval_{}.pth'.format(episode))
        print('Loading Q_eval network successfully!')
        self.q_target.load_checkpoint(self.checkpoint_dir + 'Q_target/DQN_Q_target_{}.pth'.format(episode))
        print('Loading Q_target network successfully!')

算法仿真环境是在gym库中的LunarLander-v2环境,因此需要先配置好gym库。进入Aanconda中对应的Python环境中,执行下面的指令

pip install gym

但是,这样安装的gym库只包括少量的内置环境,如算法环境、简单文字游戏环境和经典控制环境,无法使用LunarLander-v2。因此还要安装一些其他依赖项,具体可以参照这篇blog:AttributeError: module ‘gym.envs.box2d‘ has no attribute ‘LunarLander‘ 解决办法

训练脚本如下:

import gym
import numpy as np
import argparse
from DQN import DQN
from utils import plot_learning_curve, create_directory

parser = argparse.ArgumentParser()
parser.add_argument('--max_episodes', type=int, default=500)
parser.add_argument('--ckpt_dir', type=str, default='./checkpoints/DQN/')
parser.add_argument('--reward_path', type=str, default='./output_images/avg_reward.png')
parser.add_argument('--epsilon_path', type=str, default='./output_images/epsilon.png')

args = parser.parse_args()


def main():
    env = gym.make('LunarLander-v2')
    agent = DQN(alpha=0.0003, state_dim=env.observation_space.shape[0], action_dim=env.action_space.n,
                fc1_dim=256, fc2_dim=256, ckpt_dir=args.ckpt_dir, gamma=0.99, tau=0.005, epsilon=1.0,
                eps_end=0.05, eps_dec=5e-4, max_size=1000000, batch_size=256)
    create_directory(args.ckpt_dir, sub_dirs=['Q_eval', 'Q_target'])
    total_rewards, avg_rewards, eps_history = [], [], []

    for episode in range(args.max_episodes):
        total_reward = 0
        done = False
        observation = env.reset()
        while not done:
            action = agent.choose_action(observation, isTrain=True)
            observation_, reward, done, info = env.step(action)
            agent.remember(observation, action, reward, observation_, done)
            agent.learn()
            total_reward += reward
            observation = observation_

        total_rewards.append(total_reward)
        avg_reward = np.mean(total_rewards[-100:])
        avg_rewards.append(avg_reward)
        eps_history.append(agent.epsilon)
        print('EP:{} reward:{} avg_reward:{} epsilon:{}'.
              format(episode + 1, total_reward, avg_reward, agent.epsilon))

        if (episode + 1) % 50 == 0:
            agent.save_models(episode + 1)

    episodes = [i for i in range(args.max_episodes)]
    plot_learning_curve(episodes, avg_rewards, 'Reward', 'reward', args.reward_path)
    plot_learning_curve(episodes, eps_history, 'Epsilon', 'epsilon', args.epsilon_path)


if __name__ == '__main__':
    main()

训练时还会用到画图函数和创建文件夹函数,我将他们另外放在一个utils.py脚本中,具体代码如下:

import os
import matplotlib.pyplot as plt


def plot_learning_curve(episodes, records, title, ylabel, figure_file):
    plt.figure()
    plt.plot(episodes, records, linestyle='-', color='r')
    plt.title(title)
    plt.xlabel('episode')
    plt.ylabel(ylabel)

    plt.show()
    plt.savefig(figure_file)


def create_directory(path: str, sub_dirs: list):
    for sub_dir in sub_dirs:
        if os.path.exists(path + sub_dir):
            print(path + sub_dir + ' is already exist!')
        else:
            os.makedirs(path + sub_dir, exist_ok=True)
            print(path + sub_dir + ' create successfully!')

仿真结果如下图所示:

通过平均奖励曲线可以看出,大概迭代到400步左右时算法趋于收敛。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度强化学习-DQN算法原理与代码 的相关文章

  • 生产者-消费者问题(有例题!!!)

    文章目录 前言问题描述如何实现思考 xff1a 能否改变相邻P V操作的顺序 知识回顾与重要考点 前言 此篇文章是我在B站学习时所做的笔记 xff0c 大部分图片都是课件老师的PPT xff0c 方便复习用 此篇文章仅供学习参考 提示 xf
  • 计算机网络习题——循环冗余校验

    3 07 要发送的数据为1101011011 采用CRC的生成多项式是 P X 61 X 4 43 X 43 1 试求应添加在数据后面的余数 xff08 1 xff09 若要发送的数据在传输过程中最后一个1变成了0 xff0c 即变成了11
  • 计算机网络课后答案(谢希仁第八版)

    计算机网络课后答案 谢希仁第八版
  • linux系统 删除文件命令

    Linux系统下删除文件是一个非常高频的需求 xff0c 几乎每天都会遇到 xff0c 所以rm命令是一个非常常用Linux命令 rm命令是英文单词 remove 的缩写 xff0c 它主要作用是 xff1a 1 删除文件 xff1b 2
  • 常见的HTTP状态码列表

    HTTP状态码列表 状态码 状态码英文名称 中文描述 1xx xff08 信息性状态码 xff09 xff1a 请求已被接受 xff0c 需要继续处理 100 Continue 继续 客户端应继续其请求 101 Switching Prot
  • 二进制的加减法_二进制加减法

    二进制的加减法 1 二进制加法 1 Binary Addition Since binary numbers consist of only two digits 0 and 1 so their addition is different
  • SQL注入攻击方法

    SQL注入攻击是一种利用Web应用程序中存在的安全漏洞 xff0c 通过在输入框中插入恶意的SQL代码 xff0c 从而实现对数据库的非法操作 以下是一些常见的SQL注入攻击方法 xff1a 使用单引号 xff08 39 xff09 进行字
  • 利用Python+selenium技术,实现浏览器基本操作详解,代码有详细注释

    首先 xff0c 需要安装selenium库和对应的浏览器驱动程序 以Chrome浏览器为例 xff0c 可以使用以下命令安装selenium和chromedriver xff1a pip install selenium 然后 xff0c
  • &和&&的区别(简单易懂)

    amp xff08 按位与 xff09 和 amp amp xff08 逻辑与 xff09 的区别如下 xff1a 1 amp amp 具有短路功能 xff0c 而 amp 不具有短路功能 2 当 amp 运算符两侧的表达式的结果均为真时
  • Spring框架学习笔记

    一 什么是Spring框架 Spring框架是由于软件开发的复杂性而创建的 Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情 然而 xff0c Spring的用途不仅仅限于服务器端的开发 从简单性 可测试性和松耦
  • 人工智能——DBSCAN密度聚类(Python)

    目录 1 概述 1 1 概念 1 2 DBSCAN数据点分类 2 DBSCAN算法流程 2 1 DBSCAN算法流程 xff1a 2 2 举例 3 案例1 xff08 Python实现 xff09 3 1 案例 3 2 Python实现 3
  • @RequestMapping的参数和用法

    在Spring MVC 中使用 64 RequestMapping 来映射请求 xff0c 也就是通过它来指定控制器可以处理哪些URL请求 xff0c 相当于Servlet中在web xml中配置 源码 xff1a 该注解说明可以在类和方法
  • Linux 实验:记录型信号量 生产者-消费者问题详解

    进程同步问题是一个非常重要且相当有趣的问题 xff0c 因而吸引了很多学者对他进行研究 本文就选取其中较为代表性的生产者 消费者问题来进行学习 xff0c 以帮助我们更好的理解进程同步的概念及实现方法 一 问题描述 有一群生产者进程在生产产
  • Linux C语言线程解决生产者与消费者

    前言 生产者 消费者模式 xff0c 生产者这边负责生产产品 而消费者负责消费产品 xff0c 对于消费者来说 xff0c 没有产品的时候只能等待产品出来 xff0c 有产品就使用它 这里我们使用一个变量来表示这个这个产品 xff0c 生产
  • Mariadb

    文章目录 1 数据库的介绍2 mariadb的安装与开启3 软件基本信息4 数据库的安全初始化4 1 执行安全初始化脚本4 2 关闭数据库开放端口 5 数据库的基本管理5 1查看5 2新建5 3更改5 4备份与删除 6 数据库密码管理6 1
  • 【FPGA】按键消抖

    文章目录 一 按键消抖概述1 为何要进行按键消抖2 消抖的方式 二 系统设计1 系统模块划分2 系统时序图 三 代码实现1 按键消抖模块 xff08 key debounce xff09 2 呼吸灯模块 xff08 led breath x
  • java字符串添加字符_如何在Java中向字符串添加字符?

    java字符串添加字符 Case 1 In a simple way we will learn how to add characters by using predefined methods 情况1 xff1a 以简单的方式 xff0
  • 【Ubuntu18.04更换国内源及404错误解决办法】

    Ubuntu18 04 arm换源方法 及 404错误解决办法 换源将下面的任选一组源放入到上面的sources list中 xff0c 96 保存退出并更新即可 96 清华源阿里源中科大源 错误apt get update 后出现 404
  • 【Linux中QT加载.so库与调用Python】

    Linux中QT添加 so库与Python库 一 如何导入 so库1 1 不同系统中 库名称各有不同1 2 Linux中的QT导入库方法 xff1a 二 调用Python2 1 添加Python库2 2 创建Python文件 引入头文件2
  • QT连接SQLServer并添加ODBC数据源

    QT连接SQLServer并添加ODBC数据源 一 创建数据源1 打开ODBC数据源2 创建数据源3 测试数据源 二 QT连接SQLServer1 连接代码2 测试成功样图 一 创建数据源 1 打开ODBC数据源 在搜索框中进行搜索ODBC

随机推荐

  • QT生成exe独立运行文件

    目录 一 封装QT独立运行的 exe文件好处1 1 xff1a 封装软件 xff1a Enigma Virtual Box 9 901 2 xff1a 下载链接 xff1a 阿里云盘 https www aliyundrive com s
  • 【1期 QT之控件的创建与使用】

    前言 QT一开始在1991年被奇趣公司研发 xff0c 创建的目的就是实现GUI图形界面开发与非GUI的开发 后来被诺基亚收购了 xff0c 维护至今 当然在诺基亚手里也是越发展越好 好了QT就介绍这么多了 我们直接上干货 xff1a 我将
  • 【2期 QT信号与槽函数&回调函数与函数指针】

    前言 信号与槽函 xff1a 一对多 多对一 多对多 类似于C 43 43 设计模式中的观察者模式 信号与槽函数不是C 43 43 标准代码 xff0c 是QT特有的 xff0c 最终通过moc meta Object Complier 进
  • 关于约瑟夫环问题的思考(数组做法)

    这几天做题时碰见了一个很有意思的问题 xff0c 也是一个十分经典问题 约瑟夫环问题 问题很简单 xff0c 就是有n个人围成一个圈 xff0c 每隔m个人就自杀一个 xff0c 直到剩下最后一个人为止 xff0c 问最后剩下的最后一个人是
  • Windows Powershell相关(历史命令、命令)

    Powershell取证 历史命令 xff1a win10 powershell的命令历史记录存储在 span class token operator span USERPROFILE span class token operator
  • 符合ASTM标准的雨流计数法及其不同的改进方法

    随着研究的深入 xff0c 人们发现采用时间序列计算载荷谱太麻烦了 xff0c 处理的工作量太大 xff0c 我们不需要将每个时刻点的载荷都做运算 xff0c 疲劳计算只需要提供幅值 均值和循环次数 xff0c 鉴于此发展出了很多不同的计数
  • 对载荷谱进行雨流计数的几个主要步骤(以四点雨流计数为例)

    对载荷谱进行雨流计数的几个主要步骤 xff08 以四点雨流计数为例 xff09 滤除小幅值 Hysteresis Filtering 峰谷滤波 Peak Valley Filtering 离散化 Discretization 四点法雨流计数
  • bigdecimal乘法_Java BigDecimal乘法()方法与示例

    bigdecimal乘法 BigDecimal类的multiple 方法 BigDecimal Class multiply method Syntax 句法 xff1a public BigDecimal multiply BigDeci
  • 侯捷老师C++学习笔记——大气编程(上)

    本课程要有一点点C或C 43 43 的基础 xff0c 学习效果会更好哦 侯捷老师讲的特别通透 xff0c 听完收获很大 Lesson1 简介 课程基础 xff1a 曾经学过某种面向过程的编程语言 procedural language 知
  • 侯捷老师C++学习笔记——大气编程(下)

    侯捷老师C 43 43 课程下半部分 本课程是对之前课程中所提的一些东西的补充 xff0c 以及C 43 43 11新特性的一些讲解 Lesson1 介绍 本课程会讲的内容 xff1a 泛型编程深入探索面向对象继承关系所形成的的对象模型 x
  • JS基础-22-HTTP和HTTPS协议

    HTTP与HTTPS 一 前言 xff1a 先来观察这两张图 xff0c 第一张访问域名http www 12306 cn xff0c 谷歌浏览器提示不安全链接 xff0c 第二张是https kyfw 12306 cn otn regis
  • Spring架构图

    1 xff0e 核心容器模块 核心容器提供Spring框架的基本功能 xff0c 包括Core Beans Context EL模块 Core模块封装了框架依赖的最底层部分 xff0c 包括资源访问 类型转换及一些常用工具类 Beans模块
  • Spring Boot 中的日志

    文章目录 一 日志的输出1 1 日志等级划分1 2 日志输出操作1 3 日志等级设置 二 日志的存储三 SpringBoot 中的日志框架四 lombok 原理 当一个项目报错以后 xff0c 如何快速的定位错误的原因 xff0c 找到解决
  • face_recognition人脸检测

    文章目录 原理一 效果预览二 实现过程 原理 face recognition是最简单的人脸识别库 xff0c 该模型的准确率为 99 38 代码仅需要三行 xff1a span class token keyword import spa
  • python自动化运维第一步-利用psutil模块获取服务器信息

    文章目录 1 简易版演示 2 不成熟版演示 初学python不久 xff0c 尝试使用python写一些简单脚本 1 简易版 shell居多 span class token keyword import span os total mem
  • 判断链表是否有环(Java)

    快慢指针 xff0c 如果有环 xff0c 两指针一定会相遇 注意 xff1a 空链表和一个元素的链表直接返回false 如果没环 xff0c 快指针一定会先到null xff0c 所以只需判断快指针是否为null 注意空指针异常 xff0
  • Nginx学习笔记06——Nginx反向代理

    正向代理和反向代理 正向代理 用户和外网不能互通 xff0c 通过代理服务器将用户请求发送给外网 反向代理 用户和nginx是互通的 xff0c 用户和应用服务器是不互通的 xff0c 用户发送请求到nginx xff0c nginx作为代
  • Python调用adb shell

    在Android开发中 xff0c ADB xff08 Android Debug Bridge xff09 是一个非常重要的工具 它可以让我们通过命令行或者其他的客户端与安装了ADB驱动的Android设备进行通信 xff0c 并进行一些
  • c语言幂函数_C ++中的幂函数

    c语言幂函数 C 43 43 幂函数 C 43 43 power functions Power functions are used to calculate the powers like raise to power square r
  • 深度强化学习-DQN算法原理与代码

    DQN算法是DeepMind团队提出的一种深度强化学习算法 xff0c 在许多电动游戏中达到人类玩家甚至超越人类玩家的水准 xff0c 本文就带领大家了解一下这个算法 xff0c 论文和代码的链接见下方 论文 xff1a Human lev