torch 断电恢复训练

ps:機器A經由tokenizer生成的訓練集、驗證集,放到B機器上繼續訓練,F1值等和A機器訓練沒斷的情況下不一樣,故此斷電恢復訓練仍有待測試

1、示例:pytorch断点续训/恢复训练,以及绘制学习率曲线 - 知乎 

2、chatgpt3.5问答: 

 

torch有累计梯度的模型,中断后怎么恢复训练?
ChatGPT
如果你正在使用PyTorch训练模型,并且使用了累计梯度 (gradient accumulation) 来处理大型数据集或增大batch size,当训练被中断后,你可能希望从上次中断的地方恢复训练。下面是一些步骤,帮助你实现这个目标:

保存模型和优化器状态: 在训练过程中,定期保存模型的权重和优化器的状态。这不仅包括模型的参数,还应该包括优化器的状态,因为优化器(如Adam)有自己的内部参数(如动量)。
python
Copy code
torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss,
            ...
            }, 'checkpoint.pth')
加载模型和优化器状态: 如果训练中断,你可以从最后一个checkpoint加载模型和优化器的状态。
python
Copy code
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
恢复累计梯度: 如果你正在进行累计梯度,那么除了上述内容之外,你还需要跟踪当前的累计次数,并在加载checkpoint后恢复这个计数。
为了恢复累计梯度,你需要:

在保存checkpoint时,存储当前的累计次数。
加载checkpoint时,恢复这个累计次数。
python
Copy code
# 保存时
torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss,
            'accumulation_step': accumulation_step,
            ...
            }, 'checkpoint.pth')

# 加载时
checkpoint = torch.load('checkpoint.pth')
...
accumulation_step = checkpoint['accumulation_step']
继续训练: 一旦你加载了模型、优化器的状态和累计步骤,你可以继续训练。
python
Copy code
for epoch in range(starting_epoch, num_epochs):
    ...
    for batch in dataloader:
        ...
        loss.backward()
        if (batch_idx + 1) % accumulation_steps == 0:  # 累计一定步骤后进行一次参数更新
            optimizer.step()
            optimizer.zero_grad()
这样,即使在使用累计梯度的情况下,你的训练中断,你也可以从中断的地方恢复训练,而不是从头开始。




User
有详细的代码示例吗?
ChatGPT
当然可以。以下是一个详细的代码示例,演示了如何在使用累积梯度的情况下保存和恢复训练。

python
Copy code
import torch
import torch.nn as nn
import torch.optim as optim

# 定义一个简单的模型
class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc = nn.Linear(10, 1)

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

# 初始化模型和优化器
model = SimpleModel()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 定义累积梯度的步骤数
accumulation_steps = 4

# 保存训练状态的函数
def save_checkpoint(epoch, model, optimizer, accumulation_step, filename='checkpoint.pth'):
    torch.save({
        'epoch': epoch,
        'model_state_dict': model.state_dict(),
        'optimizer_state_dict': optimizer.state_dict(),
        'accumulation_step': accumulation_step
    }, filename)

# 加载训练状态的函数
def load_checkpoint(filename='checkpoint.pth'):
    checkpoint = torch.load(filename)
    model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    return checkpoint['epoch'], checkpoint['accumulation_step']

# 假设数据集
data = torch.randn(100, 10)
labels = torch.randn(100, 1)

# 开始训练
starting_epoch = 0
accumulation_step = 0
num_epochs = 5

# 如果需要从checkpoint恢复
# starting_epoch, accumulation_step = load_checkpoint()

for epoch in range(starting_epoch, num_epochs):
    for batch_idx, (input_data, target) in enumerate(zip(data, labels)):
        outputs = model(input_data)
        loss = nn.MSELoss()(outputs, target)
        loss.backward()

        if (accumulation_step + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()
            accumulation_step = 0
        else:
            accumulation_step += 1

        # 假设每20个batch保存一次checkpoint
        if batch_idx % 20 == 0:
            save_checkpoint(epoch, model, optimizer, accumulation_step)

# 通过上面的代码,即使中断训练,也可以通过加载checkpoint从上次的位置恢复训练。
上述示例中定义了一个简单的模型和优化器,使用MSE作为损失函数,并模拟了一个简单的数据集。在训练过程中,每经过一个累积步骤,会更新模型的参数。同时,代码每20个batch会保存一个checkpoint。如果训练被中断,只需加载checkpoint然后继续训练即可。




User
那如果还使用了lr_scheduler.step()呢?
ChatGPT
如果你使用了学习率调度器 (lr_scheduler),那么它的状态也需要在每个checkpoint中保存和恢复。学习率调度器有它自己的内部状态,这通常与训练的epoch或步骤相关联,所以当你恢复训练时,也需要确保从正确的位置恢复学习率调度。

以下是如何修改前面的示例来包括lr_scheduler的步骤:

初始化lr_scheduler:
python
Copy code
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)
在保存checkpoint时保存lr_scheduler的状态:
python
Copy code
def save_checkpoint(epoch, model, optimizer, accumulation_step, scheduler, filename='checkpoint.pth'):
    torch.save({
        'epoch': epoch,
        'model_state_dict': model.state_dict(),
        'optimizer_state_dict': optimizer.state_dict(),
        'scheduler_state_dict': scheduler.state_dict(),
        'accumulation_step': accumulation_step
    }, filename)
在加载checkpoint时恢复lr_scheduler的状态:
python
Copy code
def load_checkpoint(filename='checkpoint.pth'):
    checkpoint = torch.load(filename)
    model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
    return checkpoint['epoch'], checkpoint['accumulation_step']
在训练过程中使用lr_scheduler:
python
Copy code
for epoch in range(starting_epoch, num_epochs):
    for batch_idx, (input_data, target) in enumerate(zip(data, labels)):
        outputs = model(input_data)
        loss = nn.MSELoss()(outputs, target)
        loss.backward()

        if (accumulation_step + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()
            accumulation_step = 0
        else:
            accumulation_step += 1

        # 假设每20个batch保存一次checkpoint
        if batch_idx % 20 == 0:
            save_checkpoint(epoch, model, optimizer, accumulation_step, scheduler)

    # 调用scheduler.step()来更新学习率
    scheduler.step()
注意:确保在所有epoch结束后调用scheduler.step(),除非你使用的是ReduceLROnPlateau,那么可能需要在每次loss不再下降时调用它。

通过这种方式,即使中断训练,你也可以从上次的位置恢复训练,并确保学习率调度器的状态与中断时相匹配。




User
scheduler.step()跟步骤关联的怎么恢复?
ChatGPT
如果scheduler.step()与每一步(或batch)而不是每一轮(epoch)相关联,那么恢复过程会稍微复杂一些。你需要在保存和加载checkpoint时,额外记录已完成的总步骤数,并在恢复后,确保scheduler正确地从上次中断的步骤继续。

初始化lr_scheduler:
python
Copy code
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)
定义一个变量来跟踪总的步骤数,并在保存checkpoint时保存这个数值:
python
Copy code
total_steps = 0

def save_checkpoint(epoch, model, optimizer, accumulation_step, scheduler, total_steps, filename='checkpoint.pth'):
    torch.save({
        'epoch': epoch,
        'model_state_dict': model.state_dict(),
        'optimizer_state_dict': optimizer.state_dict(),
        'scheduler_state_dict': scheduler.state_dict(),
        'accumulation_step': accumulation_step,
        'total_steps': total_steps
    }, filename)
在加载checkpoint时恢复lr_scheduler的状态和总步骤数:
python
Copy code
def load_checkpoint(filename='checkpoint.pth'):
    checkpoint = torch.load(filename)
    model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
    return checkpoint['epoch'], checkpoint['accumulation_step'], checkpoint['total_steps']
在训练循环中,每完成一个batch时更新总步骤数和调用scheduler.step():
python
Copy code
# 加载checkpoint
starting_epoch, accumulation_step, total_steps = load_checkpoint()

for epoch in range(starting_epoch, num_epochs):
    for batch_idx, (input_data, target) in enumerate(zip(data, labels)):
        outputs = model(input_data)
        loss = nn.MSELoss()(outputs, target)
        loss.backward()

        if (accumulation_step + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()
            
            # 更新总步骤数并调用scheduler
            total_steps += 1
            scheduler.step()
            
            accumulation_step = 0
        else:
            accumulation_step += 1

        # 假设每20个batch保存一次checkpoint
        if batch_idx % 20 == 0:
            save_checkpoint(epoch, model, optimizer, accumulation_step, scheduler, total_steps)
通过上述方式,无论你的scheduler.step()与epochs还是steps相关联,你都可以确保在训练中断后,从中断的地方恢复,并且学习率调度器的状态与中断时一致。




User
上面total_steps好像没什么用?
ChatGPT
抱歉,你是对的。在上面的代码中,我在save_checkpoint中存储了total_steps,但在加载和恢复时并没有真正利用它来确保scheduler与具体的训练步骤同步。

如果scheduler根据步骤更新(例如每N个步骤调整一次学习率),则确实需要知道从哪个步骤开始以继续调度。

以下是如何更有效地使用total_steps来确保在加载checkpoint后正确地恢复scheduler:

在开始训练之前初始化total_steps:
python
Copy code
total_steps = 0
在每个训练步骤中递增total_steps并根据需要调用scheduler.step():
python
Copy code
for epoch in range(starting_epoch, num_epochs):
    for batch_idx, (input_data, target) in enumerate(zip(data, labels)):
        outputs = model(input_data)
        loss = nn.MSELoss()(outputs, target)
        loss.backward()

        if (accumulation_step + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()
            
            # 更新总步骤数
            total_steps += 1
            
            # 根据总步骤数决定是否调用scheduler.step()
            if total_steps % N == 0:  # 例如每N个步骤调整一次学习率
                scheduler.step()
            
            accumulation_step = 0
        else:
            accumulation_step += 1

        # 假设每20个batch保存一次checkpoint
        if batch_idx % 20 == 0:
            save_checkpoint(epoch, model, optimizer, accumulation_step, scheduler, total_steps)
当从checkpoint加载时,你可以从上一次保存的total_steps值恢复:
python
Copy code
def load_checkpoint(filename='checkpoint.pth'):
    checkpoint = torch.load(filename)
    model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
    return checkpoint['epoch'], checkpoint['accumulation_step'], checkpoint['total_steps']

starting_epoch, accumulation_step, total_steps = load_checkpoint()
这样,total_steps就会记录已完成的总步骤数,并确保在从checkpoint恢复后,scheduler可以正确地继续工作。




User
使用上述方法后,loss和中断之前相差很大
ChatGPT
如果从checkpoint恢复后,你发现loss与中断前有显著差异,可能是由于以下原因:

随机性:在深度学习中,许多操作都涉及到随机性。例如,随机初始化、数据增强、Dropout等。如果你的数据加载过程有任何随机性(例如随机数据增强或shuffling),那么每次重新启动训练时,你的模型都可能会得到不同的数据样本。

Batch Normalization:如果模型中使用了Batch Normalization,那么中断和恢复可能会导致统计数据的不连续性,从而影响loss。这是因为Batch Normalization层在训练时会维护一个运行的平均值和方差。

加载checkpoint的问题:确保正确加载了模型状态、优化器状态和学习率调度器状态。任何遗漏都可能导致loss的变化。

累积梯度:如果使用累积梯度,确保正确地恢复了累积状态。

要解决这个问题,你可以采取以下步骤:

固定随机种子:在训练开始前固定所有的随机种子。这包括PyTorch、NumPy和Python的随机种子。

python
Copy code
torch.manual_seed(0)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
np.random.seed(0)
random.seed(0)
更频繁地保存checkpoint:这样,即使训练中断,你也不会丢失太多的进度。

Double-check checkpoint保存和加载:确保模型、优化器和scheduler的状态都被正确保存和加载。

考虑累积状态:确保累积梯度的状态正确保存和恢复。

确保其他状态保持一致:例如,如果你使用了特定的数据拆分或特定顺序的数据加载,确保这在重新启动训练时保持不变。

Batch Normalization:如果使用Batch Normalization,并且认为这可能是问题所在,考虑保存更多关于BatchNorm层的信息,例如运行平均值和方差。

如果采取了上述措施仍然遇到问题,考虑简化问题:使用一个小数据集和一个简单的模型进行测试,看看是否能够成功地保存和恢复训练,然后逐步增加复杂性以确定问题所在。




User
我用bert做ner,有用到Batch Normalization吗?
ChatGPT
BERT(Bidirectional Encoder Representations from Transformers)本身在其原始架构中并没有使用Batch Normalization。BERT主要由多层Transformer组成,这些Transformer使用了Layer Normalization而不是Batch Normalization。

Layer Normalization与Batch Normalization的主要区别在于正则化的维度:

Batch Normalization对一个batch中所有样本的同一个特征进行正则化。
Layer Normalization对单个样本的所有特征进行正则化。
当你使用BERT进行NER(命名实体识别)时,如果你没有向模型中明确地添加Batch Normalization层,那么BERT模型应该是不包含Batch Normalization的。大部分预训练的BERT模型和相关的实现(例如HuggingFace的Transformers库)按照原始BERT论文的架构,使用Layer Normalization而不是Batch Normalization。

因此,如果你的模型在加载checkpoint后loss发生了显著变化,可能与Batch Normalization无关。你可能需要检查其他因素,例如正确的模型初始化、正确的权重加载、优化器状态等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值