【youcans的深度学习 D02】PyTorch例程:创建 LeNet 模型进行图像分类

news2024/12/27 1:24:10

欢迎关注『youcans的深度学习』系列


【youcans的深度学习 D02】PyTorch例程:创建 LeNet 模型进行图像分类

    • 1. PyTorch 深度学习建模的基本步骤
    • 2. 加载 CIFAR-10 数据集
    • 3. 定义 LeNet-5 模型类
      • 3.1 LeNet 网络
      • 3.2 LeNet-5 网络
      • 3.3 定义 LeNet-5 网络模型类
      • 3.4 构建网络的图层定义函数
        • 3.4.1 卷积层
        • 3.4.2 最大池化层
        • 3.4.3 线性层
        • 3.4.4 ReLU 激活函数
    • 4. LeNet 模型训练完整例程
      • 4.1 实例化 LeNet 模型
      • 4.2 模型训练
      • 4.3 模型训练例程
    • 5. LeNet 模型预测完整例程


在前面的章节我们已经介绍了 Pytorch 中的数据加载和模型建立,本节在此基础上,实现一个简单而完整的案例:创建 LeNet 网络模型,使用 CIFAR-10 数据集训练模型,进行图像分类。


1. PyTorch 深度学习建模的基本步骤

使用 PyTorch 建立、训练和使用神经网络模型的基本步骤如下。

  1. 准备数据集(Prepare dataset):准备好训练数据集和测试数据集,可以使用 Pytorch 中的 DataLoader 和 Dataset 对数据进行加载和预处理。
  2. 创建网络模型(Design model using Class):使用 torch.nn 模块中的类来构建模型,可以选择使用已有的模型,如 ResNet、VGG、AlexNet 等,也可以自定义模型。
  3. 定义损失函数和优化器(Construct loss and optimizer):根据分类任务的需要,选择合适的损失函数和优化器。
  4. 模型训练(Trainning the model):将数据输入到模型中进行训练,一般需要进行多个 epoch 的训练。
  5. 模型测试(Testing the model):使用测试数据集来评估训练好的模型的性能,计算模型的准确率等指标。
  6. 模型保存与加载(Saving and loading a model):保存训练好的模型,以便以后使用或部署。
  7. 模型推理(Inferring model):对新的数据,使用训练好的模型预测输出结果。

2. 加载 CIFAR-10 数据集

使用通用的数据集(如 MNIST 或 CIFAR)训练神经网络,不仅可以显著地提高工作效率,而且通常可以获得更好的模型性能。这是由于通用数据集的样本结构均衡、信息高效,而且组织规范、易于处理。

PyTorch 提供了一些常用的图像数据集,预加载在 torchvision.datasets 类中。
torchvision 模块实现神经网络所需的核心类和方法, torchvision.datasets 包含流行的数据集、模型架构和常用的图像转换方法。

CIFAR 数据集是一个经典的图像分类小型数据集,有 CIFAR10 和 CIFAR100 两个版本。CIFAR10 有 10 个类别,CIFAR100 有 100 个类别。CIFAR10 每张图像大小为 32*32,包括飞机、去吃、鸟、,猫、鹿、狗、青蛙、马、船、卡车 10 个类别。CIFAR10 共有 60000张图像,其中训练集 50000张,测试集 10000张。每个类别有 6000张图片,数据集平衡。

加载和使用 CIFAR 数据集的方法为:

torchvision.datasets.CIFAR10()
torchvision.datasets.CIFAR100()

使用 DataLoader 类加载 CIFAR-10 数据集的例程如下。Dataloader 是一个迭代器,基本功能是传入一个 Dataset 对象,根据参数 batch_size 生成一个 batch 的数据。

import torch
from torchvision import transforms

# 定义 transform,将[0,1]的PILImage 转换为[-1,1]的Tensor
transform = transforms.Compose([  # Transform Compose of the image
        transforms.Resize([32,32]),  # 图像大小调整为 (w,h)=(32,32)
        transforms.ToTensor(),  # 将图像转换为张量 Tensor
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    
# 使用 DataLoader 类加载 CIFAR10 训练集
batch_size = 64
# 加载 CIFAR10 数据集, 如果 root 路径加载失败, 则自动在线下载
# 加载 CIFAR10 训练数据集, 50000张训练图片
train_set = torchvision.datasets.CIFAR10(root='../dataset', train=True,
            download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size,
               shuffle=True, num_workers=2)  # 用 DataLoader 加载数据

# 加载 CIFAR10 验证数据集, 10000张验证图片
valid_set = torchvision.datasets.CIFAR10(root='../dataset', train=False,
            download=True, transform=transform)
valid_loader = torch.utils.data.DataLoader(valid_set, batch_size=5000,
               shuffle=False, num_workers=2)    
      

3. 定义 LeNet-5 模型类

LeNet 是由 Yann Lecun(2018年图灵奖得主)提出的经典的卷积神经网络,最初用于手写字符识别。虽然现在看来这个网络非常简单,性能也很差,但其原理仍然是各种卷积神经网络的基础。

3.1 LeNet 网络

最初的 LeNet 网络采用 5层结构,创造性的引入了卷积神经网络的基本操作。网络结构如下:

  1. 输入层为 28×28 的单通道图像。

  2. C1 卷积层:4个 5×5 卷积核,得到 4 个 24×24 特征图。

  3. S1 池化层:平均池化层 2×2,得到 4 个 12×12 特征图。

  4. C2 卷积层:12个 5×5 卷积核,得到 12 个 8×8 特征图。

  5. S2 池化层:平均池化层 2×2,得到 12 个4×4 特征图。

  6. FC 全连接层: 全连接隐藏层,使用 sigmoid 函数。


3.2 LeNet-5 网络

1998年提出的 LeNet-5 网络是 LeNet 网络的改进版本。LeNet-5 采用 7层网络结构,包括 3个卷积层、2个池化层和2个全连接层:

  1. 输入层为 32×32 的单通道图像。

  2. C1 卷积层:6个 5×5 卷积核,得到 6 个 28×28 特征图。

  3. S2 池化层:最大池化层 2×2,得到 6 个 14×14 特征图。

  4. C3 卷积层:16个 5×5 卷积核,得到 16 个 10×10 特征图。

  5. S4 池化层:最大池化层 2×2,得到 16 个 5×5 特征图。

  6. C5 卷积层:120个 5×5 卷积核,得到 120 个 1×1 特征图。

  7. F6 全连接层:由 84 个神经元组成的全连接隐藏层,使用 sigmoid 函数。

  8. F7 输出层:由10 个神经元组成的 softmax 高斯连接层。

在这里插入图片描述


3.3 定义 LeNet-5 网络模型类

PyTorch 通过 torch.nn 模块提供了高阶的 API,可以从头开始构建网络。

使用 PyTorch 构造神经网络模型,需要运用__call__()__init__()方法定义模型类 Class。nn.Module 是所有神经网络单元(neural network modules)的基类。

PyTorch在 nn.Module 中实现了__call__()方法,在 __call__() 方法中调用 forward 函数。__init__()方法是类的初始化函数,类似于C++的构造函数。

LeNet 模型类的例程如下:

import torch.nn as nn
import torch.nn.functional as F

class LeNet(nn.Module):
    def __init__(self):  # 构造函数
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, 5)  # in_channels, out_channels, kernal_size
        self.pool1 = nn.MaxPool2d(2, 2)  # kernel_size, stride
        self.conv2 = nn.Conv2d(16, 32, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32 * 5 * 5, 120)  # in_features, out_features
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):  # 正向传播函数
        x = F.relu(self.conv1(x))  # input(3, 32, 32) output(16, 28, 28)
        x = self.pool1(x)          # output(16, 14, 14)
        x = F.relu(self.conv2(x))  # output(32, 10, 10)
        x = self.pool2(x)          # output(32, 5, 5)
        x = x.view(-1, 32*5*5)     # output(32*5*5)
        x = F.relu(self.fc1(x))    # output(120)
        x = F.relu(self.fc2(x))    # output(84)
        x = self.fc3(x)            # output(10)
        return x

3.4 构建网络的图层定义函数

在 LeNet 模型类的例程中,涉及到一些网络模型的图层定义函数。

3.4.1 卷积层

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode=‘zeros’)

参数说明:

  • in_channels:int,输入特征矩阵的通道数。如输入 RGB 彩色图像则 in_channels=3。
  • out_channels:int,卷积层输出特征矩阵的通道数,等于卷积核的数量。
  • kernel_size:int 或 tuple[int,int],卷积核的尺寸 (height, width),int 类型时表示 height=width。
  • stride:int 或 tuple[int,int],卷积核的步长,默认为1,int 类型时表示 stride_h=stride_w。
  • padding:int 或 tuple[int,int],在输入图像的边缘填充的行数,默认为 0 表示不做边缘填充。

注意事项:

卷积层的输出特征矩阵尺寸计算公式:
O u t p u t = W − F + 2 ∗ P S + 1 Output = \frac{W-F+2*P}{S} +1 Output=SWF+2P+1
其中,W 表示输入图片大小为 W*W,F 表示卷积核大小为 F*F,S 为步长,P 为边缘填充行数。


3.4.2 最大池化层

torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

参数说明:

  • kernel_size:int 或 tuple[int,int],卷积核的尺寸 (height, width),int 类型时表示 height=width。
  • stride:int 或 tuple[int,int],卷积核的步长,默认为 1,int 类型时表示 stride_h=stride_w。
  • padding:int 或 tuple[int,int],边缘填充,在两侧添加的隐式负无穷大填充,默认为 0。

3.4.3 线性层

torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

参数说明:

  • in_channels:int,输入特征向量的维数。
  • out_channels:int,输出特征向量的维数。
  • bias:bool,偏移量标志,默认为 True 表示带有偏移量,False 表示不带偏移量。

3.4.4 ReLU 激活函数

torch.nn.ReLU(inplace=False)

应用 ReLU 激活函数:

R e L U ( x ) = ( x ) + = m a x ( 0 , x ) ReLU(x) = (x)^+ = max(0, x) ReLU(x)=(x)+=max(0,x)

参数说明:

  • inplace:bool,就地操作标志,默认为 False。

4. LeNet 模型训练完整例程

4.1 实例化 LeNet 模型

上节已经定义了一个 LeNet 网络模型类。要建立一个 LeNet 模型对象进行训练,包括三个步骤:

  • 实例化 LeNet 模型对象
  • 设置损失函数 Loss
  • 设置优化器 optim
    # (4) 构造 LeNet 网络模型
    model = LeNet()  # 实例化 LeNet 网络模型
    # print(model)  # LeNet(conv1, pool, conv2, fc1, fc2, fc3)
    loss_criterion = nn.CrossEntropyLoss()  # 定义损失函数 CrossEntropy
    optimizer = optim.Adam(model.parameters(), lr=0.001)  # 定义优化器 Adam
    # 优化器对象创建时需要传入模型参数 model.parameters(),将扫描 module中的所有成员

torch.nn.functional 模块包含内置损失函数,交叉熵损失函数为 nn.CrossEntropyLoss。

torch.optim.Adam表示使用 Adam 优化器,注意要将 model 的参数 model.parameters() 传给优化器对象,以便 优化器扫描需要优化的参数。


4.2 模型训练

模型训练的基本步骤是:

  1. 前馈计算模型的输出值;
  2. 计算损失函数值;
  3. 计算权重 weight 和偏差 bias 的梯度;
  4. 根据梯度值调整模型参数;
  5. 将梯度重置为 0(用于下一循环)。

4.3 模型训练例程

完整的使用 PyTorch 导入 CIFAR10 数据集、创建 LeNet-5 网络模型和模型训练的例程如下。

# LeNet_CIFAR_train_1.py
# 构建 LeNet 网络的图像分类模型,使用 CIFAR10 数据集的模型训练
# https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#
# Crated: youcans@qq.com, 2023/04/18

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

class LeNet(nn.Module):  # # 继承 nn.Module 父类
    def __init__(self):  # 构造函数
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, 5)  # in_channels, out_channels, kernal_size
        self.pool1 = nn.MaxPool2d(2, 2)  # kernel_size, stride
        self.conv2 = nn.Conv2d(16, 32, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32 * 5 * 5, 120)  # in_features, out_features
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):  # 正向传播函数
        x = F.relu(self.conv1(x))  # input(3, 32, 32) output(16, 28, 28)
        x = self.pool1(x)          # output(16, 14, 14)
        x = F.relu(self.conv2(x))  # output(32, 10, 10)
        x = self.pool2(x)          # output(32, 5, 5)
        x = x.view(-1, 32*5*5)     # output(32*5*5)
        x = F.relu(self.fc1(x))    # output(120)
        x = F.relu(self.fc2(x))    # output(84)
        x = self.fc3(x)            # output(10)
        return x

if __name__ == '__main__':
    # (1) 将[0,1]的PILImage 转换为[-1,1]的Tensor
    transform = transforms.Compose([  # Transform Compose of the image
        transforms.Resize([32,32]),  # 图像大小调整为 (w,h)=(32,32)
        transforms.ToTensor(),  # 将图像转换为张量 Tensor
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # (2) 下载 CIFAR10 训练集
    batch_size = 64
    # 加载 CIFAR10 数据集, 如果 root 路径加载失败, 则自动在线下载
    # 加载 CIFAR10 训练数据集, 50000张训练图片
    train_set = torchvision.datasets.CIFAR10(root='../dataset', train=True,
                                            download=True, transform=transform)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size,
                                              shuffle=True, num_workers=2)  # 用 DataLoader 加载数据
    # 加载 CIFAR10 验证数据集, 10000张验证图片
    valid_set = torchvision.datasets.CIFAR10(root='../dataset', train=False,
                                           download=True, transform=transform)
    valid_loader = torch.utils.data.DataLoader(valid_set, batch_size=5000,
                                             shuffle=False, num_workers=2)
    # 创建生成器,用 next 获取一个批次的数据
    valid_data_iter = iter(valid_loader)  # _SingleProcessDataLoaderIter 对象
    valid_image, valid_label = next(valid_data_iter)  # val_image: [batch, 3, 32, 32] val_label: [batch]

    # (3) 定义类别名称 (10个类别)
    classes = ('plane', 'car', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck')

    # (4) 构造 LeNet 网络模型
    model = LeNet()  # 实例化 LeNet 网络模型
    # print(model)  # LeNet(conv1, pool, conv2, fc1, fc2, fc3)
    loss_criterion = nn.CrossEntropyLoss()  # 定义损失函数 CrossEntropy
    optimizer = optim.Adam(model.parameters(), lr=0.001)  # 定义优化器 Adam
    # 优化器对象创建时需要传入模型参数 model.parameters(),将扫描 module中的所有成员

    # (5) 用 train_loader 训练 LeNet 网络
    for epoch in range(5):  # 训练轮次 epoch
        running_loss = 0.0  # 每个 epoch 的累加损失值清零
        for step, data in enumerate(train_loader, start=0):  # 加载数据
            inputs, labels = data  # inputs: [batch, 3, 32, 32] labels: [batch]

            optimizer.zero_grad()  # 损失梯度的历史清零
            # forward + backward + optimize
            outputs = model(inputs)  # 前向传播, [batch, 10]
            loss = loss_criterion(outputs, labels)  # 计算损失
            loss.backward()  # 反向传播
            optimizer.step()  # 参数更新

            # print statistics
            running_loss += loss.item()
            if step % 100 == 99:  # 每 100 个 step 打印一次训练信息
                with torch.no_grad():  # 验证过程, 不计算损失函数梯度
                    outputs = model(valid_image)  # 对验证集进行模型推理 [batch, 10]
                    pred_label = torch.max(outputs, dim=1)[1]  # 模型预测的类别 [batch]
                    accuracy = torch.eq(pred_label, valid_label).sum().item() / valid_label.size(0)  # 计算准确率
                    print(
                        "epoch {}, step {}: loss = {:.4f}, accuracy {:.4f}".format(epoch, step, running_loss/100, accuracy))
                    running_loss = 0.0

    print('Finished Training')

    # (6) 保存 LeNet 网络模型
    model_path = "../models/CIFAR10_LeNet_1.pth"
    torch.save(model.state_dict(), model_path)

程序运行结果如下:

Files already downloaded and verified
Files already downloaded and verified
epoch 0, step 99: loss = 1.9706, accuracy 0.3682
epoch 0, step 199: loss = 1.6741, accuracy 0.4254
epoch 0, step 299: loss = 1.5687, accuracy 0.4756
epoch 0, step 399: loss = 1.4513, accuracy 0.4878
epoch 0, step 499: loss = 1.4060, accuracy 0.5300

epoch 4, step 99: loss = 0.9303, accuracy 0.6438
epoch 4, step 199: loss = 0.9322, accuracy 0.6526
epoch 4, step 299: loss = 0.9517, accuracy 0.6536
epoch 4, step 399: loss = 0.9251, accuracy 0.6626
epoch 4, step 499: loss = 0.9482, accuracy 0.6588
Finished Training

经过 5 轮训练,使用验证集 10000张图片进行验证,模型准确率为 65.88%。这个准确率当然是很低的,但这并不是本文讨论的重点,后续我们可以使用其它模型和方法来提高分类准确性。


5. LeNet 模型预测完整例程

使用训练好的 LeNet 模型,输入新的图片进行模型推理,由模型输出结果确定输入图片所属的类别。

# LeNet_CIFAR_pred_1.py
# 基于 LeNet 网络的图像分类模型,使用预训练模型进行图像分类
# https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#
# Crated: youcans@qq.com, 2023/04/18

import torch
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class LeNet(nn.Module):
    def __init__(self):    # 初始化函数
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 16, 5)  # in_channels, out_channels, kernal_size
        self.pool1 = nn.MaxPool2d(2, 2)  # kernel_size, stride
        self.conv2 = nn.Conv2d(16, 32, 5)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32 * 5 * 5, 120)  # in_features, out_features
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):  # 正向传播
        x = F.relu(self.conv1(x))  # input(3, 32, 32) output(16, 28, 28)
        x = self.pool1(x)          # output(16, 14, 14)
        x = F.relu(self.conv2(x))  # output(32, 10, 10)
        x = self.pool2(x)          # output(32, 5, 5)
        x = x.view(-1, 32*5*5)     # output(32*5*5)
        x = F.relu(self.fc1(x))    # output(120)
        x = F.relu(self.fc2(x))    # output(84)
        x = self.fc3(x)            # output(10)
        return x

if __name__ == '__main__':
    # (1) 将[0,1]的PILImage 转换为[-1,1]的Tensor
    transform = transforms.Compose([  # Transform Compose of the image
        transforms.Resize([32,32]),  # 图像大小调整为 (w,h)=(32,32)
        transforms.ToTensor(),  # 将图像转换为张量 Tensor
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

    # (2) 定义类别名称 (10个类别)
    classes = ('plane', 'car', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck')

    # (3) 加载 LeNet 预训练模型
    model = LeNet()  # 实例化 LeNet 网络模型
    model_path = "../models/CIFAR10_LeNet_1.pth"  # 模型文件路径
    model.load_state_dict(torch.load(model_path))

    # (4) 加载输入图像
    # # 方法 1:直接用 PIL 读取图像,需要转换为 CV 格式才能使用 OpenCV
    from PIL import Image  # 用 PIL 读取图像,需要转换为 CV 格式才能使用 OpenCV
    img = Image.open("../images/img_plane_01.jpg")  # PIL 读取图像文件
    img_t = transform(img)  # 进行预处理变换, torch.Size([3, 32, 32])
    batch_t = torch.unsqueeze(img_t, dim=0)  # 生成批图像 [1,3,32,32]
    print(img_t.shape, batch_t.shape)

    # (5) 模型推理
    with torch.no_grad():
        outputs = model(batch_t)
        predict = torch.max(outputs, dim=1)[1].numpy()  # 模型预测的类别
    label = classes[int(predict)]  # 模型预测的类别名称
    print(classes[int(predict)])

    # (6) 显示图像
    import cv2
    imgCV = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)  # PIL 转换为 CV
    cv2.putText(imgCV, label, (5, 50),  cv2.FONT_HERSHEY_COMPLEX, 2, (100, 20, 255), 2)
    cv2.imshow('image', imgCV)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

程序运行结果如下:

在这里插入图片描述


参考文献:

  1. Yann LeCun, Gradient-based learning applied to document recognition, 1998
  2. https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#

【本节完】


版权声明:
欢迎关注『youcans的深度学习』系列,转发请注明原文链接:
【youcans的深度学习 D01】PyTorch例程:创建 LeNet 模型进行图像分类(https://youcans.blog.csdn.net/article/details/130245409)
Copyright 2023 youcans, XUPT
Crated:2023-04-18


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

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

相关文章

AI大模型加速RPAxAI时代到来,谁会是RPA领域的杀手级应用?

GPT等AI大模型震撼来袭,基于RPA的超级自动化仍是最佳落地载体 对话弘玑CPO贾岿,深入了解国产RPA厂商对AI大模型的探索与实践 文/王吉伟 关于RPA已死的说法,在中国RPA元年(2019年)投资机构疯狂抢项目之时就已经有了。…

算法训练Day39:62.不同路径 63. 不同路径 II 动态规划

文章目录 不同路径题解(动态规划)数论方法 [不同路径 II](https://leetcode.cn/problems/unique-paths-ii/description/)题解 不同路径 CategoryDifficultyLikesDislikesContestSlugProblemIndexScorealgorithmsMedium (67.70%)17460--0 Tags Companies 一个机器人位于一个 …

Linux基础—网络设置

Linux基础—网络设置 一、查看网络配置1.查看网络接口信息 ifconfig2.查看主机名称 hostname3.查看路由表条目 route4.查看网络连接情况 netstat5.获取socket统计信息 ss 二、测试网络连接1.测试网络连接 ping2.跟踪数据包 traceroute3.域名解析 nslookup 三、使用网络配置命令…

拷贝构造与深浅拷贝

文章目录 一、拷贝构造函数二、拷贝初始化三、深浅拷贝 一、拷贝构造函数 如果一个构造函数的第一个参数是自身类型的引用,而且任何额外参数都有默认值,则此构造函数是拷贝构造函数。 class person { public: person(); //默认构造函数 pe…

54家备案法人信用评级机构名单

2023年4月20日,中国人民银行官网公示备案法人信用评级机构名单,共有54家机构获得了信用评级备案,并进行如下提示: 1.2019年11月26日,人民银行、发展改革委、财政部、证监会联合发布《信用评级业管理暂行办法》&#xf…

C语言之 顺序表(sequence chart)

线性表 线性表是n个具有相同特性的数据元素的有限序列。 常见的有:顺序表、链表、栈、队列、字符串.... 线性表在逻辑上是线性结构的,即一条连续的直线 但在物理结构上不一定是连续的,其在物理存储时,通常以数组和链式结构的形式…

观察者模式解读

目录 问题引进 天气预报项目需求 天气预报设计方案 1-普通方案 传统方式代码实现 观察者模式原理 观察者模式解决天气预报需求 代码实现 观察者模式的好处 问题引进 天气预报项目需求 1) 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式…

idm下载器是免费的吗?有哪些功能

对于PC用户来说,拥有一款好用和快速的下载工具,对我们来说至关重要,可以极大提高我们的工作效率和PC用户体验。IDM可以实现高速下载,其核心原理就是多线程下载,理论上可以达到带宽的峰值速度,深受用户的喜爱…

Python单向链表操作

目录 一、单向链表 单向链表示例图 二、单向链表的操作 1、判断链表是否为空 2,链表长度 3,遍历整个链表 4,在链表头部添加元素 5、链表尾部添加元素 6,在指定位置插入元素 7,修改指定位置的元素 8&#xff…

可视化Echarts中title、tooltip、legend、grid属性的常用设置

title中常用的设置 配置项--tooltip ​编辑 配置项--legend 配置项--grid title中常用的设置 title 标题组件,包含主标题和副标题。 以下是常用的对标题的设置 title:{//设置图表的标题text:"主标题",link:"baidu.com", //设置标题超链接…

详解C语言string.h中常见的14个库函数(三)

本篇博客继续讲解C语言string.h头文件中的库函数。本篇博客计划讲解3个函数,分别是:strstr, strtok, strerror。其中strstr函数我会用一种最简单的方式模拟实现。 strstr char * strstr ( const char * str1, const char * str2 );strstr可以在str1中查…

用yolov5+playwright过滑动验证码

目录 梳理思路 训练模型 编写代码 总结与提高 源码下载 在上一节,我们通过opencv-pythonplaywright成功过掉了QQ空间的滑动验证码。在本节,我们将使用yolov5playwright来实现相同效果。 注:因为yolov5的配置教程网上已经很多了&#xff…

C++初阶之函数重载

目录 前言 函数重载 1.函数重载的概念 2.C支持函数重载的原理--名字修饰(name Mangling) 前言 今天小编给大家带来的是C中关于函数重载的内容,和C语言不一样,函数重载是C语言特有的,那么该功能实现的底层原理是什么呢?请大家…

Idea配置maven,指定settings.xml文件不生效

一.简介 最近单位要求把项目的仓库配置从阿里云改为nexus私服,配置了一个settings-nexus.xml的配置文件,idea的maven配置指定了该settings-nexus.xml文件,发现走的还是阿里云的,新的settings-nexus.xml竟然不生效,依赖…

分支和循环语句(1)

文章目录 目录1. 什么是语句2. 分支语句(选择结构)2.1 if语句2.1.1 悬空else2.1.2 if书写形式的对比2.1.3 练习 2.2 switch语句2.2.1 在switch语句中的 break2.2.2 default子句2.2.3 练习 3. 循环语句3.1 while循环3.1.1 while语句中的break和continue 附…

记一次fastjson反序列化到内网靶标

声明:文中涉及到的技术和工具,仅供学习使用,禁止从事任何非法活动,如因此造成的直接或间接损失,均由使用者自行承担责任。 点点关注不迷路,每周不定时持续分享各种干货。 众亦信安,中意你啊&a…

多种方法解决This is usually caused by another repository pushing to the same ref的错误

文章目录 1. 复现错误2. 分析错误3. 解决错误4. 解决该错误的其他方法 1. 复现错误 今天使用git status查看文件状态,发现有一个文件未提交,如下代码所示: D:\project\test>git status On branch master Your branch is up to date with …

sftp常用命令介绍

sftp常用命令: 1. sftp 登录sftp服务器 sftp userip ​​​​​​ 如需要看全部命令:则使用help即可 2. pwd和lpwd 、 ls和lls 、cd和lcd 等 sftp登录之后默认操作是远程服务器,当需要操作本地时,就需要在前边加“l”&#…

【wpf踩坑日记】搞错了,眼睛问题(:))

背景 今天遇到一个草鸡奇葩的问题: ComboBox 选择时 没有触发绑定的属性的set。 其实看错了,Mode写出OneWay,应该是TowWay。 不然是会触发set的。兄弟们不用往下看了。。。。。 哎,有的时候就会碰到这种情况,我还…

Ubuntu上跑通PaddleOCR

书接上文。刚才说到我已经在NUC8里灌上了Windows Server 2019。接下来也顺利的启用了Hyper-V角色并装好了一台Ubuntu 22.04 LTS 的虚机。由于自从上回在树莓派上跑通了Paddle-Lite-Demo之后想再研究一下PaddleOCR但进展不顺,因此决定先不折腾了,还是从x6…