快乐的强化学习1——Q_Learning及其实现方法
学习前言
刚刚从大学毕业,近来闲来无事,开始了机器学习的旅程,深度学习是机器学习的重要一环,其可以使得机器自我尝试,并通过结果进行学习。
在机器学习的过程中,我自网上了解到大神morvanzhou,一个从土木工程转向了计算机的“聪明绝顶”的、英语特好的男人。本篇文章便是按照他的深度学习教程中的Qlearning部分撰写的。
morvanzhou的python个人主页,请有兴趣的同学关注大神morvanzhou的python教程。
简介
Q-Learning是一种value-based算法,即通过判断每一步进行的价值value来进行下一步的动作,以人物的左右移动为例子,Q-Learning的核心Q-Table可以按照如下表示:
环境 | 'Left‘ | ‘Right’ |
---|---|---|
1 | 0 | 0.125 |
2 | 0.001 | 0.132 |
3 | 0.013 | 0.231 |
当人物处于环境1的时候,此时进行’Right‘步骤的得分为0.125,进行’Left‘步骤的得分为0,此时对于Q-Learning算法而言,其会优先选择进行’Right‘步骤,此时人物往右方向走一步,抵达同时新环境,在新环境下,Q-Learning算法会进行新的判断。在实际运用中,如果人物处于环境1,我们在进行算法初始化的时候给给予其一个e_greedy值,处于0-1之间,在e_greedy值的几率内,其会选择分值大的’Right‘步骤,如果不在则随机选择。这种设定可以让算法更多的进行尝试。
Q-Learning算法的实现
接下来我将以小男孩取得玩具为例子,讲述Q-Learning算法的执行过程。
在一开始的时候假设小男孩不知道玩具在哪里,他的Q_Table一片空白,此时他开始观测自己所处的环境,这个环境是环境1,并将这个环境加入到Q_Table中。此时,他不知道左右两个环境的情况,所以向左走向右走的得分都是0,这两个得分都是小男孩心中预测出来的,并不是真实的左右两个环境的得分,我们给这个得分一个名字叫做q_predict。
环境 | 'Left‘ | ‘Right’ |
---|---|---|
1 | 0 | 0 |
他往左走往右走都是有可能的,如果他往左走,他会到达环境0,环境0是一个深渊,然后小男孩就挂了。此时环境会给他一个反馈得分,由于他掉入了深渊,所以他的得分是-1,这个得分是环境0的实际得分,与上面小男孩处于环境1对环境0的预测得分不同,我们给这个得分一个名字叫做q_target,由于这个得分与上一步预测的环境0的得分不同,所以我们需要对上一个表格进行更新,这个得分会乘上学习率后在环境1的’Left’ Action得到体现。此时他的Q_Table表为。
环境 | 'Left‘ | ‘Right’ |
---|---|---|
1 | -0.01 | 0 |
此时小男孩重生了,在这一轮他会选择往右走,此时他又到达了一个新环境,此时他开始观测自己所处的环境,这个环境是环境2,并将这个环境加入到Q_Table中。此时,他不知道这个新环境的情况,向左走和向右走对他来说是一样的,所以这个新环境的左右两个Actions得分都是0。
环境 | 'Left‘ | ‘Right’ |
---|---|---|
1 | -0.01 | 0 |
2 | 0 | 0 |
此时他会随机选择一个方向走,之后不断重复探测新环境和走回旧环境的过程,如果他在不断测试中找到了最后的toys,他会得到正数得分,这个得分会一步一步驱使他走向toys。在经过许多次的尝试之后,小男孩会找到一条最快通向玩具的道路。假设环境4就是玩具,小男孩在经过多次尝试后,最后的Q_Table可能是这样。
环境 | 'Left‘ | ‘Right’ |
---|---|---|
1 | -0.020 | 0.033 |
2 | 0.001 | 0.154 |
3 | 0.000 | 0.547 |
对于Q-Learning算法的主体而言,Q-Learning算法主要由两个对象组成,分别是Q-Learning的大脑和大环境。
Q-Learning的大脑包括以下部分,其对应的功能为
模块名称 | 作用/功能 |
---|---|
初始化 | 初始化学习率、可执行动作、Q_table等参数 |
动作选择 | 根据小男孩当前所处的环境和Q_table进行动作选择 |
学习 | 根据小男孩当前所处的环境对其它环境的预测情况q_predict和下一步环境的实际情况q_target更新Q_table表 |
确认是否存在该环境 | 由于在学习之前环境是未知的,当进入一个新环境时,需要生成一个得分都为0的动作表格 |
大环境包括以下部分,其对应的功能为
模块名称 | 作用/功能 |
---|---|
初始化 | 初始化环境参数、用于构建环境 |
图画更新 | 用于更新当前的图画,便于用户观察 |
环境观察 | 用于返回当前环境情况 |
终点观察 | 用于返回是否到达终点 |
更新坐标 | 用于更新当前所处位置 |
下一环境获取 | 用于获取下一步的环境的实际情况 |
参数归零 | 用于每一个世代坐标和当前行走步数的归零 |
在完成两个对象的构建后,需要有一个主函数将两个对象联系起来使用,主函数需要完成以下功能,我将以伪代码的形式呈现:
初始化测试环境对象
初始化Q-Learning的大脑对象
for episode in range(TIMES):
环境观察
图画更新
while(1):
终点观察
if(到达终点):
打印当前进行世代数,共使用多少步数
参数归零
break;
动作选择
获取下一步的环境的实际情况
学习
更新坐标
图画更新
在观察完Q_Learning算法的伪代码形式我们可以发现,其大脑每次学习的过程都是在获取下一步的环境的实际情况之后的,学习函数对Q-Table表更新的重要参数之一就是获取下一步的环境的实际情况。具体来说,在进行学习过程时,Q-Learning的大脑对象会根据所处的当前环境对各个动作的预测得分和下一步的环境的实际情况(最大得分)对当前环境的Q-Table表进行更新,具体的代码如下所示:
def learn(self,observation_now,action,score,observation_after,done):
self.check_observation(observation_after) #检查是否存在下一步的环境对应的方向状态
q_predict = self.q_table.loc[observation_now,action] #所处的当前环境对下一个动作的预测得分
if done:
q_influence = score #如果完成了则下一个环境的实际情况得分为1
else:
q_influence = score + self.gamma * self.q_table.loc[observation_after, :].max() #如果未完成则取下一个环境若干个动作中的最大得分作为这个环境的价值传递给当前环境
self.q_table.loc[observation_now, action] += self.lr * (q_influence - q_predict) #更新当前观测状态对应的Q值
具体实现代码
具体的实现代码分为三个部分,这是第一部分,Q-Learning的大脑:
import numpy as np
import pandas as pd
class QL:
def __init__(self, actions, learning_rate=0.05, reward_decay=0.9, e_greedy=0.9):
self.actions = actions #初始化可以进行的各种行为,传入为列表
self.lr = learning_rate #学习率,用于更新Q_table的值
self.gamma = reward_decay #当没有到达终点时,下一环境对当前环境的影响
self.epsilon = e_greedy #随机选择几率为1-e_greedy,当处于e_greedy内时,不随机选择。
self.q_table = pd.DataFrame(columns=self.actions, dtype=np.float64) #生成q_table,列向量为columns
def choose_action(self,observation):
self.check_observation(observation) #检测是否到达过这个点,如果没到达过,在Q表中增加这个节点
action_list = self.q_table.loc[observation,:] #取出当前observation所在的不同方向
if(np.random.uniform() < self.epsilon): #如果在epsilon几率内
action = np.random.choice(action_list[action_list == np.max(action_list)].index) #选出当前observation中Q值最大的方向
else:
action = np.random.choice(self.actions) #如果不在epsilon内,则随机选择一个动作
return action #返回应当做的action
def learn(self,observation_now,action,score,observation_after,done):
self.check_observation(observation_after) #检查是否存在下一环境对应的方向状态
q_predict = self.q_table.loc[observation_now,action] #获得当前状态下,当前所作动作所对应的预测得分
if done:
q_target = score #如果完成了则q_target为下一个环境的实际情况得分,本例子中此时score为1
else:
q_target = score + self.gamma * self.q_table.loc[observation_after, :].max() #如果未完成则取下一个环境若干个动作中的最大得分作为这个环境的价值传递给当前环境
#根据所处的当前环境对各个动作的预测得分和下一步的环境的实际情况更新当前环境的q表
self.q_table.loc[observation_now, action] += self.lr * (q_target - q_predict)
def check_observation(self,observation):
if observation not in self.q_table.index: #如果不存在
self.q_table = self.q_table.append( #则通过series函数生成新的一列
pd.Series(
[0]*len(self.actions),
index=self.actions,
name=observation,)
)
第二部分是环境的构建:
import numpy as np
import pandas as pd
import time
class Env:
def __init__(self,column,maze_column):
self.column = column #表示地图的长度
self.maze_column = maze_column - 1 #宝藏所在的位置
self.x = 0 #初始化x
self.map = np.arange(column) #给予每个地点一个标号
self.count = 0 #用于技术一共走了多少步
def draw(self):
a = []
for j in range(self.column) : #更新图画
if j == self.x:
a.append('o')
elif j == self.maze_column:
a.append('m')
else:
a.append('_')
interaction = ''.join(a)
print('\r{}'.format(interaction),end = '')
def get_observation(self):
return self.map[self.x] #返回现在在所
def get_terminal(self):
if self.x == self.maze_column: #如果得到了宝藏,则返回已经完成
done = True
else:
done = False
return done
def update_place(self,action):
self.count += 1 #更新的时候表示已经走了一步
if action == 'right':
if self.x < self.column - 1:
self.x += 1
elif action == 'left': #left
if self.x > 0:
self.x -= 1
def get_target(self,action):
if action == 'right': #获得下一步的环境的实际情况
if self.x + 1 == self.maze_column:
score = 1
pre_done = True
else:
score = 0
pre_done = False
return self.map[self.x + 1],score,pre_done
elif action == 'left': #left
if self.x - 1 == self.maze_column:
score = 1
pre_done = Ture
else:
score = 0
pre_done = False
return self.map[self.x - 1],score,pre_done
def retry(self): #初始化
self.x = 0
self.count = 0
其运行的主函数为:
from Env import Env
from QL import QL
import numpy as np
import time
LONG = 6 #总长度为6
MAZE_PLACE = 6 #宝藏在第六位
TIMES = 15 #进行15次迭代
people = QL(['left','right']) #生成QLearn主体的对象,包含left和right
site = Env(LONG,MAZE_PLACE) #生成测试环境
for episode in range(TIMES):
state = site.get_observation() #观察初始环境
site.draw() #生成图像
time.sleep(0.3) #暂停
while(1):
done = site.get_terminal() #判断当前环境是否到达最后
if done: #如果到达,则初始化
interaction = '\n第%s次世代,共使用步数:%s。'%(episode+1 ,site.count)
print(interaction)
site.retry()
time.sleep(2)
break
action = people.choose_action(state) #获得下一步方向
state_after,score,pre_done = site.get_target(action) #获得下一步的环境的实际情况
people.learn(state,action,score,state_after,pre_done) #根据所处的当前环境对各个动作的预测得分和下一步的环境的实际情况更新当前环境的q表
site.update_place(action) #更新位置
state = state_after #状态更新
site.draw() #更新画布
time.sleep(0.3)
print(people.q_table)
每一段代码我都给予了详细的注释,三个文件的名称分别为 QL.py,Env.py,Test.py。
(不知道为什么这个编辑器给我生成了个什么网页……- -)
GITHUB下载连接
https://github.com/bubbliiiing/Reinforcement-Learning
希望得到朋友们的喜欢。
有不懂的朋友可以评论询问噢。