所有文章 > AI驱动 > KAN:Kolmogorov–Arnold Networks分类模型实现

KAN:Kolmogorov–Arnold Networks分类模型实现

KAN是当前提出的一种全新的神经网络架构,传统的MLP多层感知器中,通常使用的激活函数是非线性的,例如ReLU、sigmoid或tanh,这些激活函数在大多数深度学习框架中都是不可学习的函数,只是应用于每个神经元的输出,MLP的线性层(全连接层)的权重是可学习的,但是KAN解决了激活函数在MLP中不可学习的问题,它把可学习的激活函数放在权重上,让其进行学习

接下来作者将利用KAN进行对鸢尾花的分类实现,体现它相对于MLP无法比拟的可解释性、交互性特点,当然KAN也有其缺点就是目前版本训练速度较慢

代码实现

数据读取

import pandas as pd
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
import numpy as np
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
iris_df['target'] = iris.target
df = iris_df[iris_df['target'] != 2] # 只要0和1完成一个二分类问题
df.head()

这里将数据简单梳理为二分类问题,并且将这个分类问题看作回归问题,去探讨不同输出维度下的KAN

KAN输出维度=1

from sklearn.model_selection import train_test_split
import torch
train_input, test_input, train_label, test_label = train_test_split(df.iloc[:, 0:4], df['target'],
test_size=0.2, random_state=42, stratify=df['target'])

# 将 DataFrame 和 Series 转换为 np.array
train_input = train_input.to_numpy()
test_input = test_input.to_numpy()
train_label = train_label.to_numpy()
test_label = test_label.to_numpy()
# 转换为pytorch张量
dataset = {}
dataset['train_input'] = torch.from_numpy(train_input)
dataset['test_input'] = torch.from_numpy(test_input)
dataset['train_label'] = torch.from_numpy(train_label[:,None])
dataset['test_label'] = torch.from_numpy(test_label[:,None])

分割数据集且将原始的 DataFrame 数据转换为适合在 PyTorch 中使用的张量形式

from kan import KAN
model = KAN(width=[4,1], grid=3, k=3)
# 初始化绘制KAN
model(dataset['train_input']);
model.plot(beta=100)

这里创建一个KAN:4D输入,1D输出,没有隐藏的神经元,三次样条 (k=3),3个网格间隔 (grid=3),如果要添加隐藏的神经元在width中添加既可,它表示每层中的神经元数,例如,[2,5,5,3] 表示 2D 输入,3D 输出,具有 2 层 5 个隐藏神经元,创建这样的模型后对其可视化,当前这个模型还没有进行训练,接下来训练这个模型

# 定义训练集准确率计算函数
def train_acc():
# 使用模型对训练输入进行预测,取预测值的第一个输出并四舍五入
# 将预测值与训练标签进行比较,计算准确率
return torch.mean((torch.round(model(dataset['train_input'])[:, 0]) == dataset['train_label'][:, 0]).float())

# 定义测试集准确率计算函数
def test_acc():
# 使用模型对测试输入进行预测,取预测值的第一个输出并四舍五入
# 将预测值与测试标签进行比较,计算准确率
return torch.mean((torch.round(model(dataset['test_input'])[:, 0]) == dataset['test_label'][:, 0]).float())

# 训练模型,使用LBFGS优化器,训练20步,计算训练和测试集的准确率
results = model.train(dataset, opt="LBFGS", steps=20, metrics=(train_acc, test_acc))
model.plot()

定义了两个函数 train_acc() 和 test_acc() 分别用于计算训练集和测试集上模型的准确率,然后使用 LBFGS 优化器对模型进行训练,训练步数为 20 步,并同时计算并输出训练和测试集的准确率,最后对模型进行可视化,对比模型初始可视化可以发现激活函数明显不一样了,这就是KAN对激活函数学习的一个结果,接下来我们把这个模型进行解释性输出

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]
formula

可以发现KAN模型相对其其它深度学习框架,它可以输出一个具体的公式,当然这个KAN是单输出所以只有一个公式,通过这个公式它不在是一个黑箱模型,而是可以被我们所解释的模型,实际上把相应的X值输入公式并进行四舍五入返回的值就是0或1也就是我们的实际类别,接下来通过这个公式来输出在训练集、测试集上的模型精确度

def acc(formula, X, y):
batch = X.shape[0] # 获取批量大小
correct = 0 # 初始化正确预测的数量
for i in range(batch):
# 构建替换字典,将 x_1, x_2, x_3, x_4 替换为当前样本的值
subs_dict = {'x_1': X[i, 0], 'x_2': X[i, 1], 'x_3': X[i, 2], 'x_4': X[i, 3]}
# 使用给定的公式对当前样本进行预测,并将结果转换为浮点数
prediction = float(formula.subs(subs_dict))
# 四舍五入预测值,与真实标签进行比较
if np.round(prediction) == y[i, 0]:
correct += 1
# 计算准确率
accuracy = correct / batch
return accuracy

# 计算训练集和测试集的准确率
train_accuracy = acc(formula, dataset['train_input'], dataset['train_label'])
test_accuracy = acc(formula, dataset['test_input'], dataset['test_label'])
print('train acc of the formula:', train_accuracy)
print('test acc of the formula:', test_accuracy)

通过准确率可知这个单输出的二分类KAN模型,表现的很好只是在训练集上出现了一点错误,接下来我们重新去构建一个二输出的KAN模型

KAN输出维度=2

dataset = {}
dataset['train_input'] = torch.from_numpy(train_input)
dataset['test_input'] = torch.from_numpy(test_input)
dataset['train_label'] = torch.from_numpy(train_label).type(torch.long)
dataset['test_label'] = torch.from_numpy(test_label).type(torch.long)

model = KAN(width=[4,2], grid=3, k=3)
model(dataset['train_input']);
model.plot(beta=100)

这个模型相对于第一个模型只去修改了它的输出维数为二,同样还是把它看作是一个回归模型

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())

results = model.train(dataset, opt="LBFGS", steps=20, metrics=(train_acc, test_acc), loss_fn=torch.nn.CrossEntropyLoss())
model.plot()

同样是对激活函数进行学习,并可视化

lib = ['x','x^2','x^3','x^4','exp','log','sqrt','tanh','sin','abs']
model.auto_symbolic(lib=lib)
formula1, formula2 = model.symbolic_formula()[0]
formula1
formula2

这是一个输出维数为二的KAN模型相应的它的输出都有与它一一对应的数学公式来进行解释

def acc(formula1, formula2, X, y):
batch = X.shape[0]
correct = 0
for 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]).subs('x_4', X[i,3])).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]).subs('x_4', X[i,3])).astype(np.float64)
correct += (logit2 > logit1) == y[i]
return correct/batch

print('train acc of the formula:', acc(formula1, formula2, dataset['train_input'], dataset['train_label']))
print('test acc of the formula:', acc(formula1, formula2, dataset['test_input'], dataset['test_label']))

相应的计算这个KAN模型的准确率,可以发现这个输出维数为二的KAN比输出维度为一的KAN要好,这个KAN模型在这个数据集上百分比预测正确,这里利用的是预测结果(即 logit2 > logit1 的布尔值)与真实标签 y[i] 相等,则返回 True(1),否则返回 False(0),来进行准确率计算,到这里就完成了这个分类模型的构建,读者也可以尝试对所有数据集进行三分类KAN构建,下面是作者对完整鸢尾花数据进行构建的KAN模型可视化

本文章转载微信公众号@Python机器学习AI