强化学习Q-learnin学习笔记
前言
本文是学习【莫烦python】的课程笔记没找到本子就记在这里了,课程网址如下:https://www.bilibili.com/video/BV13W411Y75P?p=1
一、Q-learning简介
1.情景叙述
机器学习就是一个不断试错,不断学习的过程,为了简化问题,下图中的例子只考虑a1,a2两种情况,s1表示状态1,a1表示动作看电视,a2表示动作写作业。
孩子做a1,a2的不同抉择,会影响之后的状态,如果孩子在每一个状态s中都一直重复看电视的a1动作,最后就会挨打。Q学习的目的就是从结果中汲取教训反复训练,以至于最后达到理想的结果。
2.Q表的引入
为了更好的模拟孩子的行为,我们将上文孩在不同状态s下的不同动作a进行抽象,得到了如图的Q表(左下角)。
(s1,a2)表示孩子在s1状态下,选择a2动作(即学习),而(s1,a2)的值表示该状态下执行该动作的价值。
训练成功的机器人,就可以分析孩子所在的状态s,然后查阅Q表比较s下不同的动作的价值,“高概率”地选择价值更高的动作,以此类推,再到下一个动作,然后选择经过训练学习后最具价值的那个动作。
简而言之,Q表的样子,决定了机器决定了孩子接下来会做什么动作,所以机器学习中Q-learning算法的训练过程,其实就是一个将Q表从一个空表,不断完善成具有参考价值的Q表的过程。
二、Q-learning公式
Q表的迭代过程的核心公式及步骤如下:
1.将当前状态的s1的值作为估计值,得出下一步机器想要在s2状态执行a2动作。
2.比较得出下一个状态下s2中最具价值的动作,即maxQ(s2),将此值乘y(这里的y一般取0.8),然后加上从s1到s2状态下的奖惩值R(可以取正也可以取负)
(这里在s1状态就利用了Q表中s2的过程,其实就是一个对未来的估计过程)
3.求出二者的差值
4.更新(s1,a2)处的值
5.重复迭代
算法的伪代码如下:
这里的ε表示执行Q-learning算法的概率,如果不执行,则随机选择动作。(为了达到全局最优而设置的)
这里多提一嘴,为什么要在当前状态s1下考虑s2的值呢?究其原因,就是要让机器学习能够简单的“预测”(或者“学习”)将来的结果,如果将来的结果是错误的,那么在不断的学习迭代的过程中,这种错误的结果会一步步的返回到之前的一步步动作决策中去。
例如我在s3状态下做了关键性决策a2,最后导致在s20状态下陷入失败,那么Q表在不断的更新后就会对a2的值进行不断的削减,以至于最后在Q表中,s3状态下的a1比a2的价值更高。
三、Q-learning代码
我们搭建了一个简单的机器学习环境,让字符‘o’通过自主学习右移到字符’T’中,如下:
o----T 直到 ----oT
【莫烦python】课中给了python代码,我自己敲了一遍,然后又自己用C++强化了练习了一下,二者代码如下:
1.python代码
import numpy as np
import pandas as pd
import time
np.random.seed(2)
N_SATAES = 6
ACTIONS = ['left','right']
EPSILON = 0.9 #greedy police百分比选择最优动作率
ALPHA=0.1 #learning rate
LAMBDA = 0.9 #discount factor衰减度
MAX_EPSDDES = 50 #maximum dpisodes
FRESH_TIME = 0.01 #fresh time for one move
def buil_q_table(n_states,actions):
table = pd.DataFrame(np.zeros((n_states,len(actions))),#q_table inital values
columns=actions,#action's name
)
return table
def choose_action(state,q_table):
#这是如何选择动作
state_actions = q_table.iloc[state,:]
if (np.random.uniform()>EPSILON ) or ((state_actions== 0).all()):
#概率性问题的解决
action_name = np.random.choice(ACTIONS)
else:#选择比较大的数
action_name = state_actions.idxmax()#返回最大值对应的纵坐标
return action_name
def get_env_feedback(S,A):
#这是代理如何对环境进行交流
if A == 'right': #move right
if S == N_SATAES-2: #terminate
S_ = 'terminal'
R = 1 #代表成功时给予的奖励
else:
S_ = S+1
R = 0
else:
R = 0
if S ==0:
S_ = S #reach the wall
else:
S_ = S - 1
return S_,R
def update_env(S,episode,stpe_counter):
#环境,可以不看
env_list = ['-']*(N_SATAES-1) + ['T']
if S == 'terminal':
interaction = 'Episode %s:total_steps = %s'%(episode+1,stpe_counter)
print('\r{}'.format(interaction),end='')
time.sleep(2)
print('\r ',end='')
else:
env_list[S] = 'o'
interaction = ''.join(env_list)
print('\r{}'.format(interaction),end='')
time.sleep(FRESH_TIME)
def rl():
q_table = buil_q_table(N_SATAES,ACTIONS)
for episode in range(MAX_EPSDDES):
step_counter = 0
S= 0
is_terminated = False
update_env(S,episode,step_counter)
while not is_terminated:
A = choose_action(S,q_table)
S_,R = get_env_feedback(S,A)
q_predict = q_table.loc[S,A]
if S_ != 'terminal':
q_target = R + LAMBDA * q_table.iloc[S_,:].max()
else:
q_target = R
is_terminated = True
q_table.loc[S,A] += ALPHA * (q_target - q_predict)
S = S_
update_env(S, episode, step_counter+1)
step_counter += 1
return q_table
q_table = rl()
print('\r\nQ-table:\n')
print(q_table)
2.C++代码
#include<iostream>
#include<time.h>
#include<vector>
#include<string.h>
#include<stdio.h>
#include<windows.h>
using namespace std;
#define N_SATAES 6
#define EPSILON 0.9
#define ALPHA 0.1
#define LAMBDA 0.9
#define MAX_EPSDDES 20
#define FRESH_TIME 0.1
void get_env_feedback(int S, int A,int &S_,int &R) {
//这是代理如何对环境进行交流
if (A == 1) //move right
{
if (S == N_SATAES - 2) //terminate
{
S_ = -1;
R = 1; //代表成功时给予的奖励
}
else
{
S_ = S + 1;
R = 0;
}
}
else
{
R = 0;
if (S == 0)
S_ = S;//reach the wall
else
S_ = S - 1;
}
}
int choose_action(int state, vector<pair<float, float>> q_table) {
//这是如何选择动作,action_name=0表示左移,1表示右翼
int action_name =9999;
float ra = ((rand() % 100) / 100.0);
if (( ra> EPSILON) || ((q_table[state].first == 0) && (q_table[state].second == 0)))
{
action_name = rand() % 2;
}
else//选择比较价值大的数
{
if (q_table[state].first > q_table[state].second)
action_name = 0;
else
action_name = 1;
}
return action_name;
}
void update_env(int S,int episode,int stpe_counter) {
//环境,可以不看
string interaction;
char env_list[] = "-----T";
if (S == -1)
{
printf("\rEpisode %d:total_steps = %d", episode + 1, stpe_counter);
Sleep(2*1000);
printf("\r ");
}
else
{
env_list[S] = 'o';
printf("\r %s",env_list);
Sleep(FRESH_TIME*1000);
}
}
int q_learning {
//新建空的Q表
vector<pair<float, float>> q_table;
pair<float, float> q_one = make_pair(0, 0);
for (int i = 0;i < N_SATAES;i++) {
q_table.push_back(q_one);
}
srand(2);
//***********主循环*********
for (int i = 0;i < MAX_EPSDDES;i++)
{
int step_counter = 0;
int S = 0;
int A, S_, R = 999;
float q_predict, q_target;
bool is_terminated = false;
update_env(S, i, step_counter);
while (!is_terminated)
{
A = choose_action(S, q_table);
get_env_feedback(S, A, S_, R);
if (A == 0)
q_predict = q_table[S].first;
else
q_predict = q_table[S].second;
if (S_ != -1)
{
if (q_table[S].first > q_table[S].second)
q_target = R + LAMBDA * q_table[S_].first;
else
q_target = R + LAMBDA * q_table[S_].second;
}
else
{
q_target = R;
is_terminated = true;
}
if (A == 0)
q_table[S].first += ALPHA * (q_target - q_predict);
else
q_table[S].second += ALPHA * (q_target - q_predict);
S = S_;
update_env(S, i, step_counter + 1);
step_counter += 1;
}
}
printf("\r\nQ-table:\n");
printf(" right left\n");
for(int i = 0;i<6;i++)
printf("%d %f %f\n",i,q_table[i].first, q_table[i].second);
system("pause");
return 0;
}