深度学习计算
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. 常见方法改变卷积层通道数
-
修改卷积核数量:卷积核的数量决定了卷积层的输出通道数。通过增加或减少卷积核的数量,可以改变卷积层的通道数。
-
添加或删除卷积层:通过添加或删除卷积层,可以改变卷积层的通道数。例如,可以添加一个新的卷积层来增加通道数,或删除一个卷积层来减少通道数。 #当卷积核的大小较小时,它只能捕捉到局部的特征,因此需要更多的卷积核来捕捉全局特征,从而需要更多的输出通道数
-
修改卷积核大小:卷积核的大小也可以影响卷积层的通道数。通过增加或减少卷积核的大小,可以改变卷积层的通道数。
-
添加或删除池化层:池化层可以用来降低卷积层的通道数。通过添加池化层,可以减少卷积层的通道数,反之亦然。 #跨通道池化是一种特殊的池化操作,它在每个通道内分别执行池化操作,并将每个通道的池化结果拼接在一起,从而形成新的通道
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是一种广泛使用的高性能机器学习算法,特别适合对结构化数据进行分类和回归任务。但其缺点也不能忽视,需要在具体应用中权衡考虑
标签:nn,卷积,梯度,self,动手,pytorch,参数,深度,通道 From: https://www.cnblogs.com/dwletsgo/p/17304454.html