CartPole 强化学习详解1 - DQN

2023-05-16

工作中常会接触到强化学习的内容,自己以gym环境中的Cartpole为例动手实现一下,记录点实现细节。环境:python = 3.6.13; pytorch = 1.10.2

目录

1. gym-CartPole环境准备

2. PID控制

3. DQN控制

3.1 问题1:网络要训成什么样才算能用?

3.2 问题2:调整哪些细节可以提升网络表现?

4. 遗留问题

5. DQN改进



1. gym-CartPole环境准备

环境是用的gym中的CartPole-v1,就是火柴棒倒立摆。gym是openai的开源资源,具体如何安装可参照:

强化学习一、基本原理与gym的使用_wshzd的博客-CSDN博客_gym 强化学习

这个环境的具体细节(参考gym源码cartpole.py):

action只有向左向右两个选择,离散量

观测值有4个,x, x_dot, theta, theta_dot,源文件里也给了具体的值域范围,超过这个范围,会认为done。另外这个环境超过500步就会认为是done了。


| Num | Observation           | Min                  | Max                |
|-----|-----------------------|----------------------|--------------------|
| 0   | Cart Position         | -4.8                 | 4.8                |
| 1   | Cart Velocity         | -Inf                 | Inf                |
| 2   | Pole Angle            | ~ -0.418 rad (-24°)  | ~ 0.418 rad (24°)  |
| 3   | Pole Angular Velocity | -Inf                 | Inf                |  

2. PID控制

这个问题其实是个很基本的控制问题,也不太涉及到要靠什么多步决策优化来搞,所以简单搞了个PID控一下,主要为了熟悉下这个环境,具体没啥好解释的,直接放一下代码,因为随便玩一下,参数没有细调,大概能用。

import gym
from matplotlib import animation
import matplotlib.pyplot as plt

env = gym.make('CartPole-v1')
obs = env.reset()

kp = 0.000
kv = -0.002
ka = -0.3
kav = -0.01
ks = -0.000
sum_angle = 0.000
frames = []

def save_gif(frames):
    patch = plt.imshow(frames[0])
    plt.axis('off')
    def animate(i):
        patch.set_data(frames[i])
    anim = animation.FuncAnimation(plt.gcf(), animate, frames=len(frames), interval=5)
    anim.save('./CartPortCtrl.gif', writer='imagemagick', fps=30)

def CalcAction(obs):
    action = 0 # 0 meanleft, 1 means right
    global sum_angle
    sum = kp * obs[0] + kv * obs[1] + ka * obs[2] + kav * obs[3] + ks * sum_angle
    sum_angle += obs[2]
    if (sum < 0.0):
        action = 1
    else:
        action = 0
    return action

for _ in range(200):
    frames.append(env.render(mode='rgb_array'))
    action = CalcAction(obs)
    print('action = %d' % action)
    obs, reward, done, info = env.step(action)
env.close()
save_gif(frames)


具体效果见上面,200步内还是比较稳的,但是到300-400步,会渐渐发散掉。

3. DQN控制

因为是离散型问题,选用了最简单的DQN实现,用Pytorch实现的,这里代码实现很多参考的是:

强化学习算法实例DQN代码PyTorch实现 - -Rocky- - 博客园

另外有些基本概念学习了下莫烦的视频:

强化学习 (Reinforcement Learning) | 莫烦Python

基本公式:

上述这两个资料只能教你最简单的实现,将程序搭到一个能跑通的状态,但如何得到一个好用的网络 & 调试中的trick没有仔细介绍,为此本文记录了一些细节。调试过程中困惑我比较久的一个问题:根据网上的方法训练的网络loss看上去收敛了,其实并不能很好地控制CartPole,所以这里尝试回答两个问题:

问题1:网络要训成什么样才算能用?

问题2:调整哪些细节可以提升网络表现?

先放代码再回答问题:

GitHub - BITcsy/gymTesthttps://github.com/BITcsy/gymTest本文涉及的两段代码都可以在上述地址找到。

import gym
from matplotlib import animation
import matplotlib.pyplot as plt

import torch.nn as nn
import torch.nn.functional as F
import torch
import numpy as np

class Net(nn.Module):
    def __init__(self, n_states, n_actions):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(n_states, 10)
        self.fc2 = nn.Linear(10, n_actions)
        self.fc1.weight.data.normal_(0, 0.1)
        self.fc2.weight.data.normal_(0, 0.1)

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

class DQN:
    def __init__(self, n_states, n_actions):
        print("<DQN init>")
        self.eval_net, self.target_net = Net(n_states, n_actions), Net(n_states, n_actions) # nit two nets
        self.loss = nn.MSELoss()
        self.optimizer = torch.optim.Adam(self.eval_net.parameters(), lr=0.01)
        self.n_actions = n_actions
        self.n_states = n_states
        # 使用变量
        self.learn_step_counter = 0  # target网络学习计数
        self.memory_counter = 0  # 记忆计数
        self.memory = np.zeros((2000, 2 * n_states + 1 + 1))  # s, s', a, r
        self.cost = []  # 记录损失值
        self.done_step_list = []

    def choose_action(self, state, epsilon):
        state = torch.unsqueeze(torch.FloatTensor(state), 0)  # (1,2)
        if np.random.uniform() < epsilon:
            action_value = self.eval_net.forward(state)
            action = torch.max(action_value, 1)[1].data.numpy()[0] # d the max value in softmax layer. before .data, it is a tensor
        else:
            action = np.random.randint(0, self.n_actions)
        # print("action=", action)
        return action

    def store_transition(self, state, action, reward, next_state):
        # print("<store_transition>")
        transition = np.hstack((state, [action, reward], next_state))
        index = self.memory_counter % 2000  # 满了就覆盖旧的
        self.memory[index, :] = transition
        self.memory_counter += 1

    def learn(self):
        # print("<learn>")
        # target net 更新频率,用于预测,不会及时更新参数
        if self.learn_step_counter % 100 == 0:
            self.target_net.load_state_dict((self.eval_net.state_dict()))
            # print("update eval to target")
        self.learn_step_counter += 1

        # 使用记忆库中批量数据
        sample_index = np.random.choice(2000, 16)  # 200个中随机抽取32个作为batch_size
        memory = self.memory[sample_index, :]  # 取的记忆单元,并逐个提取
        state = torch.FloatTensor(memory[:, :self.n_states])
        action = torch.LongTensor(memory[:, self.n_states:self.n_states + 1])
        reward = torch.LongTensor(memory[:, self.n_states + 1:self.n_states + 2])
        next_state = torch.FloatTensor(memory[:, self.n_states + 2:])

        # 计算loss,q_eval:所采取动作的预测value,q_target:所采取动作的实际value
        q_eval = self.eval_net(state).gather(1, action) # eval_net->(64,4)->按照action索引提取出q_value
        q_next = self.target_net(next_state).detach()
        # torch.max->[values=[],indices=[]] max(1)[0]->values=[]
        q_target = reward + 0.9 * q_next.max(1)[0].unsqueeze(1) # label
        loss = self.loss(q_eval, q_target)  # td error
        self.cost.append(loss)
        # 反向传播更新
        self.optimizer.zero_grad()  # 梯度重置
        loss.backward()  # 反向求导
        self.optimizer.step()  # 更新模型参数

    def plot_cost(self):
        plt.subplot(1,2,1)
        plt.plot(np.arange(len(self.cost)), self.cost)
        plt.xlabel("step")
        plt.ylabel("cost")

        plt.subplot(1,2,2)
        plt.plot(np.arange(len(self.done_step_list)), self.done_step_list)
        plt.xlabel("step")
        plt.ylabel("done step")
        plt.show()

def save_gif(frames):
    patch = plt.imshow(frames[0])
    plt.axis('off')

    def animate(i):
        patch.set_data(frames[i])

    anim = animation.FuncAnimation(plt.gcf(), animate, frames=len(frames), interval=5)
    anim.save('./CartPortRL.gif', writer='imagemagick', fps=30)

if __name__ == "__main__":
    env = gym.make('CartPole-v1')
    frames = []
    # train
    counter = 0
    done_step = 0
    max_done_step = 0
    num = 200000
    negative_reward = -10.0
    x_bound = 1.0
    state = env.reset()
    model = DQN(
        n_states=4,
        n_actions=2
    )  # 算法模型
    model.cost.clear()
    model.done_step_list.clear()
    for i in range(num):
        # env.render()
        # frames.append(env.render(mode='rgb_array'))
        epsilon = 0.9 + i / num * (0.95 - 0.9)
        # epsilon = 0.9
        action = model.choose_action(state, epsilon)
        # print('action = %d' % action)
        state_old = state
        state, reward, done, info = env.step(action)
        x, x_dot, theta, theta_dot = state
        # r1 = (env.x_threshold - abs(x)) / env.x_threshold - 0.8  # x_threshold 4.8
        # r2 = (env.theta_threshold_radians - abs(theta)) / env.theta_threshold_radians - 0.5
        if (abs(x) > x_bound):
            r1 = 0.5 * negative_reward
        else:
            r1 = negative_reward * abs(x) / x_bound + 0.5 * (-negative_reward)
        if (abs(theta) > env.theta_threshold_radians):
            r2 = 0.5 * negative_reward
        else:
            r2 = negative_reward * abs(theta) / env.theta_threshold_radians + 0.5 * (-negative_reward)
        reward = r1 + r2
        if done:
            reward += negative_reward
        # print("x = %lf, r1 = %lf, theta = %lf, r2 = %lf" % (x, r1, theta, r2))
        model.store_transition(state_old, action, reward, state)
        if (i > 2000 and counter % 10 == 0):
            model.learn()
            counter = 0
        counter += 1
        done_step += 1
        if (done):
            # print("reset env! done_step = %d, epsilon = %lf" % (done_step, epsilon))
            if (done_step > max_done_step):
                max_done_step = done_step
            state = env.reset()
            model.done_step_list.append(done_step)
            done_step = 0
    #model.plot_cost()  # 误差曲线
    #print("reccurent time = %d, max done step = %d, final done step = %d" % (retime, max_done_step, model.done_step_list[-1]))

    # test
    state = env.reset()
    for _ in range(400):
        frames.append(env.render(mode='rgb_array'))
        action = model.choose_action(state, 1.0)
        state, reward, done, info = env.step(action)
        if (done):
            state = env.reset()
            print("test try again")
            break
    env.close()
    save_gif(frames)




基本语法:(遗留)

3.1 问题1:网络要训成什么样才算能用?

这个算法里loss就是TD error,起初训练出来的网络在loss图上已经收敛了,如图:

但是用这个网络实际控制CartPole完全就是在乱动,一味地增大训练步数效果甚微。考虑到这个任务其实是希望将CartPole立住更长的时间,因此通过训练过程中训练能走到的最大步数作为判断网络质量的依据,这里提了两个指标:训练中达到的最大步数 & 训练截止前最后一次网络坚持到的步数这么定不完全严谨,因为DQN在选动作的过程中是会有随机因素在,不过多次测试可以大致反应训练效果了。严谨的做法就是直接用这个网络去控制CartPole看效果。

3.2 问题2:调整哪些细节可以提升网络表现?

直觉上猜测主要的影响因素:训练步长,epsilon,reward设计。其中reward设计是看了莫烦的视频得到的启发,因为CartPole环境里默认的reward实在太粗糙了,只有0,1,没法表征出比较连续的量。所以我尝试了一下下面这些因素,指标用的是3.1小节里提到的那两个步数,为了避免随机因素的影响,每组参数我做了10组实验,通过均值比对分析了下各个指标的影响:

  • 因素1:训练步数,默认10万步
  • 因素2:异常结束(没有到500步)失败reward惩罚
  • 因素3:rewad = r1 + r2(同莫烦视频)
  • 因素4:reward基准数值(莫烦视频里r1,r2分别有个-0.8和-0.5的偏移量,我个人存疑所以试了一下)
  • 因素5:epsilon渐变(0.9->0.95渐变)
  • 因素6:target网络同步频率,就是对应于DQN里的fix target策略,要多久一次更新一下target
  • 因素7:batch size
  • 因素8:Gamma折扣系数

直接看结果:

 结论:

  • 因素1:训练步数。表格中没给对比结果,我实验过程中有明显影响,得保证一定量才行,这个问题推荐20万步以上
  • 因素2:异常结束(没有到500步)失败reward惩罚。影响很大,如果没有网络基本不能用,这是该任务关键的稀疏奖励
  • 因素3:rewad = r1 + r2(同莫烦视频)。有负向影响,去掉反而更好了
  • 因素4:reward基准数值(同莫烦视频)。有负向影响,去掉反而更好了
  • 因素5:epsilon渐变(0.9->0.95渐变)。直接用0.9反而更好了,训练过程中发现使用可变epsilon的时候如果网络还没有收敛到比较好的解,epsilon就比较大了,网络可能就崩了,再也回不来了。如图:
  • 因素6:target网络同步频率。影响很大,太小的话不利于收敛。侧面反应fix target的重要性,不能同步太快。
  • 因素7:batch size。没太大影响
  • 因素8:Gamma折扣系数。影响很大,最开始没注意到这个数太小,调成0.9网络效果要好很多。

最后做了几个调整:

  • 训练20万步
  • 配合负向reward,做整个reward的渐变,防止reward跳变导致reward太稀疏
  • Gamma = 0.9
  • target同步频率:训练100次同步一次

最后效果:可以达到500步,不过网络训练有随机性,训练多版网络不能保证每个网络都很稳:

4. 遗留问题

在训练过程中思考了强化学习在实际工业场景中使用的几个问题,遗留在这里希望大佬指导:

  • gym环境是一个同步环境,是可以step运行的。但是常用的机器人/无人平台系统通常都是异步系统,是不太可能异步运行的,具体任务中如何解决异步系统和同步系统差异带来的问题?

  • 如何确保模型能用?我在这里采用了和任务本身强相关的一种评价方式,那对于大多数任务是否必须通过另外的一套测评系统才能验证?训练过程中哪些信息可以作为辅助判断?
  • 稀疏奖励是否有设置技巧?权重是否要明显大于密集型reward?不然是否会淹没在数据海中?
  • RL的泛化性存在很大的问题,如何知道网络有效边界,保证在边界中一定能用?以CartPole为例,离中心位置越远的地方被探索到的数据越少,实际运行过程中,很可能因为没有探索到导致网络结果崩了,如何有效提升其泛化性,或者如何知道其边界?

5. DQN改进

  • Double DQN(DDQN):为解决DQN过估计的问题,修改了Q现实计算中直接取最大的方法,利用Q估计来确定a,再以该a来确定Q现实。
  • Prioritized Experience Replay:为了解决始终用无效数据训练的问题,优先计算td error较大的数据,算法实现用了SumTree的数据结构。
  • Dueling DQN:加速收敛。将Q拆分成了V(s) + Adv(a)这样的形式,一个和s有关,一个和a有关。训练过程中也加入了求均值等trick,方式训练退化成了直接学Q。
  • 其他详见Rainbow的解析:Rainbow: 融合DQN六种改进的深度强化学习方法! - 知乎 

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

CartPole 强化学习详解1 - DQN 的相关文章

  • 两个向量叉乘(定义是类似多项式相乘再求和)和点乘(定义是对应位置的坐标相乘再求和)的定义和证明,以及和四元数乘法的联系和区别

    向量叉乘 xff1a 两个向量的坐标量积又叫做向量积 xff0c 用于计算法向量 游戏内应用 xff1a 用于判断物体在自身的左右方位 还有应用于图形学里 xff0c 对环境光照于自身顶点或者片元的法向量的夹角来判断光漫反射的强弱 用于相机
  • Android开机log和常见异常的分析

    Android开机log分析 1 如果开机过程还没有出现开机动画 xff0c 就已经异常 xff0c 直接抓取UART串口log xff1b 2 如果开机动画已经显示 xff0c 后面出现异常 xff0c 可以通过adb logcat抓取l
  • 面试题整理简历中深度学习机器学习相关的知识及linux操作系统命令

    深度学习与机器学习 都在整理关于后台的 xff0c 被问到后忘了 xff0c 尴尬 的确是我的问题 xff0c 基本的机器学习知识还是要整理一波 o inception 网络 xff1a 主要应用了深度可分离卷积 xff1a 主要用了大尺度
  • 面试可能遇到的问题野指针等解决方法

    空指针 xff1a 一般声明一个指针变量赋值为NULL xff0c 这就是空指针 xff0c 各个类型的空指针都存在确确实实的内存地址 xff0c 但是不会指向任何有效的值的内存地址 xff0c 对空指针操作 xff0c 例如访问属性和方法
  • 大规模分布式储存系统笔记(一)

    分布式储存系统的特性 xff1a 1 可扩展性 可按集群规模增长 xff0c 系统性能线性增长 xff1b 2 低成本 系统自动容错 xff0c 自动负载均衡 xff0c 运维方便 3 高性能 4 易用性 对外提供接口 数据类型 xff1a
  • 用MATLAB实现对运动物体识别与跟踪

    不得不说MATLAB的图像处理函数有点多 xff0c 但速度有时也是出奇的慢 还是想c的指针 xff0c 虽然有点危险 xff0c 但速度那是杠杠的 第二个MATLAB程序 xff0c 对运动物体的识别与追踪 这里我们主要运用帧差法实现运动
  • PS 开启GPU加速图片处理

    还认为你的电脑的速度效果比不上苹果吗 xff1f 还在嫌电脑渲染速度慢吗 xff1f 试一下 xff0c 电脑开启GPU硬件加速吧 xff01 只要有独显轻松加速 xff08 毕竟苹果笔记本要配独显电脑的价格基本上在15000以上 xff0
  • 【附源码】计算机毕业设计SSM社区团购系统

    项目运行 环境配置 xff1a Jdk1 8 43 Tomcat7 0 43 Mysql 43 HBuilderX xff08 Webstorm也行 xff09 43 Eclispe xff08 IntelliJ IDEA Eclispe
  • Maven 使用slf4j 没有输出控制台

    Maven 使用slf4j 没有输出控制台并报如下错 xff1a log4j WARN No appenders could be found span class hljs keyword for span logger Redissen
  • 管道鸟cortex-M4(TM4C1294)

    看到满屏的贪吃蛇 xff0c 我也来开源一个Ti开发板 xff08 TM4C1294 xff09 的游戏 将简化版的管道鸟 xff0c 根据自己玩的经历 xff0c 在cortexm4开发板上重新撸了一边 xff0c 设计思路 xff1a
  • C#连接MYSQL数据库并进行查询

    之前用MFC开发结果界面太难看被pass了 要求用C 重新来开发 gt lt 不过终于摆脱VC6 0的蛋疼操作了Y 先来连接数据库 xff08 1 xff09 用c 连接MYSQL数据库需要用到mysql connector net xff
  • Jetson tx2刷机过程中的坑

    暑假各种事忙得差不多后 xff0c 终于有时间拿出早就申请到的tx2 xff0c 开始刷机教程 xff0c 这两天几乎踩边了所有的坑 第一个坑 xff0c 虚拟机 一般在安装VMware虚拟机时 xff0c 建议的安装空间20GB xff0
  • python词云实现

    python的一个蛮酷炫的功能是可以轻松地实现词云 github上有关于这个项目的开源代码 xff1a https github com amueller word cloud 注意跑例程时要删除里面的wordcloud文件夹 词云的功能有
  • ubuntu18切换为gnome桌面托盘图标消失

    在软件菜单中选择 优化 拓展 gt Ubuntu appindicators xff0c 打开此项即可 效果
  • Expression #1 of ORDER BY clause is not in SELECT list, references column 'ekbX1.t0.name' which is n

    报错信息 xff1a Expression 1 of ORDER BY clause is not in SELECT list references column 39 ekbX1 t0 name 39 which is not in S
  • Archlinux + kde桌面环境 安装

    1 首先余留磁盘空间 2 xff1a 官网下载ArchLinux镜像 3 xff1a rufus刻录 4 xff1a 打开电脑从u盘启动 5 xff1a 首先联网 如果用wifi终端输入下面命令 xff1a wifi menu 如果是用网线
  • 判断图的连通子图个数

    题目要求 给定一个具有n个顶点 m条边的无向图G 假设项点的编号为1 n 基于深度优先搜索算法 xff0c 编写程序 求无向图G连通子图的个数 输入格式 第一行两个整数n m 分别表示图G的顶点数和边的数量 下面m行的每 行有两个整数a和b
  • Java常用包有哪些

    Java lang 语言包 Java语言的基础类 xff0c 包括Object类 Thread类 String Math System Runtime Class Exception Process等 xff0c 是Java的核心类库 最重
  • QT 建立透明背景图像QPixmap

    列将下面背景透明图片1转变成图片2 图1 图2 span class hljs preprocessor include 34 mainwindow h 34 span span class hljs preprocessor includ
  • Spring Boot——日志文件

    文章目录 1 日志的作用2 日志的用法3 自定义日志打印日志格式的说明 4 日志级别5 在配置文件中设置日志级别5 1设置全局的日志级别和局部文件夹的日志级别 6 日志持久化7 更简单的日志输出 lombok 1 日志的作用 日志的作用 x

随机推荐

  • VxWorks入门06:虚拟机中运行

    在下载的VxWorks安装包中 xff0c 包含了一份在虚拟机下安装测试的文档 xff0c 我们按照这个文档测试一下 VxWorks 6 8 3 43 VxWorks Workbench 3 2 3 43 VMware 16 1 2 43
  • 后浪小萌新Python --- 类中的属性

    一 什么是属性 我们通过类中的属性来保存类相关的属性 二 属性的分类 类中的属性分为两种 xff1a 类属性和对象属性 类属性 类的字段 a 定义 xff1a 直接定义在类中的变量就是类属性 b 使用 xff1a 类 属性 c 什么时候用
  • 业务架构的定义、特性和方法

    引言 业务架构一般不被开发重视 xff0c 开发人员喜欢追求新技术 xff0c 而技术是服务于业务的 xff0c 现在没有一项技术是自娱自乐的 xff0c 一定要支撑业务 xff0c 否则没有场景 设计好业务架构要考虑的方面比较多 xff0
  • VMware虚拟机扩展磁盘空间Ubuntu(超简单)

    一 简介 在平时使用时 xff0c 会遇到安装的虚拟机磁盘空间不足 的情况 此时需要给系统扩展磁盘空间 网上的很多教程都是输入一堆命令 xff0c 申请 分配 初始化 挂载等等特别麻烦 今天介绍一个最简单 最实用 的方法 二 实操 先进入r
  • Ubuntu 安装git及git命令

    1 检查git是否已经安装 xff0c 输入git version命令即可 xff0c 如果没有显示版本号表示没有安装git 2 安装git sudo apt get install git 3 配置git全局环境 git config g
  • Bad method handle type 7异常解决

    在利用androidx版本写demo时 xff0c 在添加了一些依赖后 xff0c 遇到了java lang ClassNotFoundExceptionbug xff0c 这就很奇怪了 xff0c 我就添加rxjava3的依赖 xff0c
  • linux防火墙添加端口

    iptables版 iptables nL line number vi etc sysconfig iptables 添加以下语句 A RH Firewall 1 INPUT p tcp m state state NEW m tcp d
  • 如何在webstorm使用eslint检查代码规范

    一 安装esLint xff08 一 xff09 打开项目代码 xff0c 进入terminal xff08 二 xff09 安装esLint 1 安装esLint npm install eslint span class token o
  • VUE基本格式

    96 VUE基本格式 lt template gt lt div gt lt div gt lt template gt lt script gt export default beforeCreate function data retu
  • Decode Ways 解码方法

    一条包含字母 A Z 的消息通过以下方式进行了编码 xff1a 39 A 39 gt 1 39 B 39 gt 2 39 Z 39 gt 26 给定一个只包含数字的非空 字符串 xff0c 请计算解码方法的总数 示例 1 输入 34 12
  • CAS单点登录6 - 服务端自定义返回的用户信息

    原理 返回的用户信息是在deployerConfigContext xml中的配置的 既然想自定义返回的用户信息 xff0c 那么继承org jasig services persondir support StubPersonAttrib
  • kotlin-android-extensions处理方案

    不幸的是 xff0c kotlin android extensions官方提示过时了 xff0c 而且列出来了几个过时的原因 但是这些我都不在乎 xff0c 也不觉得会对我产生什么影响 那可以尝试这样吧 xff0c 再被as彻底删除之后
  • Fragment跳转到Activity无动画

    这段代码无效果 xff1a startActivity span class hljs keyword new span Intent mContext GalleryActivity span class hljs keyword cla
  • Matlab科研绘图颜色补充(特别篇)—51种中国传统颜色

    前几天在找资料的时候 xff0c 发现了这个 xff1a 这是由 中国国家地理 杂志社制作的色卡 xff0c 据说总共包含98种中国传统颜色 xff0c 但目前能找到的就是这51种 xff08 而且比较模糊 xff09 百草霜 竹月 胭脂
  • Tensorflow Lite GPU在安卓上实现

    在近期工作中 xff0c 采用TensorFlow Lite将ssd mobilenet目标检测模型移植安卓机上 从安卓机测试的效果来看 xff0c 非量化的模型每帧图像推理的速率较慢 为压缩模型提升推理速度 xff0c 采用了减少模型深度
  • 面试官让我手写一个生产者消费者模式

    不知道你是否遇到过面试官让你手写生产者消费者代码 别说 xff0c 前段时间有小伙伴还真的遇到了这种情况 当时是一脸懵逼 但是 xff0c 俗话说 xff0c 从哪里跌倒就要从哪里爬起来 既然这次被问到了 xff0c 那就回去好好研究一下
  • 2-11 附:文档的基本操作 - 查询

  • ActiveMQ的简单Topic实现案例

    首先我们在这里要说一下消息中间件中有两个角色 xff0c 生产者Producer与消费者Consumer xff0c 简单理解即使发发送消息者与接收消息者 xff0c 在编写代码之前我们需要将下载到的ActiveMQ压缩包中的activem
  • Java学习之旅--集合的使用(Map集合)

    好几天没有更新了 xff0c 主要是最近正在学习集合 xff0c 让博主有点头大 所以就耽误了 xff1a 现在就来说说集合里的Map集合 xff1a import java span class hljs preprocessor uti
  • CartPole 强化学习详解1 - DQN

    工作中常会接触到强化学习的内容 xff0c 自己以gym环境中的Cartpole为例动手实现一下 xff0c 记录点实现细节 环境 xff1a python 61 3 6 13 xff1b pytorch 61 1 10 2 目录 1 gy