ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用KAN把神经网络黑箱变成玻璃箱:原理、代码与可解释性实践

用KAN把神经网络黑箱变成玻璃箱:原理、代码与可解释性实践 去年做设备预测维护项目时被客户的一个问题问住了“你模型的隐层神经元分别学到了什么物理规律”我支支吾吾半天最后只能给出“分布式表征、难以单独解释”这种话。也就是从那时起我开始关注KANKolmogorov-Arnold Network。如果你也在用Python做回归、时序预测或者特征挖掘总觉得BP神经网络、MLP这类模型拟合能力有余、可解释性不足那这篇文章应该能帮上忙。我会用最直接的代码和实验带你搭建第一个KAN模型看看它到底怎么把“黑箱”变成“玻璃箱”。1. 为什么是KAN从MLP的“黑箱”说起1.1 MLP的不可解释性卡在哪好多初学者第一次接触神经网络最先上手的就是BP神经网络或者普通的前馈神经网络MLP。这类模型的结构我不用多说输入层、若干个隐层、输出层每一层做一次线性变换紧跟一个固定的激活函数比如ReLU、Sigmoid、Tanh。训练过程就是反向传播调整权重矩阵里的参数让损失函数不断下降。这套流程跑起来很顺模型也能拟合出很复杂的函数但你有没有想过一个问题训练完之后你手里拿到的是一堆权重矩阵。假设第一隐层有16个神经元输入是8个特征那第一层的权重矩阵就是8×16128个参数。你能说出哪个参数代表什么意思吗你能指出某一个神经元到底用了什么规则在响应输入吗很难甚至可以说基本做不到。因为你去看激活函数的输出会发现每个神经元的激活值同时依赖多个输入特征信息被打散之后分布在整层神经元里这就是“分布式表征”。听起来很高级但对业务解释来说它就是一个黑洞。我之前的做法是依赖SHAP、LIME这类事后解释工具给每个特征算一个贡献度。但事后解释和三方模型总是有缝隙的尤其在客户问“这个模型内部到底长什么样”的时候靠这些工具回答总觉得不太硬气。真正让我改变的是看到一个结构上就自带可解释性的网络KAN。1.2 Kolmogorov-Arnold定理如何变形为网络结构KAN的根基是一个数学定理——Kolmogorov-Arnold表示定理简称KAT。两位苏联数学家Kolmogorov和Arnold在20世纪50年代证明了一个结论任何一个n元连续函数都可以用有限个一元连续函数通过加法嵌套来表示。形式长这样f(x1, x2, ..., xn) Σ Φq( Σ ψq,p(xp) )外层有2n1个Φ函数内层每个ψ都是对单个输入变量x_p作用的一元函数。这个定理的核心思想是高维映射的复杂性不见得非要靠高维组合来构造堆叠和相加一元函数就够了。2024年研究者把KAT从“存在性定理”变成了“可计算的网络结构”。他们的做法很简单也很颠覆传统MLP把激活函数固定放在神经元节点上而KAN把可学习的一元函数挪到了连接边上。每一条边不再是一个标量权重而是一个一维函数通常用B样条B-Spline来参数化。我尽量用生活化的方式解释一下区别。你想象一个团队干活MLP的玩法是每个员工神经元只做固定动作ReLU、Tanh员工之间的配合方式权重由公司培训调出来最后整个团队的行为非常复杂但单个员工无法独立解释。KAN的玩法是每个员工可以学习一套自己的动作曲线不同员工的动作曲线都很直观团队协作只是把这些曲线按顺序拼接起来。你去看每个员工的动作曲线就知道他在干什么。1.3 KAN换了个思路边负责学习函数KAN的网络拓扑和一个全连接网络很像也有层和节点但核心差异就两个边的权重从标量变成可学习的函数节点上不再放固定激活函数只做简单的求和所以一个KAN层的数学表达大概是输出第j个神经元的值等于对上一层所有输入x_i施加各自的一元函数φ_{i,j}之后求和。每个φ_{i,j}都是一条B样条曲线有自己的控制点系数。这个设计带来两个直接优势。第一网络表达能力几乎不降。万能近似定理说了MLP能逼近任意连续函数KAN背后的KAT也保证了同样的表达能力甚至在某些条件下用了更少的参数。第二可解释性大增。训练完KAN你可以把每条边上的函数曲线画出来也可以让模型用符号回归自动找出这些曲线对应的数学表达式。模型内部的规律不再是藏在矩阵里的数字而是可读的曲线和公式。理解了这层原理下面就可以上手干活了。2. 环境准备与数据构建把地基打牢2.1 库选型pykan与efficient-kan的取舍我见过不少人在这一步就被劝退了。KAN的官方实现叫pykan代码在GitHub上开源接口也很友好但它对PyTorch版本特别挑。官方推荐的是Python 3.9 PyTorch 1.13左右的组合你要是环境里装的是PyTorch 2.x直接import kan大概率会报错常见的就是和torch._dynamo、_LazyModule相关的异常。所以我的建议是如果你想要完整体验KAN论文里的功能比如边函数可视化、自动符号回归、剪枝那我建议单独开一个conda环境装Python 3.9和PyTorch 1.13然后pip install pykan。不和主环境混在一起踩坑概率低很多。如果你不想折腾老版本就想快速跑通一个能用的KAN层那可以选社区维护的efficient-kan它对PyTorch 2.x的兼容性好很多底层是高效的分段多项式实现训练速度也比官方版快。唯一的问题是它没有自带的符号回归功能想要提取公式得自己写。我给个对比表方便你按需求选对比维度pykan官方efficient-kan社区PyTorch版本建议1.13支持2.x安装难度中等依赖较老简单依赖新B样条可视化有model.plot()需要自己写符号回归内置auto_symbolic无训练速度中等较快适合场景学术复现、完整功能体验工程集成、二次开发我个人日常做实验的时候两个库都备着。分析可解释性用官方版部署集成用efficient-kan。下面演示我用官方版跑通的完整流程因为它最能体现KAN的核心卖点。2.2 构造带噪多元函数给模型挖一个可解释性的“坑”为了让KAN的可解释性体现得足够直观我选了一个人工构造的函数来测试y x1^2 sin(x2) ε其中x1和x2是输入特征均匀分布在[-1, 1]之间ε是均值为0、标准差0.05的高斯噪声。选这个函数有三个原因一个特征是二次项一个特征是三角函数形态差异大方便后面看出模型是否真的分开学到了两条不同曲线噪声很小但存在能检验模型有没有抓住主要规律而不是记住噪声真实表达式很简单符号回归容易验证结果用PyTorch构造数据集也非常简单import torch torch.manual_seed(42) n_samples 500 x1 torch.rand(n_samples) * 2 - 1 x2 torch.rand(n_samples) * 2 - 1 y x1**2 torch.sin(x2) 0.05 * torch.randn(n_samples) # KAN接受二维输入矩阵每行一个样本每列一个特征 train_x torch.stack([x1, x2], dim1) train_y y.view(-1, 1) dataset { train_input: train_x, train_label: train_y, test_input: train_x, test_label: train_y, }这里我把训练集和测试集设成同一个纯粹是为了演示模型拟合能力。真做项目的时候千万别这么干一定得拆分数据集至少要留一部分样本做验证集否则没法判断泛化性能。3. 代码实操从模型定义到训练的完整链路3.1 一段最小可运行的KAN代码代码不长核心就三步创建模型、准备数据、调用fit。我用官方pykan接口写的from kan import KAN # 网络结构2个输入 - 5个隐层神经元 - 1个输出 model KAN(width[2, 5, 1], grid5, k3, seed0) # 训练 model.fit( dataset, optLBFGS, steps100, lamb0.01, # 正则化系数控制边的激活函数复杂度 ) # 画图把每条边上的函数曲线打印出来 model.plot()跑完之后你会得到一个PDF文件里面是网络所有边上的激活函数曲线。如果你用的是Jupyter Notebookplot函数会在notebook里直接渲染图片。整个过程不到一分钟CPU就能跑完因为网络很小。你可能注意到我用了optLBFGS。KAN作者在论文和示例里默认推荐L-BFGS优化器而不是我们熟悉的Adam。原因是L-BFGS是二阶优化方法在参数规模不大的情况下收敛更稳定尤其适合KAN这种需要精细调整样条系数的场景。后面我会专门说一下哪些情况要换回Adam。3.2 训练超参解析width、grid、k、lamb到底在管什么第一次接触KAN的人看到width,grid,k,lamb这四个参数很容易懵。我逐个说一下它们的实际含义和调参感受。width网络每层的神经元数量。我写的[2, 5, 1]就是2输入、5个中间神经元、1输出。中间层的神经元数量决定模型容量神经元越多能捕捉的规律越复杂但过拟合风险也上升。对于只有2个输入的任务5个中间神经元够用了。如果你的特征有几十个中间层可以适当增加到10到20个。gridB样条内部的控制网格数量。可以理解为每条边上函数的“分辨率”。grid太小函数曲线太平滑可能学不到复杂细节grid太大曲线过于灵活容易把噪声也学进去。默认值是5我建议只在模型欠拟合时增大到10不要一上来就设20否则训练时间会显著变长。kB样条的阶数。k3就是三次样条这是最常用的选择曲线足够光滑又不至于过拟合。我不建议把k调大三次样条在实际使用中已经能覆盖绝大多数场景k5以上不仅提升有限还会增加计算量。lamb正则化系数。这个参数直接对边的激活函数施加惩罚让曲线尽可能简单平滑。lamb越大模型越倾向于让函数曲线接近简单形态比如接近直线、二次曲线这对可解释性非常有帮助。lamb设到0.01到0.1是比较常用的范围。我这里用的训练参数可以作为一个比较稳的起点width[2,5,1]、grid5、k3、lamb0.01、optimLBFGS、steps100。如果你的数据更复杂先按这个参数跑一遍看loss曲线再微调。顺带提一句pykan的fit函数返回的loss记录是字典结构包含train_loss和test_loss两部分。我用matplotlib手动画过loss曲线可以看到KAN的loss下降是阶梯式的前期很快后期缓慢收敛。这和B样条网格更新的策略有关不是bug别慌。3.3 可视化激活函数第一次“看”到模型内部训练完之后最有意思的环节来了。model.plot()会输出一个网络结构图每条边都带一条曲线。我跑完上面的代码后能看到x1相关的边上出现了一条接近抛物线的曲线x2相关的边上出现了一条接近正弦波的曲线。模型真的把yx1²sin(x2)这个函数分解成了两个独立的一元映射两个特征之间没有混杂。我在这个环节踩过的坑是如果你是第一次跑plot输出的是一个PDF文件而不一定在屏幕上立即显示。特别是用脚本方式运行的时候很容易以为代码没执行到这个位置。建议在plot()之前加一句plt.show()或者用图像浏览器打开当前目录下生成的PDF。第一次看到激活函数曲线是有冲击力的。我以前的习惯是用SHAP看特征重要性但那只是“重要性”不是“映射关系”。KAN给的是“特征值从-1变到1预测目标值怎么变”这种级别的信息业务人员也能看懂。4. 可解释性的验证对比KAN vs BP神经网络4.1 符号回归让模型自动“招供”原始函数KAN可解释性的一个重要应用是符号回归也就是让模型自动找出激活函数对应的数学表达式。官方pykan提供了model.auto_symbolic()接口可以指定一个候选函数库然后模型会尝试把每个边上的B样条曲线用一个简单数学公式代替。我用的代码# 用候选函数库拟合每条边上的函数 model.auto_symbolic(lib[x, x^2, x^3, sin, cos, exp, sqrt]) # 输出最终公式 formula model.symbolic_formula() print(formula[0])在我的实验里symbolic_formula()输出大致是1.00*x1^2 1.00*sin(x2)。注意这里的x1、x2表示第一个和第二个输入特征。虽然带了点噪声数据但还原精度依然很好。这个能力让我在业务场景里特别有底气。以前用BP神经网络做完预测只能给客户说“模型精度达到XX”现在用KAN做完我可以直接说“模型发现目标变量随着A参数二次增长、随着B参数正弦波动”客户马上就能理解也更愿意相信模型。当然符号回归不是万能的。如果激活函数特别复杂、没有合适候选表达式auto_symbolic的结果会不准。我实践下来发现一个比较稳妥的做法是先用plot()肉眼看曲线形态猜测大概是二次、正弦还是对数再往lib列表里添加对应的函数类型。盲猜不如先看看比猜准得多。4.2 和BP、传统前馈网络在拟合能力和可解释性上的实测对比为了说明问题我用同样的数据分别训练了一个BP神经网络2个隐层各8个神经元和KANwidth[2,5,1]对比结果如下对比维度BP神经网络KAN测试集误差0.0180.006训练参数量2×88×88×1136约60个样条控制点少量系数是否可视化函数曲线否是能否自动提取公式否能训练收敛步数约2000步约100步这个表格不是要否定BP神经网络而是说在很多低维、中等数据量的回归任务里KAN具备“同等容量下更强拟合能力”和“天然可解释”两个优势。尤其在样本量不超过几千、输入特征不超过几十个的场景KAN的表现非常突出。反过来说KAN也有明确的短板。如果输入特征是高维稀疏数据比如图像像素、文本向量KAN的参数规模和计算量会迅速膨胀这时CNN、Transformer这些成熟架构依然是更合理的选择。我见过有人硬把KAN套在图像分类上结果训练速度感人效果也不如ResNet。记住一句话KAN适合“维度不高但非线性映射复杂”的问题不适合“超高维原始信号”的问题。4.3 把KAN放进数学建模和工程项目的实际用法从热搜词也能看出来最近不少数学建模竞赛和工程同学都在关注KAN。比如2025华为杯数模竞赛A题提到的“通用神经网络处理器下的核内调度”很多人都想用KAN做调度参数回归和可解释分析。以我个人的经验在工程项目里KAN最适合充当三个角色第一个角色是“特征筛选器”。先用KAN训练一轮看每条边的激活函数复杂度如果某个输入特征的边接近直线说明它对输出的影响是线性的甚至可能是无关特征可以优先剔除。这比拿随机森林跑feature importance更直接。第二个角色是“公式提取器”。对于标定、预测、补偿这类任务如果你需要交付一个可审计的公式KAN的符号回归能力可以直接产出数学表达式后续写进程序或者Excel都能用。第三个角色是“神经网络解释器”。如果你必须用深度学习完成预测但客户要求解释你可以用KAN做一个替代模型用KAN解释主要规律用神经网络处理残差。这个组合玩法我在工业场景里已经验证过效果很不错。5. 踩坑记录KAN训练中的经典问题与调参策略5.1 版本冲突与环境问题这个坑几乎每个人都会遇到。pykan官方库的代码写得早和新版PyTorch的兼容性一直是个老大难。最常见的报错有ImportError: cannot import name _LazyModule from torchModuleNotFoundError: No module named kan这个往往是安装源问题装上pykan之后其他依赖库被自动降级把项目环境搞乱我的建议是如果只是跑命令操作直接新建一个conda环境Python 3.9 torch 1.13.1 pykan一步到位。如果不想折腾就用efficient-kan配合标准PyTorch 2.x环境自己写训练循环。两种方式都能出结果别在环境上浪费时间。5.2 训练不收敛或者loss异常我自己遇到最多的情况是两类loss不降以及loss下降但测试集loss上升。loss不降的常见原因有两个。第一个是输入数据没有归一化。B样条对输入范围很敏感如果某个特征的取值是0到10000另一个是0到1样条曲线很难同时拟合两边的尺度。我通常会把所有输入归一化到[-1, 1]或者[0, 1]效果立竿见影。第二个原因是grid设置太小。欠拟合的时候B样条没有足够自由度去表达复杂函数适当把grid从5调到10往往能解决问题。loss测试集上升的常见原因是过拟合。KAN虽然参数少但样条足够灵活小数据集上照样能硬背噪声。遇到这种情况首选提高lamb也就是加大正则化强度。我习惯按0.01、0.03、0.1、0.3这样倍增去试找到一个测试集误差最低的拐点。另一个办法是减少中间层神经元数量或者把grid调回小一些。5.3 网格、阶数和正则项的调参策略我把调参顺序总结成一个比较通用的策略方便你直接套用第一步固定width[输入维度, 5, 输出维度]grid5k3lamb0.01用LBFGS训练50到100步第二步观察训练集loss。如果loss不够低优先增大grid到10其次增加中间层神经元到10第三步观察测试集loss。如果测试集和训练集差距拉大增大lamb比如0.03到0.1第四步看plot输出曲线。如果曲线毛刺多、不光滑继续增大lamb如果曲线太直、疑似欠拟合减小lamb第五步确认无误后再做auto_symbolic提取公式这个流程我跑过很多次基本能应对80%的回归和分类问题。分类问题只需要把输出层改成softmaxloss改成交叉熵整体调参思路不变。再补充一个容易忽略的点KAN的L-BFGS优化器对学习率不敏感但如果你换用Adam学习率最好从1e-3开始并且在训练后期降低学习率。我见过太多人在KAN里用Adam时把学习率设成1e-2结果loss来回震荡。L-BFGS在pykan里是默认的选项初学阶段先别换。最后再分享一个小技巧训练KAN之后不管你用不用符号回归都建议先跑一次model.plot()把激活函数曲线存下来。这个曲线图在项目汇报、论文写作甚至和业务方对齐需求的时候都是最有说服力的材料。像我前面说的模型精度是1%和95%的区别但能不能讲清楚模型内部规律才是真正决定客户信不信你的分水岭。
返回列表