微调过程中有关于accelerate库的学习
使用tensorboard跟踪
方法一
在这种方法中,TensorBoard
和Accelerator
是分别初始化的
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from accelerate import Accelerator
from torch.utils.tensorboard import SummaryWriter
import os
# 1. 初始化Accelerator
accelerator = Accelerator()
# 2. 使用TensorBoard追踪训练,注意要加判断语句,确保只有主进程执行
if accelerator.is_main_process:
tbwriter = SummaryWriter(log_dir=os.path.join('testfolder', 'test'))
# 3. 定义一个简单的模型和优化器
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc = nn.Linear(10, 2)
def forward(self, x):
return self.fc(x)
model = SimpleModel()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 4. 定义虚拟数据
x = torch.randn(100000, 10)
y = (torch.randn(100000) > 0.5).long()
dataset = TensorDataset(x, y)
loader = DataLoader(dataset, batch_size=2048, shuffle=True)
# 5. 使用accelerator.prepare函数准备模型和优化器
model, optimizer, loader = accelerator.prepare(model, optimizer, loader)
# 6. 训练循环
iter = 1
for epoch in range(10000):
for batch in loader:
inputs, targets = batch
outputs = model(inputs)
loss = nn.CrossEntropyLoss()(outputs, targets)
optimizer.zero_grad()
accelerator.backward(loss)
optimizer.step()
print(f"Loss: {loss.item()}")
# 只有主进程向TensorBoard添加数据
if accelerator.is_main_process:
tbwriter.add_scalar('loss', loss.item(), iter)
iter += 1
print("Training completed!")
方法二
使用Accelerator
的log_with
参数和init_trackers
方法来集成TensorBoard
,示例如下:
使用这种方法时,Accelerator
会自动处理多进程环境下的TensorBoard
记录,无需手动判断主进程,但需要按照Accelerate
的规范来使用log
方法记录数据
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from accelerate import Accelerator
from accelerate.utils import ProjectConfiguration
# 1. 初始化Accelerator的同时启用TensorBoard
config = ProjectConfiguration(project_dir=".", logging_dir="testfolder")
accelerator = Accelerator(log_with="tensorboard", project_config=config)
accelerator.init_trackers("my_project")
# 2. 定义一个简单的模型和优化器
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc = nn.Linear(10, 2)
def forward(self, x):
return self.fc(x)
model = SimpleModel()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 3. 定义虚拟数据
x = torch.randn(100000, 10)
y = (torch.randn(100000) > 0.5).long()
dataset = TensorDataset(x, y)
loader = DataLoader(dataset, batch_size=2048, shuffle=True)
# 4. 使用accelerator.prepare函数准备模型和优化器等
model, optimizer, loader = accelerator.prepare(model, optimizer, loader)
# 5. 训练循环
for epoch in range(10000):
for batch in loader:
inputs, targets = batch
outputs = model(inputs)
loss = nn.CrossEntropyLoss()(outputs, targets)
optimizer.zero_grad()
accelerator.backward(loss)
optimizer.step()
print(f"Loss: {loss.item()}")
# 使用accelerator.log记录数据
accelerator.log({"loss": loss.item()}, step=epoch)
print("Training completed!")
如果使用第二种方法,即通过`Accelerator`的`log_with`参数和`init_trackers`方法来集成`TensorBoard`,
查看`TensorBoard`记录结果的步骤如下:
- 训练过程中实时查看
- 如果你希望在模型训练过程中实时查看训练指标(如损失值、准确率等)的变化情况,你可以在训练开始前启动 TensorBoard。这样,在训练循环中,每当使用
accelerator.log
(在accelerate
集成的情况下)或者手动向TensorBoard
写入数据(如SummaryWriter.add_scalar
等方法)后,这些新的数据就会被实时更新到TensorBoard
的可视化界面中。 - 例如,在启动训练脚本之前,在命令行中运行
tensorboard --logdir=<日志目录>
tensorboard --logdir=<日志目录>
(其中<日志目录>
是你在代码中指定的保存TensorBoard
日志文件的目录),然后在浏览器中访问就可以打开
TensorBoard
界面,随着训练的进行,就能看到指标的动态变化。
- 如果你希望在模型训练过程中实时查看训练指标(如损失值、准确率等)的变化情况,你可以在训练开始前启动 TensorBoard。这样,在训练循环中,每当使用
- 训练完成后查看结果
- 当你只是关心模型训练结束后的最终结果,或者想要对整个训练过程的指标变化进行事后分析,你可以在训练完成后启动 TensorBoard。
- 这种情况下,在训练脚本执行完后,按照上述启动
TensorBoard
的方式(运行tensorboard --logdir=<日志目录>
并在浏览器中访问相应网址),就可以查看训练过程中记录的所有数据,包括损失值的下降趋势、准确率的提升情况等,以此来评估模型训练的效果。 -
检查TensorBoard日志格式
确保您的TensorBoard日志文件格式正确。TensorBoard日志文件通常是事件文件(
events.out.tfevents
),如果您的日志文件不是这种格式,TensorBoard可能无法正确解析。 -
使用不同的端口启动TensorBoard:如果您无法停止占用端口的程序,或者您不希望停止它,您可以通过指定不同的端口来启动TensorBoard。例如,使用端口6007:
tensorboard --logdir=/work/home/output/qwen_7b/try_data_ep3/logs --port=6007
- 模型调试阶段
- 在调试模型时,启动 TensorBoard 来观察模型中间层的输出、参数分布等信息是很有帮助的。例如,你可以在模型的特定层记录激活值(通过
add_histogram
等方法)或者输出的特征图(通过add_image
方法)。 - 可以在调试相关代码部分之前启动 TensorBoard,这样就能及时查看这些调试相关的数据,帮助你发现模型可能存在的问题,如梯度消失或爆炸、过拟合等情况,进而调整模型的架构或训练参数。
- 在调试模型时,启动 TensorBoard 来观察模型中间层的输出、参数分布等信息是很有帮助的。例如,你可以在模型的特定层记录激活值(通过
accelerator自动分配主进程
在使用深度学习和机器学习框架时,如Hugging Face的Accelerate库,主进程的选择通常是自动配置的,基于当前的运行环境和硬件配置。以下是一些常见的情况:
-
单机单卡:在这种情况下,只有一个进程在运行,因此默认情况下它就是主进程。
-
单机多卡:当使用多个GPU时,框架(如PyTorch的torch.distributed或TensorFlow的tf.distribute)会根据环境变量(如LOCAL_RANK)自动选择一个进程作为主进程。Accelerate库会封装这些细节,自动检测并设置主进程。
-
多机多卡:在分布式训练环境中,通常会有一个主节点(node),该节点上的一个进程会被设置为主进程。这通常通过环境变量(如RANK)来确定,Accelerate库同样会处理这些配置。
Accelerate库通过检查环境变量和硬件配置来自动确定主进程。例如,它可能会检查以下环境变量:
- LOCAL_RANK:在单机多卡环境中,每个GPU都有一个LOCAL_RANK,通常LOCAL_RANK=0的进程被视为主进程。
- RANK:在多机环境中,每个节点上的进程都有一个全局唯一的RANK,通常RANK=0的进程被视为主进程。
Accelerate库的accelerator.is_main_process属性会返回一个布尔值,指示当前进程是否是主进程。这个属性是由库内部自动配置的,用户不需要手动设置。这样,用户就可以专注于编写模型训练代码,而不必担心进程管理和分布式训练的复杂性。
总之,主进程的选择是由Accelerate库根据运行环境自动配置的,用户可以直接使用is_main_process属性来判断当前进程是否为主进程,并据此执行特定操作。
accelerator.wait_for_everyone() 是 Hugging Face Accelerate 库中的一个函数,用于在分布式训练环境中同步所有进程。当在多GPU或多节点上运行训练脚本时,不同的进程可能会以不同的速度执行指令。在某些需要所有进程协调执行的操作上,比如保存模型或打印日志之前,确保所有进程都到达了相同的执行点是非常重要的。
以下是accelerator.wait_for_everyone()函数的几个关键点:
-
同步点:该函数会阻塞所有先到达这个函数调用点的进程,直到所有其他进程也都执行到这里。这意味着,无论各个进程的执行速度如何,wait_for_everyone() 都会确保它们在继续执行下一步之前都达到了这个同步点。
-
分布式训练:在分布式训练中,不同的GPU或节点可能在不同时间完成各自的任务。使用wait_for_everyone()可以确保所有进程在继续执行之前都已经完成了当前步骤,这对于避免数据不一致和错误非常重要。
-
避免不必要的等待:如果脚本只在单个GPU或CPU上运行,wait_for_everyone()不会做任何事情,因为它没有其他进程需要等待。
-
使用场景:典型的使用场景包括在所有进程都完成训练步骤后保存模型,或者在所有进程都准备好后开始下一个训练阶段。
下面是一个简单的代码示例,展示了accelerator.wait_for_everyone()的用法:
在这个例子中,主进程会休眠两秒,其他进程会等待主进程醒来。两秒后,所有进程会同时打印"Everyone is here"。通过wait_for_everyone(),可以在分布式环境下轻松实现多进程同步。
import time
from accelerate import Accelerator
accelerator = Accelerator()
# 假设有两个GPU进程
if accelerator.is_main_process:
time.sleep(2) # 主进程休眠2秒
else:
print("I'm waiting for the main process to finish its sleep...")
accelerator.wait_for_everyone() # 等待所有进程到达这一点
# 所有进程会同时打印
print("Everyone is here")
有关于优化的问题(还是不太明白)
优化和梯度更新之间的关系
-
优化:
- 优化是指在模型训练过程中,通过调整模型参数来最小化(或最大化)一个目标函数(也称为损失函数或成本函数)的过程。
- 优化的目标是找到一组参数,使得模型在给定数据上的预测尽可能接近真实值,或者使得模型在特定任务上的性能达到最佳。
-
梯度更新:
- 梯度更新是优化过程中的一个具体步骤,它涉及使用梯度信息来调整模型参数。
- 梯度是一个向量,表示目标函数在当前参数点的最陡上升方向。通过计算目标函数关于每个参数的梯度,我们可以知道如何调整参数来减少(或增加)目标函数的值。
- 在梯度下降(或上升)算法中,我们沿着梯度的反方向(或方向)更新参数,以期望在下一次迭代中得到更低(或更高)的目标函数值。
-
关系:
- 梯度更新是实现优化的一种手段。通过反复进行梯度更新,我们可以逐步接近目标函数的最小值(或最大值)。
- 优化过程通常包括多个梯度更新步骤,每个步骤都基于当前参数的梯度信息。
- 梯度更新的频率和幅度由学习率(learning rate)控制,学习率是优化算法中的一个超参数。
-
其他优化方法:
- 除了梯度下降(或上升)之外,还有其他优化方法,如牛顿法、拟牛顿法、共轭梯度法等,它们在某些情况下可能比梯度下降更有效。
- 这些方法可能使用二阶导数(Hessian矩阵)或其他信息来指导参数更新,而不是仅仅依赖于梯度。
-
总结:
- 优化是整个过程,目标是找到最佳参数。
- 梯度更新是优化过程中的一个步骤,用于根据梯度信息调整参数。
- 梯度更新的目的是在每次迭代中使目标函数值更接近最小值(或最大值)。
学习率调度器
这段注释解释了在深度学习训练中创建学习率调度器(learning rate scheduler)的原因和注意事项,特别是在使用数据并行训练时。以下是对注释内容的详细解释:
-
创建学习率调度器:
- 学习率调度器用于在训练过程中调整学习率,通常是为了使模型在训练初期快速收敛,在训练后期则减慢学习速度以细化模型参数。
-
加速器(accelerator).step()的影响:
- 在使用数据并行训练时,accelerator.step() 方法(通常是在多GPU环境中使用的)会调用实际调度器的 .step() 方法多次,次数等于进程数(num_processes)。
- 这是因为学习率调度器通常是基于整个训练集初始化的,而在数据并行训练中,每个进程只看到训练集的一个子集(即整个训练集的 1/num_processes)。
-
数据并行训练中的学习率更新:
- 在数据并行训练中,每个进程需要多次更新学习率,以确保在所有进程中总的学习率更新次数与整个训练集的学习率更新次数相匹配。
-
设置num_training_steps:
- num_training_steps 是指在整个训练过程中计划执行的训练步骤总数。
- 如果指定了训练周期(epochs),则 num_training_steps 应该基于整个训练集来设置。
- 如果没有指定周期,而是直接指定了训练步骤数,则需要将 num_training_steps 乘以进程数(num_processes),以确保总的学习率更新次数与基于整个训练集设置的 num_training_steps 相匹配。
-
总结:
- 注释强调了在使用数据并行训练时,需要正确设置学习率调度器,以确保每个进程中学习率的更新次数与整个训练集的学习率更新次数一致。
- 这是为了确保学习率调度器能够按照预期工作,不会因为每个进程只处理训练集的一部分而导致学习率更新不同步。
在实际应用中,正确配置学习率调度器对于模型的训练效果至关重要。它可以帮助模型在训练过程中动态调整学习率,从而提高训练效率和模型性能。
因篇幅问题不能全部显示,请点此查看更多更全内容