3--深度学习计算

news/2025/3/30 14:56:06/

3.1 层和块

        (block)可以描述单个层、由多个层组成的组件或整个模型本身。以下代码通过实例化nn.Sequential来构建模型, 层的执行顺序是作为参数传递的。 简而言之,nn.Sequential定义了一种特殊的Module, 即在PyTorch中表示一个块的类, 它维护了一个由Module组成的有序列表。 注意,两个全连接层都是Linear类的实例, Linear类本身就是Module的子类。 

net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))

    3.1.1 自定义块

         每个块必须提供的功能:

  1. 将输入数据作为其前向传播函数的参数。

  2. 通过前向传播函数来生成输出。请注意,输出的形状可能与输入的形状不同。例如,我们上面模型中的第一个全连接的层接收一个20维的输入,但是返回一个维度为256的输出。

  3. 计算其输出关于输入的梯度,可通过其反向传播函数进行访问。通常这是自动发生的。

  4. 存储和访问前向传播计算所需的参数。

  5. 根据需要初始化模型参数。

import torch
from torch import nn
from torch.nn import functional as FX = torch.rand(2,20)class MLP(nn.Module):def __init__(self):super().__init__()#调用MLP的父类Module的构造函数进行必要的初始化 例如线性的w和bself.hidden = nn.Linear(20,256)#隐藏层self.out = nn.Linear(256,10)#输出层def forward(self,X):#定义模型的前向传播return self.out(F.relu(self.hidden(X)))# ReLU的函数版本,其在nn.functional模块中定义net = MLP()
net(X)#块的一个主要优点是它的多功能性。 我们可以子类化块以创建层(如全连接层的类)、 整个模型(如上面的MLP类)或具有中等复杂度的各种组件。

输出结果:

tensor([[ 0.1207, -0.1397, -0.0507, 0.0215, 0.1688, 0.3081, 0.1183, -0.3262, -0.2948, 0.0578], [ 0.1575, -0.1467, -0.0656, 0.1134, 0.0938, 0.1988, 0.0573, -0.3333, -0.2152, 0.1373]], grad_fn=<AddmmBackward0>) 

3.1.2 顺序块-Sequential

     Sequential的设计是为了把其他模块顺序的串起来,自定义Sequential类只需要定义两个关键函数:

  1. 一种将块逐个追加到列表中的函数。

  2. 一种前向传播函数,用于将输入按追加块的顺序传递给块组成的“链条”。

class MySequential(nn.Module):def __init__(self,*args):super().__init__()for idx, module in enumerate(args):#enumerate()在遍历中可以获得索引和元素值。”self._modules[str(idx)] = module#把每个模块添加到字典里面#这里不使用自己定义列表是因为,在模块的参数初始化过程中系统知道在_modules字典中查找需要初始化参数的子块。def forward(self,X):for block in self._modules.values():#X = block(X)#每一个块依次进行计算return X
net = MySequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
net(X)

 输出结果:

tensor([[-0.0415, -0.1059, -0.2242, 0.2065, -0.2658, 0.2432, 0.0123, 0.0771, 0.2188, -0.0420], [ 0.0145, -0.0870, -0.1768, 0.2148, -0.1436, 0.1497, 0.0112, 0.0389, 0.0588, -0.0525]], grad_fn=<AddmmBackward0>)

 3.1.3 在向前传播函数中执行代码

         Sequential类使模型构造变得简单,允许组合新的架构,而不必定义自己的类。 然而,并不是所有的架构都是简单的顺序架构。 当需要更强的灵活性时,我们需要定义自己的块。 例如,我们可能希望在前向传播函数中执行Python的控制流。 此外,我们可能希望执行任意的数学运算, 而不是简单地依赖预定义的神经网络层。

class FixedHiddenMLP(nn.Module):def __init__(self):super().__init__()self.rand_weight = torch.rand((20,20),requires_grad=False)self.linear = nn.Linear(20,20)def forward(self,X):X = self.linear(X)X = F.relu(torch.mm(X,self.rand_weight)+1)X = self.linear(X)while X.abs().sum()>1:X /= 2return X.sum()
net = FixedHiddenMLP()
net(X)

 输出结果:

tensor(-0.1532, grad_fn=<SumBackward0>)

总结: 

  • 一个块可以由许多层组成;一个块可以由许多块组成。

  • 块可以包含代码。

  • 块负责大量的内部处理,包括参数初始化和反向传播。

  • 层和块的顺序连接由Sequential块处理 

3.2 参数管理

3.2.1 访问参数

        当通过Sequential类定义模型时, 我们可以通过索引来访问模型的任意层。 这就像模型是一个列表一样,每层的参数都在其属性中。 

import torch
from torch import nnnet = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
X = torch.rand(size=(2, 4))
net(X)print(net[2].state_dict())#访问第2层的参数字典 即nn.Linear(8.1)的参数
print(net[2].weight)#访问weight
print(net[2].bias)#访问bias
print(net[2].weight.data)#直接输出值

输出结果:

OrderedDict([('weight', tensor([[-0.3429, 0.2159, -0.3050, 0.3406, -0.1746, 0.0702, 0.2869, -0.1781]])), ('bias', tensor([0.0261]))])

Parameter containing: tensor([[-0.3429, 0.2159, -0.3050, 0.3406, -0.1746, 0.0702, 0.2869, -0.1781]], requires_grad=True)

Parameter containing: tensor([0.0261], requires_grad=True)

tensor([[-0.3429, 0.2159, -0.3050, 0.3406, -0.1746, 0.0702, 0.2869, -0.1781]])

直接访问所有层的所有参数:

print(*[(name, param.shape) for name, param in net[0].named_parameters()])
print(*[(name, param.shape) for name, param in net.named_parameters()])
#加*的目的是取出list中的每个值
#例:print(*[1,2,3]) --> 1 2 3

输出结果:

('weight', torch.Size([8, 4])) ('bias', torch.Size([8])) ('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1])) 

3.2.2 参数初始化

        自定义参数的初始化规则

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))
def init_normal(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, mean=0, std=0.01)#将所有权重参数初始化为标准差为0.01的高斯随机变量#nn.init.constant_(m.weight, 1) #将wight参数初始化为给定的常数1nn.init.zeros_(m.bias)#将偏置参数设置为0
net.apply(init_normal)
net[0].weight.data[0],net[0].bias.data#nn.Linear(4, 8)初始化后的权重和偏置

输出结果: 

(tensor([-0.0001, -0.0070, 0.0078, 0.0049]), tensor([0., 0., 0., 0., 0., 0., 0., 0.]))

#(tensor([1., 1., 1., 1.]), tensor([0., 0., 0., 0., 0., 0., 0., 0.]))

        对不同层网络使用不同的初始化参数方法,例如使用Xavier初始化方法初始化第一个神经网络层, 然后将第三个神经网络层初始化为常量值42。 

def init_xavier(m):if type(m)==nn.Linear:nn.init.xavier_uniform_(m.weight)
def init_42(m):if type(m)==nn.Linear:nn.init.constant_(m.weight,42)
net[0].apply(init_xavier)
net[2].apply(init_42)
print(net[0].weight.data[0])
print(net[2].weight.data)

输出结果:

 tensor([-0.1781, -0.4115, 0.3015, 0.1715]) tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])

        实现w满足该函数的初始化代码(思想跟前面一致,这里函数的表达自己写不出来 所以记录下来):

def my_init(m):if type(m) == nn.Linear:print("Init", *[(name, param.shape)for name, param in m.named_parameters()][0])nn.init.uniform_(m.weight, -10, 10)m.weight.data *= m.weight.data.abs() >= 5net.apply(my_init)

3.2.3 不同模型组件间共享参数 

        在多个层间共享参数以定义一个稠密层,然后使用它的参数来设置另一个层的参数 。

# 需要给共享层一个名称,以便可以引用它的参数
shared = nn.Linear(8, 8)
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(),shared, nn.ReLU(),shared, nn.ReLU(),nn.Linear(8, 1))
net(X)
# 检查参数是否相同
print(net[2].weight.data[0] == net[4].weight.data[0])
net[2].weight.data[0, 0] = 100
# 确保它们实际上是同一个对象,而不只是有相同的值
print(net[2].weight.data[0] == net[4].weight.data[0])输出:
tensor([True, True, True, True, True, True, True, True])
tensor([True, True, True, True, True, True, True, True])

3.3 自定义层

3.3.1 不带参数的层 

        这里不需要参数,就直接使用父类的初始化,并设置一个向前传播forward函数(这里设置为从输入中减去均值)。

class Mylayer(nn.Module):def __init__(self):super().__init__()def forward(self,X):return X-X.mean()net = nn.Sequential(nn.Linear(8,128),Mylayer())#可直接传入Sequential进行使用
X = torch.rand(4,8)
Y = net(X)
Y.mean()

输出结果:(由于处理的是浮点数,因为存储精度的原因,我们仍然可能会看到一个非常小的非零数。)

tensor(9.3132e-09, grad_fn=<MeanBackward0>) 

3.3.2 带参数的层

        该层需要两个参数,一个用于表示权重,另一个用于表示偏置项。 使用修正线性单元作为激活函数。 该层需要输入参数:in_unitsunits,分别表示输入数和输出数。

class MyLinear(nn.Module):def __init__(self, in_units, units):super().__init__()self.weight = nn.Parameter(torch.randn(in_units, units))self.bias = nn.Parameter(torch.randn(units,))def forward(self, X):linear = torch.matmul(X, self.weight.data) + self.bias.datareturn F.relu(linear)linear = MyLinear(5, 3)
linear.weight

输出结果:

Parameter containing:
tensor([[-1.4779, -0.6027, -0.2225],[ 1.1270, -0.6127, -0.2008],[-2.1864, -1.0548,  0.2558],[ 0.0225,  0.0553,  0.4876],[ 0.3558,  1.1427,  1.0245]], requires_grad=True)

3.4 读写文件

3.4.1 加载和保存张量

        可以直接调用loadsave函数分别读写它们。 这两个函数都要求我们提供一个名称,save要求将要保存的变量作为输入。

x = torch.arange(4)
torch.save(x, 'x-file')#写到名为“x-file”的文件中y = torch.load('x-file')
x==y 

输出:

tensor([True, True, True, True]) 

3.4.2 加载和保存模型参数

         深度学习框架提供了内置函数来保存和加载整个网络。 需要注意的一个重要细节是,这将保存模型的参数而不是保存整个模型。

class MLP(nn.Module):def __init__(self):super().__init__()self.hidden = nn.Linear(20, 256)self.output = nn.Linear(256, 10)def forward(self, x):return self.output(F.relu(self.hidden(x)))net = MLP()
X = torch.randn(size=(2, 20))
Y = net(X)torch.save(net.state_dict(), 'mlp.params')#实例化了原始多层感知机模型的一个备份,这里不需要随机初始化模型参数,而是直接读取文件中存储的参数。
clone = MLP()
clone.load_state_dict(torch.load('mlp.params'))Y_clone = clone(X)
Y_clone == Y#检查是否一致

输出结果:

tensor([[True, True, True, True, True, True, True, True, True, True],[True, True, True, True, True, True, True, True, True, True]])

3.5 GPU

  • 我们可以指定用于存储和计算的设备,例如CPU或GPU。默认情况下,数据在主内存中创建,然后使用CPU进行计算。

  • 深度学习框架要求计算的所有输入数据都在同一设备上,无论是CPU还是GPU。

  • 不经意地移动数据可能会显著降低性能。一个典型的错误如下:计算GPU上每个小批量的损失,并在命令行中将其报告给用户(或将其记录在NumPy ndarray中)时,将触发全局解释器锁,从而使所有GPU阻塞。最好是为GPU内部的日志分配内存,并且只移动较大的日志。


http://www.ppmy.cn/news/300760.html

相关文章

Pytorch神经网络基础——层和块(笔记)

一。层和块 1.多层感知机 import torch from torch import nn from torch.nn import functional as Fnet nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10)) # 定义网络 X torch.rand(2, 20) # 2*20随机矩阵 print(net(X)) 输出结果&#xff1a; tens…

白盒交换机公司产品列表

信息来源&#xff1a; http://www.pica8.com/products/hardware-compatibility-list https://cumulusnetworks.com/products/hardware-compatibility-list/ https://opennetlinux.org/hcl https://github.com/Azure/SONiC/wiki/Supported-Devices-and-Platforms DELL Z9100-ON …

Windows Phone Mango(版本号为7712)的申请开放

最近几天&#xff0c;微软已经开始向App Hub中的注册开发者们推送版本号为7712的Mango更新(不是RTM版)。如果你一直没有得到过微软的Mango Beta测试邀请&#xff0c;现在可以直接到微软申请了。 你需要到https://www.research.net/s/Z95RKYZ填写自己的App Hub中的一些注册信息。…

Mango 7712 is coming

更新完成后&#xff0c;开机LOGO变成红色 估计是为了更加醒目的表示是测试版吧 支持的账户格式更多了 包括office365&#xff0c;Twitter&#xff0c;Linkedin等等 有兴趣的自己update吧 本文转自 sun8134 博客园博客&#xff0c;原文链接&#xff1a; http://www.cnblogs.com/…

Σ-Δ型ADC转换原理及程序设计--AD7712

要理解Σ-Δ型 ADC的工作原理&#xff0c;首先应对以下概念有所了解&#xff1a;①过采样、②噪声成形、③数字滤波和抽取。 ①过采样 单音&#xff08;基频&#xff09;信号的功率与所有频率的噪声的RMS功率之和的比值就是信号噪声比&#xff08;SNR&#xff09;。对于一个N位…

63个国家7712个设计项目参赛,第11届艾特奖引发全球关注

第11届艾特奖全球作品征集已于2021年2月11日24时截止。本届艾特奖共收到来自全球63个国家的参赛设计作品7712件&#xff0c;参赛国家、参赛作品数量均突破以往。 第11届艾特奖参赛项目统计 国际空间设计大奖—艾特奖&#xff08;英文简称IDEA-TOPS&#xff09;是当今中国最具国…

万维网服务器

一、域名解析gethostbyname函数 struct hostent {char *h_name; /* 官方域名 */char **h_aliases; /* 别名*/int h_addrtype; /* 地址族&#xff08;地址类型&#xff09; */int h_length; /* 地址长度 */char **h_addr_list; …

深入聊一下机械硬盘的相关内容

本文是《数据存储通识课》合集的一部分,本合集希望通过一系列文章科普数据存储相关技术内容。同时,本系列文章不仅仅是科普,还会进行有深度解析,理论结合实现,从代码实现层面进行剖析​ 介绍存储技术当然要从存储技术最基本的组件磁盘开始介绍了。目前市面上我们见得最多的…