动手学深度学习(pytorch)学习记录19-参数管理[学习记录]

2024-08-29 00:52

本文主要是介绍动手学深度学习(pytorch)学习记录19-参数管理[学习记录],希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 参数访问
    • 目标参数
    • 一次性访问所有参数
    • 从嵌套块收集参数
  • 参数初始化
    • 内置初始化
    • 自定义初始化
  • 参数绑定
  • 延后初始化

本节内容:
访问参数,用于调试、诊断和可视化;
参数初始化;
在不同模型组件间共享参数;
延后初始化。

# 单隐藏层的多层感知机
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())
OrderedDict([('weight', tensor([[ 0.0143, -0.1299, -0.0290,  0.2700,  0.2086, -0.3533,  0.0657,  0.2856]])), ('bias', tensor([-0.1287]))])

通过输出可知:全连接层包含两个参数,分别是该层的权重和偏置。
每个参数都表示为参数类的一个实例。 要对参数执行任何操作,首先我们需要访问底层的数值。

目标参数

print(type(net[2].bias))
print(net[2].bias)
print(net[2].bias.data)
<class 'torch.nn.parameter.Parameter'>
Parameter containing:
tensor([-0.1287], requires_grad=True)
tensor([-0.1287])

参数是复合的对象,包含值、梯度和额外信息。
在上面这个网络中,由于我没有调用反向传播,所以参数的梯度处于初始状态。

net[2].weight.grad == None
True

一次性访问所有参数

当我们需要对所有参数执行操作时,逐个访问它们可能会很麻烦。 当我们处理更复杂的块(例如,嵌套块)时,情况可能会变得特别复杂, 因为我们需要递归整个树来提取每个子块的参数。 下面,演示来访问第一个全连接层的参数和访问所有层。

print(*[(name, param.shape) for name, param in net[0].named_parameters()]) # *应该是用于解包操作,对比有无*,发现删去*后,输出或多一个[]
print(*[(name, param.shape) for name, param in net.named_parameters()])
('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]))

另一种访问网络参数的方法

net.state_dict()['2.bias'].data
tensor([-0.1287])

从嵌套块收集参数

def block1():return nn.Sequential(nn.Linear(4, 8), nn.ReLU(),nn.Linear(8, 4), nn.ReLU())def block2():net = nn.Sequential()for i in range(4):# 在这里嵌套net.add_module(f'block {i}', block1())
# 用 add_module 方法将 block1 函数返回的 nn.Sequential 对象添加到 net 容器中。每次迭代都会添加一个新的模块,并且每个模块都有一个唯一的名称(例如 'block 0', 'block 1', ...)return netrgnet = nn.Sequential(block2(), nn.Linear(4, 1))
rgnet(X)
tensor([[0.1559],[0.1559]], grad_fn=<AddmmBackward0>)

查看一下网络结构

print(rgnet)
Sequential((0): Sequential((block 0): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU())(block 1): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU())(block 2): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU())(block 3): Sequential((0): Linear(in_features=4, out_features=8, bias=True)(1): ReLU()(2): Linear(in_features=8, out_features=4, bias=True)(3): ReLU()))(1): Linear(in_features=4, out_features=1, bias=True)
)

因为层是分层嵌套的,所以也可以像通过嵌套列表索引一样访问它们。 下面,访问第一个主要的块中、第二个子块的第一层的偏置项。

rgnet[0][1][0].bias.data
tensor([-0.4003,  0.3388,  0.2142,  0.3416, -0.0377,  0.3460, -0.1539,  0.0325])

参数初始化

PyTorch的nn.init模块提供了多种预置初始化方法。

内置初始化

首先调用内置的初始化器。 下面的代码将所有权重参数初始化为标准差为0.01的高斯随机变量, 且将偏置参数设置为0。

def init_normal(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, mean=0, std=0.01)nn.init.zeros_(m.bias)
net.apply(init_normal)
net[0].weight.data[0], net[0].bias.data[0]
(tensor([0.0106, 0.0016, 0.0035, 0.0076]), tensor(0.))

还可以将所有参数初始化为给定的常数,比如初始化为6

def init_constant(m):if type(m) == nn.Linear:nn.init.constant_(m.weight, 6)nn.init.zeros_(m.bias)
net.apply(init_constant)
net[0].weight.data[0], net[0].bias.data[0]
(tensor([6., 6., 6., 6.]), tensor(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.5471,  0.4637, -0.2951,  0.1913])
tensor([[42., 42., 42., 42., 42., 42., 42., 42.]])

自定义初始化

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() >= 5# 将所有权重的绝对值小于5的权重设置为0,而保持权重的绝对值大于或等于5的权重不变net.apply(my_init)
net[0].weight[:2]
Init weight torch.Size([8, 4])
Init weight torch.Size([1, 8])tensor([[ 5.1561, -0.0000,  5.4036,  5.2387],[-7.6918,  9.9444,  0.0000, -9.6561]], grad_fn=<SliceBackward0>)

参数绑定

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

# 我们需要给共享层一个名称,以便可以引用它的参数
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])

这个例子表明第三个和第五个神经网络层的参数是绑定的。 它们不仅值相等,而且由相同的张量表示。 因此,如果我们改变其中一个参数,另一个参数也会改变。

延后初始化

我们定义了网络架构,但没有指定输入维度。
我们添加层时没有指定前一层的输出维度。
我们在初始化参数时,甚至没有足够的信息来确定模型应该包含多少参数。
这里的诀窍是框架的延后初始化(defers initialization), 即直到数据第一次通过模型传递时,框架才会动态地推断出每个层的大小。

import torch
from torch import nn
from d2l import torch as d2l
net = nn.Sequential(nn.LazyLinear(256), nn.ReLU(), nn.LazyLinear(10))
C:\anaconda3\envs\pytorch\lib\site-packages\torch\nn\modules\lazy.py:181: UserWarning: Lazy modules are a new feature under heavy development so changes to the API or functionality can happen at any moment.warnings.warn('Lazy modules are a new feature under heavy development '

网络络还不知道输入层权重的维度,因为输入维度仍然未知。

net[0].weight
<UninitializedParameter>

通过网络传递数据后,框架最终初始化参数。
一旦知道输入维度20,框架就可以通过插入20的值来识别第一层权重矩阵的形状。识别出第一层的形状后,框架继续到第二层,通过计算图依次进行,直到所有形状都已知。请注意,在这种情况下,只有第一层需要延迟初始化,但框架会顺序初始化。一旦已知所有参数形状,框架最终就可以初始化参数。

X = torch.rand(2, 20)
net(X)net[0].weight.shape
torch.Size([256, 20])

以下方法通过网络传递伪输入进行试运行,以推断所有参数形状并随后初始化参数。稍后当不需要默认随机初始化时将使用它。

@d2l.add_to_class(d2l.Module)  #@save
def apply_init(self, inputs, init=None):self.forward(*inputs)if init is not None:self.net.apply(init)
AttributeError: module 'd2l.torch' has no attribute 'add_to_class'

封面图片来源

欢迎点击我的主页查看更多文章。
本人学习地址https://zh-v2.d2l.ai/
恳请大佬批评指正。

这篇关于动手学深度学习(pytorch)学习记录19-参数管理[学习记录]的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1116376

相关文章

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

在Node.js中使用.env文件管理环境变量的全过程

《在Node.js中使用.env文件管理环境变量的全过程》Node.js应用程序通常依赖于环境变量来管理敏感信息或配置设置,.env文件已经成为一种流行的本地管理这些变量的方法,本文将探讨.env文件... 目录引言为什么使php用 .env 文件 ?如何在 Node.js 中使用 .env 文件最佳实践引

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

docker编写java的jar完整步骤记录

《docker编写java的jar完整步骤记录》在平常的开发工作中,我们经常需要部署项目,开发测试完成后,最关键的一步就是部署,:本文主要介绍docker编写java的jar的相关资料,文中通过代... 目录all-docker/生成Docker打包部署文件配置服务A的Dockerfile (a/Docke

python库pydantic数据验证和设置管理库的用途

《python库pydantic数据验证和设置管理库的用途》pydantic是一个用于数据验证和设置管理的Python库,它主要利用Python类型注解来定义数据模型的结构和验证规则,本文给大家介绍p... 目录主要特点和用途:Field数值验证参数总结pydantic 是一个让你能够 confidentl

MySQL使用EXISTS检查记录是否存在的详细过程

《MySQL使用EXISTS检查记录是否存在的详细过程》EXISTS是SQL中用于检查子查询是否返回至少一条记录的运算符,它通常用于测试是否存在满足特定条件的记录,从而在主查询中进行相应操作,本文给大... 目录基本语法示例数据库和表结构1. 使用 EXISTS 在 SELECT 语句中2. 使用 EXIS