文章目录
- 一、说明
- 二、基础概念
- 三、kolmogorov-Arnold 网络性质
- 3.1 KAN 的潜在优势
- 3.2 挑战和注意事项
- 四、基本 KAN 超参数
- 五、COLAB 代码
- 六、注意点
一、说明
kolmogorov-Arnold 网络 (KAN) 是深度学习领域的一项“创新”,它提供了一种受现有 Kolmogorov-Arnold 表示定理启发的构建神经网络的新方法。它们被认为是广泛使用的多层感知器 (MLP) 的潜在替代品,而 MLP 为许多深度学习应用提供了支持。
二、基础概念
虽然 MLP 在节点(“神经元”)上具有固定的激活函数,但 KAN 在边缘(“权重”)上具有可学习的激活函数。KAN 根本没有线性权重——每个权重参数都被参数化为样条函数的单变量函数取代,从而允许在粗粒度和细粒度网格之间切换。
如果f是有界域上的多元连续函数,则f可以写成单变量连续函数与二元加法运算的有限组合。
其中 ϕq,p : [0, 1] → R 且 Φq : R → R。对于下一层l,我们有:
其中 Φ l是第 l个 KAN 层对应的函数矩阵。一般的 KAN 网络是由 L 层组成的:给定一个输入向量x0 ∈ R ,KAN 的输出为
在数学中,符号“◦”通常用来表示函数组合。
工作原理如下:
- 假设有两个函数,f(x) 和 g(x)。
- 函数组合,表示为f ∘ g(读作“f 与 g 组合”),是通过先应用g(x) ,然后取g(x)的输出并将其作为f(x)的输入来创建的一个新函数。
它将高维函数分解为几个一维函数,得到 α = k+1(其中 k 是样条函数的分段多项式阶)。
分段多项式函数是由其域内不同区间的不同多项式函数定义的函数。它本质上是将较简单的多项式片段拼接在一起以创建更复杂的函数。
从数学上来说,它被写成:
( f ∘ g ) ( x ) = f ( g ( x ) ) (f ∘ g)(x) = f(g(x)) (f∘g)(x)=f(g(x))
这基本上意味着您对输入值x执行g(x),然后获取结果输出并将其插入f(x)以获得最终答案。
以下是关于函数组合的一些要点:
- 顺序很重要:(f ∘ g)(x) 通常与 (g ∘ f)(x) 不同,这意味着函数组合是非交换的。
- 函数组合可以与多个函数链接在一起。例如, (h ∘ f ∘ g)(x) 将首先应用g(x) ,然后将f应用于g的输出,最后将h应用于f的输出。
西虽然 MLP 依赖于在隐藏层内的每个神经元上应用的固定激活函数(如 ReLU 或 sigmoid),但 KAN 采用了不同的方法。它们将重点放在神经元之间的连接(边缘)上。它们的区别如下:
- MLP: MLP 中的激活函数是预定义的非线性函数。它们为网络引入了学习输入数据和期望输出之间复杂关系的基本能力。神经元之间的连接带有简单的权重,这些权重在训练期间会进行调整。
- KAN: KAN 不会在每个神经元上使用固定的激活函数,而是在边缘本身上使用可学习的激活函数。这些激活函数是使用基函数(如B 样条函数)构建的。将基函数想象成可以组合起来创建更复杂函数的构建块。通过学习这些基函数的系数,KAN 可以在每个连接上实现高度灵活且富有表现力的激活函数。值得注意的是,KAN 完全从网络中移除了传统的线性权重,所有权重参数都被边缘激活函数中的系数所取代。
三、kolmogorov-Arnold 网络性质
3.1 KAN 的潜在优势
- 提高准确率:与 MLP 的固定激活相比,KAN 中的可学习激活函数有可能捕捉数据中更复杂的关系。这种灵活性可以使模型在特定任务上表现出色,尤其是那些涉及复杂模式的任务。
- 可解释性增强:与 MLP(黑匣子)的不透明性不同,KAN 提供了一定程度的可解释性。通过分析学习到的基函数系数,研究人员可以深入了解网络如何做出决策。这对于理解模型的内部工作原理并进一步提高其性能非常有价值。这将消除许多采用障碍,尤其是在人工智能监管过度的国家。
3.2 挑战和注意事项
- 增加训练时间:学习激活函数中基函数系数的过程可能耗费大量计算资源。与 MLP 相比,这意味着 KAN 需要更长的训练时间和更多的 RAM 需求,尤其是对于复杂的网络架构而言。
- 超参数调整: KAN 引入了与所用基函数的选择和数量相关的新超参数。与调整更简单的 MLP 架构相比,找到这些超参数的最佳配置需要付出更多努力。
KAN 可能对所有事物都过度拟合:在这个实验中,KAN 将特征中的纯随机数据与提供的标签进行拟合,准确率极高。这很麻烦。
与 MLP 一样,KAN 具有必须在训练期间进行调整的超参数。以下是 Kolmogorov-Arnold 神经网络中宽度、网格和 k 的细分:
四、基本 KAN 超参数
- 宽度width
在标准神经网络中,宽度是指隐藏层中神经元的数量。在 KAN 中,该概念类似,但侧重于用于构建每层激活函数的基函数数量。
更广泛的 KAN(更多基础函数)允许更复杂、更具表现力的激活,从而有可能在特定任务上带来更好的性能。
然而,增加宽度也会导致更长的训练时间和潜在的过度拟合。
- 网格grid
KAN 利用B 样条(或类似技术)来定义激活函数。这些 B 样条是在点网格上构建的。
网格定义了激活函数运行的间隔及其可以捕获的细节级别。
更精细的网格(更多点)可以捕获数据中更精细的细节,但也会增加计算成本和过度拟合。
- k
此参数与激活函数中使用的B 样条函数的具体类型有关。它决定了B 样条函数的平滑程度。正如你可能猜测的那样,我们也可以把 k 用作正则化工具。
较高的 k 值会导致B 样条曲线更平滑,从而可能提高训练期间的稳定性。
然而,非常高的 k 可能会限制激活函数的灵活性。
- 关键点:
宽度、网格和 k 都是 KAN 中的超参数,需要进行调整才能在特定任务上获得最佳性能。
在选择这些值时,需要在表达力、训练时间和潜在的过度拟合之间进行权衡。
在 KAN 中,每条边的索引为 ( l,i,j ),其中l 是层索引,i是输入神经元索引,j是输出神经元索引。当我们为每一层、输入和输出神经元创建公式时,您将看到这一点。
五、COLAB 代码
我们从 Colab 开始安装必要的库:
!pip install pykan==0.0.5 -q
数据集可以在kaggle上找到,与信用卡欺诈有关。“该数据集包含欧洲持卡人在 2013 年 9 月通过信用卡进行的交易。该数据集显示两天内发生的交易,在 284,807 笔交易中,有 492 笔是欺诈交易。该数据集非常不平衡,正类(欺诈)占所有交易的 0.172%。
它仅包含数值输入变量,这些变量是 PCA 转换的结果。[…] 特征 V1、V2、…V28 是通过 PCA 获得的主要成分,唯一没有通过 PCA 转换的特征是“时间”和“金额”。特征“时间”包含数据集中每笔交易与第一笔交易之间经过的秒数。特征“金额”是交易金额,此特征可用于基于示例的成本敏感学习。特征“类别”是响应变量,在存在欺诈的情况下取值为 1,否则取值为 0。”
获取 CSV 文件并添加到您的 Google Drive文件夹。然后:
from google.colab import drive
drive.mount('/content/drive')
然后转到 CSV 所在的文件夹:
%cd /content/drive/My Drive/folder/
我们已经准备好开始了:
from kan import KAN
import matplotlib.pyplot as plt
import torch
import numpy as np
import pandas as pd
from scipy import stats## DATASET AT : https://www.kaggle.com/datasets/mlg-ulb/creditcardfrauddf=pd.read_csv('creditcard.csv').reset_index()
df=df.iloc[:,1:]
df.shape
数据形状:(284807, 31)
如果有必要,我们会删除缺失的值。
df=df.dropna().sample(frac=1)
df.shape
对于变量选择,我使用了一个简单的 t 检验,这是一种统计检验,用于比较两组的平均值以确定它们之间是否有显著差异。它可以用来评估连续变量的平均值在分类变量定义的类别之间是否有差异。差异越大越显著,从选择该变量中获得的信息就越多。
# t-test
selected=[]
for i in range(1,df.shape[1]-1):t_stat, p_value = stats.ttest_ind(df[df.Class==0].iloc[:,i], df[df.Class==1].iloc[:,i])if p_value<0.01 and abs(t_stat>19):selected.append(i)
我们选择 3 个变量,V12、V14 和 V17。现在我们准备数据,对其进行混洗并拆分训练/测试。在这里,您可能希望减少示例数量,以避免在下一步 ( model.symbolic_formula() ) 中耗尽计算机 RAM。
from sklearn.model_selection import train_test_splitdataset = {}
X_train, X_test, y_train, y_test = train_test_split(df.iloc[:,select], np.array(df.Class), test_size=0.2, random_state=42, shuffle=True)
dataset['train_input'] = torch.from_numpy(np.array(X_train))
dataset['test_input'] = torch.from_numpy(np.array(X_test))
dataset['train_label'] = torch.from_numpy(np.array(y_train))
dataset['test_label'] = torch.from_numpy(np.array(y_test))X = dataset['train_input']
y = dataset['train_label']
我们定义KAN模型、第一个隐藏层、第二个隐藏层、深度和分段多项式:
model = KAN(width=[3,3], grid=3, k=3)
我们还定义了训练和测试准确度的函数:
def train_acc():return torch.mean((torch.argmax(model(dataset['train_input']), dim=1) == dataset['train_label']).float())def test_acc():return torch.mean((torch.argmax(model(dataset['test_input']), dim=1) == dataset['test_label']).float())
然后我们对 KAN 进行 20 个 epoch 的训练。由于我们处理的是分类问题,因此我们将使用 CrossEntropy 作为损失函数。
results = model.train(dataset, opt="Adam", steps=20, metrics=(train_acc, test_acc), loss_fn=torch.nn.CrossEntropyLoss())
请注意,更重要的权重更加明显,而不太重要的权重则逐渐消失:
model.plot(beta=0.2,scale=1.3,in_vars=[r'$x_{}$'.format(i) for i in range(1,4)])
让我们提取激活函数的值:l 是层索引,i是输入神经元索引,j是输出神经元索引。
让我们绘制所有的激活函数:
import numpy as np
import matplotlib.pyplot as pltfig, ax = plt.subplots(3, 3, figsize=(6, 6))
l=0# Iterate over all combinations of i, j
for i in range(3):for j in range(3):# Get the appropriate subplotinputs = model.spline_preacts[l][:, j, i]outputs = model.spline_postacts[l][:, j, i]rank = np.argsort(inputs)inputs = inputs[rank]outputs = outputs[rank]# Plot on the appropriate subplotax[i, j].plot(inputs, outputs, marker="o")ax[i, j].set_title(f'l={l}, i={i}, j={j}')# Adjust layout
plt.tight_layout()
plt.show()
接下来,我们修剪模型并重新训练:
model = model.prune()
model(dataset['train_input'])
results = model.train(dataset, opt="Adam", steps=20, metrics=(train_acc, test_acc), loss_fn=torch.nn.CrossEntropyLoss());
results['train_acc'][-1], results['test_acc'][-1]
model.plot()
model.fix_symbolic(0,0,0,'sin') # Symbolification
model.plot(beta=0.2)
model.unfix_symbolic(0,0,0)
model.plot(beta=0.2)
现在我们将获得包含这 3 个变量(输入、第 1 层中的变量/激活 B 样条、第 2 层中的变量/激活 B 样条)的所有符号公式:
lib = ['x','x^2','x^3','x^4','exp','log','sqrt','tanh','sin','tan','abs']
model.auto_symbolic(lib=lib)
formula = model.symbolic_formula()[0][0]
formula1, formula2,formula3 = model.symbolic_formula()[0]
formula1
正如您所注意到的,我们在这个公式中加入了权重,这使得它具有可解释性,这与 MLP 不同。现在我们来检查一下这个公式的准确性。在下面的代码中,subs是一种逐项用变量值替换公式中的值的方法:
# how accurate is this formula? subs == substitute value in formula by variable value
def acc(formula1, formula2,formula3, X, y):batch = X.shape[0]correct = 0for i in range(batch):logit1 = np.array(formula1.subs('x_1', X[i,0]).subs('x_2', X[i,1]).subs('x_3', X[i,2])).astype(np.float64)logit2 = np.array(formula2.subs('x_1', X[i,0]).subs('x_2', X[i,1]).subs('x_3', X[i,2])).astype(np.float64)logit3 = np.array(formula3.subs('x_1', X[i,0]).subs('x_2', X[i,1]).subs('x_3', X[i,2])).astype(np.float64)correct += (logit3 > logit2 > logit1) == y[i]return correct/batchprint('train acc of the formula:', acc(formula1, formula2,formula3, dataset['train_input'], dataset['train_label']))
print('test acc of the formula:', acc(formula1, formula2,formula3, dataset['test_input'], dataset['test_label']))
在原文中,每个B 样条函数的公式(或方程)都是按顺序排列的,以确保函数f的正确表示和近似。B样条函数是在特定区间内定义的,并且它们的支撑(非零区域)是有限的。因此,按顺序排列B 样条方程可确保通过将这些基函数以线性组合的形式相加,从而准确地重建函数f在其整个域上的值。
此外,这种有序结构对于近似界限至关重要。该定理依赖于B 样条函数的有序应用,以网格大小G为依据提供近似误差的界限。这种有序应用保持了近似所需的连续性和可微性。
按照这个顺序,我们可以系统地细化网格(从粗到细),同时保持近似过程的准确性和稳定性。这种方法对于确保近似误差随着网格变得更细而减小至关重要,这是基于样条的近似理论的一个关键方面。
logit1 = np.array(formula1.subs('x_1', X[i,0]).subs('x_2', X[i,1]).subs('x_3', X[i,2])).astype(np.float64)
logit2 = np.array(formula2.subs('x_1', X[i,0]).subs('x_2', X[i,1]).subs('x_3', X[i,2])).astype(np.float64)
logit3 = np.array(formula3.subs('x_1', X[i,0]).subs('x_2', X[i,1]).subs('x_3', X[i,2])).astype(np.float64)(logit1>logit2>logit3)
我们将会得到如下结果:
(155.2>143.7>162.5)
(logit1>logit2>logit3).astype(float)
六、注意点
H然而,在测试多个超参数和正则化因子设置时,我直觉地认为 KAN 绝对适合一切,甚至是随机数据。所以,我做了一个测试,我使用随机变量作为特征:
df.V12=np.random.random(df.shape[0])
df.V14=np.random.random(df.shape[0])
df.V17=np.random.random(df.shape[0])
输出如下:
请注意,无论是在训练集还是测试集中,准确率确实非常高。事实上,KAN 能够拟合任何连续函数,包括随机数据,其机制类似于向 MLP 添加不相关变量以提高准确率,但要极端得多。据说,通过适当的正则化、B 样条的平滑度和网络的深度,KAN 找到最佳拟合的机会比 MLP 更高。但这种说法需要实证支持,需要更多的研究。
有趣的是,作者在论文中指出,“柯尔莫哥洛夫-阿诺德表示定理在机器学习中基本上被判了死刑,被认为理论上合理但实际上毫无用处[1][2]”。
请注意,我采取了一种批判性的方法,强调需要对这种过度拟合问题的原因进行更深入的分析。我并不是想贬低 KAN。但在 MLP 中,至少我们有垃圾进垃圾出问题,MLP 不会无差别地适应一切。
根据 [2],柯尔莫哥洛夫定理是一个迷人的数学定理,它告诉我们如何用更简单的函数(仅依赖于一个变量的函数)来表示复杂函数(依赖于多个变量的函数),就像我们之前看到的一样。这对于设计神经网络来说可能非常有用,神经网络本质上是我们用来逼近复杂函数的工具。
然而,早在 1989 年,[2] 就指出柯尔莫哥洛夫定理对于创建实用的神经网络没有帮助,原因如下:
-
辅助函数的复杂性:为了使柯尔莫哥洛夫定理奏效,它使用了一些更简单的函数(我们称之为“辅助函数”)。但为了使该定理成立,这些辅助函数往往非常复杂。如果我们希望这些辅助函数平滑(以处理噪音)且易于使用,该定理就无法按预期发挥作用。另一位名叫维图什金的数学家早在 1954 年就证明了这一点。在 KAN 中,这是可调的,我们也有正则化工具。
-
辅助函数的特殊性:在实际的神经网络中,我们希望我们的网络具有固定的结构,我们可以对其进行调整以适应不同的问题。在 Kolmogorov 的设置中,我们需要的特定辅助函数实际上取决于我们试图表示的特定函数。这意味着对于每个新问题,我们都需要一组完全不同的辅助函数。嗯,MLP 也存在这个问题,这就是 CNN 存在的原因,也是我们将 MLP 微调到特定领域的原因。
-
实践中的复杂性:即使我们可以使用该定理,辅助函数最终也可能与我们试图近似的原始函数一样复杂。这违背了目的,因为我们想要一种更简单的方法来处理和计算这些函数。这一点引起了我的注意,因为 35 年后,KAN 和 MLP 都存在这个问题。我看到到处都是数学体操,试图解决只有认识论颠覆才能解决的问题。