第一步:导包
导入所使用的的包
import numpy as np
import torch
from torch.utils import data
from matplotlib import pyplot as plt
import torchvision
import torchvision.transforms as transforms
第二步:构建数据集
加载或者下载所训练和测试数据集
参数:位置,是否是训练集,下载与否,
mnist_train = torchvision.datasets.MNIST(root='~/Datasets/MNIST', train=True, download=True,
transform=transforms.ToTensor())
mnist_test = torchvision.datasets.MNIST(root='~/Datasets/MNIST', train=False, transform=transforms.ToTensor())
第三步:定义数据迭代器
定义批次量batch_size为128,然后定义迭代器iter,shuffle为是否打散数据。num_workers=0是windows系统。
# 通过DataLoader 读取小批量数据样本
batch_size = 128
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=0)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=0)
第四步:定义模型及其前向传播过程
首先定义模型参数,输入大小num_inputs,输出大小num_outputs,中间大小num_hiddens。
第一层的权重和偏置,第二层的权重和偏置。self.params
是包含可训练参数的列表,接下来requires_grad属性设置为True是计算网络的前向传播和反向传播过程中,上述参数会被保留并计算梯度。一边根据损失函数更新参数的值。通过requires_grad会自动求导和参数的更新。很大程度上降低了难度。
接下来定义模型的结构。三层结构,输入层,隐藏层和输出层。
@staticmethod
def my_ReLU(x):
return torch.max(input=x, other=torch.tensor(0.0))
然后就是神经网络的前向传播过程,通过数据x通过各层(输入,隐藏和输出)的运算(就是上面定义的)饭后最终的输出结果。
首先调用输入层,也就是进行扁平化。然后将得到的输出作为输入投入隐藏层。最后将结果作为输出层的输入传入输出层。得到结果。
class Net():
def __init__(self):
# 定义并初始化模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens, num_inputs)), dtype=torch.float32)
b1 = torch.zeros(1, dtype=torch.float32)
W2 = torch.tensor(np.random.normal(0, 0.01, (num_outputs, num_hiddens)), dtype=torch.float32)
b2 = torch.zeros(1, dtype=torch.float32)
# 上述四个变量求梯度
self.params = [W1, b1, W2, b2]
for param in self.params:
param.requires_grad_(requires_grad=True)
# 定义模型的结构
self.inputs_layer = lambda x: x.view(x.shape[0], -1)
self.hiddens_layer = lambda x: self.my_ReLU(torch.matmul(x, W1.t()) + b1)
self.outputs_layer = lambda x: torch.matmul(x, W2.t()) + b2
@staticmethod
def my_ReLU(x):
return torch.max(input=x, other=torch.tensor(0.0))
def forward(self, x):
flatten_input = self.inputs_layer(x)
hidden_output = self.hiddens_layer(flatten_input)
final_output = self.outputs_layer(hidden_output)
return final_output
第五步:定义损失函数及优化算法
loss_func = torch.nn.CrossEntropyLoss()
用于计算交叉熵损失的CrossENtropLoss对象。交叉熵损失经常用于多分类任务,在神经网络中作为损失函数来衡量预测结果与真实标签之间的差距。
使用随机梯度下降(SGD)优化算法,第一个参数params包含待更新参数的列表或者迭代器。lr是学习率表示更新参数时的步长大小。
在函数内部,使用一个循环遍历params中的每个参数param,param.grad表示param的梯度值。
对于每个参数都通过param.data -= lr * param.grad
这样的操作进行更新,即减去学习率乘以梯度值的方式,更新参数数值。
SGD这种优化方法更具梯度值和学习率更新参数数值,以最小化损失函数。
为什么通过减去学习率乘以梯度的方式更新参数的数值?
这样做是为了沿着梯度的反方向进行参数更新,从而逐渐接近或者达到损失函数的最小值。 梯度代表了损失函数关于参数的变化率,梯度的方向指示了损失函数下降最快的方向,即参数更新的方向,学习率决定了每次参数更新的步长大小。
loss_func = torch.nn.CrossEntropyLoss()
def SGD(params, lr):
for param in params:
param.data -= lr * param.grad
第六步:定义测试函数
def tesst(data_iter, net, loss_func):
test_loss_sum, c = 0.0, 0 # 累计测试集上的损失值和样本数量
for X, y in data_iter:# X是输入特征,y是对应的真实标签。
result = net.forward(X)# 调用前向 传播方法,
test_loss_sum += loss_func(result, y).item() # 调用损失函数。
c += 1 # 样本+1
return test_loss_sum / c # 返回平均损失。
三个参数,(数据迭代器,神经网络模型,计算损失的函数),这个函数的作用主要是评估模型的性能。通过计算平均损失来衡量模型的拟合效果。较低的平均损失值代表了模型对测试集的预测结果和真是标签之间的差距较小,即模型具有较好的性能。
第七步:定义模型训练函数
首先分析一下接受的参数(神经网路模型,训练数据的迭代器,损失函数,训练轮次,小批量样本的数量,学习率,优化器)。
两个列表用于保存每轮训练集和测试集上的损失值。
dim指的是哪一个维度,y_hat是一个大小为[batch_size,num_classes]的张量,
def train(net, train_iter, loss_func, num_epochs, batch_size, lr=None, optimizer=None):
train_loss_list = []
test_loss_list = []
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n, c = 0.0, 0.0, 0, 0# 损失值和准确率,样本数量,批次量
for X, y in train_iter: # x和y分别是小批量样本的特征和标签
y_hat = net.forward(X)# 前向传播,
l = loss_func(y_hat, y)# 计算损失
l.backward()# 反向传播,计算梯度
optimizer(net.params, lr)# 优化器更新模型的参数
for param in net.params:
param.grad.data.zero_()# 初始化
train_l_sum += l.item()# 累加损失值
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item() # 累加精确度,
n += y.shape[0]# 累加样本数
c += 1# 累加批次量
test_loss = tesst(test_iter, net, loss_func)# 计算在测试集上的损失
train_loss_list.append(train_l_sum / c)# 将训练集的损失值添加进去
test_loss_list.append(test_loss)# 将测试集的损失值添加进去
# draw_loss(train_l_sum/c, test_loss, None)
print('epoch %d, train_loss %.4f,test_loss %.4f' % (epoch + 1, train_l_sum / c, test_loss))# 打印轮次。
return train_loss_list, test_loss_list
第八步:结果可视化
传入的参数(训练集上的损失值列表,测试集上的损失值列表,验证集的损失值(可选可不选))。
首先生成一个长度为列表长度的等差序列。
def draw_loss(train_loss, test_loss, valid_loss=None):
x = np.linspace(0, len(train_loss), len(train_loss)) \
if valid_loss is None else np.linspace(0, len(train_loss), len(test_loss), len(valid_loss))
plt.plot(x, train_loss, label="Train_Loss", linewidth=1.5)
plt.plot(x, test_loss, label="Test_Loss", linewidth=1.5)
if valid_loss is not None:
plt.plot(x, test_loss, label="Valid_loss", linewidth=1.5)
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.legend()# 添加图例
plt.show()
第九步:训练模型
if __name__ == "__main__":
net = Net()
num_epochs = 100
lr = 0.03
optimizer = SGD
train_loss, test_loss = train(net, train_iter, loss_func, num_epochs, batch_size, lr, optimizer)
draw_loss(train_loss, test_loss)
通过Net()创建一个神经网络实例,然后定义迭代轮次,学习率,优化器。然后进行训练。然后通过draw_loss进行可视化。