本文分为两部分来讲解如何建立使用Python和PyTorch来预测足球比赛结果的神经网络。第一部分中,我们将解释如何使用Python和Pandas库从数据集中收集和准备数据。第二部分中,我们将解释如何使用Python和PyTorch平台创建和设置神经网络模型。
第一部分
关于数据
我们使用的数据是足球比赛结果,这些结果来自巴西联赛,2010赛季(BLC 2010),2011赛季(BLC 2011)和2012赛季(BLC 2012)。这些不同的数据集可以提供该方法的预测能力。联赛采用20支球队积分制。每支球队都将在主客场各进行一次比赛。以这种方式,总共进行38轮比赛并且每轮进行了10场比赛,因此每届联赛共产生380场比赛。
这些数据集来自UOL Sports网站BLC 2010,BLC 2011,BLC 2012,并转换为三个CSV文件(每个赛季一个)。
数据集格式
查看CSV文件中的数据集。
每个CSV文件包含一个标题行和760行数据。每行包含49个特征。我们不会使用数据集中提供的所有这49个特征。我们只使用其中的27个特征。
在本问中,我们将使用Python作为编程语言。为了收集和准备数据集,我们将使用Pandas库。对于神经网络模型(训练和测试),我们将使用PyTorch平台。
收集并准备数据集
首先,我们需要下载CSV文件并保存。在本文中,我们只使用BLC 2010中的数据集。您也可以使用其他CSV文件作为数据集进行尝试。
输入:
import pandas
filepath = "data/training_2010.csv"
df = pandas.read_csv(filepath)
print(df) # print to check
输出:
Pandas库是处理数据的绝佳选择。
在上面的代码中,我们只是将CSV文件中的所有数据加载到Panda Dataframe(df)。我们现在有一个包含所有数据的大数据帧,我们需要选择所需的数据并从数据帧中提取它。如上所述,此数据集包含760行数据,每行包含49个特征。每行代表该队在一轮比赛中的所有数据。为了准备在PyTorch模型中使用的数据,我们需要遵循以下方式:
这个方法对于准备要在PyTorch模型中使用的数据非常方便。本文中,我们将使用BLC 2010数据集完成此方法,也可以用于其他数据集。
选择并提取特征
如上所述,数据集包含49个特征。在本文中,我们值使用其中的的27个特征。
输入:
extract = [5, 6, 7, 8, 9, 10, 13, 14, 16, 17, 18, 20, 21, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 35, 36, 37, 38, 47, 48, 49]
df = df.iloc[:, extract] # df with the desired columns (features)
print(df) # print to the check
输出:
在上面的代码中,我们创建了一个数组来选择我们想要的特征,其中列索引来自我们的。然后我们使用Pandas iloc函数从原始数据帧中提取所需的特征。可以尝试提取其他特征。重要提示:最后三个索引[47, 48, 49]是必不可少的,因为它们存储匹配结果。
分离数据进行训练和测试
我们将使用Pandas iloc函数将数据集拆分为训练和测试集。训练集是第1轮到第37轮每场比赛的统计数据。测试集是第38轮中每场比赛的统计数据。每轮包含10场比赛(即10行数据)。
输入:
training = df.iloc[:-10] # select all the rows except the last 10
test = df.iloc[-10:] # elect the last 10 rows
print(training) # print to check
print(test) # print to check
输出:
输出训练和测试数据时,您可以查看数据帧的维度。请注意,训练数据帧有[750行x 30列],这意味着我们有一个包含从第1轮到第37轮统计数据的数据帧。请注意,测试数据帧有[10行x 30列],这意味着我们有一个包含第38轮统计数据的数据帧。
将0到1之间的数据归一化
查看我们现在的值。值可以从0变化到300以上。我们需要将这些值归一化到0和1之间。我们将检查每个列(最后三个除外,即结果)的最大值,然后将该列的所有值除以10、100或1000。我们可以使用以下代码解决此问题。
输入:
# normalize the data between 0 and 1
for e in range(len(training.columns) - 3): # iterate for each column
# check the maximum value in each column
num = max(training.iloc[:, e].max(), test.iloc[:, e].max())
if num < 10:
training.iloc[:, e] /= 10
test.iloc[:, e] /= 10
elif num < 100:
training.iloc[:, e] /= 100
test.iloc[:, e] /= 100
elif num < 1000:
training.iloc[:, e] /= 1000
test.iloc[:, e] /= 1000
else:
print("Error in normalization! Please check!")
print(training) # print to check
print(test) # print to check
输出:
现在我们有两个数据帧(一个用于训练,一个用于测试),两者都在0和1之间归一化。
随机选择训练和测试集
为什么要随机选择数据?随机选择数据可确保模型保持通用性并且减少过度拟合。在我们的例子中,我们可能需要对数据进行混洗,因为该数据集按其类/目标(即团队/结果)进行排序。在这里,您需要进行随机选择,以确保您的训练/测试集能够代表数据的整体分布。
在训练集中每行代表一场比赛的一个队的统计数据。所有比赛都按顺序排列,这意味着第0行的球队与第1行的球队比赛;第2行的球队与第3行的球队比赛;以此类推。.这意味着如果在偶数行(0,2,4,......)中的球队赢得比赛,则下面的球队将失败。同时也意味着如果一支偶数队中的球队得到平局,下面的球队也会得到平局。这是因为第0行和第1行是相同的匹配。
随机选择是一项简单的任务,可以通过多种方式完成。我们使用Pandas中的sample方法来解决此问题。
输入:
training = training.sample(frac=1) # shuffle the training data
test = test.sample(frac=1) # shuffle the test data
print(training) # print to check
print(test) # print to check
输出:
frac参数指定了随机样本中要返回的行的比例,所以frac = 1表示返回所有行(随机顺序)。
将训练和测试集分成输入和输出
接下来要做的是将训练和测试集分成输入和输出集。这是一个简单的任务,我们将使用iloc来完成它。
输入:
# all rows, all columns except for the last 3 columns
training_input = training.iloc[:, :-3]
# all rows, the last 3 columns
training_output = training.iloc[:, -3:]
# all rows, all columns except for the last 3 columns
test_input = test.iloc[:, :-3]
# all rows, the last 3 columns
test_output = test.iloc[:, -3:]
print(test_input) #print to check
print(test_output) #print to check
输出:
可以输出所有结果来查看。我们在这里做的是将训练数据帧分成另外两个数据帧:training_input和training_output。我们对测试集做了同样的事情。
转换两个输出集
训练输入集和测试输入集已经准备就绪。现在我们可能需要转换两个输出集。因为两个输出集都包含三个列,分别代表win(赢),draw(平),defeat(输)。如果win列中有1,则表示该轮比赛中的该队赢得了比赛。平和输列的情况相同。这意味着我们有三个类,我们在模型的输出层需要三个节点/神经元。为了更好地处理我们的模型,我们将这三个类转换为两个类。
这个新的输出将代表这支球队是否获胜(0或者1)。因此,通过这种方式,我们将训练三种不同的神经网络模型:一个模型表示胜利,一个模型表示平局,一个模型表示失败。
本文中我将展示赢家的方案。输家和平局方案遵循相同的想法。如果队伍赢得了比赛,则输出值为1。如果队伍失败或者平局,则输出值为0。我们需要做的就是将三列输出转换为一列。
输入:
# separating the output into two classes: win or draw-defeat
# for the winners convert the output:
# from (1, 0, 0) to 1
# from (0, 1, 0) to 0
# from (0, 0, 1) to 0
def convert_output_win(source):
target = source.copy() # make a copy from source
target['new'] = 2 # create a new column with any value
for i, rows in target.iterrows():
if rows['win'] == 1:
rows['new'] = 1
if rows['draw'] == 1:
rows['new'] = 0
if rows['defeat'] == 1:
rows['new'] = 0
return target.iloc[:, -1] # return all rows, the last column
training_output = convert_output_win(training_output)
test_output = convert_output_win(test_output)
您可能希望创建一个函数来解决此问题,以便更轻松地维护/重用代码。
到目前为止我们已经完成了数据整理!
第二部分
这部分中我们将重点关注创建和设置神经网络模型。然后我们将训练和测试模型,最后我们将检查结果。
什么是PyTorch?
根据官方网站介绍,PyTorch是一个基于python的科学计算软件包:
PyTorch最好的东西之一是Tensors(张量)。Tensors与NumPy的ndarray类似,另外Tensors也可以用于GPU以加速计算。
安装PyTorch
安装PyTorch很简单。只需按照PyTorch官方网站的“入门”部分中的说明操作即可。
如何构造随机初始化矩阵的示例:
import torch
x = torch.rand(5, 3)
print(x)
首先
什么是人工神经网络(ANN)。根据维基百科:
人工神经网络是一种计算系统,它受到构成动物大脑的生物神经网络的启发,但不一定完全相同。这类系统通过考虑实例“学习”执行任务,通常不需要编写任何特定于任务的规则。
人工神经网络是基于一组被称为人工神经元的连接单元或节点的集合,这些单元或节点对生物大脑中的神经元进行的建模。每个连接,就像生物大脑中的突触一样,都能将信号从一个人工神经元传递到另一个。一个接收到信号的人工神经元可以处理它,然后向连接到它的其他人工神经元发出信号。
下图显示了神经网络图的一个例子。
构建前馈神经网络
现在让我们看看如何构建一个简单的前馈网络模型。前馈模型在输入和输出层之间具有隐藏层。在每个隐藏层之后,应用一个激活函数来引入非线性。下面是一个前馈模型的例子。
import torch
class Net(torch.nn.Module):
def __init__(self, input_size, hidden_size):
super(Net, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.fc1 = torch.nn.Linear(self.input_size, self.hidden_size)
self.relu = torch.nn.ReLU()
self.fc2 = torch.nn.Linear(self.hidden_size, 1)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
hidden = self.fc1(x)
relu = self.relu(hidden)
output = self.fc2(relu)
output = self.sigmoid(output)
return output
正如您在此示例中所看到的,我们创建的模型只包含一个隐藏层,但您可以根据需要添加任意数量的隐藏层。当我们有两个以上的隐藏层时,该模型也称为深层或多层前馈模型或多层感知器模型(MLP)。
在隐藏层之后,我们使用ReLU作为激活,然后将信息发送到输出层。这是为了从隐藏层的线性输出引入非线性。ReLU在这里做的是:如果函数应用于一组数值,则任何负值都将转换为0,否则这些值将保持不变。例如,如果输入集为[-2,0,3,-4,7],则函数将返回[0,0,3,0,7]。
我们使用Sigmoid作为输出激活函数。我们要做的预测是我们在第一部分中提到的二元分类任务,这意味着我们有二元类别可以预测。Sigmoid是一个很好用的函数,因为它计算目标输出为label1的概率(范围在0和1之间)。激活函数的选择取决于你的任务。
现在,让我们使用我们建立的这个模型来创建足球比赛的预测器。
训练模型
神经网络的典型训练程序如下:
数据
在第一部分中,我们的数据已准备就绪。我们有四个数据帧(training_input,training_output,test_input,test_output)。要在PyTorch中使用此集合,我们可能需要将这些数据帧转换为Tensors。使用torch.FloatTensor()很简单。
输入:
#convert to tensors
training_input = torch.FloatTensor(training_input.values)
training_output = torch.FloatTensor(training_output.values)
test_input = torch.FloatTensor(test_input.values)
test_output = torch.FloatTensor(test_output.values)
设置模型,标准,优化器
让我们定义模型,它的输入维度等于我们拥有的特征数量。对于隐藏维度,我们将其定义为30。对于损失函数(标准),我们使用BCELoss()(二进制交叉熵损失),因为我们的任务是对二进制结果进行分类。我们使用的优化器是SGD(随机梯度下降),学习率为0.9。
输入:
input_size = training_input.size()[1] # number of features selected
hidden_size = 30 # number of nodes/neurons in the hidden layer
model = Net(input_size, hidden_size) # create the model
criterion = torch.nn.BCELoss() # works for binary classification
# without momentum parameter
optimizer = torch.optim.SGD(model.parameters(), lr = 0.9)
#with momentum parameter
optimizer = torch.optim.SGD(model.parameters(), lr = 0.9, momentum=0.2)
训练模型
为了检验模型的改进情况,我们可以在模型训练前检验测试损失,并将其与训练后的测试损失进行比较。
输入:
model.eval()
y_pred = model(test_input)
before_train = criterion(y_pred.squeeze(), test_output)
print('Test loss before training' , before_train.item())
在mode.eval()这里设置PyTorch模块为评估模式。在此模式下,模型不会学习新的权重。我们只是想在训练前看到损失。为了训练,我们应该将模式切换回训练模式。
输入:
model.train()
epochs = 5000
errors = []
for epoch in range(epochs):
optimizer.zero_grad()
# Forward pass
y_pred = model(training_input)
# Compute Loss
loss = criterion(y_pred.squeeze(), training_output)
errors.append(loss.item())
print('Epoch {}: train loss: {}'.format(epoch, loss.item()))
# Backward pass
loss.backward()
optimizer.step()
现在开始进行训练,首先,我们将模块模式切换到model.train(),以便在每个周期之后可以学习新的权重。然后我们将训练的周期数定义为5000(您可以根据需要更改)。optimizer.zero_grad()在我们开始反向传播之前将梯度设置为零。这是必要的步骤,因为PyTorch积累了来自前一个周期的反向传递的梯度。我们还设置了一个误差数组以保持每个周期的损失,并在过程结束时绘制图表。
在正向传递和损失计算之后,我们通过调用loss.backward()来执行反向传递,其计算梯度。然后相应地使用optimizer.step()更新权重。训练过程可能需要一段时间,具体取决于您设置的训练周期。
测试模型
现在,训练已经完成。让我们看看训练后测试损失是如何变化的。同样,我们将模块模式切换回评估模式并检查测试损失,如下例所示。
输入:
model.eval()
y_pred = model(test_input)
after_train = criterion(y_pred.squeeze(), test_output)
print('Test loss after Training' , after_train.item())
您一定想知道我们的模型是否产生了好的或坏的预测。让我们看一下使用matplotlib库绘制一些图表。
import matplotlib.pyplot as plt
def plotcharts(errors):
errors = np.array(errors)
plt.figure(figsize=(12, 5))
graf02 = plt.subplot(1, 2, 1) # nrows, ncols, index
graf02.set_title('Errors')
plt.plot(errors, '-')
plt.xlabel('Epochs')
graf03 = plt.subplot(1, 2, 2)
graf03.set_title('Tests')
a = plt.plot(test_output.numpy(), 'yo', label='Real')
plt.setp(a, markersize=10)
a = plt.plot(y_pred.detach().numpy(), 'b+', label='Predicted')
plt.setp(a, markersize=10)
plt.legend(loc=7)
plt.show()
plotcharts(errors)
可视化数据和结果是很重要的事情之一!
这些图表非常简单,只是为了便于结果的可视化。我们不打算使用图表来证实或反驳模型的效率。
第一个图表是每个训练周期的误差进度。看看它在训练过程中如何减少。第二个图表用黄色圆圈表示实际值(Win :x = 1,Draw-Defeat : x = 0); 以及用蓝色十字表示预测值。蓝色十字越接近黄色圆圈,这个预测就越准确。如果蓝色十字在黄色圆圈内部,或者非常接近它,则意味着模型正确地预测了结果。换句话说,如果蓝色十字架远离黄色圆圈,则意味着模型对这场比赛做出了错误的预测。
在这个特殊情况下,我们的模型做出了8个正确的预测,2个错误的预测。我们的模型预测比赛结果的准确率达到80%。
为了改进预测,以下是可以应尝试更改的参数列表:
重要的提示:
正如我们前面提到的,这个例子演示了一个模型,这个模型可以预测一支球队是赢得比赛还是平局或者失败。运行其他模型以预测另外两个类别,即一个队伍是否会输掉一场比赛或打成平局;或者一支球队是平局还是会胜利或失败。为了正确执行此操作,请记住正确更改convert_output_win() 函数。
由于有许多不同的超参数和模型选择技术被广泛使用,所以,您可以选择提供最佳性能的其他超参数和模型结构。
在本文中我们介绍了很多内容:
我们希望您已经学会了如何使用神经网络来预测体育比赛的结果。
留言与评论(共有 0 条评论) |