动手学深度学习4.5 权重衰减-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。

本节课程地址:权重衰退_哔哩哔哩_bilibili

本节教材地址:4.5. 权重衰减 — 动手学深度学习 2.0.0 documentation (d2l.ai)

本节开源代码:...>d2l-zh>pytorch>chapter_multilayer-perceptrons>weight-decay.ipynb


权重衰减

前一节我们描述了过拟合的问题,本节我们将介绍一些正则化模型的技术。 我们总是可以通过去收集更多的训练数据来缓解过拟合。 但这可能成本很高,耗时颇多,或者完全超出我们的控制,因而在短期内不可能做到。 假设我们已经拥有尽可能多的高质量数据,我们便可以将重点放在正则化技术上。

回想一下,在多项式回归的例子(4.4节)中, 我们可以通过调整拟合多项式的阶数来限制模型的容量。 实际上,限制特征的数量是缓解过拟合的一种常用技术。 然而,简单地丢弃特征对这项工作来说可能过于生硬。 我们继续思考多项式回归的例子,考虑高维输入可能发生的情况。 多项式对多变量数据的自然扩展称为单项式(monomials), 也可以说是变量幂的乘积。 单项式的阶数是幂的和。 例如, x_1^2 x_2 和 x_3 x_5^2 都是3次单项式。

注意,随着阶数 d 的增长,带有阶数 d 的项数迅速增加。 给定 k 个变量,阶数为 d 的项的个数为 {k - 1 + d} \choose {k - 1} ,即  {k - 1 + d} \choose {k - 1} 。 因此即使是阶数上的微小变化,比如从 2 到 3 ,也会显著增加我们模型的复杂性。 仅仅通过简单的限制特征数量(在多项式回归中体现为限制阶数),可能仍然使模型在过简单和过复杂中徘徊, 我们需要一个更细粒度的工具来调整函数的复杂性,使其达到一个合适的平衡位置。


多项式展开项数:

d个阶数分配给k个变量, 等同于有k-1个隔板将d个阶数划分为k份,每个变量的阶数为0~d不等, 每个变量的阶数等于每两个隔板之间的分配阶数。

因此,排列数等于将d个阶数和k-1个隔板排列在一起,插入k-1个隔板,也即: C^{k-1}_{k-1+d} = \frac{(k-1+d)!}{(d)!(k-1)!}


范数与权重衰减

在 2.3.10节 中, 我们已经描述了 L_2 范数和 L_1 范数, 它们是更为一般的 L_p 范数的特殊情况。 在训练参数化机器学习模型时, 权重衰减(weight decay)是最广泛使用的正则化的技术之一, 它通常也被称为  L_2  正则化 这项技术通过函数与零的距离来衡量函数的复杂度, 因为在所有函数 f 中,函数 f=0 (所有输入都得到值 0 ) 在某种意义上是最简单的。 但是我们应该如何精确地测量一个函数和零之间的距离呢? 没有一个正确的答案。 事实上,函数分析和巴拿赫空间理论的研究,都在致力于回答这个问题。

一种简单的方法是通过线性函数 f(\mathbf{x}) = \mathbf{w}^\top \mathbf{x} 中的权重向量的某个范数来度量其复杂性, 例如 |𝑤|2 。 要保证权重向量比较小, 最常用方法是将其范数作为惩罚项加到最小化损失的问题中。 将原来的训练目标最小化训练标签上的预测损失, 调整为最小化预测损失和惩罚项之和。 现在,如果我们的权重向量增长的太大, 我们的学习算法可能会更集中于最小化权重范数 | \mathbf{w} |^2 。 这正是我们想要的。 让我们回顾一下 3.1节 中的线性回归例子。 我们的损失由下式给出:

L(\mathbf{w}, b) = \frac{1}{n}\sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.

回想一下, \mathbf{x}^{(i)} 是样本 𝑖 的特征, y^{(i)} 是样本 𝑖 的标签, (\mathbf{w}, b) 是权重和偏置参数。 为了惩罚权重向量的大小, 我们必须以某种方式在损失函数中添加 | \mathbf{w} |^2 , 但是模型应该如何平衡这个新的额外惩罚的损失? 实际上,我们通过正则化常数 𝜆 来描述这种权衡, 这是一个非负超参数,我们使用验证数据拟合:

L(\mathbf{w}, b) + \frac{\lambda}{2} \|\mathbf{w}\|^2,

对于 \lambda = 0 ,我们恢复了原来的损失函数。 对于 \lambda > 0 ,我们限制 | \mathbf{w} | 的大小。 这里我们仍然除以 2 :当我们取一个二次函数的导数时, 2 和 1/2 会抵消,以确保更新表达式看起来既漂亮又简单。 为什么在这里我们使用平方范数而不是标准范数(即欧几里得距离)? 我们这样做是为了便于计算。 通过平方 L_2 范数,我们去掉平方根,留下权重向量每个分量的平方和。 这使得惩罚的导数很容易计算:导数的和等于和的导数。

此外,为什么我们首先使用 L_2 范数,而不是 L_1 范数。 事实上,这个选择在整个统计领域中都是有效的和受欢迎的。 L_2 正则化线性模型构成经典的岭回归(ridge regression)算法, L_1 正则化线性回归是统计学中类似的基本模型, 通常被称为套索回归(lasso regression)。 使用 L_2 范数的一个原因是它对权重向量的大分量施加了巨大的惩罚。 这使得我们的学习算法偏向于在大量特征上均匀分布权重的模型。 在实践中,这可能使它们对单个变量中的观测误差更为稳定。 相比之下, L_1 惩罚会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零。 这称为特征选择(feature selection),这可能是其他场景下需要的。

使用与 (3.1.10)中的相同符号, L_2 正则化回归的小批量随机梯度下降更新如下式:

\begin{aligned} \mathbf{w} & \leftarrow \left(1- \eta\lambda \right) \mathbf{w} - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \mathbf{x}^{(i)} \left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right). \end{aligned}


补充:

计算梯度: \frac{\partial}{\partial \mathbf{w}} {(l(\mathbf{w}, b)+\frac{\lambda}{2}\|\mathbf{w}\|^2)} = \frac{\partial l(\mathbf{w}, b)}{\partial \mathbf{w}} + \lambda \mathbf{w} 

更新权重:

 \mathbf{w}_{t+1} = \mathbf{w}_{t} - \eta \frac{\partial l(\mathbf{w}, b)}{\partial \mathbf{w}_{t}} \\ = \mathbf{w}_{t} - {\eta} (\frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}} + \lambda \mathbf{w})\\ = (1- \eta\lambda) \mathbf{w}_{t} - {\eta} \frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}} 

通常 \eta\lambda < 1 ,因此叫做权重衰退。

超参数 \lambda 控制了正则项的重要程度:

\lambda = 0:无作用; \lambda \rightarrow \infty : \mathbf{w}^* \rightarrow 0


根据之前章节所讲的,我们根据估计值与观测值之间的差异来更新 \mathbf{w} 。 然而,我们同时也在试图将 𝑤 的大小缩小到零。 这就是为什么这种方法有时被称为权重衰减。 我们仅考虑惩罚项,优化算法在训练的每一步衰减权重。 与特征选择相比,权重衰减为我们提供了一种连续的机制来调整函数的复杂度。 较小的 \lambda 值对应较少约束的 \mathbf{w} , 而较大的 \lambda 值对 \mathbf{w} 的约束更大。

是否对相应的偏置 b^2 进行惩罚在不同的实践中会有所不同, 在神经网络的不同层中也会有所不同。 通常,网络输出层的偏置项不会被正则化。

高维线性回归

我们通过一个简单的例子来演示权重衰减。

%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l

首先,我们[像以前一样生成一些数据],生成公式如下:

y = 0.05 + \sum_{i = 1}^d 0.01 x_i + \epsilon \text{ where } \epsilon \sim \mathcal{N}(0, 0.01^2).

我们选择标签是关于输入的线性函数。 标签同时被均值为0,标准差为0.01高斯噪声破坏。 为了使过拟合的效果更加明显,我们可以将问题的维数增加到 𝑑=200 , 并使用一个只包含20个样本的小训练集。

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)
# 生成数据迭代器,将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)

从零开始实现

下面我们将从头开始实现权重衰减,只需将 L_2 的平方惩罚添加到原始目标函数中。

[初始化模型参数]

首先,我们将定义一个函数来随机初始化模型参数。

def init_params():
    w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)
    b = torch.zeros(1, requires_grad=True)
    return [w, b]

(定义 𝐿2 范数惩罚)

实现这一惩罚最方便的方法是对所有项求平方后并将它们求和。

def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2

[定义训练代码实现]

下面的代码将模型拟合训练数据集,并在测试数据集上进行评估。 从 3节 以来,线性网络和平方损失没有变化, 所以我们通过d2l.linregd2l.squared_loss导入它们。 唯一的变化是损失现在包括了惩罚项。

def train(lambd):
    w, b = init_params()
    net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
    # 用lambda匿名函数定义net,输入为X,输出为d2l.linreg(X, w, b)
    num_epochs, lr = 100, 0.003
    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:
            # 增加了L2范数惩罚项,
            # 广播机制使l2_penalty(w)成为一个长度为batch_size的向量
            l = loss(net(X), y) + lambd * l2_penalty(w)
            l.sum().backward()
            d2l.sgd([w, b], lr, batch_size)
        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范数是:', torch.norm(w).item())
    # .item() 方法用于将包含单个元素的张量转换为 Python 标量(普通的整数或浮点数)

[忽略正则化直接训练]

我们现在用lambd = 0禁用权重衰减后运行这个代码。 注意,这里训练误差有了减少,但测试误差没有减少, 这意味着出现了严重的过拟合。

train(lambd=0)
w的L2范数是: 13.373031616210938

[使用权重衰减]

下面,我们使用权重衰减来运行代码。 注意,在这里训练误差增大,但测试误差减小。 这正是我们期望从正则化中得到的效果。

train(lambd=3)
w的L2范数是: 0.38239341974258423

[简洁实现]

由于权重衰减在神经网络优化中很常用, 深度学习框架为了便于我们使用权重衰减, 将权重衰减集成到优化算法中,以便与任何损失函数结合使用。 此外,这种集成还有计算上的好处, 允许在不增加任何额外的计算开销的情况下向算法中添加权重衰减。 由于更新的权重衰减部分仅依赖于每个参数的当前值, 因此优化器必须至少接触每个参数一次。

在下面的代码中,我们在实例化优化器时直接通过weight_decay指定weight decay超参数。 默认情况下,PyTorch同时衰减权重和偏移。 这里我们只为权重设置了weight_decay,所以偏置参数b不会衰减。

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())

[这些图看起来和我们从零开始实现权重衰减时的图相同]。 然而,它们运行得更快,更容易实现。 对于更复杂的问题,这一好处将变得更加明显。

train_concise(0)
w的L2范数: 12.444916725158691

train_concise(3)
w的L2范数: 0.3768826425075531

到目前为止,我们只接触到一个简单线性函数的概念。 此外,由什么构成一个简单的非线性函数可能是一个更复杂的问题。 例如,再生核希尔伯特空间(RKHS) 允许在非线性环境中应用为线性函数引入的工具。 不幸的是,基于RKHS的算法往往难以应用到大型、高维的数据。 在这本书中,我们将默认使用简单的启发式方法,即在深层网络的所有层上应用权重衰减。

小结

  • 正则化是处理过拟合的常用方法:在训练集的损失函数中加入惩罚项,以降低学习到的模型的复杂度。
  • 保持模型简单的一个特别的选择是使用 𝐿2 惩罚的权重衰减。这会导致学习算法更新步骤中的权重衰减。
  • 权重衰减功能在深度学习框架的优化器中提供。
  • 在同一训练代码实现中,不同的参数集可以有不同的更新行为。

练习

  1. 在本节的估计问题中使用 \lambda 的值进行实验。绘制训练和测试精度关于 \lambda 的函数。观察到了什么?

解:
当 
\lambda 值较小时,正则化项的影响较小,模型可能会过拟合训练集,导致在测试集上表现较差。随着λ值增大,正则化项的影响加强,可以降低模型的复杂度和过拟合的风险,从而提高在测试集上的表现。
然而,当 
\lambda 值过大时,正则化项可能会过分约束模型导致欠拟合,从而使训练和测试精度都较低。
代码如下:

animator = d2l.Animator(xlabel='lambda', ylabel='loss', yscale='log', 
                        xlim=[0, 20], legend=['train', 'test'])
wds = [0, 5, 10, 15, 20]
w = []
for wd in wds:
    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)
    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()    
    animator.add(wd,
                 (d2l.evaluate_loss(net, train_iter, loss),
                  d2l.evaluate_loss(net, test_iter, loss)))
    w.append(f'lambda={wd}, w的L2范数: {net[0].weight.norm().item()}')
for i in w:
    print(i, end = '\n')

lambda=0, w的L2范数: 13.640575408935547
lambda=5, w的L2范数: 0.3943481147289276
lambda=10, w的L2范数: 0.09477921575307846
lambda=15, w的L2范数: 0.048191435635089874
lambda=20, w的L2范数: 0.043202996253967285

2. 使用验证集来找到最佳值 \lambda。它真的是最优值吗?这有关系吗?

解:
使用验证集来找到最佳的 
\lambda 值是一种常用的方法,但并不一定保证找到的 \lambda 值是真正的最优值。这是因为验证集是从训练数据中独立选择出来的一部分数据,可用来评估不同超参数设置下模型的性能,但并不能代表整个数据集上的真实性能。
代码如下:

animator = d2l.Animator(xlabel='lambda', ylabel='loss', yscale='log', 
                        xlim=[0, 25], legend=['test'])
wds = [0, 5, 10, 15, 20, 25]
min_loss = 1
best_lambda = 0
for wd in wds:
    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)
    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()    
    animator.add(wd,
                 (d2l.evaluate_loss(net, test_iter, loss)))
    if d2l.evaluate_loss(net, test_iter, loss) < min_loss:
        min_loss = d2l.evaluate_loss(net, test_iter, loss)
        best_lambda = wd

print(f'best lambda value in test set is: {best_lambda}')

best lambda value in test set is: 15

3. 如果我们使用 \sum_i |w_i| 作为我们选择的惩罚(L_1 正则化),那么更新方程会是什么样子?

解:
计算梯度: \frac{\partial}{\partial \mathbf{w}} {(l(\mathbf{w}, b)+{\lambda}\sum_i |w_i|)} = \frac{\partial l(\mathbf{w}, b)}{\partial \mathbf{w}} + \lambda sgn({w}) 其中: 

sgn(x)=\left\{ \begin{aligned} 1, {w} > 0 \\ 0, {w} = 0 \\ -1, {w} < 0 \end{aligned} \right. 

更新权重:

 \mathbf{w}_{t+1} = \mathbf{w}_{t} - \eta \frac{\partial l(\mathbf{w}, b)}{\partial \mathbf{w}_{t}} \\ = \mathbf{w}_{t} - {\eta} (\frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}} + \lambda sgn({w}))\\ = \mathbf{w}_{t} - {\eta} \frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}} - {\eta} \lambda sgn({w})\\ = \left\{ \begin{aligned} \mathbf{w}_{t} - {\eta} \frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}} - {\eta} \lambda, {w} > 0 \\ \mathbf{w}_{t} - {\eta} \frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}}, {w} = 0 \\ \mathbf{w}_{t} - {\eta} \frac {\partial l(\mathbf{w}_{t}, b_{t})}{\partial \mathbf{w}_{t}} + {\eta} \lambda, {w} < 0 \end{aligned} \right.

由于权重更新方程中存在sgn(w),因此,当w>0时,梯度下降时更新后的w变小,当w<0时,梯度下降时更新后的w变大,也即,L1正则化使得权重w从正负两端向0靠近,使网络中的权重尽可能为0,相当于减小了网络复杂度,防止过拟合。

4. 我们知道 |\mathbf{w}|^2 = \mathbf{w}^\top \mathbf{w} 。能找到类似的矩阵方程吗(见 2.3.10节 中的Frobenius范数)?
解:
将 |\mathbf{w}|^2 = \mathbf{w}^\top \mathbf{w} 推广到矩阵形式:
\|\mathbf{A}\|^2_F = trace({A}^T{A}) 其中, |\mathbf{A}|^2_F 表示Frobenius范数的平方, trace(\mathbf{A}) 表示 𝐴 的迹( trace(\mathbf{A}) = \sum_{i=1}^{n}{a}_{ii} )。

5. 回顾训练误差和泛化误差之间的关系。除了权重衰减、增加训练数据、使用适当复杂度的模型之外,还能想出其他什么方法来处理过拟合?
解:
其他常用方法包括:
早停(Early Stopping):在训练过程中监控验证误差,当验证误差达到最小值后,停止训练。这样可以防止模型过分拟合训练数据。

数据增强(Data Augmentation):通过对训练数据进行一系列的随机变换或扩增,如平移、旋转、缩放、翻转等,可以引入一定的随机性和多样性,增加训练数据的多样性,从而减少过拟合。

Dropout:在训练过程中,随机选择一部分神经元将其输出置为零,这样可以减少神经元之间的共适应性,一定程度上减少过拟合。

集成方法(Ensemble Methods):通过组合多个不同的模型,如 Bagging、Boosting、Stacking 等方法,可以减少模型的方差,提高模型的泛化能力。

模型选择(Model Selection):通过交叉验证等技术,选择最佳的模型超参数,从而减少过拟合。

6. 在贝叶斯统计中,我们使用先验和似然的乘积,通过公式 P(w \mid x) \propto P(x \mid w) P(w) 得到后验。如何得到带正则化的 P(w) ?

解:
当 w_i\sim{N(0,\sigma^2)} 时, P(w) = \prod_{i}\frac{1}{\sigma \sqrt{2\pi}}\exp{-\frac{(w_i-0)^2}{2\sigma^2}} 

\log{P(w)} = \log{\prod_{i}\frac{1}{\sigma \sqrt{2\pi}}\exp{-\frac{(w_i-0)^2}{2\sigma^2}}} \\ = -\frac{1}{2\sigma^2}\sum_i{w_i^2} + C(C为常数)

相当于L2正则化;
当 w_i\sim{Laplace(0,b)} 时, P(w) = \prod_{i}\frac{1}{2b}\exp{-\frac{|w_i - 0|}{b}} 

\log{P(w)} = \log{\prod_{i}\frac{1}{2b}\exp{-\frac{|w_i - 0|}{b}}} \\ = -\frac{1}{b}\sum_i{w_i} + C(C为常数)

相当于L1正则化。

因此,从贝叶斯公式的角度也可以理解正则化:
根据 P(w \mid x) \propto P(x \mid w) P(w) ,
最大后验估计(MAP)为:
Loss = \arg \max P(w \mid x) \ =\arg \max P(x \mid w) P(w) \\ = \arg \max \log{P(x \mid w) P(w)} \ = \arg \max (\log{P(x \mid w)} + \log{P(w)}) 

其中,
当 w_i=C(常数) 时,最大后验估计=最大似然估计(MLE),同3.1节内容;
当 w_i\sim{N(0,\sigma^2)} 时, 相当于在损失函数中增加L2正则化;
当 w_i\sim{Laplace(0,b)} 时, 相当于在损失函数中增加L1正则化。

推荐相关视频讲解:贝叶斯解释“L1和L2正则化”,本质上是最大后验估计。如何深入理解贝叶斯公式?_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/658306.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

对比表征学习(一)Contrastive Representation Learning

对比表征学习&#xff08;二&#xff09;Sentence Embedding 主要参考翁莉莲的Blog&#xff0c;本文主要聚焦于对比损失函数 对比表示学习&#xff08;Contrastive Representation Learning&#xff09;可以用来优化嵌入空间&#xff0c;使相似的数据靠近&#xff0c;不相似的数…

src挖掘技巧--别人能挖到,你不来看看吗?

漏洞类型&#xff1a;拒绝服务漏洞 原理&#xff1a;通过控制修改验证码的长和宽&#xff0c;请求大量资源&#xff0c;导致拒绝服务漏洞&#xff0c;可以通过数据包的返回量值和返回时间来判断是否存在该漏洞。 实战报告 在获取验证码的时候进行抓包 右键打开验证码图片&am…

关于Linux软链你必须知道的实用知识点(非常详细)零基础入门到精通,收藏这一篇就够了

背景 Linux中的软链&#xff0c;是非常强大的工具&#xff0c;如果只是一知半解&#xff0c;在解决问题时一定会让你栽跟头或者浪费大量的时间。非常有必要提前掌握Linux软链的几个实用的知识点。 分析 软链是什么&#xff1f; 在Linux中&#xff0c;软链接&#xff08;sym…

【CALayer-CALayer的基本属性 Objective-C语言】

一、接下来,我们来说这个Layer啊, 1.首先,Layer能接触到的,就是我们之前说截图啊,就是我们self.view里面,有一个layer属性, [self.view.layer renderInContext:(CGContextRef t)]; 那个里面,有一个layer属性,然后呢,是CALayer类型的, 接下来,我们就来学习一…

帆软报表点击表格给数据集传递参数案例

一、效果 有四个模块&#xff0c;分别是采购总金额&#xff0c;采购总数量&#xff0c;采购合同数量&#xff0c;采购合同申请数量通过点击单元格上的月份&#xff0c;展示不同的月份数据&#xff0c;进行单元格和表格之间的联动 二、准备好数据库表和展示数据内容 2.1 建表 …

【Java EE】网络原理——HTTP响应

目录 1.认识“状态码”&#xff08;status code&#xff09; 1.1 200 OK 1.2 404 Not Found 1.3 403 Forbodden 1.4 Method Not Allowed 1.5 Internal Sever Error 1.6 504 Gsteway Timeout 1.7 Move temporarily 1.8 Moved Permanently 1.9状态码小结 2.认识响应“报…

c语言 分而治之(施特拉森矩阵乘法)

给定两个大小分别为 nxn 的方阵 A 和 B&#xff0c;求它们的乘法矩阵。 朴素方法&#xff1a;以下是两个矩阵相乘的简单方法。 void multiply(int A[][N], int B[][N], int C[][N]) { for (int i 0; i < N; i) { for (int j 0; j < N; j) { …

【Python特征工程系列】利用SHAP进行特征重要性分析-决策树模型为例(案例+源码)

这是我的第290篇原创文章。 一、引言 SHAP 属于模型事后解释的方法&#xff0c;它的核心思想是计算特征对模型输出的边际贡献&#xff0c;再从全局和局部两个层面对“黑盒模型”进行解释。SHAP构建一个加性的解释模型&#xff0c;所有的特征都视为“贡献者”。 对于每个预测样…

北京证券公司港股通交易佣金手续费最低是多少?万0.8?港股通纳入规则是怎么样的?

港股通交易佣金概述 港股通的交易佣金可能会因证券公司和投资者的不同而有所差异。 北京证券公司的港股通交易佣金最低可能万分之零点八&#xff08;0.008%&#xff09;&#xff0c;但这需要投资者与证券公司客户经理了解&#xff0c;进行沟通和申请。 一般来说&#xff0c;…

树莓派部署harbor_arm64

文章目录 树莓派4b部署Harbor-arm64版本docker-compose维护命令访问harbor 192.168.1.111认用户名密码admin/Harbor12345 树莓派4b部署Harbor-arm64版本 harbor-arm版本 部署&#xff1a;参考 wget https://github.com/hzliangbin/harbor-arm64/releases/download/v1.9.3/ha…

常用压力、流量单位换算表

一、压力为单位面积所承受的力 压力&#xff1a;绝对压力 、表压力 、大气压力。相互关系&#xff1a;绝对压力表压力大气压力 绝对压力:当压力表示与完全真空的差。测量处的实际压力。 表压力:当表示其气体数值与该地域大气压力的差值。 大气压力&#xff1a;由大气重量所…

网吧|基于SprinBoot+vue的网吧管理系统(源码+数据库+文档)

网吧管理系统 目录 基于SprinBootvue的网吧管理系统 一、前言 二、系统设计 三、系统功能设计 1 管理员功能模块 2 网管功能模块 3 会员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#…

Docker搭建FRP内网穿透服务器

使用Docker搭建一个frp内网穿透 在现代网络环境中&#xff0c;由于防火墙和NAT等原因&#xff0c;内网设备无法直接被外网访问。FRP (Fast Reverse Proxy) 是一款非常流行的内网穿透工具&#xff0c;它能够帮助我们将内网服务暴露给外网。本文将介绍如何在Linux服务器上使用Do…

LitCTF2023

[LitCTF 2023]enbase64 base 64 里面有一个换表的函数 写代码 #include<stdio.h> #include<string.h> #include<stdlib.h> int main() {char *result; char Destination[65]; int v3[65];int j;int i; char Source[]"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde…

多线程新手村3--多线程代码案例

1.1 单例模式 单例模式是设计模式中非常经典的一种。那么有同学肯定就会好奇了&#xff0c;什么是设计模式呢&#xff1f; 设计模式简单的说就是程序员的“棋谱”&#xff0c;我们下象棋时肯定或多或少都背过棋谱&#xff0c;例如当头炮、马后炮等&#xff0c;设计模式也是这…

防火墙技术基础篇:基于Ensp配置防火墙NAT server(服务器映射)

配置防火墙NAT server(服务器映射) 什么是NAT Server (服务器映射) NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;是一种允许多个设备共享一个公共IP地址的技术。NAT Server&#xff0c;也称为服务器映射&#xff0c;是NAT技术中的一种应…

Windows找出权限维持的后门

Windows权限维持主要包含活动隐藏、自启动等技术。 隐藏文件 利用文件属性 最简单的一种隐藏文件的方式&#xff0c;文件右键属性&#xff0c;勾选隐藏&#xff0c;点击确定后&#xff0c;在这个文件里看不到刚刚的文件了。 如果要让文件显示出来&#xff0c;就点击查看&…

经典文献阅读之--SMERF(通过标清导航地图增强车道感知和拓扑理解)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&#xff0c;并附带200G…

目标检测——家庭日常用品数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

给pdf加水印,python实现

from PyPDF2 import PdfReader, PdfWriterdef add_watermark(pdf_file_in, pdf_file_mark, pdf_file_out):"""把水印添加到pdf中"""pdf_output PdfWriter()input_stream open(pdf_file_in, rb)pdf_input PdfReader(input_stream, strictFalse…