算法基础
强化学习
人类通过长期与环境进行交互并在这个过程中不断地学习,在这个过程中我们会获得大量具有与指导意义的信息,并根据这些信息结合我们自身的策略采取一系列的动作,以期获得自身收益的最大化。这些信息同时也是我们对于自身与环境了解的主要来源,例如如何与人交往或学习知识。通过这种交互来学习,是几乎所有智能理论和学习理论的源头。
强化学习就是这样一种源自于“与环境交互并学习”的思想而被发明的一种机器学习方法,它强调让Agent从这种交互中学习达成目标的策略。
强化学习主要用来解决决策问题,使用代理人Agent去寻找所处环境“状态”与代理可执行的“行动”之间的对应关系,使得在一系列的行动之后收获最大数值的“奖励”。其中,Agent的行为选择并非来自人为告知,而是通过“试错”的形式积累经验,并从经验中学习到使奖励最大的行动序列。
因此Agent具有以下几个特点:(1)可以感知环境,能够接受并解析环境所给出的信息;(2)所执行的动作可作用于当前状态;(3)它有一个与环境相关的目标且环境可以及时给予奖励。
除了智能体与环境,一个完整的强化学习系统还应该包含策略(Policy)、奖励函数(Reward Function)以及价值函数(Value Function)。
总体而言,策略π是一个以state为输入的函数,负责将环境给出的state映射成Agent的action。奖励是Agent的目标,奖励函数负责将环境给出的state和Agent因此所作出的action映射成奖励给予Agent以优化Agent的策略。而价值函数则是Agent对长期奖励的一个估计,它标记了所选行为的好坏程度。这些要素使得智能体能够通过观察环境状态采取相应行动,然后通过行动影响环境,并在这个过程中学习并优化自己的策略。在这里产生的数据可以表示为:
{
s
1
,
a
1
,
r
1
,
s
2
,
a
2
,
r
2
,
.
.
.
,
s
t
,
a
t
,
r
t
}
\{s_1,a_1,r_1,s_2,a_2,r_2,...,s_t,a_t,r_t\}
{s1,a1,r1,s2,a2,r2,...,st,at,rt}
其中,
s
t
s_t
st表示在时刻t智能体所能观察到的状态,
a
t
a_t
at表示在时刻t智能体所采取的行动,
r
t
r_t
rt表示在时刻
t
t
t智能体在执行完动作后收到的立即奖励。强化学习就是在这些样本中学习并优化自己的策略。下面是学习过程。
TD算法
强化学习按照模型主要分为两类——基于模型的以及基于无模型的,其中基于模型的算法主要的核心思想是动态规划,即Agent需要知道环境的所有信息,了解状态之间的转换概率,且其当前状态值函数的更新依赖于已知的其他状态的值函数并应用Bellman方程求解值函数。基于无模型的算法主要分为两类,时间差分法以及蒙特卡洛法。在实际问题中,很多时候环境的信息并不是完全的,也无法知晓各个状态之间的转换概率,这时候我们很难应用基于模型的强化学习去解决这些问题。而无模型算法则不需要太多的环境信息因此在这些问题上具有十分出彩的表现。蒙特卡洛法应用统计学的思想通过多次数据采样,然后求其平均值以近似的方式逼近值函数。但是蒙特卡洛法必须假设问题是有限步的,基于回合的,这是因为蒙特卡洛法需要到回合结束时进行学习。如果问题具有无穷多步,则蒙特卡洛法则不适用。
基于上述两种思想,TD算法结合了蒙特卡洛的采样数据和动态规划中利用后继状态的值函数估计当前值函数(bootstrapping)的思想,因此TD算法也是基于无模型的算法且单步更新。其值函数的计算方式如下:
V
(
s
t
)
←
V
(
s
t
)
+
α
(
R
t
+
1
+
γ
V
(
s
t
+
1
)
−
V
(
s
t
)
)
V(s_t)\gets V(s_t)+\alpha(R_{t+1}+\gamma V(s_{t+1})-V(s_t))
V(st)←V(st)+α(Rt+1+γV(st+1)−V(st))
其中,我们利用bootstrapping的方式估计当前值函数,避免了蒙特卡洛回合更新的问题,从而提升了学习的速度。
而根据对
V
(
s
t
+
1
)
V(s_{t+1})
V(st+1)估计的不同,TD算法衍生出Sarsa和QLearning。
QLearning和Sarsa
QLearning与Sarsa都是基于值的无模型的强化学习技术。具体而言,QLearning和Sarsa可以用来为任何给定的(有限的)马尔科夫决策过程找到最优的行动选择策略。他们都通过学习一个基于action-value(Q表)的价值函数工作,并在遵循一个最优策略的情况下给出状态s下执行一个动作a后的期望结果。这两种算法都可以通过这个价值函数选择每个状态下最高值来构建最优策略。
QLearning与Sarsa的算法模型非常相似,Agent可以从一个状态转移到另一个状态,
而在一个特定的状态下,环境会根据Agent所执行的动作提供一个奖赏(或惩罚)。Agent
将会通过学习找出当前状态下一系列动作中的具有最高长期回报的动作从而实现总的
回报最大化。这个回报的计算是从当前状态开始,对所有未来步骤的预期价值的加权和。其中,从学习步骤
∆
t
∆t
∆t进入下一步骤所使用的权重是通过折扣因子
γ
∆
t
(
γ
∈
[
0
,
1
]
)
γ^{∆t}(γ ∈ [0,1])
γ∆t(γ∈[0,1])来计算的。折扣因子主要是用于平衡即时回报
r
t
r_t
rt以及延时回报
∑
i
=
t
+
1
r
i
∑_{i=t+1}r_i
∑i=t+1ri的权重。因此,QLearning与Sarsa的价值函数为:
Q
:
S
×
A
→
R
Q:S\times A\to R
Q:S×A→R
在学习开始之前,Q表被初始化为一个可能的固定值。然后在每个回合的每个步骤t中Agent选择一个动作
a
t
a_t
at并发现一个奖励rt以及一个新状态
s
t
+
1
s_{t+1}
st+1。由马尔科夫性可知,这个新状态
s
t
+
1
s_{t+1}
st+1属于何种状态取决于之前的状态st以及所选择的的动作,Q表根据上述奖励以及状态进行更新。
以上是QLearning与Sarsa主要的算法步骤,这两者之间的不同在于Q表的更新上。
QLearning
Agent将会采取贪婪算法 ,选择下一个状态的收益最大值
m
a
x
a
′
Q
(
s
t
+
1
,
a
′
)
max_{a'}Q(s_{t+1},a')
maxa′Q(st+1,a′)来更新Q表,不知道大家能不能理解这样的含义,这意味着Agent只是拿下一时刻的值更新Q表,而不是一定会选择这个最大值所代表的的动作。
Q表的更新方法如下:
Q
(
s
t
,
a
)
←
Q
(
s
t
,
a
)
+
α
[
r
t
+
γ
m
a
x
a
′
Q
(
s
t
+
1
,
a
′
)
−
Q
(
s
t
,
a
)
]
,
α
∈
(
0
,
1
)
Q(s_t,a)\gets Q(s_t,a)+\alpha [r_t+\gamma max_{a'}Q(s_{t+1},a')-Q(s_t,a)] ,\alpha \in (0,1)
Q(st,a)←Q(st,a)+α[rt+γmaxa′Q(st+1,a′)−Q(st,a)],α∈(0,1)
其中,α为学习率,用于决定这次的误差有多少是要被学习的。
Sarsa
Agent将会选择下一个状态下的收益
Q
(
s
t
+
1
,
a
′
)
Q(s_{t+1},a')
Q(st+1,a′)更新Q表,这需要Agent先确定好下一步的行动并获得其收益的值,和QLearning不同,Sarsa的下一步是确定的。Sarsa的Q表更新方法如下:
Q
(
s
t
,
a
)
←
Q
(
s
t
,
a
)
+
α
[
r
t
+
γ
Q
(
s
t
+
1
,
a
′
)
−
Q
(
s
t
,
a
)
]
,
α
∈
(
0
,
1
)
Q(s_t,a)\gets Q(s_t,a)+\alpha [r_t+\gamma Q(s_{t+1},a')-Q(s_t,a)] ,\alpha \in (0,1)
Q(st,a)←Q(st,a)+α[rt+γQ(st+1,a′)−Q(st,a)],α∈(0,1)
不难看出,QLearning是一个离线策略的算法,这使得Agent更新Q表可以不基于正在经历的经验,而是学习他人的经验或过去的经验;与QLearning相对的,Sarsa是一个在线策略的算法,它虽然是基于值的强化学习,但与基于策略的Policy Gradients非常类似。
最大化收益的QLearning和Sarsa
现有强化学习模式主要由两个绩效标准指导:预期累积奖励和平均奖励标准。这两个标准都假定奖励具有内在的累积性或可加性。然而,在某些情况下,这种固有的报酬累积并不是必然的。这时候如果依然将报酬进行累计,可能会出现次最优策略拖累Agent无法发现最优策略,这时候我们需要对Q表的更新进行改进。
最大奖励强化学习的Agent可以表现出最大的奖励导向行为。它试图寻找能够带来最大回报的政策,而不是沿路而来的中低回报。这使得它对问题域中存在的次最优策略具有更好的免疫性。此外,最大报酬学习被证明在金融定价环境下,能够有效地估计未来的收益。
对于最大奖励强化学习,收益的计算方式为:
R
t
=
m
a
x
(
r
t
+
1
,
γ
r
t
+
2
,
γ
2
r
t
+
3
,
.
.
.
)
R_t=max(r_{t+1},\gamma r_{t+2},\gamma^2 r_{t+3},...)
Rt=max(rt+1,γrt+2,γ2rt+3,...)
TD算法的值函数可以被改写为:
V
t
(
s
t
)
=
m
a
x
(
r
t
+
1
,
γ
V
t
(
s
t
+
1
)
)
−
V
t
(
s
t
)
V_t(s_t)=max(r_{t+1},\gamma V_t(s_{t+1}))-V_t(s_t)
Vt(st)=max(rt+1,γVt(st+1))−Vt(st)
又根据对
V
(
s
t
+
1
)
V(s_{t+1})
V(st+1)估计的不同,QLearning和Sarsa的公式分别被改写为
QLearning:
Q
(
s
t
,
a
)
=
m
a
x
(
r
t
+
1
,
γ
m
a
x
a
Q
(
s
t
+
1
,
a
t
)
)
−
Q
(
s
t
,
a
)
Q(s_t,a)=max(r_{t+1},\gamma max_{a}Q(s_{t+1},a_t))-Q(s_t,a)
Q(st,a)=max(rt+1,γmaxaQ(st+1,at))−Q(st,a)
Sarsa:
Q
(
s
t
,
a
)
=
m
a
x
(
r
t
+
1
,
γ
Q
(
s
t
+
1
,
a
t
+
1
)
)
−
Q
(
s
t
,
a
)
Q(s_t,a)=max(r_{t+1},\gamma Q(s_{t+1},a_{t+1}))-Q(s_t,a)
Q(st,a)=max(rt+1,γQ(st+1,at+1))−Q(st,a)
代码
以上就是强化学习之中使用最广泛的TD算法中最基础的QLearning和Sarsa以及它们最大化收益的改进的原理,由于比较懒,很多公式的推导都没有写下来,只是单纯写了最核心的值函数更新,其他的公式如果有需要可以看看莫烦python之中讲强化学习的部分,最大化收益的TD算法需要看K.H. Quah1的论文。
那么其他废话就不说了,直接上代码吧。
import numpy as np
import pandas as pd
# 强化学习超类
class RL:
def __init__(self, action, alpha=0.1, gamma=0.9, e_greedy=0.9):
'''
初始化Agent
:param actions: 表示Agent具有多少种可选行动,因为是简单的交易员,所以只有两种
:param action_value :表示Agent所采取的行动的方向性,因为需要Agent学会卖出不良资产,所以当卖出不良资产时,利用行动的方向性使Agent获得正的奖励,表现在金融交易市场中被称为做空
:param alpha:学习率
:param gamma:折扣因子
:param epsilon:贪婪因子
:param q_table:Q表,即Agent的值函数
:param P0:Agent买入资产时资产价格
'''
self.actions = action
self.action_value = {'sell': -1, 'buy': 1}
self.alpha = alpha
self.gamma = gamma
self.epsilon = e_greedy
self.q_table = pd.DataFrame(columns=self.actions, dtype=np.float64)
self.P0 = None
pass
def choose_action(self, observation):
'''
Agent根据所处环境的情况作出相应的行动选择,如果该情况未曾经历过,则随机选一个行为
:param observation:一个元组(episode,price),用于表示当前资产交易日以及当前价格
'''
self.check_state_exist(observation)
if np.random.uniform() < self.epsilon:
# choose best action
state_action = self.q_table.loc[observation[0], :]
# some actions may have the same value, randomly choose on in these actions
action = np.random.choice(state_action[state_action == np.max(state_action)].index)
else:
# choose random action
action = np.random.choice(self.actions)
return action
def learn(self, *arg):
'''
Agent学习过程,因为这个类是超类所以并没有实现这个方法
'''
pass
def check_state_exist(self, state):
'''
Agent识别该状态是否经历过,没有的话在Q表添加一行新行,防止程序报错影响模型运行
'''
if state[0] not in self.q_table.index:
# append new state to q table
self.q_table = self.q_table.append(
pd.Series([0] * len(self.actions), index=self.q_table.columns, name=state[0], ), ignore_index=True)
# QLearning
class QLearning(RL):
def __init__(self, action, alpha=0.1, gamma=0.9, e_greedy=0.9):
super(QLearning, self).__init__(action, alpha=alpha, gamma=gamma, e_greedy=e_greedy)
def learn(self, s, a, r, s_):
'''
QLearning学习过程,就是更新Q表的过程
:param s:当前状态
:param a:当前行动
:param r:当前状态下当前行动环境所反馈的收益
:param s_:下一个状态
'''
r_ = np.log(r) * self.action_value[a]
q_predict = self.q_table.loc[s[0], a]
if s_[1]:
self.check_state_exist(s_)
q_target = r_ + self.gamma * self.q_table.loc[s_[0], :].max() # next state is not terminal
else:
q_target = r_ # next state is terminal
self.q_table.loc[s[0], a] = (self.q_table.loc[s[0], a] + self.alpha * (q_target - q_predict)).values[0] # update
# Sarsa
class Sarsa(RL):
def __init__(self, action, alpha=0.01, gamma=0.9, e_greedy=0.9):
super(Sarsa, self).__init__(action, alpha=alpha, gamma=gamma, e_greedy=e_greedy)
def learn(self, s, a, r, s_, a_):
'''
Sarsa学习过程,就是更新Q表的过程
:param s:当前状态
:param a:当前行动
:param r:当前状态下当前行动环境所反馈的收益
:param s_:下一个状态
:param a_:下一个状态下Agent的行动
'''
r_ = np.log(r) * self.action_value[a]
q_predict = self.q_table.loc[s[0], a]
if s_[1]:
self.check_state_exist(s_)
q_target = r_ + self.gamma * self.q_table.loc[s_[0], a_] # next state is not terminal
else:
q_target = r_ # next state is terminal
self.q_table.loc[s[0], a] = (self.q_table.loc[s[0], a] + self.alpha * (q_target - q_predict)).values[0] # update
# 最大收益QLearning
class QLearningM(QLearning):
def __init__(self, action, alpha=0.1, gamma=0.9, e_greedy=0.9):
super(QLearningM, self).__init__(action, alpha=alpha, gamma=gamma, e_greedy=e_greedy)
pass
def learn(self, s, a, r, s_):
'''
最大化收益的QLearning学习过程,就是更新Q表的过程
:param s:当前状态
:param a:当前行动
:param r:当前状态下当前行动环境所反馈的收益
:param s_:下一个状态
'''
r_ = np.log(r) * self.action_value[a]
q_predict = self.q_table.loc[s[0], a]
if s_[1]:
self.check_state_exist(s_)
q_target = pd.Series(max(float(r_),self.gamma * self.q_table.loc[s_[0], :].max()), name='return_rate') # next state is not terminal
else:
q_target = r_ # next state is terminal
self.q_table.loc[s[0], a] = (self.q_table.loc[s[0], a] + self.alpha * (q_target - q_predict)).values[0] # update
pass
# 最大收益Sarsa
class SarsaM(Sarsa):
def __init__(self, action, alpha=0.01, gamma=0.9, e_greedy=0.9):
super(SarsaM, self).__init__(action, alpha=alpha, gamma=gamma, e_greedy=e_greedy)
pass
def learn(self, s, a, r, s_, a_):
'''
最大户收益的Sarsa学习过程,就是更新Q表的过程
:param s:当前状态
:param a:当前行动
:param r:当前状态下当前行动环境所反馈的收益
:param s_:下一个状态
:param a_:下一个状态下Agent的行动
'''
r_ = np.log(r) * self.action_value[a]
q_predict = self.q_table.loc[s[0], a]
if s_[1]:
self.check_state_exist(s_)
q_target = pd.Series(max(float(r_), self.gamma * self.q_table.loc[s_[0], a_]),name='return_rate') # next state is not terminal
else:
q_target = r_ # next state is terminal
self.q_table.loc[s[0], a] = (self.q_table.loc[s[0], a] + self.alpha * (q_target - q_predict)).values[0] # update
pass
总结
以上就是TD算法的四个实现,因为强化学习需要根据环境需要对Agent内部的行动进行实现,所以目测这份代码的可移植性不是很高,稍微参考一下还可以,如果想直接用在其他地方建议还是别了。
现在也完成了强化学习的部分了,那么就到ELM和RL结合的地方了。但其实也没有什么东西,就是ELM将预测结果交给RL,RL根据多个ELM的预测结果进行交易学习,然后跑到生产中进行交易,这其实是水到渠成的事情。看心情吧,也不知道会不会写这个。
Quah K H, Chai Q. Maximum reward reinforcement learning: A non-cumulative reward criterion[J]. Expert Systems with Applications. 2006, 31(2): 351-359. ↩︎