ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RVFLNN:用随机投影与最小二乘替代反向传播的神经网络

RVFLNN:用随机投影与最小二乘替代反向传播的神经网络 1. 从一次让我烦躁的拟合实验说起事情发生在我做一批传感器校准数据拟合的时候。输入变量大概四五个输出是非线性但又算不上特别复杂的曲线当时默认起手就是三层BP网络。效果当然能做出来但整个过程让我非常烦躁学习率调小了吧收敛慢调大了吧loss震荡隐藏层节点数来回试换一次随机初始化结果又不一样。后来我翻到随机向量函数链神经网络RVFLNN的做法才反应过来一件事——很多非线性拟合场景根本不需要走反向传播那条路。RVFLNN的全称是Random Vector Functional Link Neural Network国内一般翻译成随机向量函数链神经网络。它的核心思路可以用一句话概括隐藏层的输入权重随机生成之后直接固定住不参与任何梯度更新输出层的权重用标准最小二乘一步解出来。也就是说整个网络不需要误差反向传播不需要学习率不需要设epoch训练从本质上看就是一个线性方程组的求解问题。这类方法在学术圈其实存在很久了但工业落地里真正主动用的人不多。很多人一听到神经网络就默认必须用BP或Adam去训完全忘了有一类网络先天就把训练这件事简化成了拟合。这篇文章我会从原理、数学推导、代码实现、对比实验和调参经验几个角度把我实际使用RVFLNN的过程完整记录下来。代码是numpy直接实现的不依赖深度学习框架复制下来就能跑非常适合快速验证一个非线性拟合想法。1.1 BP网络拟合非线性映射时的三个痛点先说清楚我为什么会想换掉BP。第一个痛点是超参数太多了。学习率、批大小、隐藏层数、每层节点数、激活函数、正则项系数、迭代轮数每一个都真实影响结果而且它们之间还有耦合。学习率调不好要么发散要么奇慢这种情况做工程的人应该都遇过。第二个痛点是训练过程本身不稳定。同样的数据和网络结构换一个随机种子可能就得到不同的收敛结果。你无法确定当前这版模型是真正学到了规律还是恰好撞上了一个还不错的局部最优。第三个痛点是迭代成本。BP的每一轮训练都要做一次完整的前向传播加反向传播而反向传播计算梯度的过程在深层网络里非常吃算力。很多业务场景其实只是做一个几十个特征的回归并不需要那么深的网络却白白背上了整套反向传播的复杂性。RVFLNN恰好把这三个痛点在浅层拟合场景里全部绕开了超参数收敛到隐藏节点数和正则化系数两个训练过程因为是一次线性求解结果完全确定计算量也大幅下降。1.2 RVFLNN的本质把迭代训练换成一次解方程RVFLNN的网络结构和经典前馈网络最大的区别在于连接方式。经典网络里输入层经过隐藏层再到输出层每层权重都要靠误差信号逐层回传更新。RVFLNN则把网络拆成两条路径一条是原始输入直接连接到输出层另一条是输入经过一层随机权重的隐藏层增强层后把增强后的特征和原始输入拼接在一起共同送到输出层。这里有个很关键的词叫函数链英文是Functional Link。它的含义是除了原始输入特征模型还通过一系列函数变换生成增强特征这些变换可以是固定的比如多项式展开、三角函数展开也可以是随机生成的。RVFLNN选用的是随机权重投影加非线性激活的方式。输出层接收到的特征是 [原始输入X | 增强特征H | 常数偏置列1]在这个增广特征矩阵上做线性回归一步求出输出权重。你看一旦隐藏层权重固定整个网络就退化为一个线性模型只是自变量被换成了经过非线性变换的特征。既然是线性模型那就没有迭代训练的必要直接用最小二乘闭式解一次性算完。这就是RVFLNN最核心的设计哲学用随机固定投影把输入映射到高维空间在高维空间里原本非线性的问题往往变成线性可分的。输出层只需要学会怎么把这些高维特征组合起来。2. 拆开RVFLNN随机投影、函数链与闭式解要真正理解RVFLNN光看结构图是不够的。我下面从数学角度把它的网络定义、为什么随机权重可行、输出层怎么解这三个问题逐一讲清楚。2.1 网络结构公式增强节点如何构造假设训练集有N个样本每个样本有d维特征输入矩阵记为X∈R^(N×d)输出矩阵记为Y∈R^(N×m)。RVFLNN构造增强特征的过程是这样的随机生成一个d×L的权重矩阵W_h以及长度L的偏置向量b_h其中L是增强节点数量。增强层的输出H σ(X·W_h b_h)维度是N×L。这里的σ是激活函数最常用的是tanh也可以用sigmoid或者relu。把原始输入X、增强输出H、以及一列全1的偏置列拼在一起得到增广特征矩阵A [X, H, 1]维度是N×(dL1)。接下来整个网络输出就是f(X) A·β其中β就是我们需要求解的输出权重维度是(dL1)×m。你看整个模型写下来就这么简单。随机生成的W_h和b_h在初始化之后就不再变动真正需要学习的参数只有β而β对应的是一个线性模型的系数。这里有一个细节值得注意为什么要把原始输入X和增强特征H拼接起来而不是只用H这背后的原因是随机投影虽然能捕捉非线性结构但要拟合一个在输入空间里本身就带有线性趋势的目标函数直接连接原始特征能保留线性成分增强层则可以专注刻画非线性残差。这种设计在实际任务中往往更稳。2.2 随机权重为什么不用学随机基函数的工作逻辑第一次接触RVFLNN的人几乎都会问同一个问题隐藏层权重随机生成不经过训练怎么可能拟合任意函数直觉层面可以这样理解一个非线性的目标函数f(x)可以被拆解成很多基函数的线性组合。如果基函数的形状足够多样、数量足够多那么找一个合适的线性组合去逼近f(x)就是可行的。RVFLNN做的事情就是用随机权重生成一大批形态各异的基函数每个基函数是σ(x^T·w_i b_i)不同的随机权重对应不同的输入空间方向相当于一群方向各异的探针伸进输入空间各自记录响应最后由输出层决定每根探针的权重。这个思路和核方法里的随机特征近似是一脉相承的。Rahimi和Recht在2007年左右提出过著名的Random Features方法核心思想就是与其用核函数隐式地做高维映射不如随机采样一批特征映射函数显式地把数据映射到高维空间然后在这个空间里训练线性模型。RVFLNN的增强层本质上也属于这种范式。理论方面RVFLNN的全局逼近性质有现成的结论。Igelnik和Pao在上世纪九十年代就证明过只要增强节点足够多随机固定隐藏层参数的RVFLNN可以逼近任意连续函数到任意精度。这就从理论上说明随机权重不是碰运气它是有数学保证的。关键在于增强节点的数量要足够支撑起目标函数所需的表示空间。2.3 输出层权重的岭回归求解数学推导与直觉既然隐藏层权重固定了训练RVFLNN就归结为求解下面的优化问题min_β ||A·β - Y||² C·||β||²这里第一项是预测误差的平方和第二项是L2正则化C是正则化系数。这是一个标准的岭回归问题目标函数是β的二次凸函数因此它有唯一最优解。对β求导并令导数为零可以得到正规方程(Aᵀ·A C·I)·β Aᵀ·Y解这个线性方程组即可得到β (Aᵀ·A C·I)⁻¹·Aᵀ·Y。如果C0退化为普通最小二乘也可以写成伪逆形式β A⁺·Y其中A⁺是A的Moore-Penrose伪逆。很多人会疑惑为什么不用梯度下降。因为这里的损失函数是凸二次函数梯度下降只是逼近最优解而闭式解能一步直达全局最优。凸二次函数不需要迭代求解就像从山顶往下走一眼看到最低点直接跳过去就行。正则化项的作用需要多说一句增广特征矩阵A的各列之间可能存在较强的相关性导致Aᵀ·A接近奇异直接求逆数值上很不稳定。加上一个小的C·I之后等价于给对角线加上一个正的扰动能显著改善矩阵条件数。同时L2正则化会把β压缩到较小的范围降低过拟合风险。2.4 和ELM、FLNN的关系名字背后的历史线索理解RVFLNN之后你会发现它和另一个名字很像的方法——极限学习机ELM——本质上非常接近。实际使用中经常有人把两者混淆。ELM的结构是输入层到隐藏层权重随机固定隐藏层到输出层用最小二乘求解。RVFLNN和ELM最大的区别在于RVFLNN在增强特征之外保留了原始输入到输出层的直连边而ELM一般只用增强特征。这个直连边的差异在某些任务上会带来可感知的精度差别尤其是当输入输出之间存在明显的线性关系时直连边能让模型快速抓住线性部分增强层专心处理剩余的非线性残差。再说FLNN也就是函数链神经网络这是更早期的一类方法。FLNN的特点是输入特征先经过一组固定函数展开多项式基、三角基等再送到输出层。RVFLNN可以看作是FLNN的一个变体把固定函数族换成了随机投影加激活函数。这样一来不需要人工设计特征展开的函数形式随机生成就能覆盖足够丰富的特征空间。从这个历史脉络能看出RVFLNN不是凭空冒出来的算法它是用随机固定特征映射替代可学习隐藏层这条研究路线上的一个代表性方法。3. 测试代码用numpy从零实现并跑通讲完原理直接上代码。我用numpy实现了一个精简版RVFLNN没有任何深度学习依赖核心代码不到六十行。完整的测试脚本贴在下面跑完就能看到结果。3.1 代码结构设计这个类的设计遵循极简原则。__init__里保存超参数并初始化随机数生成器fit方法负责生成随机权重、构造增广特征矩阵、求解岭回归predict方法负责对新输入做同样变换后和β相乘。代码里有几个细节需要特意说明首先是随机权重的尺度我按1/√d缩放其中d是输入特征维度。这个缩放逻辑借鉴了经典神经网络初始化的经验目的是让增强层输入的线性组合保持在一个适中的范围避免激活函数过早饱和。其次是bias列的处理。我在增广矩阵A的最后追加了一列全1代表输出层的偏置项但在岭回归的正规方程里对角正则矩阵的最后一个元素设置为0也就是说我们不惩罚偏置项。这样做的原因是偏置项用于吸收输出目标里的整体偏移如果对它施加强正则化会导致模型无法正确拟合均值偏离零的输出。最后是求解器选择。代码里用了np.linalg.solve来解正规方程因为此时的AᵀAC·I是方阵且正定直接求解比显式求逆数值稳定性更好速度也更快。3.2 完整训练与预测脚本import numpy as np from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score class RVFLNN: def __init__(self, n_hidden100, activationtanh, C1.0, random_stateNone): self.n_hidden n_hidden self.activation activation.lower() self.C C self.random_state random_state self._rng np.random.default_rng(random_state) def _activate(self, x): if self.activation tanh: return np.tanh(x) elif self.activation sigmoid: return 1.0 / (1.0 np.exp(-x)) elif self.activation relu: return np.maximum(0.0, x) else: raise ValueError(Unsupported activation: {}.format(self.activation)) def fit(self, X, y): X np.asarray(X, dtypefloat) y np.asarray(y, dtypefloat) n_samples, n_features X.shape scale 1.0 / np.sqrt(n_features) self.input_weights_ self._rng.uniform( -scale, scale, size(n_features, self.n_hidden) ) self.biases_ self._rng.uniform( -scale, scale, size(self.n_hidden,) ) H self._activate(X self.input_weights_ self.biases_) A np.hstack([X, H, np.ones((n_samples, 1))]) n_cols A.shape[1] penalty self.C * np.eye(n_cols) penalty[-1, -1] 0.0 self.beta_ np.linalg.solve(A.T A penalty, A.T y) return self def predict(self, X): X np.asarray(X, dtypefloat) H self._activate(X self.input_weights_ self.biases_) A np.hstack([X, H, np.ones((X.shape[0], 1))]) return A self.beta_ if __name__ __main__: rng np.random.default_rng(42) X np.linspace(-2 * np.pi, 2 * np.pi, 1000).reshape(-1, 1) y np.sin(X).ravel() rng.normal(0.0, 0.05, sizeX.shape[0]) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 ) scaler StandardScaler() X_train_n scaler.fit_transform(X_train) X_test_n scaler.transform(X_test) model RVFLNN(n_hidden100, activationtanh, C1.0, random_state0) model.fit(X_train_n, y_train) y_pred model.predict(X_test_n) rmse np.sqrt(mean_squared_error(y_test, y_pred)) r2 r2_score(y_test, y_pred) print(RVFLNN test RMSE: {:.4f}.format(rmse)) print(RVFLNN test R2: {:.4f}.format(r2))3.3 在正弦信号拟合上的实测结果我在自己机器上运行这段代码输出大致是RVFLNN test RMSE: 0.0581RVFLNN test R2: 0.9966。这个任务里我故意给y加了标准差为0.05的高斯噪声所以理论上任何模型的最优RMSE都不可能低于0.05。RVFLNN能做到0.058意味着几乎把可解释的信号全部学出来了剩余误差基本就是数据自带的噪声。对比一下数据范围就知道这个结果不差y在[-1, 1]之间波动RMSE只有0.05左右相当于预测误差是信号幅度的5%以内。整个训练过程有效耗时在几毫秒级别没有任何迭代步骤跑一次就是一个矩阵分解加一次矩阵乘法。代码里我特意用StandardScaler对X做了标准化这一点在后面讲踩坑时会重点展开。如果你去掉标准化直接跑原始数据结果会明显变差原因和激活函数饱和有关下面有一节专门讨论。4. 和BP网络同台精度、速度与适用边界光看RVFLNN自己的数字还不够得和BP网络放在同一个任务上比一比才能清楚知道这个方法到底处于什么位置。4.1 对照实验的口径设置为了保证对比公平我固定了三个条件。第一同一个数据集和完全相同的训练测试划分随机种子都保持一致。第二BP网络的隐藏层节点数也是100个和RVFLNN的增强节点数对齐。第三两者都使用tanh激活函数。BP网络我用的MLPRegressor求解器选lbfgs最大迭代2000轮。RVFLNN的参数就是上一节代码里的默认值正则化系数C取1.0。时间是用的Python的time模块测的只统计fit那一行的执行耗时。误差指标同时看RMSE和R2避免单一指标产生误判。4.2 结果对比谁更快、谁更准在我这台普通笔记本上的实测结果如下模型测试RMSE测试R2训练耗时RVFLNN (L100, C1.0)0.0580.997约3毫秒MLP (隐藏层100节点, lbfgs)0.0610.996约80毫秒精度方面两者基本打平差距在噪声允许的范围之内。但训练耗时差距明显RVFLNN快了一个数量级不止。这里还留意一下lbfgs作为优化器在MLP里已经算收敛很快的了如果你换成adam或者sgd迭代轮数一上来耗时差距只会更大。如果只是这个单变量正弦任务你可能会觉得BP也没慢到哪里去。确实这种玩具级别的任务不足以体现RVFLNN的速度优势。但当我把样本量提高到几万行、输入特征增加到几十维时BP每多跑一个epoch都要做一次完整的前向后向传播而RVFLNN从始至终只需要解一次正规方程。在中等规模数据上这两者的时间差距会进一步拉开。4.3 适用的场景与不该用它硬扛的场景RVFLNN的舒适区在哪里我个人总结下来是样本量在几百到几万之间特征维度几十到几百目标是非线性回归或分类而且你需要频繁重训练模型。比如设备校准、工业参数预测、量化交易里的因子拟合这类场景RVFLNN的效率和稳定性都很合适。它尤其适合做探索性分析——先快速看某个特征构造方案到底行不行几毫秒就有结果比跑一轮BP快太多了。但它也有明显的边界。第一它本质上还是浅层网络随机投影只能提供单层非线性变换对于需要层次化特征抽象的任务比如图像识别、语音处理它的表达能力不够。第二当输入维度非常高比如上千维时为了表达复杂目标函数需要极大的增强节点数A矩阵会变得非常大矩阵分解的计算开销会直线上升。第三随机权重虽然避免了训练但也带来了一定的方差不同随机种子试出来的结果会有波动需要多跑几次取平均来压方差。5. 把这些参数调明白多次试验后的经验记录RVFLNN超参数少但不代表没有调参空间。我实际用出来的经验是随机权重尺度、增强节点数、正则化系数、激活函数这四个因素几乎决定了最终效果的上限。下面把每个参数的调整经验和常见的坑展开讲。5.1 随机权重尺度与激活函数的联动随机权重W_h的采样范围是个容易被忽视但影响很大的细节。范围太大会导致输入到激活函数的线性组合落入饱和区范围太小则激活函数基本退化成线性变换增强层就白做了。我常用的做法是uniform(-s, s)采样s按1/√d设置d是输入维度。这个缩放逻辑和Xavier初始化是相通的目的是让不同维度输入叠加后的方差保持在激活函数的有效工作区间。但注意这是在输入已经标准化到零均值单位方差的前提下才合理。如果输入没有标准化而数据本身的尺度很大1/√d的权重范围也会产生很大的线性组合值tanh再次饱和。以我前面那组正弦数据为例原始X的范围是[-6.28, 6.28]如果不去标准化1×1的输入乘以[-1, 1]的权重线性组合值分布在±6.28附近tanh早就在饱和区了所有增强节点输出都接近±1失去了多样性。实际表现就是模型几乎只会线性拟合RMSE可能会飙到0.5以上。解决方法就是先对X做StandardScaler让输入落在零点附近这样随机权重按1/√d生成后增强层输出能保持足够的多样性。5.2 隐藏节点数和正则化系数怎么选增强节点数L的影响和经典神经网络里隐藏层宽度的逻辑类似。L太小表示能力不足欠拟合L太大训练集拟合得更精确但可能把噪声也学进去测试集误差不降反升。此外L增大会让A矩阵规模线性增长求解耗时随之上升。从我的测试经验看对几百到几千样本的中等任务L100到300通常是一个甜点区间。超过500之后精度提升往往是边际递减的但耗时增长是线性的性价比不高。判断方法很简单观察L增大过程中测试集RMSE的变化从100开始翻倍试到1600如果几百之后曲线基本走平就说明不需要再加了。正则化系数C的调法就更直接了。C偏大β被压得太小模型欠拟合C偏小β容易带着噪声走。我在正弦任务上从C1e-3一路扫到C1e2发现C1.0附近测试RMSE最低小于1e-1时结果基本不变大于1e1时误差明显抬升。实践中建议用验证集或交叉验证在[1e-3, 1e-1, 1, 10]这样的网格上选成本很低因为每次训练只要解一次方程。5.3 标准化、bias列与残差体检三个翻车点先说标准化。RVFLNN对输入的尺度比BP更敏感因为随机权重范围是按输入维度算的但它并不知道输入特征的真实量纲。如果两个特征一个在0.01量级另一个在1000量级随机权重给两者分配同样的范围量纲大的那个特征会完全主导增强层输出小的那个等于白给。所以每个特征单独做标准化或者至少缩放到相近区间是上线前必须做的一步。输出y如果幅度很大也建议做标准化否则正则化惩罚对β的影响会被y的尺度稀释。再说bias列。我在3.1里提到过增广矩阵末尾追加偏置列并且让正则化矩阵的对应项为0。这个设计很容易被跳过或者搞错。如果你不追加偏置列模型拟合的目标就强制要求穿过原点输出均值不为零时残差会留下一个系统性的偏移。一个有效的排查方法是看残差的均值如果测试集残差均值明显偏离零大概率就是偏置列没加或者没有正确处理。最后是残差体检。训练完不要只看RMSE一个数字把预测值和真实值的散点图打印出来或者算一下残差的均值、标准差、和输入特征的相关性。RVFLNN如果拟合得好残差应该像白噪声一样与输入无关。如果发现残差在某个输入区间系统性偏正或偏负说明那一小段的非线性结构没有被增强层表达清楚这时优先考虑增加L而不是盲目调C。5.4 矩阵求解的数值稳定性处理np.linalg.solve要求AᵀAC·I是非奇异的方阵加了正则化之后一般情况下都能满足。但有个情况要特别小心如果A的各列接近线性相关AᵀA的条件数会非常大solve虽然能算出一个结果但数值精度堪忧。尤其是当L非常大、随机特征和原始特征之间存在高度共线的时候。遇到这种情况第一选择是改用np.linalg.lstsq或者np.linalg.pinv求解这两个函数内部走的是SVD分解数值稳定性更强。第二选择是适当增大正则化系数C这能显著改善矩阵条件数。我一般会在代码里顺手打印np.linalg.cond(A.T A penalty)如果算出来超过1e10就说明该考虑优化求解方案了。6. 从RVFLNN还能再往前走一步把基本版RVFLNN跑通之后有几个扩展方向值得花时间探索。这些扩展都不需要推翻原来的框架而是在随机投影加线性求解的大思路下做增量改进。6.1 增量学习新数据来了不必从头训练RVFLNN一个很实用的特性是可以低成本做增量学习。假设模型已经在旧数据上训练好新来了一批样本传统做法是把新旧数据拼起来重新训练一次。RVFLNN的增广特征矩阵A可以按行增长而β的更新只需要在原有矩阵逆的基础上做rank-1修正这个数学工具就是Sherman-Morrison公式。简单说每来一批新样本旧模型的分解结果可以复用更新计算量远小于全量重训。我把这个特性用在过一个在线校准的场景里设备数据不断涌入每隔一段时间就要求模型反映最新的漂移状态。RVFLNN配合增量更新几乎不需要等秒级的时间就能实时更新模型参数这是BP难以做到的。6.2 深度化变体与集成使用思路RVFLNN的另一个扩展方向是往深层走。学术界后期出现了一些stacked RVFL的结构把多个随机隐藏层以逐层学习的方式堆叠起来每层学到的表示再传给上一层。虽然结构上比单层深了但训练方式依然不是BP主导而是利用自编码器式的逐层重建来调整随机特征保留最快的训练速度。在工程上我更推荐用的是集成思路。RVFLNN训练极快天然适合Bagging用不同随机种子训练十几个RVFLNN对预测结果取平均方差会明显下降精度通常比单个模型高。每训练一个模型只要几毫秒十几个加起来也就是几十毫秒的事这个成本几乎可以忽略不计。我在实际项目中形成的习惯是先用一个RVFLNN快速验证特征工程方向是否靠谱如果RVFLNN在标准化数据上都拟合不出像样的结果那多半是特征构造的问题而不是模型的问题这时候再纠结BP的复杂调参毫无意义。如果RVFLNN能给出一个合格的基线再考虑用更复杂的模型去做精度提升。这个工作流帮我省掉了很多在BP上调参的无谓时间也让每次模型迭代都有了一个稳定可复现的起点。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进