递推最小二乘参数辨识偏差根源与遗忘因子调参实践

简介:面向控制系统、信号处理与机器学习方向的学习者与工程人员,这份资源提供最小二乘类参数辨识的MATLAB实现,重点解决如何对稳定系统进行在线参数估计与辨识的问题,适合已具备一定MATLAB基础、希望动手复现递推算法的读者。压缩包内共1个docx文档,约78KB,以文字说明配合完整程序代码的形式呈现,便于边读边在MATLAB中运行验证。资源已有2161人学习,说明其在参数辨识入门实践中具有一定参考价值。内容围绕一个极点位于单位圆内的稳定系统展开,先产生500点M序列作为输入并叠加白噪声扰动,再用递推最小二乘算法估计参数,定义估计值Theta、估计方差P与增益矩阵K完成迭代更新,并绘制参数估计过程曲线;随后改用渐消记忆法,将增益与方差更新语句中的系数调整为0.998,对比两种算法的辨识精度差异。读者可据此理解递推最小二乘与渐消记忆法的实现细节、参数初值设置及可视化分析方法。

1. 递推最小二乘:为什么参数明明能估出来,却总差那么一点

跑过系统辨识的人大概都遇到过这种玄学现象:递推最小二乘(RLS)跑完五百个点,四条参数曲线稳稳地收敛了,可收敛到的位置跟真值就是差一截,不偏不倚地卡在那儿。你把噪声方差调小、把数据量加大,它还是差。这不是代码写错了,也不是数值精度不够,而是算法本身在“稳态”下的一种固有行为。这份 MATLAB 程序把这件事完整地摆了出来:先用标准 RLS 辨识一个二阶系统,四条参数曲线收敛但带偏差;再把增益和协方差更新里的那个1换成0.998,曲线立刻贴到真值上。一个数字的改动,背后是遗忘因子、协方差矩阵、增益矩阵三者联动的机制。搞懂这个“差一点”从哪来、又为什么能被0.998抹掉,比会调这段代码重要得多。

2. 系统与数据:辨识问题是怎么被“造”出来的

2.1 差分方程决定了待估参数的物理含义

程序辨识的对象是一个二阶差分方程:

Z(k+2) = 1.8*Z(k+1) - 0.9*Z(k) + 0.75*u(k+1) + 0.5*u(k) + v(k)

把它整理成“当前输出 = 历史输出与输入的线性组合”的形式,待估参数就是四个:两个自回归系数a1 = -1.8、a2 = 0.9(注意程序里数据生成用的是1.8和-0.9,而回归向量里取的是-z(i-1)、-z(i-2),符号约定要对齐),两个输入系数b1 = 0.75、b2 = 0.5。这个系统的特征方程根落在单位圆内,所以它是稳定的——稳定这一点很关键,因为只有稳定系统,输出序列才不会发散,递推过程里的h向量才不会把数值撑爆。

常见的实现思路是先把模型写成线性回归形式z(k) = h(k)' * θ + v(k),其中h(k) = [-z(k-1); -z(k-2); u(k-1); u(k-2)],θ就是那四个待估参数。程序里h=[-z(i-1);-z(i-2);M(i-1);M(i-2)]正是这个结构。理解这一点,后面所有矩阵运算才有落脚点:RLS 本质上是在解一个带递推更新的加权最小二乘问题,而不是在“拟合曲线”。

2.2 M 序列为什么比随机输入更适合做辨识激励

程序用 9 级移位寄存器加异或反馈生成 500 点 M 序列:

x=[0 1 0 1 1 0 1 1 1]; % 寄存器初值,不能全零 n=503; N=500; for i=1:n temp=xor(x(4),x(9)); % 第4位与第9位异或作为反馈 M(i)=x(9); % 输出取末位 for j=9:-1:2 x(j)=x(j-1); % 整体右移 end x(1)=temp; % 反馈位填入首位 end

M 序列(最大长度伪随机二进制序列)的价值在于它的自相关函数接近冲激函数,频谱足够宽,能持续激励系统的所有模态。如果输入是常数或者缓慢变化的信号,回归向量h在各次递推之间高度相关,协方差矩阵P会迅速退化,参数就“推不动”了。这是辨识里最常见的翻车点之一:数据看着不少,但信息量不够。M 序列用确定性的方式造出了近似白噪声的激励效果,而且可复现,调试时不会因为换一组随机数就得到完全不同的收敛轨迹。

提示:移位寄存器的初值不能全零,否则异或反馈恒为零,序列退化成全零,激励就没了。程序里给的[0 1 0 1 1 0 1 1 1]是一个合法非零初值。

2.3 白噪声与观测序列的生成顺序

噪声用v=randn(1,N)生成,方差为 1。观测序列的递推是:

z=zeros(N+2,1); z(1)=-1; z(2)=0; % 两个初值,对应二阶系统的历史状态 for i=3:N+2 z(i)=1.8*z(i-1)-0.9*z(i-2)+0.75*M(i-1)+0.5*M(i-2)+v(i-2); end

这里有个容易看漏的细节:z的长度是N+2,而v只有N个点,索引上v(i-2)刚好把噪声对齐到z(3)到z(N+2)。初值z(1)=-1、z(2)=0是人为设定的,它们会让前若干步的回归向量带有“初值污染”,这也是为什么递推从i=3开始、并且初期参数曲线会有明显抖动。真实系统里初值往往未知,工程上一般让算法先“热身”一段再开始记录估计值,或者给P一个较大的初值让算法快速摆脱初值影响。

3. 递推最小二乘的核心:增益、协方差与那个“1”

3.1 三个矩阵各自在干什么

RLS 每次迭代只做三件事:算增益K、更新参数Theta、更新协方差P。程序主体是:

P=100*eye(4); % 协方差初值,表示“我对初值很不确定” Theta(:,1)=[1;1;1;1]; % 参数初值,随便给 K=[1;1;1;1]; for i=3:N+2 h=[-z(i-1);-z(i-2);M(i-1);M(i-2)]; K=P*h*inv(h'*P*h+1); % 增益:新息该被信任多少 Theta(:,i-1)=Theta(:,i-2)+K*(z(i)-h'*Theta(:,i-2)); P=(eye(4)-K*h')*P; % 协方差收缩 Pstore(:,i-1)=[P(1,1),P(2,2),P(3,3),P(4,4)]; end

P是参数估计的不确定性度量,初值取100*eye(4)意味着“我几乎不相信自己给的初值”,所以前期增益K很大,参数被新数据猛拉。h'*P*h是预测方差,+1是观测噪声方差(这里噪声方差恰好为 1)。K的物理含义是:新息z(i)-h'*Theta里有多少比例该归因于参数误差、多少归因于噪声。噪声越大,K越小,算法越“保守”。

Theta的更新是标准的“旧估计 + 增益 × 新息”。新息就是当前观测与基于旧参数预测值之差,这是整个递推的信息来源。P的更新(I - K*h')*P让协方差随数据积累不断收缩,表示参数越来越确定。

3.2 稳态偏差从哪来:增益趋于零,算法“停止学习”

标准 RLS 在噪声方差恒定的假设下,随着迭代进行,P会单调收缩趋向零矩阵,K也随之趋零。这意味着算法后期几乎不再吸收新信息,参数被“冻结”在一个由早期数据和噪声共同决定的位置上。如果模型结构完全匹配、噪声真是零均值白噪声,这个冻结位置理论上应无偏;但有限样本下,早期噪声的影响被永久固化,加上初值污染,最终就表现为曲线收敛但带一个固定偏差。这就是程序里“辨识结果会与其精确值始终存在偏差”的机制解释——不是算错了,是算法把历史噪声当成了确定信息存进了P的收缩过程里。

3.3 渐消记忆:把1换成0.998到底改了什么

程序给出的修改只有两处:

K=P*h*inv(h'*P*h+0.998); % 原为 +1 P=(eye(4)-K*h')*P/0.998; % 原为 *P

0.998就是遗忘因子 λ。它的作用是在每次迭代时把协方差矩阵“放大”一点(除以 λ 等价于乘以略大于 1 的数),抵消P的自然收缩。这样P不会趋于零,K在后期仍保持一定幅度,算法持续对最新数据敏感,早期噪声的影响被逐步“忘掉”。λ 越接近 1,记忆越长、越平滑但偏差残留越久;λ 越小,跟踪越快但对噪声越敏感。0.998对应约 500 步的有效记忆窗口,恰好和 500 点数据量匹配,所以曲线能贴到真值上。

注意:遗忘因子不是越小越好。λ 太小会让参数估计在噪声驱动下剧烈波动,甚至发散。工程上常取 0.95 到 0.999,具体看系统时变速度和噪声水平。

4. 从公式到代码:几个必须对齐的实现细节

4.1 索引偏移与参数存储的对齐

程序里Theta(:,i-1)存的是第i步的估计,而i从 3 开始,所以Theta第一列对应i=2的初值状态。Pstore同理。这种“错一位”的写法在手工推导时极易搞混,导致画图时横轴和参数曲线对不上。常见的做法是显式维护一个迭代计数器k,让Theta(:,k)与数据点z(k+2)严格对应,避免后期排查时把索引问题误判成算法问题。

4.2inv的使用与数值稳定性

inv(h'*P*h+1)在这里是标量求逆(h'*P*h是 1×1),所以没问题。但如果推广到多输出系统,h'*P*h变成矩阵,直接inv在条件数差时会放大误差。更稳的写法是用左除K = P*h/(h'*P*h+λ),MATLAB 内部会选合适的分解。这是从这段教学代码走向工程代码时第一个要改的地方。

4.3 参数初值与协方差初值的搭配

Theta初值给[1;1;1;1],真值是[-1.8;0.9;0.75;0.5],差得很远;但P=100*eye(4)很大,所以前几十步就能拉回来。如果P给小了(比如eye(4)),初值又给错,收敛会非常慢甚至看起来“卡住”。这两个初值必须成对考虑:对初值越没把握,P就该越大。这是新手最容易忽略的搭配关系。

5. 避坑与排查:辨识跑不出结果时先看这几处

5.1 曲线收敛但偏差大,调大数据量也没用

现象:参数曲线早早收敛,位置偏离真值,增加N后偏差依旧。原因:标准 RLS 的P已收缩到接近零,增益消失,算法停止学习,偏差被固化。解决:引入遗忘因子,把+1改为+λ、*P改为/λ,让协方差保持下限;或者改用带协方差重置的变体。

5.2 参数曲线剧烈震荡甚至发散

现象:Theta上下大幅跳动,Pstore对角线不降反升。原因:遗忘因子过小,或输入激励不足导致h'*P*h接近零、增益被异常放大。解决:先把 λ 调回接近 1,检查 M 序列是否真的非零且周期足够;确认h的符号约定与数据生成方程一致,符号反了会让回归完全错位。

5.3 前几十步抖动严重,后期才平稳

现象:曲线开头一段乱跳,之后才收敛。原因:z(1)、z(2)的人为初值与真实系统状态不符,前若干步的回归向量带污染;同时P初值大,增益大,把污染放大。解决:丢弃前若干步的估计结果再画图,或让系统先空跑一段再开始记录;也可适当减小P初值换取更平滑的起步。

5.4 画图时参数曲线与真值线对不上号

现象:图上四条估计曲线和四条参考线交叉错位,看不出谁对应谁。原因:Theta的行顺序是[a1;a2;b1;b2],而参考线画的是-1.8, 0.9, 0.75, 0.5,符号和顺序必须严格对应;另外i=1:N+1的横轴与Theta列数要核对。解决:在plot前打印size(Theta)和参考值向量,逐行核对,别凭记忆对号。

5.5 换一组噪声就得到完全不同的结论

现象:每次运行结果差异明显,无法判断算法好坏。原因:randn未固定种子,单次实验的随机性被误读为算法特性。解决:在生成噪声前加rng(固定值),做多次蒙特卡洛取平均,再比较标准 RLS 与渐消记忆 RLS 的偏差和方差,结论才站得住。

6. 设计取舍:遗忘因子是“后悔药”,但剂量要算清楚

这段程序最值得琢磨的地方,不是它教会了 RLS 怎么写,而是它用一个数字展示了辨识算法里一对根本矛盾:记忆与跟踪。标准 RLS 的记忆是无限的,它把历史上每一个噪声样本都当成永久证据存进协方差里,于是估计越来越“稳”,也越来越“僵”。遗忘因子本质上是一剂后悔药——让算法有权逐渐不信任旧数据。但药量必须和数据的有效信息量匹配:λ=0.998 对应约 500 步记忆窗口,正好覆盖这段 500 点数据,所以效果立竿见影;如果数据只有 100 点却用 0.998,旧信息忘得太慢,偏差照样残留;如果数据有 5000 点还用 0.998,窗口太短,参数会在噪声里来回晃。

我一般会这么理解这三者的关系:P是算法对“自己有多不确定”的自我认知,K是这种认知转化为行动的比例,λ 则是强行给这种认知设一个下限,防止它自我封闭。三者任何一处失衡,表现都是曲线不对——要么僵、要么抖、要么飘。所以调试辨识程序时,我习惯先固定随机种子、跑一遍标准 RLS 看稳态偏差,再扫几个 λ 值看偏差与波动的权衡曲线,最后才去动初值和数据长度。从那以后我每次做参数辨识,都会先把遗忘因子和有效记忆窗口的对应关系算一遍再下手,而不是凭感觉填一个 0.99。希望帮到你。

本文还有配套的精品资源,点击获取