深度学习训练框架——监督学习为例

训练框架


本文内容以pytorch为例

1. 模型网络结构

自定义网络模型继承‘nn.Module’,实现模型的参数的初始化与前向传播;自定义网络模型可以添加权重初始化、网络模块组合等其他方法

        import torch.nn as nn
        import torch.nn.functional as F

        class Model(nn.Module):
            def __init__(self):
                super().__init__()
                self.conv1 = nn.Conv2d(1, 20, 5)
                self.conv2 = nn.Conv2d(20, 20, 5)

            def forward(self, x):
                x = F.relu(self.conv1(x))
                return F.relu(self.conv2(x))

2. 数据读取与数据加载

数据集类的基础方法

  • dataset:

需要包含数据迭代方法

def __getitem__(self, index):
        image, target = self.list(index)
        return image, target

利用torch.utils.data.DataLoader封装后,用于迭代遍历数据元素;

数据长度方法

def __len__(self):
    return self.dataset_size

数据加载

  • dataloader:
    对数据集类(通常实现了 getitemlen 方法)时,
    你可以使用 DataLoader 来轻松地进行批量加载、打乱数据、并行加载以及多进程数据加载。
    collate_fn:将字典或数组数据流进行拆分,拆分为图像、label、边界框、文字编码等不同类型数据与模型的输入与输出相匹配

2.1Dataloater参数

参数:

  • dataset (Dataset): 加载数据的数据集。
  • batch_size (int, 可选): 每批加载的样本数量(默认:1)。
  • shuffle (bool, 可选): 设置为 True 以在每个 epoch 重新洗牌数据(默认:False)。
  • sampler (Sampler 或 Iterable, 可选): 定义从数据集中抽取样本的策略。可以是任何实现了 len 的 Iterable。如果指定了 sampler,则不能指定 :attr:shuffle。
  • batch_sampler (Sampler 或 Iterable, 可选): 与 :attr:sampler 类似,但一次返回一批索引。与 :attr:batch_size, :attr:shuffle, :attr:sampler, 和 :attr:drop_last 互斥。
    num_workers (int, 可选): 数据加载使用的子进程数量。0 表示数据将在主进程中加载(默认:0)。
  • collate_fn (Callable, 可选): 将样本列表合并以形成 Tensor(s) 的 mini-batch。在使用 map-style 数据集的批量加载时使用。
  • pin_memory (bool, 可选): 如果设置为 True,则数据加载器将在返回它们之前将 Tensors 复制到设备/CUDA 固定内存中。如果你的数据元素是自定义类型,或者你的 :attr:collate_fn 返回的批次是自定义类型,请参见下面的例子。
  • drop_last (bool, 可选): 设置为 True 以丢弃最后一个不完整的批次,如果数据集大小不能被批量大小整除。如果设置为 False 并且数据集大小不能被批量大小整除,则最后一个批次会较小(默认:False)。
  • timeout (numeric, 可选): 如果为正数,这是从工作进程收集一个批次的超时值。应始终为非负数(默认:0)。
  • worker_init_fn (Callable, 可选): 如果不是 None,这将在每个工作进程子进程上调用,输入为工作进程 id(一个在 [0, num_workers - 1] 中的 int),在设置种子和数据加载之前。(默认:None)
  • generator (torch.Generator, 可选): 如果不是 None,这个 RNG 将被 RandomSampler 用来生成随机索引,并被多进程用来为工作进程生成 base_seed。(默认:None)
  • prefetch_factor (int, 可选,关键字参数): 每个工作进程预先加载的批次数量。2 意味着将有总共 2*num_workers 个批次被预先加载。(默认值取决于 num_workers 的设定值。如果 num_workers=0,默认是 None。否则如果 num_workers>0,默认是 2)。
  • persistent_workers (bool, 可选): 如果设置为 True,则数据加载器在数据集被消费一次后不会关闭工作进程。这允许保持工作进程的 Dataset 实例存活。(默认:False)。
  • pin_memory_device (str, 可选): 如果将 pin_memory 设置为 true,则数据加载器将在返回它们之前将 Tensors 复制到设备固定内存中。

2.2 collate_fn

class CollateFunc(object):
    def __call__(self, batch):
        targets = []
        images = []

        for sample in batch:
            image = sample[0]
            target = sample[1]

            images.append(image)
            targets.append(target)

        images = torch.stack(images, 0) # [B, C, H, W]

        return images, targets

3. 优化器与学习率调整

3.1 优化器

在训练过程中,根据梯度变化、损失函数、动量(momontum)、学习率来调整模型参数

optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler = ExponentialLR(optimizer, gamma=0.9)

for epoch in range(20):
    for input, target in dataset:
        optimizer.zero_grad()
        output = model(input)
        loss = loss_fn(output, target)
        loss.backward()
        optimizer.step()
    scheduler.step()

优化器调整的模型参数参数包含权重的偏置与归一化项;
优化器可以为不同的网络层设置学习率与权重衰减

import torch
import torch.nn as nn

# 定义一个简单的神经网络模型
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.fc = nn.Linear(320, 50)

    def forward(self, x):
        x = torch.relu(torch.max_pool2d(self.conv1(x), 2))
        x = torch.relu(torch.max_pool2d(self.conv2(x), 2))
        x = x.view(-1, 320)
        x = self.fc(x)
        return x

# 创建模型实例
model = SimpleModel()

# 使用 named_parameters() 获取参数名称和参数
for name, param in model.named_parameters():
    print(name, param.size())
  
'''
输出结果: 
conv1.weight torch.Size([10, 1, 5, 5])
conv1.bias torch.Size([10])
conv2.weight torch.Size([20, 10, 5, 5])
conv2.bias torch.Size([20])
fc.weight torch.Size([320, 50])
fc.bias torch.Size([50])
'''

Optimizer.add_param_group 将参数组添加到Optimizerparam_groups中。
Optimizer.load_state_dict 加载优化器状态。
Optimizer.state_dict 以字典的形式返回优化器的状态dict。
Optimizer.step 参数更新
Optimizer.zero_grad 重置累计梯度梯度(梯度累计发生在反向传播之前)

优化器在模型训练中的作用是调整模型的参数,以最小化损失函数。训练过程通常遵循以下步骤:

  • 重置梯度:在每次迭代开始时,需要将模型参数的梯度清零,以避免累积。
  • 前向传播:模型接收输入数据,通过其参数进行计算,得到预测值。
  • 计算损失:使用损失函数(如均方误差、交叉熵等)计算模型预测值与真实值之间的差异,这个差异被称为损失值。损失函数为模型提供了优化的方向。
  • 反向传播:根据损失值对模型参数进行反向传播,计算每个参数的梯度,这些梯度指示了- 如何调整参数以减少损失。
  • 梯度累计
    梯度:表示模型参数发生微小变化,损失函数该如何变化
    学习率:控制参数更新的步长,学习率在参数更新前进行更新
    Momentum :考虑过去梯度的指数加权平均值来调整参数的更新规则,从而帮助模型更快地收敛,并在梯度很小时减少震荡
    累加梯度:在每次迭代中(反向传播后),将计算得到的梯度累加到梯度累积器中,而不是立即更新模型参数。(梯度累计是一种灵活的技术,它使得在资源有限的情况下训练大型模型成为可能,并且可以帮助优化训练过程。在进行反向传播之前,如果没有直接进行模型的梯度更新,一般会进行梯度累计)

3.2 学习率调度

学习率控制着模型参数的更新变化率,在训练过程中采用不同的学习率衰减策略,能更帮助模型更好的拟合数据,提升模型的泛化能力,定义优化器时,会设置初始学习率,利用 torch.optim.lr_scheduler中的学习率函数对优化器与学习率调整策略进行封装,结果返回封装了optimizer,scheduler对象。更新optimizer的学习率
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)

  • 训练完一个epoch进行更新
  • 迭代一次进行一次更新
  • 可以在训练过程中设置不同参数层的学习率

4迭代训练

train_eopch:训练完全部数据跟新一次学习或优化器参数,或者指定更新优化器参数的更新频率
iteration: 没迭代一次更新一次优化器参数;
两者的主要区别在于遍历数据的形式不同

4.1 train_epoch

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# 定义一个简单的模型
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = nn.Linear(10, 2)  # 一个简单的线性层

    def forward(self, x):
        return self.linear(x)

# 实例化模型、损失函数和优化器
model = SimpleModel()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 创建数据集和数据加载器
x_dummy = torch.randn(1000, 10)
y_dummy = torch.randint(0, 2, (1000,))
dataset = TensorDataset(x_dummy, y_dummy)
data_loader = DataLoader(dataset, batch_size=100, shuffle=True)

# 假设我们想要模拟的批量大小是1000,但由于内存限制,我们只能实际使用批量大小为100
accumulation_steps = 10  # 需要累积10个steps的梯度
model.train()

for epoch in range(2):  # 训练2个epoch
    for i, (inputs, targets) in enumerate(data_loader):
        # 前向传播
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        
        # 累加梯度而不是立即清零
        loss.backward()
        
        # 每累积一定步数后更新一次参数
        if (i + 1) % accumulation_steps == 0:
            # 更新模型参数之前,我们需要梯度
            optimizer.step()
            optimizer.zero_grad()  # 清零梯度,准备下一次累积

        # 打印损失信息
        if (i + 1) % (accumulation_steps * 10) == 0:  # 每100个iteration打印一次
            print(f'Epoch [{epoch+1}/{2}], Step [{i+1}/{len(data_loader)*accumulation_steps}], Loss: {loss.item():.4f}')

print("Training complete.")

4.2 train iteration

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# 定义一个简单的模型
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = nn.Linear(10, 2)  # 一个简单的线性层

    def forward(self, x):
        return self.linear(x)

# 实例化模型、损失函数和优化器
model = SimpleModel()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# 创建数据集和数据加载器
x_dummy = torch.randn(1000, 10)
y_dummy = torch.randint(0, 2, (1000,))
dataset = TensorDataset(x_dummy, y_dummy)
data_loader = DataLoader(dataset, batch_size=100, shuffle=True)

# 假设我们想要模拟的批量大小是1000,但由于内存限制,我们只能实际使用批量大小为100
accumulation_steps = 10  # 需要累积10个steps的梯度
model.train()
max_iter = 100
# 设置最大迭代次数
iterator = iter(data_loader)
for iter in range(1,max_iter):  # 训练max_iter个iter
	# 迭代数据,若完成数据一轮迭代,则重新初始化iterator = iter(train_loader)
	# 直至完成max_iter次迭代
	try:
         inputs, targets = next(iterator)
            
        except:
            iterator = iter(train_loader)
            inputs, targets = next(iterator)
    # 前向传播
    outputs = model(inputs)
    loss = criterion(outputs, targets)
        
    # 累加梯度而不是立即清零
    loss.backward()
        
    # 每累积一定步数后更新一次参数
    if (iter) % accumulation_steps == 0:
        # 更新模型参数之前,我们需要梯度
        optimizer.step()
        optimizer.zero_grad()  # 清零梯度,准备下一次累积

    # 打印损失信息
    if (iter) % (accumulation_steps * 10) == 0:  # 每100个iteration打印一次
        print(f'Epoch [{epoch+1}/{2}], Step [{i+1}/{len(data_loader)*accumulation_steps}], Loss: {loss.item():.4f}')

print("Training complete.")

5.1 保存模型权重

以字典形式,保存权重与详细的参数

torch.save({'model': model_eval.state_dict(),
                        'mAP': -1.,
                        'optimizer': self.optimizer.state_dict(),
                        'epoch': self.epoch,
                        'args': self.args}, 
                        checkpoint_path)

只保存模型参数

torch.save(model.state_dict(), save_temp_weights+"_fg{}.pt".format(it))
  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵不吃雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值