【机器学习】基于KAN的王者荣耀账号价格预测

319 阅读13分钟

摘要:本文通过分析王者荣耀账号交易数据,构建并优化了多个机器学习模型,特别是引入了先进的知识增强网络(KAN)模型,以提高账号价格预测的准确性。同时,开发了基于PyQt5的界面,使得预测过程更加直观和便捷。通过本文你可以学习到,如何搭建一个回归模型,预测、保存模型、结果分析。

file

1 数据清洗

1.1 数据读取与信息查看

导入所需要的模块

import pandas as pd
from sklearn import datasets
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn import metrics
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False		# 显示负号
import warnings
import joblib # 用来保存模型
warnings.filterwarnings("ignore") # 忽略警告

读取数据

data=pd.read_csv("data.csv")
data.describe()

file

信息介绍:

标题:用户所填写的标题内容

价格:账号价格,也是我们搭建回归模型要进行预测的内容。

系统版本:安卓版、苹果版

账号类型:QQ、微信

服务器服务区:全区全服、或者指定区

发布时间:用户所发布账号的时间

段位:无、青铜、铂金....、王者等等

二次实名:是否可以二次实名

贵族等级:V0-V10

皮肤数量:皮肤个数

防成迷限制:是否有防成迷限制

删除没有用的信息,其中标题、发布时间显然没有用,标题为其内容的整合,并无重要作用,发布时间也感觉大概率没有用。

data=data.drop("标题",axis=1)
data=data.drop("发布时间",axis=1)
data

1.2 数据映射

由于模型所接受的输入为数字,因此我们需要将文本映射为对应的数字。

将价格贵族等级、皮肤数量进行映射

data['价格'] = data['价格'].str.replace('¥', '').astype(float)
data['贵族等级'] = data['贵族等级'].str.replace('V', '')
data['贵族等级'] = pd.to_numeric(data['贵族等级'], errors='coerce')
data['贵族等级'].fillna(0, inplace=True)
data['皮肤数量'] = pd.to_numeric(data['皮肤数量'], errors='coerce')
data['皮肤数量'].fillna(0, inplace=True)

对文本进行映射,使用map函数和lambda表达式进行映射

data['系统版本'] = data['系统版本'].map({'安卓版': 1, '苹果版': 0})
data['账号类型'] = data['账号类型'].map({'QQ': 1, '微信': 0})
data['二次实名'] = data['二次实名'].map({'可二次': 1, '不可二次': 0})
data['防沉迷限制'] = data['防沉迷限制'].map({'无防沉迷': 1, '有防沉迷': 0})
data['服务区'] = data['服务区'].apply(lambda x: 1 if x == '全区' else 0)
data['服务器'] = data['服务器'].apply(lambda x: 1 if x == '全服' else 0)
data

采用同样的方式对段位进行映射,构建映射字典

rank_map={"未知":0,"暂无段位":0,"无":0,'看商品描述':0,
          "倔强青铜":1,
          "秩序白银":2,"白银":2,
          "荣耀黄金I":3,"荣耀黄金II":3,"荣耀黄金III":3,"荣耀黄金IV":3,"黄金":3,"荣耀黄金":3,
          "尊贵铂金I":4,"尊贵铂金II":4,"尊贵铂金III":4,"尊贵铂金IV":4,"铂金":4,"尊贵铂金":4,
          "永恒钻石":5,"永恒钻石I":5,"永恒钻石III":5,"永恒钻石II":5,"永恒钻石V":5,"永恒钻石IV":5,"钻石":5,
          "至尊星耀":6,"至尊星耀V":6,"至尊星耀IV":6,"至尊星耀I":6,"至尊星耀III":6,"至尊星耀II":6,"星耀":6,
          "最强王者":7,"至圣王者":8,"无双王者":9,"非凡王者":10,"绝世王者":11,"荣耀王者":12,"传奇王者":13,
          }
data["段位"]=data["段位"].map(rank_map)

最终的表格

file

1.3 数据清洗

将数据都转为float类型数据

data=data.astype(float)
data.info()

绘制箱线图

data[["段位","贵族等级","皮肤数量","账号类型"]].plot(kind='box', subplots=True, layout=(2, 2), sharex=False, sharey=False)
plt.tight_layout()  # 自动调整子图布局
plt.show()

file

其中段位集中在5左右也就是钻石分段,贵族等级主要集中在V7左右,皮肤数量主要集中在500个左右。

绘制频率分布直方图

data[["段位","贵族等级","皮肤数量","价格"]].hist()

file

可以看到价格分布比较非常规,有些账号出售价格在5w以上。

重点关注一下价格

data[["价格"]].plot()

file

可以看到大于4w的账号比较少,我们去除价格大于4w的账号。

data['价格'].describe()
data=data[data['价格']<40000]

绘制段位和贵族等级的KDE函数

data[["段位","贵族等级"]].plot(kind='kde')

file

段位和贵族等级第二多的主要集中在0也就是无段位和贵族等级。

1.4 相关性分析

绘制 段位、皮肤数量、贵族等级 这三个变量的相关性

antV = ['#1890FF', '#2FC25B', '#FACC14', '#223273', '#8543E0', '#13C2C2', '#3436c7', '#F04864']
f, axes = plt.subplots(3, 1, figsize=(8, 8))
sns.pointplot(x='段位', y='贵族等级', data=data[["段位","贵族等级"]], palette=antV, ax=axes[0])
axes[0].set_xlabel("段位")
sns.pointplot(x='段位', y='皮肤数量', data=data[["段位","皮肤数量"]], palette=antV, ax=axes[1])
axes[1].set_xlabel("段位")
sns.pointplot(x='贵族等级', y='皮肤数量', data=data[["皮肤数量","贵族等级"]], palette=antV, ax=axes[2])
axes[2].set_xlabel("贵族等级")
plt.tight_layout()
plt.show()

file

随着段位的增长,皮肤数量和贵族等级都有一定程度的增长,但增长到一定高度便变得平滑。而且贵族等级和皮肤数量呈现正相关的分布(充的钱越多,皮肤买的越多)

绘制pairplot图

g = sns.pairplot(data=data[["段位","皮肤数量","贵族等级"]], palette=antV)

file

绘制各个变量相关性热图

plt.figure(figsize=(12,8))
sns.heatmap(data.corr(), annot=True, fmt='.2f', cmap='PuBu')

file

保存清洗后的数据

data = data.dropna()
data.to_csv('DataCleaningData.csv', index=False)

2 搭建机器学习模型

2.1 线性归回模型

数据集划分,按照8:2划分为训练集和测试集

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error


X ,y = data[['系统版本', '账号类型', '服务区', '服务器', '段位', '二次实名', '贵族等级', '皮肤数量',
       '防沉迷限制']], data['价格']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
## 查看维度
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)

建立线性模型

linear_model = LinearRegression()
linear_model.fit(X_train, y_train)
coef = linear_model.coef_#回归系数
line_pre = linear_model.predict(X_test)
print('SCORE:{:.4f}'.format(linear_model.score(X_test, y_test)))
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test, line_pre))))
coef

SCORE:0.3862

RMSE:1511.9022

可视化线性模型相关系数

df_coef = pd.DataFrame()
df_coef['Title'] = data.columns.delete(-1)
df_coef['Coef'] = coef
df_coef

file

预测值和真实之间的差距

data_pre = pd.DataFrame()
data_pre['Predict'] = line_pre
data_pre['Truth'] = list(y_test.values)
data_pre[:100].plot(figsize=(18,8))

file

真实值会存在超高的情况,这也是可能导致模型效果不好的原因。

绘制散点图

plt.scatter(y_test, line_pre,label='y')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=4,label='predicted')

file

其中横坐标为真实值,纵坐标为真实值。

2.2 多模型验证

单纯的使用线性模型效果并不是很理想,为此进行一下多模型验证。

重新导入数据

X ,y = data[['系统版本', '账号类型', '服务区', '服务器', '段位', '二次实名', '贵族等级', '皮肤数量',
       '防沉迷限制']], data['价格']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

GradientBoosting(梯度提升)

from sklearn import ensemble
#params = {'n_estimators': 500, 'max_depth': 4, 'min_samples_split': 1,'learning_rate': 0.01, 'loss': 'ls'}
#clf = ensemble.GradientBoostingRegressor(**params)
clf = ensemble.GradientBoostingRegressor()
clf.fit(X_train, y_train)
clf_pre=clf.predict(X_test) #预测值
print('SCORE:{:.4f}'.format(clf.score(X_test, y_test)))#模型评分
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test, clf_pre))))#RMSE(标准误差)
joblib_file = "gradient_boosting_regressor_model.pkl"
joblib.dump(clf, joblib_file) # 保存模型

SCORE:0.6953

RMSE:1065.2251

其结果最好,我们将其保存下来,后来搭建Qt界面可能需要。

加载模型预测,单个样本预测

# 加载模型
clf_loaded = joblib.load(joblib_file)

# 使用加载的模型进行预测
clf_pre_loaded = clf_loaded.predict(X_test)
print('SCORE:{:.4f}'.format(clf_loaded.score(X_test, y_test)))
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test, clf_pre_loaded))))
# 单个样本值预测
one_brochure = clf_loaded.predict([[1,1,1,1,5,1,5,100,1]])
one_brochure

Lasso回归

Lasso 回归 (Least Absolute Shrinkage and Selection Operator)

Lasso也是惩罚其回归系数的绝对值。

与岭回归不同的是,Lasso回归在惩罚方程中用的是绝对值,而不是平方。这就使得惩罚后的值可能会变成0

from sklearn.linear_model import Lasso
from sklearn.metrics import r2_score as r2
lasso = Lasso()
lasso.fit(X_train,y_train)
y_predict_lasso = lasso.predict(X_test)
r2_score_lasso = r2(y_test,y_predict_lasso)

print('SCORE:{:.4f}'.format( lasso.score(X_test, y_test)))#模型评分
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test,y_predict_lasso))))#RMSE(标准误差)
print('Lasso模型的R-squared值为:',r2_score_lasso)

SCORE:0.3863

RMSE:1511.8387

Lasso模型的R-squared值为: 0.38625049437563475

ElasticNet 回归

ElasticNet回归是Lasso回归和岭回归的组合

from sklearn.linear_model import ElasticNet


enet = ElasticNet()
enet.fit(X_train,y_train)
y_predict_enet = enet.predict(X_test)
r2_score_enet = r2(y_test,y_predict_enet)

print('SCORE:{:.4f}'.format( enet.score(X_test, y_test)))#模型评分
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test,y_predict_enet))))#RMSE(标准误差)
print("ElasticNet模型的R-squared值为:",r2_score_enet)

SCORE:0.3552

RMSE:1549.6468

ElasticNet模型的R-squared值为: 0.35516936307722435

Support Vector Regression (SVR)

from sklearn.linear_model import ElasticNet
from sklearn.svm import SVR
from sklearn.metrics import confusion_matrix, classification_report
from sklearn.metrics import r2_score as r2, mean_squared_error as mse, mean_absolute_error as mae

def svr_model(kernel):
    svr = SVR(kernel=kernel) 
    svr.fit(X_train, y_train)
    y_predict = svr.predict(X_test)

    # score(): Returns the coefficient of determination R^2 of the prediction.
    print(kernel,' SVR的默认衡量评估值值为:', svr.score(X_test,y_test))
    print(kernel,' SVR的R-squared值为:', r2(y_test, y_predict))
    print(kernel,' SVR的均方误差(mean squared error)为:',mse(y_test, y_predict))
    print(kernel,' SVR的平均绝对误差(mean absolute error)为:',mae(y_test,y_predict))
    # print(kernel,' SVR的均方误差(mean squared error)为:',mse(scalery.inverse_transform(y_test), scalery.inverse_transform(y_predict)))
    # print(kernel,' SVR的平均绝对误差(mean absolute error)为:',mae(scalery.inverse_transform(y_test),scalery.inverse_transform(y_predict)))
    
    return svr

linear 线性核函数

linear_svr = svr_model(kernel='linear') 

linear SVR的默认衡量评估值值为: 0.2060437158935332

linear SVR的R-squared值为: 0.2060437158935332

linear SVR的均方误差(mean squared error)为: 2956762.0793989105

linear SVR的平均绝对误差(mean absolute error)为: 656.9329830164387

poly 多项式核

poly_svr = svr_model(kernel='poly')  

poly SVR的默认衡量评估值值为: 0.5809252554550037

poly SVR的R-squared值为: 0.5809252554550037

poly SVR的均方误差(mean squared error)为: 1560670.704305769

poly SVR的平均绝对误差(mean absolute error)为: 490.37693873641024

rbf(Radial Basis Function) 径向基函数

rbf_svr = svr_model(kernel='rbf') 

rbf SVR的默认衡量评估值值为: 0.23146171984198738

rbf SVR的R-squared值为: 0.23146171984198738

rbf SVR的均方误差(mean squared error)为: 2862103.2276292797

rbf SVR的平均绝对误差(mean absolute error)为: 620.1054545008733

SVM(支持向量机)回归-- 线性核

from sklearn.svm import SVR
linear_svr = SVR(kernel="linear")
linear_svr.fit(X_train, y_train)
linear_svr_pre = linear_svr.predict(X_test)#预测值
print('SCORE:{:.4f}'.format(linear_svr.score(X_test, y_test)))#模型评分
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test, linear_svr_pre))))#RMSE(标准误差)

SCORE:0.2060

RMSE:1719.5238

SVM(支持向量机)回归-- 多项式核

在使用SVM回归-- 多项式核的时候,首先要对数据进行一个标准化处理

from sklearn.preprocessing import StandardScaler
ss_x = StandardScaler()
X_train = ss_x.fit_transform(X_train)
X_test = ss_x.transform(X_test)
ss_y = StandardScaler()
y_train = ss_y.fit_transform(y_train.values.reshape(-1, 1))
y_test = ss_y.transform(y_test.values.reshape(-1, 1))
poly_svr = SVR(kernel="poly")
poly_svr.fit(X_train, y_train)
poly_svr_pre = poly_svr.predict(X_test)#预测值
print('SCORE:{:.4f}'.format(poly_svr.score(X_test, y_test)))#模型评分
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test, poly_svr_pre))))#RMSE(标准误差)

SCORE:0.5813

RMSE:0.5651

决策树回归

from sklearn.tree import DecisionTreeRegressor
tree_reg=DecisionTreeRegressor(max_depth=2)
tree_reg.fit(X_train, y_train)
tree_reg_pre = tree_reg.predict(X_test)#预测值
print('SCORE:{:.4f}'.format( tree_reg.score(X_test, y_test)))#模型评分
print('RMSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test,tree_reg_pre))))#RMSE(标准误差)

SCORE:0.5854

RMSE:0.5624

最终,经过多轮模型训练和参数调整,Gradient Boosting决策树模型凭借其卓越的性能和稳定性,最终脱颖而出,获得了最好的评分。在Kaggle比赛中,这种模型频繁出现,证明了它在处理各种复杂数据集时的强大能力和广泛适用性。

3 KAN回归分析

file

一篇名为《KAN: Kolmogorov–Arnold Network》的论文在机器学习领域引起了广泛关注。该论文提出了一种全新的神经网络视角,并提出了一种可以替代现有多层感知器(MLP)的新方案。要知道,多层感知器是当前机器学习技术的基石,如果 KAN 方案证明有效,将极大地推动深度学习的发展。

KAN 的设计灵感来源于 Kolmogorov-Arnold 表示定理。与传统的多层感知器(MLP)不同,KAN 通过使用可学习的函数替代固定的激活函数,从根本上消除了对线性权重矩阵的依赖。这种创新设计有望在神经网络的灵活性和性能方面带来显著提升。

本文重点不在与技术上的介绍,主要介绍如何使用KAN。

主要使用的库有:torch、pykan

为了防止出训练时模型不work(数值溢出从而影响稳定性和精度),我们首先需要对数据进行标准化。

data=pd.read_csv("DataCleaningData.csv")
X = data.drop(['价格'],axis=1)
y = data['价格']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 标准化
x_mean = X_train.mean()
x_std =  X_train.std()
y_mean = y.mean()
y_std = y.std()
X_train = (X_train - x_mean)/x_std
y_train = (y_train-y_mean)/y_std
X_test = (X_test - x_mean)/x_std
y_test = (y_test - y_mean)/y_std
# 转为ndarray
X_trai = X_train.to_numpy()
X_tes = X_test.to_numpy()
y_trai = y_train.to_numpy()
y_tes = y_test.to_numpy()

转为pykan库所支持的输入形式

import torch
dataset = {}
dataset['train_input'] = torch.from_numpy(X_trai).float()  
dataset['test_input'] = torch.from_numpy(X_tes).float()
dataset['train_label'] = torch.from_numpy(y_trai[:, None]).float()
dataset['test_label'] = torch.from_numpy(y_tes[:, None]).float()

导入Kan模型

from kan import KAN
          
# initialize KAN with G=3
model = KAN(width=[8,2,1], grid=3, k=3)
model(dataset['train_input'])
model.plot(beta=100)

file

在这里,我们将创建一个 KAN 模型:输入维度为 8(自变量),输出维度为 1(因变量),包含 2 个隐藏神经元,使用三次样条(k=3),并设置 3 个网格间隔(grid=3)。读者可以通过细化网格来最大化 KAN 的拟合能力,也可以通过修改网格间隔来得到更细粒度的 KAN,同时可以调整其他参数以提高模型的拟合度。在这里,我们不详细展示如何进行模型调参,接下来将直接训练模型。

进行训练

model.fit(dataset, opt="LBFGS", steps=5)
model.plot(beta=20)

file

训练过程中可学习函数方法变化。

将这些函数整理为数学公式的形式

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

−0.0291066939516225x1−0.130845952639758x2−0.0193875451759595x3−0.0434097207413198x4+0.0144947783482181x5+0.0303935667942831x6−0.0456227302981874x7+0.222429063203816(−x8−0.438255345031041)2−0.0353414960555791sin(0.579119861125946x7+8.77983951568604)+0.0316731859053845sin(0.416879594326019x8−7.38455963134766)−0.219533397590532

最终所得到的公式

通过公式,来进行模型预测并评价

prediction = []
def acc(formula, X):
    batch = X.shape[0]  # 获取批量大小
    for i in range(batch):
        subs_dict = {
            'x_1': X[i, 0],
            'x_2': X[i, 1],
            'x_3': X[i, 2],
            'x_4': X[i, 3],
            'x_5': X[i, 4],
            'x_6': X[i, 5],
            'x_7': X[i, 6],
            'x_8': X[i, 7]
        }
        # 使用给定的公式对当前样本进行预测,并将结果转换为浮点数
        predict = float(formula.subs(subs_dict))
        prediction.append(predict)  # 将预测结果添加到列表中   
    return prediction
          
test_pred = acc(formula, dataset['test_input'])
y_test_h = y_test*y_std+y_mean
pred_test_h = np.array(test_pred)*y_std+y_mean
import seaborn as sns
colors = sns.color_palette("husl", 3)

plt.figure(figsize=(15,5),dpi=300)
plt.scatter(y_test_h, pred_test_h, label='测试集', alpha=0.3, color=colors[2])
plt.xlabel('真实值')
plt.ylabel('预测值')
plt.legend()
plt.tight_layout()
plt.show()

file

整体上还是大致在45°这条线上。

模型评估

from sklearn import metrics

mse = metrics.mean_squared_error(y_test_h, pred_test_h)
rmse = np.sqrt(mse)
mae = metrics.mean_absolute_error(y_test_h, pred_test_h)
r2 = metrics.r2_score(y_test_h, pred_test_h)

def mean_absolute_percentage_error(y_true, y_pred):
    return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
mape = mean_absolute_percentage_error(y_test_h, pred_test_h)

print(f"Mean Squared Error (MSE): {mse:.4f}")
print(f"Root Mean Squared Error (RMSE): {rmse:.4f}")
print(f"Mean Absolute Error (MAE): {mae:.4f}")
print(f"R-squared (R2): {r2:.4f}")
print(f"Mean Absolute Percentage Error (MAPE): {mape:.4f}%")

Mean Squared Error (MSE): 1642743.8444

Root Mean Squared Error (RMSE): 1281.6957

Mean Absolute Error (MAE): 610.5355

R-squared (R2): 0.5589

Mean Absolute Percentage Error (MAPE): 101.1254%

结果证明,模型成功的work,但和其余机器学习模型相比,结果较差一些。

4 pyQt界面搭建

既然要玩这个数据,那我们就玩到底。

搭建一个QT界面来辅助使用

输入我们的账号信息

file

点击预测

file

结果还好不是很离谱(😂)。

[!NOTE]

创作不易,关注下方GZH【阿欣Python与机器学习】,发送【王者荣耀】获取全部数据和代码,数据分析,Qt界面等。

file