《动手学深度学习》5.3~5.5

目录

一、延后初始化

1.1实例化网络

二、自定义层

2.1不带参数的层

2.2带参数的层

三、读写文件

3.1加载和保存张量

3.2加载保存模型参数


一、延后初始化

在学习前面章节的时候,不知道你们有没有这种感觉,这些张量的形状自己总是搞不明白。作者一开始是这样的,后来代码写多了才渐渐理解的。

可在遇到这些东西的时候,还是要稍微停下来时间思考一下。

那是因为,到目前为止,我们忽略了建立网络时需要做的以下这些事情:

  • 我们定义了网络架构,但没有指定输入维度。

  • 我们添加层时没有指定前一层的输出维度。

  • 我们在初始化参数时,甚至没有足够的信息来确定模型应该包含多少参数

既然提出来问题了,那肯定是有解决办法的。这个问题的解决办法就是:框架的延后初始化

有了该技术将更加方便。 现在我们在编写代码时无须知道维度是什么就可以设置参数, 这种能力可以大大简化定义和修改模型的任务。

1.1实例化网络

实例化一个感知机:

python 复制代码
import torch
import torch.nn as nn

net = nn.Sequential(nn.LazyLinear(256),
                    nn.ReLU(),
                    nn.LazyLinear(10))
print(net[0].weight)
print(net)

这里的LazyLinear()就是延后初始化,我们不知道输入,只知道输出。

运行结果:

<UninitializedParameter>

Sequential(

(0): LazyLinear(in_features=0 , out_features=256, bias=True)

(1): ReLU()

(2): LazyLinear(in_features=0 , out_features=10, bias=True)

)

我们给这个网络一个特征值。

python 复制代码
X = torch.rand(2,20)
net(X)
print(net[0].weight)
print(net)

运行结果:

Parameter containing:

tensor(\[-0.1142, -0.1500, -0.2170, ..., -0.0608, -0.0959, 0.0969,

-0.1418, 0.1375, 0.2137, ..., 0.1531, -0.0622, 0.1335,

-0.1582, -0.0558, 0.1994, ..., -0.0048, -0.0288, -0.1821,

...,

0.1771, -0.0401, 0.1677, ..., 0.1472, 0.1971, -0.1043,

-0.0685, -0.1094, -0.0304, ..., 0.2133, -0.2186, -0.1285,

0.1542, 0.0978, 0.0081, ..., -0.1878, -0.1788, 0.1976],

requires_grad=True)

Sequential(

(0): Linear(in_features=20 , out_features=256, bias=True)

(1): ReLU()

(2): Linear(in_features=256 , out_features=10, bias=True)

)

可以看到,发生了变化,这就是pytorch的延后初始化。

二、自定义层

我们可以用创造性的方式组合不同的层,从而设计出适用于各种任务的架构。 但还是那个问题,再厉害的框架,也不可能包揽所有种类的层。

这个时候,就需要我们手搓一个我们专用的层,也就是自定义层。

2.1不带参数的层

这是一个把输入减去输入的均值的层,虽然可能在实际中没有任何意义,但是这里的重点在于,PyTorch框架里面没有。

python 复制代码
import torch
import torch.nn.functional as F
from torch import nn

class CenteredLayer(nn.Module):
    def __init__(self):
        super().init__()

    def forward(self, X):
        return X - X.mean()

让我们验证它是否能按预期工作。

python 复制代码
layer = CenteredLayer()
print(layer(torch.FloatTensor([1, 2, 3, 4, 5])))

运行结果:

tensor(-2., -1., 0., 1., 2.)

我们可以像5.1节那样,把它合并到更复杂的模型中,但在这里就不掩饰了。

2.2带参数的层

回想一下我们之前学过的那些全连接层,需要两个参数,一个用于表示权重,另一个用于表示偏置项。该层需要输入参数:in_unitsunits,分别表示输入数和输出数。

python 复制代码
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.data
        return F.relu(linear)
    
linear = MyLinear(5, 3)
print(linear.weight)

print(linear(torch.rand(2, 5)))#向前传播

matmul()是矩阵乘法。

我们使用修正线性单元(ReLU)作为激活函数。

除此之外,应该没有什么需要解释的地方了。

运行结果:

tensor(\[-1.2881, -1.2804, 0.7337,

-0.5448, -1.2296, -0.8849,

-0.0550, -0.0579, 0.2759,

-0.2484, -1.8061, 0.4572,

0.2487, -0.8685, 0.1223], requires_grad=True)

tensor(\[0.5101, 2.0328, 2.0510,

0.8924, 0.0000, 0.0000])

三、读写文件

目前为止,我们讨论了如何处理数据, 以及如何构建、训练和测试深度学习模型。

但是,我们目前训练的模型,都存储在内存中。程序运行停止之后就没有了。

我们目前手搓的模型体量都很小,但是在训练FashionMINIST的时候,已经感觉有些吃力了。那些数以亿万计的模型消耗的时间成本更是不知多少。

所以,现在是时候学习如何加载和存储权重向量和整个模型了。

3.1加载和保存张量

对于张量,我们使用load和save方法进行加载和保存。

单个张量的保存和加载:

python 复制代码
import torch
from torch import nn
from torch.nn import functional as F

x = torch.arange(4)
torch.save(x, 'x-file')#保存
print(torch.load('x-file'))#加载

运行结果:

tensor(0, 1, 2, 3)

张量列表也是用这个方法:

python 复制代码
y = torch.zeros(4)
torch.save([x,y],'x-files')
print(torch.load('x-files'))

运行结果:

tensor(\[0, 1, 2, 3\]), tensor(\[0., 0., 0., 0.\])

我们还可以写入或读取从字符串映射到张量的字典 。 当我们要读取或写入模型中的所有权重时,这很方便。

python 复制代码
mydict = {'x': x, 'y': y}
torch.save(mydict, 'mydict')
mydict2 = torch.load('mydict')
print(mydict2)

运行结果:

{'x': tensor(0, 1, 2, 3), 'y': tensor(0., 0., 0., 0.)}

3.2加载保存模型参数

如果我们想要保存整个模型,只靠上面学到的是不够的,因为模型可能有数百个参数散布在各处。

于是,深度学习框架提供了内置函数来保存和加载整个网络。

但要注意,这将保存模型的参数而不是保存整个模型。

因为模型本身可以包含任意代码,所以模型本身难以序列化。为了恢复模型,我们需要用代码生成架构, 然后从磁盘加载参数。

为了理解上面的话,我们来创建一个多层感知机演示一下。

python 复制代码
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')#保存参数

这个是我们常用的单个隐藏层,隐藏层经过relu函数激活的多层感知机。

我们调用save方法,通过**.state_dict()**获取参数字典,保存在mlp.params参数中。

python 复制代码
clone = MLP()
clone.load_state_dict(torch.load('mlp.params'))
clone.eval()
print(clone)

现在我们重新创建一个同类型的多层感知机clone,然后让他加载保存的参数。

运行结果:

MLP(

(hidden): Linear(in_features=20, out_features=256, bias=True)

(output): Linear(in_features=256, out_features=10, bias=True)

)

两个实例具有相同的模型参数,在输入相同的X时, 两个实例的计算结果应该相同。 让我们来验证一下。

python 复制代码
Y_clone = clone(X)
print(Y_clone == Y)

运行结果:

tensor(\[True, True, True, True, True, True, True, True, True, True,

True, True, True, True, True, True, True, True, True, True])

相关推荐
Drgfd6 分钟前
智造赋能品控:汪进进以精益生产,夯实质量制造底座
人工智能·制造
米小虾16 分钟前
"Chat is dead":OpenAI 正在杀死的不是聊天,是整个 AI 交互范式
人工智能·openai
冬奇Lab20 分钟前
Agent 系列(18):成本与性能优化——省钱且更快
人工智能·llm·agent
Hefei GlobefishAI33 分钟前
合肥合豚AI硬件方案:专为智能售货柜厂商定制的无人零售接口套件
人工智能·零售·自动售货机·无人零售硬件·ai硬件方案·智能售货柜·接口套件
冬奇Lab39 分钟前
每日一个开源项目(第127篇):PM Skills Marketplace - 把顶级产品方法论塞进 AI Agent
人工智能·开源·资讯
吴佳浩1 小时前
Hermes vs OpenClaw:基于源码的 Agent Loop 全面分析
人工智能·llm·agent
AI袋鼠帝1 小时前
腾讯出手了!彻底入局企业级Agent。
人工智能
和平宇宙1 小时前
AI笔记005. hermes-DeepSeek V4 Pro, 128K上下文引发的探索
前端·人工智能·笔记
56AI1 小时前
360 智语 AI 企业智能体平台深度评测:从 L4 蜂群架构到政企落地实战
人工智能·架构
IT_陈寒1 小时前
Redis持久化这个坑,我爬了一整天才出来
前端·人工智能·后端