动手深度学习pytorch 5-7章

发布时间 2023-04-23 21:01:52作者: dunimaa

深度学习计算

1. 块提供的基本功能:

  1. 输入数据作为前向传播函数的参数

  2. 通过前向传播函数生成输出

  3. 计算其输出关于输入的梯度

  4.存储和访问前向传播计算所需的参数

  5. 根据需要初始化模型参数

2. Sequential 类

  1. 将块逐个追加到列表中的函数

  2. 前向传播函数,用于将输入按追加块的顺序传递给块组成的链条

for idx, module in enumerate(args):

  self._modules[str(idx)] = module

  Python中类的初始化方法(init)中的一部分。它用于将一个或多个PyTorch模块(module)添加到一个自定义的神经网络模型中。

具体来说,它遍历了参数列表args中的每个模块,并使用Python内置的enumerate()函数为每个模块生成一个索引idx。然后,它使用PyTorch中的_modules属性,将每个模块添加到自定义模型的模块字典中,其中模块的键为索引idx的字符串表示形式。_modules属性是PyTorch中nn.Module类的一个属性。在模块的参数初始化过程中,系统知道在_modules字典中查找需要初始化参数的⼦块。

    def forward(self, X):
        X = self.linear(X) #线性变换得到中间张量
        # 矩阵乘法和加法运算,ReLU激活函数对其进行非线性变换
        X = F.relu(torch.mm(X, self.rand_weight) + 1)
        # 复⽤全连接层。这相当于两个全连接层共享参数
        X = self.linear(X)
        # 归一化,除以2直到绝对值之和小于
        while X.abs().sum() > 1:
            X /= 2
        return X.sum()

3. python 全局解释器锁

  (Global Interpreter Lock,GIL)是一种Python解释器的实现细节,它可以确保同一时刻只有一个线程在执行Python代码。这个锁的存在是为了保证线程安全,避免多个线程同时修改同一份内存数据时出现竞争和冲突。

由于GIL的存在,Python中的多线程编程通常会受到一些限制。例如,Python中的多线程并不能真正的并行执行,因为只有一个线程可以获取GIL并执行Python代码。因此,Python中的多线程通常只适用于I/O密集型任务,而不适用于计算密集型任务。需要注意的是,GIL只是Python解释器的一种实现细节,不是Python语言本身的特性。因此,一些Python解释器的替代品,例如Jython、IronPython和PyPy等,都采用了不同的方式来处理线程安全和并发性。

4. 参数管理

网络参数访问

print(*[(name, param.shape) for name, param in net.named_parameters()])

print(net.state_dict()['2.bias'].data)

自定义初始化参数

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

#调用自定义初始化

class MyModel(nn.Module):
  def __init__(self):
    super(MyModel, self).__init__()
    self.fc1 = nn.Linear(10, 20)
    self.fc2 = nn.Linear(20, 30)
    self.fc3 = nn.Linear(30, 2)    

    self.apply(my_init) # 调用my_init函数对模型进行初始化

  def forward(self, x):
    x = nn.functional.relu(self.fc1(x))
    x = nn.functional.relu(self.fc2(x))
    x = self.fc3(x)
    return x

参数绑定/共享参数

  参数绑定时,梯度会发⽣什么情况:由于模型参数包含梯度,因此在反向传播期间第⼆个隐藏层(即第三个神经⽹络层)和第三个隐藏层(即第五个神经⽹络层)的梯度会加在⼀起

延后初始化:torch.nn.LazyLinear

自定义层:设计⼀个返回输⼊数据的傅⽴叶系数前半部分的层

import torch
import torch.nn as nn

class FourierCoefficientLayer(nn.Module):
    def __init__(self, n): #n 傅里叶系数的个数
        super(FourierCoefficientLayer, self).__init__() #super(class, self)的写法
        self.n = n
        self.register_buffer('k', torch.arange(n).float().view(1, -1)) #注册一个(1,n)的张量k,存储傅里叶系数下标

    def forward(self, x):
        # 计算傅里叶系数
        kx = 2 * torch.pi * self.k * x.unsqueeze(-1) / self.n  # B x L x n
        cos_kx = torch.cos(kx)
        sin_kx = torch.sin(kx)
        real = cos_kx.sum(dim=1, keepdim=True) / self.n  # B x 1 x n
        imag = sin_kx.sum(dim=1, keepdim=True) / self.n  # B x 1 x n
        complex = torch.cat([real, imag], dim=1)  # B x 2 x n
        return complex

 5. 互相关和卷积

  区别在于卷积的其中一个序列需要先翻转再滑动相乘

  学习卷积核时,无论用严格卷积运算还是互相关运算,卷积层的输出不会受太大影响

#卷积神经网络训练过程
Y_hat = conv2d(X)
l = (Y_hat - Y) ** 2
conv2d.zero_grad()
l.sum().backward()
# 迭代卷积核
conv2d.weight.data[:] -= lr * conv2d.weight.grad

l.sum()表示对损失函数l中所有元素进行求和,得到一个标量值,这个标量值是一个关于卷积核权重的函数。
通过对这个函数进行求导,可以得到损失函数对卷积核权重的梯度,从而用于更新卷积核的权重。 反向传播,计算出损失函数对卷积核权重的梯度。这个梯度可以通过conv2d.weight.grad来获取 梯度下降法:根据梯度下降的方向和步长更新参数值。
#矩阵乘法计算互相关运算
def conv2d_by_mul(X, K):
    h, w = K.shape
    outh = X.shape[0] - h + 1
    outw = X.shape[1] - w + 1
    K = K.reshape(-1, 1) # 展开成一个(h*w)*1 的列向量,参数 -1 表示根据 K 矩阵的大小自动计算相应的维度,
保证展平后的列向量大小与 K 矩阵中的元素数量相同 Y
= [] for i in range(outh): for j in range(outw): Y.append(X[i:i + h, j:j + w].reshape(-1)) #遍历X,将每个h*w子矩阵展开为一个大小为(h*w)*1的列向量 Y = torch.stack(Y, 0) #将Y列表的所有列向量拼接成一个大小为 (N1​​h+1)×(N2​​w+1)×(h×w) 的三维矩阵 res = (torch.matmul(Y, K)).reshape(outh, outw) return res

1*1 卷积层通常用于调整网络层的通道数量和控制模型复杂性

 eg:MobileNet,GoogLeNet,ResNet,YOLO

理解通道数变化:卷积神经网络中,通道数是指卷积层输出的特征图数目,而每个特征图是由一组卷积核对输入图像进行卷积得到的。

  通道数就是使用的卷积核的个数。

6. 汇聚层

  具有双重⽬的:降低卷积层对位置的敏感性,同时降低对空间降采样表⽰的敏感性

  汇聚层的输出通道数与输⼊通道数相同,在每个输入通道上单独运算

7. 现代卷积神经网络

  2012前,SIFT,SURF,HOG,bags of visual words和类似特征提取方法占据主导地位

  2012年后,数据,硬件的推动,卷积和矩阵乘法都是可以在硬件上并行化的操作

8. 常见方法改变卷积层通道数 

  1. 修改卷积核数量:卷积核的数量决定了卷积层的输出通道数。通过增加或减少卷积核的数量,可以改变卷积层的通道数。

  2. 添加或删除卷积层:通过添加或删除卷积层,可以改变卷积层的通道数。例如,可以添加一个新的卷积层来增加通道数,或删除一个卷积层来减少通道数。 #当卷积核的大小较小时,它只能捕捉到局部的特征,因此需要更多的卷积核来捕捉全局特征,从而需要更多的输出通道数

  3. 修改卷积核大小:卷积核的大小也可以影响卷积层的通道数。通过增加或减少卷积核的大小,可以改变卷积层的通道数。

  4. 添加或删除池化层:池化层可以用来降低卷积层的通道数。通过添加池化层,可以减少卷积层的通道数,反之亦然。 #跨通道池化是一种特殊的池化操作,它在每个通道内分别执行池化操作,并将每个通道的池化结果拼接在一起,从而形成新的通道

9. 批量归一化层BN

  解决当输入数据的分布发生变化后,如何减少对深层网络结构的影响(参数的影响)

  批量归一化固定小批量中的均值方差,然后学习出适合的偏移和缩放。可以加速收敛速度但一般不改变模型精度。

  可学习的参数为y和B
  作用在全连接层和卷积层输出上,激活函数前全连接层和卷积层输入上。对全连接层,作用在特征维;对于卷积层,作用在通道维

  最初论文想哟过来减少内部协变量转移,后续指出可能通过在每个小批量里加入噪音来控制模型复杂度,因此没必要跟dropout丢弃法混合使用。

10. “bottleneck”架构 

  1. ResNet V1 - 原始的ResNet结构,使用3x3卷积核和stride为2的maxpool层来进行下采样。

  2. ResNet V2 - 修改了V1结构,使用stride为2的3x3卷积核替代maxpool层进行下采样,以避免信息损失。

  3. Pre-activation ResNet - 在 residual block的输入端和输出端各添加一个激活函数,而不是在两层卷积之间添加激活函数。这可以让梯度在shortcut路径上更好地反向传播,有助于模型的优化。

  4. Wide ResNet - 通过增加residual block中的卷积过滤器数量,来构建更宽更深的网络结构。这可以让梯度更好地反向传播到更深层,并且有更强的特征表达能力。

  5. ResNeXt - 采用“cardinality”(基数)的概念,利用更多的小型卷积核代替标准的大型卷积核,这往往需要更少的参数但具有更强的表达能力。

ResNet中的“bottleneck”架构指的是,在residual block内使用1x1卷积核进行降维、3x3卷积核进行特征提取,再使用1x1卷积核进行升维的设计。这种设计的好处是:  

  1. 1x1卷积用于降维可以减少3x3卷积的参数量和计算量,从而降低模型复杂度。

  2. 低维特征空间的3x3卷积能更高效地提取特征。

  3. 1x1卷积用于升维可以将特征 maps 恢复到原始尺寸,计算残差并添加到输入信号中。

所以,“bottleneck”架构通过维度变换使得ResNet能够更有效地进行特征提取与残差学习,这在一定程度上减小了模型复杂度,是ResNet设计的重要组成部分。其他变体如ResNet V2、Pre-activation等则在此基础上进行了优化,以改进ResNet的性能。

11. GBDT (Gradient Boosting Decision Tree)

  它是一种集成学习方法,通过迭代构建一系列的弱学习器(如决策树),并通过提升的方式来提高学习器的预测性能。

  GBDT的主要思想是:

  1. 初始时使用全部样本训练一个决策树,得到初步的预测结果。

  2. 计算初步预测结果与真实标签之间的损失(一般使用均方误差),并根据损失量计算每个样本的梯度。

  3. 使用梯度作为再次训练的样本权重,高梯度的样本权重更大,低梯度的样本权重更小。

  4. 基于新的训练样本及其权重,构建第二棵决策树,并对初步预测结果进行提升(通常是增加)。

  5. 重复第2-4步,逐步提高学习器的预测性能,直至损失不再减小或达到预设的最大迭代次数。

所以,GBDT通过不断提升弱学习器(决策树)的预测性能,来获得一个很强的分类器或回归器。它把多棵树的预测结果合成一个最终的预测结果,

这种做法俗称“森林”。GBDT实际上是一个”森林“模型。GBDT的主要优点是:1. 可用于分类和回归任务。2. 可以处理各种类型的数据,包括数值型

和标称型等。3. 易于理解和解释。决策树是一种可视化的机器学习模型。4. 可以处理missing values。决策树在训练过程中可以自动处理缺失的值。

5. 性能高效而稳定。

GBDT是一个ensemble模型,可以取得很高的预测性能。GBDT的主要缺点是:1. 过度依赖参数调整。GBDT有许多超参数需要调整,这增加了模型开发难度。2. 记忆能力差。GBDT更适用于静态的数据集,对动态变化的数据集的适应能力较差。3. 可解释性差。虽然单个决策树还易于解释,但是多个决策树组成的”森林“就比较难以解释了。所以,GBDT是一种广泛使用的高性能机器学习算法,特别适合对结构化数据进行分类和回归任务。但其缺点也不能忽视,需要在具体应用中权衡考虑