神经网络分类任务

代码:
 


from pathlib import Path
import requests
#导入 Path 类用于处理文件路径,requests 库用于从网络下载文件。
DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"

PATH.mkdir(parents=True, exist_ok=True)# 这行代码调用PATH对象的mkdir方法来创建目录。mkdir是创建文件夹的命令
# parents = True表示如果父目录(这里是data目录)不存在,则先创建父目录。
# exist_ok = True表示如果目录已经存在,则不会引发异常。这个设置在脚本可能多次运行并且不需要重复创建已有目录的情况下非常有用。
URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"

if not (PATH / FILENAME).exists():#如果文件或目录存在,exists() 方法返回 True;如果不存在,则返回 False
        content = requests.get(URL + FILENAME).content
        (PATH / FILENAME).open("wb").write(content)
import pickle#这行代码导入pickle模块,pickle模块用于序列化和反序列化 Python 对象。在这里是为了后续从下载的文件中加载数据。
import gzip#这行代码导入gzip模块,因为下载的文件是.gz压缩格式的,需要使用gzip模块来解压缩
with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
#这行代码使用gzip.open以二进制读取模式(rb)打开PATH路径下的FILENAME文件(先解压缩)。as_posix()方法是将Path对象转换为适合gzip.open使用的字符串形式。然后使用with语句来确保文件在使用后正确关闭,将打开的文件对象赋值给f
    ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")
#在with语句块内,这行代码使用pickle.load从文件对象f中加载数据。数据被加载并解包为((x_train, y_train), (x_valid, y_valid), _)的形式,其中_表示可能存在的第三个元组中的未使用部分(可能是测试集数据,但这里没有使用它的名称)。
#这里使用encoding="latin-1"是因为文件可能是以这种编码方式存储数据的
print(x_train.shape)#784是mnist数据集每个样本的像素点个数
import torch
x_train, y_train, x_valid, y_valid = map(
    torch.tensor, (x_train, y_train, x_valid, y_valid)
)#这里使用了 Python 的 map 函数,将 torch.tensor 函数应用到 x_train、y_train、x_valid、y_valid 这四个对象上,
# 将它们转换为 PyTorch 张量。这样做是为了在 PyTorch 框架下进行后续的深度学习操作,如模型训练等。
n, c = x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
#这行代码计算了一些关于训练集数据的信息,包括 x_train 本身、x_train 的形状、y_train 中的最小值和最大值。不过,这里只是计算了这些值,没有对结果进行任何处理或保存。
print(x_train, y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())
import torch.nn.functional as F#导入 PyTorch 的神经网络函数库,这里主要是为了使用其中的 cross_entropy 函数作为损失函数。
loss_func = F.cross_entropy#定义了交叉熵损失函数,这是分类问题中常用的损失函数,用于衡量模型预测结果与真实标签之间的差异。
def model(xb):
    return xb.mm(weights) + bias
#定义了一个简单的线性模型函数 model。其中 xb 是输入的小批量(mini - batch)数据张量,假设其形状为 [batch_size, 784]。
#函数内部通过矩阵乘法 xb.mm(weights) 将输入数据与权重进行线性变换,然后加上偏置 bias,得到模型的输出
torch.randn([784, 10], dtype = torch.float,  requires_grad = True)
#这行代码创建了一个与前面定义的 weights 形状相同且具有相同属性(数据类型和需要计算梯度)的随机张量,但这个张量在后续代码中没有被使用,属于冗余代码。

bs = 64
xb = x_train[0:bs]  # a mini-batch from x
yb = y_train[0:bs]
weights = torch.randn([784, 10], dtype = torch.float,  requires_grad = True)
#创建了一个形状为 [784, 10] 的权重张量,数据类型为浮点数,并且设置 requires_grad = True,表示这个权重张量在计算过程中需要计算梯度。
#这个权重张量将用于线性模型中输入特征(维度为 784)与输出特征(维度为 10,可能对应 10 个类别)之间的线性变换。
bs = 64
bias = torch.zeros(10, requires_grad=True)
#创建了一个形状为 10 的偏置张量,数据类型为浮点数且需要计算梯度。偏置将被添加到线性变换的结果中。
print(loss_func(model(xb), yb))
#首先通过 model(xb) 调用之前定义的线性模型对小批量数据 xb 进行前向传播,得到模型的预测结果,然后将预测结果和对应的小批量标签 yb 传入损失函数 loss_func(之前定义为交叉熵损失函数 F.cross_entropy),
# 最后打印出计算得到的损失值。这一步是为了评估模型在这个小批量数据上的预测误差。


from torch import nn
#这行代码从 PyTorch 库中导入 nn 模块,nn 模块是 PyTorch 用于构建神经网络的核心模块,包含了如 Linear(全连接层)、各种激活函数等构建神经网络的基本组件。

class Mnist_NN(nn.Module):#定义了一个名为 Mnist_NN 的新类,它继承自 nn.Module。在 PyTorch 中,自定义的神经网络模型通常都继承自 nn.Module,这样可以方便地使用 PyTorch 提供的模型管理和训练功能。

    def __init__(self):#这是类的构造函数,用于初始化类的实例。
        super().__init__()#调用父类(nn.Module)的构造函数,确保在自定义类中正确初始化父类的属性和方法。这一步是必要的,因为 nn.Module 类内部有一些初始化操作,例如注册模型的参数等。
        self.hidden1 = nn.Linear(784, 128)#在模型的构造函数中,定义了第一个隐藏层 self.hidden1。nn.Linear 是 PyTorch 中的000全连接层,
        # 它接受输入维度为 784(对于 MNIST 数据集,每个图像展平后是 784 个像素点),输出维度为 128 的神经元。这个全连接层会自动创建权重和偏置参数。
        self.hidden2 = nn.Linear(128, 256)#定义了第二个隐藏层 self.hidden2,它的输入维度是第一个隐藏层的输出维度 128,输出维度为 256。
        self.out = nn.Linear(256, 10)#定义了输出层 self.out,其输入维度为第二个隐藏层的输出维度 256,输出维度为 10,对应于 MNIST 数据集的 10 个类别(数字 0 - 9)。

    def forward(self, x):#这是定义神经网络前向传播的函数。在 PyTorch 中,当调用模型实例时(例如 model(x)),实际上是调用这个 forward 函数。
        x = F.relu(self.hidden1(x))#首先,将输入 x 通过第一个隐藏层 self.hidden1 进行线性变换,然后对线性变换的结果应用 ReLU(Rectified Linear Unit)激活函数。
        # 这里假设已经导入了 F(可能是 torch.nn.functional),ReLU 函数用于引入非线性,使得神经网络能够拟合更复杂的函数关系。
        x = F.relu(self.hidden2(x))#将上一步的结果再通过第二个隐藏层 self.hidden2 进行线性变换,然后再次应用 ReLU 激活函数。
        x = self.out(x)#最后,将经过两个隐藏层处理后的结果通过输出层 self.out 进行线性变换,得到模型的最终输出。
        return x


net = Mnist_NN()#创建了一个 Mnist_NN 类的实例 net,这个实例就是一个可以用于训练和预测的神经网络模型。
print(net)#打印出神经网络模型的结构信息,包括模型中的各个层及其参数信息。


from matplotlib import pyplot#导入 matplotlib 库中的 pyplot 模块,用于数据可视化。matplotlib 是 Python 中常用的绘图库,可以绘制各种类型的图表,如折线图、柱状图、图像等。
import numpy as np

pyplot.imshow(x_train[0].reshape((28, 28)), cmap="gray")
#这行代码用于可视化 MNIST 数据集中的第一张图片。x_train 可能是已经加载和处理过的 MNIST 训练集数据,x_train[0] 表示取第一张图片的数据,
# 然后通过 reshape((28, 28)) 将展平的 784 个像素点重新恢复成 28x28 的图像形状,最后使用 pyplot.imshow 函数将图像显示出来,cmap = "gray" 指定使用灰度颜色映射来显示图像。

print(x_train.shape)#打印训练集数据 x_train 的形状,这有助于了解数据的结构,例如数据的维度等信息。
for name, parameter in net.named_parameters():#遍历神经网络模型 net 的所有命名参数。named_parameters 方法返回一个迭代器,它可以同时返回参数的名称(name)和参数本身(parameter)。
    print(name, parameter,parameter.size())#对于每个命名参数,打印出参数的名称、参数的值(部分显示,取决于数据量大小)以及参数的形状(size)。这有助于查看模型中各个层的权重和偏置的形状等信息。

from torch.utils.data import TensorDataset#从 PyTorch 的 torch.utils.data 模块中导入 TensorDataset 类。TensorDataset 用于将多个张量(如输入数据和对应的标签数据)打包成一个数据集对象,方便后续的数据加载和处理。
from torch.utils.data import DataLoader#导入 DataLoader 类。DataLoader 用于将数据集(如 TensorDataset 创建的数据集)按照指定的批量大小(batch_size)、是否打乱数据(shuffle)等方式加载数据,以便在训练和评估模型时使用。


train_ds = TensorDataset(x_train, y_train)
#使用 TensorDataset 将训练集数据 x_train 和对应的标签数据 y_train 打包成一个训练集数据集对象 train_ds。这样,train_ds 就包含了完整的训练数据和标签信息。
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True)
#使用 DataLoader 创建一个训练集数据加载器 train_dl。它会按照指定的批量大小 bs(之前应该已经定义过这个变量)加载训练集数据,并且在每个 epoch(训练轮次)开始时打乱数据顺序,这有助于提高模型的泛化能力。
valid_ds = TensorDataset(x_valid, y_valid)#类似于创建训练集数据集,使用 TensorDataset 将验证集数据 x_valid 和对应的标签数据 y_valid 打包成一个验证集数据集对象 valid_ds。
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)
#创建验证集数据加载器 valid_dl,它按照批量大小为 bs * 2(可能是为了在验证时使用更大的批量大小)加载验证集数据,但不打乱数据顺序(默认 shuffle = False)。
def get_data(train_ds, valid_ds, bs):#定义了一个名为 get_data 的函数,它接受训练集数据集 train_ds、验证集数据集 valid_ds 和批量大小 bs 作为参数。
    return (
        DataLoader(train_ds, batch_size=bs, shuffle=True),#在返回值中,首先返回按照批量大小 bs 且打乱顺序的训练集数据加载器。
        DataLoader(valid_ds, batch_size=bs * 2),#接着返回批量大小为 bs * 2 的验证集数据加载器。这个函数的作用是方便地获取训练集和验证集的数据加载器。
    )

import numpy as np

def fit(steps, model, loss_func, opt, train_dl, valid_dl):
    #定义了一个名为 fit 的函数,用于模型的训练和评估。它接受训练的步数(steps)、模型(model)、损失函数(loss_func)、优化器(opt)、训练集数据加载器(train_dl)和验证集数据加载器(valid_dl)作为参数
    for step in range(steps):#开始一个循环,按照指定的训练步数 steps 进行迭代。
        model.train()#在每个训练步开始时,将模型设置为训练模式。
        # 在训练模式下,模型中的一些层(如 Dropout 层、BatchNorm 层等)会有不同的行为,以适应训练过程中的需求。
        for xb, yb in train_dl:#对于训练集数据加载器中的每个小批量数据(xb 表示输入数据,yb 表示对应的标签数据)进行迭代。
            loss_batch(model, loss_func, xb, yb, opt)
            #调用 loss_batch 函数处理每个小批量数据。这个函数会计算小批量数据的损失、进行反向传播(如果优化器 opt 不为空)并更新模型参数。

        model.eval()#在完成一轮训练数据的处理后,将模型设置为评估模式。
        # 在评估模式下,模型中的一些层(如 Dropout 层)会停止随机操作,以提供更稳定的评估结果。
        with torch.no_grad():#使用 torch.no_grad() 上下文管理器,在这个范围内的操作不会计算梯度。因为在评估模型时不需要计算梯度来更新参数。
            losses, nums = zip(
                *[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]
            )#对于验证集数据加载器中的每个小批量数据,调用 loss_batch 函数(此时不传入优化器,因为不需要更新参数),得到每个小批量的损失值和数据数量。
            # 然后使用 zip 函数将所有小批量的损失值和数据数量分别打包成两个元组(losses 和 nums)。
        val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
        #计算验证集的平均损失。首先使用 np.multiply 将每个小批量的损失值乘以其数据数量,然后使用 np.sum 分别计算总和,最后相除得到平均损失值。

        print('当前step:'+str(step), '验证集损失:'+str(val_loss))
        #打印出当前的训练步数和验证集的平均损失值,这有助于监控模型的训练进度和评估模型在验证集上的性能。
from torch import optim#从 PyTorch 库中导入 optim 模块,optim 模块包含了各种优化算法,如随机梯度下降(SGD)、Adam 等,用于更新模型的参数。
def get_model():
    model = Mnist_NN()#在函数内部创建了一个 Mnist_NN 类的实例,即构建了一个神经网络模型。
    return model, optim.SGD(model.parameters(), lr=0.001)
#返回构建好的模型和一个使用随机梯度下降(SGD)算法的优化器。优化器的学习率设置为 0.001,它将用于更新模型的参数

def loss_batch(model, loss_func, xb, yb, opt=None):#定义了一个名为 loss_batch 的函数,用于处理小批量数据的损失计算和参数更新(如果需要)。
    loss = loss_func(model(xb), yb)#首先,将小批量输入数据 xb 通过模型得到预测结果,然后将预测结果和对应的小批量标签数据 yb 传入损失函数 loss_func,计算得到小批量数据的损失值

    if opt is not None:#判断优化器是否为空,如果不为空,表示处于训练模式,需要进行以下操作。
        loss.backward()#对损失值进行反向传播,计算模型参数的梯度。

        opt.step()#使用优化器根据计算得到的梯度更新模型参数。
        opt.zero_grad()#在更新完参数后,将优化器的梯度清零,为下一次的梯度计算和参数更新做准备。

    return loss.item(), len(xb)#返回小批量数据的损失值(通过 loss.item() 获取标量值)和小批量数据的数量(通过 len(xb) 获取)。

train_dl, valid_dl = get_data(train_ds, valid_ds, bs)#调用 get_data 函数,根据训练集数据集 train_ds、验证集数据集 valid_ds 和批量大小 bs 获取训练集和验证集的数据加载器。
model, opt = get_model()#调用 get_model 函数,获取构建好的神经网络模型和对应的优化器。
fit(25, model, loss_func, opt, train_dl, valid_dl)
#调用 fit 函数,使用构建好的模型、损失函数、优化器以及训练集和验证集的数据加载器进行 25 步的训练和评估。

损失到0.37/0.36左右就差不多了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值