34李沐动手学深度学习v2/多GPU训练,数据并行,从0开始实现

news/2024/4/25 0:03:54/文章来源:https://blog.csdn.net/baidu_35805755/article/details/126969747
%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):'''使用functional定义LeNet'''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)# torch.mm 数学矩阵乘法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')

向多个设备分发参数

def get_params(params, device):'''将参数都放到指定设备上,并要求新设备上的参数计算梯度'''# 参数原来就在gpu上,没有clone() ,不会做任何操作# new_params = [p.clone().to(device) for p in params]new_params = [p.to(device) for p in params]# 需要对每个参数求梯度for p in new_params:# !重要,要求新设备上的参数计算梯度p.requires_grad_()return new_params# 
new_params = get_params(params, d2l.try_gpu(0))
print('b1 权重:', new_params[1])
print('b1 梯度:', new_params[1].grad)
b1 权重: tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],device='cuda:0', requires_grad=True)
b1 梯度: None

累加不同设备得出的梯度,并将结果广播给所有GPU

def allreduce(data):'''map-reduce:param data 梯度列表 不同设备得出的梯度'''# 将所有数据累加到第0个数据上for i in range(1, len(data)):# 相加到同一个gpu上data[0][:] += data[i].to(data[0].device)# 将累加结果广播给原数据所在gpu,并将原变量赋值为累加结果值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])
allreduce之前:tensor([[1., 1.]], device='cuda:0') tensor([[2., 2.]], device='cuda:1')
allreduce之后:tensor([[3., 3.]], device='cuda:0') tensor([[3., 3.]], device='cuda:1')

将一个小批量数据均匀地分布在多个GPU上

data = torch.arange(20).reshape(4, 5)
devices = [torch.device('cuda:0'), torch.device('cuda:1')]
# 并行 分散器 样本均匀切开,余数放到最后1个gpu上
split = nn.parallel.scatter(data, devices)
print('input :', data)
print('load into', devices)
print('output:', split)
input : tensor([[ 0,  1,  2,  3,  4],[ 5,  6,  7,  8,  9],[10, 11, 12, 13, 14],[15, 16, 17, 18, 19]])
load into [device(type='cuda', index=0), device(type='cuda', index=1)]
output: (tensor([[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]], device='cuda:0'), tensor([[10, 11, 12, 13, 14],[15, 16, 17, 18, 19]], device='cuda:1'))
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和y拆分到多个设备上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:l.backward()# 梯度# 累加不同设备上的梯度,并将结果广播给所有GPU# 进入with,自动进入with后的部分(__entor__())# 退出with,自动退出with后的部分(__exit__())with torch.no_grad():for i in range(len(device_params[0])):allreduce([device_params[c][i].grad for c in range(len(devices))])# 优化函数# 对不同设备上的参数,调用相同的优化函数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)]# 参数device_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:train_batch(X, y, device_params, devices, lr)# 同步 等待torch.cuda.synchronize()timer.stop()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)}')

在1个GPU上运行

train(num_gpus=1, batch_size=256, lr=0.2)
测试精度:0.84,2.7秒/轮,在[device(type='cuda', index=0)]

在这里插入图片描述

在2个GPU上运行

# 多gpu数据并行训练,速度没有变快
# 原因1:数据IO时间>>计算时间
# 原因2:小批量被切成数据块,每个gpu处理的数据变少,不能充分利用gpu的计算能力。
# 处理2:还是保证每个gpu拿到相同大小的mini_batch_size,加大lr
# 原因3:从0开始写的代码,pytorch 不能很好的多gpu
# 原因4:LeNet网络模型容量小,复杂度不够
train(num_gpus=2, batch_size=256*2, lr=0.2)
测试精度:0.78,4.3秒/轮,在[device(type='cuda', index=0), device(type='cuda', index=1)]

在这里插入图片描述

总结

单设备训练

  • 数据
  • 模型
  • 参数
  • 超参数
  • 损失函数
  • 优化函数
  • 训练
    – 前向传播
    – 后向传播

多设备训练

  • 数据
  • 设备,设备数量
  • 模型,模型相同
  • 参数,参数相同,将参数放到不同设备上
  • 超参数
  • 损失函数
  • 优化函数
  • 训练,小批量数据切分到不同设备上
    – 前向传播,不同数据,相同参数,相同模型,在不同设备上运行
    – 后向传播,不同数据,相同参数,相同模型,在不同设备上运行
    – 累加不同设备上计算出来的梯度,并将累加结果广播给不同设备

多设备训练过程:

  1. 获取数据
  2. 获取设备
  3. 将相同参数放到不同设备上
  4. 将小批量数据分出不同数据块放到不同设备上
  5. 在不同设备上使用相同模型,相同参数,不同数据块计算损失函数值,获取多个损失函数值
  6. 在不同设备上计算损失的梯度,获取多个梯度值
  7. 累加不同设备上计算出来的梯度,并将累加结果广播给不同设备
  8. 在不同设备上调用相同优化函数优化相同的参数
  9. goto 4

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

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

相关文章

机器学习模型2——决策树

前置知识 信息熵 信息增益 信息增益率 基尼系数 主要内容 决策树 本质是⼀颗由多个判断节点组成的树。 信息熵 信息熵:用来描述信息源各可能事件发生的不确定性。信息熵的值越小,说明样本的纯度越高。 以两点分布 X~为例: 信息增益(ID…

集成学习思想

集成学习算法 集成学习(ensemble learning)是时下非常流行的机器学习算法,它本身不是一个单独的机器学习算法,而是通过在数据上构建多个模型,集成所有模型的建模结果。 我们前面介绍的单一模型可以被称为“个体学习器…

Java真的不难(四十八)Redis的入门及使用(1)

Redis的入门及使用: 一、什么是Redis? REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库,Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、…

Tableau文件管理

Tableau文件管理 可以使用多种不同的Tableau文件类型,如工作簿、打包工作簿、数据提取、数据源和书签等,来保存和共享工作成果和数据源,见下表。 下面对常用的文件类型分别进行介绍。 Tableau工作簿(.twb)&#xff1…

XML 测试用例分类Variants参数

🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe&…

#2 我们有多少技术债务,每年花费多少?

#2 我们有多少技术债务,每年花费多少? 从系列中,董事会成员应该问的 7 个问题让我们从定义技术债务开始。 传统定义: 将快速交付优先于好的代码。 仁的定义: 将交付速度置于所有其他工作之上,几乎总是包括关键任务,导致工作积压不断累积,成本不断增加。 累积的技术债务…

国泰环保递交注册:年营收3.3亿同比降28% 陈柏校夫妇为实控人

雷递网 雷建平 9月20日杭州国泰环保科技股份有限公司(简称:“国泰环保”)日前递交注册,准备在深交所创业板上市。国泰环保计划募资3.3亿元,其中,1.58亿元用于成套设备制造基地项目,1.71亿元用于…

Windows 11 22H2 (2022 年更新) 发布,简体中文版、英文版下载

2022 年 9 月 20 日 今天,Windows 11 2022 更新在 190 多个国家/地区推出。 随着去年 Windows 11 的推出,我们对 PC 进行了现代更新,让您可以更快、更轻松地完成您最依赖于 PC 的任务。我们在家庭和工作场所的 Windows 中添加了内置的基础安…

【元宇宙欧米说】SchrodingerHQ:如何玩转NFT+盲盒的商业新模式

闲置的NFT如何更好地再进入市场流通?NFT与盲盒结合如何创造“11>2”的商业价值? 9月27日下午四点,SchrodingerHQ项目商务拓展TJ将以“SchrodingerHQ:如何玩转NFT盲盒的商业新模式”为题,与大家共同探讨现如…

【毕业设计】单片机森林火灾监控防护预警系统 - 物联网 嵌入式

文章目录0 前言1 简介2 主要器件3 实现效果4 硬件设计SIM800L模块的工作原理基于物联网的森林火灾探测系统框图Arduino火灾检测系统的电路图5 软件说明用于基于物联网的森林火灾检测的Arduino程序设置Thingspeak帐户6 最后0 前言 🔥 这两年开始毕业设计和毕业答辩的…

【牛客 - 剑指offer】JZ7 重建二叉树 Java实现 两种方案(递归+非递归stack)

文章目录剑指offer题解汇总 Java实现本题链接题目方案一 递归方案二 非递归 用栈实现剑指offer题解汇总 Java实现 https://blog.csdn.net/guliguliguliguli/article/details/126089434 本题链接 知识分类篇 - 树 - JZ7 重建二叉树 题目 题目的主要信息 根据二叉树的前序和中…

计算机组成原理笔记(王道考研) 第一章:计算机系统概述

内容基于中国大学MOOC的2023考研计算机组成原理课程所做的笔记。 感谢LY,他帮我做了一部分笔记。由于听的时间不一样,第四章前的内容看起来可能稍显啰嗦,后面会记得简略一些。 西电的计算机组织与体系结构课讲法和王道考研的课不太一样&…

Affinity Propagation (AP)近邻传播聚类

近邻传播聚类:根据 N 个数据点之间的相似度聚类,相似度可以是对称的,即两个数据点互相之间的相似度一样(如欧氏距离);也可以是不对称的,即两个数据点互相之间的相似度不等。这些相似度组成 NN 的相似度矩阵 S (N代表N个…

IP静态路由

IP静态路由基础概述 为了实现数据的转发,路由器必须有能力建立、刷新路由表,并根据路由表转发数据包 定义 路由是数据通信网络中的最基本的要素。路由信息就是知道报文发送的路径信息,路由的过程就是报文中继转发的过程 目的 为了实现数据的转发,路由器、路由表和路由协议是…

selenium工具之find_element(by=By.xx, value=xxx) find_elements(by=By.xx, value=xxx)详解

前言 selenium是一款十分强大的Web应用自动化框架,我们可以通过它来自动操控浏览器。操控浏览器的实质是操控浏览器的界面元素,因此定位元素是使用selenium的关键,selenium中通过 find_element() 方法来完成定位。 用法 1、通过webdriver对象的 find_element(by="属性名…

【教程】在 visual studio 共享和重用项目属性

环境 os:windows 10IDE:visual studio 2015 前言 在 visual studio 下开发项目时,通常会配置项目的属性,比如引入外部头文件,引入外部库之类的 尤其是不同的开发模式,debug 和 release,不同…

PHP+经贸时间轴 毕业设计-附源码211617

基于php经贸时间轴小程序 摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,经贸时间轴小程序被用户普遍使用…

Cache与内存映射

全相联 主存的某一Block可以映射到Cache中的任意一Block&#xff0c;多对多N<>M&#xff1b; 全相联地址格式&#xff1a; 高位为块地址与tag比较&#xff0c;offset负责取出Block内的字节 放一道例题把&#xff1a; 既然新开了一章写就写的细一点&#xff0c;Cache全…

深度学习入门:基于Python的理论与实现

1.Python入门 python中使用class关键字来定义类&#xff1a; class 类名&#xff1a;def __init__(self, 参数,...):#构造函数...def 方法1(self, 参数, ...): # 方法1...def 方法2(self, 参数, ...): # 方法2...这里有一股特殊的__init__方法&#xff0c;这是进行初始化的方…

合成/聚合复用原则

合成/聚合复用原则 很多情况继承会带来麻烦:对象的继承关系是在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有非常密切的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当需要复用子类时,如果继承下来的实现不适合解决新…