摘要:本文通过分析王者荣耀账号交易数据,构建并优化了多个机器学习模型,特别是引入了先进的知识增强网络(KAN)模型,以提高账号价格预测的准确性。同时,开发了基于PyQt5的界面,使得预测过程更加直观和便捷。通过本文你可以学习到,如何搭建一个回归模型,预测、保存模型、结果分析。
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()
信息介绍:
标题:用户所填写的标题内容
价格:账号价格,也是我们搭建回归模型要进行预测的内容。
系统版本:安卓版、苹果版
账号类型: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)
最终的表格
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()
其中段位集中在5左右也就是钻石分段,贵族等级主要集中在V7左右,皮肤数量主要集中在500个左右。
绘制频率分布直方图
data[["段位","贵族等级","皮肤数量","价格"]].hist()
可以看到价格分布比较非常规,有些账号出售价格在5w以上。
重点关注一下价格
data[["价格"]].plot()
可以看到大于4w的账号比较少,我们去除价格大于4w的账号。
data['价格'].describe()
data=data[data['价格']<40000]
绘制段位和贵族等级的KDE函数
data[["段位","贵族等级"]].plot(kind='kde')
段位和贵族等级第二多的主要集中在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()
随着段位的增长,皮肤数量和贵族等级都有一定程度的增长,但增长到一定高度便变得平滑。而且贵族等级和皮肤数量呈现正相关的分布(充的钱越多,皮肤买的越多)
绘制pairplot图
g = sns.pairplot(data=data[["段位","皮肤数量","贵族等级"]], palette=antV)
绘制各个变量相关性热图
plt.figure(figsize=(12,8))
sns.heatmap(data.corr(), annot=True, fmt='.2f', cmap='PuBu')
保存清洗后的数据
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
预测值和真实之间的差距
data_pre = pd.DataFrame()
data_pre['Predict'] = line_pre
data_pre['Truth'] = list(y_test.values)
data_pre[:100].plot(figsize=(18,8))
真实值会存在超高的情况,这也是可能导致模型效果不好的原因。
绘制散点图
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')
其中横坐标为真实值,纵坐标为真实值。
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回归分析
一篇名为《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)
在这里,我们将创建一个 KAN 模型:输入维度为 8(自变量),输出维度为 1(因变量),包含 2 个隐藏神经元,使用三次样条(k=3),并设置 3 个网格间隔(grid=3)。读者可以通过细化网格来最大化 KAN 的拟合能力,也可以通过修改网格间隔来得到更细粒度的 KAN,同时可以调整其他参数以提高模型的拟合度。在这里,我们不详细展示如何进行模型调参,接下来将直接训练模型。
进行训练
model.fit(dataset, opt="LBFGS", steps=5)
model.plot(beta=20)
训练过程中可学习函数方法变化。
将这些函数整理为数学公式的形式
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()
整体上还是大致在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界面来辅助使用
输入我们的账号信息
点击预测
结果还好不是很离谱(😂)。
[!NOTE]
创作不易,关注下方GZH【阿欣Python与机器学习】,发送【王者荣耀】获取全部数据和代码,数据分析,Qt界面等。