摘要: 首先,已知最小二乘法的解后,推导出解的递推形式;然后通过一个简单的公式y=x+w的形式引入遗忘因子,并改写成递推形式,最后将遗忘因子引入带递推的最小二乘法中。

递推最小二乘法

对多组数据 $\vec{x}_i$ 和 $y_i$,满足

其中 $\vec{x}_i$ 是输入数据向量,$y_i$ 是输出数据标量。写成矩阵形式

其中(以3组数据、2个未知参数为例)

完整地写作(以3组数据、2个未知参数为例)

有$k$组数据时记作

(此时注意区分一些符号,例如 $\vec{y}_k$ 与 $y_k$,$X$ 与 $\vec{x}$ 等)
已知最小二乘法的解为

令 $P = (X^\mathrm{T}X)^{-1}$,
则加上递推后

同理可得

于是代入式(1)得到

其中倒数第3行到倒数第一行的过程

用到了下面的矩阵求逆公式及其引理

总结成递推公式得到

一些注意事项

  因为 $P=(X^\text{T}X)^{-1}$,$\vec{x}_1\vec{x}_1^\text{T}$ 一般很小,所以实际使用时一般把 $P_1$ 初始化为一个接近无穷大的单位阵。
  可以看出 $P$ 是个对称矩阵,如果因为数值计算的原因导致 $P$ 不严格对称时,代码里需要加一步:$P\leftarrow(P+P^\text{T})/2$。

引入遗忘因子

先考虑一个比较简单的情况

其中 $\theta$ 是要估计的参数,$w_n$ 是高斯白噪声,$x_n$ 是观测到的数据,总共有 $N$ 组数据,将 $N$ 组数据取平均得到 $\theta$ 的一个估计值为

写成递推形式为

例如,

现在对之前的值乘一个衰减系数 $\lambda\in(0,1)$,也就是

写成递推形式为

当 $n\rightarrow\infty$ 时,递推形式可以近似为

递推最小二乘中的遗忘因子

类似地,在最小二乘的误差 $S_i=||y_i-\vec{x}_i^\text{T}\vec\theta||^2$ 中加入遗忘因子,得到总误差(以3组数据为例)

完整的方程可以写作

类似地代入最小二乘的解式(1),并求式(2)(3)的递推形式

代入式(1)得到

总结成递推公式得到

与不带遗忘因子的公式相比,第3个式子 $\vec{\theta}_k$ 不变,前两个稍微有变化。

一个例子与代码

  对方程 $y=ax^2+bx+c$,输入多组数据,$x$ 取一些高斯噪声,以4组数据为例,

代码使用了自用的矩阵运算库zhnmat,见 自用的矩阵运算库zhnmat使用说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <cmath>
#include "zhnmat.hpp"
using namespace zhnmat;
using namespace std;
int main() {
constexpr double a = 0.5;
constexpr double b = 1.1;
constexpr double c = 2.1;
double U, V, randx;
double y, lambda=0.5;
Mat K;
Mat vx(3, 1);
Mat P = eye(3)*1e6;
Mat theta(3, 1);
for (int i = 0; i < 10; i++) {
U = (rand()+1.0) / (RAND_MAX+1.0);
V = (rand()+1.0) / (RAND_MAX+1.0);
randx = sqrt(-2.0 * log(U))* cos(6.283185307179586477 * V); // `randx`服从标准正态分布
randx *= 2;
vx.set(0, 0, randx*randx);
vx.set(1, 0, randx);
vx.set(2, 0, 1);
y = a*randx*randx + b*randx + c;
K = P * vx / (lambda + (vx.T() * P * vx).at(0, 0));
P = (eye(3) - K * vx.T()) * P / lambda;
theta = theta + K * (y - (vx.T() * theta).at(0, 0));
cout << "theta" << theta << endl;
}
return 0;
}