目录
前言
一、强化学习
1.马可洛夫链
2.蒙地卡罗
3.时序差分TD
4.gym学习编辑
FrozenLake
二、RL基本算法
1.Q-learning和SARSA
2.DQN
Deep network + Qlearning = DQN
三、PG策略算法
总结
前言
这段时间学习深度强化学习的总结。
一、强化学习
强化学习是做出最佳决策的科学。它可以帮助我们制定活的物种所表现出的奖励动机行为。比方说,你想让一个孩子坐下来学习考试。要做到这一点非常困难,但是如果每次完成一章/主题时都给他一块巧克力,他就会明白,如果他继续学习,他会得到更多的巧克力棒。所以他会有一些学习考试的动机。
孩子代表着Agent代理 。奖励制度和考试代表了Environment环境。今天的题目是类似于强化学习的States状态。所以,孩子必须决定哪些话题更重要(即计算每种行为的价值)。这将是我们的工作的 Value-Function价值方程。所以,每次他从一个国家到另一个国家旅行时,他都会得到Reward奖励,他用来在时间内完成主题的方法就是我们的Policy决策。
1.马可洛夫链
马尔科夫链是用来描述智能体和环境互动的过程。
马尔科夫链包含三要素:state,action,reward
智能体在一个状态(S)下,选择了某个动作(A),进入了另外一个状态,并获得奖励(用R代表)的过程。
所以,我们希望通过让智能体在环境里获取最多的奖励,把智能体训练成我们想要的样子——就是能完成某些特定的任务。
马尔科夫链,其实应该叫马尔科夫树
S到A是agent的选择 ,而A到S时环境的随机性。
马尔科夫链具有不确定性。
2.蒙地卡罗
Q和V:
评估动作的价值,称为Q值:它代表了选择这个动作后,一直到最终状态奖励总和的期望;
评估状态的价值,称为V值:它代表了在这个状态下,一直到最终状态的奖励总和的期望。
算上奖励R,Q和V之间的关系:
V就是子节点的Q的期望,Q就是子节点的V的期望.
蒙地卡罗:
我们把agent放到环境的任意状态;从这个状态开始按照策略进行选择动作,并进入新的状态。
重复上一步,直到最终状态;我们从最终状态开始向前回溯:计算每个状态的G值。
重复前面多次,然后平均每个状态的G值,这就是我们需要求的V值。
缺点: 每一次都需要先从头走到尾,再进行回溯更新。如果最终状态很难达到,可能每一次都要转很久很久才能更新一次G值。就是说如果环境的状态空间非常大,或者最终状态只有非常小的概率达到。
3.时序差分TD
TD算法对蒙地卡罗(MC)进行了改进:
1. 和蒙地卡罗(MC)不同:TD算法只需要走N步。就可以开始回溯更新。
2. 和蒙地卡罗(MC)一样:小猴需要先走N步,每经过一个状态,把奖励记录下来。然后开始回溯。
3. 状态的V值计算:和蒙地卡罗一样,假设N步之后,就到达了最终状态了。
可以简单来说就是,TD不走完整段路程,而是半路就截断。用半路的路牌,更新当前的路牌。就是说不需要一直到最后,我们可以先用后面的估算,然后调整当前状态。
在MC,G是更新目标,而在TD,把更新目标从G,改成r+gamma*V
4.gym学习
- 导入:首先我们先import gym。导入gym包。
- 创建环境:env = gym.make('环境名')。 其中环境名可以在这找到,还有相关的说明。其中,env就是environment的简称。 一般我们会顺手进行初始化。env.unwrapped 或者我们一起写。env = gym.make('环境名').unwrapped
- 获取状态特征数和动作空间 N_S = env.observation_space N_A = env.action_space 还记得我们之前说的吗?状态特征,就是我们能观察到的环境。 例如自动驾驶汽车,交通灯算是一个状态特征,这个特征有不同的值,红灯/绿灯。距离最近的行人是一个特征,例如距离这个行人多远。自动驾驶汽车能观察的环境特征越多,感知能力越强,对抉择就越有帮助。所以不一般来说,工程师都会想不断利用各种算法和硬件,从真实环境提取环境特征。 GYM环境对于初学者方便的地方是,对于每个实验,环境都直接提供了环境特征。不需要我们自己去提取了。 如果是连续的状态空间,和动作空间,我们可以获取到边界值 s_low_bound = env.observation_space.low s_high_bound = env.observation_space.high 在这里大家可以理解为,老师出题的格式,和学生答案的格式。
- 初始化环境,并返回一个初始状态。我们一般这样写。 s = env.reset() 老师出的第一道题!
- 学生开始做题了。这里的f其实就是我们的算法。 现在我们就有了一个初始状态s。 假设有一个函数f(s),输入s,获得可以选择出动作a。我们的目标就是找出一个f,能让我们在这个环境中获取最多的reward。 a = f(s)
- 学生提交答案,老师改卷了。把分返回给学生。并且开始出下一题。 我们选取a后。代入step函数,会根据 环境配置,会返回:进入的下一个状态,当前获得的奖励r。除此之外,还有一个标志位done,表示是否进入最终状态。另外还有一个包含信息的值(info),但一般我们用不到。可以先忽略。 s_,r,done,info = env.step(a)
- 我们把s = s_.并把4-6组成一个循环,那么游戏就能自动进行下去了。
- render。如果有render可以把游戏给渲染出来。
FrozenLake
S:start,出发点。 G:target,终点。 F:可以走的路,但偶尔你会因为滑到,自动往前滑一格。 H:hole:是陷阱。
FrozenLake-v0是一个4*4的网络格子,每个格子可以是起始块,目标块、冻结块或者危险块。 我们的目标是让agent学习从开始块如何行动到目标块上,而不是移动到危险块上。 agent可以选择向上、向下、向左或者向右移动,同时游戏中还有可能吹来一阵风,将agent吹到任意的方块上。
我们的任务就是让我们的agent,从起点,走到终点。
二、RL基本算法
1.Q-learning和SARSA
V(St+1)的意义是,在St+1到最终状态获得的奖励期望值。 Q(St,At)的意义是,在Q(St,At)到最终状态获得的奖励期望值。 所以我们可以把V(St+1)看成是下山途中的一个路牌,这个路牌告诉我们下山到底还有多远,然后加上R这一段路,就知道Q(St,At)离山脚有多长的路。
因为从状态St+1到动作At+1之间没有奖励反馈,所以我们直接用At+1的Q价值,代替St+1的V价值。
在St+1下,可能有很多动作At+1。不同动作的Q值自然是不同的。 所以Q(St+1,At+1)并不能等价于V(St+1)。
虽然不相等,但不代表不能用其中一个来代表V(St+1)。人们认为有个可能的动作产生的Q值能够一定程度代表V(St+1)。
- 在相同策略下产生的动作At+1。这就是SARSA。
- 选择能够产生最大Q值的动作At+1。这就是Qlearning。
Qlearning和SARSA唯一的不同,就是用什么动作的Q值替代St+1的V值。SARSA 选择的是在St同一个策略产生的动作。Qlearning 选择的是能够产生最大的Q值的动作。
Qleanring的更新流程。 我们将会在任意的state出发
1. 我们将会用noisy-greedy的策略选定动作A
2. 在完成动作后,我们将会进入新状态St+1;
3. 检查St+1中所有动作,看看哪个动作的Q值最大;
4. 用以下的公式更新当前动作A的Q值;
5. 继续从s'出发,进行下一步更新 1-6步我们作为一个EP,进行N个EP的迭代。
"""Q-Table learning algorithm.
Non deep learning - TD Learning, Off-Policy, e-Greedy Exploration
Q(S, A) <- Q(S, A) + alpha * (R + lambda * Q(newS, newA) - Q(S, A))
See David Silver RL Tutorial Lecture 5 - Q-Learning for more details.
For Q-Network, see tutorial_frozenlake_q_network.py
EN: https://medium.com/emergent-future/simple-reinforcement-learning-with-tensorflow-part-0-q-learning-with-tables-and-neural-networks-d195264329d0#.5m3361vlw
CN: https://zhuanlan.zhihu.com/p/25710327
tensorflow==2.0.0a0
tensorlayer==2.0.0
"""
import argparse
import os
import time
import gym
import matplotlib.pyplot as plt
import numpy as np
parser = argparse.ArgumentParser()
parser.add_argument('--train', dest='train', action='store_true', default=True)
parser.add_argument('--test', dest='test', action='store_true', default=True)
parser.add_argument(
'--save_path', default=None, help='folder to save if mode == train else model path,'
'qnet will be saved once target net update'
)
parser.add_argument('--seed', help='random seed', type=int, default=0)
parser.add_argument('--env_id', default='FrozenLake-v0')
args = parser.parse_args()
## Load the environment
alg_name = 'Qlearning'
env_id = args.env_id
env = gym.make(env_id)
render = False # display the game environment
##================= Implement Q-Table learning algorithm =====================##
## Initialize table with all zeros
Q = np.zeros([env.observation_space.n, env.action_space.n])
## Set learning parameters
lr = .85 # alpha, if use value function approximation, we can ignore it
lambd = .99 # decay factor
num_episodes = 10000
t0 = time.time()
if args.train:
all_episode_reward = []
for i in range(num_episodes):
## Reset environment and get first new observation
s = env.reset()
rAll = 0
## The Q-Table learning algorithm
for j in range(99):
if render: env.render()
## Choose an action by greedily (with noise) picking from Q table
a = np.argmax(Q[s, :] + np.random.randn(1, env.action_space.n) * (1. / (i + 1)))
## Get new state and reward from environment
s1, r, d, _ = env.step(a)
## Update Q-Table with new knowledge
Q[s, a] = Q[s, a] + lr * (r + lambd * np.max(Q[s1, :]) - Q[s, a])
rAll += r
s = s1
if d is True:
break
print(
'Training | Episode: {}/{} | Episode Reward: {:.4f} | Running Time: {:.4f}'.format(
i + 1, num_episodes, rAll,
time.time() - t0
)
)
if i == 0:
all_episode_reward.append(rAll)
else:
all_episode_reward.append(all_episode_reward[-1] * 0.9 + rAll * 0.1)
# save
path = os.path.join('model', '_'.join([alg_name, env_id]))
if not os.path.exists(path):
os.makedirs(path)
np.save(os.path.join(path, 'Q_table.npy'), Q)
plt.plot(all_episode_reward)
if not os.path.exists('image'):
os.makedirs('image')
plt.savefig(os.path.join('image', '_'.join([alg_name, env_id])))
# print("Final Q-Table Values:/n %s" % Q)
if args.test:
path = os.path.join('model', '_'.join([alg_name, env_id]))
Q = np.load(os.path.join(path, 'Q_table.npy'))
for i in range(num_episodes):
## Reset environment and get first new observation
s = env.reset()
rAll = 0
## The Q-Table learning algorithm
for j in range(99):
## Choose an action by greedily (with noise) picking from Q table
a = np.argmax(Q[s, :])
## Get new state and reward from environment
s1, r, d, _ = env.step(a)
## Update Q-Table with new knowledge
rAll += r
s = s1
if d is True:
break
print(
'Testing | Episode: {}/{} | Episode Reward: {:.4f} | Running Time: {:.4f}'.format(
i + 1, num_episodes, rAll,
time.time() - t0
)
)
2.DQN
Deep network + Qlearning = DQN
在Qlearning中,有一个Qtable,记录着在每一个状态下,各个动作的Q值。
Qtable的作用是当输入状态S,通过查表返回能够获得最大Q值的动作A。也就是需要找一个S-A的对应关系。
这种方式很适合格子游戏。因为格子游戏中的每一个格子就是一个状态,但在现实生活中,很多状态并不是离散而是连续的。
例如在GYM中经典的CartPole游戏,杆子的角度是连续而不是离散的。在Atari游戏中,状态也是连续的。
遇到这些情况,Qtable就没有办法解决。
Qtable的作用就是找一个S-A的对应关系。所以我们就可以用一个函数F表示,我们有F(S) = A。这样我们就可以不用查表了,而且还有个好处,函数允许连续状态的表示。
这时候,我们深度神经网络就可以派上用场了。因为神经网络可以看成一个万能的函数。
这个万能函数接受输入一个状态S,它能告诉我,每个动作的Q值是怎样的。
Qlearning和DQN并没有根本的区别。只是DQN用神经网络,也就是一个函数替代了原来Qtable而已。解决连续问题。
更新当前状态St下的某动作A的Q值:Q(S,A),我们可以这样做:
1. 执行A,往前一步,到达St+1;
2. 把St+1输入Q网络,计算St+1下所有动作的Q值;
3. 获得最大的Q值加上奖励R作为更新目标;
4. 计算损失 - Q(S,A)相当于有监督学习中的logits - maxQ(St+1) + R 相当于有监督学习中的lables - 用mse函数,得出两者的loss
5. 用loss更新Q网络。
用Q网络估算出来的两个相邻状态的Q值,他们之间的距离,就是一个r的距离。
和有监督学习不同,深度强化学习中,我们需要自己找更新目标。通常在马尔科夫链体系下,两个相邻状态状态差一个奖励r,经常能被利用。
"""
Deep Q-Network Q(a, s)
-----------------------
TD Learning, Off-Policy, e-Greedy Exploration (GLIE).
Q(S, A) <- Q(S, A) + alpha * (R + lambda * Q(newS, newA) - Q(S, A))
delta_w = R + lambda * Q(newS, newA)
See David Silver RL Tutorial Lecture 5 - Q-Learning for more details.
Reference
----------
original paper: https://storage.googleapis.com/deepmind-media/dqn/DQNNaturePaper.pdf
EN: https://medium.com/emergent-future/simple-reinforcement-learning-with-tensorflow-part-0-q-learning-with-tables-and-neural-networks-d195264329d0#.5m3361vlw
CN: https://zhuanlan.zhihu.com/p/25710327
Note: Policy Network has been proved to be better than Q-Learning, see tutorial_atari_pong.py
Environment
-----------
# The FrozenLake v0 environment
https://gym.openai.com/envs/FrozenLake-v0
The agent controls the movement of a character in a grid world. Some tiles of
the grid are walkable, and others lead to the agent falling into the water.
Additionally, the movement direction of the agent is uncertain and only partially
depends on the chosen direction. The agent is rewarded for finding a walkable
path to a goal tile.
SFFF (S: starting point, safe)
FHFH (F: frozen surface, safe)
FFFH (H: hole, fall to your doom)
HFFG (G: goal, where the frisbee is located)
The episode ends when you reach the goal or fall in a hole. You receive a reward
of 1 if you reach the goal, and zero otherwise.
Prerequisites
--------------
tensorflow>=2.0.0a0
tensorlayer>=2.0.0
To run
-------
python tutorial_DQN.py --train/test
"""
import argparse
import os
import time
import gym
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import tensorlayer as tl
# add arguments in command --train/test
parser = argparse.ArgumentParser(description='Train or test neural net motor controller.')
parser.add_argument('--train', dest='train', action='store_true', default=True)
parser.add_argument('--test', dest='test', action='store_true', default=True)
args = parser.parse_args()
tl.logging.set_verbosity(tl.logging.DEBUG)
##################### hyper parameters ####################
env_id = 'FrozenLake-v0'
alg_name = 'DQN'
lambd = .99 # decay factor
e = 0.1 # e-Greedy Exploration, the larger the more random
num_episodes = 10000
render = False # display the game environment
##################### DQN ##########################
def to_one_hot(i, n_classes=None):
a = np.zeros(n_classes, 'uint8')
a[i] = 1
return a
## Define Q-network q(a,s) that ouput the rewards of 4 actions by given state, i.e. Action-Value Function.
# encoding for state: 4x4 grid can be represented by one-hot vector with 16 integers.
def get_model(inputs_shape):
ni = tl.layers.Input(inputs_shape, name='observation')
nn = tl.layers.Dense(4, act=None, W_init=tf.random_uniform_initializer(0, 0.01), b_init=None, name='q_a_s')(ni)
return tl.models.Model(inputs=ni, outputs=nn, name="Q-Network")
def save_ckpt(model): # save trained weights
path = os.path.join('model', '_'.join([alg_name, env_id]))
if not os.path.exists(path):
os.makedirs(path)
tl.files.save_weights_to_hdf5(os.path.join(path, 'dqn_model.hdf5'), model)
def load_ckpt(model): # load trained weights
path = os.path.join('model', '_'.join([alg_name, env_id]))
tl.files.save_weights_to_hdf5(os.path.join(path, 'dqn_model.hdf5'), model)
if __name__ == '__main__':
qnetwork = get_model([None, 16])
qnetwork.train()
train_weights = qnetwork.trainable_weights
optimizer = tf.optimizers.SGD(learning_rate=0.1)
env = gym.make(env_id)
t0 = time.time()
if args.train:
all_episode_reward = []
for i in range(num_episodes):
## Reset environment and get first new observation
s = env.reset() # observation is state, integer 0 ~ 15
rAll = 0
if render: env.render()
for j in range(99): # step index, maximum step is 99
## Choose an action by greedily (with e chance of random action) from the Q-network
allQ = qnetwork(np.asarray([to_one_hot(s, 16)], dtype=np.float32)).numpy()
a = np.argmax(allQ, 1)
## e-Greedy Exploration !!! sample random action
if np.random.rand(1) < e:
a[0] = env.action_space.sample()
## Get new state and reward from environment
s1, r, d, _ = env.step(a[0])
if render: env.render()
## Obtain the Q' values by feeding the new state through our network
Q1 = qnetwork(np.asarray([to_one_hot(s1, 16)], dtype=np.float32)).numpy()
## Obtain maxQ' and set our target value for chosen action.
maxQ1 = np.max(Q1) # in Q-Learning, policy is greedy, so we use "max" to select the next action.
targetQ = allQ
targetQ[0, a[0]] = r + lambd * maxQ1
## Train network using target and predicted Q values
# it is not real target Q value, it is just an estimation,
# but check the Q-Learning update formula:
# Q'(s,a) <- Q(s,a) + alpha(r + lambd * maxQ(s',a') - Q(s, a))
# minimizing |r + lambd * maxQ(s',a') - Q(s, a)|^2 equals to force Q'(s,a) ≈ Q(s,a)
with tf.GradientTape() as tape:
_qvalues = qnetwork(np.asarray([to_one_hot(s, 16)], dtype=np.float32))
_loss = tl.cost.mean_squared_error(targetQ, _qvalues, is_mean=False)
grad = tape.gradient(_loss, train_weights)
optimizer.apply_gradients(zip(grad, train_weights))
rAll += r
s = s1
## Reduce chance of random action if an episode is done.
if d ==True:
e = 1. / ((i / 50) + 10) # reduce e, GLIE: Greey in the limit with infinite Exploration
break
## Note that, the rewards here with random action
print('Training | Episode: {}/{} | Episode Reward: {:.4f} | Running Time: {:.4f}' \
.format(i, num_episodes, rAll, time.time() - t0))
if i == 0:
all_episode_reward.append(rAll)
else:
all_episode_reward.append(all_episode_reward[-1] * 0.9 + rAll * 0.1)
save_ckpt(qnetwork) # save model
plt.plot(all_episode_reward)
if not os.path.exists('image'):
os.makedirs('image')
plt.savefig(os.path.join('image', '_'.join([alg_name, env_id])))
if args.test:
load_ckpt(qnetwork) # load model
for i in range(num_episodes):
## Reset environment and get first new observation
s = env.reset() # observation is state, integer 0 ~ 15
rAll = 0
if render: env.render()
for j in range(99): # step index, maximum step is 99
## Choose an action by greedily (with e chance of random action) from the Q-network
allQ = qnetwork(np.asarray([to_one_hot(s, 16)], dtype=np.float32)).numpy()
a = np.argmax(allQ, 1) # no epsilon, only greedy for testing
## Get new state and reward from environment
s1, r, d, _ = env.step(a[0])
rAll += r
s = s1
if render: env.render()
## Reduce chance of random action if an episode is done.
if d: break
print('Testing | Episode: {}/{} | Episode Reward: {:.4f} | Running Time: {:.4f}' \
.format(i, num_episodes, rAll, time.time() - t0))
三、PG策略算法
总结
对深度强化学习的初步学习,从强化学习到深度强化学习,学习了一下算法,之后还需要深入实验,如GYM等。加油加油。
好的参考:
强化学习极简入门:通俗理解MDP、DP MC TC和Q学习、策略梯度、PPO_强化学习极简入门:通俗理解mdp、dpmc-CSDN博客
【强化学习】Q-Learning算法详解-CSDN博客
强化学习方法汇总 | 莫烦Python (mofanpy.com)
深度强化学习【1】-强化学习入门必备基础(含Python迷宫游戏求解实例)_强化学习 迷宫-CSDN博客