当前位置: 首页 > news >正文

影业的网站怎么做注册公司名字查询系统

影业的网站怎么做,注册公司名字查询系统,黄山旅游攻略ppt,wordpress 简繁摘要 本文分类使用的是resNet34,什么不用yolo v8#xff0c;yolo v10系列,虽然他们也可以分类#xff0c;因为yolo系列模型不纯粹#xff0c;里面包含了目标检测的架构#xff0c;所以分类使用的是resNet 本文详细介绍了三种不同的方法来训练卷积神经网络进行 CIFAR-10 图…摘要 本文分类使用的是resNet34,什么不用yolo v8yolo v10系列,虽然他们也可以分类因为yolo系列模型不纯粹里面包含了目标检测的架构所以分类使用的是resNet 本文详细介绍了三种不同的方法来训练卷积神经网络进行 CIFAR-10 图像分类任务帮助读者从零开始学习如何设计和优化深度学习模型。首先我们通过手工设计一个卷积神经网络并进行训练讲解每个层次的作用和设计思想接着我们使用一个开源模型 ResNet34并对其进行微调以适应我们的数据集最后我们通过一系列高效的训练技巧显著提高训练效率减少训练时间。最终您将不仅能实现这一目标而且能够举一反三设计自己的模型提升模型效率。每个批次由原来的111秒变成12秒准确率最终达到了90.24 %。 引言 在深度学习的世界中图像分类任务无疑是最经典且最具挑战性的任务之一。CIFAR-10 数据集作为经典的图像分类数据集包含了10个类别的32x32像素彩色图像广泛用于模型训练和测试。今天我们将介绍如何使用三种不同的方法来训练卷积神经网络CNN以完成 CIFAR-10 的分类任务。 本文的目标不仅是教会你如何实现这些方法更重要的是让你理解背后的设计思想以及如何将这些思想转化为你自己的模型。通过阅读本文你将掌握设计卷积神经网络、使用开源模型以及进行高效训练的能力。 目录 第一部分手工设计卷积神经网络 1.1 网络结构设计思路1.2 训练与验证模型1.3 训练效果与分析 第二部分使用开源模型——ResNet34 2.1 为什么选择 ResNet342.2 如何微调 ResNet34 适应 CIFAR-102.3 训练与验证模型2.4 训练效果与分析 第三部分高效训练技巧 3.1 为什么需要优化训练效率3.2 高效训练技巧与代码优化3.3 训练效果与分析 总结与展望 第一部分手工设计卷积神经网络 在本部分我们将手工设计一个卷积神经网络用于进行 CIFAR-10 图像分类。我们将会从设计网络结构开始逐步增加网络层并讲解每一层的作用。 1.1 网络结构设计思路 卷积神经网络CNN通常由以下几部分组成 卷积层用于提取图像的特征。通过卷积核或滤波器对输入图像进行卷积运算得到特征图。激活层使用非线性激活函数如 ReLU来增加网络的表达能力。池化层对卷积后的特征图进行下采样减小数据维度保留重要特征。全连接层将卷积和池化操作提取的特征映射到最终的分类输出。Dropout用于防止过拟合。 根据这些部分我们设计了一个简单的卷积神经网络包含两层卷积层、两层全连接层、以及 Dropout。 1.2 训练与验证模型 使用 PyTorch 框架我们构建了卷积神经网络并加载了 CIFAR-10 数据集。下面是训练过程的代码(完整代码在最后面此处只展示相关代码) import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision.datasets import CIFAR10 from torchvision.transforms import ToTensor, Compose import timeclass ImageClassification(nn.Module):def __init__(self):super(ImageClassification, self).__init__()# --------------------# 卷积层1输入通道3(RGB)输出通道6卷积核3x3# 主要用来提取图像初步特征# --------------------self.conv1 nn.Conv2d(in_channels3, out_channels6, kernel_size3)# --------------------# BatchNorm1对6个通道进行批归一化# 可以加速收敛、稳定训练# --------------------self.bn1 nn.BatchNorm2d(num_features6)# --------------------# MaxPool层核大小2x2步幅2# 进行下采样减小特征图空间尺寸# --------------------self.pool nn.MaxPool2d(kernel_size2, stride2)# --------------------# 卷积层2输入通道6输出通道16卷积核3x3# 继续提取更深层次特征# --------------------self.conv2 nn.Conv2d(in_channels6, out_channels16, kernel_size3)# --------------------# BatchNorm2对16个通道进行批归一化# --------------------self.bn2 nn.BatchNorm2d(num_features16)# --------------------# 全连接层1输入57616通道 * 6 * 6 输出120# 提取卷积层输出的全局特征# --------------------self.fc1 nn.Linear(in_features576, out_features120)# --------------------# 全连接层2输入120 输出84# 进一步映射到更低维空间# --------------------self.fc2 nn.Linear(in_features120, out_features84)# --------------------# 全连接层3输入84 输出10# 最终分类层对应CIFAR10的10个类别# --------------------self.fc3 nn.Linear(in_features84, out_features10)# --------------------# Dropout在全连接层后随机失活一些神经元# 防止过拟合# --------------------self.dropout nn.Dropout(p0.5)def forward(self, x):# x shape: (batch_size, 3, 32, 32)# 第一次卷积 批归一化 ReLU MaxPool# 结果 shape: (batch_size, 6, 14, 14)x self.pool(torch.relu(self.bn1(self.conv1(x))))# 第二次卷积 批归一化 ReLU MaxPool# 结果 shape: (batch_size, 16, 6, 6)x self.pool(torch.relu(self.bn2(self.conv2(x))))# 展平 (batch_size, 16*6*6576)x x.view(-1, 576)# 全连接层1 ReLU Dropoutx torch.relu(self.fc1(x))x self.dropout(x)# 全连接层2 ReLU Dropoutx torch.relu(self.fc2(x))x self.dropout(x)# 最后全连接层 输出10维(对应CIFAR10的10个类别)x self.fc3(x)return x 主要流程 1. 两次卷积 批归一化 ReLU 池化 提取特征并减小特征图空间尺寸。 2. Flatten 将卷积层输出展平为一维向量方便进入全连接层。 3. 全连接层 Dropout 将特征映射到分类空间。Dropout 随机失活部分神经元减少过拟合。 4. 输出 10 类 对应 CIFAR-10 的 10 个类别0~9。 1.3 训练效果与分析 经过100个epoch的训练我们的网络达到了约64.85%的准确率。尽管这是一个不错的起点但显然可以进一步提高网络的表现。因此我们决定使用开源模型 ResNet34 来尝试提升准确率。 第二部分使用开源模型——ResNet34 在这一部分我们将展示如何使用 ResNet34 这一经过验证的模型并通过微调使其适应我们的 CIFAR-10 数据集。 2.1 为什么选择 ResNet34 ResNet34 是一个深度神经网络由多个残差模块Residual Blocks组成。与传统的 CNN 相比ResNet34 能够通过跳跃连接skip connections解决深度网络中的梯度消失问题从而使模型能够在非常深的层数中仍然保持有效的学习能力。 2.2 如何微调 ResNet34 适应 CIFAR-10 ResNet34 在默认情况下是为 ImageNet 数据集设计的因此我们需要对其进行一些修改以适应 CIFAR-10 数据集。具体来说我们将调整第一层卷积层和最后的全连接层。 import torchvision.models as models # 导入 torchvision 模型模块便于加载 ResNet34 模型 import torch.nn as nn # 导入 torch.nn 模块用于构建和修改神经网络层def get_resnet34_cifar10():# 加载 ResNet34 模型注意这里不使用预训练权重pretrainedFalse# 因为我们可能希望在 CIFAR-10 数据集上从头微调模型或者你想使用自己的预训练权重model models.resnet34(pretrainedFalse)# 修改第一层卷积层conv1# 原始 ResNet34 中的 conv1 是一个 7x7 卷积stride2这会使 32x32 的 CIFAR-10 图像尺寸迅速减小# 因此我们将其替换为 3x3 卷积stride1padding1以保持图像尺寸更稳定便于后续的特征提取model.conv1 nn.Conv2d(in_channels3, # 输入通道数为3RGB图像out_channels64, # 输出通道数为64与原始网络保持一致kernel_size3, # 使用 3x3 卷积核stride1, # 步幅设为1不进行下采样padding1, # 填充1保证卷积后输出尺寸不变biasFalse # 不使用偏置项后续的 BatchNorm 会处理偏置问题)# 修改最后一层全连接层fc# 原始 ResNet34 的 fc 层用于 ImageNet 分类其输出类别数为1000# 这里我们将其替换为适用于 CIFAR-10 的全连接层输出类别数设置为10model.fc nn.Linear(in_featuresmodel.fc.in_features, # 保持原 fc 层输入特征数不变out_features10 # 输出特征数设为10匹配 CIFAR-10 的类别数量)# 返回修改后的模型return model 代码说明 1. 导入模块 • torchvision.models用于加载各种预定义的模型架构如 ResNet34。 • torch.nn提供构建和修改神经网络层所需的类和函数。 2. 加载 ResNet34 模型,但没有使用模型的网络权重 使用 models.resnet34(pretrainedFalse) 加载 ResNet34 模型不使用预训练权重因为 CIFAR-10 图像尺寸较小或你可能希望从头微调模型。 3. 修改第一层卷积 • 原始 ResNet34 的第一层卷积层使用的是 7x7 卷积核和 stride2会使输入图像CIFAR-10 的 32x32 图像尺寸大幅度缩小。 • 为了适应 CIFAR-10 的图像尺寸我们使用 3x3 卷积核、stride1 和 padding1这样可以保持特征图尺寸相对稳定有利于后续特征提取。 4. 修改全连接层 • 原模型的全连接层输出 1000 个类别这对应于 ImageNet 数据集。 • 将其修改为输出 10 个类别适应 CIFAR-10 数据集的分类任务。 通过这些修改这个函数返回一个经过定制化调整后的 ResNet34 模型适合用来处理 CIFAR-10 图像分类任务。 2.3 训练与验证模型 在微调完成后我们就可以使用 ResNet34 来进行训练。训练过程类似于代码1中的流程但我们直接使用开源模型并做出适应性调整。 2.4 训练效果与分析 使用 ResNet34 后我们发现训练时间显著提高每个批次需要112秒这么算100批次就要训练3小时。虽然准确率有所提升但我们希望通过进一步优化训练过程来提高训练效率。因此直接手动kill  掉了训练过程下面是我们的优化过程 第三部分高效训练技巧 现在我们已经有了一个效果较好的模型ResNet34。但是训练一个大型网络如 ResNet34可能需要很长时间。为了提高训练效率我们在这一部分介绍了几种高效训练的技巧。 3.1 为什么需要优化训练效率 在使用 GPU 训练时尽管我们的 GPUV100具有强大的计算能力但如果训练过程中的数据加载、内存传输等环节没有得到优化GPU 的计算能力将得不到充分发挥。 3.2 高效训练技巧与代码优化 为了优化训练我们采用了以下几种方法 增加 num_workers 和 pin_memory通过多线程加速数据加载同时使用 pin_memory 将数据从主机内存直接传输到 GPU减少数据传输的时间。混合精度训练通过 torch.cuda.amp 进行混合精度训练这可以在不损失精度的情况下提高计算效率。调整批量大小Batch Size通过增加批量大小来提高 GPU 的利用率。 # 创建训练集和验证集的 DataLoader # DataLoader 用于按批次加载数据加快训练过程并支持多线程数据预处理 train_loader DataLoader(train_dataset, # 训练数据集batch_size128, # 每个批次加载128张图像shuffleTrue, # 每个epoch开始前随机打乱数据增强模型泛化能力num_workers8, # 使用8个子进程并行加载数据可根据CPU核心数调整pin_memoryTrue # 将数据加载到锁页内存中方便快速转移到GPU )valid_loader DataLoader(valid_dataset, # 验证数据集batch_size128, # 每个批次加载128张图像shuffleFalse, # 验证时不需要打乱数据保持顺序便于评估num_workers8, # 同样使用8个子进程加速加载pin_memoryTrue # 使用锁页内存提高数据传输速度 )# 初始化混合精度训练的梯度缩放器 # 混合精度训练Automatic Mixed Precision, AMP可以使用float16加速训练并降低显存占用 scaler torch.cuda.amp.GradScaler()# 开始训练过程遍历指定的训练轮数EPOCHS for epoch in range(EPOCHS):model.train() # 切换模型到训练模式启用Dropout和BatchNorm的训练行为for x, y in train_loader:# 将优化器中的梯度清零防止梯度累加optimizer.zero_grad()# 自动混合精度上下文启用 float16 运算以提高效率with torch.cuda.amp.autocast(device_typedevice.type):output model(x) # 前向传播计算模型输出loss criterion(output, y) # 计算损失比较模型输出和真实标签# 通过梯度缩放器将损失值进行缩放后反向传播防止梯度溢出scaler.scale(loss).backward()# 使用梯度缩放器更新参数结合自动混合精度运算的结果scaler.step(optimizer)# 更新梯度缩放器状态为下一个迭代做准备scaler.update() 详细说明 1. DataLoader 配置 • batch_size128每个批次包含128张图片有助于充分利用GPU的并行计算能力。 • shuffleTrue (训练集)在每个epoch开始前打乱数据防止模型记住数据顺序提高泛化能力。验证集通常不打乱。 • num_workers8开启8个工作进程来加载数据加快数据预处理和加载速度。 • pin_memoryTrue将加载的数据存放在页锁定内存中以便更快地传输到GPU中。 2. 混合精度训练AMP • torch.cuda.amp.GradScaler()初始化梯度缩放器用于在混合精度训练时动态调整梯度缩放因子防止数值不稳定。 • with torch.cuda.amp.autocast()在此上下文中部分计算会自动采用半精度float16运算既加快了计算速度又降低显存消耗。 3. 训练循环 • optimizer.zero_grad()清零梯度防止前一次梯度累加。 • 模型前向传播将输入数据传入模型得到输出。 • 计算损失使用损失函数如交叉熵损失比较模型输出与真实标签。 • 梯度反向传播和更新通过梯度缩放器缩放损失后反向传播再通过 scaler.step(optimizer) 更新模型参数最后更新 scaler 的状态以适应下一次计算。 这样设置可以充分利用混合精度训练的优势提高训练速度并降低显存占用同时保证模型的数值稳定性。 3.3 训练效果与分析 经过这些优化我们的训练时间从每个批次 111 秒缩短至 12 秒由原来的3小时变成了20分钟显著提高了训练效率。从而且准确率保持在了 87.5%。 第四部分使用预训练权重 4.1 为什么需要ImageNet的原始权重 对于 CIFAR-10 这种数据集相对较小且与 ImageNet 有一定相似性大多数情况下建议使用预训练权重。使用预训练权重的优势主要有 • 加速收敛预训练模型已经学到了一些通用的低级特征如边缘、纹理等在微调时可以更快达到较好的效果。 • 更高的准确率特别是在数据量不足以从头训练深层网络时预训练权重可以帮助避免过拟合并提高最终的分类准确率。 • 稳定性更好预训练模型在大规模数据集如 ImageNet上经过充分训练其特征具有较好的泛化性迁移到 CIFAR-10 上通常能获得较稳定的性能。 当然如果你有足够的数据或你希望针对特定领域做更深入的调整也可以考虑从头训练但这往往需要更多的计算资源和更长的训练时间。 综上所述我的建议是对于 CIFAR-10 等常见数据集建议使用原始的 ImageNet 预训练权重然后进行微调。这通常能带来更好的效果和更快的训练收敛。 4.2 使用ImageNet的原始权重代码 import torchvision.models as models # 导入 torchvision 中的预定义模型 import torch.nn as nn # 导入 torch.nn 模块用于构建和修改网络层def get_resnet34_cifar10():构建适用于 CIFAR-10 图像分类任务的 ResNet34 模型。该函数主要做了以下三处修改1) 修改第一个卷积层conv1将原来的 7x7 卷积替换成 3x3 卷积并设置 stride1 和 padding1以防止 CIFAR-10 图像32×32的尺寸被过度缩小。2) 修改最大池化层maxpool将其替换成 Identity()也就是不做任何池化操作。3) 修改最后全连接层fc将输出类别数从原来的 1000 改为 10因为 CIFAR-10 有 10 类。# 使用 torchvision 官方提供的 ResNet34 模型并加载在 ImageNet 1K 数据集上预训练的权重# 这里使用的是 weightsmodels.ResNet34_Weights.IMAGENET1K_V1net models.resnet34(weightsmodels.ResNet34_Weights.IMAGENET1K_V1)# weightsmodels.ResNet34_Weights.IMAGENET1K_V1 的解释# - models.ResNet34_Weights 是一个枚举类其中定义了 ResNet34 模型可用的预训练权重选项。# - IMAGENET1K_V1 是该枚举中的一个具体成员表示在 ImageNet 1K 数据集上训练得到的第一个版本的预训练权重。# 使用这个权重可以让模型初始状态已经学到大量通用的视觉特征# 这对于迁移学习和微调任务例如 CIFAR-10 分类非常有帮助。# 修改第一层卷积层# 原始 ResNet34 的 conv1 层默认使用的是 7x7 卷积核stride2# 这对于尺寸只有32x32的 CIFAR-10 图像来说会过于激进导致特征图尺寸急剧减小# 所以这里我们使用一个 3x3 的卷积核并设置 stride1 和 padding1# 这样可以更好地保留图像的细节信息。net.conv1 nn.Conv2d(in_channels3, # 输入通道数为3RGB图像out_channels64, # 输出通道数为64保持与原模型一致kernel_size3, # 使用 3x3 的卷积核stride1, # 步幅设为1不进行下采样padding1, # 填充1保证卷积后输出尺寸与输入相同biasFalse # 不使用偏置项通常和 BatchNorm 搭配时可省略偏置)# 修改 maxpool 层# 原始模型中 conv1 后接有一个 3x3 的最大池化层stride2# 用于快速减小特征图尺寸但对于小尺寸图像如32x32来说可能导致信息丢失# 因此这里将 maxpool 替换为 Identity() 层即直接将输入原样传递不做池化操作。net.maxpool nn.Identity()# 修改全连接层# 原始 ResNet34 的 fc 层输出 1000 维向量对应 ImageNet 1000 个类别# 这里我们将其替换为一个新的全连接层使得输出类别数变为 10# 以适应 CIFAR-10 数据集的分类任务。net.fc nn.Linear(in_featuresnet.fc.in_features, # 保持输入特征数不变通常为512out_features10, # 输出10个类别的概率biasTrue # 使用偏置项)# 返回经过修改的模型return net 详细讲解 1. 预训练权重的选择 • 使用 weightsmodels.ResNet34_Weights.IMAGENET1K_V1 可以加载在 ImageNet 1K 数据集上预训练好的权重。 • 为什么选择这个 • 预训练优势ImageNet 数据集包含百万级图像和1000个类别模型在此数据上学到了大量通用的视觉特征如边缘、纹理等这些特征在其他图像分类任务中例如 CIFAR-10也非常有效。 • 加速收敛预训练模型提供了一个较好的初始化状态微调时可以更快达到较优性能避免从头训练带来的长时间收敛问题。 • 稳定性预训练权重通常经过大量数据训练具有良好的泛化能力在数据量较少的 CIFAR-10 上使用可以减少过拟合风险。 2. 为什么修改第一层卷积层 • 原始 ResNet34 的第一层是 7x7 卷积stride2这对 ImageNet224×224这样的高分辨率图像效果很好但对于 32×32 的 CIFAR-10 图像来说会使得特征图尺寸过小从而丢失很多细节信息。 • 将卷积核修改为 3x3、stride1 和 padding1 后可以保持较多原始信息有助于在后续层中提取更有区分度的特征。 3. 为什么去掉 MaxPool 层 • 在原始 ResNet 中conv1 后的池化层用于进一步减小特征图尺寸。但对于 CIFAR-10 这种低分辨率图像过早下采样会使得信息量严重减少。 • 用 nn.Identity() 替换 maxpool 层即让数据原样传递不做任何池化这样可以保留更多细节有助于分类任务。 4. 修改全连接层的原因 • 原始 ResNet34 的全连接层输出 1000 个类别用于 ImageNet 分类。 • CIFAR-10 数据集只有 10 个类别所以需要将 fc 层的输出维度调整为 10以适应具体任务。 4.3 训练效果与分析 经过使用原始权重这些优化我们的训练时间依旧 12 秒但是显著提高了准确率。准确率保持在了 90.24 %。 因此90.24 %准确率的分类该分类模型已经完成了产品的需求是可以直接应用市场的了 4.3 关于应用自己训练或他人训练的模型权重 使用自己训练的模型权重 • 假设你已经使用类似的网络结构训练了一个模型并将权重保存到了一个文件例如 my_weights.pth你可以这样加载并应用权重 # 加载自己训练好的权重文件 state_dict torch.load(my_weights.pth, map_locationdevice) model get_resnet34_cifar10() # 构建模型架构 model.load_state_dict(state_dict) # 将保存的权重加载到模型中 • 这里需要注意如果你保存的是整个模型包括结构和权重使用 torch.load 直接加载可能需要调用 torch.load(my_model.pth)但通常建议只保存权重state_dict这样更灵活。 使用他人训练的模型权重也是一样的 • 如果你下载了他人训练好的权重文件流程与上面类似 state_dict torch.load(downloaded_weights.pth, map_locationdevice) model get_resnet34_cifar10() # 构建模型架构 model.load_state_dict(state_dict) # 加载他人训练好的权重 • 确保你下载的权重文件与模型结构完全一致否则可能出现加载错误。如果权重来自不同的版本或结构需要先做适配。 通过这种方式无论是使用自己训练的权重还是下载的权重你都可以将预训练好的参数加载到模型中进行微调或直接推理。 完整代码全过程 代码1手工设计卷积神经网络分类模型   import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms import ToTensor, Compose import torch.optim as optim from torch.utils.data import DataLoader import time import matplotlib.pyplot as plt from torchsummary import summary# 配置字体参数 plt.rcParams[font.family] sans-serif plt.rcParams[font.sans-serif] [Noto Sans CJK SC, DejaVu Sans] plt.rcParams[axes.unicode_minus] False# 额外导入用于混淆矩阵可视化 import numpy as np from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# 全局超参数 BATCH_SIZE 8 # 批量大小 EPOCHS 100 # 训练轮数演示时设置10实际可设置更多 LEARNING_RATE 1e-3 # 学习率def create_dataset():加载 CIFAR10 数据集并返回训练集和测试集。数据集下载路径: rootdata这里使用 ToTensor() 将图片转为 PyTorch 张量。Returns:train (Dataset): 训练集valid (Dataset): 测试集train CIFAR10(rootdata,trainTrue,transformCompose([ToTensor()]),downloadTrue)valid CIFAR10(rootdata,trainFalse,transformCompose([ToTensor()]),downloadTrue)return train, validclass ImageClassification(nn.Module):该卷积神经网络用于对 CIFAR10 数据集进行分类。输入: 3 通道图像 (3, 32, 32)输出: 10 类别网络结构加入 BatchNorm 后:- Conv1: 输入通道3, 输出通道6, 卷积核3x3- BatchNorm1: 对6个通道进行归一化- ReLU- MaxPool1: 核大小2x2, 步长2- Conv2: 输入通道6, 输出通道16, 卷积核3x3- BatchNorm2: 对16个通道进行归一化- ReLU- MaxPool2: 核大小2x2, 步长2- Flatten 全连接层- Linear1: 输入576 - 输出120- ReLU- Dropout (防止过拟合)- Linear2: 输入120 - 输出84- ReLU- Dropout- Output: 输入84 - 输出10def __init__(self):super(ImageClassification, self).__init__()# 第一个卷积层self.conv1 nn.Conv2d(in_channels3, out_channels6, kernel_size3, stride1)# 第一个批归一化层self.bn1 nn.BatchNorm2d(num_features6)# 第一个池化层self.pool1 nn.MaxPool2d(kernel_size2, stride2)# 第二个卷积层self.conv2 nn.Conv2d(in_channels6, out_channels16, kernel_size3, stride1)# 第二个批归一化层self.bn2 nn.BatchNorm2d(num_features16)# 第二个池化层self.pool2 nn.MaxPool2d(kernel_size2, stride2)# 全连接层前特征图大小 16通道 * 6 * 6 576self.linear1 nn.Linear(in_features576, out_features120)self.dropout1 nn.Dropout(p0.5) # 添加 Dropoutself.linear2 nn.Linear(in_features120, out_features84)self.dropout2 nn.Dropout(p0.5) # 添加 Dropoutself.out nn.Linear(in_features84, out_features10)def forward(self, x):前向传播流程:1) Conv1 - BatchNorm1 - ReLU - Pool12) Conv2 - BatchNorm2 - ReLU - Pool23) Flatten4) Linear1 - ReLU - Dropout5) Linear2 - ReLU - Dropout6) 输出层# 第一次卷积x self.conv1(x)# 加入 BatchNorm1 归一化x self.bn1(x)# 激活函数x torch.relu(x)# 第一次池化x self.pool1(x)# 第二次卷积x self.conv2(x)# 加入 BatchNorm2 归一化x self.bn2(x)# 激活函数x torch.relu(x)# 第二次池化x self.pool2(x)# 将特征图展平为向量x x.view(x.size(0), -1)# 全连接层 激活 Dropoutx torch.relu(self.linear1(x))x self.dropout1(x)x torch.relu(self.linear2(x))x self.dropout2(x)# 输出层x self.out(x)return xdef validate_model(model, valid_loader, criterion, device):在验证集上评估模型的损失和准确率。参数:model (nn.Module): 当前待评估的神经网络模型valid_loader (DataLoader): 验证集数据加载器用于逐批次提供验证数据criterion: 损失函数这里通常使用交叉熵损失函数 CrossEntropyLoss返回:avg_loss (float): 验证集上的平均损失值accuracy (float): 验证集上的整体准确率model.eval() # 将模型设置为评估模式。在评估模式下Dropout、BatchNorm等层会表现为推理模式# 例如 Dropout 会关闭随机失活BatchNorm会使用固定的均值和方差进行归一化# 这样可以确保在验证或测试时模型行为稳定不受训练时随机因素的影响。total_loss 0.0 # 初始化累计损失用于累加每个批次计算得到的损失值total_correct 0 # 初始化累计正确预测的样本数用于计算准确率total_samples 0 # 初始化累计样本总数用于后续计算准确率# 使用 torch.no_grad() 上下文管理器表示在该代码块内不计算梯度降低内存消耗、提高运行速度。with torch.no_grad():# 遍历验证集 DataLoader 中的每个批次数据for x, y in valid_loader:x, y x.to(device), y.to(device)# x: 当前批次输入数据形状通常为 (batch_size, channels, height, width)# y: 当前批次对应的真实标签形状通常为 (batch_size,)output model(x) # 将输入 x 传入模型进行前向传播得到输出结果 output# output 通常是一个形状为 (batch_size, num_classes) 的张量# 每一行表示每个样本各类别的预测分数或概率在未经过 softmax 的情况下为logitsloss criterion(output, y)# 计算当前批次的损失值# 将模型输出 output 与真实标签 y 输入到损失函数中# 例如对于多分类问题常用的 CrossEntropyLoss该损失函数内部会对 output 进行 softmax 计算# 并根据真实标签 y 计算交叉熵损失。total_loss loss.item()# loss.item() 将当前批次的损失值从张量中取出为 Python 数值# 并累加到 total_loss 中目的是统计整个验证集上的累计损失。preds torch.argmax(output, dim-1)# torch.argmax(output, dim-1) 沿着最后一个维度类别维度取最大值的索引# 得到模型对每个样本预测的类别标签。即预测类别为得分最高的那一项。total_correct (preds y).sum().item()# (preds y) 会返回一个布尔型张量表示每个预测是否与真实标签相等# .sum() 对布尔值进行求和True 计为1False计为0得到本批次预测正确的数量# .item() 将结果转换为 Python 数值并累加到 total_correct 中。total_samples len(y)# 累加本批次样本数。 len(y) 返回当前批次的样本数量# 累加到 total_samples 中用于后续计算整体准确率。# 计算整个验证集的平均损失avg_loss total_loss / len(valid_loader)# len(valid_loader) 表示验证集中批次数目所以 avg_loss 是每个批次的平均损失值。# 计算验证集的准确率accuracy total_correct / total_samples# 用累计正确预测数除以总样本数得到准确率一个介于0到1之间的浮点数return avg_loss, accuracydef train_model(model, train_dataset, valid_dataset, device):使用给定的模型和训练数据集进行训练并在每个Epoch结束后在验证集上评估。- 损失函数: CrossEntropyLoss适用于多分类问题- 优化器: AdamW自带权重衰减改进版Adam- 保存最优模型基于验证集准确率- 记录训练过程中每个Epoch的Loss和Accuracy用于后续可视化# 创建交叉熵损失函数对象用于计算模型输出和真实标签之间的误差criterion nn.CrossEntropyLoss()# 创建AdamW优化器传入模型所有参数和设定学习率optimizer optim.AdamW(model.parameters(), lrLEARNING_RATE)# 利用 DataLoader 将训练数据集按照指定批量大小随机打乱后加载train_loader DataLoader(train_dataset, batch_sizeBATCH_SIZE, shuffleTrue)# 验证集 DataLoader不需要打乱顺序valid_loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse)# 初始化两个列表用于保存每个Epoch的训练损失和验证损失train_losses, valid_losses [], []# 初始化两个列表用于保存每个Epoch的训练准确率和验证准确率train_accuracies, valid_accuracies [], []# 初始化一个变量记录历史最高验证准确率用于保存最佳模型best_acc 0.0# 指定保存最佳模型的文件路径best_model_path ./深度学习实战/model/image_classification/image_classification_best.pth# 开始遍历每个训练周期Epochfor epoch_idx in range(EPOCHS):# 将模型设置为训练模式这会启用Dropout、BatchNorm等训练时的特殊行为model.train()# 记录当前Epoch开始的时间用于计算训练耗时start_time time.time()# 初始化本Epoch内累计的训练损失、正确预测数和样本总数total_loss 0.0total_correct 0total_samples 0# 遍历训练数据加载器中的每个批次数据for x, y in train_loader:# 将当前批次数据迁移到指定设备x, y x.to(device), y.to(device)# 前向传播将当前批次输入数据 x 传入模型得到预测输出 outputoutput model(x)# 计算当前批次的损失值将模型输出和真实标签 y 输入到损失函数中计算交叉熵损失loss criterion(output, y)# 优化器梯度归零防止梯度累加PyTorch中梯度默认累加optimizer.zero_grad()# 反向传播根据当前损失计算梯度loss.backward()# 更新模型参数根据计算得到的梯度和优化器规则更新参数optimizer.step()# 累加当前批次的损失值loss.item()转换为Python数值total_loss loss.item()# 使用torch.argmax在最后一个维度上取最大值的索引得到每个样本预测的类别preds torch.argmax(output, dim-1)# 比较预测类别与真实标签统计当前批次中预测正确的样本数并累加到total_correcttotal_correct (preds y).sum().item()# 累加当前批次的样本数total_samples len(y)# 计算本Epoch的平均训练损失总损失除以批次数即train_loader中批次数量avg_train_loss total_loss / len(train_loader)# 计算本Epoch的训练准确率正确预测样本数除以总样本数train_accuracy total_correct / total_samples# 在验证集上评估模型性能调用validate_model函数返回验证集平均损失和准确率avg_valid_loss, valid_accuracy validate_model(model, valid_loader, criterion, device)# 将本Epoch的训练和验证损失、准确率分别记录到对应的列表中便于后续绘图和分析train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accuracies.append(train_accuracy)valid_accuracies.append(valid_accuracy)# 如果当前Epoch的验证准确率优于历史最佳验证准确率则更新best_acc并保存当前模型参数if valid_accuracy best_acc:best_acc valid_accuracytorch.save(model.state_dict(), best_model_path, _use_new_zipfile_serializationTrue)# 计算本Epoch耗时当前时间减去开始时间elapsed time.time() - start_time# 打印本Epoch的训练信息包含训练损失、训练准确率、验证损失、验证准确率和耗时print(fEpoch [{epoch_idx1}/{EPOCHS}] f训练损失: {avg_train_loss:.4f}, 训练准确率: {train_accuracy:.2f}, f验证损失: {avg_valid_loss:.4f}, 验证准确率: {valid_accuracy:.2f}, f耗时: {elapsed:.2f}s)# 训练结束后将最终模型最后一次训练得到的参数保存到指定文件中final_model_path ./深度学习实战/model/image_classification/image_classification_last.pthtorch.save(model.state_dict(), final_model_path, _use_new_zipfile_serializationTrue)print(f训练完成最优验证准确率: {best_acc:.2f}, 模型已保存至: {best_model_path} 和 {final_model_path})# 返回训练过程中每个Epoch的训练/验证损失和训练/验证准确率方便后续进行曲线可视化return train_losses, valid_losses, train_accuracies, valid_accuraciesdef plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):可视化训练过程中每个Epoch的损失曲线和准确率曲线。参数train_losses (list): 一个列表保存了每个Epoch的训练损失值valid_losses (list): 一个列表保存了每个Epoch的验证损失值train_accs (list): 一个列表保存了每个Epoch的训练准确率valid_accs (list): 一个列表保存了每个Epoch的验证准确率# 创建一个迭代器从1开始到训练轮数Epoch1# epochs_range 中的每个值代表一个Epoch的编号epochs_range range(1, len(train_losses) 1)# 创建一个包含1行2列子图的画布画布大小设定为宽12英寸高5英寸# 这样可以在同一画布上并排显示两个图左边显示Loss曲线右边显示Accuracy曲线fig, axs plt.subplots(1, 2, figsize(12, 5))# 绘制左侧子图Loss曲线# 在左侧子图上绘制训练Loss曲线使用圆形标记oaxs[0].plot(epochs_range, train_losses, label训练Loss, markero)# 同样在左侧子图上绘制验证Loss曲线使用叉形标记xaxs[0].plot(epochs_range, valid_losses, label验证Loss, markerx)# 设置X轴的标签为训练轮数axs[0].set_xlabel(训练轮数)# 设置Y轴的标签为Lossaxs[0].set_ylabel(Loss)# 设置子图的标题为训练 验证 Lossaxs[0].set_title(训练 验证 Loss)# 在左侧子图中显示图例图例会自动显示各曲线对应的label名称axs[0].legend()# 绘制右侧子图Accuracy曲线# 在右侧子图上绘制训练准确率曲线使用圆形标记oaxs[1].plot(epochs_range, train_accs, label训练Accuracy, markero)# 在右侧子图上绘制验证准确率曲线使用叉形标记xaxs[1].plot(epochs_range, valid_accs, label验证Accuracy, markerx)# 设置X轴的标签为训练轮数axs[1].set_xlabel(训练轮数)# 设置Y轴的标签为Accuracyaxs[1].set_ylabel(Accuracy)# 设置子图的标题为训练 验证 Accuracyaxs[1].set_title(训练 验证 Accuracy)# 在右侧子图中显示图例axs[1].legend()# 调整子图布局使其不会重叠并使整个画布看起来更紧凑plt.tight_layout()# 显示图像plt.show()def test_model_and_confusion(valid_dataset, device):使用验证集上表现最优的模型在测试集上评估准确率并生成混淆矩阵。参数valid_dataset (Dataset): 验证集或测试集数据集device (torch.device): 指定的设备如 CPU、GPU 或 MPS用于模型和数据的迁移# 利用 DataLoader 构建验证集的批次数据加载器# batch_size 为全局变量 BATCH_SIZEshuffle 设置为 False 保持顺序便于统计和混淆矩阵计算valid_loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse)# 指定保存最优模型的路径best_model_path ./深度学习实战/model/image_classification/image_classification_best.pth# 实例化 ImageClassification 模型对象必须保证模型结构与训练时一致model ImageClassification()# 加载保存的最佳模型参数# torch.load(best_model_path) 从指定路径加载保存的模型参数state_dict# model.load_state_dict(...) 将加载的参数应用到当前模型中model.load_state_dict(torch.load(best_model_path))model model.to(device)# 设置模型为评估模式# eval() 会使模型进入推理状态关闭 Dropout、使 BatchNorm 使用训练时计算的均值和方差等model.eval()# 初始化统计变量用于累加预测正确的样本数和总样本数total_correct 0total_samples 0# 初始化两个列表分别用于记录所有预测的标签和真实标签all_preds []all_labels []# 使用 torch.no_grad() 表示在验证和测试阶段不计算梯度降低内存占用、提高推理速度with torch.no_grad():# 遍历验证数据加载器中的每个批次for x, y in valid_loader:# 将当前批次的输入数据 x 和标签 y 迁移到指定设备上例如 GPU 或 MPSx, y x.to(device), y.to(device)# 将输入数据传入模型进行前向传播得到输出结果 output# output 的形状通常为 (batch_size, num_classes)output model(x)# 通过 torch.argmax 从 output 中选取得分最大的索引作为预测的类别# 这里 dim-1 表示沿着最后一个维度进行比较即每个样本在所有类别上的得分preds torch.argmax(output, dim-1)# 将当前批次的预测结果转换为 CPU 上的 NumPy 数组并追加到 all_preds 列表中# 这样做便于后续使用 scikit-learn 计算混淆矩阵all_preds.extend(preds.cpu().numpy())# 同理将当前批次的真实标签转换为 NumPy 数组并追加到 all_labels 列表中all_labels.extend(y.cpu().numpy())# 比较预测结果与真实标签统计预测正确的样本数# (preds y) 会生成一个布尔张量.sum() 求和True 计为1再用 .item() 转为 Python 数值total_correct (preds y).sum().item()# 累计本批次的样本数len(y) 返回当前批次中样本的数量total_samples len(y)# 计算整体准确率正确预测的样本数除以总样本数accuracy total_correct / total_samples# 打印在测试集上的准确率格式化为百分比保留两位小数print(f使用最优模型在测试集上的准确率: {accuracy * 100:.2f}%)# 利用 scikit-learn 的 confusion_matrix 函数计算混淆矩阵# 参数 all_labels 为真实标签列表all_preds 为模型预测的标签列表cm confusion_matrix(all_labels, all_preds)# 利用 ConfusionMatrixDisplay 将混淆矩阵可视化# display_labelsrange(10) 表示显示 0~9 共10个类别的标签disp ConfusionMatrixDisplay(confusion_matrixcm, display_labelsrange(10))# 绘制混淆矩阵图像cmapplt.cm.Blues 设置配色方案为蓝色渐变disp.plot(cmapplt.cm.Blues)# 为混淆矩阵图添加标题plt.title(CIFAR10 混淆矩阵)# 显示混淆矩阵图plt.show()if __name__ __main__:import os# 判断是否在远程服务器环境if os.path.exists(/home/ubuntu/data/pycharm_project_377):# 远程服务器环境os.chdir(/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject)print(os.getcwd())else:# 本地环境os.chdir(/Users/coyi/PycharmProjects/GPU_32_pythonProject)print(os.getcwd())# 从这里开始你原有的代码无需修改# 假设后续代码中有 ./model/image_classification 之类的相对路径# 它们都会基于你上面 chdir 到的目录进行解析# 检查 GPU 是否可用如果没有则检查苹果 MPS 是否可用否则使用 CPUif torch.cuda.is_available():device torch.device(cuda)elif torch.backends.mps.is_available():device torch.device(mps)else:device torch.device(cpu)print(使用的设备, device)# 1. 加载数据集并查看基本信息train_dataset, valid_dataset create_dataset()print(训练集类别映射:, train_dataset.class_to_idx)print(训练集数据形状:, train_dataset.data.shape)print(测试集数据形状:, valid_dataset.data.shape)# 2. 可视化一张训练集图片plt.figure(figsize(2, 2))plt.imshow(train_dataset.data[1])plt.title(f类别索引: {train_dataset.targets[1]})plt.show()# 3. 实例化网络并查看结构model ImageClassification().to(device)summary(model, input_size(3, 32, 32), batch_size1)# model model# 4. 训练模型并记录曲线数据train_losses, valid_losses, train_accs, valid_accs train_model(model, train_dataset, valid_dataset, device)# 5. 绘制训练过程曲线plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# 6. 使用最佳模型进行测试并展示混淆矩阵test_model_and_confusion(valid_dataset, device) 代码1输出 代码2: 使用ResNet34 自己从0训练权重未优化未使用原始权重 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision.datasets import CIFAR10 from torchvision.transforms import ToTensor, Composeimport torchvision.models as models # 用于加载官方resnet34 import time import matplotlib.pyplot as plt from torchsummary import summary# 方便中文显示的 matplotlib 配置 plt.rcParams[font.family] sans-serif plt.rcParams[font.sans-serif] [Noto Sans CJK SC, DejaVu Sans] plt.rcParams[axes.unicode_minus] Falseimport numpy as np from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# --------------------------- # 1. 全局超参数 # --------------------------- BATCH_SIZE 8 # 每批处理多少张图 EPOCHS 100 # 训练轮数 LEARNING_RATE 1e-3 # 学习率# --------------------------- # 2. 数据集加载函数 # --------------------------- def create_dataset():加载 CIFAR-10 数据集。CIFAR-10训练集50000张测试集10000张图像32x32RGB三通道分成10类。ToTensor() 会把 [0,255] 的像素值归一化到 [0,1] 的 float32 张量。downloadTrue 会自动下载到根目录 data/ 下。train CIFAR10(rootdata, # 指定下载/存储路径trainTrue, # True表示训练集transformCompose([ToTensor()]), # 将图像转为张量downloadTrue # 若本地无数据则自动下载)valid CIFAR10(rootdata,trainFalse, # False表示测试集transformCompose([ToTensor()]),downloadTrue)return train, valid# --------------------------- # 3. 构建 resnet34 模型并适配 CIFAR-10 # --------------------------- def get_resnet34_cifar10():使用官方 torchvision.models.resnet34(pretrainedFalse)。1) 将 conv1 改成 3x3, stride1, padding12) 将 maxpool 改成 Identity() 不执行池化3) 将 fc 改成输出通道数10CIFAR10有10类net models.resnet34(pretrainedFalse) # 不使用ImageNet预训练权重# 修改第一层卷积 原本7x7, stride2容易把32x32图像缩得太小net.conv1 nn.Conv2d(3, 64, kernel_size3, stride1, padding1, biasFalse)# 去掉 maxpool 不进行额外下采样net.maxpool nn.Identity()# 最后全连接层 改为输出10类net.fc nn.Linear(in_features512, out_features10, biasTrue)return net# --------------------------- # 4. 验证过程 # --------------------------- def validate_model(model, valid_loader, criterion, device):在验证集/测试集上计算Loss和Accuracy用于评估模型性能。model: 当前的神经网络valid_loader: 验证集DataLoadercriterion: 损失函数device: cuda 或 cpu返回: (平均loss, 准确率)model.eval() # 切换到评估模式关闭Dropout等随机操作total_loss 0.0total_correct 0total_samples 0with torch.no_grad():for x, y in valid_loader:x, y x.to(device), y.to(device) # 将数据放到指定设备上output model(x) # 前向传播loss criterion(output, y) # 计算损失total_loss loss.item()# argmax 找到输出张量中得分最大的类别preds torch.argmax(output, dim-1)total_correct (preds y).sum().item()total_samples len(y)avg_loss total_loss / len(valid_loader)accuracy total_correct / total_samplesreturn avg_loss, accuracy# --------------------------- # 5. 训练过程 # --------------------------- def train_model(model, train_dataset, valid_dataset, device):训练并验证模型。若发现更高验证集准确率则保存最优权重。返回各Epoch的Loss和Accuracy供绘图使用。criterion nn.CrossEntropyLoss() # 交叉熵损失optimizer optim.AdamW(model.parameters(), lrLEARNING_RATE) # AdamW优化器train_loader DataLoader(train_dataset, batch_sizeBATCH_SIZE, shuffleTrue)valid_loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse)train_losses, valid_losses [], []train_accs, valid_accs [], []best_acc 0.0# 当验证集准确率超过之前最优时保存到 best_model_pathbest_model_path ./深度学习实战/model/resnet34_cifar10_best.pthfor epoch_idx in range(EPOCHS):model.train() # 切换到训练模式start_time time.time()total_loss 0.0total_correct 0total_samples 0for x, y in train_loader:x, y x.to(device), y.to(device)output model(x) # 前向loss criterion(output, y)optimizer.zero_grad() # 梯度清零loss.backward() # 反向传播optimizer.step() # 更新参数total_loss loss.item()preds torch.argmax(output, dim-1)total_correct (preds y).sum().item()total_samples len(y)avg_train_loss total_loss / len(train_loader)train_acc total_correct / total_samples# 在验证集上评估avg_valid_loss, valid_acc validate_model(model, valid_loader, criterion, device)train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accs.append(train_acc)valid_accs.append(valid_acc)# 如果当前验证准确率超过历史最优 保存if valid_acc best_acc:best_acc valid_acctorch.save(model.state_dict(), best_model_path)elapsed time.time() - start_timeprint(fEpoch [{epoch_idx1}/{EPOCHS}] fTrain Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}, fValid Loss: {avg_valid_loss:.4f}, Valid Acc: {valid_acc:.2f}, fTime: {elapsed:.2f}s)# 最后再保存一次final_path ./深度学习实战/model/resnet34_cifar10_last.pthtorch.save(model.state_dict(), final_path)print(f训练完成最优验证准确率: {best_acc:.2f}. 模型已保存到 {best_model_path} 和 {final_path})return train_losses, valid_losses, train_accs, valid_accs# --------------------------- # 6. 绘制训练过程曲线 # --------------------------- def plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):传入训练/验证Loss和Acc的列表画出随Epoch变化的曲线。epochs_range range(1, len(train_losses)1)fig, axs plt.subplots(1, 2, figsize(12,5))# 左边画 Lossaxs[0].plot(epochs_range, train_losses, labelTrain Loss, markero)axs[0].plot(epochs_range, valid_losses, labelValid Loss, markerx)axs[0].set_xlabel(Epochs)axs[0].set_ylabel(Loss)axs[0].set_title(Train Valid Loss)axs[0].legend()# 右边画 Accuracyaxs[1].plot(epochs_range, train_accs, labelTrain Acc, markero)axs[1].plot(epochs_range, valid_accs, labelValid Acc, markerx)axs[1].set_xlabel(Epochs)axs[1].set_ylabel(Accuracy)axs[1].set_title(Train Valid Accuracy)axs[1].legend()plt.tight_layout()plt.show()# --------------------------- # 7. 使用最佳模型 混淆矩阵 # --------------------------- def test_model_and_confusion(valid_dataset, device):加载最优模型在测试集上计算准确率并绘制混淆矩阵。loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse)# 初始化网络 需要跟训练时的结构一致model get_resnet34_cifar10()best_model_path ./深度学习实战/model/resnet34_cifar10_best.pthmodel.load_state_dict(torch.load(best_model_path)) # 加载最佳权重model model.to(device)model.eval()total_correct 0total_samples 0all_preds []all_labels []with torch.no_grad():for x, y in loader:x, y x.to(device), y.to(device)output model(x)preds torch.argmax(output, dim-1)# 用于混淆矩阵all_preds.extend(preds.cpu().numpy())all_labels.extend(y.cpu().numpy())total_correct (preds y).sum().item()total_samples len(y)acc total_correct / total_samplesprint(f使用最优模型在测试集上的准确率: {acc*100:.2f}%)# 混淆矩阵cm confusion_matrix(all_labels, all_preds)disp ConfusionMatrixDisplay(cm, display_labelsrange(10))disp.plot(cmapplt.cm.Blues)plt.title(resnet34 on CIFAR10 Confusion Matrix)plt.show()# --------------------------- # 8. 主函数入口 # --------------------------- if __name__ __main__:import os# 检查是远程路径还是本地路径if os.path.exists(/home/ubuntu/data/pycharm_project_377):os.chdir(/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject)print(当前工作目录远程, os.getcwd())else:os.chdir(/Users/coyi/PycharmProjects/GPU_32_pythonProject)print(当前工作目录本地, os.getcwd())# 检查设备 优先用 GPUif torch.cuda.is_available():device torch.device(cuda:0)else:device torch.device(cpu)print(使用的设备, device)# (1) 加载CIFAR10train_dataset, valid_dataset create_dataset()print(训练集类别映射:, train_dataset.class_to_idx)print(训练集数据形状:, train_dataset.data.shape)print(测试集数据形状:, valid_dataset.data.shape)# (2) 可视化一张图plt.figure()plt.imshow(train_dataset.data[0])plt.title(f标签: {train_dataset.targets[0]})plt.show()# (3) 实例化 resnet34 并查看结构model get_resnet34_cifar10().to(device)summary(model, input_size(3, 32, 32), batch_size1)# (4) 训练train_losses, valid_losses, train_accs, valid_accs train_model(model, train_dataset, valid_dataset, device)# (5) 可视化训练plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# (6) 测试集上做预测 混淆矩阵test_model_and_confusion(valid_dataset, device) 输出 然后被我KIll了因为太慢了 代码3: 使用ResNet34 自己从0训练权重优化训练过程未使用原始权重 代码是在代码2的基础上加了优化过程 import torchtorch.backends.cudnn.benchmark True #torch.backends.cudnn.benchmark True 会自动搜索最佳的卷积算法当输入尺寸固定时可以大幅加速卷积计算。 import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision.datasets import CIFAR10 from torchvision.transforms import ToTensor, Composeimport torchvision.models as models # 用于加载官方resnet34 import time import matplotlib.pyplot as plt from torchsummary import summary# 方便中文显示的 matplotlib 配置 plt.rcParams[font.family] sans-serif plt.rcParams[font.sans-serif] [Noto Sans CJK SC, DejaVu Sans] plt.rcParams[axes.unicode_minus] Falseimport numpy as np from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# --------------------------- # 1. 全局超参数 # --------------------------- BATCH_SIZE 256 # 每批处理多少张图 EPOCHS 100 # 训练轮数 LEARNING_RATE 1e-3 # 学习率# --------------------------- # 2. 数据集加载函数 # --------------------------- def create_dataset():加载 CIFAR-10 数据集。CIFAR-10训练集50000张测试集10000张图像32x32RGB三通道分成10类。ToTensor() 会把 [0,255] 的像素值归一化到 [0,1] 的 float32 张量。downloadTrue 会自动下载到根目录 data/ 下。train CIFAR10(rootdata, # 指定下载/存储路径trainTrue, # True表示训练集transformCompose([ToTensor()]), # 将图像转为张量downloadTrue # 若本地无数据则自动下载)valid CIFAR10(rootdata,trainFalse, # False表示测试集transformCompose([ToTensor()]),downloadTrue)return train, valid# --------------------------- # 3. 构建 resnet34 模型并适配 CIFAR-10 # --------------------------- def get_resnet34_cifar10():使用官方 torchvision.models.resnet34(pretrainedFalse)。1) 将 conv1 改成 3x3, stride1, padding12) 将 maxpool 改成 Identity() 不执行池化3) 将 fc 改成输出通道数10CIFAR10有10类net models.resnet34(pretrainedFalse) # 不使用ImageNet预训练权重# 修改第一层卷积 原本7x7, stride2容易把32x32图像缩得太小net.conv1 nn.Conv2d(3, 64, kernel_size3, stride1, padding1, biasFalse)# 去掉 maxpool 不进行额外下采样net.maxpool nn.Identity()# 最后全连接层 改为输出10类net.fc nn.Linear(in_features512, out_features10, biasTrue)return net# --------------------------- # 4. 验证过程 # --------------------------- def validate_model(model, valid_loader, criterion, device):在验证集/测试集上计算Loss和Accuracy用于评估模型性能。model: 当前的神经网络valid_loader: 验证集DataLoadercriterion: 损失函数device: cuda 或 cpu返回: (平均loss, 准确率)model.eval() # 切换到评估模式关闭Dropout等随机操作total_loss 0.0total_correct 0total_samples 0with torch.no_grad():for x, y in valid_loader:x, y x.to(device), y.to(device) # 将数据放到指定设备上output model(x) # 前向传播loss criterion(output, y) # 计算损失total_loss loss.item()# argmax 找到输出张量中得分最大的类别preds torch.argmax(output, dim-1)total_correct (preds y).sum().item()total_samples len(y)avg_loss total_loss / len(valid_loader)accuracy total_correct / total_samplesreturn avg_loss, accuracy# --------------------------- # 5. 训练过程 # --------------------------- def train_model(model, train_dataset, valid_dataset, device):训练并验证模型。若发现更高验证集准确率则保存最优权重。返回各Epoch的Loss和Accuracy供绘图使用。criterion nn.CrossEntropyLoss() # 交叉熵损失optimizer optim.AdamW(model.parameters(), lrLEARNING_RATE) # AdamW优化器# 优化 DataLoader数值 num_workers 可根据 CPU 核心数进行调整。# • 增加 num_workersDataLoader 的 num_workers 参数决定了数据预处理的并行程度。增大 num_workers 可以加快数据加载速度避免因数据加载速度过慢而使 GPU 等待数据。# • 设置 pin_memoryTrue对于 GPU 训练设置 pin_memoryTrue 可以加速数据从主机内存传输到 GPU。train_loader DataLoader(train_dataset, batch_sizeBATCH_SIZE, shuffleTrue, num_workers8, pin_memoryTrue)valid_loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse, num_workers8, pin_memoryTrue)train_losses, valid_losses [], []train_accs, valid_accs [], []best_acc 0.0# 当验证集准确率超过之前最优时保存到 best_model_pathbest_model_path ./深度学习实战/model/resnet34_cifar10_best.pthscaler torch.amp.GradScaler() # 混合精度梯度缩放器for epoch_idx in range(EPOCHS):model.train() # 切换到训练模式start_time time.time()total_loss 0.0total_correct 0total_samples 0for x, y in train_loader:x, y x.to(device), y.to(device)# output model(x) # 前向# loss criterion(output, y)# optimizer.zero_grad() # 梯度清零# loss.backward() # 反向传播# optimizer.step() # 更新参数optimizer.zero_grad()# 自动混合精度上下文with torch.amp.autocast(device_typedevice.type):output model(x)loss criterion(output, y)scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()total_loss loss.item()preds torch.argmax(output, dim-1)total_correct (preds y).sum().item()total_samples len(y)avg_train_loss total_loss / len(train_loader)train_acc total_correct / total_samples# 在验证集上评估avg_valid_loss, valid_acc validate_model(model, valid_loader, criterion, device)train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accs.append(train_acc)valid_accs.append(valid_acc)# 如果当前验证准确率超过历史最优 保存if valid_acc best_acc:best_acc valid_acctorch.save(model.state_dict(), best_model_path)elapsed time.time() - start_timeprint(fEpoch [{epoch_idx1}/{EPOCHS}] fTrain Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}, fValid Loss: {avg_valid_loss:.4f}, Valid Acc: {valid_acc:.2f}, fTime: {elapsed:.2f}s)# 最后再保存一次final_path ./深度学习实战/model/resnet34_cifar10_last.pthtorch.save(model.state_dict(), final_path)print(f训练完成最优验证准确率: {best_acc:.2f}. 模型已保存到 {best_model_path} 和 {final_path})return train_losses, valid_losses, train_accs, valid_accs# --------------------------- # 6. 绘制训练过程曲线 # --------------------------- def plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):传入训练/验证Loss和Acc的列表画出随Epoch变化的曲线。epochs_range range(1, len(train_losses)1)fig, axs plt.subplots(1, 2, figsize(12,5))# 左边画 Lossaxs[0].plot(epochs_range, train_losses, labelTrain Loss, markero)axs[0].plot(epochs_range, valid_losses, labelValid Loss, markerx)axs[0].set_xlabel(Epochs)axs[0].set_ylabel(Loss)axs[0].set_title(Train Valid Loss)axs[0].legend()# 右边画 Accuracyaxs[1].plot(epochs_range, train_accs, labelTrain Acc, markero)axs[1].plot(epochs_range, valid_accs, labelValid Acc, markerx)axs[1].set_xlabel(Epochs)axs[1].set_ylabel(Accuracy)axs[1].set_title(Train Valid Accuracy)axs[1].legend()plt.tight_layout()plt.show()# --------------------------- # 7. 使用最佳模型 混淆矩阵 # --------------------------- def test_model_and_confusion(valid_dataset, device):加载最优模型在测试集上计算准确率并绘制混淆矩阵。loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse, num_workers8, pin_memoryTrue)# 初始化网络 需要跟训练时的结构一致model get_resnet34_cifar10()best_model_path ./深度学习实战/model/resnet34_cifar10_best.pthmodel.load_state_dict(torch.load(best_model_path, weights_onlyTrue)) # 加载最佳权重model model.to(device)model.eval()total_correct 0total_samples 0all_preds []all_labels []with torch.no_grad():for x, y in loader:x, y x.to(device), y.to(device)output model(x)preds torch.argmax(output, dim-1)# 用于混淆矩阵all_preds.extend(preds.cpu().numpy())all_labels.extend(y.cpu().numpy())total_correct (preds y).sum().item()total_samples len(y)acc total_correct / total_samplesprint(f使用最优模型在测试集上的准确率: {acc*100:.2f}%)# 混淆矩阵cm confusion_matrix(all_labels, all_preds)disp ConfusionMatrixDisplay(cm, display_labelsrange(10))disp.plot(cmapplt.cm.Blues)plt.title(resnet34 on CIFAR10 Confusion Matrix)plt.show()# --------------------------- # 8. 主函数入口 # --------------------------- if __name__ __main__:import os# 检查是远程路径还是本地路径if os.path.exists(/home/ubuntu/data/pycharm_project_377):os.chdir(/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject)print(当前工作目录远程, os.getcwd())else:os.chdir(/Users/coyi/PycharmProjects/GPU_32_pythonProject)print(当前工作目录本地, os.getcwd())# 检查设备 优先用 GPUif torch.cuda.is_available():device torch.device(cuda:0)else:device torch.device(cpu)print(使用的设备, device)# (1) 加载CIFAR10train_dataset, valid_dataset create_dataset()print(训练集类别映射:, train_dataset.class_to_idx)print(训练集数据形状:, train_dataset.data.shape)print(测试集数据形状:, valid_dataset.data.shape)# (2) 可视化一张图plt.figure()plt.imshow(train_dataset.data[0])plt.title(f标签: {train_dataset.targets[0]})plt.show()# (3) 实例化 resnet34 并查看结构model get_resnet34_cifar10().to(device)summary(model, input_size(3, 32, 32), batch_size1)# (4) 训练train_losses, valid_losses, train_accs, valid_accs train_model(model, train_dataset, valid_dataset, device)# (5) 可视化训练plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# (6) 测试集上做预测 混淆矩阵test_model_and_confusion(valid_dataset, device) 输出 代码4: 使用ResNet34 的预训练权重优化训练过程 在代码3的基础上使用了resnet34的原始权重的基础上进行训练 import torchtorch.backends.cudnn.benchmark True #torch.backends.cudnn.benchmark True 会自动搜索最佳的卷积算法当输入尺寸固定时可以大幅加速卷积计算。 import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision.datasets import CIFAR10 from torchvision.transforms import ToTensor, Composeimport torchvision.models as models # 用于加载官方resnet34 import time import matplotlib.pyplot as plt from torchsummary import summary# 方便中文显示的 matplotlib 配置 plt.rcParams[font.family] sans-serif plt.rcParams[font.sans-serif] [Noto Sans CJK SC, DejaVu Sans] plt.rcParams[axes.unicode_minus] Falseimport numpy as np from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# --------------------------- # 1. 全局超参数 # --------------------------- BATCH_SIZE 256 # 每批处理多少张图 EPOCHS 100 # 训练轮数 LEARNING_RATE 1e-3 # 学习率# --------------------------- # 2. 数据集加载函数 # --------------------------- def create_dataset():加载 CIFAR-10 数据集。CIFAR-10训练集50000张测试集10000张图像32x32RGB三通道分成10类。ToTensor() 会把 [0,255] 的像素值归一化到 [0,1] 的 float32 张量。downloadTrue 会自动下载到根目录 data/ 下。train CIFAR10(rootdata, # 指定下载/存储路径trainTrue, # True表示训练集transformCompose([ToTensor()]), # 将图像转为张量downloadTrue # 若本地无数据则自动下载)valid CIFAR10(rootdata,trainFalse, # False表示测试集transformCompose([ToTensor()]),downloadTrue)return train, valid# --------------------------- # 3. 构建 resnet34 模型并适配 CIFAR-10 # --------------------------- def get_resnet34_cifar10():使用官方 torchvision.models.resnet34(pretrainedFalse)。1) 将 conv1 改成 3x3, stride1, padding12) 将 maxpool 改成 Identity() 不执行池化3) 将 fc 改成输出通道数10CIFAR10有10类net models.resnet34(weightsmodels.ResNet34_Weights.IMAGENET1K_V1) # 使用ImageNet预训练权重# 修改第一层卷积 原本7x7, stride2容易把32x32图像缩得太小net.conv1 nn.Conv2d(3, 64, kernel_size3, stride1, padding1, biasFalse)# 去掉 maxpool 不进行额外下采样net.maxpool nn.Identity()# 最后全连接层 改为输出10类net.fc nn.Linear(in_features512, out_features10, biasTrue)return net# --------------------------- # 4. 验证过程 # --------------------------- def validate_model(model, valid_loader, criterion, device):在验证集/测试集上计算Loss和Accuracy用于评估模型性能。model: 当前的神经网络valid_loader: 验证集DataLoadercriterion: 损失函数device: cuda 或 cpu返回: (平均loss, 准确率)model.eval() # 切换到评估模式关闭Dropout等随机操作total_loss 0.0total_correct 0total_samples 0with torch.no_grad():for x, y in valid_loader:x, y x.to(device), y.to(device) # 将数据放到指定设备上output model(x) # 前向传播loss criterion(output, y) # 计算损失total_loss loss.item()# argmax 找到输出张量中得分最大的类别preds torch.argmax(output, dim-1)total_correct (preds y).sum().item()total_samples len(y)avg_loss total_loss / len(valid_loader)accuracy total_correct / total_samplesreturn avg_loss, accuracy# --------------------------- # 5. 训练过程 # --------------------------- def train_model(model, train_dataset, valid_dataset, device):训练并验证模型。若发现更高验证集准确率则保存最优权重。返回各Epoch的Loss和Accuracy供绘图使用。criterion nn.CrossEntropyLoss() # 交叉熵损失optimizer optim.AdamW(model.parameters(), lrLEARNING_RATE) # AdamW优化器# 优化 DataLoader数值 num_workers 可根据 CPU 核心数进行调整。# • 增加 num_workersDataLoader 的 num_workers 参数决定了数据预处理的并行程度。增大 num_workers 可以加快数据加载速度避免因数据加载速度过慢而使 GPU 等待数据。# • 设置 pin_memoryTrue对于 GPU 训练设置 pin_memoryTrue 可以加速数据从主机内存传输到 GPU。train_loader DataLoader(train_dataset, batch_sizeBATCH_SIZE, shuffleTrue, num_workers8, pin_memoryTrue)valid_loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse, num_workers8, pin_memoryTrue)train_losses, valid_losses [], []train_accs, valid_accs [], []best_acc 0.0# 当验证集准确率超过之前最优时保存到 best_model_pathbest_model_path ./深度学习实战/model/resnet34_cifar10_best.pthscaler torch.amp.GradScaler() # 混合精度梯度缩放器for epoch_idx in range(EPOCHS):model.train() # 切换到训练模式start_time time.time()total_loss 0.0total_correct 0total_samples 0for x, y in train_loader:x, y x.to(device), y.to(device)# output model(x) # 前向# loss criterion(output, y)# optimizer.zero_grad() # 梯度清零# loss.backward() # 反向传播# optimizer.step() # 更新参数optimizer.zero_grad()# 自动混合精度上下文with torch.amp.autocast(device_typedevice.type):output model(x)loss criterion(output, y)scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()total_loss loss.item()preds torch.argmax(output, dim-1)total_correct (preds y).sum().item()total_samples len(y)avg_train_loss total_loss / len(train_loader)train_acc total_correct / total_samples# 在验证集上评估avg_valid_loss, valid_acc validate_model(model, valid_loader, criterion, device)train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accs.append(train_acc)valid_accs.append(valid_acc)# 如果当前验证准确率超过历史最优 保存if valid_acc best_acc:best_acc valid_acctorch.save(model.state_dict(), best_model_path)elapsed time.time() - start_timeprint(fEpoch [{epoch_idx1}/{EPOCHS}] fTrain Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}, fValid Loss: {avg_valid_loss:.4f}, Valid Acc: {valid_acc:.2f}, fTime: {elapsed:.2f}s)# 最后再保存一次final_path ./深度学习实战/model/resnet34_cifar10_last.pthtorch.save(model.state_dict(), final_path)print(f训练完成最优验证准确率: {best_acc:.2f}. 模型已保存到 {best_model_path} 和 {final_path})return train_losses, valid_losses, train_accs, valid_accs# --------------------------- # 6. 绘制训练过程曲线 # --------------------------- def plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):传入训练/验证Loss和Acc的列表画出随Epoch变化的曲线。epochs_range range(1, len(train_losses)1)fig, axs plt.subplots(1, 2, figsize(12,5))# 左边画 Lossaxs[0].plot(epochs_range, train_losses, labelTrain Loss, markero)axs[0].plot(epochs_range, valid_losses, labelValid Loss, markerx)axs[0].set_xlabel(Epochs)axs[0].set_ylabel(Loss)axs[0].set_title(Train Valid Loss)axs[0].legend()# 右边画 Accuracyaxs[1].plot(epochs_range, train_accs, labelTrain Acc, markero)axs[1].plot(epochs_range, valid_accs, labelValid Acc, markerx)axs[1].set_xlabel(Epochs)axs[1].set_ylabel(Accuracy)axs[1].set_title(Train Valid Accuracy)axs[1].legend()plt.tight_layout()plt.show()# --------------------------- # 7. 使用最佳模型 混淆矩阵 # --------------------------- def test_model_and_confusion(valid_dataset, device):加载最优模型在测试集上计算准确率并绘制混淆矩阵。loader DataLoader(valid_dataset, batch_sizeBATCH_SIZE, shuffleFalse, num_workers8, pin_memoryTrue)# 初始化网络 需要跟训练时的结构一致model get_resnet34_cifar10()best_model_path ./深度学习实战/model/resnet34_cifar10_best.pthmodel.load_state_dict(torch.load(best_model_path, weights_onlyTrue)) # 加载最佳权重model model.to(device)model.eval()total_correct 0total_samples 0all_preds []all_labels []with torch.no_grad():for x, y in loader:x, y x.to(device), y.to(device)output model(x)preds torch.argmax(output, dim-1)# 用于混淆矩阵all_preds.extend(preds.cpu().numpy())all_labels.extend(y.cpu().numpy())total_correct (preds y).sum().item()total_samples len(y)acc total_correct / total_samplesprint(f使用最优模型在测试集上的准确率: {acc*100:.2f}%)# 混淆矩阵cm confusion_matrix(all_labels, all_preds)disp ConfusionMatrixDisplay(cm, display_labelsrange(10))disp.plot(cmapplt.cm.Blues)plt.title(resnet34 on CIFAR10 Confusion Matrix)plt.show()# --------------------------- # 8. 主函数入口 # --------------------------- if __name__ __main__:import os# 检查是远程路径还是本地路径if os.path.exists(/home/ubuntu/data/pycharm_project_377):os.chdir(/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject)print(当前工作目录远程, os.getcwd())else:os.chdir(/Users/coyi/PycharmProjects/GPU_32_pythonProject)print(当前工作目录本地, os.getcwd())# 检查设备 优先用 GPUif torch.cuda.is_available():device torch.device(cuda:0)else:device torch.device(cpu)print(使用的设备, device)# (1) 加载CIFAR10train_dataset, valid_dataset create_dataset()print(训练集类别映射:, train_dataset.class_to_idx)print(训练集数据形状:, train_dataset.data.shape)print(测试集数据形状:, valid_dataset.data.shape)# (2) 可视化一张图plt.figure()plt.imshow(train_dataset.data[0])plt.title(f标签: {train_dataset.targets[0]})plt.show()# (3) 实例化 resnet34 并查看结构model get_resnet34_cifar10().to(device)summary(model, input_size(3, 32, 32), batch_size1)# (4) 训练train_losses, valid_losses, train_accs, valid_accs train_model(model, train_dataset, valid_dataset, device)# (5) 可视化训练plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# (6) 测试集上做预测 混淆矩阵test_model_and_confusion(valid_dataset, device) 输出 总结与展望 本文以 CIFAR-10 为例提供了四 份完整可运行的 PyTorch 代码分别展示了以下四个层次的图像分类实战案例 • 代码一自定义 CNN 介绍了如何手工设计一个卷积神经网络突出基础原理与手工实现让你真正理解卷积神经网络的构造和各层设计的作用。 • 代码二使用开源模型 ResNet34 演示如何快速拿来使用经过 ImageNet 预训练的经典模型并做少量调整如修改第一层卷积和全连接层以适应 CIFAR-10 的数据和分类任务从而得到更高的准确率。 • 代码三高效训练优化 针对训练效率问题采用了多线程加载、混合精度训练AMP、cuDNN 加速提高批次 等策略大幅提升训练速度同时保证精度最终案例准确率达到了 87.57% 单次训练时长12秒。 • 代码四使用开源模型 ResNet34 的原生权重微调 最终案例准确率达到了 90。24% 单次训练时长12秒。完成了需求 通过这篇文章你已经具备了独立实现或改造 CNN 分类项目的能力并能举一反三将这些方法拓展到更大规模或更复杂的项目中。未来可以尝试加入更多优化技巧如模型压缩、数据增强等进一步提高训练效率和模型准确率。 参考文献/引用来源 1. PyTorch 官方文档 PyTorch documentation — PyTorch 2.6 documentation 2. torchvision.models 源码 官方 GitHub 仓库及文档中提供了各种预训练模型的详细实现。 3. Deep Residual Learning for Image Recognition (ResNet) He, K., Zhang, X., Ren, S., Sun, J. (2016). Deep Residual Learning for Image Recognition. arXiv:1512.03385 4. CIFAR-10 数据集 CIFAR-10 and CIFAR-100 datasets — 官方网站 如果你觉得这篇文章对你有所帮助欢迎你点赞、收藏、评论和转发支持一下你的关注与互动将是我持续创作高质量内容的最大动力也欢迎在评论区分享你训练的结果和心得与大家一起交流进步 希望以上整理能满足你的需求感谢阅读我们下篇文章再见
http://www.hkea.cn/news/14449656/

相关文章:

  • 邯郸国外网站建设费用网站开发公司人员配置
  • 南充网站建设公司商城网站开发项目描述
  • 宝安公司免费网站建设电脑培训学校
  • 网站建设听取需求微信公众帐号平台官网
  • 软件园做网站网络营销一个月能挣多少钱
  • 北京最好的网站建设wordpress h5自适应
  • 南宁网站建公司吗程序定制开发
  • 建站公司兴田德润简介深圳做网站可用乐云seo十年
  • 布吉附近公司做网站建设多少钱广告片
  • 网站建设怎么加音乐怎样上传网站程序
  • 微网站哪家好免费做app网站建设
  • 网站建设评比考核报告活动营销方案
  • 如何做网站短链接在线简易网页制作网站
  • 一级a做爰片付费网站在线网站建设工程标准
  • 网站可以免费看设计师推荐网站
  • 875网站建设怎么样wordpress域名改了
  • 企业网站搜索优化正规的网站制作哪个好
  • 电子商务网站建设的常用开发方法做国外网站的零售
  • 学校网站建设情况说明做公司网站别人能看到吗
  • 汉中网站建设邯郸高端网站建设
  • 我有一个网站怎么做外贸网上做兼职做网站
  • 中国贸易网站兼职做放单主持那个网站好
  • 手机怎么搭建网站网站备案查询 美橙网
  • 临沂网站建设方案书php源码 个人网站
  • 手机网站域名如何解析如何进行搜索引擎的优化
  • 网站建设管理和维护阿里云多网站
  • 企业网站营销典型案例网站建设哪家做的好一点
  • 网站建设包含哪些费用网站网络架构
  • 最好的建站平台湖南建设网塔吊证查询
  • 网站手机站怎么做的php网站漂浮广告代码