pytorch12:GPU加速模型训练

在这里插入图片描述

目录

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

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


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AIGC】一组精美动物AI智能画法秘诀

如何使用AI绘画,从以下角度,依据表格内容梳理,表格如下: 外貌特征物种姿势特征描述场景风格技术描述小巧可爱幼小浣熊倚在桌子上具有人形特征中世纪酒馆电影风格照明8k分辨率细节精致毛茸茸手持咖啡杯Jean-Baptiste Monge的风格蓝…

【AIGC-文本/图片生成视频系列-8】Align your Latents: 基于潜在扩散模型的高分辨率视频合成

目录 一. 项目概述与贡献 二. 方法详解 三. 应用总览 四. 个性化视频生成 五. 实时卷积合成 六. 更多结果 七. 论文 八. 个人思考 AI生成高分辨率视频一直是一个挑战。 今天讲解一篇潜在扩散模型(LDM)用于高分辨率、时间一致且多样化的视频生成…

模拟数字转换器

本节主要介绍以下内容: ADC简介 ADC功能框图详解 参考资料:《零死角玩转STM32》“ADC—电压采集”章节 一、ADC简介 ADC :Analog to Digital,模拟数字转换器 三个独立的ADC 1 / 2 / 3分辨率为12位每个ADC具有18个通道,其中…

自旋锁和互斥锁的区别

自旋锁和互斥锁的区别_自旋锁和互斥锁有什么区别?-CSDN博客

网工内推 | 高级网工,H3C认证优先,朝九晚六,周末双休

01 万德 招聘岗位:高级网络工程师 职责描述: 1、项目交付:项目管理和交付,包括项目前期的规划、实施以及后期的运维支持、项目验收等。 2、技术支持:为客户及合作伙伴提供网上问题远程和现场支持;对公司内…

【MySQL】锁机制

文章目录 一、表级锁和行级锁二、排他锁和共享锁三、InnoDB行级锁行级锁间隙锁意向共享锁和意向排他锁 四、InnoDB表级锁五、死锁六、锁的优化建议 一、表级锁和行级锁 表级锁: 对整张表加锁。开销小,加锁快,不会出现死锁;锁粒度…

msvcp140_codecvt_ids.dll缺失的解决方法,dll文件全面解析

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“msvcp140CODECVTIDS.dll丢失”。那么,msvcp140CODECVTIDS.dll是什么文件?它的作用是什么?为什么会丢失?本文将详细介绍msvcp140CODECVTIDS.d…

你知道程序员如何利用citywork实现财富自由吗?

周末到了,我要去citywalk寻找心灵的呼吸!”有谁没有设想过疲惫的工作日之后好好地去走一走,亲近大自然呢?谁又不想在闲暇之余唤起对生活的趣味呢?可是对于我们悲催的打工人而言,没有citywalk,只…

在WindowsServer2012中部署war项目

目录 前言 一.jdk安装 二.Tomact安装 三.MySQL安装 ​编辑​编辑​编辑​编辑​编辑​编辑​编辑 四.开放端口号 MySQL开放端口号 Tomact开放端口号 ​编辑 五.项目部署 1.将war放置在tomact中 2.配置项目sql脚本 3.最终效果 前言 安装Java开发工具包&#xff08…

ROS2——Parameters

节点可以使用参数来配置各项操作,这些参数可以说布尔值、整数、字符串等类型。节点在启动时会读取参数。我们将参数单独列出来,而不是写在源文件中,这样做可以方便我们调试,因为在不同的机器人、环境中,我们需要的参数…

Java并发编程——伪共享和缓存行问题

在Java并发编程中,伪共享(False Sharing)和缓存行(Cache Line)是与多线程访问共享数据相关的两个重要概念。 伪共享指的是多个线程同时访问同一个缓存行中的不同变量或数据,其中至少一个线程对其中一个变…

EM planner 论文阅读

论文题目:Baidu Apollo EM Motion Planner 0 前言 EM和Lattice算法对比 EM plannerLattice Planner参数较多(DP/QP,Path/Speed)参数少且统一化流程复杂流程简单单周期解空间受限简单场景解空间较大能适应复杂场景适合简单场景 …

回归预测 | Matlab基于SMA+WOA+SFO-LSSVM多输入单输出回归预测

回归预测 | Matlab基于SMAWOASFO-LSSVM多输入单输出回归预测 目录 回归预测 | Matlab基于SMAWOASFO-LSSVM多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 SMAWOASFO-LSSVM回归预测 基于黏菌算法鲸鱼算法向日葵算法优化LSSVM回归预测 其中包含三种改进…

ADOV路由和DSR路由matlab对比仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 ADOV路由(Ad hoc On-demand Distance Vector Routing) 4.2 DSR路由(Dynamic Source Routing) 5.完整程序 1.程序功能描述 ADOV路由和DSR…

SQL优化小技巧

在表中建⽴索引,优先考虑 where group by 使⽤到的字段。 查询时尽量避免使⽤select * ,只查询需要⽤到的字段。 避免在where⼦句中使⽤关键字两边都是%的模糊查询,尽量在关键字后使⽤模糊查询。 尽量避免在where⼦句中使⽤IN 和NOT IN。 …

【Vue系列】Vue3快速构建项目,以及在已有代码情况首次打开如何初始化依赖项

欢迎来到《小5讲堂》 大家好,我是全栈小5。 这是是《前端》序列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点的理解和掌…

【AI视野·今日NLP 自然语言处理论文速览 第七十三期】Tue, 9 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Tue, 9 Jan 2024 Totally 80 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers FFSplit: Split Feed-Forward Network For Optimizing Accuracy-Efficiency Trade-off in Language Model Infe…

SpringBoot外部配置文件

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏&…

三防便携式手持PDA能提高企业仓储数字化管理吗

随着数字化管理的不断普及,企业对于仓储管理的要求也越来越高。在这种背景下,三防便携式手持PDA作为一种智能化的仓储管理设备,具备了多种强大的功能,为企业提供了数字化管理的便利和高效。本文将从PDA的数据采集功能、人脸识别功…

RK3399平台入门到精通系列讲解(基础篇)__LITTLE_ENDIAN_BITFIELD 宏的使用

🚀返回总目录 文章目录 一、什么是字节序二、小端模式(Little-Endian)三、大端模式(Big-Endian)四、__LITTLE_ENDIAN_BITFIELD 使用案例一、什么是字节序 在计算机中,数据是以最原始的二进制 0 和 1 的方式被存储的。在大多数现代计算机体系架构中,计算机的最小可寻址数…