acclerator和tensorboard共同使用采坑记录

news2024/11/25 0:02:44

acclerator和tensorboard共同使用采坑记录

  • 问题描述
  • 可采用的方案
    • 可采用的方案1
    • 可采用的方案2
    • 其他可采用方案
    • 可采用方案的总结
  • 建议的最终方案

问题描述

最近在做用多个GPU训练,我选择的是hugging face开源团队的acclerate库中的accelerator类来实现的,在这种情况下,使用tensorboard来记录追踪训练情况,需要对代码进行相应的调整。

可采用的方案

在使用了accelerator类的情况下,使用tensorboard进行训练追踪,可参考官方文档给出的一些方案来进行。以下列举一些

可采用的方案1

一种方案是直接使用tensorboard,如下述代码所示,在这种方法中,tensorboardaccelerator是分别初始化的。

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追踪训练
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()}")

        tbwriter.add_scalar('loss', loss.item(), iter)
        iter += 1

print("Training completed!")

看一下结果,在testfolder文件夹下的test文件夹中,竟然有两个文件

在这里插入图片描述

再使用–logdir=来查看训练情况,发现loss曲线很奇怪,貌似是一个横坐标对应了两个值

在这里插入图片描述

出现这种情况,原因其实在于accelerator是通过多进程来实现多GPU计算的,所以代码中tbwriter = SummaryWriter(log_dir=os.path.join('testfolder', 'test'))tbwriter.add_scalar('loss', loss.item(), epoch)语句都被两个进程执行了两次。所以才会产生两个文件,在可视化窗口一个横坐标也对应了两个值。

解决方案就是在这些语句前加一句判断语句,如下所示:

if accelerator.is_main_process:
    tbwriter = SummaryWriter(log_dir=os.path.join('testfolder', 'test'))
if accelerator.is_main_process:
    tbwriter.add_scalar('loss', loss.item(), iter)

这样运行的结果就是正确的,而且仅会产生一个文件

在这里插入图片描述

总结:这种该方法简单,改动小,但是每次向tensorboard添加数据的时候都需要判断一次是否是主程序,很麻烦

可采用的方案2

根据官方文档的内容,使用accelerator的情况下用tensorboard记录训练情况还可以这么写:

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
from torch.utils.tensorboard import SummaryWriter
import os

# 1. 初始化 Accelerator的同时使用启用tensorbaord
config = ProjectConfiguration(project_dir=".", logging_dir="testfolder")
accelerator = Accelerator(log_with="tensorboard", project_config=config)
accelerator.init_trackers("test")


# 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. 训练循环
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()}")

        accelerator.log({"training_loss": loss}, step=iter)
        iter += 1

print("Training completed!")

这样也能出来正确的结果

在这里插入图片描述

这种方法优点在于不用每次都去判断是否是主进程。

然而,这种方案仅添加数值,无法添加figure,也无法使用tensorboard中丰富的方法来记录训练过程。

将上述代码稍微更改下,能够使用loglog_images两种方法,但是也只能使用这两种方法来追踪训练过程,仍然不够自由。

初始化代码改为:

# 1. 初始化 Accelerator的同时使用启用tensorbaord
config = ProjectConfiguration(project_dir=".", logging_dir="testfolder")
accelerator = Accelerator(log_with="tensorboard", project_config=config)
accelerator.init_trackers("test")
tensorboard_tracker = accelerator.get_tracker("tensorboard")

在训练循环中追踪训练的代码改为:

tensorboard_tracker.log({"training_loss": loss}, step=epoch)

其他可采用方案

除此之外,还有其他类似的方案,比如访问内部追踪器之类的,这些方法和上面提到的大同小异,没有明显区别,所以在这里不再赘述。

可采用方案的总结

通过上述研究不难发现,在使用accelerator在多GPU上训练的时候,使用tensorboard进行训练追踪存在“鱼和熊掌不能兼得”的情况:

如果使用accelerator类封装包裹好的方法,那么无法使用tensorboard中的add_figure等方法来添加图片;

如果直接使用tensorboard来记录,则需要在每次记录的时候都判断一次目前是否是主进程,让代码变的很冗长。

通过查阅官方文档,我发现还有一种方法可以解决上述问题,就是使用自定义的追踪器来实现:

在这里插入图片描述

建议的最终方案

官方文档给出了使用wandb用作自定义追踪器的方法:

from accelerate.tracking import GeneralTracker, on_main_process
from typing import Optional

import wandb


class MyCustomTracker(GeneralTracker):
    name = "wandb"
    requires_logging_directory = False

    @on_main_process
    def __init__(self, run_name: str):
        self.run_name = run_name
        run = wandb.init(self.run_name)

    @property
    def tracker(self):
        return self.run.run

    @on_main_process
    def store_init_configuration(self, values: dict):
        wandb.config(values)

    @on_main_process
    def log(self, values: dict, step: Optional[int] = None):
        wandb.log(values, step=step)

我们可以以此为参考,并参考accelerator中已写好的关于tensorboard的追踪器,写一个基于tensorboard的自定义追踪器,并使用这个自定义追踪器来追踪训练过程,完整代码如下所示

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
from accelerate.tracking import GeneralTracker, on_main_process
import os
from typing import Union
import numpy as np
import matplotlib.pyplot as plt


# 0. 自定义追踪器
class MyCustomTracker(GeneralTracker):
    """
    my custom `Tracker` class that supports `tensorboard`. Should be initialized at the start of your script.

    Args:
        run_name (`str`):
            The name of the experiment run
        logging_dir (`str`, `os.PathLike`):
            Location for TensorBoard logs to be stored.
        kwargs:
            Additional key word arguments passed along to the `tensorboard.SummaryWriter.__init__` method.
    """

    name = "tensorboard"
    requires_logging_directory = True

    @on_main_process
    def __init__(self, run_name: str, logging_dir: Union[str, os.PathLike],
                 **kwargs):
        super().__init__()
        self.run_name = run_name
        self.logging_dir = os.path.join(logging_dir, run_name)
        self.writer = SummaryWriter(self.logging_dir, **kwargs)

    @property
    def tracker(self):
        return self.writer

    @on_main_process
    def add_scalar(self, tag, scalar_value, **kwargs):
        self.writer.add_scalar(tag=tag, scalar_value=scalar_value, **kwargs)

    @on_main_process
    def add_text(self, tag, text_string, **kwargs):
        self.writer.add_text(tag=tag, text_string=text_string, **kwargs)

    @on_main_process
    def add_figure(self, tag, figure, **kwargs):
        self.writer.add_figure(tag=tag, figure=figure, **kwargs)


# 1. 初始化 Accelerator,并使用自定义的tensorbaord追踪器
mycustomtracker = MyCustomTracker(run_name='test', logging_dir='testfolder')
accelerator = Accelerator(log_with=mycustomtracker)


# 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.画图demo
fig1, ax1 = plt.subplots()
xplot1 = np.array([1, 2, 3, 4, 5])
yplot1 = np.array([1, 2, 3, 4, 5])
ax1.plot(xplot1, yplot1)

fig2, ax2 = plt.subplots()
xplot2 = np.array([1, 2, 3, 4, 5])
yplot2 = np.array([5, 4, 3, 2, 1])
ax2.plot(xplot2, yplot2)

mycustomtracker.add_figure(tag="test_plot", figure=fig1, global_step=1, close=True, walltime=None)
mycustomtracker.add_figure(tag="test_plot", figure=fig2, global_step=2, close=True, walltime=None)

# 5.添加textdemo
mycustomtracker.add_text(tag="test_text", text_string='This is a test string')

# 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()}")

        mycustomtracker.add_scalar(tag='training loss',
                                   scalar_value=loss.item(),
                                   global_step=iter)
        iter += 1

print("Training completed!")

上述代码的核心就在于第一部分,自定义了一个追踪器,自定义的追踪器可以将tensoboard中丰富的方法都添加进来,并且用@on_main_process修饰,保证了仅有主进程可以访问方法,这样就不用每次在追踪前判断if accelerator.is_main_process:了,效果如下:

在这里插入图片描述
画图和添加文本也是能够使用的:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

成功达到目标!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1009714.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多元函数的微分法

目录 复合函数微分法 隐函数微分法 复合函数求导与全微分 隐函数偏导数与全微分 复合函数微分法 复合函数微分法是一种求导方法,用于计算复合函数的导数。 假设有一个复合函数yf(u),其中ug(x),则复合函数微分法可以用于计算y对x的导数。根…

3D模型转换工具HOOPS Exchange如何实现OBJ格式轻量化?

什么是OBJ模型轻量化? OBJ格式是一种常用的三维模型文件格式,通常包含模型的顶点、法线、纹理坐标等信息,但有时候这些信息可能会使模型文件变得较大,不利于网络传输、加载和运行。 OBJ(Object)模型轻量化…

又一重磅利好来袭!Zebec Payroll 集成至 Nautilus Chain 主网

流支付协议 Zebec Protocol 正在积极的拓展自身生态,随着此前其全新路线图的发布,揭示了该生态从 Web3 世界向 Web2 世界跨越的决心。根据其最新路线图,Zebec Protocol 正在从最初构建在 Solana 上的流支付协议,拓展为囊括模块化公…

Linux下修改jar包中的配置文件application.conf

文件位置 jar包文件工程目录 打包后解压jar包目录 提取和上传 jar tf XXX.jar # 获取包内文件 application.conf是jar包的配置文件,如果修改需要 提取文件 jar xf my-app.jar application.conf 修改后上传文件 jar uf my-app.jar application.conf

快速加入Health Kit,一文了解审核流程

HUAWEI Health Kit是为华为生态应用打造的基于华为帐号和用户授权的运动健康数据开放平台。 在获取用户授权后,开发者可以使用Health Kit提供的开放能力获取运动健康数据,基于多种类型数据构建运动健康领域应用与服务,为用户打造丰富、便捷、…

EXCEL如何把一个单元格内的文本和数字分开?例如:龚龚15565 = 龚龚 15565

使用工具:WPS 举例: EXCEL如何把一个单元格内的文本和数字批量分开?不使用数据分列。 第一步、将第二行数据冻结 第二步、在B1、C1单元格输入需要分开的示例 第三步、点击选中B1单元格,输入快捷键【CTRLE】进行填充。B2单元格也是…

C++新经典 | C++ 查漏补缺(类)

目录 1. 类对象的复制 2. 权限修饰符 3. 成员函数的定义与声明 4. 构造函数 (1)explicit关键字 (2)构造函数初始化列表 (3)默认构造函数 (4)default;和delete&…

SEC的下一步目标是什么?过时的证券法与加密货币行业,哪个会被先淘汰?

加密货币已经“不合规”了,尤其是其“商业模式”,至少美国证券交易委员会(SEC)主席Gary Gensler这样认为。由于这种观点在美国监管机构中普遍存在,因此涉及加密的执法行动达到历史最高水平也不足为奇。 在短短几年内,我们目睹了所…

MFC项目改为多字节字符集界面风格变为win98风格的问题

在项目->属性->配置属性中,将字符集改为多字节字符集,则界面风格变成了win98风格 解决办法,在stdafx.h中有 #ifdef _UNICODE #if defined _M_IX86 #pragma comment(linker,"/manifestdependency:\"typewin32 nameMicrosoft.Windows.Com…

【建议收藏】职场人口头和书面沟通必备词语,瞬间高大上

这年头,在职场不但要会做,还要会说。 会说还不能平铺直叙的说,还要能把普通的工作说出话来,这就需要一些“考究”的用词。尤其是在某些头部企业的带领下,业务不够、产品不行、解决方案不够新,就用华丽的辞…

浅谈C++|STL之list+forward_list篇

一.list基本概念 功能:将数据进行链式存储 链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成:链表由—系列结点组成 结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结…

前端深入理解JavaScript面向对象编程与Class

🎬 岸边的风:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 引言 1. 什么是面向对象编程? 2. Class的基本概念 3. Class的语法 3.1 构造函数 3.2 属性 3.3 方…

中断子系统 --- 硬件相关层

中断控制器驱动 由于linux支持中断控制器的级联,因此多个中断控制器就可能包含相同的硬件中断号。为了可以通过中断号唯一地标识一个中断,linux引入了irq domain机制以实现将硬件中断号映射为全局唯一的逻辑中断号。 Hwirq映射到irq 每个中断控制器都对…

hive创建hbase表映射

将hbase中的表映射至hive中,便于表的操作 create external table student_info(id string,student_name string,gender string,pwd string,school_name string,location string ) stored by org.apache.hadoop.hive.hbase.HBaseStorageHandler withserdeproperties…

Python二级 每周练习题18

练习一: 从键盘输入任意字符串,按照下面要求分离字符串中的字符: 1、分别取出该字符串的第偶数位的元素(提醒注意:是按照从左往右数的方式确定字符串的位置) 2、并依次存储到一个列表中; 3、输出这个列表。 答案: ninput(请输入任意字符串:) #创建变量n存放用户…

【深度学习】卷积神经网络(LeNet)

卷积神经网络 LeNet #前言LeNet 模型代码实现MINST代码分块解析1 构建 LeNet 网络结构2 加载数据集3 初始化模型和优化器4 训练模型5 训练完成 完整代码 Fashion-MINST代码分块解析1 构建 LeNet 网络结构2 初始化模型参数3 加载数据集4 定义损失函数和优化器5 训练模型 完整代码…

6种最流行的API架构风格

作为一名 Java程序员,编写业务 API是非常日常的开发工作,那么,如何选择合适的 API框架?今天我们就来一起聊聊当下最流行的 6种API架构风格。 一、SOAP 定义 SOAP,Simple Object Access Protocol,中文翻译…

Android端Base64解码表情emoj乱码

一、背景:H5端用户评论中包含表情包,通过JSBridge 传递给客户端,Android Base64解码之后,显示乱码(是菱形问号)。小程序和iOS可以正常解码出表情。用Base64在线编码解码(Base64 在线编码解码 | …

中项系统集成项目管理2023上半年真题及解析

中项系统集成项目管理2023上半年真题及解析 上午题1. 在 (1) 领域,我国还远未达到世界先进水平,需要发挥新型举国体制优势,集中政府和市场两方面的力量全力发展2. ChatGPT于 2022年 11 月 30 日发布,它是人工智能驱动的 (2) 工具3…

Pandas数据分析一览-短期内快速学会数据分析指南(文末送书)

前言 三年耕耘大厂数据分析师,有些工具是必须要掌握的,尤其是Python中的数据分析三剑客:Pandas,Numpy和Matplotlib。就以个人经验而已,Pandas是必须要掌握的,它提供了易于使用的数据结构和数据操作工具&am…