线性神经网络(sotfmax回归)

news2024/11/18 1:39:43

sotfmax回归

  • 定义
  • 网络架构
  • softmax运算
  • softmax回归实现(MNIST数据集)
    • 数据集的处理
      • 读取数据集
      • 查看形状
      • 数据可视化
      • 读取小批量
      • 整合所有组件
    • 神经网络的搭建
      • 加载数据集
      • 初始化模型参数
      • 定义softmax函数
      • 定义模型
      • 定义损失函数(难点)
      • 分类精度
      • 训练
    • 预测
  • softmax公式的优化
  • softmax的简洁实现(基于pytorch的API)

本文章系统讲解如何从零实现一个softmax回归神经网络,并附上pytorch代码的详细解释
!!本文章代码部分来自《动手学深度学习》

定义

为了估计所有可能类别的条件概率,我们需要一个有多个输出的模型,每个类别对应一个输出。 为了解决线性模型的分类问题,我们需要和输出一样多的仿射函数(affine function)。 每个输出对应于它自己的仿射函数。 在我们的例子中,由于我们有4个特征和3个可能的输出类别, 我们将需要12个标量来表示权重(带下标的), 3个标量来表示偏置(带下标的)。
(3.4.2)

网络架构

与线性回归一样,softmax回归也是一个单层神经网络。 由于计算每个输出o1、o2和o3取决于所有输入x1、x2、x3和x4, 所以softmax回归的输出层也是全连接层。
在这里插入图片描述

softmax运算

softmax函数能够将未规范化的预测变换为非负数并且总和为1,同时让模型保持 可导的性质。 为了完成这一目标,我们首先对每个未规范化的预测求幂,这样可以确保输出非负。 为了确保最终输出的概率值总和为1,我们再让每个求幂后的结果除以它们的总和。
在这里插入图片描述

softmax回归实现(MNIST数据集)

数据集的处理

读取数据集

MNIST数据集 (LeCun et al., 1998) 是图像分类中广泛使用的数据集之一,但作为基准数据集过于简单。 我们将使用类似但更复杂的Fashion-MNIST数据集 (Xiao et al., 2017)。

%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l

d2l.use_svg_display()

这段代码用于下载和加载 Fashion-MNIST 数据集,并对图像进行转换处理。

首先,transforms.ToTensor() 方法将每张图片转换为 PyTorch 中的张量形式,并将像素值的范围从 [0, 255] 转换为 [0, 1]。

然后,torchvision.datasets.FashionMNIST() 方法分别从 Fashion-MNIST 数据集的训练集和测试集中下载数据,并对其进行转换处理。其中,root 参数指定了数据集存储的根目录,train=True 表示加载训练集,train=False 表示加载测试集,transform=trans 表示对每张图片进行转换处理,download=True 表示如果本地没有该数据集则进行下载。

最后,mnist_train 和 mnist_test 分别存储了转换后的训练集和测试集数据。可以通过 torch.utils.data.DataLoader() 方法将这些数据打包成数据迭代器,以便后续的训练和测试。

# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
# 并除以255使得所有像素的数值均在0~1之间
trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(
    root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
    root="../data", train=False, transform=trans, download=True)

Fashion-MNIST由10个类别的图像组成, 每个类别由训练数据集(train dataset)中的6000张图像 和测试数据集(test dataset)中的1000张图像组成。 因此,训练集和测试集分别包含60000和10000张图像。 测试数据集不会用于训练,只用于评估模型性能。

查看形状

在这里插入图片描述

数据可视化

获取Fashion-MNIST数据集的文本标签

def get_fashion_mnist_labels(labels):  #@save
    """返回Fashion-MNIST数据集的文本标签"""
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

展示图片

def show_img(imgs,num_rows,num_cols,titles=None,scale=1.5):
	# 首先,该函数计算图形的大小,其中 scale 参数控制图形的缩放比例。接着,调用 subplots() 方法创建一个新的图形,并返回一个元组 (fig, axes),其中 fig 是整个图形对象,axes 是图形中的子图形列表。
    figsize=(num_cols*scale,num_rows*scale)
    _,axes=d2l.plt.subplots(num_rows,num_cols,figsize=figsize)
    # 接下来,将子图形列表 axes 转换成一维数组,以便后续可以使用单个索引访问子图形
    axes=axes.flatten()
    # 接着,函数使用 zip() 方法将子图形列表 axes 和要显示的图片列表 imgs 配对,并逐一遍历它们。对于每一对子图形 ax 和图片 img,函数分别调用 ax.imshow() 方法将图片添加到子图形中,并将其 x 轴和 y 轴标签设置为不可见。如果提供了 titles 参数,函数还会为每张图片添加标题。
    for i,(ax,img) in enumerate(zip(axes,imgs)):
        if torch.is_tensor(img):
            ax.imshow(img.numpy())
        else:
            ax.imshow(img)
        ax.axes.get_xaxis().set_visible(False)
        ax.axes.get_yaxis().set_visible(False)
        if titles:
            ax.set_title(titles[i])
    return axes

首先,该函数计算图形的大小,其中 scale 参数控制图形的缩放比例。接着,调用 subplots() 方法创建一个新的图形,并返回一个元组 (fig, axes),其中 fig 是整个图形对象,axes 是图形中的子图形列表。

接下来,将子图形列表 axes 转换成一维数组,以便后续可以使用单个索引访问子图形。

接着,函数使用 zip() 方法将子图形列表 axes 和要显示的图片列表 imgs 配对,并逐一遍历它们。对于每一对子图形 ax 和图片 img,函数分别调用 ax.imshow() 方法将图片添加到子图形中,并将其 x 轴和 y 轴标签设置为不可见。如果提供了 titles 参数,函数还会为每张图片添加标题。

最后,函数返回子图形列表 axes,以便调用者可以进一步操作和定制化。


显示前几个样本的图像和标签

X,y=next(iter(data.DataLoader(mnist_train,batch_size=18)))
show_img(X.reshape(18,28,28),2,9,titles=get_fashion_mnist_labels(y))

在这里插入图片描述

读取小批量

batch_size = 256

def get_dataloader_workers():  #@save
    """使用4个进程来读取数据"""
    return 4

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())

这段代码使用PyTorch内置的DataLoader将Fashion-MNIST训练数据集mnist_train转换为一个多线程的数据迭代器train_iter。每次迭代将返回一个批量的数据样本和对应的标签,每个批量的大小为batch_size,这里设置为256。shuffle=True表示每次迭代前打乱数据样本的顺序。num_workers=get_dataloader_workers()指定了数据加载过程中使用的线程数,这里设置为4个进程。

整合所有组件

def load_data_fashion_mnist(batch_size, resize=None):  #@save
    """下载Fashion-MNIST数据集,然后将其加载到内存中"""
    trans = [transforms.ToTensor()]
    if resize:
        trans.insert(0, transforms.Resize(resize))
    trans = transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(
        root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(
        root="../data", train=False, transform=trans, download=True)
    return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                            num_workers=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))

这是一个函数 load_data_fashion_mnist,它会下载 Fashion-MNIST 数据集并将其加载到内存中,最终返回训练集和测试集的 DataLoader 对象。其中 DataLoader 对象是 PyTorch 中用来封装数据集的对象,可以轻松地迭代数据集。

具体而言,load_data_fashion_mnist 的输入参数为 batch_size(批量大小)和 resize(图像大小调整)。这里默认情况下不进行大小调整。返回的结果包括训练集和测试集的 DataLoader 对象,其中的参数包括:

root:数据集存放的路径;
train:表示是否为训练集;
transform:图像转换方式,这里是将图像转为 Tensor 格式;
download:表示是否需要下载。
在 PyTorch 中,我们可以使用 transforms 模块进行数据增强或数据处理。transforms.Compose() 可以把多个变换组合在一起,构成一个变换序列。
在这个代码中,trans = [transforms.ToTensor()] 声明了一个列表变量 trans,其中包含一个变换:把 PIL 图片转换成 PyTorch 张量。如果 resize 参数被提供了,那么在这个列表的最前面插入一个 transforms.Resize(resize) 变换,它可以将 PIL 图片缩放到指定的大小。最后,transforms.Compose(trans) 把 trans 列表中的变换按照顺序组合起来,构成一个变换序列,可以在数据集加载时使用。

神经网络的搭建

加载数据集

from IPython import display
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

初始化模型参数

num_inputs = 784
num_outputs = 10

W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)

784是如何来的:我们知道原始数据集中每个样本都是12828的,我们将其展开铺平,看作是长度为784的向量

定义softmax函数

def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制

定义模型

def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)

在 PyTorch 中,当我们在调用 reshape 函数时,可以将一个维度设为 -1,这样 PyTorch 会根据张量的总元素数自动推断出这个维度的长度。例如,如果一个张量的形状为 (2, 3, 4),当我们将其形状调整为 (6, -1) 时,PyTorch 会自动计算出第二个维度的长度为 4。
在 x.reshape((-1, W.shape[0])) 中,由于第一个维度被设为 -1,PyTorch 会自动根据张量的总元素数和第二个维度的长度来计算出第一个维度的长度。这个操作的结果是,x 张量中的所有元素都被按行排列成一个二维矩阵,这个矩阵的列数等于 W 张量的行数。


pytorch的广播机制:
假设我们有一个形状为 (3, 4) 的矩阵 A A A 和一个形状为 (4,) 的向量 b b b,它们分别如下所示:

A = [ 1 2 3 4 5 6 7 8 9 10 11 12 ] A = \begin{bmatrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \end{bmatrix} A= 159261037114812

b = [ 2 3 4 5 ] b = \begin{bmatrix} 2 & 3 & 4 & 5 \end{bmatrix} b=[2345]

在这种情况下,向量 b b b 将被扩展为形状为 (3, 4) 的矩阵,以便与矩阵 A A A 进行加法运算。扩展后的 b b b 如下所示:

b ′ = [ 2 3 4 5 2 3 4 5 2 3 4 5 ] b' = \begin{bmatrix} 2 & 3 & 4 & 5 \\ 2 & 3 & 4 & 5 \\ 2 & 3 & 4 & 5 \end{bmatrix} b= 222333444555

现在我们可以通过将 A A A b ′ b' b 相加来执行广播加法:

A + b ′ = [ 1 2 3 4 5 6 7 8 9 10 11 12 ] + [ 2 3 4 5 2 3 4 5 2 3 4 5 ] = [ 3 5 7 9 7 9 11 13 11 13 15 17 ] A + b' = \begin{bmatrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \end{bmatrix} + \begin{bmatrix} 2 & 3 & 4 & 5 \\ 2 & 3 & 4 & 5 \\ 2 & 3 & 4 & 5 \end{bmatrix} = \begin{bmatrix} 3 & 5 & 7 & 9 \\ 7 & 9 & 11 & 13 \\ 11 & 13 & 15 & 17 \end{bmatrix} A+b= 159261037114812 + 222333444555 = 371159137111591317

可以看到, b b b 在加法运算中被广播为 b ′ b' b,矩阵 A A A b ′ b' b 形状相同,因此可以进行加法运算。


定义损失函数(难点)

我们使用交叉熵作为损失函数

def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])

首先解释:
y_hat的形状是(n, k),其中n是样本个数,k是类别数。那么y_hat[i, j]表示第i个样本属于第j个类别的概率。对于每个样本i,在标签y[i]对应的位置j上的概率值,就是y_hat[i, y[i]]。

y_hat[range(len(y_hat)), y]的意义是取出概率向量中,对应于样本标签的位置上的概率值。这里用到了Python中的高级索引(advanced indexing)。

而交叉熵损失函数的定义就是基于这些概率的,它测量的是模型预测出的概率分布与真实概率分布之间的差异。

这是一个计算交叉熵损失函数的函数。其中 y_hat 是模型的输出, y 是真实标签,两者形状相同。

这段代码实现了标准的交叉熵损失函数。我们知道,交叉熵是一种衡量概率分布之间差异的测量方法。在分类问题中,我们使用softmax函数将网络的输出转换为类别概率分布,然后将此分布与真实类别的分布进行比较。

假设 y y y 是真实类别的索引, y i y_i yi 是类别 i i i 的指示函数(如果样本属于类别 i i i,则 y i = 1 y_i = 1 yi=1,否则 y i = 0 y_i = 0 yi=0)。 y ^ \hat{y} y^ 是预测的类别概率分布,即 y ^ i = P ( y = i ∣ x ) \hat{y}_i = P(y = i \mid x) y^i=P(y=ix)。那么,交叉熵损失函数定义为:

loss ⁡ ( y , y ^ ) = − ∑ i y i log ⁡ y ^ i \operatorname{loss}(\mathbf{y}, \hat{\mathbf{y}}) = -\sum_i y_i \log \hat{y}_i loss(y,y^)=iyilogy^i

由于在 y y y 中只有一个元素是 1,所以 y i log ⁡ y ^ i = 0 y_i \log \hat{y}_i = 0 yilogy^i=0 对所有 i i i 不等于 y y y i i i 成立,所以只有真实类别对损失函数做出了贡献,即

loss ⁡ ( y , y ^ ) = − log ⁡ y ^ y \operatorname{loss}(\mathbf{y}, \hat{\mathbf{y}}) = -\log \hat{y}_{y} loss(y,y^)=logy^y

这就是上述代码中的实现。

分类精度

def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
        y_hat = y_hat.argmax(axis=1)
    cmp = y_hat.type(y.dtype) == y
    return float(cmp.type(y.dtype).sum())

这是一个计算分类准确率的函数,它接收两个参数,分别为预测值 y_hat 和真实标签 y。在函数中,首先判断 y_hat 的形状是否为二维且第二个维度的大小大于 1,如果是,则取每个样本预测值最大的下标作为预测类别。然后,将预测值与真实标签相比较,生成一个布尔类型的张量 cmp,其中预测正确的位置为 True,否则为 False。最后,将 cmp 中为 True 的数量求和并转换为浮点数返回,即为准确率。

class Accumulator:  #@save
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

这是一个累加器类,可以在n个变量上累加。累加器有以下几个方法:

__ init__(self, n):初始化一个长度为n的列表,列表的每个元素初始化为0.0。
add(self, *args):接受任意数量的参数,并将其转换为浮点数。然后,它将args和当前数据列表逐个相加。
reset(self):将数据列表中的所有元素重置为0.0。
__ getitem__(self, idx):获取数据列表中索引为idx的元素的值。

def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 正确预测数、预测总数
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

这个函数用于计算模型在指定数据集上的精度(accuracy)。其中,net是待评估的模型,data_iter是数据集迭代器。函数首先判断net是否为torch.nn.Module的实例,如果是,则将模型设置为评估模式,即禁用Dropout层和BatchNormalization层等,以避免对模型性能的影响。metric是一个Accumulator类的实例,用于累加模型的正确预测数和预测总数。然后使用torch.no_grad()上下文管理器禁用梯度计算,避免占用过多的内存,同时加速模型的计算。接着,遍历数据集迭代器data_iter,对每个小批量数据进行预测,计算正确预测数和预测总数,最后返回正确预测数占预测总数的比例(即精度)。

训练

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module):
        net.train()
    # 训练损失总和、训练准确度总和、样本数
    metric = Accumulator(3)
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y)
        if isinstance(updater, torch.optim.Optimizer):
            # 使用PyTorch内置的优化器和损失函数
            updater.zero_grad()
            l.mean().backward()
            updater.step()
        else:
            # 使用定制的优化器和损失函数
            l.sum().backward()
            updater(X.shape[0])
        metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    return metric[0] / metric[2], metric[1] / metric[2]

这段代码实现了一个训练模型的一个迭代周期的函数,具体流程如下:

将模型设置为训练模式
定义一个 metric 对象,该对象是一个 Accumulator 类型,用于累计训练损失总和、训练准确度总和、样本数
对于每一个迭代周期中的每一个 mini-batch,执行以下操作:
1.使用模型进行预测,得到预测值 y_hat
2.计算损失函数 loss(y_hat, y) 的值
如果使用的是 PyTorch 内置的优化器,执行以下操作:
1.梯度清零
2.反向传播求梯度
3.使用优化器进行参数更新
如果使用的是自定义的优化器,执行以下操作:
1.反向传播求梯度
2.使用自定义的优化器进行参数更新
将当前 mini-batch 的损失值、训练准确度和样本数添加到 metric 对象中
计算训练损失和训练精度,并返回
在展示训练函数的实现之前,我们定义一个在动画中绘制数据的实用程序类Animator,以下代码可以不了解,与本节无关

class Animator:  #@save
    """在动画中绘制数据"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

训练函数

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

这段代码定义了一个函数train_ch3,用于训练神经网络模型。

函数的参数包括:

net:神经网络模型
train_iter:训练数据集迭代器
test_iter:测试数据集迭代器
loss:损失函数
num_epochs:训练迭代周期数
updater:优化器
函数中使用了Animator类,用于可视化训练过程中的损失函数和精度变化。

在函数中,首先使用Animator类初始化一个动画器。接下来进行循环,每次循环代表一个训练迭代周期。在每个训练周期内,调用train_epoch_ch3函数进行一次训练,并计算训练损失和训练精度。然后调用evaluate_accuracy函数计算测试集的精度,并将结果添加到动画器中。最后,对训练结果进行一些断言,确保训练的结果符合预期。

定义超参数和优化器

lr = 0.1

def updater(batch_size):
    return d2l.sgd([W, b], lr, batch_size)

开始训练

num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

结果
在这里插入图片描述
训练结束之后全局变量W和b就是最后得出的最优参数

预测

def predict_ch3(net, test_iter, n=6):  #@save
    for X, y in test_iter:
        break
    trues = get_fashion_mnist_labels(y)
    preds = get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])

predict_ch3(net, test_iter)

predict_ch3函数的作用是对测试集数据进行预测并展示预测结果。具体而言,函数从测试集中取出前n个样本,将它们输入到训练好的模型net中进行预测,并将预测结果和真实标签拼接成字符串作为图像的标题,最后通过d2l.show_images函数展示这些图像。其中,d2l.get_fashion_mnist_labels函数用于将标签从数值形式转换为文本形式。

softmax公式的优化

为什么进行优化?
因为exp()函数为指数函数,有可能出现上溢的情况
解决方法:
每个数字在计算exp前先减去最大值max
在这里插入图片描述
新的问题:
此时有可能出现较小的负值导致下溢或者在反向传播时出现梯度消失
解决方法:
通过取对数的形式减少较小数对数值稳定性的干扰
此时softmax和交叉熵是结合起来的
在这里插入图片描述

softmax的简洁实现(基于pytorch的API)

import torch
from torch import nn
from d2l import torch as d2l
from IPython import display
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
num_epochs = 10
train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有序表之跳表

文章目录1、前言2、跳表简介3、理解“跳表”4、用跳表查询到底有多快5、跳表是不是很浪费内存6、高效的动态插入和删除7、跳表索引动态更新8、跳表代码实现1、前言 在开始讲解跳表之前&#xff0c;先来说一说积压结构。 何为积压结构&#xff1f;就是当数据达到了一定程度&am…

【ROS2实践】Vmware17下安装ubuntu22.04和ros2-humble

一、简介 ROS2-foxy已经不再维护&#xff0c;ROS2-humble成为主角&#xff0c;因而该转变一下开发场景了。如何安装&#xff1f;官方文档没有错&#xff0c;然而&#xff0c;照着做却无法进行。实超中遇到的需要变通的地方&#xff0c;官网是不给你提供解决的&#xff0c;本文给…

宽刈幅干涉高度计SWOT(Surface Water and Ocean Topography)卫星进展(待完善)

以下信息搬运自SWOT官方网站等部分文献资料&#xff0c;如有侵权请联系&#xff1a;sunmingzhismz163.com 排版、参考文献、部分章节待完善 概况 2022年12月16日地表水与海洋地形卫星SWOT (Surface Water and Ocean Topography)在加利福尼亚州范登堡航天基地由SpaceX猎鹰9号(Sp…

mysql面试题(最全)

1. 数据库三大范式是什么&#xff1f; 什么是范式&#xff1f; 范式是数据库设计时遵循的一种规范&#xff0c;不同的规范要求遵循不同的范式。 最常用的三大范式 第一范式(1NF)&#xff1a;属性不可分割&#xff0c;即每个属性都是不可分割的原子项。(实体的属性即表中的列)…

ESXi主机CVE-2021-21972漏洞复现安全处置建议

一、漏洞简介 vSphere 是 VMware 推出的虚拟化平台套件&#xff0c;包含 ESXi、vCenter Server 等一系列的软件。其中 vCenter Server 为 ESXi 的控制中心&#xff0c;可从单一控制点统一管理数据中心的所有 vSphere 主机和虚拟机。 vSphere Client&#xff08;HTML5&#xf…

【博客624】MAC地址表、ARP表、路由表(RIB表)、转发表(FIB表)

MAC地址表、ARP表、路由表(RIB表/FIB表) MAC地址表 MAC地址表是交换机等网络设备记录MAC地址和端口的映射关系&#xff0c;代表了交换机从哪个端口学习到了某个MAC地址&#xff0c;交换机把这个信息记录下来&#xff0c;后续交换机需要转发数据的时候就可以根据报文的目的MAC地…

SpringBoot社区版专业版带你配置热部署

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 SpringBoot社区版专业版带你配置热部署 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收藏⭐ 评论&#x1…

C++类基础(十七)

类的继承——补充知识 ● public 与 private 继承&#xff08;C Public, Protected and Private Inheritance&#xff09; 改变了类所继承的成员的访问权限 //公有继承 struct Base { public:int x; private:int y; protected:int z; }; struct Derive : public Base //公有继承…

【数据结构与算法】时间复杂度与空间复杂度

目录 一.前言 二.时间复杂度 1.概念 二.大O的渐进表示法 概念&#xff1a; 总结&#xff1a; 三.常见时间复杂度计算举例 例1 例2 例3 例4 例5.计算冒泡排序的时间复杂度 例6.二分算法的时间复杂度 例7.阶乘递归Fac的时间复杂度 例8.斐波那契递归的时间复杂度 …

【MyBatis】| MyBatis的注解式开发

目录 一&#xff1a;MyBatis的注解式开发 1. Insert注解 2. Delete注解 3. Update注解 4. Select注解 5. Results注解 一&#xff1a;MyBatis的注解式开发 MyBatis中也提供了注解式开发⽅式&#xff0c;采⽤注解可以减少Sql映射⽂件的配置。 当然&#xff0c;使⽤注…

推荐几款好用的数据库管理工具

本文主要介绍几款常用的数据库管理软件&#xff08;客户端&#xff09;&#xff0c;包括开源/免费的、商用收费的&#xff0c;其中有一些是专用于 MySQL 数据库的&#xff0c;例如 MySQL Workbench、phpMyAdmin&#xff0c;有一些是支持多种 SQL、NoSQL 数据库的&#xff0c;例…

Kubernetes集群维护—备份恢复与升级

Etcd数据库备份与恢复 需要先安装etcd备份工具yum install etcd -y按不同安装方式执行不同备份与恢复kubeadm部署方式&#xff1a; 备份&#xff1a;ETCDCTL_API3 etcdctl snapshot save snap.db --endpointshttps://127.0.0.1:2379 --cacert/etc/kubernetes/pki/etcd/ca.cr…

知其然更要知其所以然,聊聊SQLite软件架构

SQLite是一个非常受欢迎的数据库&#xff0c;在数据库排行榜中已经进入前十的行列。这主要是因为该数据库非常小巧&#xff0c;而且可以支持Linux、Windows、iOS和Andriod的主流的操作系统。 SQLite非常简单&#xff0c;是一个进程内的动态库数据库。其最大的特点是可以支持不同…

spring的了解以及项目构建

spring理念&#xff1a; 使现有的技术更容易使用&#xff0c;其本身是一个大杂烩&#xff0c;整合了现有的技术框架。 ssh&#xff1a; struct2 spring hibernate ssm &#xff1a;springmvc spring mybatis 优点&#xff1a; spring 是一个免费的开源框架&#xff08;容器…

特征归一化(Normalization)和Batch Normalization的理解

一、理解BN必备的前置知识&#xff08;BN, LN等一系列Normalization方法的动机&#xff09; Feature Scaling&#xff08;特征归一化/Normalization&#xff09;:通俗易懂理解特征归一化对梯度下降算法的重要性 总结一下得出的结论&#xff1a; &#xff08;以下举的例子是针对…

创建基于Vue2.0开发项目的两种方式

前天开始接触基于Vue2.0的前端项目&#xff0c;实际操作中肯定会遇到一些问题&#xff0c;慢慢摸索和总结。   其实&#xff0c;作为开发一般企事业单位应用的小项目&#xff0c;前端的懂一点HTMLCSSJavaScroptJQueryJson&#xff08;或者Xml&#xff09;&#xff0c;后端懂一…

PGLBox全面解决图训练速度、成本、稳定性、复杂算法四大问题!

图神经网络&#xff08;Graph Neural Network&#xff0c;GNN&#xff09;是近年来出现的一种利用深度学习直接对图结构数据进行学习的方法&#xff0c;通过在图中的节点和边上制定聚合的策略&#xff0c;GNN能够学习到图结构数据中节点以及边内在规律和更加深层次的语义特征。…

Dubbo学习笔记2

Dubbo学习笔记&#xff08;二&#xff09; Dubbo常用配置 覆盖策略 规则&#xff1a; 1、精确优先&#xff08;方法级优先&#xff0c;接口次之&#xff0c;全局配置再次之&#xff09; 2、消费者设置优先&#xff08;如果级别一样&#xff0c;则消费方优先&#xff0c;提供…

网络安全-信息收集- 谷歌浏览器插件收集信息,谷歌hacking搜索语法-带你玩不一样的搜索引擎

网络安全-信息收集- 谷歌浏览器插件收集信息&#xff0c;谷歌hacking搜索语法-带你玩不一样的搜索引擎 前言 一&#xff0c;我也是初学者记录的笔记 二&#xff0c;可能有错误的地方&#xff0c;请谨慎 三&#xff0c;欢迎各路大神指教 四&#xff0c;任何文章仅作为学习使用 …

图解LeetCode——剑指 Offer 28. 对称的二叉树

一、题目 请实现一个函数&#xff0c;用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样&#xff0c;那么它是对称的。 二、示例 2.1> 示例 1&#xff1a; 【输入】root [1,2,2,3,4,4,3] 【输出】true 2.2> 示例 2&#xff1a; 【输入】root [1,2,2,nul…