我的飞浆学习赛:英雄联盟大师预测
原始代码
import numpy as np
import paddle
import pandas as pd
from sklearn.feature_selection import VarianceThreshold
import paddle.nn as nn
train_df = pd.read_csv('data/data137276/train.csv.zip')
test_df = pd.read_csv('data/data137276/test.csv.zip')
train_df = train_df.drop(['id', 'timecc'], axis=1)
a=train_df
test_df = test_df.drop(['id', 'timecc'], axis=1)
for col in train_df.columns[1:]:
train_df[col] /= train_df[col].max()
test_df[col] /= test_df[col].max()
class Classifier(paddle.nn.Layer):
self代表类的实例自身
def __init__(self):
初始化父类中的一些参数
super(Classifier, self).__init__()
self.fc1 = paddle.nn.Linear(in_features=29, out_features=40)
self.fc2 = paddle.nn.Linear(in_features=40, out_features=1)
self.relu = paddle.nn.ReLU()
网络的前向计算
def forward(self, inputs):
x = self.relu(self.fc1(inputs))
x = self.fc2(x)
return x
model = Classifier()
model.train()
opt = paddle.optimizer.SGD(learning_rate=0.01, parameters=model.parameters())
loss_fn = paddle.nn.BCEWithLogitsLoss()
EPOCH_NUM = 2 # 设置外层循环次数
BATCH_SIZE = 100 # 设置batch大小
training_data = train_df.iloc[:-1000,].values.astype(np.float32)
val_data = train_df.iloc[-1000:, ].values.astype(np.float32)
# 定义外层循环
for epoch_id in range(EPOCH_NUM):
# 在每轮迭代开始之前,将训练数据的顺序随机的打乱
np.random.shuffle(training_data)
# 将训练数据进行拆分,每个batch包含10条数据
mini_batches = [training_data[k:k+BATCH_SIZE] for k in range(0, len(training_data), BATCH_SIZE)]
# 定义内层循环
for iter_id, mini_batch in enumerate(mini_batches):
x = np.array(mini_batch[:, 1:]) # 获得当前批次训练数据
y = np.array(mini_batch[:, :1]) # 获得当前批次训练标签
# 将numpy数据转为飞桨动态图tensor的格式
features = paddle.to_tensor(x)
y = paddle.to_tensor(y)
# 前向计算
predicts = model(features)
# 计算损失
loss = loss_fn(predicts, y, )
avg_loss = paddle.mean(loss)
if iter_id%200==0:
acc = (predicts > 0).astype(int).flatten() == y.flatten().astype(int)
acc = acc.astype(float).mean()
print("epoch: {}, iter: {}, loss is: {}, acc is {}".format(epoch_id, iter_id, avg_loss.numpy(), acc.numpy()))
# 反向传播,计算每层参数的梯度值
avg_loss.backward()
# 更新参数,根据设置好的学习率迭代一步
opt.step()
# 清空梯度变量,以备下一轮计算
opt.clear_grad()
model.eval()
test_data = paddle.to_tensor(test_df.values.astype(np.float32))
test_predict = model(test_data)
test_predict = (test_predict > 0).astype(int).flatten()
pd.DataFrame({'win':test_predict.numpy()}).to_csv('submission.csv', index=None)
数据集
对于数据集中每一行为一个玩家的游戏数据,数据字段如下所示:
id:玩家记录id
win:是否胜利,标签变量
kills:击杀次数
deaths:死亡次数
assists:助攻次数
largestkillingspree:最大 killing spree(游戏术语,意味大杀特杀。当你连续杀死三个对方英雄而中途没有死亡时)
largestmultikill:最大mult ikill(游戏术语,短时间内多重击杀)
longesttimespentliving:最长存活时间
doublekills:doublekills次数
triplekills:doublekills次数
quadrakills:quadrakills次数
pentakills:pentakills次数
totdmgdealt:总伤害
magicdmgdealt:魔法伤害
physicaldmgdealt:物理伤害
truedmgdealt:真实伤害
largestcrit:最大暴击伤害
totdmgtochamp:对对方玩家的伤害
magicdmgtochamp:对对方玩家的魔法伤害
physdmgtochamp:对对方玩家的物理伤害
truedmgtochamp:对对方玩家的真实伤害
totheal:治疗量
totunitshealed:痊愈的总单位
dmgtoturrets:对炮塔的伤害
timecc:法控时间
totdmgtaken:承受的伤害
magicdmgtaken:承受的魔法伤害
physdmgtaken:承受的物理伤害
truedmgtaken:承受的真实伤害
wardsplaced:侦查守卫放置次数
wardskilled:侦查守卫摧毁次数
firstblood:是否为firstblood 测试集中label字段win为空,需要选手预测。
评分结果
改进方式
构建更多的前馈神经网络
构建更多的前馈神经网络虽然并不总是能够提高准确率。但前馈神经网络是一种常见的深度学习模型,由多个隐藏层组成,其每个节点只与下一层的节点相连。在训练过程中,每一层都通过激活函数将其输出映射到下一层,并依次传递到输出层。
增加网络深度可以提高模型的非线性表达能力,从而可以更好地拟合数据,提高准确率。但是,构建更多的前馈神经网络并不总是能够提高准确率,因为过度的深度和宽度可能导致过拟合,从而降低模型的泛化能力,使得模型在新的数据上表现不佳。但是此题我通过构建6层连接层模型的准确率得到了提升。
class Classifier(paddle.nn.Layer):
# self代表类的实例自身
def __init__(self):
# 初始化父类中的一些参数
super(Classifier, self).__init__()
self.fc1 = paddle.nn.Linear(in_features=25, out_features=64)
self.fc2 = paddle.nn.Linear(in_features=64, out_features=128)
self.fc3 = paddle.nn.Linear(in_features=128, out_features=64)
self.fc4 = paddle.nn.Linear(in_features=64, out_features=32)
self.fc5 = paddle.nn.Linear(in_features=32, out_features=16)
self.fc6 = paddle.nn.Linear(in_features=16, out_features=1)
self.relu = paddle.nn.ReLU()
# 网络的前向计算
def forward(self, inputs):
x = self.relu(self.fc1(inputs))
x = self.fc2(x)
x = self.relu(self.fc3(x))
x = self.fc4(x)
x = self.relu(self.fc5(x))
x = self.fc6(x)
return x
修改模型的超参数
除了上述提到的增加了连接层(从模型结构层面进行更改),我也更改了学习率,我使用了一个学习率调节器
学习率
scheduler = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=0.01, T_max=EPOCH_NUM)
CosineAnnealingDecay是一种学习率下降策略,它根据余弦函数来调整学习率。在训练过程中,每个 epoch 结束后CosineAnnealingDecay会根据当前 epoch 的数值,以余弦函数的形式计算出一个新的学习率,该学习率会取代原先的学习率,从而实现动态调整学习率的目的。在这段代码中,创建了一个 CosineAnnealingDecay对象,设置了初始学习率为 0.01,以及最大 epoch 数量 T_max,用于指定学习率变化的周期。在训练过程中,可以将该调度器(scheduler)传递给优化器(optimizer),然后在每个 epoch 结束后,调用 step() 方法,即可让调度器自动的更新学习率。
学习率调度器是深度学习中常用的工具之一,通过动态调整学习率,可以提高模型的性能和泛化能力,帮助模型更好地适应训练数据,从而得到更好的效果。
batch_size和epoch
batch_size和epoch目前测试后分别调试为64与100效果最佳。
进行特征选择
# 创建方差阈值对象,删除方差低于阈值的所有特征
selector = VarianceThreshold(threshold=0.001)
# 对训练数据进行拟合
selector.fit(train_df)
# 选择最相似的特征(即剩余的特征)
train_df = selector.transform(train_df)
#根据特征选择结果,对训练数据和测试数据进行列选择,get_support() 方法可以获取特征选择器所选择的特征对应的布尔值,该布尔值为 True 则表示对应的特征需保留,False 表示需舍弃。
deleted_features = np.logical_not(selector.get_support())
#取反该布尔数组,反转 True 和 False 的值
inverted_list = [not x for x in deleted_features]
train_df = a.loc[:, inverted_list]
del inverted_list[0]
test_df = test_df.loc[:, inverted_list]
使用方差阈值进行特征选择,将数据集中4个特征,似乎对准确率提升有所帮助。
改进的结果
目前正在改进的方法
使用自编码器构建网络结构,将梯度下降发改为随机森林。
自编码器与前馈神经网络的区别
自编码器网络结构是一种无监督学习模型,用于学习数据的低维表示。相比之下,前馈神经网络(Feedforward Neural Network)属于监督学习模型,用于分类或回归问题。
以下是自编码器网络结构与前馈网络结构的主要区别:
数据输入方式:
自编码器网络通常将数据直接输入编码器(Encoder),通过编码器和解码器(Decoder)来学习数据的低维表示。而前馈网络则需要将输入和输出配对,以便在监督学习过程中训练和优化。
学习目标:
自编码器网络的目标是最小化输入和输出之间的重构误差(即重构损失),并学习表示数据的潜在结构。而前馈网络通常会定义特定的目标函数(如均方误差、交叉熵等)来进行分类或回归任务的训练。
网络结构:
自编码器网络通常由编码器和解码器组成,它们之间共享权重。在神经网络中,权重通常是随机初始化的,并通过训练进行学习。前馈网络则通常由多个隐藏层组成,每一层都由一组权重和偏置组成,并且每个节点都与前一层的所有节点相连。
训练方式:
自编码器网络通常使用无监督学习方法进行训练,因此可以在没有标签数据的情况下进行学习。自编码器的训练通常使用反向传播算法(Backpropagation)和梯度下降(Gradient Descent)或其变体来最小化重构误差,从而优化网络权重。前馈网络则使用标准的监督学习方法进行训练,例如反向传播和随机梯度下降(Stochastic Gradient Descent)等。
自编码器网络结构的优点在于它们能够学习数据的低维表示,并被广泛应用于特征提取、数据降维和数据重建等任务。相比之下,前馈神经网络可以处理不同类型的监督学习任务,例如分类和回归问题。
总之,自编码器网络结构和前馈网络结构的区别在于它们的输入和输出方式、学习目标、网络结构和训练方式等方面。具体应用时需要根据具体情况选择合适的模型来解决问题。
#定义一个前馈自编码器
class Autoencoder(nn.Layer):
def __init__(self):
super(Autoencoder, self).__init__()
# encoder
self.encoder = nn.Sequential(
nn.Linear(25, 16),
nn.ReLU(),
nn.Linear(16, 8),
nn.ReLU(),
nn.Linear(8, 4),
nn.ReLU()
)
# decoder
self.decoder = nn.Sequential(
nn.Linear(4, 8),
nn.ReLU(),
nn.Linear(8, 16),
nn.ReLU(),
nn.Linear(16, 25),
nn.ReLU(),
nn.Linear(25, 1)
)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return decoded
随机森林与梯度下降法的优劣
梯度下降和随机森林是两种不同的机器学习算法,其优劣主要取决于应用场景及问题需要解决的特征。
梯度下降是一种优化算法,主要用于训练神经网络等机器学习模型,目的是最小化损失函数并学习到最优的参数,从而使得模型拟合数据更好。它的优点在以下几个方面:
可以用于训练大量数据:通常,训练神经网络等深度学习模型时,需要使用大量的数据,梯度下降算法可以处理这些数据。
可以应用于各种类型的参数:梯度下降算法适用于各种类型和规模的参数,例如权重和偏置等。
应用广泛:梯度下降算法是各种机器学习和深度学习模型的基础,被广泛应用于各种任务和场景。
随机森林是一种基于决策树的集成学习算法,也是一种监督学习算法,其优点在以下几个方面:
准确性高:随机森林算法具有较高的准确性,可以有效地解决分类和回归问题。
解释性好:随机森林模型较为简单,可解释性强,可以帮助分析模型中影响结果的重要变量。
可以避免过拟合:随机森林算法可以通过个体树的随机特征和随机样本选择,减少了过度拟合的风险。
梯度下降和随机森林的选择应该根据具体的问题和数据特征等因素来决定。总的来说,对于大量复杂的数据和高维度的问题,用梯度下降来学习更适合。如果数据集简单且要求模型具有较强的解释性或者需要解决分类问题,则可以选择随机森林算法。