动手学深度学习(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内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

在Spring Boot中集成RabbitMQ的实战记录

《在SpringBoot中集成RabbitMQ的实战记录》本文介绍SpringBoot集成RabbitMQ的步骤,涵盖配置连接、消息发送与接收,并对比两种定义Exchange与队列的方式:手动声明(... 目录前言准备工作1. 安装 RabbitMQ2. 消息发送者(Producer)配置1. 创建 Spr

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

k8s上运行的mysql、mariadb数据库的备份记录(支持x86和arm两种架构)

《k8s上运行的mysql、mariadb数据库的备份记录(支持x86和arm两种架构)》本文记录在K8s上运行的MySQL/MariaDB备份方案,通过工具容器执行mysqldump,结合定时任务实... 目录前言一、获取需要备份的数据库的信息二、备份步骤1.准备工作(X86)1.准备工作(arm)2.手

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项