1.初始化
在处理机器学习或深度学习的数据准备过程,将训练集和验证集转换为PyTorch张量类型以便于后续的模型训练。下面逐句解析代码的意义:
数据转换为 PyTorch Tensors
x_train, y_train, x_valid, y_valid = map(torch.tensor, (x_train, y_train, x_valid, y_valid))
这条语句的作用是将x_train
、y_train
、x_valid
、y_valid
(即训练特征、训练标签、验证特征、验证标签)都转化为torch.Tensor
对象,这是PyTorch中用于数值计算的基本数据结构。这一转化使得数据能够在GPU上加速运算,并且能够配合PyTorch神经网络模块和其他数学运算函数。
获取形状和范围
n, c = x_train.shape
这里x_train.shape
返回的是训练集特征的维度大小,赋值给了变量n
和c
:
n
: 训练样本的数量。它表示x_train
中有多少个样本或实例。c
: 特征数量或每个样本的维度。在图像识别任务中,如果是灰度图,则c
代表图片的宽度或高度;如果是彩色图像(例如RGB),则c=3
,分别对应红绿蓝三个通道。
在使用梯度下降算法进行模型训练时的常见操作。下面逐行解析这段代码:
定义批量大小
bs = 64
bs
代表批量大小(batch size),是每次迭代中模型将同时处理的数据量。在训练深度学习模型时,数据集通常被划分为多个小批量(batches),模型在每个小批量上进行前向和反向传播,然后更新权重。选择合适的批量大小对于训练效率和模型性能都很重要。
获取一个批次的数据
xb = x_train[0:bs] # a mini-batch from x
yb = y_train[0:bs]
这里xb
和yb
分别从训练集特征x_train
和标签y_train
中提取了前bs
个数据点,形成了一个批次的特征和标签数据。这些将被用于模型的单次前向传播和梯度计算。
初始化权重和偏置
weights = torch.randn([784, 10], dtype = torch.float, requires_grad = True)
weights
是模型的权重矩阵,其维度为[784, 10]
。这假设输入的特征向量长度为784(例如28x28的图像数据),模型有10个输出类别(比如数字0到9的分类)。torch.randn
函数用于从标准正态分布中随机初始化权重,requires_grad = True
指定了这个张量需要梯度计算,以便在反向传播中更新权重。
bias = torch.zeros(10, requires_grad=True)
bias
是模型的偏置向量,长度为10,初始化为全零向量。偏置项的作用是使模型能够拟合数据的偏移,它同样被标记为需要梯度计算。
整个过程描述了从数据准备到模型初始化的初步步骤,为后续的模型训练(前向传播、损失计算、反向传播和权重更新)奠定了基础。
2.定义了一个简单的多层感知机(MLP)模型
该模型被设计用来处理像MNIST这样的手写数字分类问题。下面是代码分析及完善后的版本:
import torch
import torch.nn as nn
import torch.nn.functional as F
class MnistModel(nn.Module): # 使用更具描述性的类名
def __init__(self):
super(MnistModel, self).__init__() # 调用父类构造方法
self.fc1 = nn.Linear(784, 128) # 输入层到隐藏层1
self.fc2 = nn.Linear(128, 256) # 隐藏层1到隐藏层2
self.fc3 = nn.Linear(256, 10) # 隐藏层2到输出层
def forward(self, x):
x = x.view(-1, 784) # 将输入重塑成 [batch_size, 784] 的一维向量
x = F.relu(self.fc1(x)) # 应用ReLU激活函数
x = F.relu(self.fc2(x)) # 应用ReLU激活函数
x = self.fc3(x) # 输出层没有激活函数
return x # 返回模型的输出结果
-
模型构造:
- 类定义
MnistModel
继承自nn.Module
。 - 构造函数调用了基类
nn.Module
的构造函数并通过super()
实现。 - 定义了三层线性变换(全连接层):两层隐含层和一层输出层。
- 类定义
-
前向传播:
- 在
forward
函数中定义了数据通过网络的流动过程。 - 输入首先被展平成一维向量,这是因为卷积层会生成二维或多维的特征图,而全连接层接收的是扁平化的向量。
- ReLU 激活函数应用于每一层全连接层后除最后一层外的所有层。
- 最终输出直接来自最后一个全连接层,不需要经过激活函数是因为交叉熵损失函数包含了Softmax操作。
- 在
-
可学习参数访问:
- 可以通过
model.named_parameters()
或者model.parameters()
来获取模型中的所有可学习参数的迭代器,这对于设置optimizer(优化器)非常重要,因为后者需要知道哪些参数应该被更新。
- 可以通过
这样构建的模型可以直接用于训练,只需要适配正确的输入数据格式(一般为 [batch_size, 1, 28, 28]
)并配置好损失函数(如 nn.CrossEntropyLoss
)和优化器(如 torch.optim.SGD
或 torch.optim.Adam
)。
3.主要处理函数流程
get_data函数
使用TensorDataset
和DataLoader
可以极大地简化数据处理和加载的过程,特别是在深度学习中。下面是对这段代码的详细解析:
使用TensorDataset
封装数据
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
train_ds = TensorDataset(x_train, y_train)
valid_ds = TensorDataset(x_valid, y_valid)
TensorDataset
接收多个Tensor
参数,通常是一个特征Tensor
(x_train
或x_valid
)和一个目标Tensor
(y_train
或y_valid
)。这些数据通常存储在内存中,适用于不需从磁盘实时读取数据的场景。- 通过使用
TensorDataset
,可以将特征数据和标签数据打包成一个数据集,便于后续使用DataLoader
进行处理。
使用DataLoader
加载数据
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)
DataLoader
是PyTorch中用于数据加载的工具,它可以从数据集中批量、高效地获取数据,并可进行数据的打乱(shuffle)以提高模型的泛化能力。batch_size
参数指定了数据加载的批量大小,shuffle
参数如果设置为True
,则每次迭代前数据会被打乱,这对于训练是很有益的。valid_dl
的batch_size
设置为bs * 2
,这是因为验证集通常不需要参与训练,因此可以使用更大的批量来加快验证过程的速度。
封装数据加载器
def get_data(train_ds, valid_ds, bs):
return (
DataLoader(train_ds, batch_size=bs, shuffle=True),
DataLoader(valid_ds, batch_size=bs * 2),
)
- 这个函数接受训练数据集、验证数据集以及批量大小作为参数,返回一个元组,包含了训练数据加载器和验证数据加载器。
- 这种封装方式使得数据加载的配置更加灵活,便于在不同的训练配置中重用。
总结
通过TensorDataset
和DataLoader
,可以方便地管理数据的加载和预处理,特别是在模型训练和验证过程中。这种方式不仅简化了数据处理的代码,还提高了数据加载的效率和模型训练的性能。
get_model
函数
from torch import optim
def get_model():
model = Mnist_NN()
return model, optim.SGD(model.parameters(), lr=0.001)
此函数的作用是初始化一个模型实例及对应的优化器。具体过程如下:
- 实例化模型:这里假设已经定义了一个名为
Mnist_NN
的类,该类继承自 PyTorch 的nn.Module
,代表一个可以处理MNIST数据集的神经网络结构。 - 创建优化器:基于模型的所有可学习参数(
model.parameters()
),使用随机梯度下降(SGD)作为优化算法,设定初始学习率为 0.001。
最终返回的是模型实例和优化器对象。
损失函数
def loss_batch(model, loss_func, xb, yb, opt=None):
loss = loss_func(model(xb), yb)
if opt is not None:
loss.backward()
opt.step()
opt.zero_grad()
return loss.item(), len(xb)
这个函数主要用于计算单个批次(batch)的数据在模型预测时的损失值,并在训练状态下更新模型参数。具体步骤如下:
- 计算损失:首先利用模型对输入数据
xb
进行前向传播得到预测结果,然后使用给定的损失函数loss_func
来比较预测结果与真实标签yb
的差距,从而获得损失值loss
。 - 参数更新:如果提供了优化器
opt
(即此函数是在训练过程中被调用),那么会进行梯度清零(opt.zero_grad()
)、反向传播(loss.backward()
)以及应用优化器更新规则(opt.step()
),完成一次迭代。 - 返回结果:最后,函数返回该批次的损失值(转换为 Python 原生数值类型)和批大小。
这两个函数都是构建机器学习/深度学习模型训练流程的基础组件。其中,get_model
函数负责初始化模型和优化器;loss_batch
函数则是每次迭代的核心部分,用来计算损失并在必要时更新模型参数。
训练流程
一般在训练模型时加上model.train(),这样会正常使用Batch Normalization和 Dropout
- 测试的时候一般选择model.eval(),这样就不会使用Batch Normalization和 Dropout
import numpy as np
def fit(steps, model, loss_func, opt, train_dl, valid_dl):
for step in range(steps):
model.train()
for xb, yb in train_dl:
loss_batch(model, loss_func, xb, yb, opt)
model.eval()
with torch.no_grad():
losses, nums = zip(
*[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]
)
val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
print('当前step:'+str(step), '验证集损失:'+str(val_loss))
实现一个简易的模型训练循环,通常在深度学习中用于训练神经网络模型。下面是对这段代码的详细中文解析:
import numpy as np
这一行导入了NumPy库,它是一个强大的数学计算库,常用于科学计算,特别是数组和矩阵运算。
def fit(steps, model, loss_func, opt, train_dl, valid_dl):
定义了一个名为fit
的函数,这个函数接受以下参数:
steps
: 训练的总步数,即整个训练集要遍历的次数(epoch数量);model
: 要训练的机器学习或深度学习模型;loss_func
: 损失函数,用于量化模型预测值与实际值之间的差距;opt
: 优化器,负责根据损失函数调整模型参数;train_dl
: 训练数据加载器,用于提供小批量(batch)的训练数据;valid_dl
: 验证数据加载器,用于评估模型在未见数据上的表现。
for step in range(steps):
...
接下来是一个主循环,按照指定的steps
数量进行迭代。
model.train()
将模型设为训练模式,这是很重要的一步,因为在训练和验证/测试时,一些层的行为会不同,比如Batch Normalization和Dropout。
for xb, yb in train_dl:
loss_batch(model, loss_func, xb, yb, opt)
内部嵌套循环,遍历每个批次的数据(xb
表示输入特征,yb
表示对应的目标标签),并将它们传递给loss_batch
函数执行单个批次的前向传播、计算损失、梯度下降等步骤。
model.eval()
with torch.no_grad():
...
将模型转换为评估模式,并开启无梯度计算上下文,防止在模型评估期间积累不必要的计算图,节省内存和提高速度。
losses, nums = zip(*[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl])
这里用列表推导式对验证集的每一批数据计算损失,然后把所有这些损失和相应批次大小存入losses
和nums
元组中。
val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
计算平均验证损失,通过乘以每个批次的样本数再求和得到加权损失总和,最后除以总的样本数获得平均损失。
print('当前step:' + str(step)...)
打印当前轮次的信息,便于监控训练进程。
整体来看,实现了基本的监督学习训练流程,包括多个epoch的训练和验证阶段,是深度学习模型训练的一个常见模板。
4.总结主流程
### 三行搞定!
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
model, opt = get_model()
fit(25, model, loss_func, opt, train_dl, valid_dl)
汇总了一个典型深度学习模型训练流程的主要步骤,下面将对每一行进行详细解析:
1. train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
这一行负责创建训练数据加载器 (train_dl
) 和验证数据加载器 (valid_dl
)。这里的 get_data
是一个自定义的函数,它接收三个参数:
train_ds
: 训练数据集对象。valid_ds
: 验证数据集对象。bs
: batch size,即每次训练时使用的样本数量。
数据加载器 (DataLoader
) 在PyTorch中是一个非常重要的组件,它可以从数据集中批量读取数据,并在多个数据点上执行mini-batch梯度下降。这不仅提高训练效率,还通过随机采样帮助避免过拟合。
2. model, opt = get_model()
在这一步骤中,get_model
函数被调用,返回模型对象 (model
) 和优化器 (opt
)。
-
model
: 这是由get_model
函数初始化并返回的具体神经网络模型。这个函数可能包含模型结构的定义和初始化权重的操作。 -
opt
: 优化器对象,用于更新模型的参数。常见的优化器有SGD(随机梯度下降)、Adam等等,它们决定了如何根据计算得到的梯度调整模型的权重。
3. fit(25, model, loss_func, opt, train_dl, valid_dl)
最后这行代码调用了训练循环(fit
函数),这是整个模型训练的核心部分。它接受以下参数:
-
25
: 迭代轮数或epoch数量,表示模型将在完整数据集上训练的次数。 -
model
: 上面提到的模型对象,用于训练和评估。 -
loss_func
: 损失函数,它是衡量模型预测值与真实标签之间差距的一种方式。常见类型有交叉熵损失、均方误差等。 -
opt
: 之前创建的优化器,用于更新模型参数。 -
train_dl
: 训练数据加载器,提供每一批次的数据供模型训练。 -
valid_dl
: 验证数据加载器,用于在每个epoch结束时评估模型的泛化能力,而不改变模型的权重。
综上所述,这段代码实现了模型的定义、数据准备、模型训练和性能评估的基本流程。在实际应用中,get_data
, get_model
和 fit
函数的具体实现可能会有所不同,但整体逻辑遵循上述框架。