pytorch12:GPU加速模型训练

news2024/11/19 18:31:58

在这里插入图片描述

目录

  • 1、CPU与GPU
  • 2、数据迁移至GPU
    • 2.1 to函数使用方法
  • 3、torch.cuda常用方法
  • 4、多GPU并行运算
    • 4.1 torch.nn.DataParallel
    • 4.2 torch.distributed加速并行训练
  • 5、gpu总结

往期回顾

pytorch01:概念、张量操作、线性回归与逻辑回归
pytorch02:数据读取DataLoader与Dataset、数据预处理transform
pytorch03:transforms常见数据增强操作
pytorch04:网络模型创建
pytorch05:卷积、池化、激活
pytorch06:权重初始化
pytorch07:损失函数与优化器
pytorch08:学习率调整策略
pytorch09:可视化工具-TensorBoard,实现卷积核和特征图可视化
pytorch10:正则化(weight_decay、dropout、Batch Normalization)
pytorch11:模型加载与保存、finetune迁移训练


1、CPU与GPU

CPU(Central Processing Unit, 中央处理器):主要包括控制器和运算器
GPU(Graphics Processing Unit, 图形处理器):处理统一的,无依赖的大规模数据运算
cpu的控制单元和存储单元要比GPU多,比如我们加载的数据缓存一般都在cpu当中,GPU的计算单元到比cpu多,在算力方面要远远超过cpu
注意:运算的数据必须在同一个处理器上,如果一个数据在cpu一个在gpu上,则两个数据无法进行相关的数学运算。

在这里插入图片描述

2、数据迁移至GPU

如果想要将数据进行处理器迁移,所使用的工具是to函数,并在中间选择想要迁移的处理器类型。
data一般有两种数据类型:tensor、module。
在这里插入图片描述

2.1 to函数使用方法

to函数:转换数据类型/设备

  1. tensor.to(args, kwargs)
  2. module.to(args, kwargs)
    区别: 张量不执行inplace,要构建一个新的张量,模型执行inplace,不需要等号赋值。

inplace操作:"inplace"操作是指对数据进行原地修改的操作,即直接在原始数据上进行更改,而不是创建一个新的副本。在深度学习框架中,许多函数和方法都支持"inplace"操作,这意味着它们可以直接修改输入的张量或数组,而不需要额外的内存来存储结果。

在这里插入图片描述


1、将tensor数据放到gpu上

import torch
import torch.nn as nn
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")  #调用gpu只需要一行代码
# ========================== tensor to cuda
# flag = 0
flag = 1
if flag:
    x_cpu = torch.ones((3, 3))
    print("x_cpu:\ndevice: {} is_cuda: {} id: {}".format(x_cpu.device, x_cpu.is_cuda, id(x_cpu)))

    x_gpu = x_cpu.to(device)
    print("x_gpu:\ndevice: {} is_cuda: {} id: {}".format(x_gpu.device, x_gpu.is_cuda, id(x_gpu)))

打印结果:
发现数据id地址发生了变化,说明创建的新的变量存储数据。
在这里插入图片描述


2、module转移到gpu上

flag = 1
if flag:
    net = nn.Sequential(nn.Linear(3, 3))

    print("\nid:{} is_cuda: {}".format(id(net), next(net.parameters()).is_cuda))

    net.to(device)
    print("\nid:{} is_cuda: {}".format(id(net), next(net.parameters()).is_cuda))

打印结果:
id地址没有发生变化,执行了inplace操作。
在这里插入图片描述

3、torch.cuda常用方法

  • torch.cuda.device_count():计算当前可见可用gpu数
  • torch.cuda.get_device_name():获取gpu名称
  • torch.cuda.manual_seed():为当前gpu设置随机种子
  • torch.cuda.manual_seed_all():为所有可见可用gpu设置随机种子
  • torch.cuda.set_device():设置主gpu为哪一个物理gpu(不推荐
    推荐:== os.environ.setdefault(“CUDA_VISIBLE_DEVICES”, “2, 3”)==
    该方法要如何理解呢?

需要理解两个概念:物理gpu和逻辑gpu;物理gpu是我们电脑真实存在的0、1、2、3等显卡,逻辑gpu是Python脚本可见的gpu。
当我们设置2,3时,我们物理gpu连接的是我们真实电脑存在的第2号和第3号gpu。
在这里插入图片描述

4、多GPU并行运算

分发 → 并行运算 →结果回收
在AlexNet这篇网络中,使用了多gpu训练,在第三层卷积开始,每个特征图的信息都是从2个gpu获取,在2个gpu提取特征并进行训练,最后再将信息汇总到一起;
在这里插入图片描述

4.1 torch.nn.DataParallel

torch.nn.DataParallel(module, device_ids = None, output_device=None, dim=0)

功能:包装模型,实现分发并行机制;假设我们batch_size=16,如果有两块gpu,在训练的时候将会将数据平均分发到每一个gpu上进行训练,也就是每一块gpu训练8个数据。
主要参数:

  • module: 需要包装分发的模型
  • device_ids : 可分发的gpu,默认分发到所有可见可用gpu
  • output_device: 结果输出设备,也就是主gpu上

代码实现:

# -*- coding: utf-8 -*-

# 导入必要的库
import os
import numpy as np
import torch
import torch.nn as nn

# ============================ 手动选择gpu
# flag变量用于控制是否手动选择GPU或根据内存情况自动选择主GPU
# 如果flag为1,则执行以下代码块
flag = 1
if flag:
    # 手动选择GPU列表,这里选择第一个GPU
    gpu_list = [0]
    # 将GPU列表转换为逗号分隔的字符串形式,并设置环境变量CUDA_VISIBLE_DEVICES
    gpu_list_str = ','.join(map(str, gpu_list))
    os.environ.setdefault("CUDA_VISIBLE_DEVICES", gpu_list_str)
    # 根据CUDA是否可用选择设备,如果可用则使用cuda,否则使用cpu
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# ============================ 依内存情况自动选择主gpu
# flag变量用于控制是否根据显存情况自动选择主GPU
# 如果flag为1,则执行以下代码块
flag = 1
if flag:
    # 定义一个函数get_gpu_memory,用于获取GPU的显存情况
    def get_gpu_memory():
        import platform
        if 'Windows' != platform.system():
            import os
            os.system('nvidia-smi -q -d Memory | grep -A4 GPU | grep Free > tmp.txt')
            memory_gpu = [int(x.split()[2]) for x in open('tmp.txt', 'r').readlines()]
            os.system('rm tmp.txt')
        else:
            memory_gpu = False
            print("显存计算功能暂不支持windows操作系统")
        return memory_gpu

    # 调用get_gpu_memory函数获取显存情况,如果显存可用则执行以下代码块
    gpu_memory = get_gpu_memory()
    if gpu_memory:
        print("\ngpu free memory: {}".format(gpu_memory))
        # 根据显存情况对GPU列表进行排序,取排序后的第一个GPU作为主GPU,并设置环境变量CUDA_VISIBLE_DEVICES
        gpu_list = np.argsort(gpu_memory)[::-1]
        gpu_list_str = ','.join(map(str, gpu_list))
        os.environ.setdefault("CUDA_VISIBLE_DEVICES", gpu_list_str)
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class FooNet(nn.Module):
    def __init__(self, neural_num, layers=3):
        # 初始化FooNet类,继承自nn.Module,用于构建神经网络模型
        super(FooNet, self).__init__()
        # 定义一个线性层列表,用于存储多个线性层,层数为layers个
        self.linears = nn.ModuleList([nn.Linear(neural_num, neural_num, bias=False) for i in range(layers)])

    def forward(self, x):
        # 前向传播方法,输入参数为x,输出结果为x经过多个线性层和ReLU激活函数后的结果
        print("\nbatch size in forward: {}".format(x.size()[0]))  # 打印输入张量的batch size
        for (i, linear) in enumerate(self.linears):  # 遍历线性层列表中的每个元素进行循环迭代
            x = linear(x)  # 将输入张量传入线性层进行计算,得到输出结果x'
            x = torch.relu(x)  # 对输出结果应用ReLU激活函数,得到新的输出结果x''
        return x  # 返回新的输出结果x''

if __name__ == "__main__":
    # 如果是主程序运行,则执行以下代码块
    batch_size = 16  # 设置批量大小为16
    inputs = torch.randn(batch_size, 3)  # 生成一个形状为(batch_size, 3)的随机张量作为输入数据
    labels = torch.randn(batch_size, 3)  # 生成一个形状为(batch_size, 3)的随机张量作为标签数据
    inputs, labels = inputs.to(device), labels.to(device)

    # model
    net = FooNet(neural_num=3, layers=3)
    net = nn.DataParallel(net)
    net.to(device)

    # training
    for epoch in range(1):

        outputs = net(inputs)

        print("model outputs.size: {}".format(outputs.size()))

    print("CUDA_VISIBLE_DEVICES :{}".format(os.environ["CUDA_VISIBLE_DEVICES"]))
    print("device_count :{}".format(torch.cuda.device_count()))

打印结果:
在这里插入图片描述

4.2 torch.distributed加速并行训练

DataParallel: 单进程控制多GPU
DistributedDataParallel: 多进程控制多GPU,一起训练模型

和单进程训练不同的是,多进程训练需要注意一下事项:

  1. 在喂数据的时候,一个batch被分到了多个进程,每个进程在取数据的时候要确保拿到的是不同的数据(DistributedSampler)
  2. 要告诉每个进程自己是谁,使用哪块GPU(args.local_rank)
  3. 在做BN的时候注意同步数据。

使用方式
在多进程的启动方面,我们无需自己手写multiprocess进行一系列复杂的CPU、GPU分配任务,PyTorch为我们提供了一个很方便的启动器torch.distributed.launch用于启动文件,所以我们运行训练代码的方式就变成这样:

CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 main.py

初始化

在启动器为我们启动python脚本后,在执行过程中,启动器会将当前进行的index通过参数传递给python,我们可以这样获得当前进程的index:即通过参数local_rank来告诉我们当前进程使用的是哪个GPU,用于我们在每个进程中指定不同的device:

def parse():
	parser = argparse.ArgumentParser()
	parser.add_argument('--local_rank', type=int, default=0, help='node rank for distributed training')
	args = parser.parse_args()
	return args

def main():
	args = parse()
	torch.cuda.set_device(args.local_rank)
	torch.distributed.init_process_group(
		'nccl',
		init_method='env://'
	)
	device = torch.device(f'cuda:{args.local_rank}')

其中torch.distributed.init_process_group用于初始化GPU通信方式(NCLL)和参数的获取方式(env代表通过环境变量)。使用init_process_group设置GPU之间通信使用的后端和端口,通过NCCL实现GPU通信

Dataloader

在我们初始化data_loader的时候需要使用到torch.utils.data.distributed.DistributedSampler这个特性:

train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=..., sampler=train_sampler)

这样就能给每个进程一个不同的sampler,告诉每个进程自己分别取哪些数据

模型的初始化
和nn.DataParallel的方式一样,

model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.local_rank])

使用DistributedDataParallel包装模型, 它能帮助我们为不同GPU上求得的提取进行all reduce(即汇总不同GPU计算所得的梯度,并同步计算结果)。all reduce 后不同GPU中模型的梯度均为all reduce之前各GPU梯度的均值。

5、gpu总结

我们在模型训练当中想要提高训练速率,需要在以下三个地方添加gpu

  1. 将模型放到gpu上:resnet18_ft.to(device)
  2. 训练过程中数据: inputs, labels = inputs.to(device), labels.to(device)
  3. 验证过程中数据: inputs, labels = inputs.to(device), labels.to(device)

常见的gpu报错:

报错1:
RuntimeError: Attempting to deserialize object on a CUDA device but
torch.cuda.is_available() is False. If you are running on a CPU -only machine, please
use torch.load with map_location=torch.device(‘cpu’) to map your storages to the
CPU.
解决: torch.load(path_state_dict, map_location=“cpu”)

报错2:RuntimeError: Error(s) in loading state_dict for FooNet:
Missing key(s) in state_dict: “linears.0.weight”, “linears.1.weight”, “linears.2.weight”.
Unexpected key(s) in state_dict: “module.linears.0.weight”,
“module.linears.1.weight”, “module.linears.2.weight”.
解决:
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in state_dict_load.items ():
namekey = k[7:] if k.startswith(‘module.’) else k
new_state_dict[namekey] = v


在这里插入图片描述

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

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

相关文章

.NET 反射的介绍和简单应用

什么是反射? 反射就是动态发现类型信息的能力。它帮助程序设计人员在程序运行时利用一些信息去动态地使用类型,这些信息在设计时是未知的,这种能力类似于后期绑定。反射还支持的更高级的行为,能在运行时动态创建新类型&#xff0…

云原生微服务之分布式锁框架 Redisson

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 系列专栏目录 [Java项目…

倒F天线设计经验分享

一、IFA天线理论分析 为了改善ILA天线难以使用的缺点,在ILA天线的基础上再增加一个倒L结构,形成IFA天线,此种天线体积小、易于匹配并具有双极化的特点,而在蓝…

「JavaSE」类和对象2

🎇个人主页:Ice_Sugar_7 🎇所属专栏:快来卷Java啦 🎇欢迎点赞收藏加关注哦! 类和对象2 🍉匿名对象🍉关键字static🍌static修饰成员变量🍌static修饰成员方法&…

springboot项目创建及采用本地tomcat打包发布

springboot项目发布 maven使用 解压maven安装包 修改配置文件settings.xml 更改镜像(使用maven添加依赖时&#xff0c;选择下载的地址&#xff0c;百度云已提供) <mirror><id>nexus-aliyun</id><mirrorOf>*</mirrorOf><name>Nexus aliyu…

天津Java开发培训哪家好?选Java培训班要考虑

在当今社会&#xff0c;Java语言在软件开发领域具有举足轻重的地位&#xff0c;Java是一门面向对象编程语言&#xff0c;Java语言集合了C的优点&#xff0c;丢弃了缺点&#xff0c;所以Java语言表现的功能强大而简单易用&#xff0c;已经得到越来越多的应届毕业生和职场新人的认…

Vue Axios——前端技术栈

文章目录 基本介绍Vue是什么&#xff1f; MVVMVue的使用快速入门注意事项和使用细节 Vue 数据绑定机制分析数据单向渲染注意事项和细节 双向数据绑定事件绑定示例&#xff1a;注意事项和使用细节课后作业1课后作业2 修饰符示例 条件渲染/控制: v-if v-showv-if VS v-show课后作…

ART-Adversarial Robustness Toolbox检测AI模型及对抗攻击的工具

一、工具简介 Adversarial Robustness Toolbox 是 IBM 研究团队开源的用于检测模型及对抗攻击的工具箱&#xff0c;为开发人员加强 AI模型被误导的防御性&#xff0c;让 AI 系统变得更加安全&#xff0c;ART支持所有流行的机器学习框架 &#xff08;TensorFlow&#xff0c;Ker…

外贸建站是什么意思?海洋建站需要多少钱?

外贸建站与独立站的区别&#xff1f;新人如何玩转外贸自建站&#xff1f; 在当今数字化时代&#xff0c;随着全球市场的不断融合&#xff0c;外贸建站成为了许多企业扩大业务的关键一环。那么&#xff0c;究竟什么是外贸建站&#xff1f;海洋建站将深入探讨这个问题&#xff0…

AIGC ChatGPT 4 Prompt 万能提示词公式

最近大家都在使用ChatGPT来帮助自己完成相应的工作。很多时候大家提出的问题得不到很清晰,很明确的答案。 我们应该怎么样来和ChatGPT进行有效的沟通呢? 例如我们先来问一问ChatGPT: 要获得最准确的回复,请确保遵循以下建议: 明确性:请尽量明确描述您的问题。确保提供足…

leetcode 17 电话号码字母组合

题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits “23” 输出&#xf…

目标检测应用场景—数据集【NO.25】牛行为检测数据集

写在前面&#xff1a;数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0c;方便大家下载数据集&#xff0c;若无法下载可关注后私信领取。关注免费领取整理好的数据集资料&#xff01;今天分享…

【AIGC】美丽自然风光的一组绝美高清图提示词解析

好的&#xff0c;我可以帮你提取关键信息并整理到表格中。以下是根据你提供的描述&#xff0c;整理的表格&#xff1a; 关键信息描述场景夜晚的海滩风格奇幻色彩蓝色和绿色的荧光光源巨大的月亮、精灵般的光芒、星星、星斑海面泛着荧光沙滩金色&#xff0c;闪烁着光芒夜空繁星…

Odrive 学习系列二:将烧录工具从ST-Link V2修改为JLink

一、背景: 通过观察odrive解压后的内容,可以看到在下面配置文件及makefile文件中的配置设置的均为openOCD + stlink v2,例如makefile中: # This is only a stub for various commands. # Tup is used for the actual compilation.BUILD_DIR = build FIRMWARE = $(BUILD_DI…

Camtasia2024最新版本如何进行电脑录制屏幕?

在现在的网络互联网时代&#xff0c;越来越多的人走上了自媒体的道路。有些自媒体人会自己在网络上录制精彩视频&#xff0c;也有一些人会将精彩、热门的电影剪辑出来再加上自己给它的配音&#xff0c;做成大家喜欢看的电影剪辑片段。相信不管大家是自己平时有独特的爱好也好、…

酷开系统 | 酷开科技挖掘与消费者深度链接的价值共鸣

市场的变化更迭速度日新月异&#xff0c;随着OTT行业的迅速发展&#xff0c;以及品牌方对于营销水准要求的逐步提高&#xff0c;以大屏为主的营销方向已成为目前营销发展的主要趋势。在互联网信息化发展的大趋势下&#xff0c;酷开科技不拘泥于现有格局&#xff0c;不断提升自身…

LeetCode 589. N 叉树的前序遍历

589. N 叉树的前序遍历 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示&#xff0c;每组子节点由空值 null 分隔&#xff08;请参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [1,nul…

打破效率瓶颈:运用Excel提升文秘与行政工作质量

文章目录 一、数据整理二、数据分析三、报表制作四、图表展示五、模板应用六、宏编程七、安全与隐私《Excel高效办公&#xff1a;文秘与行政办公&#xff08;AI版&#xff09;》编辑推荐内容简介作者简介目录获取方式 在现代企业中&#xff0c;文秘与行政办公人员的工作内容繁杂…

智慧公厕!高科技手段提升城市品质与形象

近年来&#xff0c;随着科技的不断进步&#xff0c;智慧公厕正以其独特的功能和对公共厕所全方位的信息化和数字化&#xff0c;成为智慧城市建设中的重要一环&#xff0c;悄然崭露头角。如广州中期科技有限公司自主研发的智慧公厕管理系统&#xff0c;借助于厕位监测、环境监测…