动手学深度学习——深度学习计算
层和块
为了实现这些复杂的网络,我们引入了神经网络块的概念。 块(block)可以描述单个层、由多个层组成的组件或整个模型本身。
从编程的角度来看,块由类(class)表示。 它的任何子类都必须定义一个将其输入转换为输出的前向传播函数, 并且必须存储任何必需的参数。
于是我们有下面的类定义:
import torch from torch import nn from torch.nn import functional as F class MySequential(nn.Module): def __init__(self, *args): super().__init__() for idx, module in enumerate(args): # 这里,module是Module子类的一个实例。我们把它保存在'Module'类的成员 # 变量_modules中。_module的类型是OrderedDict self._modules[str(idx)] = module def forward(self, X): # OrderedDict保证了按照成员添加的顺序遍历它们 for block in self._modules.values(): X = block(X) return X
nn.Module是父类
_modules的主要优点是: 在模块的参数初始化过程中, 系统知道在_modules字典中查找需要初始化参数的子块。
还有 net.add_module()函数 用法就是向_module中加入module
如上我们就定义了一个块,这个块中包含对参数的组织和初始化__init__函数,以及进行计算的前向传播函数forward函数
我们可以用nn.Sequential与自定义的块进行结果
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) # 使用创建的常量参数以及relu和mm函数 X = F.relu(torch.mm(X, self.rand_weight) + 1) # 复用全连接层。这相当于两个全连接层共享参数 X = self.linear(X) # 控制流 while X.abs().sum() > 1: X /= 2 return X.sum() 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)
参数管理
访问模型参数
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1)) # net[n]表示的是Sequential中的第n个参数(从0开始计数) # .state_dict()展现的是一个包含w,b的字典 print(net[2].state_dict()) # 通过.bias查看偏差b,.weight查看权重w print(type(net[2].bias)) print(net[2].bias) print(net[2].bias.data) print(net[2].weight.data) # 同时进一步.grad查看梯度 net[2].weight.grad == None # 查看全部层的信息 print(*[(name, param.shape) for name, param in net.named_parameters()]) # 查看模型是如何工作的 print(net)
初始化模型参数
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) # nn.init.normal_是正态,nn.init.zero_是置零 # 还有nn.init.xavier_uniform_(m.weight),这个是xavier初始化,在多层感知机中避免过拟合的时候我们讲过 # nn.init.constant_(m.weight, 1),这个是置常数 # 直接访问初始化 net[0].weight.data[:] += 1 net[0].weight.data[0, 0] = 42 net[0].weight.data[0]
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 net.apply(my_init) # 上述 nn.init.uniform_(m.weight, -10, 10)是均匀分布初始化
上面均匀分布实现的是如上图函数
参数绑定
有时我们希望在多个层间共享参数:
我们可以定义一个稠密层,然后使用它的参数来设置另一个层的参数。
# 我们需要给共享层一个名称,以便可以引用它的参数 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])
这个例子表明第三个和第五个神经网络层的参数是绑定的。
它们不仅值相等,而且由相同的张量表示。
因此,如果我们改变其中一个参数,另一个参数也会改变。
这里有一个问题:当参数绑定时,梯度会发生什么情况?
答案是由于模型参数包含梯度,因此在反向传播期间第二个隐藏层
(即第三个神经网络层)和第三个隐藏层(即第五个神经网络层)的梯度会加在一起。
加载和保存参数
# 将X保存到文件x-file中 torch.save(X, 'x-file') # 除了上述可以保存X外,还可以保存列表,字典等 torch.save([x, y],'x-files') mydict = {'x': x, 'y': y} torch.save(mydict, 'mydict') # 相应地我们可以从我们保存的文件中读取 X2 = torch.load('x-file') x2, y2 = torch.load('x-files') mydict2 = torch.load('mydict') # clone是我们一个网络,可以看成是nn的一个子类吧 # .load是加载参数出来,_state_dict是加载出字典,将key与网络clone的参数对应起来 # 通过下面的方法我们可以保存我们训练的时候的参数,并取出来 torch.save(net.state_dict(), 'mlp.params') clone.load_state_dict(torch.load('mlp.params'))
结果与问题
损失率抖动很大可能是:
- 样本特征丰富,可以通过增加训练时的样本个数缓解
- 学习率低了
本文作者:次林梦叶
本文链接:https://www.cnblogs.com/cilinmengye/p/17758056.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!