惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
C
Cisco Blogs
The Hacker News
The Hacker News
T
Tor Project blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Register - Security
The Register - Security
云风的 BLOG
云风的 BLOG
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
Vercel News
Vercel News
C
CERT Recently Published Vulnerability Notes
I
InfoQ
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
M
MIT News - Artificial intelligence
I
Intezer
aimingoo的专栏
aimingoo的专栏
U
Unit 42
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 热门话题
Microsoft Security Blog
Microsoft Security Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
P
Proofpoint News Feed
P
Proofpoint News Feed
B
Blog
T
Threat Research - Cisco Blogs
博客园 - 叶小钗
Recorded Future
Recorded Future
Last Week in AI
Last Week in AI
N
News and Events Feed by Topic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Engineering at Meta
Engineering at Meta
G
Google Developers Blog
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
MyScale Blog
MyScale Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Schneier on Security
Schneier on Security
N
News | PayPal Newsroom
C
Cybersecurity and Infrastructure Security Agency CISA
H
Help Net Security
博客园 - 聂微东
H
Hackread – Cybersecurity News, Data Breaches, AI and More
G
GRAHAM CLULEY

极客兔兔

Go sync.Cond | Go 语言高性能编程 Go 死码消除与调试(debug)模式 | Go 语言高性能编程 Go sync.Once | Go 语言高性能编程 Go 逃逸分析 | Go 语言高性能编程 2020 年终总结 | 极客兔兔 Go struct 内存对齐 | Go 语言高性能编程 Go 空结构体 struct{} 的使用 | Go 语言高性能编程 控制协程(goroutine)的并发数量 | Go 语言高性能编程 | 极客兔兔 如何退出协程 goroutine (其他场景) | Go 语言高性能编程 如何退出协程 goroutine (超时场景) | Go 语言高性能编程 Go 语言陷阱 - 数组和切片 | Go 语言高性能编程 减小 Go 代码编译后的二进制体积 | Go 语言高性能编程 Go Reflect 提高反射性能 | Go 语言高性能编程 读写锁和互斥锁的性能比较 | Go 语言高性能编程 | 极客兔兔 for 和 range 的性能比较 | Go 语言高性能编程 切片(slice)性能及陷阱 | Go 语言高性能编程 | 极客兔兔 字符串拼接性能及原理 | Go 语言高性能编程 | 极客兔兔 pprof 性能分析 | Go 语言高性能编程 benchmark 基准测试 | Go 语言高性能编程 Go 语言高性能编程 | 极客兔兔 Go 接口型函数的使用场景 | 极客兔兔 Python 简明教程 | 快速入门 | 极客兔兔 Go 语言笔试面试题(代码输出) | 极客面试 | 极客兔兔 动手写RPC框架 - GeeRPC第七天 服务发现与注册中心(registry) | 极客兔兔 动手写RPC框架 - GeeRPC第六天 负载均衡(load balance) 动手写RPC框架 - GeeRPC第五天 支持HTTP协议 | 极客兔兔 动手写RPC框架 - GeeRPC第四天 超时处理(timeout) | 极客兔兔 动手写RPC框架 - GeeRPC第三天 服务注册(service register) 动手写RPC框架 - GeeRPC第二天 支持并发与异步的客户端 | 极客兔兔 动手写RPC框架 - GeeRPC第一天 服务端与消息编码 | 极客兔兔 7天用Go从零实现RPC框架GeeRPC | 极客兔兔 Go 语言笔试面试题(并发编程) | 极客面试 | 极客兔兔 Go 语言笔试面试题(基础语法) | 极客面试 | 极客兔兔 Go 语言笔试面试题汇总 | 极客面试 | 极客兔兔 Go Context 并发编程简明教程 | 快速入门 Go Mmap 文件内存映射简明教程 | 快速入门 动手写ORM框架 - GeeORM第七天 数据库迁移(Migrate) | 极客兔兔 动手写ORM框架 - GeeORM第六天 支持事务(Transaction) | 极客兔兔 动手写ORM框架 - GeeORM第五天 实现钩子(Hooks) | 极客兔兔 动手写ORM框架 - GeeORM第四天 链式操作与更新删除 | 极客兔兔 动手写ORM框架 - GeeORM第三天 记录新增和查询 | 极客兔兔 动手写ORM框架 - GeeORM第二天 对象表结构映射 | 极客兔兔 动手写ORM框架 - GeeORM第一天 database/sql 基础 SQLite 常用命令 | 速查表(Cheat Sheet) 7天用Go从零实现ORM框架GeeORM | 极客兔兔 动手写分布式缓存 - GeeCache第七天 使用 Protobuf 通信 动手写分布式缓存 - GeeCache第六天 防止缓存击穿 | 极客兔兔 动手写分布式缓存 - GeeCache第五天 分布式节点 | 极客兔兔 动手写分布式缓存 - GeeCache第四天 一致性哈希(hash) | 极客兔兔 Go Mock (gomock)简明教程 | 快速入门 动手写分布式缓存 - GeeCache第三天 HTTP 服务端 动手写分布式缓存 - GeeCache第二天 单机并发缓存 | 极客兔兔 Go Test 单元测试简明教程 | 快速入门 7天用Go从零实现分布式缓存GeeCache | 极客兔兔 Go WebAssembly (Wasm) 简明教程 | 快速入门 Go RPC & TLS 鉴权简明教程 | 快速入门 Go Protobuf 简明教程 | 快速入门 Go语言动手写Web框架 - Gee第七天 错误恢复(Panic Recover) WSL, Git, Mircosoft Terminal 等常用工具配置 Rust 简明教程 | 快速入门 | 极客兔兔 Go语言动手写Web框架 - Gee第六天 模板(HTML Template) 百宝箱 - 值得收藏的工具网站 | 极客兔兔 Go语言动手写Web框架 - Gee第五天 中间件Middleware | 极客兔兔 Go语言动手写Web框架 - Gee第四天 分组控制Group | 极客兔兔 Go语言动手写Web框架 - Gee第三天 前缀树路由Router | 极客兔兔 博客折腾记(七) - Gitalk Plus | 极客兔兔 Go语言动手写Web框架 - Gee第二天 上下文Context | 极客兔兔 Go2 新特性简明教程 | 快速入门 | 极客兔兔 博客折腾记(六) - 不要为了流量忘记了初心 | 极客兔兔 Go语言动手写Web框架 - Gee第一天 http.Handler | 极客兔兔 7天用Go从零实现Web框架Gee教程 | 极客兔兔 Go Gin 简明教程 | 快速入门 Go 语言简明教程 | 快速入门 | 极客兔兔 机器学习笔试面试题 11-20 | 极客面试 | 极客兔兔 机器学习笔试面试题 1-10 | 极客面试 | 极客兔兔 机器学习笔试面试题汇总 | 极客面试 | 极客兔兔 TensorFlow 2 中文文档 - RNN LSTM 文本分类 TensorFlow 2 中文文档 - TFHub 迁移学习 TensorFlow 2 中文文档 - 卷积神经网络分类 CIFAR-10 TensorFlow 2 中文文档 - 保存与加载模型 TensorFlow 2 中文文档 - 过拟合与欠拟合 TensorFlow 2 中文文档 - 回归预测燃油效率 TensorFlow 2 中文文档 - 特征工程结构化数据分类 TensorFlow 2 中文文档 - IMDB 文本分类 TensorFlow 2 中文文档 - MNIST 图像分类 TensorFlow 2 / 2.0 中文文档 TensorFlow 2.0 (九) - 强化学习 70行代码实战 Policy Gradient 博客折腾记(五) - 友链这件事,没那么简单 | 极客兔兔 博客折腾记(四) - 原创资格是争取来的 | 极客兔兔 TensorFlow 2.0 (七) - 强化学习 Q-Learning 玩转 OpenAI gym 博客折腾记(三) - 主题设计、彩蛋与阅读量翻倍 | 极客兔兔 TensorFlow 2.0 (六) - 监督学习玩转 OpenAI gym game 博客折腾记(二) - 对搜索引擎的理解 | 极客兔兔 博客折腾记(一) - 极致性能的尝试 | 极客兔兔 Pandas 数据处理(三) - Cheat Sheet 中文版 TensorFlow 2.0 (五) - mnist手写数字识别(CNN卷积神经网络) TensorFlow入门(四) - mnist手写数字识别(制作h5py训练集) | 极客兔兔 TensorFlow入门(三) - mnist手写数字识别(可视化训练) | 极客兔兔 Pandas 数据处理(二) - 筛选数据 | 极客兔兔 Pandas 数据处理(一) - DataFrame 与 Series
TensorFlow 2.0 (八) - 强化学习 DQN 玩转 gym Mountain Car
2019-06-28 · via 极客兔兔

源代码/数据集已上传到 Github - tensorflow-tutorial-samples

Geektutu Q-Learning MountainCar Failed

这篇文章是 TensorFlow 2.0 Tutorial 入门教程的第八篇文章。

实现DQN(Deep Q-Learning Network)算法,代码90行

MountainCar 简介

上一篇文章TensorFlow 2.0 (七) - 强化学习 Q-Learning 玩转 OpenAI gym介绍了如何用**Q表(Q-Table)**,来更新策略,使小车顺利达到山顶,整个代码只有50行。我们先回顾一下上一篇文章的要点。

  1. MountainCar-v0 的游戏目标

向左/向右推动小车,小车若到达山顶,则游戏胜利,若200回合后,没有到达山顶,则游戏失败。每走一步得-1分,最低分-200,越早到达山顶,则分数越高。

  1. MountainCar-v0 的几个重要的变量
  • State: [position, velocity],position 范围 [-0.6, 0.6],velocity 范围 [-0.1, 0.1]
  • Action: 0(向左推) 或 1(不动) 或 2(向右推)
  • Reward: -1
  • Done: 小车到达山顶或已花费200回合
  1. Q-Table 的更新方程

Q[s][a] = (1 - lr) * Q[s][a] + lr * (reward + factor * max(Q[next_s]))

神经网络替换 Q-Table

这一篇文章,我们将借助TensorFlow 2.0中的keras库,搭建深度神经网络(Deep Netural Network, DNN),替代Q-Table,即**深度Q网络(Deep Q-Learning Network, DQN)**,实现Q值的计算。

我们将神经网络比作一个函数,神经网络代替Q-Table其实就是在做 函数拟合,也可以称为**值函数近似(Value Function Approximation)**。

维基百科上有一个**万能近似定理(Universal approximation theorem)**,Universal approximation theorem定理表明:前馈神经网络,只需具备单层隐含层和有限个神经单元,就能以任意精度拟合任意复杂度的函数。

这是我们上篇文章定义的Q-Table

1
Q = defaultdict(lambda: [0, 0, 0])
  • 输入(key): 一维向量,(position, velocity)
  • 输出(value):一维向量,(action0_value, action1_value, action2_value)

接下来那我们按照定义的输入输出,简单搭一个神经网络吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38


from collections import deque
import random
import gym
import numpy as np
from tensorflow.keras import models, layers, optimizers

class DQN(object):
def __init__(self):
self.step = 0
self.update_freq = 200
self.replay_size = 2000
self.replay_queue = deque(maxlen=self.replay_size)
self.model = self.create_model()
self.target_model = self.create_model()

def create_model(self):
"""创建一个隐藏层为100的神经网络"""
STATE_DIM, ACTION_DIM = 2, 3
model = models.Sequential([
layers.Dense(100, input_dim=STATE_DIM, activation='relu'),
layers.Dense(ACTION_DIM, activation="linear")
])
model.compile(loss='mean_squared_error',
optimizer=optimizers.Adam(0.001))
return model

def act(self, s, epsilon=0.1):
"""预测动作"""

if np.random.uniform() < epsilon - self.step * 0.0002:
return np.random.choice([0, 1, 2])
return np.argmax(self.model.predict(np.array([s]))[0])

def save_model(self, file_path='MountainCar-v0-dqn.h5'):
print('model saved')
self.model.save(file_path)

网络结构很简单,只有一层隐藏层的全连接网络(Full Connected Network, FC)。但是我们用这个网络结构生成了2个model,一个是预测使用的model,另一个是训练时使用的target_model。看完下面的代码,就容易理解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32


class DQN(object):

def remember(self, s, a, next_s, reward):
"""历史记录,position >= 0.4时给额外的reward,快速收敛"""
if next_s[0] >= 0.4:
reward += 1
self.replay_queue.append((s, a, next_s, reward))

def train(self, batch_size=64, lr=1, factor=0.95):
if len(self.replay_queue) < self.replay_size:
return
self.step += 1

if self.step % self.update_freq == 0:
self.target_model.set_weights(self.model.get_weights())

replay_batch = random.sample(self.replay_queue, batch_size)
s_batch = np.array([replay[0] for replay in replay_batch])
next_s_batch = np.array([replay[2] for replay in replay_batch])

Q = self.model.predict(s_batch)
Q_next = self.target_model.predict(next_s_batch)


for i, replay in enumerate(replay_batch):
_, a, _, reward = replay
Q[i][a] = (1 - lr) * Q[i][a] + lr * (reward + factor * np.amax(Q_next[i]))


self.model.fit(s_batch, Q, verbose=0)

整个结构如下图所示:

Geektutu DQN

我们需要用到上文提到的更新方程,来构造训练数据。其中Q_next是对next_s的预测值,在这里其实也可以使用model,但是model变化得太过频繁,而且我们在训练时,是以batch为单位进行训练的,也就是说很多训练数据对应的是之前状态的model,而不是频繁更新值的model,因此,我们使用更新频率低的target_model来计算next_s的Q值。

同时使用2个Q-Network的算法被称为双Q网络(Double DQN, DDQN)。因为传统的DQN普遍会过高估计Action的Q值,误差会随着Action的增加而增加,可能导致某个次优的值超过了最优Action的Q值,永远无法找到最优解。DDQN能够有效地解决这个问题。DQN 在比较简单的游戏,比如CartPole-v0能够取得较好的效果,但在MountainCar-v0这个游戏中,如果只使用 DQN 很难找到最优解。

target_model每训练update_freq(200)次,更新权重与model一致。

那为什么在Q-Table中,可以用单步的数据来进行更新,但换作了神经网络,就需要以batch为单位来进行训练呢?这个问题在知乎有过讨论,链接在这里:深度学习中的batch的大小对学习效果有何影响?,简单说,如果单步训练,即batch为1,每次朝着单步的梯度方向修正,横冲直撞各自为政,难以收敛。如果batch过大,容易过拟合。而且DQN是增强学习算法,前面的训练数据质量较差,随着训练的进行,产生的动作价值越来越高,增强学习更为看重后面的训练数据,所以batch也不宜过大。

而这一点,也是replay_queue的最大容量设置为2000的原因。队列有先进先出的特性,当后面的数据加进来后,如果数据条数超过2000,前面的数据就会从队列中移除。后面的训练数据对于强化学习更重要。

可改动的 Reward

代码中还有这么一个细节:

1
2
if next_s[0] >= 0.4:
reward += 1

MountainCar-v0这个游戏中,State由2个值构成,(position, velocity)。山顶的位置是0.5,因此当position大于0.4时,给Reward额外加1。这么做,是希望加快神经网络的收敛,更快地达到预期结果。每一步的Reward其实都是可以调整的,怎么做会让训练效果更好,可以动动脑,尝试尝试。

提前终止的 DQN 训练

好,神经网络已经准备就绪,接下来就开始训练吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


env = gym.make('MountainCar-v0')
episodes = 1000
score_list = []
agent = DQN()
for i in range(episodes):
s = env.reset()
score = 0
while True:
a = agent.act(s)
next_s, reward, done, _ = env.step(a)
agent.remember(s, a, next_s, reward)
agent.train()
score += reward
s = next_s
if done:
score_list.append(score)
print('episode:', i, 'score:', score, 'max:', max(score_list))
break

if np.mean(score_list[-10:]) > -160:
agent.save_model()
break
env.close()

如果看过TensorFlow 2.0 (六) - 监督学习玩转 OpenAI gym gameTensorFlow 2.0 (七) - 强化学习 Q-Learning 玩转 OpenAI gym这两篇文章的话,这部分代码就非常简单了。

我们在训练过程中,记录了每一次游戏的分数。并且,如果最近10次的平均分高于**-160**时,结束训练,并保存模型。

运行一下,看看效果吧。

1
2
3
4
5
6
7
8
9
10
11
12
$ python dqn.py
episode: 0 score: -200.0 max: -200.0
episode: 1 score: -200.0 max: -200.0
episode: 2 score: -200.0 max: -200.0
...
episode: 124 score: -200.0 max: -200.0
episode: 125 score: -138.0 max: -138.0
...
episode: 166 score: -144.0 max: -97.0
episode: 167 score: -166.0 max: -97.0
episode: 168 score: -136.0 max: -97.0
model saved

可以看到,在第125次时,首次成功爬到了山顶,在168次的时候,平均分达到预期,停止了训练。

训练效果绘图

接下来,我们添加3行代码,将整个训练过程中的score_list的变化情况画出来,直观感受强化学习的学习过程。

1
2
3
4
import matplotlib.pyplot as plt

plt.plot(score_list, color='green')
plt.show()

geektutu MountainCar v0 scores

模型预测/测试

和之前一样,准备了一个非常简单的可视化的测试代码,直观地感受下最终的游戏效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


import time
import gym
import numpy as np
from tensorflow.keras import models
env = gym.make('MountainCar-v0')
model = models.load_model('MountainCar-v0-dqn.h5')
s = env.reset()
score = 0
while True:
env.render()
time.sleep(0.01)
a = np.argmax(model.predict(np.array([s]))[0])
s, reward, done, _ = env.step(a)
score += reward
if done:
print('score:', score)
break
env.close()

运行一下,还不错~

1
2
$ python test_dqn.py
score: -161.0

Geektutu Q-Learning MountainCar Success

代码已经上传到Github - tensorflow-tutorial-samplesdqn.py只有90行,不妨试一试吧~

附 推荐



上一篇 « TensorFlow 2.0 (七) - 强化学习 Q-Learning 玩转 OpenAI gym 下一篇 » 博客折腾记(四) - 原创资格是争取来的