ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RBFNN源码实战:从K均值聚类到最小二乘预测的完整拆解

RBFNN源码实战:从K均值聚类到最小二乘预测的完整拆解 简介这份资源面向机器学习入门者与需要做非线性数据预测的开发者提供径向基神经网络RBFNN的完整Python实现。压缩包共8个文件约6KB包含2个py脚本、4个npy参数文件和2个csv数据集训练脚本负责学习并保存中心点、宽度向量等模型参数测试脚本加载模型对测试集预测并输出MSE、RMSE等误差指标npy文件存储训练后的网络参数csv文件则分别提供训练与测试数据。已有407人学习下载。读者可借此理解RBFNN从数据预处理、网络构建、参数训练到预测评估的全流程掌握高斯径向基函数、K均值确定中心点、梯度下降优化等关键知识点并直接运行代码复现结果、对比误差指标为函数逼近与时间序列分析等场景提供可复用的实践模板。1. 拿到 RBFNN.rar 之后这份源码到底能不能直接跑出预测结果很多人第一次接触径向基神经网络RBFNN是在做小样本非线性预测任务的时候。线性回归拟合不动BP 网络又容易过拟合调参调到怀疑人生这时候 RBFNN 就成了一个折中选项。它结构简单隐藏层用高斯函数做局部响应输出层是线性加权训练速度快对小样本仿真数据特别友好。这次拆的这份 RBFNN.rar里面包含 RBFNN.py 训练脚本、test.py 预测脚本、train.csv 和 test.csv 两份数据集以及训练后生成的 b1.npy、b2.npy、w1.npy、w2.npy 四个参数文件。它解决的核心问题很明确给你一套能跑通的 RBFNN 训练加预测流程不用从零推导公式直接看代码怎么把中心点、宽度、权重一步步算出来并保存成 .npy 文件。适合谁适合已经装好 Python 和 NumPy、想拿一份能复现的源码来理解 RBFNN 实际工程实现的人也适合需要快速搭一个非线性预测基线、再对比其他模型的从业者。但要注意这份代码不是开箱即用的工业级框架数据格式、参数初始化、归一化处理都有它的脾气直接换自己的 csv 大概率会翻车。下面按「先搞懂它怎么跑 → 再动手复现 → 最后避开那几个血泪坑」的顺序拆开讲。2. 拆开 RBFNN.py中心点、宽度、权重到底怎么算出来的2.1 从 train.csv 到隐藏层K 均值选中心点的实际逻辑RBFNN 最核心的一步是确定隐藏层神经元的中心点。常见做法有两种一种是随机从训练样本里挑另一种是用 K 均值聚类。这份源码走的是 K 均值路线因为聚类得到的中心点更能代表输入空间的分布后续高斯函数的局部响应也更合理。打开 RBFNN.py你会看到它先读 train.csv把特征列和标签列分开然后对特征做归一化或者标准化处理。这一步很关键因为高斯函数对输入尺度敏感如果某个特征量纲是几千另一个是零点几宽度参数根本没法统一设置。代码里通常会有一个kmeans函数或者直接调用 NumPy 手写聚类循环。聚类的簇数就是隐藏层神经元个数这个数不是固定的需要根据数据复杂度调。比如你的训练集只有几百条、特征维度十几维簇数设 20 到 50 之间比较常见设太少欠拟合设太多过拟合且训练变慢。聚类完成后每个簇的中心点存成一个向量所有中心点拼成矩阵最终保存为 b1.npy 或者类似的中心点文件。这里有个细节源码里 b1.npy 和 b2.npy 分别对应什么需要看代码里np.save的顺序。通常 b1 是隐藏层中心点b2 是输出层偏置但不同作者习惯不同一定要对着代码确认别凭文件名猜。import numpy as np import pandas as pd # 读取训练数据假设最后一列是标签 data pd.read_csv(train.csv) X data.iloc[:, :-1].values y data.iloc[:, -1].values # 归一化按列做 min-max避免量纲差异影响高斯宽度 X_min X.min(axis0) X_max X.max(axis0) X_norm (X - X_min) / (X_max - X_min 1e-8) # K 均值聚类确定中心点k 为隐藏层神经元个数 def kmeans_init(X, k, max_iter100): # 随机选 k 个样本作为初始中心 idx np.random.choice(len(X), k, replaceFalse) centers X[idx].copy() for _ in range(max_iter): # 计算每个样本到各中心的距离 dists np.linalg.norm(X[:, None, :] - centers[None, :, :], axis2) labels np.argmin(dists, axis1) new_centers np.array([X[labels i].mean(axis0) if np.any(labels i) else centers[i] for i in range(k)]) if np.allclose(new_centers, centers): break centers new_centers return centers k 30 # 隐藏层神经元个数需要根据数据调整 centers kmeans_init(X_norm, k) np.save(b1.npy, centers) # 保存中心点上面这段代码展示了从读取 csv 到聚类保存中心点的完整链路。参数说明k是隐藏层节点数直接决定模型容量max_iter是聚类最大迭代次数一般 100 足够归一化用 min-max 而不是 z-score是因为高斯函数定义域在实数域min-max 能把输入压到 [0,1]宽度参数更容易设。逻辑上聚类完成后每个中心点代表输入空间的一个局部区域后续计算高斯响应时离中心越近的样本激活值越大。2.2 宽度参数与输出权重从高斯响应到最小二乘求解中心点确定后下一步是算宽度参数。宽度通常用每个簇内样本到中心点距离的均值或者标准差来表示源码里可能会存成 w1.npy 或者直接写在 b2.npy 里。宽度决定了高斯函数的“胖瘦”宽度太小只有离中心极近的样本才有响应模型变成查表宽度太大所有样本响应都差不多退化成线性模型。常见做法是取簇内距离的均值乘以一个缩放因子比如 1.0 到 2.0 之间。这个因子没有理论最优值只能靠验证集试。隐藏层响应算完之后输出层权重可以用最小二乘法直接求解也可以用梯度下降迭代。这份源码大概率用的是最小二乘因为 RBFNN 的输出层是线性的给定隐藏层输出矩阵 H 和标签 y权重 W 满足H * W y直接求伪逆就行。求解完保存为 w2.npy 或者 w1.npy。这里要注意如果 H 矩阵条件数很大伪逆求解会不稳定预测结果波动剧烈。解决办法是加一个小的正则项也就是岭回归的思路代码里可能体现为np.linalg.pinv(H.T H lambda * np.eye(k)) H.T y其中 lambda 取 1e-6 到 1e-3 之间。# 计算宽度参数每个簇内样本到中心距离的均值 def compute_widths(X, centers, labels): widths np.zeros(len(centers)) for i in range(len(centers)): if np.any(labels i): dists np.linalg.norm(X[labels i] - centers[i], axis1) widths[i] dists.mean() if dists.mean() 1e-8 else 1e-8 else: widths[i] 1.0 return widths # 重新计算 labels 用于宽度估计 dists np.linalg.norm(X_norm[:, None, :] - centers[None, :, :], axis2) labels np.argmin(dists, axis1) widths compute_widths(X_norm, centers, labels) np.save(w1.npy, widths) # 保存宽度向量 # 计算隐藏层输出矩阵 H def rbf_response(X, centers, widths): # 高斯核exp(-||x-c||^2 / (2*sigma^2)) dist_sq np.sum((X[:, None, :] - centers[None, :, :]) ** 2, axis2) return np.exp(-dist_sq / (2 * widths[None, :] ** 2)) H rbf_response(X_norm, centers, widths) # 最小二乘求输出权重加正则项防止病态 lam 1e-5 W np.linalg.pinv(H.T H lam * np.eye(k)) H.T y np.save(w2.npy, W) # 保存输出权重这段代码把宽度计算、高斯响应、权重求解串起来了。参数说明widths是每个隐藏节点的宽度直接影响响应曲线的形状lam是正则化系数数据噪声大就调大一点但别超过 1e-2否则模型欠拟合。逻辑上H 矩阵的每一列对应一个隐藏节点的响应W 是输出层线性权重整个模型就是y_pred H W。训练完成后b1.npy 存中心点w1.npy 存宽度w2.npy 存权重b2.npy 可能是输出偏置或者训练过程中的中间变量具体看代码。2.3 训练脚本的完整执行流程与参数落盘把上面几步串起来RBFNN.py 的完整流程是读 train.csv → 归一化 → K 均值聚类得中心点 → 算宽度 → 算隐藏层响应矩阵 → 最小二乘求权重 → 保存四个 .npy 文件。整个过程不需要迭代几百轮一次前向计算加一次伪逆就结束这也是 RBFNN 训练速度快的根本原因。但速度快不代表结果好中心点选得不好、宽度设得不对、正则项没加都会让预测误差飙升。执行的时候在终端里直接python RBFNN.py就行前提是当前目录下有 train.csv并且 NumPy、pandas 已经装好。如果报ModuleNotFoundError: No module named numpy说明环境没配用pip install numpy pandas补上。训练结束后检查当前目录应该多出 b1.npy、b2.npy、w1.npy、w2.npy 四个文件文件大小和隐藏层节点数、特征维度有关。比如 k30、特征 10 维b1.npy 大概是 30×10 的浮点数组文件几 KB 到几十 KB。如果某个文件是 0 字节或者没生成说明代码在保存前就异常退出了往回看报错信息。提示训练前先把 train.csv 的列顺序确认一遍源码通常默认最后一列是标签前面是特征。如果你的 csv 标签在第一列直接跑会拿标签当特征结果完全不对。3. 用 test.py 跑预测加载 .npy 参数与误差指标解读3.1 加载训练好的参数并对 test.csv 做同样预处理test.py 的任务很纯粹加载 b1.npy、w1.npy、w2.npy读 test.csv做和训练时一模一样的归一化然后算预测值最后输出 MSE、RMSE 等误差指标。这里最容易翻车的地方是归一化参数不一致。训练时用 train.csv 的 min 和 max 做了归一化测试时如果重新用 test.csv 自己的 min 和 max尺度就变了预测结果会离谱。正确做法是把训练时的 min 和 max 也保存下来测试时直接用。这份源码可能没保存归一化参数那你就得手动改代码在训练脚本里把 X_min 和 X_max 存成 npy测试时加载。import numpy as np import pandas as pd # 加载训练阶段保存的参数 centers np.load(b1.npy) widths np.load(w1.npy) W np.load(w2.npy) # 读取测试数据 test_data pd.read_csv(test.csv) X_test test_data.iloc[:, :-1].values y_test test_data.iloc[:, -1].values # 关键使用训练集的 min/max 做归一化而不是测试集自己的 X_min np.load(x_min.npy) # 需要在训练脚本里补上保存 X_max np.load(x_max.npy) X_test_norm (X_test - X_min) / (X_max - X_min 1e-8) # 计算隐藏层响应 dist_sq np.sum((X_test_norm[:, None, :] - centers[None, :, :]) ** 2, axis2) H_test np.exp(-dist_sq / (2 * widths[None, :] ** 2)) # 预测并计算误差 y_pred H_test W mse np.mean((y_pred - y_test) ** 2) rmse np.sqrt(mse) mae np.mean(np.abs(y_pred - y_test)) print(fMSE: {mse:.6f}, RMSE: {rmse:.6f}, MAE: {mae:.6f})这段代码里x_min.npy和x_max.npy不是原始压缩包自带的需要你在训练脚本里加两行np.save补上。参数说明y_pred是模型输出mse对异常值敏感mae更稳健两个一起看能判断误差分布。逻辑上测试流程和训练的前向计算完全一致只是不更新权重。如果 MSE 比训练时大很多说明模型过拟合或者测试集分布和训练集差异大。3.2 MSE、RMSE、MAE 三个指标的实际含义与误读MSE 是均方误差单位是标签单位的平方数值大小受量纲影响不适合跨数据集比较。RMSE 是 MSE 开根号单位和标签一致解释起来更直观比如房价预测里 RMSE5 万就是说平均预测偏差 5 万左右。MAE 是平均绝对误差对极端值不敏感如果 MAE 远小于 RMSE说明存在少数预测偏差极大的样本需要回去检查那些样本的特征是不是异常。很多人只看 MSE 一个数就判断模型好坏这是不够的。更靠谱的做法是把预测值和真实值画成散点图看是否沿对角线分布再画残差图看残差有没有随预测值变化的趋势。如果残差呈现喇叭形说明模型对某些区间的数据拟合不好可能需要增加隐藏层节点或者调整宽度。这份源码只输出数值指标可视化需要自己加 matplotlib但加图之前先把数值指标跑通。注意如果 test.csv 的标签列和 train.csv 不一致比如训练时标签在最后一列测试时标签在第一列误差计算会完全错乱。跑之前用head test.csv看一眼列名和顺序。3.3 换自己的数据集时csv 格式和列顺序怎么对齐想用自己的数据跑这份源码最稳妥的方式是模仿 train.csv 和 test.csv 的结构所有特征列在前标签列在最后没有索引列没有多余的表头行。如果原始数据有缺失值先填充或者删除RBFNN 对 NaN 没有容错一个 NaN 就能让整个矩阵计算变成 NaN。类别特征需要先做独热编码或者标签编码不能直接塞字符串。时间序列数据要注意不能随机打乱得按时间顺序切训练集和测试集否则会用未来数据预测过去指标虚高。另外特征维度变了之后b1.npy 的形状也会变重新训练就行不用手动改。但隐藏层节点数 k 需要重新调特征越多k 一般也要适当增加但别超过训练样本数否则每个中心点对应一个样本模型退化成插值测试集上表现会很差。常见做法是 k 取训练样本数的平方根到十分之一之间再配合验证集选。4. 避坑与排查RBFNN 跑不通时先看这五条4.1 现象预测结果全是同一个值或者接近标签均值原因通常是宽度参数设得太大所有隐藏节点的响应都接近 1H 矩阵每列几乎一样最小二乘求出来的权重只能输出一个常数。解决方法是检查 w1.npy 里的宽度值如果都在 10 以上而输入特征归一化后在 [0,1] 之间那宽度明显过大。把宽度改成簇内距离均值的 0.5 到 1.0 倍重新训练。另一个可能是中心点没聚类成功所有中心点挤在一起同样导致响应无区分度。4.2 现象训练集 MSE 很小测试集 MSE 大得离谱这是典型过拟合。隐藏层节点数 k 设太多或者正则项 lam 设太小模型把训练集的噪声也学进去了。解决办法先把 k 降到训练样本数的十分之一左右再把 lam 从 1e-6 调到 1e-3观察测试集 MSE 是否下降。如果数据本身噪声大还可以在训练前做平滑或者剔除异常点。别指望 RBFNN 能自动抗过拟合它的容量控制全靠 k 和 lam 两个旋钮。4.3 现象运行时报ValueError: shapes not aligned矩阵维度对不上常见于换了自己的 csv 之后。检查 train.csv 的特征列数是否和 test.csv 一致检查 b1.npy 的列数是否等于特征数。如果训练时特征 10 维测试时特征 12 维中心点矩阵根本没法减。解决办法是确保训练和测试用同一套特征列增删特征后必须重新训练不能拿旧参数直接预测。另外np.load出来的数组形状可以用print(centers.shape)确认别靠猜。4.4 现象.npy 文件生成了但 test.py 加载时报文件不存在路径问题。RBFNN.py 和 test.py 如果在不同目录下运行保存和加载的相对路径基准不一样。常见做法是在代码里用os.path.dirname(os.path.abspath(__file__))拼绝对路径或者统一在同一个目录下执行两个脚本。如果是从压缩包解压后直接双击运行工作目录可能是解压工具临时目录也会导致找不到文件。建议在终端里cd到解压目录再执行python RBFNN.py和python test.py。4.5 现象误差指标是 NaN 或者无穷大输入数据里有 NaN 或者无穷值或者归一化时分母为 0。检查 train.csv 和 test.csv 有没有空单元格用pd.read_csv之后data.isnull().sum()看一眼。归一化时X_max - X_min如果某一列所有值相同分母就是 0加一个 1e-8 能避免。另外如果标签列本身有极端大的值最小二乘求解时数值不稳定可以先对标签也做归一化预测完再反归一化回来。5. 进阶技巧用验证集选隐藏层节点数别靠猜隐藏层节点数 k 是 RBFNN 里最玄学的参数没有之一。k 太小模型欠拟合训练集和测试集误差都大k 太大过拟合训练集误差小但测试集误差反弹。靠猜或者默认值跑十有八九翻车。我一般会写一个简单的网格搜索把 train.csv 再切出一部分做验证集比如 80% 训练、20% 验证然后让 k 从 5 到 100 步长 5 跑一遍每个 k 训练一次记录验证集 RMSE最后选验证误差最小的那个 k。这个过程不需要改 RBFNN.py 的核心逻辑只要把聚类和权重求解包成一个函数外面套循环就行。from sklearn.model_selection import train_test_split # 切分训练集和验证集 X_train, X_val, y_train, y_val train_test_split(X_norm, y, test_size0.2, random_state42) best_k None best_rmse float(inf) results [] for k in range(5, 101, 5): centers kmeans_init(X_train, k) dists np.linalg.norm(X_train[:, None, :] - centers[None, :, :], axis2) labels np.argmin(dists, axis1) widths compute_widths(X_train, centers, labels) H_train rbf_response(X_train, centers, widths) lam 1e-5 W np.linalg.pinv(H_train.T H_train lam * np.eye(k)) H_train.T y_train # 验证集预测 dists_val np.linalg.norm(X_val[:, None, :] - centers[None, :, :], axis2) H_val np.exp(-dists_val / (2 * widths[None, :] ** 2)) y_val_pred H_val W rmse np.sqrt(np.mean((y_val_pred - y_val) ** 2)) results.append((k, rmse)) if rmse best_rmse: best_rmse rmse best_k k print(f最佳隐藏层节点数: {best_k}, 验证集 RMSE: {best_rmse:.6f})这段代码把 k 的选择从拍脑袋变成数据驱动。参数说明test_size0.2是验证集比例数据量小可以设 0.3random_state固定随机种子保证每次切分一致lam在搜索 k 的时候先固定选完 k 再单独调 lam。逻辑上验证集不参与训练只用来评估泛化能力所以选出来的 k 比训练集误差最小的 k 更靠谱。跑完这个循环你会得到一张 k 和 RMSE 的对应表通常 RMSE 先降后升最低点就是相对合理的 k。除了 k宽度缩放因子和正则项 lam 也可以用同样的方式网格搜索但三个参数一起搜计算量会爆炸。我一般先固定宽度缩放因子为 1.0、lam 为 1e-5搜 kk 定了之后再微调宽度缩放因子最后调 lam。这样分步走每步计算量可控也容易看出哪个参数对结果影响最大。从那以后我每次拿到新的 RBFNN 代码都强制先跑一遍验证集选参再去看测试集指标不然测试集就成了变相的训练集指标再好也没意义。希望帮到你。本文还有配套的精品资源点击获取
返回列表