前言
终于要毕业了,毕业设计也做完了,我的毕设是《极限学习机和强化学习在单一资产交易中的应用》,本质上用以极限学习机为值函数逼近器的一类强化学习算法去对一个资产进行交易。既然毕设也做完了,大学生涯也要结束了,那在去工作之前将毕设的东西好好总结一下,也方便以后自己看。
算法基础
ELM
极限学习机ELM是一类Single-hidden Layer Feedforward Neural Network(SLFNs)算法,由Huang等基于 Moore-Penrose 广义逆的理论提出,主要针对SLFNs中存在的学习速率慢,迭代时间长,学习参数如学习步长、学习率需要人为提前设置等问题。与传统的神经网络学习算法相比,ELM只需要设置合适的隐层节点数,随机生成隐层所需所有参数,利用最小二乘法确定输出层权值。整个学习过程只需一步而无需多次更新隐层参数。正是因为ELM算法的快速学习能力以及较强的非线性逼近能力等特点,使得ELM在实际应用中受到了研究者们的青睐。例如ELM及改进算法被广泛应用于故障检测、时间序列预测、姿态识别、化学分析、医疗诊断、智能供电等领域。
如果说ELM有什么公式比较重要的话,那应该是
T
=
H
β
T=H\beta
T=Hβ和
β
=
H
†
T
\beta=H^\dag T
β=H†T。这里一个用以求得预测值
T
T
T,一个用来求解输出权重
β
\beta
β。需要注意
H
†
H^\dag
H†是
H
H
H的广义逆。
而RELM则是在标准ELM在ELM的二次型指标中引入
l
2
l_2
l2正则化项。省略掉推导过程的话,
β
\beta
β的求解公式将变成:
β
=
H
†
T
=
(
H
T
H
+
g
I
)
\beta = H^\dag T=(H^TH+gI)
β=H†T=(HTH+gI)
其中,
g
g
g为用于调整正则化作用的权重系数,
I
I
I为单位矩阵。
OS-ELM
在线学习的极限学习机和标准ELM之间的区别在于引入在线机制,这个机制的引入涉及到输出权重的更新,省略推导过程的话,当第k批样本到达时的
β
\beta
β更新方式:
β
k
=
β
k
−
1
+
P
k
H
k
T
(
T
k
−
H
k
β
k
−
1
)
\beta^{k}=\beta^{k-1}+P_kH^{T}_k(T_k-H_k\beta^{k-1})
βk=βk−1+PkHkT(Tk−Hkβk−1)
其中,命名
P
k
P_k
Pk为工作矩阵,为
H
H
H的转置与
H
H
H乘积的逆,初始化时,
P
0
=
(
H
0
T
H
0
)
−
1
P_0=(H^T_0H_0)^{-1}
P0=(H0TH0)−1
其更新方式为:
P
k
=
(
P
k
−
1
−
1
+
H
k
T
H
k
)
=
P
k
−
1
−
P
k
−
1
H
k
T
(
I
+
H
k
H
k
T
)
−
1
H
k
P
k
−
1
\begin{aligned} P_k & =(P^{-1}_{k-1}+H^T_kH_k)\\ & =P_{k-1}-P_{k-1}H^T_k(I+H_kH^T_k)^{-1}H_kP_{k-1} \end{aligned}
Pk=(Pk−1−1+HkTHk)=Pk−1−Pk−1HkT(I+HkHkT)−1HkPk−1
如果用标准的ELM进行学习,除了每次需要对已有的样本进行重新学习,这浪费了很多计算资源,而且需要记录下之前的样本的特征,当样本量增加到一定程度的时候,学习机都没来得及学习更新一批样本就来了,那学习机就根本学不下去了。但OS-ELM完成学习后由于无需对已有样本做后续工作,所以不需要记录下样本的特征,可以说节省了许多内存。
FOS-ELM
带遗忘机制的OS-ELM和OS-ELM之间的区别在于引入遗忘机制,用以消除过期数据对预测的影响。遗忘机制要求我们需要记录学习机内样本。FOS-ELM要求初始化时设定一个样本量,当超过这个量,学习机就会启动遗忘机制。如果未启动遗忘机制的话,FOS-ELM的
β
\beta
β的更新方式和OS-ELM一样。如果启动了遗忘机制,设待删除的样本为第
l
l
l批,对工作矩阵
P
k
P_k
Pk有:
P
k
=
(
∑
i
=
k
−
l
+
1
k
H
i
T
H
i
+
H
k
T
H
k
)
−
1
=
(
P
k
−
1
−
1
+
H
k
T
H
k
−
H
k
−
l
T
H
k
−
l
)
−
1
\begin{aligned} P_k&=(\sum\limits_{i=k-l+1}^{k}H^T_iH_i+H^T_{k}H_{k})^{-1}\\ &=(P^{-1}_{k-1}+H^T_kH_{k}-H^T_{k-l}H_{k-l})^{-1} \end{aligned}
Pk=(i=k−l+1∑kHiTHi+HkTHk)−1=(Pk−1−1+HkTHk−Hk−lTHk−l)−1
那么
β
\beta
β的更新方式为:
β
k
=
P
k
(
P
k
−
1
β
k
+
[
−
H
k
−
l
H
k
]
T
[
T
k
−
l
T
k
]
)
=
P
k
(
(
P
k
−
1
−
[
−
H
k
−
l
H
k
]
T
[
T
k
−
l
T
k
]
)
β
k
−
1
+
[
−
H
k
−
l
H
k
]
T
[
T
k
−
l
T
k
]
)
=
β
k
−
1
+
P
k
[
−
H
k
−
l
H
k
]
T
(
[
T
k
−
l
T
k
]
−
[
H
k
−
l
H
k
]
β
k
−
1
)
\begin{aligned} \beta^{k} & =P_{k}(P^{-1}_k\beta^{k}+\begin{bmatrix}-H_{k-l} \\H_{k}\end{bmatrix} ^{T}\begin{bmatrix}T_{k-l} \\T_{k}\end{bmatrix})\\ &=P_k((P^{-1}_k-\begin{bmatrix}-H_{k-l} \\H_{k}\end{bmatrix} ^{T}\begin{bmatrix}T_{k-l} \\T_{k}\end{bmatrix})\beta^{k-1}+\begin{bmatrix}-H_{k-l} \\H_{k}\end{bmatrix} ^{T}\begin{bmatrix}T_{k-l} \\T_{k}\end{bmatrix})\\ &=\beta^{k-1}+P_{k}\begin{bmatrix}-H_{k-l} \\H_{k}\end{bmatrix} ^{T}(\begin{bmatrix}T_{k-l} \\T_{k}\end{bmatrix}-\begin{bmatrix}H_{k-l} \\H_{k}\end{bmatrix}\beta^{k-1}) \end{aligned}
βk=Pk(Pk−1βk+[−Hk−lHk]T[Tk−lTk])=Pk((Pk−1−[−Hk−lHk]T[Tk−lTk])βk−1+[−Hk−lHk]T[Tk−lTk])=βk−1+Pk[−Hk−lHk]T([Tk−lTk]−[Hk−lHk]βk−1)
代码
以上就是三个算法最主要的一些公式,接下来模型的实现就直接上代码吧。同时为了开发的方便,我把三个模型写成了继承关系,其中RELM为OS-ELM的父类,OS-ELM为FOS-ELM的父类。
import numpy as np
from sklearn.preprocessing import OneHotEncoder
class RELM_HiddenLayer:
"""
正则化的极限学习机
:param x: 初始化学习机时的训练集属性X
:param num: 学习机隐层节点数
:param C: 正则化系数的倒数
"""
def __init__(self, x, num, C=10):
row = x.shape[0]
columns = x.shape[1]
rnd = np.random.RandomState()
# 权重w
self.w = rnd.uniform(-1, 1, (columns, num))
# 偏置b
self.b = np.zeros([row, num], dtype=float)
for i in range(num):
rand_b = rnd.uniform(-0.4, 0.4)
for j in range(row):
self.b[j, i] = rand_b
self.H0 = np.matrix(self.softplus(np.dot(x, self.w) + self.b))
self.C = C
self.P = (self.H0.H * self.H0 + len(x) / self.C).I
@staticmethod
def sigmoid(x):
"""
激活函数sigmoid
:param x: 训练集中的X
:return: 激活值
"""
return 1.0 / (1 + np.exp(-x))
@staticmethod
def softplus(x):
"""
激活函数 softplus
:param x: 训练集中的X
:return: 激活值
"""
return np.log(1 + np.exp(x))
@staticmethod
def tanh(x):
"""
激活函数tanh
:param x: 训练集中的X
:return: 激活值
"""
return (np.exp(x) - np.exp(-x))/(np.exp(x) + np.exp(-x))
# 回归问题 训练
def regressor_train(self, T):
"""
初始化了学习机后需要传入对应标签T
:param T: 对应属性X的标签T
:return: 隐层输出权值beta
"""
all_m = np.dot(self.P, self.H0.H)
self.beta = np.dot(all_m, T)
return self.beta
# 回归问题 测试
def regressor_test(self, test_x):
"""
传入待预测的属性X并进行预测获得预测值
:param test_x:被预测标签的属性X
:return: 被预测标签的预测值T
"""
b_row = test_x.shape[0]
h = self.softplus(np.dot(test_x, self.w) + self.b[:b_row, :])
result = np.dot(h, self.beta)
return result
# 分类问题 训练
def classifisor_train(self, T):
"""
初始化了学习机后需要传入对应标签T
:param T: 对应属性X的标签T
:return: 隐层输出权值beta
"""
if len(T.shape) > 1:
pass
else:
self.en_one = OneHotEncoder()
T = self.en_one.fit_transform(T.reshape(-1, 1)).toarray()
pass
all_m = np.dot(self.P, self.H0.H)
self.beta = np.dot(all_m, T)
return self.beta
pass
# 分类问题 测试
def classifisor_test(self, test_x):
"""
传入待预测的属性X并进行预测获得预测值
:param test_x:被预测标签的属性X
:return: 被预测标签的预测值T
"""
b_row = test_x.shape[0]
h = self.softplus(np.dot(test_x, self.w) + self.b[:b_row, :])
result = np.dot(h, self.beta)
result = [item.tolist().index(max(item.tolist())) for item in result]
return result
pass
class OS_DELM_HiddenLayer(RELM_HiddenLayer):
"""
正则化的在线学习的极限学习机
:param x: 初始化学习机时的训练集属性X
:param num: 学习机隐层节点数
:param C: 正则化系数的倒数
"""
def __init__(self, x, num, C=10):
super(OS_DELM_HiddenLayer, self).__init__(x, num, C=C)
# 回归问题 在线学习
def online_regressor_study(self, x, t):
"""
在线学习模块
:param x: 待学习训练集的属性X
:param t: 待学习训练集的标签Y
"""
if len(x.shape) == 1:
x.reshape(1, -1)
b_row = x.shape[0]
H_k = np.matrix(self.softplus(np.dot(x, self.w) + self.b[:b_row, :]))
self.P = np.linalg.pinv(self.P.I + H_k.H * H_k + b_row / self.C)
self.beta = self.beta + self.P * H_k.H * (t - H_k * self.beta)
# 分类问题 在线学习
def online_classifisor_study(self, x, t):
"""
在线学习模块
:param x: 待学习训练集的属性X
:param t: 待学习训练集的标签Y
"""
if len(t.shape) > 1:
pass
else:
t = self.en_one.transform(t.reshape(-1, 1)).toarray()
pass
self.online_regressor_study(x, t)
pass
class FOS_DELM_HiddenLayer(OS_DELM_HiddenLayer):
"""
:param x: 初始化学习机时的训练集属性X
:param num: 学习机隐层节点数
:param C: 正则化系数的倒数
带遗忘机制的正则化的在线学习极限学习机
"""
def __init__(self, x, num, C=10):
super(FOS_DELM_HiddenLayer, self).__init__(x, num, C=C)
def regressor_train(self, T):
"""
这里之所以重写train方法是因为启动遗忘机制时需要用到将要被删除的标签的值
:param T: 对应属性X的标签T
:return: 隐层输出权值beta
"""
self.T = T
return super(FOS_DELM_HiddenLayer, self).regressor_train(T)
def online_regressor_study(self, x, t):
"""
在线学习模块,这里在线学习直接启动了遗忘机制
:param x: 待学习训练集的属性X
:param t: 待学习训练集的标签Y
"""
self.more_than_k(x, t)
def less_than_k(self, x, t):
"""
在线学习模块,当学习机内部样本数量还没超过阈值时,依然使用在线学习的方式
:param x: 待学习训练集的属性X
:param t: 待学习训练集的标签Y
"""
super(FOS_DELM_HiddenLayer, self).online_regressor_study(x, t)
def more_than_k(self, x, t):
"""
在线学习模块,当学习机内部样本数量超过阈值时,启动遗忘机制
:param x: 待学习训练集的属性X
:param t: 待学习训练集的标签Y
"""
b_row = x.shape[0]
if len(x.shape) == 1:
b_row = 1
H_k = np.matrix(self.softplus(np.dot(x, self.w) + self.b[:b_row, :]))
T_lost = self.T[0: b_row]
H_lost = self.H0[0: b_row]
P_k = np.linalg.pinv(self.P + H_k.H * H_k - H_lost.H * H_lost + b_row / self.C)
mid2 = np.transpose(np.row_stack((-H_lost, H_k)))
mid3 = np.row_stack((T_lost, t))
self.beta = self.beta + P_k * mid2 * (mid3 - np.row_stack((H_lost, H_k)) * self.beta)
self.P = P_k
self.H0 = np.row_stack((self.H0[b_row:], H_k))
self.T = np.row_stack((self.T[b_row:], t))
pass
在这里我预先写了三个激活函数,通过对比发现softplus函数的精度是最高的,如果有需要其实是可以将激活函数替换成其他函数的。
总结
以上就是3个ELM的实现。说真的,一开始做这个课题的时候拼命在网上找看OS-ELM的实现一直没找到,其实是很烦的。但是烦过以后自己动手一点点实现它其实是很有成就感的。ELM做股价预测来说,预测的精度和现有的很多模型的精度是相当的,大概也是在0.55左右。ELM属于我的毕设的第一部分,它将作为我的交易Agent的值函数逼近器。下一部分将会写毕设的第二部分强化学习(RL)的一些实现,应该会尽可能在上班前完成的,加油!!!