☰
神经网络逼近理论:控制应用中的边界与工程落地
2026/10/22 20:02:23 网站建设 项目流程

做控制的人第一次接触深度学习时,最常问的问题倒不是“怎么训练”,而是“神经网络到底凭什么当万能逼近器用”。这个问题不是纯数学爱好。模型预测控制器拿神经网络做预测模型,反馈线性化控制器拿神经网络补偿没建模的非线性项,自适应控制器拿神经网络在线估计未知参数——这几条路全踩在同一块地基上:连续函数能被神经网络以任意精度逼近,也就是神经网络逼近理论基础。系列第二篇,我打算把这层地基彻底讲透:万能逼近定理保证了什么、不保证了什么;逼近误差一拆四块,每块对应什么改法;深度和宽度怎么权衡;这些结论又如何直接指导网络结构、激活函数和控制器鲁棒项的标定。适合正准备把深度学习接进控制算法、却对理论边界心里没底的工程师,也适合想弄清“为什么控制器里的神经网络模型总要留一条安全边界”的研究者。

1. 为什么控制回路里需要“函数逼近”

1.1 传统模型覆盖不了的真实系统

很多人刚开始做控制时,第一反应是“我先把被控对象的机理模型推出来”。这个思路在简单系统上没问题,但一到实际工程就露馅了。机械臂关节里的摩擦力矩,库仑摩擦加粘滞摩擦再叠一个Stribeck效应,表达式写出来跟天书一样,而且参数还随温度、湿度、磨损程度漂移;液压伺服阀的流量-压差关系天生非线性,还带死区和饱和;动力电池的SOC-OCV曲线随温度和老化不断变化,热工过程又是典型的变增益对象。你可以在仿真里把机理模型推得又细又漂亮,一上现场就被参数漂移打脸。

这种时候大家其实都明白:与其死磕物理方程,不如把“说不清楚的那部分”当成一个未知函数,用数据把它学出来。这个念头一起,就自然踏入函数逼近的领域。神经网络只是众多逼近工具之一,但因为它能处理多变量、高维度、强耦合的映射关系,而且自动求导方便,所以迅速成了控制方向的主流选择。

1.2 控制设计里的“未知函数”是怎么冒出来的

把一段连续系统写成状态空间形式并不难。常见写法类似 x_dot = f(x) + g(x)u,其中 x 是状态,u 是控制输入,f 和 g 分别代表系统的内部动态和输入通道增益。工程上真正麻烦的是,f(x) 精确表达式通常不存在,我们手里只有一个标称模型,甚至可能只是个在平衡点附近线性化出来的 A 矩阵和 B 矩阵。

于是问题就变成了:把标称部分拆出来以后,剩下所有“说不清、道不明”的动态,全部塞进一个未知项 Δf(x)。神经网络的作用,就是去学一个近似映射 f_hat(x) 来逼近这个 Δf(x)。这个方法不止用在正向模型里,逆模型前馈也需要,比如你想做前馈补偿,就得学习被控对象的逆动态;强化学习里的策略函数和值函数,本质上也是函数逼近问题。换句话说,凡是控制算法里出现“未知函数需要估计”,背后就是神经网络逼近理论在用劲。

注意:神经网络在这里被当作“函数逼近器”使用,而不是什么神秘黑箱。它做的是把一个多变量映射用参数化网络表示出来,然后通过数据把参数调准。理解了这层定位,后面很多东西就顺了。

1.3 为什么必须选神经网络,而不是查表、多项式或高斯过程

其实能逼近函数的工具不止神经网络。查表法最简单,但多变量表的存储量随维度爆炸,工业现场几个状态变量就把内存吃光了;多项式拟合整体性强,可稍微复杂一点的函数就会出现大幅震荡,你永远不知道两个采样点之间它干了什么;高斯过程在小样本上效果很好,可惜预测时要遍历训练数据,实时控制根本扛不住这个计算量。

神经网络的优势在于参数化紧致、表达能力分层递进、自动微分顺手。网络一旦训练完,前向推理就是一组矩阵乘法,计算时间非常确定,适合写进控制周期。而且神经网络的组合结构对高维函数有天然的压缩效率,同样规模参数往往比浅层方法更省。但注意,这些优势不是免费的,它建立在逼近理论的若干前提之上,比如激活函数要有一定条件、输入要落在训练覆盖的空间里。下一篇我会专门展开万能逼近定理的边界,这里先记住一句:神经网络是“够用”的函数逼近器,不是“万能”的预言机。

2. 神经网络逼近的数学内核与直觉解构

2.1 万能逼近定理:保证什么,又没保证什么

万能逼近定理通常这样表述:对一个定义在紧致集上的连续函数,只要激活函数满足温和条件(比如S形函数),单隐层前馈神经网络在隐层神经元足够多时,就能以任意给定的精度逼近这个函数。这就是“万能逼近”名字的由来。

但定理有三件事没保证,恰恰是工程上最要命的。第一,它只说存在一组参数,没说怎么找到;第二,它假设隐层神经元数量可以无限增加,实际你用几十个神经元能逼近成什么样,完全取决于你的结构选型;第三,紧致集三个字意味着有效范围有限,出了你训练数据覆盖的那片区域,网络输出完全不受控。我习惯拿“拼图”来类比:给你足够多的S形磁贴,理论上的确能拼出任意一条曲线,但没人告诉你怎么拼最快,而且拼接的“画布”大小已经被数据限定死了,出了画布,磁贴就散架了。

这对控制设计有一个直白的启示:神经网络模型只在训练覆盖的工况附近可靠,绝不可以在训练范围之外做无约束外推。所有控制算法里的“安全边界”“鲁棒项”,本质上都是给这层“画布”的边界打补丁。

2.2 逼近误差的四层结构:别让控制性能死在细节上

实际训练里我们说的“误差”,不是单一来源。我习惯把它劈成四层:结构误差、估计误差、优化误差、噪声误差。结构误差来自网络容量不够,表达不了目标函数的复杂度;估计误差来自训练样本太少或分布不均,没覆盖到关键区域;优化误差来自反向传播困在局部极小,没找到够好的参数;噪声误差来自传感器和环境扰动,这部分无法消除,只能靠数据量压制。

判断问题出在哪一层,方法很直接:训练完以后看残差。如果残差明显跟着输入变化、有规律地起伏,那多半是结构误差或输入特征缺项,先去加宽度深度、调整输入变量;如果残差像白噪声一样没有相关性,说明结构基本够了,剩下的是数据噪声和正则化强度问题。控制设计最终关心的是整体误差上界,也就是 Δf(x) = f(x) - f_hat(x) 在整个工作范围内的最大值,四层里任何一层没压住,都会直接反射到闭环性能上。

2.3 深度为什么往往比“巨型单隐层”划算

万能逼近定理本身讲的是单隐层网络,但实际工程几乎没人用巨型单隐层。原因在于,深度网络有一种“组合表达”的优势:一个复杂函数可以拆成若干个简单函数的嵌套,深层网络每一层都在构建更高一级的特征,最终用更少的参数达到同等逼近精度。从逼近论的角度说,有些函数用深层网络表示,参数规模随维度多项式级增长;用浅层巨宽网络则可能指数级爆炸。这就是为什么图像、语音、控制特征提取全都往深度走。

但控制场景和图像不一样,数据量通常有限,现场迭代成本高,网络一旦太深就面临过拟合和训练不稳定。我的经验是:非线性不强烈的系统,两层隐层足够;强非线性或者状态耦合明显的对象,先上三层,宽度从64或128起步,然后用验证误差来判断是否继续加深。非线性激活函数在这里是灵魂,因为线性网络叠再多层依然是线性映射,根本逼近不了非线性函数。控制场景选激活函数时,可微性和有界性比峰值性能更重要,这个我在3.2节专门讲。

3. 网络结构、激活函数与数据预处理的落地选择

3.1 输入输出维度怎么定才不浪费

神经网络逼近理论告诉你网络能逼近函数,但没告诉你输入该接什么。这步只能靠对象知识。控制任务里,输入一般选状态变量、历史控制量、参考信号,输出是你要逼近的未知动态或者下一时刻的预测值。不要盲目堆输入维度,维度越高,需要的数据量成倍增长,这就是“维度灾难”。

我习惯先做残差分析:先放一组最物理相关的输入,训练完看验证误差;然后逐步增加候选输入(比如增加控制延时阶数、某个辅助变量),如果验证误差明显下降,说明这个输入有用,留着;如果只是训练误差降而验证误差不降,说明是过拟合,果断删掉。输入变量之间相关性太高时,先用物理筛选或者PCA压一下维度,能省大量训练时间。

3.2 激活函数对照与初始化

激活函数的选择直接决定逼近能力和可微性。控制里尤其是要跟控制器求导联动时,可微性不是锦上添花,而是硬约束。

激活函数优点缺点控制场景建议
ReLU计算快、梯度传播好在0点不可微,容易死神经元隐层慎用,输出层可用
Leaky ReLU缓解ReLU死神经元依然不可微一般场景凑合
tanh光滑、有界、导数好求深层时梯度偏小隐层强烈推荐
swish光滑、非单调、训练稳计算稍贵隐层可用
RBF局部响应直观高维需要大量中心点低维小范围可用

除了最后一层,我几乎只用 tanh 或 swish,原因不是玄学,而是后向传播时的梯度比较平稳,且输出有界,这对控制闭环里做稳定性分析非常友好。最后一层通常用线性激活,让输出范围不受限,否则你学出来的 Δf_hat 天生被截断在某个区间里。

初始化方面,隐层权重用 Xavier 或者 He 初始化,偏置设0。有一个小技巧常被人忽略:输出层的偏置直接设为训练集目标值的均值,这样网络起步就在合理范围,收敛能明显加快。别小看这一步,某些场景下能省几百轮的训练。

3.3 数据归一化、时序切分与打乱陷阱

工程现场来的数据,状态量量纲各不相同,位置是米、速度是米每秒、温度是摄氏度。如果不归一化,网络前几层就会被大数值特征带着跑,训练极不稳定。正确做法是用训练集的均值和方差做归一化,把每个输入特征压到0均值、单位方差附近,预测结果再反归一化回来。这里有个坑:归一化的统计量必须从训练集算,不能用整个数据集算,否则会引入信息泄漏。

时间序列数据还有一个更隐蔽的陷阱:随机打乱。控制系统的样本是连续采样的,前一秒和后一秒强相关,如果像普通分类任务那样随机打乱再切验证集,相当于让模型“偷看”未来数据,验证误差会异常好看,但现场一跑就废。安全做法是把连续数据按时间切块,前80%训练、后20%验证,再留一段完全没参与训练的数据做最终测试。

3.4 防过拟合战术组合:贝叶斯正则化、早停、集成

控制场景样本量普遍不大,防过拟合比堆网络容量更重要。我试过最有效的一套组合是这样的:小样本回归优先试贝叶斯正则化,它把正则强度本身也当作随机变量去推断,典型实现就是MATLAB的trainbr,训练完基本不用手动去调 λ;如果用自己的深度学习框架,就老老实实加L2正则加早停,监控验证loss,一旦验证loss连续若干轮不降就停止;如果系统安全要求高,再训练多个不同初始化或不同数据子集的网络组成集成,用成员间的离散程度估计模型不确定性。

这么多年的经验有一个总结:训练阶段的指标永远只是参考,最终判断标准是闭环仿真和现场测试。你训练loss压到小数点后六位数,可能还不如一个残差分布更均匀但最大误差更小的模型。控制关心的是最坏情况,不是平均情况,这个思维要先扭转过来。

4. 逼近误差、鲁棒项与稳定性:理论与工程之间的桥

4.1 逼近误差是怎么进到控制律里的

神经网络模型不会自己产生控制信号,它只是个函数近似器。真正的问题在于,这个近似误差怎么影响闭环。

拿反馈线性化控制来举例。假设原系统是 x_dot = f(x) + g(x)u,我们手里只有神经网络学出来的 f_hat 和 g_hat,控制律写成 u = g_hat(x)^(-1)(-f_hat(x) + v),其中 v 是我们期望的闭环动态。代入原系统以后,会得到 x_dot = v + Δf(x),这里的 Δf = f - f_hat 就是逼近误差。如果 Δf 一点都不处理,闭环系统的实际响应就会偏离你设计的 v,严重时直接失稳。模型预测控制同理,预测模型有误差,滚动优化的结果就有偏差,约束也可能被突破。

所以工程上有一条铁律:神经网络模型上线前,必须对逼近误差的上界有估计。这个“上界”最后会变成控制器里的一个鲁棒补偿项。

4.2 在稳定性分析中为什么要“界住”误差

很多人问,做工程还要不要推Lyapunov稳定性?我的回答是,推不推,取决于你要交付什么。如果只是做个仿真Demo,那无所谓;如果这个控制器要跑在真实设备上,你至少得理解为什么程序里会多出那个“鲁棒项”。

在神经网络自适应控制的理论推导里,通常选一个Lyapunov函数 V,然后对时间求导,逼近误差 Δf 会作为扰动项出现在 V_dot 的边界里。为了证明系统稳定,需要把误差项“界住”,通常用 Young 不等式一类的手段,把交叉项放缩成可处理的形式,再通过设计权值更新律和鲁棒项补偿。最后得到的充分条件往往是:逼近误差有界 + 输入激励持续足够丰富 + 权值更新有界。这三个条件不是摆设,它们直接对应工程动作:误差有界靠模型验证和鲁棒项,持续激励靠激励信号设计,权值有界靠在线学习的死区和正则。

提示:你不必每次写控制程序都从头推一遍稳定性,但一定要理解“为什么控制器里要留一个鲁棒项”。这个理解能帮你少走很多弯路,特别是在参数标定时。

4.3 工程标定安全边界的三板斧

第一板斧是最朴素的:在验证集上算最大残差 e_max,控制器鲁棒项的幅值 ρ 取 e_max 的1.5到2倍。留出这1.5倍就是给未曾覆盖的工况和噪声余量。第二板斧是集成网络:训练多个模型,用它们的输出标准差近似置信区间,取 3σ 作为误差上界标定安全边界。第三板斧是给在线学习加死区:误差落在噪声带以内的样本不触发更新,防止系统受传感器噪声影响而持续抖动;同时监控残差,一旦短时间内误差暴增,立刻停止更新并回退到上一版模型。这三招组合下来,神经网络建模带来的不确定性基本上能压到一个可控范围。

5. 训练避坑:数据采集、优化器选择与常见故障排查

5.1 激励信号直接决定模型适用范围

神经网络逼近的适用范围是被数据画出来的。你拿小幅正弦扰动在平衡点附近扫一遍,训练出来的模型就只能在这个小邻域内工作,工况一偏移立刻精度跳水。想要模型覆盖控制器实际会遇到的整个工作范围,激励信号必须设计过。工业上常用的两个工具是伪随机二进制序列(PRBS)和叠加正弦扫频。PRBS能在很短时间内激励出丰富的高频分量,正弦扫频则能把各个频段的响应都扫一遍。两者叠加使用,模型学出来的动态特性才会宽频带、大范围有效。

5.2 优化器与训练策略

控制类的回归任务和图像分类不太一样,样本量小、目标函数平滑,优化器选择有自己的讲究。我保留了一个组合套路:先用Adam以1e-3左右的学习率热身两三百个周期,把模型拉到比较接近的位置,再切到L-BFGS这种二阶方法收尾。Adam的优势是前期鲁棒,不挑学习率;L-BFGS在接近最优时收敛快、精度高。梯度裁剪也建议加上,尤其是用tanh激活时,防止偶尔一个异常样本把梯度炸飞。损失函数里加L2正则,正则系数可以小,但不能没有。

5.3 常见问题速查表

现象排查方向解决建议
训练loss不下降输入/输出是否归一化、激活是否饱和、学习率是否过大检查归一化,降低学习率,换tanh/swish
训练发散出现NaN学习率过大、梯度爆炸、除零加梯度裁剪,降学习率,加L2正则
训练误差小验证误差大过拟合加正则/早停,加数据,缩网络
在线更新时模型发散死区设置不当、学习率过大加大死区阈值,限制更新步长
网络输出振荡模型对噪声过度敏感或激励不足输出平滑滤波,检查激励信号频带覆盖

5.4 深度学习框架选型

PyTorch在科研原型阶段最灵活,调试体验好,自动求导直观,我现在大部分离线训练都在它上面做;TensorFlow的部署生态成熟,尤其嵌入式终端的推理库齐全;JAX在科学计算和高性能训练上有优势,适合和数值优化联动;MATLAB的Deep Learning Toolbox和Simulink集成度高,做控制系统仿真验证最省事。选型标准只有一条:看你的控制器最终部署在什么环境里,别为了研究方便选了本地训练爽、部署要重写的框架。

6. 一个可以直接跑的函数逼近与控制器标定案例

6.1 目标函数与数据生成

下面用一个足够简单、又能复现核心思路的例子,演示从数据到网络再到误差标定的完整流程。假设我们要逼近的目标函数是 f(x) = sin(x1) + 0.5x2^2 + 0.1x1*x2,其中 x1 在[-2, 2]区间,x2 在[-1, 1]区间。这个函数可以理解为被控对象里“未建模动态”的抽象表达,我们的任务是让它当被控对象的一部分,用神经网络学出来。

6.2 网络定义与训练

直接上PyTorch实现。先生成2000个样本,加高斯噪声模拟传感器测量误差;然后搭一个两隐层、每层16个神经元的tanh网络,用Adam训练。

import numpy as np import torch import torch.nn as nn # 数据生成 np.random.seed(0) n = 2000 x1 = np.random.uniform(-2, 2, n) x2 = np.random.uniform(-1, 1, n) f = np.sin(x1) + 0.5 * x2**2 + 0.1 * x1 * x2 y = f + np.random.normal(0, 0.05, size=f.shape) # 模拟测量噪声 x = np.column_stack([x1, x2]).astype(np.float32) y = y.astype(np.float32).reshape(-1, 1) # 归一化 x_mean, x_std = x.mean(axis=0), x.std(axis=0) + 1e-8 y_mean, y_std = y.mean(), y.std() + 1e-8 x_norm = (x - x_mean) / x_std y_norm = (y - y_mean) / y_std # 切分 idx = np.arange(n) np.random.shuffle(idx) train_idx, val_idx = idx[:1600], idx[1600:] x_train, y_train = torch.tensor(x_norm[train_idx]), torch.tensor(y_norm[train_idx]) x_val, y_val = torch.tensor(x_norm[val_idx]), torch.tensor(y_norm[val_idx]) # 网络 model = nn.Sequential( nn.Linear(2, 16), nn.Tanh(), nn.Linear(16, 16), nn.Tanh(), nn.Linear(16, 1) ) opt = torch.optim.Adam(model.parameters(), lr=1e-2) loss_fn = nn.MSELoss() # 训练 for epoch in range(300): opt.zero_grad() pred = model(x_train) loss = loss_fn(pred, y_train) loss.backward() opt.step() if epoch % 50 == 0: with torch.no_grad(): val_loss = loss_fn(model(x_val), y_val) print(f"epoch {epoch}, train_loss {loss.item():.6f}, val_loss {val_loss.item():.6f}") # 误差评估 with torch.no_grad(): pred_val = model(x_val) * y_std + y_mean y_val_orig = y_val.numpy() * y_std + y_mean err = (pred_val.numpy().ravel() - y_val_orig.ravel()) print(f"验证集最大误差: {np.max(np.abs(err)):.4f}") print(f"验证集均方根误差: {np.sqrt(np.mean(err**2)):.4f}")

训练过程会看到验证loss和训练loss同步下降,说明没有明显过拟合。

6.3 误差结果分析与鲁棒项标定

跑完上面代码,验证集最大误差通常在0.1到0.2之间,均方根误差在0.05上下。这个最大误差 e_max 就是你给控制器标定鲁棒项的起点。假设控制器要克服这个未知项,在补偿律里加入鲁棒项,幅值 ρ 可以取 e_max 的1.5到2倍,也就是0.2到0.4。这个余量不是拍脑袋拍的,它同时覆盖了验证集之外可能出现的模型失配、传感器噪声的极端情况。

注意:实际项目中千万别只看训练好的网络在训练集上有多准,一定要用一段完全没有参与训练的连续时间数据做最终测试。我见过太多模型,验证集上误差都很小,一接真实设备就掉链子,原因基本出在数据泄漏或激励覆盖不足上。

6.4 闭环仿真前的验证习惯

模型训练完别急着往控制器里塞。先把验证集所有样本的残差画成散点图,横轴是某个关键状态量,纵轴是残差,看最大误差是否集中在某个特定工况角落。如果误差集中在某个区域,说明那块区域的数据不够或者网络表达不足,要针对性补数据。如果残差分布均匀且像噪声,说明模型质量合格,可以进入闭环仿真阶段。

7. 部署现场必须盯住的三个小细节

7.1 归一化参数与模型一起保存

离线训练好的网络,上线部署时最容易被忽略的就是归一化参数。模型结构、权重、偏置导出没问题,但归一化用的 x_mean、x_std、y_mean、y_std 如果没跟着模型一起打包,线上推理就是灾难。你在离线训练时顺手把这些统计量存成一个JSON或者一起放进模型文件,能避免很多现场事故。

7.2 在线更新一定要带死区

很多控制项目需要在运行中持续更新神经网络参数,以跟踪系统老化或工况漂移。但如果每个控制周期都拿实时误差回传更新权重,传感器噪声就会被模型照单全收,最终模型越调越离谱。正确做法是设置一个死区:误差绝对值小于某个阈值(比如传感器噪声带的两倍)时,不触发更新;只有误差明显超过噪声带时才开始梯度更新。这套机制听起来简单,但在现场救过我好几次。

7.3 模型版本管理与回滚

神经网络模型上线以后不是一劳永逸的。现场工况变化可能导致模型需要重新训练并更新,但任何一次更新都存在风险。我坚持的流程是:每次新模型上线前,必须先在离线仿真或者影子模式里跑一段时间,对比新模型残差和旧模型残差;如果新模型在验证集上的最大误差比旧模型差,哪怕平均误差更好,也不上线。控制领域最怕的是“边角时刻失稳”,最大误差这个指标比平均值更能代表安全性。

逼近理论讲再多,最后都要落到现场数据这条命脉上。我个人最大的体会是:万能逼近定理给了我们信心,但真正让神经网络在控制里立住的,是扎实的数据采集、严格的误差标定和克制保守的部署策略。这套组合拳打下来,你就能在“理论够用”和“工程可用”之间把神经网络控制稳稳落地。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询