在torch中 微分相当于高数里的求导。
在高等数学中,求导是计算一个函数在某一点处的变化率,表示该点附近的函数值对自变量的微小变化的响应。而在 Torch 中,微分操作也是计算张量函数在某一点处的变化率,即计算函数对输入张量的偏导数。
Torch 提供了丰富的自动微分功能,可以通过 backward() 方法自动计算张量的梯度,并且可以进行高阶导数计算。这使得在深度学习中能够方便地进行反向传播算法及参数优化。
因此,在使用 Torch 进行深度学习任务时,可以使用微分来实现类似于高等数学中求导的操作。
在高等数学中,微分可以表示为 dy = f'(x) dx,其中 f'(x) 表示函数 f(x) 的导数。
在 Torch 中,我们也可以使用类似的表示方式来计算微分。假设有一个函数 y = f(x),我们可以使用 Torch 的自动微分功能来计算函数在某一点 x 处的微分。
具体而言,我们可以通过以下步骤来实现:
- 定义变量 x,并设置其 requires_grad=True,以告知 Torch 需要跟踪对 x 的梯度。
- 定义函数 y = f(x)。
- 调用 y.backward() 实现自动微分,并计算出 dy/dx 的值。
- 获取 x.grad,即可得到函数 f(x) 在 x 处的导数值。
总结起来,Torch 中的微分操作与高等数学中求导的思想是相似的。你可以通过定义变量、设置 requires_grad、调用 backward() 和获取梯度值等步骤来实现类似于高数中求导的过程。
Torch 中的微分操作可以看作是在模拟高等数学中的微分过程。它使用了类似的符号表示和计算方法来计算函数的导数。
虽然 Torch 中的微分操作是通过计算张量的梯度来实现的,与传统的数学公式略有不同,但其思想和目的都是相同的:计算函数在某一点处的变化率或导数值,进而在深度学习中进行反向传播和参数优化等任务。
在 with torch.no_grad() 块内部的操作不会记录梯度信息,也不会进行参数的梯度计算。
下面是几个涵盖了with torch.no_grad()全部功能的例子,并对其进行解释:
1. 推断阶段中的前向传播计算:在神经网络的推断阶段,我们只需要进行前向传播计算,而不需要计算和追踪梯度。使用with torch.no_grad()可以将前向传播部分置于上下文环境中,就不会为在with torch.no_grad()内的操作构建计算图,只需要做好推断预测的功能就好,从而节省内存并提高计算效率。
with torch.no_grad():
output = model(input)
在这个例子中,通过将模型的前向传播计算包裹在with torch.no_grad()中,可以确保在进行推断时不会记录和跟踪梯度,从而提高计算效率。
2. 参数固定/冻结:有时候,在训练过程中我们可能想要固定或冻结某些参数,即它们的梯度不参与参数更新。此时,可以使用with torch.no_grad()来限制被冻结参数的梯度计算和更新。
for name, param in model.named_parameters():
if name in ['conv.weight', 'conv.bias']:
with torch.no_grad():
param.requires_grad = False
在这个例子中,通过将特定参数的梯度计算和更新置于with torch.no_grad()中,with torch.no_grad() 包裹了这个操作,确保在禁用梯度计算这段时间内,PyTorch不会记录这些操作的梯度信息,然后可以将这些参数的requires_grad属性设置为False,从而使得这些参数的梯度计算被禁用,不参与优化过程。简单地说,param.requires_grad = False 是用于设置参数的 requires_grad 属性为 False,从而禁用梯度计算。而 with torch.no_grad() 能够确保在某个代码块中禁用梯度计算,并优化计算效率。在冻结参数时,通常将这两者结合使用,以达到固定参数梯度的目的。
3. 减少内存消耗:在对大型模型进行推断时,为了减少内存占用,我们可以使用with torch.no_grad()来关闭梯度追踪和计算,从而释放内存资源。
with torch.no_grad():
output = model(input)
在这个例子中,在执行模型的前向传播计算过程中,通过使用with torch.no_grad()限制梯度追踪,可以避免存储梯度所需的额外内存开销,提高内存利用率。
这些例子展示了with torch.no_grad()的几个功能:关闭梯度追踪和计算、固定/冻结参数的梯度和减少内存消耗。根据具体需求,可以使用该上下文管理器来达到所需的效果。
不过关于with torch.no_grad()的固定/冻结参数的梯度这里,我觉得我还是需要理解的更好一些:
使用with torch.no_grad()可以在指定的上下文中禁用梯度计算,但它并不能替代param.requires_grad = False来禁用梯度计算。
with torch.no_grad()的作用是临时禁用梯度计算,使得在此上下文中的计算不会被记录梯度。这对于一些测试推断或者不需要梯度的计算操作非常有用。但是,它并不会影响模型参数的requires_grad属性本身。
而param.requires_grad = False是直接将参数的requires_grad属性设置为False,从而永久地禁用梯度计算。这种方式通常用于固定模型的某些参数,使其在训练过程中不参与参数更新,以起到冻结参数的效果。
所以,如果想要禁用梯度计算并且固定/冻结参数,需要使用param.requires_grad = False来设置参数的requires_grad属性。with torch.no_grad()只能用于临时情况下禁用梯度计算,并不能实现参数的固定/冻结。
请注意,在with torch.no_grad()中进行的计算操作将自动禁用梯度,因此不需要手动设置参数的requires_grad属性为False。
with torch.no_grad()的作用是临时禁用梯度计算,并且在此上下文中生成的计算不会被记录梯度。
在 PyTorch 中,计算图是用于自动求导的关键概念。当我们进行前向传播时,PyTorch会构建一个计算图来记录所有的操作和依赖关系,以便后续进行反向传播计算梯度。但是,当我们使用with torch.no_grad()包裹某个代码块时,PyTorch会临时禁用梯度计算,并且该代码块中生成的计算不会被记录到计算图中,因此也不会有对应的梯度信息。
所以,通过使用with torch.no_grad(),我们可以临时禁用梯度计算,从而提高运行效率,并且在这个上下文中的计算结果不会被纳入计算图,也不会产生梯度信息。
with torch.no_grad()是因为先禁止生成了计算图,所以导致了不会记录梯度。
将某个参数的 requires_grad 设置为 False 时,计算图中与该参数相关的部分将会被优化器忽略,并且在反向传播过程中不会对该参数计算梯度。
具体地说,当我们将某个参数的 requires_grad 设置为 False 后,该参数不仅不会更新,而且在反向传播时也不会计算它的梯度。因此,这个参数确实不会发生变化。
需要注意的是,计算图中的其它变量和参数仍然会参与反向传播,它们的梯度仍然会被计算和更新。只有那些 requires_grad 设置为 False 的参数不会被更新,也不会产生梯度。
- %matplotlib inline
- import torch
- from torch import nn
- from d2l import torch as d2l
- n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
- true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
- train_data = d2l.synthetic_data(true_w, true_b, n_train)
- train_iter = d2l.load_array(train_data, batch_size)
- test_data = d2l.synthetic_data(true_w, true_b, n_test)
- test_iter = d2l.load_array(test_data, batch_size, is_train=False)
- def train_concise(wd):
- net = nn.Sequential(nn.Linear(num_inputs, 1))
- for param in net.parameters():
- param.data.normal_()
- loss = nn.MSELoss(reduction='none')
- num_epochs, lr = 100, 0.003
- # 偏置参数没有衰减
- trainer = torch.optim.SGD([
- {"params":net[0].weight,'weight_decay': wd},
- {"params":net[0].bias}]
- , lr=lr)
- animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
- xlim=[5, num_epochs], legend=['train', 'test'])
- for epoch in range(num_epochs):
- for X, y in train_iter:
- trainer.zero_grad()
- l = loss(net(X), y)
- l.mean().backward()
- trainer.step()
- if (epoch + 1) % 5 == 0:
- animator.add(epoch + 1,
- (d2l.evaluate_loss(net, train_iter, loss),
- d2l.evaluate_loss(net, test_iter, loss)))
- print('w的L2范数:', net[0].weight.norm().item())
trainer = torch.optim.SGD([
{"params":net[0].weight,'weight_decay': wd},
{"params":net[0].bias}]
, lr=lr)
详解:
torch.optim.SGD函数接收一个参数列表作为优化器要更新的参数。在这个列表中,有两个元素:
- 第一个元素是net[0].weight,表示神经网络模型中的权重参数。
- 第二个元素是net[0].bias,表示神经网络模型中的偏置参数。
另外,对于 net[0].weight 参数使用了 weight_decay 参数,其值 weight_decay = wd,注意这里的wd是个需要设计者自己传入函数的权重衰减系数(超参数),相当于lambd * l2_penalty(w)里的lambd,而 "l2_penalty(w)" 则代表 L2 正则化项(权重衰减项),那么wd=0就等价于对net[0].bias的效果,即不进行权重衰减。
而对于偏置参数(net[0].bias)并没有指定 weight_decay 权重衰减。所以说,在给定的代码中,偏置参数(net[0].bias)没有进行权重衰减。
权重衰减是一种正则化技术,在损失函数中引入一个项,目的是通过乘以一个较小的权重衰减系数wd来惩罚大的权重值。这样可以降低模型的复杂度,并减少过拟合的风险。通常情况下,权重衰减应用于所有的参数,包括权重和偏置参数。但在给定的代码中,只对权重参数进行了权重衰减,而偏置参数没有进行相应的衰减操作。
启发式方法是一种基于经验和直觉的问题解决方法。它不依赖于严格的数学推导或确定性算法,而是根据一定的规则、经验或启示来做出决策。在深度神经网络的所有层上应用权重衰减就是一种简单的启发式方法。
p158练习
1. 在本节的估计问题中使⽤λ的值进⾏实验。绘制训练和测试精度关于λ的函数。观察到了什么?
答:通过绘制训练和测试精度关于λ的函数曲线,可以观察到不同λ值对模型性能的影响。
在实验中,您可以尝试使用不同的λ值(例如0.001、0.01、0.1等),并记录相应的训练和测试精度。随着λ值的增大,正则化项的影响将增加,这可能导致模型更倾向于拟合简单的模式,从而减少过拟合的风险。但是,如果λ值过大,模型可能会欠拟合,导致性能下降。
通过绘制训练和测试精度与λ的函数曲线,可以观察到在某个范围内,随着λ值的逐渐增加,测试精度可能先上升后下降,而训练精度可能持续下降。这是因为合适的λ值可以平衡模型的复杂度和拟合能力,达到较好的泛化性能。
请注意,具体的结果取决于数据集和模型的特征,因此您需要根据实际情况进行实验和观察。
2. 使⽤验证集来找到最佳值λ。它真的是最优值吗?这有关系吗?
答:使用验证集来找到最佳的λ值是一种常见的做法。通过在训练过程中使用验证集来评估不同λ值下的模型性能,并选择在验证集上表现最好的λ值,可以帮助我们在一定程度上避免过拟合。
然而,最佳的λ值并不一定是绝对的最优值。选择最佳λ值时,我们通常是基于验证集的性能衡量指标(如准确率或损失函数)进行决策。这个选择可能受到验证集的特点和分布的影响。
所以,当使用验证集选择λ值时,需要注意以下几点:
① 验证集应该是代表性的,与测试集和实际应用场景具有一定的一致性。
② 验证集的大小应适中,既要包含足够的样本以反映模型的泛化性能,又要避免过度依赖验证集的情况。
③ 在使用验证集进行λ选择后,最终还需要使用独立的测试集来评估模型的总体性能。
此外,最佳的λ值还可能受到数据集规模、特征的多样性、模型的复杂度以及其他超参数的影响。因此,为了得到更可靠的结果,可能需要进行多轮实验和调整。
总之,通过使用验证集选择最佳的λ值是一种有效的方法,但它可能不一定是绝对的最优值,而且选择λ值时需要考虑验证集的特点和适用性,并结合其他因素进行综合判断。
3. 如果我们使⽤∑ i |wi |作为我们选择的惩罚(L1正则化),那么更新⽅程会是什么样⼦?
答:
如果我们使用L1正则化作为惩罚项,更新方程会有所不同。一般来说,对于梯度下降法更新权重的标准形式:
w = w - learning_rate * gradient
在加入L1正则化后,更新方程变为:
w = w - learning_rate * (gradient + lambda * sign(w))
其中,lambda是正则化参数,sign(w)是权重向量w的符号函数。
L1正则化通过将L1范数(绝对值之和)作为惩罚项,鼓励权重向量w中的部分元素趋近于零,从而使模型更加稀疏。当梯度计算出来时,与梯度下降法相比,L1正则化会添加一个额外的惩罚项lambda * sign(w),导致权重向量的更新同时受到梯度和正则化项的影响。
这样的更新方程可以促使模型更强烈地倾向于选择对结果有显著影响的特征,剔除对结果贡献较小的特征,从而达到特征选择或降维的目的。
答:
类似于向量的L2范数是由向量的内积计算得出的,矩阵的Frobenius范数也是由矩阵元素平方和的平方根计算得出。
对于一个矩阵A,其Frobenius范数表示为:
||A|| F = sqrt(sum(A^2))
其中,A^2表示矩阵A的每个元素的平方。这个表达式可以简化为:
||A|| F = sqrt(trace(A⊤A))
其中,trace(A⊤A)表示矩阵A的转置与自身的乘积的迹(即对角线元素之和)。
因此,矩阵的Frobenius范数类似于向量的L2范数,都是通过平方求和再开方的方式计算得出的。
5. 回顾训练误差和泛化误差之间的关系。除了权重衰减、增加训练数据、使⽤适当复杂度的模型之外,还 能想出其他什么⽅法来处理过拟合?
答:
除了权重衰减、增加训练数据和使用适当复杂度的模型之外,还有一些方法可以处理过拟合问题。以下是一些常用的方法:
① 提前停止训练(Early Stopping):在训练过程中监控验证误差,当验证误差开始增加时停止训练,防止过拟合。
② 数据增强(Data Augmentation):通过对原始训练数据进行扩充、变换或合成等操作,生成额外的样本,增加数据的多样性,减少过拟合风险。
③ Dropout:随机丢弃网络中的部分神经元,在每次训练迭代中随机选择不同的神经元丢弃,以此减少神经元间复杂的共适应关系,缓解过拟合。
④ 正则化(Regularization):包括L1正则化(L1 regularization)和L2正则化(L2 regularization)。通过在损失函数中引入正则化项,限制参数的大小,防止过拟合。
⑤ 模型集成(Model Ensemble):将多个不同的模型组合在一起,例如使用集成学习方法(如Bagging、Boosting、Stacking),综合各个模型的预测结果来减少过拟合的影响。
⑥ DropConnect:类似于Dropout,但是针对连接权重而不是神经元进行随机丢弃,在每个训练迭代中随机选择不同的连接丢弃,减少神经网络的复杂性。
这些方法可以单独或结合使用,根据具体情况选择适合的方法来处理过拟合问题。
7. 在⻉叶斯统计中,我们使⽤先验和似然的乘积,通过公式P(w | x) ∝ P(x | w)P(w)得到后验。如何得到带正则化的P(w)?
答:在贝叶斯统计中,符号∝表示"与之成正比"的意思。公式P(w | x) ∝ P(x | w)P(w)表示后验概率P(w | x)与似然函数P(x | w)乘以先验概率P(w)之间成正比关系。
要得到带正则化的先验概率P(w),我们可以使用贝叶斯定理,并引入一种正则化项来控制模型的复杂度。
常用的正则化项是L2正则化(也称为权重衰减),它通过向先验概率引入参数的平方和来限制参数的大小,起到正则化的效果,防止过拟合。
具体而言,带L2正则化的先验概率P(w)一般表示为高斯分布的形式:
P(w) = N(w | 0, σ^2I)
其中,N(w | 0, σ^2I)表示均值为零、方差为σ^2I的多元高斯分布,w表示模型参数,σ^2控制正则化项的强度,I为单位矩阵。
这样,在给定训练数据和似然函数P(x | w)的情况下,将似然函数与带正则化的先验概率相乘,应用了贝叶斯定理后,可以得到带正则化的后验概率P(w | x) ∝ P(x | w)P(w)。文章来源:https://www.toymoban.com/news/detail-581130.html
需要注意的是,在实际应用中,为了获得具体的后验分布,一般还需要进行归一化处理,并使用不同的优化方法来估计参数w的后验分布。文章来源地址https://www.toymoban.com/news/detail-581130.html
到了这里,关于【深度学习】日常笔记11的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!