1.马尔可夫性:
所谓马尔科夫性是指系统的下一个状态st+1 仅与当前状态st 有关,而与以前的状态无关。
定义:状态st 是马尔科夫的,当且仅当P[st+1 |st ]=P[st+1 |s1 ,…,st ]。
定义中可以看到,当前状态st 其实是蕴含了所有相关的历史信息s1 ,…,st ,一旦当前状态已知,历史信息将会被抛弃。马尔科夫性描述的是每个状态的性质,但真正有用的是如何描述一个状态序列。数学中用来描述随机变量序列的学科叫随机过程。所谓随机过程就是指随机变量序列。若随机变量序列中的每个状态都是马尔科夫的,则称此随机过程为马尔科夫随机过程。
2. 马尔可夫过程:
3. 马尔可夫决策过程:
策略的定义是用条件概率分布给出的,可见,强化学习的理论基础,概率论是必不可少的。
1. 随机变量。
随机变量是指可以随机地取不同值的变量,常用小写字母表示。在MDP中随机变量指的是当前的动作,用字母a表示。随机变量可以是离散的也可以是非离散的。
2. 概率分布。
概率分布用来描述随机变量在每个可能取到的值处的可能性大小。离散型随机变量的概率分布常用概率质量函数来描述,即随机变量在离散点处的概率。连续型随机变量的概率分布则用概率密度函数来描述。
3. 条件概率。
策略π(a|s)是条件概率。条件概率是指在其他事件发生时,我们所关心的事件所发生的概率。
4. 期望和方差。
(1)贪婪策略
π ∗ ( a ∣ s ) = { 1 i f a = a r g m a x q ∗ ( s , a ) ( a ∈ A ) 0 o t h e r w i s e \pi*(a|s)=\left\{\begin{matrix} 1 & if&a=argmaxq*(s,a) (a\in A)\\ 0 &otherwise \\ \end{matrix}\right. π∗(a∣s)={10ifotherwisea=argmaxq∗(s,a)(a∈A)
贪婪策略是一个确定性策略,即只有在使得动作值函数q* (s,a)最大的动作处取概率1,选其他动作的概率为0。
(2)ε-greedy策略
π ∗ ( a ∣ s ) ← { 1 − ε + ε ∣ A ( s ) ∣ i f a = a r g m a x a Q ( s , a ) ε ∣ A ( s ) ∣ i f a ≠ a r g m a x a Q ( s , a ) \pi *(a|s)\leftarrow \left\{\begin{matrix} 1-\varepsilon +\frac{\varepsilon }{\left| A(s)\right|} & if & a= argmax_{a}Q(s,a) \\ \frac{\varepsilon }{\left| A(s)\right|} & if & a\neq argmax_{a}Q(s,a) \\ \end{matrix}\right. π∗(a∣s)←{1−ε+∣A(s)∣ε∣A(s)∣εififa=argmaxaQ(s,a)a=argmaxaQ(s,a)
ε-greedy策略是强化学习最基本最常用随机策略。其含义是选取使得动作值函数最大的动作的概率为 1 − ε + ε ∣ A ( s ) ∣ 1-\varepsilon +\frac{\varepsilon }{\left| A(s)\right|} 1−ε+∣A(s)∣ε ,而其他动作的概率为等概率,都为 ε ∣ A ( s ) ∣ \frac{\varepsilon }{\left| A(s)\right|} ∣A(s)∣ε 。ε-greedy平衡了利用(exploitation)和探索(exploration),其中选取动作值函数最大的部分为利用,其他非最优动作仍有概率为探索部分。
(3)高斯策略:
π θ = μ θ + ε , ε ∼ N ( 0 , σ 2 ) \pi _{\theta} = \mu _{\theta } + \varepsilon,\varepsilon\sim N(0,\sigma ^{2}) πθ=μθ+ε,ε∼N(0,σ2)
其中 μ θ \mu _{\theta } μθ为确定性部分, ε为零均值的高斯随机噪声。高斯策略也平衡了利用和探索,其中利用由确定性部分完成,探索由ε完成。高斯策略在连续系统的强化学习中应用广泛。
(4)玻尔兹曼分布
π ( a ∣ s , θ ) = e x p ( Q ( s , a , θ ) ) Σ b e x p ( h ( s , b , θ ) ) \pi \left ( a|s,\theta \right )= \frac{exp(Q(s,a,\theta ))}{\Sigma _{b}exp(h(s,b,\theta ))} π(a∣s,θ)=Σbexp(h(s,b,θ))exp(Q(s,a,θ))
其中Q(s,a,θ)为动作值函数。该策略的含义是,动作值函数大的动作被选中的概率大,动作值函数小的动作被选中的概率小。
本节我们以机器人找金币为例子,构建其MDP框架。
如图3所示为机器人找金币的例子。该网格世界一共8个状态,其中状态6和状态8是死亡区域,状态7是金币区域。机器人的初始位置为网格世界中的任意一个状态,机器人从初始状态出发寻找金币,机器人每探索一次,进入死亡区域或找到金币,本次探索结束。
状态空间:S = {1,2,3,4,5,6,7,8} ;动作空间:A = {东,南,西,北} ;状态转移概率为机器人的运动方程,回报函数为:找到金币的回报为 1,进入死亡区域的回报为-1,机器人在状态1~5之间转换时,回报为0。
下面,我们基于gym构建机器人找金币的gym环境。
# -*- coding: utf-8 -*-
"""
@author: cainiao08ge
@project: py_demo
@file: grid_mdp.py
@description:
"""
import logging
import numpy
import random
from gym import spaces
import gym
logger = logging.getLogger(__name__)
class GridEnv(gym.Env):
metadata = {
'render.modes': ['human', 'rgb_array'],
'video.frames_per_second': 2
}
def __init__(self):
self.states = [1,2,3,4,5,6,7,8] #状态空间
self.x=[140,220,300,380,460,140,300,460]
self.y=[250,250,250,250,250,150,150,150]
self.terminate_states = dict() #终止状态为字典格式
self.terminate_states[6] = 1
self.terminate_states[7] = 1
self.terminate_states[8] = 1
self.actions = ['n','e','s','w']
self.rewards = dict(); #回报的数据结构为字典
self.rewards['1_s'] = -1.0
self.rewards['3_s'] = 1.0
self.rewards['5_s'] = -1.0
self.t = dict(); #状态转移的数据格式为字典
self.t['1_s'] = 6
self.t['1_e'] = 2
self.t['2_w'] = 1
self.t['2_e'] = 3
self.t['3_s'] = 7
self.t['3_w'] = 2
self.t['3_e'] = 4
self.t['4_w'] = 3
self.t['4_e'] = 5
self.t['5_s'] = 8
self.t['5_w'] = 4
self.gamma = 0.8 #折扣因子
self.viewer = None
self.state = None
def getTerminal(self):
return self.terminate_states
def getGamma(self):
return self.gamma
def getStates(self):
return self.states
def getAction(self):
return self.actions
def getTerminate_states(self):
return self.terminate_states
def setAction(self,s):
self.state=s
def _step(self, action):
#系统当前状态
state = self.state
if state in self.terminate_states:
return state, 0, True, {}
key = "%d_%s"%(state, action) #将状态和动作组成字典的键值
#状态转移
if key in self.t:
next_state = self.t[key]
else:
next_state = state
self.state = next_state
is_terminal = False
if next_state in self.terminate_states:
is_terminal = True
if key not in self.rewards:
r = 0.0
else:
r = self.rewards[key]
return next_state, r,is_terminal,{}
def _reset(self):
self.state = self.states[int(random.random() * len(self.states))]
return self.state
def render(self, mode='human', close=False):
if close:
if self.viewer is not None:
self.viewer.close()
self.viewer = None
return
screen_width = 600
screen_height = 400
if self.viewer is None:
from gym.envs.classic_control import rendering
self.viewer = rendering.Viewer(screen_width, screen_height)
#创建网格世界
self.line1 = rendering.Line((100,300),(500,300))
self.line2 = rendering.Line((100, 200), (500, 200))
self.line3 = rendering.Line((100, 300), (100, 100))
self.line4 = rendering.Line((180, 300), (180, 100))
self.line5 = rendering.Line((260, 300), (260, 100))
self.line6 = rendering.Line((340, 300), (340, 100))
self.line7 = rendering.Line((420, 300), (420, 100))
self.line8 = rendering.Line((500, 300), (500, 100))
self.line9 = rendering.Line((100, 100), (180, 100))
self.line10 = rendering.Line((260, 100), (340, 100))
self.line11 = rendering.Line((420, 100), (500, 100))
#创建第一个骷髅
self.kulo1 = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(140,150))
self.kulo1.add_attr(self.circletrans)
self.kulo1.set_color(0,0,0)
#创建第二个骷髅
self.kulo2 = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(460, 150))
self.kulo2.add_attr(self.circletrans)
self.kulo2.set_color(0, 0, 0)
#创建金条
self.gold = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(300, 150))
self.gold.add_attr(self.circletrans)
self.gold.set_color(1, 0.9, 0)
#创建机器人
self.robot= rendering.make_circle(30)
self.robotrans = rendering.Transform()
self.robot.add_attr(self.robotrans)
self.robot.set_color(0.8, 0.6, 0.4)
self.line1.set_color(0, 0, 0)
self.line2.set_color(0, 0, 0)
self.line3.set_color(0, 0, 0)
self.line4.set_color(0, 0, 0)
self.line5.set_color(0, 0, 0)
self.line6.set_color(0, 0, 0)
self.line7.set_color(0, 0, 0)
self.line8.set_color(0, 0, 0)
self.line9.set_color(0, 0, 0)
self.line10.set_color(0, 0, 0)
self.line11.set_color(0, 0, 0)
self.viewer.add_geom(self.line1)
self.viewer.add_geom(self.line2)
self.viewer.add_geom(self.line3)
self.viewer.add_geom(self.line4)
self.viewer.add_geom(self.line5)
self.viewer.add_geom(self.line6)
self.viewer.add_geom(self.line7)
self.viewer.add_geom(self.line8)
self.viewer.add_geom(self.line9)
self.viewer.add_geom(self.line10)
self.viewer.add_geom(self.line11)
self.viewer.add_geom(self.kulo1)
self.viewer.add_geom(self.kulo2)
self.viewer.add_geom(self.gold)
self.viewer.add_geom(self.robot)
if self.state is None: return None
#self.robotrans.set_translation(self.x[self.state-1],self.y[self.state-1])
self.robotrans.set_translation(self.x[self.state-1], self.y[self.state- 1])
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
这段代码在书中作者的github中有链接: https://github.com/gxnk/reinforcement-learning-code,可以直接下载,本身代码是没有问题的,但在win和linux中运行效果各不相同(书中是在linux中运行),在每个人的电脑环境中更是存在差异,我在两个系统(Ubuntu和win10)中都有测试并运行,期间碰到了各种问题,可能仅仅是我得问题,在此不一一罗列(主要是bug当时没截图,解决了就不想在测试了),问题均已解决,很多问题可能只是我的问题,欢迎读者讨论交流。