多GPU训练

写在前面

限于财力不足,本机上只有一个 GPU 可供使用,因此这部分的代码只能够稍作了解,能够使用的 GPU 也只有一个。

多 GPU 的数据并行:有几张卡,对一个小批量数据,有几张卡就分成几块,每个 GPU 分别计算梯度,然后加起来做并行。

从零开始实现

%matplotlib inline
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

简单网络

# 初始化模型参数
scale = 0.01
W1 = torch.randn(size=(20, 1, 3, 3)) * scale
b1 = torch.zeros(20)
W2 = torch.randn(size=(50, 20, 5, 5)) * scale
b2 = torch.zeros(50)
W3 = torch.randn(size=(800, 128)) * scale
b3 = torch.zeros(128)
W4 = torch.randn(size=(128, 10)) * scale
b4 = torch.zeros(10)
params = [W1, b1, W2, b2, W3, b3, W4, b4]# 定义模型
def lenet(X, params):h1_conv = F.conv2d(input=X, weight=params[0], bias=params[1])h1_activation = F.relu(h1_conv)h1 = F.avg_pool2d(input=h1_activation, kernel_size=(2, 2), stride=(2, 2))h2_conv = F.conv2d(input=h1, weight=params[2], bias=params[3])h2_activation = F.relu(h2_conv)h2 = F.avg_pool2d(input=h2_activation, kernel_size=(2, 2), stride=(2, 2))h2 = h2.reshape(h2.shape[0], -1)h3_linear = torch.mm(h2, params[4]) + params[5]h3 = F.relu(h3_linear)y_hat = torch.mm(h3, params[6]) + params[7]return y_hat# 交叉熵损失函数
loss = nn.CrossEntropyLoss(reduction='none')

向多个设备分发参数,并通过将模型参数复制到一个GPU:

def get_params(params, device): # 把一个参数复制到另外一个GPU上去new_params = [p.to(device) for p in params]for p in new_params:p.requires_grad_() #对每一个参数都需要计算梯度return new_paramsnew_params = get_params(params, d2l.try_gpu(0))
print('b1 权重:', new_params[1])
print('b1 梯度:', new_params[1].grad)

在这里插入图片描述
allreduce函数将所有向量相加,并将结果广播给所有GPU

def allreduce(data):for i in range(1, len(data)):data[0][:] += data[i].to(data[0].device)for i in range(1, len(data)):data[i][:] = data[0].to(data[i].device)data = [torch.ones((1, 2), device=d2l.try_gpu(i)) * (i + 1) for i in range(2)]
print('allreduce之前:\n', data[0], '\n', data[1])
allreduce(data)
print('allreduce之后:\n', data[0], '\n', data[1])

在这里插入图片描述
将一个小批量数据均匀地分布在多个 GPU 上

data = torch.arange(20).reshape(4, 5)
devices = [torch.device('cuda:0'), torch.device('cuda:1')]
split = nn.parallel.scatter(data, devices)
print('input :', data)
print('load into', devices)
print('output:', split)

在这里插入图片描述

#@save
def split_batch(X, y, devices):"""将X和y拆分到多个设备上"""assert X.shape[0] == y.shape[0]return (nn.parallel.scatter(X, devices),nn.parallel.scatter(y, devices))

在一个小批量上实现多GPU训练

def train_batch(X, y, device_params, devices, lr):X_shards, y_shards = split_batch(X, y, devices)# 在每个GPU上分别计算损失ls = [loss(lenet(X_shard, device_W), y_shard).sum()for X_shard, y_shard, device_W in zip(X_shards, y_shards, device_params)]for l in ls:  # 反向传播在每个GPU上分别执行l.backward()# 将每个GPU的所有梯度相加,并将其广播到所有GPUwith torch.no_grad():for i in range(len(device_params[0])):allreduce([device_params[c][i].grad for c in range(len(devices))])# 在每个GPU上分别更新模型参数for param in device_params:d2l.sgd(param, lr, X.shape[0]) # 在这里,我们使用全尺寸的小批量

定义训练模型:

def train(num_gpus, batch_size, lr):train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)devices = [d2l.try_gpu(i) for i in range(num_gpus)]# 将模型参数复制到num_gpus个GPUdevice_params = [get_params(params, d) for d in devices]num_epochs = 10animator = d2l.Animator('epoch', 'test acc', xlim=[1, num_epochs])timer = d2l.Timer()for epoch in range(num_epochs):timer.start()for X, y in train_iter:# 为单个小批量执行多GPU训练train_batch(X, y, device_params, devices, lr)torch.cuda.synchronize()timer.stop()# 在GPU0上评估模型animator.add(epoch + 1, (d2l.evaluate_accuracy_gpu(lambda x: lenet(x, device_params[0]), test_iter, devices[0]),))print(f'测试精度:{animator.Y[0][-1]:.2f}{timer.avg():.1f}秒/轮,'f'在{str(devices)}')

在单个 GPU 上运行:
在这里插入图片描述
增加为 2 个 GPU
在这里插入图片描述
并行后并没有变快,可能有以下原因:

  • Data 读取比较慢
  • GPU 增加了,但是 batch_size 没有增加

多 GPU 的简洁实现

import torch
from torch import nn
from d2l import torch as d2l

简单网络

#@save
def resnet18(num_classes, in_channels=1):"""稍加修改的ResNet-18模型"""def resnet_block(in_channels, out_channels, num_residuals,first_block=False):blk = []for i in range(num_residuals):if i == 0 and not first_block:blk.append(d2l.Residual(in_channels, out_channels,use_1x1conv=True, strides=2))else:blk.append(d2l.Residual(out_channels, out_channels))return nn.Sequential(*blk)# 该模型使用了更小的卷积核、步长和填充,而且删除了最大汇聚层net = nn.Sequential(nn.Conv2d(in_channels, 64, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(64),nn.ReLU())net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))net.add_module("resnet_block2", resnet_block(64, 128, 2))net.add_module("resnet_block3", resnet_block(128, 256, 2))net.add_module("resnet_block4", resnet_block(256, 512, 2))net.add_module("global_avg_pool", nn.AdaptiveAvgPool2d((1,1)))net.add_module("fc", nn.Sequential(nn.Flatten(),nn.Linear(512, num_classes)))return netnet = resnet18(10)
# 获取GPU列表
devices = d2l.try_all_gpus()
# 我们将在训练代码实现中初始化网络

训练

def train(net, num_gpus, batch_size, lr):train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)devices = [d2l.try_gpu(i) for i in range(num_gpus)]def init_weights(m):if type(m) in [nn.Linear, nn.Conv2d]:nn.init.normal_(m.weight, std=0.01)net.apply(init_weights)# 在多个GPU上设置模型net = nn.DataParallel(net, device_ids=devices)trainer = torch.optim.SGD(net.parameters(), lr)loss = nn.CrossEntropyLoss()timer, num_epochs = d2l.Timer(), 10animator = d2l.Animator('epoch', 'test acc', xlim=[1, num_epochs])for epoch in range(num_epochs):net.train()timer.start()for X, y in train_iter:trainer.zero_grad()X, y = X.to(devices[0]), y.to(devices[0])l = loss(net(X), y)l.backward()trainer.step()timer.stop()animator.add(epoch + 1, (d2l.evaluate_accuracy_gpu(net, test_iter),))print(f'测试精度:{animator.Y[0][-1]:.2f}{timer.avg():.1f}秒/轮,'f'在{str(devices)}')

在单个 GPU 上训练网络

train(net, num_gpus=1, batch_size=256, lr=0.1)

在这里插入图片描述
使用2个GPU进行训练

train(net, num_gpus=2, batch_size=512, lr=0.2)

在这里插入图片描述

QA 思考

Q1:验证集准确率震荡较大是哪个参数影响最大呢?
A1:lr

Q2:为什么batch_size调的比较小,比如8,精度会一直在0.1左右,一直不怎么变化
A2:因为batch_size调的比较小的时候,lr 不能太大。

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

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

相关文章

0基础 | 硬件 | 电源系统 一

降压电路LDO 几乎所有LDO都是基于此拓扑结构 图 拓扑结构 LDO属于线性电源,通过控制开关管的导通程度实现稳压,输出纹波小,无开关噪声 线性电源,IoutIin,发热功率P电压差△U*电流I,转换效率Vo/Vi LDO不适…

mysql数据库中getshell的方式总结

mysql数据库中getshell的方式总结 MySQL版本大于5.0,MySQL 5.0版本以上会创建日志文件,我们通过修改日志文件的全局变量,就可以GetSHELL,下面这篇文章主要给大家介绍了关于mysql数据库中getshell的方式,需要的朋友可以参考下 outfile和dumpfile写shell 利用条件 …

基于Python的微博数据采集

摘要 本系统通过逆向工程微博移动端API接口,实现了对热门板块微博内容及用户评论的自动化采集。系统采用Requests+多线程架构,支持递归分页采集和动态请求头模拟,每小时可处理3000+条数据记录。关键技术特征包括:1)基于max_id的评论分页递归算法 2)HTML标签清洗正则表达…

WiFi加密协议

目录 1. 认证(Authentication)‌ ‌1.1 开放系统认证(Open System Authentication)‌ 1.2 共享密钥认证(Shared Key Authentication)‌ ‌1.3 802.1X/EAP认证(企业级认证)‌ ‌2. 关联(Association)‌ ‌3. 加密协议(Security Handshake)‌ ‌整体流程总结‌…

MySQL篇(六)MySQL 分库分表:应对数据增长挑战的有效策略

MySQL篇(六)MySQL 分库分表:应对数据增长挑战的有效策略 MySQL篇(六)MySQL 分库分表:应对数据增长挑战的有效策略一、引言二、为什么需要分库分表2.1 性能瓶颈2.2 存储瓶颈2.3 高并发压力 三、分库分表的方…

极限编程(XP)简介及其价值观与最佳实践

目录 一、什么是极限编程(XP)二、极限编程的核心价值观1. 沟通2. 简单3. 反馈4. 勇气 三、极限编程的12个最佳实践1. 结对编程2. 40小时工作制3. 简单设计4. 代码规范5. 测试驱动开发(TDD)6. 系统隐喻7. 持续集成8. 重构9. 客户在…

Java进阶-day06:反射、注解与动态代理深度解析

目录 一、反射机制:Java的自我认知能力 1.1 认识反射 1.2 获取Class对象 1.3 获取类的成分 二、注解:Java的元数据机制 2.1 注解概述 2.2 元注解 2.3 注解解析 2.4 注解的实际应用 三、动态代理:灵活的间接访问机制 3.1 为什么需要…

Nacos注册中心AP模式核心源码分析(集群模式)

文章目录 概述一、客户端新注册实例信息在集群间同步二、服务端集群节点信息在集群间同步2.1、DistroMapper2.2、ProtocolManager2.3、ServerListManager2.4、RaftPeerSet 三、客户端实例状态信息在集群间同步四、服务端新节点上线同步集群数据 概述 在Nacos集群模式下&#xf…

vscode和cursor对ubuntu22.04的remote ssh和X-Windows的无密码登录

这里写自定义目录标题 写在前面需求的描述问题的引出 昨天已使能自动登录上午我的改变UBUNTU 22.04关闭密码规则一:修改 /etc/pam.d/common-password 文件二:修改 /etc/security/pwquality.conf 文件方法三:禁用 pam_pwquality.so 模块 vscod…

论文阅读:基于增强通用深度图像水印的混合篡改定位技术 OmniGuard

一、论文信息 论文名称:OmniGuard: Hybrid Manipulation Localization via Augmented Versatile Deep Image Watermarking作者团队:北京大学发表会议:CVPR2025论文链接:https://arxiv.org/pdf/2412.01615二、动机与贡献 动机: 随着生成式 AI 的快速发展,其在图像编辑领…

一周学会Pandas2 Python数据处理与分析-NumPy数组创建

锋哥原创的Pandas2 Python数据处理与分析 视频教程: 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili NumPy数组创建最常用的方式是直接创建, numpy 可以直接创建或者将 python的其他元素转为 array 对象。 下…

【全球首发】DeepSeek谷歌版1.1.5 - 免费GPT-4级别AI工具

【全球首发】DeepSeek谷歌版1.1.5 - 免费GPT-4级别AI工具 资源简介 DeepSeek谷歌版1.1.5是目前全球领先的免费AI助手,性能超越国内主流AI产品,提供类似GPT-4的智能体验。 版本信息 最新版本:1.1.5(2024最新版)应用…

小程序29-事件穿参-mark 自定义数据

小程序进行事件传参的时候,除了使用 data-*属性 传递参数外,还可以 使用 mark 标记传递参数 mark 是一种自定义属性,可以在组件上添加,用于来识别具体触发事件的 target 节点。同时 mark 还可以用于承载一些自定义数据 在组件上使…

高级:分布式系统面试题精讲

一、引言 分布式系统在现代软件开发中占据重要地位,其设计和实现需要考虑多个关键因素。面试官通过相关问题,考察候选人对分布式系统核心概念的理解、实际应用能力以及在复杂场景下的问题解决能力。本文将深入分析分布式系统的CAP定理、一致性协议、分布…

【Android Studio 下载 Gradle 失败】

路虽远行则将至,事虽难做则必成 一、事故现场 下载Gradle下载不下来,没有Gradle就无法把项目编译为Android应用。 二、问题分析 观察发现下载时长三分钟,进度条半天没动,说明这个是国外的东西,被墙住了,需…

系统思考:思考的快与慢

在做重大决策之前,什么原因一定要补充碳水化合物?人类的大脑其实有两套运作模式:系统1:自动驾驶模式,依赖直觉,反应快但易出错;系统2:手动驾驶模式,理性严谨,…

从情感分析到朴素贝叶斯法:基于朴素贝叶斯的情感分析如何让DeepSeek赋能你的工作?

文章目录 1.概率论基础1.1 单事件概率1.2 多事件概率1.3 条件概率1.3.1 多事件概率与条件概率的区别 1.4 贝叶斯定理传统思维误区贝叶斯定理计算 2. 朴素贝叶斯法2.1 基本概念2.2 模型2.3 学习策略2.4 优化算法2.5 优化技巧拉普拉斯平滑对数似然 3. 情感分析实战3.1 流程3.2 模…

获取inode的完整路径包含挂载的路径

一、背景 在之前的博客 缺页异常导致的iowait打印出相关文件的绝对路径-CSDN博客 里的 2.2.3 一节和 关于inode,dentry结合软链接及硬链接的实验-CSDN博客 里,我们讲到了在内核里通过inode获取inode对应的绝对路径的方法。对于根目录下的文件而言&#…

【51单片机】2-6【I/O口】【电动车简易防盗报警器实现】

1.硬件 51最小系统继电器模块震动传感器模块433M无线收发模块 2.软件 #include "reg52.h" #include<intrins.h> #define J_ON 1 #define J_OFF 0sbit switcher P1^0;//继电器 sbit D0_ON P1^1;//433M无线收发模块 sbit D1_OFF P1^2; sbit vibrate …

leetcode二叉树刷题调试不方便的解决办法

1. 二叉树不易构建 在leetcode中刷题时&#xff0c;如果没有会员就需要将代码拷贝到本地的编译器进行调试。但是leetcode中有一类题可谓是毒瘤&#xff0c;那就是二叉树的题。 要调试二叉树有关的题需要根据测试用例给出的前序遍历&#xff0c;自己构建一个二叉树&#xff0c;…