【卷积神经网络】LeNet实践

news2024/12/15 0:23:20

模型建立

  • 数据初始化
  • 根据模型搭建前向传播
  • 打印模型结构

前向传播数据初始化

    def __init__(self):
        super(LeNet, self).__init__()

        # 第一层卷积层:
        # 输入:灰度图像 (1通道,大小 28x28)
        # 输出:6个特征图 (大小 28x28, 通过padding=2保持尺寸不变)
        # 卷积核大小为 5x5,步幅为 1
        self.c1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2)

        # 激活函数:
        # 使用 Sigmoid 作为激活函数,将卷积输出的值压缩到 (0,1) 区间
        self.sig = nn.Sigmoid()

        # 第一层池化层:
        # 输入:6个特征图 (28x28)
        # 输出:6个特征图 (14x14, 通过池化降采样)
        # 池化窗口大小为 2x2,步幅为 2
        self.s2 = nn.AvgPool2d(kernel_size=2, stride=2)

        # 第二层卷积层:
        # 输入:6个特征图 (14x14)
        # 输出:16个特征图 (10x10, 无padding)
        # 卷积核大小为 5x5,步幅为 1
        self.c3 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)

        # 第二层池化层:
        # 输入:16个特征图 (10x10)
        # 输出:16个特征图 (5x5, 通过池化降采样)
        # 池化窗口大小为 2x2,步幅为 2
        self.s4 = nn.AvgPool2d(kernel_size=2, stride=2)

        # 展平层:
        # 输入:16个特征图 (5x5)
        # 输出:展平为长度为 400 的向量
        self.flatten = nn.Flatten()

        # 全连接层 1:
        # 输入:长度为 400 的向量
        # 输出:长度为 120 的向量
        self.f5 = nn.Linear(400, 120)

        # 全连接层 2:
        # 输入:长度为 120 的向量
        # 输出:长度为 84 的向量
        self.f6 = nn.Linear(120, 84)

        # 全连接层 3:
        # 输入:长度为 84 的向量
        # 输出:长度为 10 的向量(对应10个分类标签,如MNIST数字分类)
        self.f7 = nn.Linear(84, 10)

构建前向传播

    def forward(self, x):
        # 前向传播过程

        # 1. 卷积层 + 激活函数
        x = self.sig(self.c1(x))  # 第一层卷积 + 激活函数

        # 2. 池化层
        x = self.s2(x)  # 第一层池化

        # 3. 卷积层 + 激活函数
        x = self.sig(self.c3(x))  # 第二层卷积 + 激活函数

        # 4. 池化层
        x = self.s4(x)  # 第二层池化

        # 5. 展平
        x = self.flatten(x)  # 将特征图展平为向量

        # 6. 全连接层 1
        x = self.sig(self.f5(x))  # 第一全连接层 + 激活函数

        # 7. 全连接层 2
        x = self.sig(self.f6(x))  # 第二全连接层 + 激活函数

        # 8. 全连接层 3
        x = self.f7(x)  # 第三全连接层(输出分类结果)

        return x

打印模型参数

if __name__ == "__main__":
    # 判断当前是否有可用的GPU,如果有则使用CUDA(GPU),否则使用CPU
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # 初始化模型(LeNet类假设在之前已定义)
    # 并将模型加载到指定的设备(CPU或GPU)上
    model = LeNet().to(device)

    # 打印模型的摘要,包括模型结构、每层的参数数量以及输出形状
    # 输入形状指定为 (1, 28, 28),即单通道灰度图像
    print(summary(model, (1, 28, 28)))

运行结果

数据预处理

  • 加载训练数据集
  • 创建数据加载器

from torchvision.datasets import FashionMNIST  # 导入 FashionMNIST 数据集
from torchvision import transforms  # 图像处理工具
import torch.utils.data as Data  # 数据加载工具
import numpy as np  # 数值计算工具
import matplotlib.pyplot as plt  # 数据可视化工具

# 加载训练数据集
train_data = FashionMNIST(
    root='./data',  # 数据存储的根目录
    train=True,  # 加载训练数据
    transform=transforms.Compose([
        transforms.Resize(size=224),  # 将图像调整到 224x224 的大小
        transforms.ToTensor()  # 将图像转换为 PyTorch 张量
    ]),
    download=True  # 如果数据集不存在,则自动下载
)

# 创建数据加载器
train_loader = Data.DataLoader(
    dataset=train_data,  # 数据集
    batch_size=64,  # 每批次加载 64 张图片
    shuffle=True,  # 随机打乱数据
    num_workers=0  # 数据加载时使用的子线程数(Windows 环境下设置为 0)
)

# 提取一个批次的数据
for step, (b_x, b_y) in enumerate(train_loader):  # 遍历训练数据加载器
    if step > 0:  # 只提取第一个批次
        break

# 将图像数据转换为 NumPy 格式,并移除单一的维度
batch_x = b_x.squeeze().numpy()  # b_x: 形状为 [64, 1, 224, 224],squeeze 移除第 2 维
batch_y = b_y.numpy()  # 将标签转换为 NumPy 数组
class_label = train_data.classes  # 获取数据集中的类别标签
# print(class_label)  # 如果需要调试,可以打印类别标签列表

# 打印当前批次的维度
print("The size of batch in train data:", batch_x.shape)  # 输出批次大小,例如 [64, 224, 224]

# 可视化一个批次的图像
plt.figure(figsize=(12, 5))  # 创建一个 12x5 的画布
for ii in np.arange(len(batch_y)):  # 遍历当前批次的每个样本
    plt.subplot(4, 16, ii + 1)  # 在 4x16 网格中创建子图
    plt.imshow(batch_x[ii, :, :], cmap=plt.cm.gray)  # 绘制图像,灰度显示
    plt.title(class_label[batch_y[ii]], size=10)  # 设置标题为对应的类别标签
    plt.axis("off")  # 隐藏坐标轴
    plt.subplots_adjust(wspace=0.05)  # 调整子图之间的间距
plt.show()  # 显示图像

模型训练

数据预处理

import torch
from torchvision.datasets import FashionMNIST
from torchvision import transforms
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
from model import LeNet
import torch.nn as nn
import pandas as pd

def train_val_data_process():
    train_data = FashionMNIST(root='./data',
                              train=True,
                              transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
                              download=True)
    #划分训练集与验证集 80%用于训练
    train_data, val_data = Data.random_split(train_data, [round(0.8 * len(train_data)), round(0.2 * len(train_data))])

    #训练数据加载器
    train_dataloader = Data.DataLoader(dataset=train_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)

    #验证数据加载器
    val_dataloader = Data.DataLoader(dataset=val_data,
                                     batch_size=32,
                                     shuffle=True,
                                     num_workers=2)

    return train_dataloader, val_dataloader

# train_val_data_process()

训练逻辑

代码训练逻辑如果是学习新知识考试

初始化(学习计划制定)

  • 学生决定使用纸质书还是电子书来学习,类似于代码中选择 GPU 或 CPU
  • 优化器类似于学习方法(比如背诵、做笔记),损失函数类似于判断学习效果的标准(是否答对问题)

训练阶段(学习阶段)

  • 学生每天从学习材料中抽取一部分内容(批次)
  • 学习时通过练习题测试自己的理解,找出错误并改正(前向传播和反向传播)
  • 每天记录自己学了多少内容以及正确率(累加损失和正确样本数)

验证阶段(考试)

  • 学生在考试中用未见过的题目检验学习效果
  • 只计算考试的分数(验证损失和准确率),不再对学习内容进行改动

记录结果(反思总结)

  • 每天记录学习和考试的情况(损失和准确率)
  • 如果某天考试成绩比历史最好成绩高,就记录这一天的学习成果(保存最优模型参数)

最终总结(保存模型)

  • 保存最优模型(保存学习笔记以备后续复习)

训练函数实现

实现逻辑简单理解(训练过程类似应对考试的全流程)

###########################################
####深度学习训练与验证
##########################################
def train_model_process(model, train_dataloader, val_dataloader, num_epochs):
    ###########################################
    ###设备设置和优化器初始化
    ##########################################
    # 设定训练所用到的设备,有GPU用GPU没有GPU用CPU
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # 使用Adam优化器,学习率为0.001
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    # 损失函数为交叉熵函数
    criterion = nn.CrossEntropyLoss()
    # 将模型放入到训练设备中
    model = model.to(device)
    # 复制当前模型的参数
    best_model_wts = copy.deepcopy(model.state_dict())

    ###########################################
    ###初始化变量
    ##########################################
    # 最高准确度
    best_acc = 0.0
    # 训练集损失列表
    train_loss_all = []
    # 验证集损失列表
    val_loss_all = []
    # 训练集准确度列表
    train_acc_all = []
    # 验证集准确度列表
    val_acc_all = []
    # 当前时间
    since = time.time()

    ###########################################
    ###训练和验证循环
    ##########################################
    for epoch in range(num_epochs):
        print("Epoch {}/{}".format(epoch, num_epochs-1))
        print("-"*10)

        # 初始化参数
        # 训练集损失函数
        train_loss = 0.0
        # 训练集准确度
        train_corrects = 0
        # 验证集损失函数
        val_loss = 0.0
        # 验证集准确度
        val_corrects = 0
        # 训练集样本数量
        train_num = 0
        # 验证集样本数量
        val_num = 0

        # 对每一个mini-batch训练和计算
        for step, (b_x, b_y) in enumerate(train_dataloader):
            # 将特征放入到训练设备中
            b_x = b_x.to(device)
            # 将标签放入到训练设备中
            b_y = b_y.to(device)
            # 设置模型为训练模式
            model.train()

            # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
            output = model(b_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)

            # 将梯度初始化为0
            optimizer.zero_grad()
            # 反向传播计算
            loss.backward()
            # 根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值的作用
            optimizer.step()
            # 对损失函数进行累加(学到了多少内容)
            train_loss += loss.item() * b_x.size(0)
            # 如果预测正确,则准确度train_corrects加1(正确率)
            train_corrects += torch.sum(pre_lab == b_y.data)
            # 当前用于训练的样本数量
            train_num += b_x.size(0)

        ###########################################
        ###验证阶段
        ##########################################
        for step, (b_x, b_y) in enumerate(val_dataloader):
            # 将特征放入到验证设备中
            b_x = b_x.to(device)
            # 将标签放入到验证设备中
            b_y = b_y.to(device)
            # 设置模型为评估模式
            model.eval()
            # 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
            output = model(b_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 计算每一个batch的损失函数
            loss = criterion(output, b_y)


            # 对损失函数进行累加
            val_loss += loss.item() * b_x.size(0)
            # 如果预测正确,则准确度train_corrects加1
            val_corrects += torch.sum(pre_lab == b_y.data)
            # 当前用于验证的样本数量
            val_num += b_x.size(0)

        ###########################################
        ###记录和打印结果
        ##########################################
        # 计算并保存每一次迭代的loss值和准确率
        # 计算并保存训练集的loss值
        train_loss_all.append(train_loss / train_num)
        # 计算并保存训练集的准确率
        train_acc_all.append(train_corrects.double().item() / train_num)

        # 计算并保存验证集的loss值
        val_loss_all.append(val_loss / val_num)
        # 计算并保存验证集的准确率
        val_acc_all.append(val_corrects.double().item() / val_num)

        print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
        print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))

        ###########################################
        ###保存最优模型
        ##########################################
        if val_acc_all[-1] > best_acc:
            # 保存当前最高准确度
            best_acc = val_acc_all[-1]
            # 保存当前最高准确度的模型参数
            best_model_wts = copy.deepcopy(model.state_dict())

        # 计算训练和验证的耗时
        time_use = time.time() - since
        print("训练和验证耗费的时间{:.0f}m{:.0f}s".format(time_use//60, time_use%60))

    # 选择最优参数,保存最优参数的模型
    torch.save(best_model_wts, "E:\秋招就业\CNN卷积神经网络\测试用例\LeNet\\best_model.pth")

    train_process = pd.DataFrame(data={"epoch":range(num_epochs),
                                       "train_loss_all":train_loss_all,
                                       "val_loss_all":val_loss_all,
                                       "train_acc_all":train_acc_all,
                                       "val_acc_all":val_acc_all,})

    return train_process

可视化打印

ef matplot_acc_loss(train_process):
    # 显示每次迭代后的训练集和验证集的损失与准确率
    plt.figure(figsize=(12, 4))  # 创建一个宽 12、高 4 的画布

    # 第一个子图:训练与验证的损失函数变化
    plt.subplot(1, 2, 1)  # 第 1 行、第 1 列、第 1 个子图
    plt.plot(train_process['epoch'], train_process.train_loss_all, "ro-", label="Train loss")  # 绘制训练损失曲线
    plt.plot(train_process['epoch'], train_process.val_loss_all, "bs-", label="Val loss")  # 绘制验证损失曲线
    plt.legend()  # 显示图例
    plt.xlabel("epoch")  # x 轴标签
    plt.ylabel("Loss")  # y 轴标签

    # 第二个子图:训练与验证的准确率变化
    plt.subplot(1, 2, 2)  # 第 1 行、第 2 列、第 2 个子图
    plt.plot(train_process['epoch'], train_process.train_acc_all, "ro-", label="Train acc")  # 绘制训练准确率曲线
    plt.plot(train_process['epoch'], train_process.val_acc_all, "bs-", label="Val acc")  # 绘制验证准确率曲线
    plt.xlabel("epoch")  # x 轴标签
    plt.ylabel("Accuracy")  # y 轴标签
    plt.legend()  # 显示图例

    plt.show()  # 显示绘制的图像

if __name__ == '__main__':
    # 加载需要的模型
    LeNet = LeNet()
    # 加载数据集
    train_data, val_data = train_val_data_process()
    # 利用现有的模型进行模型的训练
    train_process = train_model_process(LeNet, train_data, val_data, num_epochs=20)
    matplot_acc_loss(train_process)

训练结果

左边错误率,右边正确率

Epoch 0/19
----------
0 train loss:0.9156 train acc: 0.6530
0 val loss:0.6296 val acc: 0.7583
训练和验证耗费的时间0m24s
Epoch 1/19
----------
1 train loss:0.5498 train acc: 0.7897
1 val loss:0.5201 val acc: 0.8092
训练和验证耗费的时间0m46s
Epoch 2/19
----------
2 train loss:0.4720 train acc: 0.8251
2 val loss:0.4770 val acc: 0.8291
训练和验证耗费的时间1m8s
Epoch 3/19
----------
3 train loss:0.4265 train acc: 0.8422
3 val loss:0.4196 val acc: 0.8462
训练和验证耗费的时间1m31s
Epoch 4/19
----------
4 train loss:0.3963 train acc: 0.8534
4 val loss:0.3953 val acc: 0.8558
训练和验证耗费的时间1m53s
Epoch 5/19
----------
5 train loss:0.3690 train acc: 0.8626
5 val loss:0.3777 val acc: 0.8602
训练和验证耗费的时间2m16s
Epoch 6/19
----------
6 train loss:0.3500 train acc: 0.8687
6 val loss:0.3656 val acc: 0.8663
训练和验证耗费的时间2m38s
Epoch 7/19
----------
7 train loss:0.3332 train acc: 0.8743
7 val loss:0.3465 val acc: 0.8734
训练和验证耗费的时间2m60s
Epoch 8/19
----------
8 train loss:0.3180 train acc: 0.8819
8 val loss:0.3365 val acc: 0.8747
训练和验证耗费的时间3m22s
Epoch 9/19
----------
9 train loss:0.3078 train acc: 0.8842
9 val loss:0.3389 val acc: 0.8735
训练和验证耗费的时间3m44s
Epoch 10/19
----------
10 train loss:0.2982 train acc: 0.8871
10 val loss:0.3327 val acc: 0.8801
训练和验证耗费的时间4m8s
Epoch 11/19
----------
11 train loss:0.2879 train acc: 0.8919
11 val loss:0.3068 val acc: 0.8852
训练和验证耗费的时间4m31s
Epoch 12/19
----------
12 train loss:0.2786 train acc: 0.8954
12 val loss:0.3040 val acc: 0.8870
训练和验证耗费的时间4m54s
Epoch 13/19
----------
13 train loss:0.2704 train acc: 0.8978
13 val loss:0.3089 val acc: 0.8881
训练和验证耗费的时间5m18s
Epoch 14/19
----------
14 train loss:0.2629 train acc: 0.9015
14 val loss:0.2877 val acc: 0.8935
训练和验证耗费的时间5m41s
Epoch 15/19
----------
15 train loss:0.2547 train acc: 0.9033
15 val loss:0.2968 val acc: 0.8901
训练和验证耗费的时间6m5s
Epoch 16/19
----------
16 train loss:0.2508 train acc: 0.9057
16 val loss:0.2915 val acc: 0.8921
训练和验证耗费的时间6m28s
Epoch 17/19
----------
17 train loss:0.2440 train acc: 0.9080
17 val loss:0.3284 val acc: 0.8809
训练和验证耗费的时间6m51s
Epoch 18/19
----------
18 train loss:0.2381 train acc: 0.9102
18 val loss:0.2834 val acc: 0.8954
训练和验证耗费的时间7m13s
Epoch 19/19
----------
19 train loss:0.2318 train acc: 0.9125
19 val loss:0.2842 val acc: 0.8950
训练和验证耗费的时间7m37s

模型测试

测试模型搭建逻辑

  • 老师加载学生的答案
    • 设备选择(GPU或CPU):老师选择在哪个地方(教室或办公室)批改试卷。如果有更多的资源(GPU),可以更快地批改;否则,就用现有的资源(CPU)
    • 模型移动到设备上:类似于老师将试卷放到指定的位置开始批改
    • 初始化计数器:老师开始计数正确和总的试题数
  • 批改数据(循环遍历测试数据)
    • 禁用梯度计算:就像老师在批改试卷时只关注答案,不需要反复修改试卷内容
    • 循环遍历试卷
      • 数据迁移:老师将试卷放到批改台上
      • 评估模式:老师保持客观,不带情绪地批改试卷
      • 前向传播:老师查看学生的答案
      • 预测标签:老师判断学生的答案是否正确
      • 计算正确数量:如果答案正确,计数器加1
      • 更新总数:统计总共批改了多少份试卷
  • 最后计算准确率

具体实现


def test_data_process():
    test_data = FashionMNIST(root='./data',
                             train=False,
                             transform=transforms.Compose([transforms.Resize(size=28), transforms.ToTensor()]),
                             download=True)

    test_dataloader = Data.DataLoader(dataset=test_data,
                                       batch_size=1,
                                       shuffle=True,
                                       num_workers=0)
    return test_dataloader

def test_model_process(model, test_dataloader):
    # 设定测试所用到的设备,有GPU用GPU没有GPU用CPU
    device = "cuda" if torch.cuda.is_available() else 'cpu'

    # 将模型放入到测试设备中
    model = model.to(device)

    # 初始化参数
    test_corrects = 0.0
    test_num = 0

    # 只进行前向传播计算,不计算梯度,从而节省内存,加快运行速度
    with torch.no_grad():
        for test_data_x, test_data_y in test_dataloader:
            # 将特征放入到测试设备中
            test_data_x = test_data_x.to(device)
            # 将标签放入到测试设备中
            test_data_y = test_data_y.to(device)
            # 设置模型为评估模式
            model.eval()
            # 前向传播过程,输入为测试数据集,输出为对每个样本的预测值
            output = model(test_data_x)
            # 查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output, dim=1)
            # 如果预测正确,则准确度test_corrects加1
            test_corrects += torch.sum(pre_lab == test_data_y.data)
            # 将所有的测试样本进行累加
            test_num += test_data_x.size(0)

    # 计算测试准确率
    test_acc = test_corrects.double().item() / test_num
    print("测试的准确率为:", test_acc)

if __name__=="__main__":
    # 加载模型
    model = LeNet()
    model.load_state_dict(torch.load('best_model.pth'))
    # 加载测试数据
    test_dataloader = test_data_process()
    # 加载模型测试的函数
    test_model_process(model, test_dataloader)





测试结果

如果想要提供精度,提升训练轮数即可

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

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

相关文章

ubuntu20.04复现 Leg-KILO

这里写目录标题 opencv版本问题下载3.2.0源代码进入解压后的目录创建构建目录运行 CMake 配置 配置时指定一个独立的安装目录,例如 /opt/opencv-3.2:出错: 使用多线程编译错误1: stdlib.h: 没有那个文件或目录错误2:er…

python学习笔记—7—变量拼接

1. 字符串的拼接 print(var_1 var_2) print("supercarry" "doinb") name "doinb" sex "man" score "100" print("sex:" sex " name:" name " score:" score) 注意: …

Redis - 消息队列 Stream

一、概述 消息队列 定义 消息队列模型:一种分布式系统中的消息传递方案,由消息队列、生产者和消费者组成消息队列:负责存储和管理消息的中间件,也称为消息代理(Message Broker)生产者:负责 产…

3D 生成重建034-NerfDiff借助扩散模型直接生成nerf

3D 生成重建034-NerfDiff借助扩散模型直接生成nerf 文章目录 0 论文工作1 论文方法2 实验结果 0 论文工作 感觉这个论文可能能shapE差不多同时期工作,但是shapE是生成任意种类。 本文提出了一种新颖的单图像视图合成方法NerfDiff,该方法利用神经辐射场 …

聊聊Oracle自适应查询优化

成也AQO败也AQO 因为工作的原因,我们接触到的客户大部分是金融和运营商行业,这些客户有个最大的特点是追求稳定,对于使用数据库新特性持保守的态度,不会轻易尝试某些可能会导致生产系统不稳定的新特性。上线前通常都会将一些新特…

【数据库】选择题+填空+简答

1.关于冗余数据的叙述中,不正确的是() A.冗余的存在容易破坏数据库的完整新 B.冗余的存在给数据库的维护增加困难 C.不应该在数据库中存储任何冗余数据 D.冗余数据是指由基本数据导出的数据 C 2.最终用户使用的数据视图称为(&…

Comparator.comparing 排序注意

1. 对数字型字符串排序 List<String> values new ArrayList<>();values.add("10");values.add("6");values.add("20");values.add("30");values.add("50");//方法1 &#xff08;正确的排序方法&#xff09;//倒…

R语言的数据结构-矩阵

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言医学数据分析实践-R语言的数据结构-CSDN博客 矩阵是一个二维数组&#xff0c;矩阵中的元素都具有相…

动态分区存储管理

一、实验目的 目的&#xff1a;熟悉并掌握动态分区分配的各种算法&#xff0c;熟悉并掌握动态分区中分区回收的各种情况&#xff0c;并能够实现分区合并。 任务&#xff1a;用高级语言模拟实现动态分区存储管理。 二、实验内容 1、实验内容 分区分配算法至少实现首次适应算法、…

JPG 转 PDF:免费好用的在线图片转 PDF 工具

JPG 转 PDF&#xff1a;免费好用的在线图片转 PDF 工具 在日常工作和生活中&#xff0c;我们经常需要将图片转换为 PDF 格式。无论是制作电子文档、准备演示材料&#xff0c;还是整理照片集&#xff0c;将图片转换为 PDF 都是一个常见的需求。今天为大家介绍一款完全免费、无需…

10、C++继承2

本章介绍菱形继承、虚继承和类型适应。 1、菱形继承与虚继承 在多继承中&#xff0c;可能会发生多个父类共基类的问题&#xff0c;即菱形继承。 例如&#xff1a; 解决办法&#xff1a; 父类继承基类时使用虚继承&#xff0c;在继承前加virtual&#xff0c;即&#xff1a;虚…

探索Telnet:实现Windows远程登录Ubuntu的实践指南

前言 在互联网技术日新月异的今天&#xff0c;远程登录已经成为许多开发者和系统管理员日常工作中不可或缺的一部分。虽然SSH已经成为远程登录的首选协议&#xff0c;但了解并掌握Telnet这一经典协议仍然具有重要意义。本文将带您一起探索如何使用Telnet实现Windows远程登录Ub…

字符串的常见操作【C语言】

一、案例内容 字符串的常见操作主要有创建&#xff0c;输出&#xff0c;查找指定字符或查找指定位置的字符、删除指定字符或删除指定位置的字符&#xff0c;在指定位置插入指定字符等。 二、案例代码 #include <stdio.h> #include <stdlib.h> #include <stri…

axios的引入和基本使用

一、axios的引入 使用 pnpm add axios 二、使用axios 三、axios的使用方法补充 axios除了直接使用它实例上的方法&#xff0c;还可以通过配置的方式进行使用axios({})&#xff0c;传入一个对象&#xff0c;这个对象可以有如下属性&#xff1a; url&#xff08;字符串&#…

Dart 3.6 发布,workspace 和 Digit separators

workspace 之前我们就聊过 Flutter 正在切换成 Monorepo 和支持 workspaces &#xff0c;Dart 3.6 开始&#xff0c;Pub 现在正式支持 monorepo 或 workspace 中 package 之间的共享解析。 pub workspaces 功能可确保 monorepo 中的 package 共享一组一致的依赖项&#xff0c…

三、nginx实现lnmp+discuz论坛

lnmp l&#xff1a;linux操作系统 n&#xff1a;nginx前端页面 m&#xff1a;mysql数据库&#xff0c;账号密码&#xff0c;数据库等等都保存在这个数据库里面 p&#xff1a;php——nginx擅长处理的是静态页面&#xff0c;页面登录账户&#xff0c;需要请求到数据库&#…

【MFC】如何读取rtf文件并进行展示

tf是微软的一个带格式的文件&#xff0c;比word简单&#xff0c;我们可以用写字板等程序打开编辑。下面以具体实例讲解如何在自己程序中展示rtf文件。 首先使用VS2022创建一个MFC的工程。 VIEW类需要选择richview类&#xff0c;用于展示&#xff0c;如下图&#xff1a; 运行效…

AudioSegment 将音频分割为指定长度时间片段 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

【新人系列】Python 入门(十六):正则表达式

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12801353.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Python 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…

Leetcode 每日一题9.回文数

&#x1f308;&#x1f308;&#x1f308;今天给大家分享的是:回文数的解法 目录 ​编辑 问题描述 输入输出格式 示例 约束条件 进阶挑战 解决方案 问题分析 过题图片 字符串转换法 数学方法 算法解释 题目链接 结论 问题描述 给定一个整数 x&#xff0c;我们需要…