早教网站模板,wordpress 远程代码,网站建设中的矢量图标,建设网站企业网上银行登录入口系列文章
李沐《动手学深度学习》预备知识 张量操作及数据处理 李沐《动手学深度学习》预备知识 线性代数及微积分 李沐《动手学深度学习》线性神经网络 线性回归 李沐《动手学深度学习》线性神经网络 softmax回归 李沐《动手学深度学习》多层感知机 模型概念和代码实现 李沐《…系列文章
李沐《动手学深度学习》预备知识 张量操作及数据处理 李沐《动手学深度学习》预备知识 线性代数及微积分 李沐《动手学深度学习》线性神经网络 线性回归 李沐《动手学深度学习》线性神经网络 softmax回归 李沐《动手学深度学习》多层感知机 模型概念和代码实现 李沐《动手学深度学习》多层感知机 深度学习相关概念 目录 系列文章一、层和块一块的概念二块的实现 二、参数管理一参数访问用于调试、诊断和可视化二参数初始化内置初始化、自定义初始化三参数绑定在不同模型组件间共享参数 三、延后初始化四、自定义层一不带参数的层二带参数的层 五、读写文件一加载和保存张量二加载和保存模型参数 六、GPU一计算设备二张量与GPU三神经网络与GPU 教材李沐《动手学深度学习》
一、层和块
一块的概念
块block可以描述单个层、由多个层组成的组件或整个模型本身
使用块进行抽象的一个好处是可以将一些块组合成更大的组件 这一过程通常是递归的通过定义代码来按需生成任意复杂度的块 我们可以通过简洁的代码实现复杂的神经网络。 二块的实现
从编程的角度来看块由类表示。每个块必须提供的基本功能 将输入数据作为其前向传播函数的参数通过前向传播函数来生成输出计算其输出关于输入的梯度可通过其反向传播函数进行访问通常这是自动发生的存储和访问前向传播计算所需的参数根据需要初始化模型参数。 层和块的顺序连接由Sequential块处理 nn.Sequential定义了一种特殊的Module 即在PyTorch中表示一个块的类 它维护了一个由Module组成的有序列表通过net(X)调用模型来获得模型的输出这实际上是net.call(X)的简写。 这个前向传播函数非常简单 它将列表中的每个块连接在一起将每个块的输出作为下一个块的输入。
net nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
X torch.rand(2, 20)
net(X)一个块可以由许多层组成一个块可以由许多块组成。
class NestMLP(nn.Module):def __init__(self):super().__init__()self.net nn.Sequential(nn.Linear(20, 64), nn.ReLU(),nn.Linear(64, 32), nn.ReLU())self.linear nn.Linear(32, 16)def forward(self, X):return self.linear(self.net(X))chimera nn.Sequential(NestMLP(), nn.Linear(16, 20), FixedHiddenMLP())
chimera(X)二、参数管理
具有单隐藏层的多层感知机
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)一参数访问用于调试、诊断和可视化
当通过Sequential类定义模型时可以通过索引来访问模型的任意层。 参数访问方式一
print(net[2].state_dict()) #获得第二个全连接层的参数
print(type(net[2].bias)) #第二个全连接层偏置的类型
print(net[2].bias) #第二个全连接层的偏置 参数是复合的对象包含值、梯度和额外信息。
print(net[2].bias.data) #第二个全连接层偏置的数值参数访问方式二
net.state_dict()[2.bias].data从嵌套块收集参数时也可以像通过嵌套列表索引一样访问它们
#获得第一个主要的块中、第二个子块的第一层的偏置项。
rgnet[0][1][0].bias.data二参数初始化内置初始化、自定义初始化
深度学习框架提供默认随机初始化 也允许我们创建自定义初始化方法 满足我们通过其他规则实现初始化权重。
内置初始化PyTorch的nn.init模块提供了多种预置初始化方法
调用内置初始化器将所有权重参数初始化为标准差为0.01的高斯随机变量偏置参数初始化为0
def init_normal(m):if type(m) nn.Linear:nn.init.normal_(m.weight, mean0, std0.01)nn.init.zeros_(m.bias)
net.apply(init_normal)
net[0].weight.data[0], net[0].bias.data[0]调用内置初始化器将参数初始化为1
def init_constant(m):if type(m) nn.Linear:nn.init.constant_(m.weight, 1)nn.init.zeros_(m.bias)
net.apply(init_constant)
net[0].weight.data[0], net[0].bias.data[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)自定义初始化 使用以下的分布为任意权重参数 w w w定义初始化方法 w { U ( 5 , 10 ) 可能性 0.25 0 可能性 0.5 U ( − 10 , − 5 ) 可能性 0.25 w \begin{cases} U(5,10) \text{可能性 0.25 } \\ 0 \text{可能性 0.5}\\ U(-10,-5) \text{可能性 0.25} \end{cases} w⎩ ⎨ ⎧U(5,10)0U(−10,−5)可能性 0.25 可能性 0.5可能性 0.25
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)
net[0].weight[:2]也可以直接设置参数值
net[0].weight.data[:] 1
net[0].weight.data[0, 0] 42
net[0].weight.data[0]三参数绑定在不同模型组件间共享参数
为了在多个层间共享参数可以定义一个稠密层然后使用它的参数来设置另一个层的参数
第三个和第五个神经网络层的参数是绑定的不仅值相等而且由相同的张量表示改变其中一个参数另一个参数也会改变反向传播期间第二个隐藏层 即第三个神经网络层和第三个隐藏层即第五个神经网络层的梯度会加在一起。
# 我们需要给共享层一个名称以便可以引用它的参数
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])三、延后初始化
框架的延后初始化直到数据第一次通过模型传递时框架才会动态地推断出每个层的大小。
四、自定义层
一不带参数的层
构建一个CenteredLayer类要从其输入中减去均值
import torch
import torch.nn.functional as F
from torch import nnclass CenteredLayer(nn.Module):def __init__(self):super().__init__()def forward(self, X):return X - X.mean()二带参数的层
实现自定义版本的全连接层
需要两个参数一个用于表示权重另一个用于表示偏置项使用修正线性单元作为激活函数in_units和units分别表示输入数和输出数。
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)五、读写文件
一加载和保存张量
save和load函数可用于张量对象的文件读写
import torch
from torch import nn
from torch.nn import functional as Fx torch.arange(4)
torch.save(x, x-file)x2 torch.load(x-file)
x2二加载和保存模型参数
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))
clone.eval()六、GPU
一计算设备
可以指定用于存储和计算的设备如CPU和GPU。 默认情况下张量是在内存中创建的然后使用CPU计算它。 在PyTorch中CPU和GPU可以用torch.device(‘cpu’) 和torch.device(‘cuda’)表示。 应该注意的是cpu设备意味着所有物理CPU和内存 这意味着PyTorch的计算将尝试使用所有CPU核心。 然而gpu设备只代表一个卡和相应的显存。 如果有多个GPU我们使用torch.device(f’cuda:{i}) 来表示第 i i i 块GPU从0开始。 另外cuda:0和cuda是等价的。 import torch
from torch import nntorch.device(cpu), torch.device(cuda), torch.device(cuda:1)查询可用GPU的数量
torch.cuda.device_count()定义函数try_gpu如果申请的GPU存在就返回GPU(i)不存在就使用CPU
def try_gpu(i0): if torch.cuda.device_count() i 1:return torch.device(fcuda:{i})return torch.device(cpu)定义函数try_all_gpus返回所有可用的GPU如果没有GPU则返回[cpu(),]
def try_all_gpus(): devices [torch.device(fcuda:{i})for i in range(torch.cuda.device_count())]return devices if devices else [torch.device(cpu)]try_gpu(), try_gpu(10), try_all_gpus()二张量与GPU
可以查询张量所在的设备。 默认情况下张量是在CPU上创建的
x torch.tensor([1, 2, 3])
x.device
#返回device(typecpu)将张量存储在GPU上
X torch.ones(2, 3, devicetry_gpu())
X
Y torch.rand(2, 3, devicetry_gpu(1))
Y复制对多个项进行操作时不同项目必须在同一个设备上
Z X.cuda(1)
print(X)
print(Z)
#返回结果显示X在cuda0Z在cuda1三神经网络与GPU
神经网络模型可以指定设备。 下面的代码将模型参数放在GPU上。
net nn.Sequential(nn.Linear(3, 1))
net net.to(devicetry_gpu())