基本设置
例子:瓜农种西瓜
• 多步决策过程
• 过程中包含状态、动作、反馈 (奖赏) 等
• 需多次种瓜,在过程中不断摸索,才能总结出较好的种瓜策略
监督学习对比强化学习
预测:最小化预测误差,预测结果不会对环境产生影响
比如,是否为肿瘤,天气预测是否影响天气
决策:决策动作会对环境产生影响
比如:肿瘤去除,股票投资会影响股市
强化学习的关键要素
<
A
,
X
,
R
,
P
>
<A,X,R,P>
<A,X,R,P>
动作空间 A
状态空间 X
奖赏函数 R:
X
×
A
×
X
→
R
X\times A\times X\rightarrow \mathcal{R}
X×A×X→R
状态转移概率P:
X
×
A
×
X
→
R
X\times A\times X\rightarrow \mathcal{R}
X×A×X→R
目标:通过在种瓜过程中不断摸索,总结出较好的种瓜策略,种出好瓜(获得的累积奖赏最大)
强化学习的目标
器通过在环境中不断尝试,从而学到一个“策略”(policy)
π
\pi
π,使得
长期执行该策略后得到的累积奖赏最大。
策略表示:
确定性策略和随机性策略
策略的评估:累计奖赏
状态值函数:从x输出用策略
π
\pi
π带来的累计奖赏
状态值函数,从状态x出发,策略
π
\pi
π的累计奖赏
{
V
T
π
(
x
)
=
E
π
[
1
T
∑
t
=
1
T
r
t
∣
x
0
=
x
]
T
步
累
计
奖
赏
V
γ
π
(
x
)
=
E
π
[
∑
t
=
0
+
∞
γ
t
r
t
+
1
∣
x
0
=
x
]
γ
折
扣
累
计
奖
赏
\left\{\begin{array}{l} V^\pi_T(x)=\mathbb{E}_\pi[\frac{1}{T}\sum^T_{t=1}r_t|x_0=x]&T步累计奖赏\\ V^\pi_\gamma(x)=\mathbb{E}_\pi[\sum^{+\infty}_{t=0}\gamma^t r_{t+1}|x_0=x]&\gamma折扣累计奖赏 \end{array}\right.
{VTπ(x)=Eπ[T1∑t=1Trt∣x0=x]Vγπ(x)=Eπ[∑t=0+∞γtrt+1∣x0=x]T步累计奖赏γ折扣累计奖赏
状态-动作值函数,从状态x出发,执行动作a再用策略
π
\pi
π的累计奖赏
{
Q
T
π
(
x
)
=
E
π
[
1
T
∑
t
=
1
T
r
t
∣
x
0
=
x
,
a
0
=
a
]
T
步
累
计
奖
赏
Q
γ
π
(
x
)
=
E
π
[
∑
t
=
0
+
∞
γ
t
r
t
+
1
∣
x
0
=
x
,
a
0
=
a
]
γ
折
扣
累
计
奖
赏
\left\{\begin{array}{l} Q^\pi_T(x)=\mathbb{E}_\pi[\frac{1}{T}\sum^T_{t=1}r_t|x_0=x,a_0=a]&T步累计奖赏\\ Q^\pi_\gamma(x)=\mathbb{E}_\pi[\sum^{+\infty}_{t=0}\gamma^t r_{t+1}|x_0=x,a_0=a]&\gamma折扣累计奖赏 \end{array}\right.
{QTπ(x)=Eπ[T1∑t=1Trt∣x0=x,a0=a]Qγπ(x)=Eπ[∑t=0+∞γtrt+1∣x0=x,a0=a]T步累计奖赏γ折扣累计奖赏
强化学习方法分类
- 马尔科夫决策过程中,A、 X、 P、R已知:动态规划
- 模型意指马尔可夫决策过程:P和R往往未知
- 基于模型(model-based)的RL:先学习/还原环境模型,即P和R再求解最优策略
- 免模型(model-free)的RL:不学习/原环境模型,直接逼近最优策略
Q-learning算法
基本思想
• 学习状态-动作值函数
Q
(
s
,
a
)
Q(s,a)
Q(s,a),再从学到的
Q
Q
Q函数里求出最优策略
π
π
π(即带来的累积奖赏最大)
• 免模型方法:无需先学习状态转移概率P和奖赏函数R
• 从与环境交互的轨迹数据中学习
一条轨迹数据:
s
1
,
a
1
,
r
2
,
s
2
,
a
2
.
.
.
s_1,a_1,r_2,s_2,a_2...
s1,a1,r2,s2,a2...
【当动作和下一时刻状态都已经(通过采样)确定时】
Q
γ
π
(
x
)
=
E
π
[
∑
t
=
0
+
∞
γ
t
r
t
+
1
∣
x
0
=
x
,
a
0
=
a
]
Q^\pi_\gamma(x)=\mathbb{E}_\pi[\sum^{+\infty}_{t=0}\gamma^t r_{t+1}|x_0=x,a_0=a]
Qγπ(x)=Eπ[t=0∑+∞γtrt+1∣x0=x,a0=a]
转换为
Q
(
s
t
,
a
t
)
=
R
(
s
t
,
a
t
)
+
γ
⋅
Q
(
s
t
+
1
,
a
t
+
1
)
Q(s_t,a_t)=R(s_t,a_t)+\gamma\cdot Q(s_{t+1},a_{t+1})
Q(st,at)=R(st,at)+γ⋅Q(st+1,at+1)
基本思想:维护两个策略
目标策略:
π
(
a
∣
s
)
\pi(a|s)
π(a∣s)用状态-动作值函数
Q
(
s
,
a
)
Q(s,a)
Q(s,a)评估
执行/行为策略(用于收集数据和做探索):
μ
(
a
∣
s
)
\mu(a|s)
μ(a∣s)用于收集轨迹数据
{
s
1
,
a
1
,
r
2
,
s
2
,
a
2
…
…
s
T
}
∼
μ
\{s_1,a_1,r_2,s_2,a_2……s_T\}\sim \mu
{s1,a1,r2,s2,a2……sT}∼μ
μ
(
a
∣
s
)
\mu(a|s)
μ(a∣s)实现,可以使用
ϵ
−
g
r
e
e
d
y
\epsilon-greedy
ϵ−greedy的环境探索法【平衡探索与利用】
π
ϵ
(
s
)
=
{
π
(
s
)
=
a
r
g
m
a
x
a
Q
(
s
,
a
)
以
概
率
1
−
ϵ
A
中
以
均
匀
概
率
选
取
动
作
概
率
ϵ
\pi^\epsilon(s)=\left\{\begin{array}{l} \pi (s) = argmax_aQ(s,a) & 以概率1-\epsilon \\ A中以均匀概率选取动作 & 概率\epsilon\end{array}\right.
πϵ(s)={π(s)=argmaxaQ(s,a)A中以均匀概率选取动作以概率1−ϵ概率ϵ
状态-动作值函数更新:
绵羊走迷宫
假设一个迷宫有5个房间,房间之间通过门相连,将这五个房间按照从0至4进行编号,且5号房间里面是草地。因绵羊要吃草,设定5号房间为绵羊的目标房间。求绵羊从任意一个房间走到目标房间(5号房间)的最优路径。
基于图表示的房间结构
• 节点:房间
• 边:如果两个房间有门相连,那么两个节点之间存在一条边。边的权重即为奖赏值(直接连接到5号房间的奖赏值为100,其他门的奖赏值为0)。没有门直接相连,奖赏值为-1
绵羊从任意一个房间走到目标房间的最优路径
→
\rightarrow
→从任意状态到达目标状态的最大累积奖赏
令学习参数γ=0.8,初始状态为1号房间
• 根据奖赏矩阵R,状态1的下一步动作有两种选择:转向状态3或者状态5。
- 根据行为策略,假设我们转向状态5。
• 绵羊位于状态5以后,有三种可能的动作选择
:转向状态1或4或5。
- 根据目标策略,选择Q值最大的动作,并更新值函数:
Q
(
1
,
5
)
=
R
(
1
,
5
)
+
γ
×
m
a
x
{
Q
(
5
,
1
)
,
Q
(
5
,
4
)
,
Q
(
5
,
5
}
=
100
Q(1,5)=R(1,5)+\gamma\times max\{Q(5,1),Q(5,4),Q(5,5\}=100
Q(1,5)=R(1,5)+γ×max{Q(5,1),Q(5,4),Q(5,5}=100
当前状态为5号房间(达到目标状态)
完成了一次episode,矩阵Q也相应地更新了
接着,假设选取3为初始状态
• 根据奖赏矩阵R,状态3有三种可能的动作选
择:转向状态1或2或4。
- 根据行为策略,假设我们转向状态1。
绵羊位于状态1以后,有两种可能的动作选择
:转向状态3或5。
- 根据目标策略,选择Q值最大的动作,并更
新值函数:
Q ( 3 , 1 ) = R ( 3 , 1 ) + γ × m a x { Q ( 1 , 3 ) , Q ( 1 , 5 ) } = 80 Q(3,1)=R(3,1)+\gamma\times max\{Q(1,3),Q(1,5)\}=80 Q(3,1)=R(3,1)+γ×max{Q(1,3),Q(1,5)}=80
当前状态为1号房间(不是目标状态)
第二次episode还没有结束
• 根据奖赏矩阵R,状态1有两种可能的动作选
择:转向状态3或5。
- 根据行为策略,假设我们转向状态5。
由前面的分析,我们有:Q(1,5)=100。更新新矩阵Q,但是Q没有发生变化。第二次episode结束
继续执行更多的episode,最终Q将收敛成如下一个矩阵
如果以3号房间为初始状态,根据Q表,累积的奖赏最大的路径是3-﹥1-﹥5或者3-﹥4-﹥5,总的reward都为100。
强化学习拓展
RL需要对环境进行探索
智能体在探索中加深对环境的理解,使得长期累积奖赏最大
比如婴儿走路
智能体在探索中加深对环境的理解,使得长期累积奖赏最大
具体阐述:
K臂老虎机(K-armed Bandit):
一个状态,k个动作,每个摇臂奖励服从某个期望位置的分布,执行有限次动作,最大化累计奖赏
简化RL(无状态转移,因为只有一个状态),把探索抽象出来单独研究
若只探索:平均分配尝试次数(浪费大量尝试)
只利用:每个摇一次,根据反馈结果只选择期望最高的摇臂(误差大)
主要难题之一:平衡探索与利用
摇臂次数有限,如何分配资源做探索和利用,使得长期累积奖赏尽可能的大?
-
环境探索方法
ϵ 贪 心 \epsilon贪心 ϵ贪心
以 ϵ \epsilon ϵ的概率探索:均匀随机选择一个摇臂
以 1 − ϵ 1-\epsilon 1−ϵ概率利用,选择当前期望最高的摇臂 -
Softmax:基于当前已知摇臂期望对探索和利用这种
if 摇臂当前平均奖赏大,选择概率高
概率用Boltzmann分布
P ( k ) = e Q ( k r ∑ i = 1 K e Q ( i ) r P(k)=\frac{e^{\frac{Q(k}{r}}}{\sum^K_{i=1} e^{ \frac{Q(i)}{r}}} P(k)=∑i=1KerQ(i)erQ(k
Q ( i ) Q(i) Q(i)是记录当前摇臂的平均奖赏
两种算法都有折中参数 ( ϵ , r ) (\epsilon,r) (ϵ,r)
深度强化学习:深度模型+强化学习
深度强化学习
使用(深度)神经网络表示策略或值函数
迷宫:有限状态、动作,小规模
深度强化学习
大规模/连续动作、状态,复杂策略表示,可考虑深度强化学习
应用
例如,DQN(深度Q网络)玩游戏,可以超过人类水平
AlphaGo:树搜索+深度强化学习(深度网络+强化学习
除了各种棋类、还应用于机器人控制、推荐系统、军事博弈、股票预测
等各种决策任务
小结
有监督学习 | 无监督学习 | 强化学习 | |
---|---|---|---|
学习依据 | 基于监督信息 | 基于对数据结构的假设 | 基于评估 |
数据来源 | 一次给定 | 一次给定 | 在时序交互中产生 |
决策过程 | 单步决策(如分类和识别) | 无 | 序贯决策(如棋类博弈) |
学习目标 | 样本到语义标签的映射 | 数据的分布模式 | 选择能够获取最大受益的状态到动作的映射 |
强化学习:多步决策过程,解决how的问题,决策会影响环境
• 强化学习拓展:探索-利用难题、深度强化学习、应用及问题
• Q-Learning 算法
迷宫寻宝
当然,你不需要写出程序所有代码,你只需要补全以下函数并提交即可。
函数部分定义如下(部分变量的说明已经在提示中详尽给出):
void Qlearning()
{
int nx = 1 + rand() % n, ny = 1 + rand() % m;
int cnt = 0;
while (!isEnd(nx,ny) && cnt < n*m) {
auto &s0 = Qstate[(nx-1)*m+ny];
int siz = s0.size();
int _next = -1,idx = 0;
if (!siz) return;
if (siz == 1) _next = s0[0].first;
else {
sort(s0.begin(),s0.end(),cmp);
double p = rand() / double(RAND_MAX);
if (p < 1 - greedy_factor) {
_next = s0[0].first;
}
else {
idx = 1 + rand() % (siz - 1);
_next = s0[idx].first;
}
}
auto &s1 = Qstate[_next];
sort(s1.begin(),s1.end(),cmp);
double _nextMax = (int)s1.size() ? s1[0].second : 0.0;
nx = (_next - 1) / m + 1;ny = (_next - (nx-1) * m) % (m+1);
/********/
// TODO
// HINTS: 使用s0[idx].second、alpha、gama、_nextMax、Reward[nx][ny]变量,
// 完成Qlearning的状态更新公式
/********/
cnt++;
}
}
#include <bits/stdc++.h>
using namespace std;
const int maxn = 21;
const int epics = 50000;
const int target_success = 5000;
const double inf = 1e8;
bool cmp(pair<int,double> X, pair<int,double> Y)
{
if(X.second == Y.second) return X.first < Y.first;
return X.second > Y.second;
}
// 迷宫尺寸 n*m
// x,y坐标从1开始 起点(1,1) 终点(n,m)
// 对于每个位置(i,j),可以向U,L,R,D四个方向移动,不可走出迷宫边界,不可走到障碍物上
int n,m;
// maze[maxn+5][maxn+5]存储迷宫 0代表可走通 1代表陷阱 (maze[i][j], 1<=i<=n,1<=j<=m)
// 答案保证有解 即 maze[1][1] = 0, maze[n][m] = 0, 起点与终点之间保证至少存在一条路径
int maze[maxn+5][maxn+5];
// 奖励函数R 设置普通格子奖励为-(abs(i-n)+abs(j-m)) 陷阱奖励为-10000 终点奖励为100000 衰减因子alpha = 0.9 , gama = 0.9
const double alpha = 0.9;
const double gama = 0.9;
double Reward[maxn+5][maxn+5];
// 状态函数Q 下一步的选取采用1-ϵ贪心
// 关于Qstate[pos]中的pos索引到二维坐标(px,py)的转换:
// px = (pos - 1) / m + 1
// py = (pos - (px-1) * m) % (m+1);
const double greedy_factor = 0.1;
vector< pair<int,double> > Qstate[(maxn+5)*(maxn+5)];
// 存储答案
vector< pair<int,int> > ans;
// 初始化奖励函数R和状态函数Q
void init_RQ(int i,int j)
{
// 陷阱不设置奖励函数(可理解成均为-inf)
if (maze[i][j] == 1) return;
// Up
if (Reward[i-1][j]) {
Qstate[(i-1)*m+j].push_back({(i-2)*m+j,-1});
}
// Left
if (Reward[i][j-1]) {
Qstate[(i-1)*m+j].push_back({(i-1)*m+j-1,-1});
}
// Right
if (Reward[i][j+1]) {
Qstate[(i-1)*m+j].push_back({(i-1)*m+j+1,-1});
}
// Down
if (Reward[i+1][j]) {
Qstate[(i-1)*m+j].push_back({i*m+j,-1});
}
}
int success_time = 0;
bool isEnd(int i,int j)
{
if (i == n && j == m) {
++success_time;
return true;
}
if (maze[i][j] == 1) {
return true;
}
return false;
}
void Qlearning()
{
int nx = 1 + rand() % n, ny = 1 + rand() % m;
int cnt = 0;
while (!isEnd(nx,ny) && cnt < n*m) {
auto &s0 = Qstate[(nx-1)*m+ny];
int siz = s0.size();
int _next = -1,idx = 0;
if (!siz) return;
if (siz == 1) _next = s0[0].first;
else {
sort(s0.begin(),s0.end(),cmp);
double p = rand() / double(RAND_MAX);
if (p < 1 - greedy_factor) {
_next = s0[0].first;
}
else {
idx = 1 + rand() % (siz - 1);
_next = s0[idx].first;
}
}
auto &s1 = Qstate[_next];
sort(s1.begin(),s1.end(),cmp);
double _nextMax = (int)s1.size() ? s1[0].second : 0.0;
nx = (_next - 1) / m + 1;ny = (_next - (nx-1) * m) % (m+1);
/********/
// TODO
// HINTS: 使用s0[idx].second、alpha、gama、_nextMax、Reward[nx][ny]变量,
// 完成Qlearning的状态更新公式
// 你的代码将被嵌在此处
/********/
cnt++;
}
}
int main()
{
ios::sync_with_stdio(false);
srand(2022);
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++) {
int t;
cin >> t;
maze[i][j] = t;
Reward[i][j] = (t == 0 ? -(abs(i-n)+abs(j-m)) : -10000.0);
}
}
Reward[n][m] = 100000.0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
init_RQ(i,j);
}
}
int cnt = 0;
while (cnt < epics && success_time != target_success) {
Qlearning();
++cnt;
}
cnt = 0;
int x = 1, y = 1;
while ((x!=n || y!=m) && cnt < n*m) {
auto &s = Qstate[(x-1)*m+y];
if (!(int)s.size()) {
// 无解情况,可忽略
cout << "Something Wrong!" << endl;
return 0;
}
ans.push_back({x,y});
sort(s.begin(),s.end(),cmp);
int _next = s[0].first;
x = (_next - 1) / m + 1; y = (_next - (x-1) * m) % (m+1);
++cnt;
}
ans.push_back({n,m});
cout << (int)ans.size() << endl;
for(auto& u : ans) {
cout << u.first << ' ' << u.second << endl;
}
return 0;
}
思路
搞清楚每个部分干啥套公式即可
代码
double former = (1 - alpha) * s0[idx].second;
double later = alpha * (Reward[nx][ny] + gama * _nextMax);
s0[idx].second = former + later;