目录
实验前准备
本实验是在Anaconda下的jupyter notebook上进行的,使用的代码语言为python。在开始实验前,我们首先需要导入所需要的库与包或者模块。本实验是一个神经网络的实验,需要处理大量的实验数据,需要处理多维数组对象,以及可能还需要画图进行可视化处理,还有一些数学公式的运用,所以我们需要导入的包为numpy、pandas、math以及matplotlib.pyplot。同时我们可能还需要使用random函数来随机选取标签,所以我们还需要导入random。而在本次实验中,我们采用的是pytorch框架来构建神经网络,所以我们还需要导入torch以及与其相关的一些包和库。而在我们后面的实验数据处理当中,我们需要将特征和标签列进行整合成一个dataset形式的数据,所以我们还需要将torch.utils.data包进行导入。
实验要求:
代码实现:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Function
import os
from torch.utils.data import TensorDataset
读入数据集并且进行处理
实验要求:
代码实现:
#your code here
# 首先将数据集读进,格式为dataframe
train_data=pd.read_csv("wine_train.csv")
test_data=pd.read_csv("wine_test.csv")
# 然后将dataframe格式的数据转换为numpy格式的数据
train_data=np.array(train_data)
test_data=np.array(test_data)
# 最后将numpy格式的数据转换为terson格式的数据
train_data=torch.from_numpy(train_data)
test_data=torch.from_numpy(test_data)
train_data=train_data.double()
test_data=test_data.double()
# 获取训练集和测试集对应的特征列和标签列
train_feature=train_data[:,:11]
train_label=train_data[:,11]
test_feature=test_data[:,:11]
test_label=test_data[:,11]
# 将特征和标签列组装成dataset的形式,方便后续迭代训练需要
train_dataset=TensorDataset(train_feature,train_label)
test_dataset=TensorDataset(test_feature,test_label)
实验结果分析:如图所示,我们首先通过read_csv将训练数据集和测试数据集进行导入,此时的数据集格式为dataframe,然后我们又调用np.array()函数将dataframe格式的数据转换为numpy格式的数据,最后由于我们是使用了pytorch框架进行神经网络的搭建,所以我们还需要将数据转换为terson格式的数据,我们调用了函数torch.from_numpy()。最后由于我们搭建的神经网络的数据类型为double,我们还需要将数据全部修改成double类型的数据。然后我们继续获取训练集和测试集对应的特征列和标签列,最后将训练集的特征列和标签列组成dataset的数据格式,类似地,将测试集的特征列和标签列组成dataset的数据格式,方便后面的神经网络的迭代训练。
搭建一个神经网络
实验要求:
代码实现:
#your code here
# 定义一个神经网络,继承自nn.Module
class Net(nn.Module):
# 输入层的维度为input_dim
# 隐藏层的维度为hidden_dim
# 输出层的维度为output_dim
def __init__(self,input_dim,hidden_dim,output_dim):
super(Net, self).__init__()
# 利用线性层建立了两个全连接层
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, output_dim)
#激活函数relu,用于在全连接层之间加入非线性变换
self.relu = nn.ReLU()
# 将模型的权重和偏置项的数据类型设置为Double
self.fc1.weight = nn.Parameter(self.fc1.weight.double())
self.fc1.bias = nn.Parameter(self.fc1.bias.double())
self.fc2.weight = nn.Parameter(self.fc2.weight.double())
self.fc2.bias = nn.Parameter(self.fc2.bias.double())
# 前向传播
def forward(self, x):
out = self.fc1(x)
out1 = self.relu(out)
out2 = self.fc2(out1)
return out1,out2
# 根据数据集的维度确定输入和输出的维度
input_dim=11
output_dim=1
# 定义隐藏层的维度
hidden_dim=6
# 创建一个神经网络实例
net = Net(input_dim,hidden_dim,output_dim)
print(net)
实验结果分析:如图所示,我们需要定义一个神经网络,其继承自nn.Module。然后在这一个神经网络里面,我们定义了一个初始化的函数,传入的参数为输入层的维度、隐藏层的维度、输出层的维度,然后在该函数内部,我们利用线性层建立了两个全连接层,分别连接了输入层和隐藏层、隐藏层和输出层之间,最后使用了激活函数relu,用于在全连接层之间加入非线性变换。然后我们还需要将神经网络模型内部的权重和偏置项的数据类型设置为double。
然后我们在该神经网络的内部,我们还定义了一个函数,用于处理前向传播,首先计算输入层的结果,然后再计算该结果的relu函数值,最后将该函数值用于计算得到从隐藏层到输出层的结果。然后我们将relu函数值和输出层的结果作为返回值进行返回。
然后我们根据题意,训练集和测试集的特征维度都为11,而且标签列都为1,所以我们需要设定该神经网络的输入层维度为11,输出层维度为1,而我们中间的隐藏层是随意的,那么我们在这里就将隐藏层设置为了5。然后我们通过前面构建的神经网络来创建一个神经网络实例,最后我们将该实例进行打印。
模型参数更新
实验要求:
代码实现:
#your code here
# 定义损失函数
criterion = nn.MSELoss()
# 定义训练参数
Epoch=30
learning_rate=0.01
# 定义优化器,随机梯度下降优化器
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
# 初始化列表用于存放每一轮迭代中的训练损失和测试损失
train_losses=[]
test_losses=[]
# 开始迭代
for epoch in range(Epoch):
# 训练模式
# 初始化这一轮迭代的训练损失为0
train_loss=0.0
# 遍历训练数据集
for feature,label in train_dataset:
#梯度清零
optimizer.zero_grad()
# 前向传播
output1,output2=net(feature)
# 计算损失
loss=criterion(output2,label)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 将损失进行累加
train_loss += loss.item()
# 计算平均训练损失
train_loss/=len(train_dataset)
# 将计算得到的训练损失加入到训练损失的列表中
train_losses.append(train_loss)
# 测试模式
net.eval()
# 初始化这一轮迭代的测试损失为0
test_loss=0.0
# 遍历测试数据集
for feature,label in test_dataset:
# 前向传播
output1,output2=net(feature)
# 计算损失
loss=criterion(output2,label)
# 将损失进行累加
test_loss+=loss.item()
# 计算平均测试损失
test_loss/=len(test_dataset)
# 将计算得到的测试损失加入到测试损失的列表中
test_losses.append(test_loss)
# 打印训练和测试损失
print('Epoch [{}/{}], Train Loss: {}, Test Loss: {}'.format(epoch+1, Epoch, train_loss, test_loss))
实验结果分析:如图所示,我们首先定义了一个损失函数,通过模型内置的nn.MSELoss()函数来创建该损失函数。然后我们需要首先定义一些超参数,我们定义的迭代轮数Epoch为30,学习率为0.01,然后定义了一个优化器,是一个随机梯度下降的优化器,一样是通过内置函数的优化器来实现。同时我们还需要初始化列表用于存放每一轮迭代中的训练损失和测试损失。
然后我们便开始迭代,通过for循环,一共迭代Epoch=30次。在每一次的迭代过程中,我们都分为训练模式和测试模式。首先是训练模式,我们通过net.train()开启了训练模式,然后初始化这一轮迭代的训练损失为0。然后我们需要迭代所有的训练样本数据,我们可以使用前面数据处理阶段的train_dataset。我们首先需要将梯度进行清零,然后调用我们构建的神经网络进行前向传播,传入的参数为训练数据的特征列,然后根据前向传播的结果output2和训练数据的标签列进行训练损失的计算。在计算完训练损失loss后,我们开始反向传播,直接通过loss的backword函数来进行反向传播即可,然后我们通过优化器来进行参数的更新,最后我们再将该训练损失进行累加即可。在遍历完所有的样本是数据后,我们需要计算所有样本的平均训练损失,可以直接将累加的训练损失除以该数据的样本数即可。然后我们需要将计算得到的平均训练损失加入到前面定义好的训练损失列表中。
在计算得到训练损失后,我们便要开始测试模式,我们直接通过net.eval()便可开启测试模式,首先还是需要初始化这一轮迭代的测试损失为0。然后便可以开始遍历所有的测试样本数据,在这里我们一样需要使用我们前面数据处理阶段整理好的test_dataset数据。我们首先通过前面我们构建好的神经网络来进行前向传播,传入的参数为测试数据的特征列,然后我们根据前向传播的结果output2以及标签列来计算得到测试损失loss。由于这里是测试模式,所以我们不需要进行参数的更新,所以我们不需要进行反向传播,所以我们直接将该计算得到的测试损失进行累计即可。在遍历完了所有的样本后,我们还需要计算所有样本的平均测试损失,我们直接将累加的测试损失除以该样本的数量即可。最后我们将计算得到的该平均测试损失加入到前面定义好的测试损失的列表中即可。
在最后,我们将我们每一轮迭代的结果计算得到的训练损失和测试损失进行打印,结果如上所示。我们可以知道,训练损失是随着迭代轮次的增加而减小的,整体来说,测试损失也是如此,但是有时候会出现一些波动情况,即有时候测试损失反而会增加。
损失的折线图
实验要求:
代码实现:
#your code here
# 画出训练损失和测试损失关于迭代轮数的折线图
plt.plot(range(1, epoch+2), train_losses, '-g', label='Train Loss')
plt.plot(range(1, epoch+2), test_losses, '-b', label='Test Loss')
# 为图像添加标题
plt.title('Training and Test Loss')
# 为图像增加x轴名称
plt.xlabel('Epochs')
# 为图像增加y轴名称
plt.ylabel('Loss')
# 显示图例,其中包含了之前设置的标签
plt.legend()
# 显示图像
plt.show()
实验结果分析:如图所示,我们在前面进行迭代的时候,我们已经将我们计算得到的训练损失和测试损失添加到了列表中,我们利用该列表中的数据进行绘制图像即可。我们首先需要画出训练损失和测试损失关于迭代轮次的折线图,使用plot函数,然后我们使用函数来为图像添加x和y轴的名称。随后使用legend函数来显示图例,其中包含了之前设置的标签。最后我们使用show函数来显示图像即可。
我们画出的训练损失和测试损失随迭代轮次变化的折线图如图所示。根据图像,我们可以知道,训练损失和测试损失都是随着迭代轮次而减小的,折线比较圆滑。我们对比了其它同学所画出来的折线图,发现我们的图像都是大同小异,而且变化的规律都是差不多的,所以我们可以知道我们的实验是成功的,我们所画出来的折线图是正确无误的,我们所编写的代码也是没有什么问题的。
实验总结
神经网络:
- 基本概念:神经网络是一种模仿生物神经系统的计算模型,由多个神经元组成的层级结构。每个神经元接收来自前一层的输入数据,并通过激活函数对输入进行加权求和和非线性转换,输出结果传递给下一层神经元。
- 基本原理:神经网络通过学习和调整权重,以逐渐提取输入数据的特征并生成预测结果。这是通过前向传播和后向传播的迭代过程实现的。
- 适用条件:神经网络适用于各种任务,包括图像识别、自然语言处理、语音识别等。特别是对于复杂的非线性问题,神经网络通常能够取得较好的效果。
- 优缺点:神经网络的优点包括能够处理大量的输入数据、具备良好的泛化能力和自适应能力。但是,神经网络的训练过程相对较慢,需要大量的计算资源,并且对超参数的选择和网络结构的设计具有一定的挑战性。
前向传播:
- 基本概念:前向传播是神经网络的基本操作之一,用于将输入数据通过网络的层级结构并计算输出结果。每个神经元将输入数据与权重相乘并经过激活函数进行非线性转换,然后将结果传递给下一层神经元。
- 基本原理:前向传播从输入层开始,逐层计算每个神经元的输出,直到达到输出层。每一层的输出作为下一层的输入,这样数据就能够在网络中流动。
- 适用条件:前向传播在神经网络的训练和推断阶段都是必需的。在训练阶段,它用于计算预测值并与实际标签进行比较以计算损失;在推断阶段,它用于生成模型的输出结果。
- 优缺点:前向传播是神经网络中的基础计算步骤,计算过程相对简单高效。然而,它只能生成预测结果,无法更新网络参数。
后向传播:
- 基本概念:后向传播是训练神经网络的关键步骤,用于计算损失函数对网络参数的梯度,并根据梯度下降的原理来更新网络参数,使得预测结果逐渐接近实际标签。
- 基本原理:后向传播使用链式法则计算梯度,从输出层向输入层进行反向传播。通过计算损失函数对每个参数的偏导数,可以得到参数的梯度值,然后使用优化算法更新参数。
- 适用条件:后向传播适用于需要训练神经网络的任务。通过计算梯度并更新参数,后向传播使得网络能够根据训练数据进行调整和优化。
- 优缺点:后向传播是训练神经网络的关键步骤。它能够高效地计算参数的梯度,并且通过梯度下降的方式更新参数,从而提高模型的性能。然而,后向传播的计算复杂度较高,尤其在深层网络中可能会出现梯度消失或梯度爆炸的问题,需要进行适当的优化和正则化处理。
训练损失和测试损失:
- 基本概念:训练损失是用于衡量模型在训练数据上的拟合程度的指标,通常使用损失函数来计算。测试损失是用于评估模型在未见过的测试数据上的泛化能力的指标。
- 基本原理:在每个训练迭代中,通过前向传播计算预测值,并与实际标签进行比较,从而计算训练损失。测试损失通过将模型应用于测试数据集并计算预测结果与实际标签之间的差异来计算。
- 适用条件:训练损失和测试损失在训练和评估神经网络时都是必需的。训练损失用于监控模型在训练数据上的拟合情况,测试损失用于评估模型在未知数据上的性能。
- 优缺点:训练损失可以指示模型在训练数据上的学习情况,而测试损失可以评估模型的泛化能力。然而,过拟合和欠拟合等问题可能会影响损失的有效性,因此需要结合其他评估指标进行综合考虑。
PyTorch框架:
- 基本概念:PyTorch是一个基于Python的开源机器学习框架,专注于深度学习任务。它提供了丰富的工具和函数,简化了神经网络的构建、训练和优化过程。
- 基本原理:PyTorch基于动态计算图的概念,允许用户在模型构建过程中使用动态图来定义和修改计算过程。PyTorch还提供了自动求导的功能,可以自动计算张量的梯度,简化了后向传播的实现。
使用PyTorch的优化器进行参数更新:
- 基本概念:PyTorch提供了各种优化器,如随机梯度下降(SGD)、Adam、Adagrad等,用于更新神经网络的参数。优化器根据参数的梯度和学习率等参数来调整参数的数值。
- 基本原理:在每个训练迭代中,通过后向传播计算参数的梯度,然后使用优化器根据梯度和学习率来更新参数。优化器可以根据不同的算法和策略来调整参数的更新过程。
- 适用条件:使用优化器进行参数更新适用于神经网络的训练过程。通过优化器,可以根据梯度下降的原理来调整网络参数,使得模型逐渐优化并提高性能。
- 优缺点:优化器提供了方便的接口和算法,可以自动处理参数的更新过程。不同的优化器具有不同的优劣势,在实际应用中需要根据具体问题和数据集来选择合适的优化器。