VGG16网络介绍及代码撰写详解(总结1)

news2024/12/29 13:58:42

  可以从本人以前的文章中可以看出作者以前从事的是嵌入式控制方面相关的工作,是一个机器视觉小白,之所以开始入门机器视觉的学习只要是一个idea,想把机器视觉与控制相融合未来做一点小东西。废话不多说开始正题。

摘要:本文是介绍VGG16网络,个人对其的知识总结,网络设计的知识点,以及代码如何撰写,基于pytorch编写代码。作为一个刚入门的小白怎么去学习别人的代码,一步一步的去理解每一行代码,怎么将网络设计变成代码,模仿大佬的代码去撰写。作为小白如有不足之处请批评指正哈。

机器视觉基础知识

在此之前,本人学习了机器视觉的基础知识,以下是本人学习时的链接希望对你有所帮助。
https://fuhanghang.blog.csdn.net/article/details/135544761?fromshare=blogdetail&sharetype=blogdetail&sharerId=135544761&sharerefer=PC&sharesource=weixin_52531699&sharefrom=from_link

【计算机视觉与深度学习 北京邮电大学 鲁鹏 清晰版合集(完整版)】 https://www.bilibili.com/video/BV1V54y1B7K3/?share_source=copy_web&vd_source=b25ae79b699fbc0a2f70ccb983f6b74a

VGG16

在网络设计之前需要明白什么是VGG16。
以下是我借鉴的文章的参考链接:
https://blog.csdn.net/weixin_46676835/article/details/129582927?fromshare=blogdetail&sharetype=blogdetail&sharerId=129582927&sharerefer=PC&sharesource=weixin_52531699&sharefrom=from_link

https://blog.csdn.net/weixin_46676835/article/details/128730174?fromshare=blogdetail&sharetype=blogdetail&sharerId=128730174&sharerefer=PC&sharesource=weixin_52531699&sharefrom=from_link
在这里插入图片描述

VGG16是一个深度卷积神经网络,它在2014年由牛津大学视觉几何组(Visual Geometry Group)提出,并在ImageNet图像分类任务中取得了显著的成绩。以下是VGG16的一些关键特点:
1. 网络结构
层数: VGG16包含16个主要的权重层,包括13个卷积层和3个全连接层。
卷积层: VGG16使用小的3x3卷积核进行卷积操作,增加了网络的深度,同时保持了较少的参数数量。
池化层: 每隔几个卷积层后,会使用2x2的最大池化层(Max Pooling)来降低特征图的尺寸,并减少计算量。
2. 激活函数
VGG网络普遍使用ReLU(Rectified Linear Unit)作为激活函数,增加了网络的非线性表达能力。
3. 输入和输出
输入: VGG16接受224x224像素的RGB图像作为输入。
输出: 最终的输出是一个1000维的向量,表示属于1000个类别的概率分布(针对ImageNet数据集)。
4. 特点与优点
深度: VGG16相对较深(16层),使其能够学习更复杂的特征。
简单性: 使用统一的小卷积核和堆叠结构,使得网络设计相对简单且易于理解。
预训练模型: VGG16在多种任务中被广泛应用,并且可以使用预训练权重进行迁移学习,以提高其他任务的性能。
5. 应用方面
VGG16被广泛应用于图像分类、物体检测、图像分割等计算机视觉任务。由于其良好的表现,许多后续的研究和模型(例如,VGG19等)都是在此基础上进行改进的。
在这里插入图片描述
从图中可以看出,VGG16包含16个主要的权重层,包括13个卷积层和3个全连接层。

计算过程

1)输入图像尺寸为224x224x3,经64个通道为3的3x3的卷积核,步长为1,padding=same填充,卷积两次,再经ReLU激活,输出的尺寸大小为224x224x64
2)经max pooling(最大化池化),滤波器为2x2,步长为2,图像尺寸减半,池化后的尺寸变为112x112x64
3)经128个3x3的卷积核,两次卷积,ReLU激活,尺寸变为112x112x128
4)max pooling池化,尺寸变为56x56x128
5)经256个3x3的卷积核,三次卷积,ReLU激活,尺寸变为56x56x256
6)max pooling池化,尺寸变为28x28x256
7)经512个3x3的卷积核,三次卷积,ReLU激活,尺寸变为28x28x512
8)max pooling池化,尺寸变为14x14x512
9)经512个3x3的卷积核,三次卷积,ReLU,尺寸变为14x14x512
10)max pooling池化,尺寸变为7x7x512
11)然后Flatten(),将数据拉平成向量,变成一维512 ×7 ×7=25088
11)再经过两层1x1x4096,一层1x1x1000的全连接层(共三层),经ReLU激活
12)最后通过softmax输出1000个预测结果,这是官方的(最终本人输出3种)

软件代码构思

有了以上理论基础后,开始构建代码思路,整体构建思路如下图所示,写代码之前一定要构思好大致思路,代码永远是为你思路框架服务的。
在这里插入图片描述

训练部分代码撰写

1.参数初始化
这一部分的代码的作用是,首先是关键参数以及头文件的初始化,再然后就是读取jpg文件,以及txt文件的内容,将其分为3类,蚂蚁,蜜蜂,狗,后续品种可以自己添加,jpg,txt训练数据多少都可以由自己添加,但是文件的命名格式必须为1.jpg,1.txt,2.jpg,2.txt才能读取文件。

(1)头文件初始化

import os
import parser
import time
import numpy as np
import torch
import torchvision.transforms as transforms
import PIL
from PIL import Image
from scipy.stats import norm
from torch import nn
from torch.utils.data import Dataset, random_split, DataLoader, ConcatDataset
import torchvision.datasets
from torch import nn
from torch.nn import MaxPool2d, Flatten

import argparse
from pathlib import Path

import os:用于读取文件路径。
import time:用于时间相关的功能,本文用其计算训练时间。
import torchvision.transforms as transforms:用于图像处理和转换的模块,常用于数据预处理。
import torchvision.datasets:提供了常用数据集的接口,例如MNIST、CIFAR等。
from PIL import Image:Pillow库,用于图像的打开、处理和保存。
from torch.nn import MaxPool2d, Flatten:导入特定的层,MaxPool2d用于进行二维最大池化,Flatten将多维输入展平为一维。
from torch.utils.data import Dataset, random_split, DataLoader, ConcatDataset:用于自定义数据集和数据加载器,方便批量处理数据。
import argparse:用于处理命令行参数,使得脚本可以接受用户输入的参数。
from pathlib import Path:提供高级路径操作功能,方便文件系统的操作。

(2)参数定义函数
用parse_opt()这个函数主要是见过许多大佬的文章都是用这个函数转义。作为小白像较为官方的标准化参数结构学习。

def parse_opt():
    parser = argparse.ArgumentParser()  # 创建 ArgumentParser 对象
    parser.add_argument('--epochs', type=int, default=5, help='total training epochs')  # 添加参数
    parser.add_argument('--batch_size', type=int, default=16, help='size of each batch')  # 添加批次大小参数
    parser.add_argument('--learning_rate', type=int, default=0.03, help='size of learning_rate')  # 添加批次大小参数
    #--device "cuda:0,cuda:1" 启用多个设备
    parser.add_argument('--device', default='cuda:0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')  #--device cuda:1
    # 解析参数
    opt = parser.parse_args()
    return opt

opt = parse_opt()  # 调用解析函数
epochs = opt.epochs              # 训练的轮数
batch_size = opt.batch_size      # 每个批次的样本数量
learning_rate = opt.learning_rate
device = torch.device(opt.device)

(3)定义文件处理类
这部分代码的编写主要是要实现一个功能,如何把1.jpg,1.txt,2.jpg,2.txt…文件读取处理,并将txt文件的内容进行处理实现分类。

class CustomDataset(Dataset):
    def __init__(self, img_dir, label_dir, transform=None):
        self.img_dir = img_dir
        self.label_dir = label_dir
        self.transform = transform
        self.img_labels = self.load_labels()

    def load_labels(self):
        label_map = {'ants': 0, 'bees': 1,'dogs': 2}    #字典label_map 来定义标签与数字之间的映射关系
        labels = []                                     #创建一个空列表 labels,用来存储从文件中读取到的标签的数字编码
        for label_file in os.listdir(self.label_dir):
            with open(os.path.join(self.label_dir, label_file), 'r') as f:  #对于每个标签文件,使用 open() 打开文件并读取第一行内容readline()
                line = f.readline().strip()         #strip() 方法用于去掉行首和行尾的空白字符(包括换行符)
                #从label_map 中获取当前行的标签对应的数字编码。如果当前行的内容不在 label_map 中,则返回-1
                labels.append(label_map.get(line, -1))
        return labels

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, idx):
        #使用 os.path.join 将图像目录路径 (self.img_dir) 和图像文件名拼接起来。文件名格式为 idx + 1(即从1开始计数),加上 .jpg 后缀。
        #例如,如果idx为0,则img_path会是 D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\ants_image\\1.jpg
        img_path = os.path.join(self.img_dir, str(idx+1) + '.jpg')
        image = Image.open(img_path).convert('RGB')  #打开指定路径的图像文件,并将其转换为 RGB 模式。
        label = self.img_labels[idx]   #列表中获取当前图像的标签,idx 是当前图像的索引

        if self.transform:   #transform变量是在CustomDataset中最终调用的transforms.Compose实现
            image = self.transform(image)

        return image, label

使用字典 label_map 来定义标签与数字之间的映射关系:‘ants’ 对应 0;‘bees’ 对应 1;‘dogs’ 对应 2。对于每个标签文件,使用 open() 打开文件并读取第一行内容(readline())。strip() 方法用于去掉行首和行尾的空白字符(包括换行符)。使用 label_map.get(line, -1) 从 label_map 中获取当前行的标签对应的数字编码。如果当前行的内容不在 label_map 中,则返回 -1。本人当时在这就犯了一个错误。可以从调试的框中看出,我有一个txt文件的内容并不是dogs致使其返回的值为-1,出现这个问题的原因是本人txt文件中dogs没有保存,所以切记一定要保存文件如果自己训练模型自建数据集的话。很多内容方面,注释写的很清楚,作者就不细讲了。
在这里插入图片描述
订正错误后的结果:
在这里插入图片描述

(4)数据集图片输入
代码需要实现一个图像大小设置功能,由于输入图像大小不确定需要将其设置成224×224大小图片,满足输入网络图像大小的设定。

# 定义转换,包括调整大小
transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 调整为 224x224 大小
    transforms.ToTensor(),          # 转换为 Tensor
    torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    # 可以添加其他转换,例如归一化等
])

# 初始化数据集
ants_data = CustomDataset(
    img_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\ants_image',
    label_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\ants_label',
    transform=transform
)


bees_data = CustomDataset(
    img_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\bees_image',
    label_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\bees_label',
    transform=transform
)

dogs_data = CustomDataset(
    img_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\dogs_image',
    label_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\dogs_label',
    transform=transform
)

transform 是通过 transforms.Compose 创建的一个组合转换对象。这个对象整合了多个图像预处理步骤,包括调整大小、转换为张量,以及归一化。调整大小: 将图像调整为 224x224 像素。转换为 Tensor: 将 PIL 图像或 NumPy 数组转换为 PyTorch 张量。归一化: 根据给定的均值和标准差对张量进行归一化处理,使得数据分布更适合神经网络训练。归一化是VGG16网络的要求,但是本人发现没有也行,但所提供的代码为按照官方要求编写的。
ants_data 是 CustomDataset 类的一个实例,用于加载蚂蚁图像数据集。
img_dir 指定了存放图像文件的目录。
label_dir 指定了存放标签文件的目录。
transform 是应用于图像的转换操作(如缩放、裁剪、归一化等),即在加载图像时对其进行预处理。也就是调用transforms.Compose 。
从代码中也可以看出,实现的效果是读取图像读取标签然后通过transform将其变成224x224。
在这里插入图片描述
训练图片标签地址如图所示。

2.将数据集总和并随机打乱并分成训练集测试集

这段代码的作用是将我所读取的数据ants_data+bees_data+dogs_data总和起来,然后随机打乱,分割出训练集测试集分别是7:3。

# 合并数据集
total_data = ants_data+bees_data
total_data = total_data+dogs_data
# 计算训练集和验证集的大小
total_size = len(total_data)
train_size = int(0.7 * total_size)  # 70%
val_size = total_size - train_size    # 30%

train_data_size = train_size
val_data_size = val_size

print("训练集长度:{}".format(train_data_size))
print("测试集长度:{}".format(val_data_size))
# 随机分割数据集
train_data, val_data = random_split(total_data, [train_size, val_size])

# 创建 DataLoader,分成小批量(batches),以便于进行训练和验证
train_dataloader = DataLoader(train_data, batch_size, shuffle=True) #shuffle=True可以随机打乱数据
val_dataloader = DataLoader(val_data, batch_size, shuffle=False)
# 打印数据集大小在这里插入图片描述

print("训练集大小:{}".format(len(train_data)))
print("验证集大小:{}".format(len(val_data)))

在这里插入图片描述
从调试图中可知ants_data = 110,bees_data = 70,dogs_data = 120,total_data = 300,这也是我训练时提供的图片数目,标太多太累了。

3.创建网络模型
这部分是根据上图的vgg16网络结构搭建的。具体代码如下,本人写法为小白写法应该通俗易懂。

class VGG16(nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        self.model = nn.Sequential(  # 构建子类,可以减少foward代码撰写
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), #最大池化将224变成112
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 56x56
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 28x28
            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 14x14
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 7x7
        )
        # 使用AdaptiveAvgPool2d来确保输出为固定大小
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))

        # 全连接层
        self.fc1 = nn.Linear(in_features=512 * 7 * 7, out_features=4096)
        self.fc2 = nn.Linear(in_features=4096, out_features=4096)
        self.fc3 = nn.Linear(in_features=4096, out_features=3)

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.3)

    def forward(self, x):
        x = self.model(x)
        x = self.avgpool(x)  # 使用自适应平均池化
        x = x.view(x.size(0), -1)  # 拉平操作,(batch_size, num_features) 也就是(8,25088) 512*7*7 = 25088
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x
x = self.avgpool(x) 自适应平均池化,将输入特征图(x)的空间维度降低到固定大小。
x = x.view(x.size(0), -1)  这行代码将输出张量 x 的形状变为 (batch_size, num_features),
其中 batch_size 是当前批次的样本数量,num_features 是特征的总数。这一步是为了将多维张量转换为二维张量,
以便进入全连接层进行分类。

4.训练加测试
这部分得先明白工作步骤再写代码。1.训练集训练调用VGG16网络结构,损失函数构建,随机梯度下降,优化训练 2.测试集测试,设置为评估模式,调用VGG16网络结构,损失函数构建,损失计算。3.输出训练损失,测试损失,准确率。这部分是机器视觉基础概念知识。

def train():
    My_VGG16 = VGG16()              #调用类
    My_VGG16 = My_VGG16.to(device)

    # (1).损失函数构建
    loss_fn = nn.CrossEntropyLoss()  #计算预测值与真实标签之间的差异
    loss_fn = loss_fn.to(device)     #将模型和数据都放在同一个设备上,GPU

    # (2).优化器
    # #随机梯度下降(Stochastic Gradient Descent)优化器的一种实现。SGD 是一种常见的优化算法
    optimizer = torch.optim.SGD(My_VGG16.parameters(), lr=learning_rate)


    # 用于存储损失和准确率
    train_losses = []
    val_losses = []
    accuracies = []

    for i in range(epochs):
        loss_temp = 0  # 临时变量
        print("--------第{}轮训练开始--------".format(i + 1))

        # 训练阶段
        My_VGG16.train()  # 设置为训练模式
        #data有两个部分分别是(8,3,224,224),(8,) 一定要注意顺序
        for data in train_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            #outputs = tensor(8,3)
            outputs = My_VGG16(imgs)
            loss = loss_fn(outputs, targets)

            # 优化器优化模型
            optimizer.zero_grad()  # 梯度清零
            loss.backward()  # 反向传播
            optimizer.step()  # 梯度更新

            loss_temp += loss.item()

        # 记录训练损失
        train_losses.append(loss_temp / len(train_dataloader))

        # 测试阶段
        My_VGG16.eval()  # 设置为评估模式
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in val_dataloader:
                imgs, targets = data
                imgs = imgs.to(device)
                targets = targets.to(device)
                outputs = My_VGG16(imgs)
                loss = loss_fn(outputs, targets)

                total_test_loss += loss.item()
                #找到每个样本的预测类别,然后与真实标签进行比较
                accuracy = (outputs.argmax(1) == targets).sum().item()  # 使用.item()将Tensor转换为Python数值
                total_accuracy += accuracy   #计算正确预测的数量并累加到 total_accuracy
imgs, targets = data这段代码

在这里插入图片描述
可以从图中看出data有两个部分分别是(8,3,224,224),(8,) 一定要注意顺序分别对应的就是imgs,targets

outputs = My_VGG16(imgs)  outputs = tensor(83

在这里插入图片描述

outputs单纯的就是输出品种结果,8是因为我的batch_size = 8,输入的是8张图片。
5.保存数据至.pth文件并绘图
这部分代码的编写需要实现的是,将训练的权值保存至.pth文件中,并在窗口中显示训练时的时间,loss,准确率输出考虑到train_loss为list:5,所以直接使用循环的打印输出效果。
在这里插入图片描述

  # 记录验证损失和准确率
        val_losses.append(total_test_loss / len(val_dataloader))
        accuracies.append(total_accuracy / val_data_size)

        print("整体测试集上的正确率:{}".format(total_accuracy / val_data_size))
        print("整体测试集上的Loss:{}".format(total_test_loss))

    # 保存模型
    torch.save(My_VGG16, "vgg16_{}.pth".format(2))
    torch.save(My_VGG16.state_dict(), "vgg16_dict_{}.txt".format(2))
    print("模型已保存")

    # 绘制损失和准确率曲线
    epochs_range = range(1, epochs + 1)

    plt.figure(figsize=(12, 5))  #创建一个新的图形窗口,设置图形的大小

    # 训练和验证损失
    plt.subplot(1, 2, 1)  #第一个图像框
    plt.plot(epochs_range, train_losses, label='Training Loss')
    #train_losses=[1.0952595781396937, 1.085733965591148, 1.07982603708903, 1.0776626953372248, 1.0816458507820412]
    plt.plot(epochs_range, val_losses, label='Validation Loss')
    plt.title('Loss vs Epochs')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    # 准确率
    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, accuracies, label='Validation Accuracy')
    plt.title('Accuracy vs Epochs')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()

    plt.tight_layout()
    plt.show()


if __name__ == '__main__':
    parse_opt()
    train()
    # 测试模型
    # model = VGG16()
    # sample_input = torch.randn(1, 3, 224, 224)  # Batch size 为 1
    # output = model(sample_input)
    # print("Final output shape:", output.shape)  # 应输出 [1, 3]

代码讲解,注释很详细,不做细数。
在这里插入图片描述
输出图片效果,这是只训练了5次的网络,可以看到训练效果是不理想的。
以上就是训练集代码及讲解。
训练整体代码:

#VGG_16训练网络模型,第一代测试代码,主要功能如下
#识别3类蚂蚁,蜜蜂,狗,可任意添加,文件名为1.jpg,1.txt,内容ants
#2代是在一代的基础上添加了图像显示,标准化参数设计方面
import os
import parser
import time
import numpy as np
import torch
import torchvision.transforms as transforms
import PIL
from PIL import Image
from scipy.stats import norm
from torch import nn
from torch.utils.data import Dataset, random_split, DataLoader, ConcatDataset
import torchvision.datasets
from torch import nn
from torch.nn import MaxPool2d, Flatten

import argparse
from pathlib import Path


# ------------------------------1.初始化,标准参数初始化-------------------------------
def parse_opt():
    parser = argparse.ArgumentParser()  # 创建 ArgumentParser 对象
    parser.add_argument('--epochs', type=int, default=5, help='total training epochs')  # 添加参数
    parser.add_argument('--batch_size', type=int, default=8, help='size of each batch')  # 添加批次大小参数
    parser.add_argument('--learning_rate', type=int, default=0.03, help='size of learning_rate')  # 添加批次大小参数
    #--device "cuda:0,cuda:1" 启用多个设备
    parser.add_argument('--device', default='cuda:0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')  #--device cuda:1
    # 解析参数
    opt = parser.parse_args()
    return opt

opt = parse_opt()  # 调用解析函数
epochs = opt.epochs              # 训练的轮数
batch_size = opt.batch_size      # 每个批次的样本数量
learning_rate = opt.learning_rate
device = torch.device(opt.device)

class CustomDataset(Dataset):
    def __init__(self, img_dir, label_dir, transform=None):
        self.img_dir = img_dir
        self.label_dir = label_dir
        self.transform = transform
        self.img_labels = self.load_labels()

    def load_labels(self):
        label_map = {'ants': 0, 'bees': 1,'dogs': 2}    #字典label_map 来定义标签与数字之间的映射关系
        labels = []                                     #创建一个空列表 labels,用来存储从文件中读取到的标签的数字编码
        for label_file in os.listdir(self.label_dir):
            with open(os.path.join(self.label_dir, label_file), 'r') as f:  #对于每个标签文件,使用 open() 打开文件并读取第一行内容readline()
                line = f.readline().strip()         #strip() 方法用于去掉行首和行尾的空白字符(包括换行符)
                #从label_map 中获取当前行的标签对应的数字编码。如果当前行的内容不在 label_map 中,则返回-1
                labels.append(label_map.get(line, -1))
        return labels

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, idx):
        #使用 os.path.join 将图像目录路径 (self.img_dir) 和图像文件名拼接起来。文件名格式为 idx + 1(即从1开始计数),加上 .jpg 后缀。
        #例如,如果idx为0,则img_path会是 D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\ants_image\\1.jpg
        img_path = os.path.join(self.img_dir, str(idx+1) + '.jpg')
        image = Image.open(img_path).convert('RGB')  #打开指定路径的图像文件,并将其转换为 RGB 模式。
        label = self.img_labels[idx]   #列表中获取当前图像的标签,idx 是当前图像的索引

        if self.transform:   #transform变量是在CustomDataset中最终调用的transforms.Compose实现
            image = self.transform(image)

        return image, label

# 定义转换,包括调整大小
transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 调整为 224x224 大小
    transforms.ToTensor(),          # 转换为 Tensor
    torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    # 可以添加其他转换,例如归一化等
])

# 初始化数据集
ants_data = CustomDataset(
    img_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\ants_image',
    label_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\ants_label',
    transform=transform
)


bees_data = CustomDataset(
    img_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\bees_image',
    label_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\bees_label',
    transform=transform
)

dogs_data = CustomDataset(
    img_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\dogs_image',
    label_dir='D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\train\\dogs_label',
    transform=transform
)
#-----------------------2.将数据集总和并随机打乱并分成训练集测试集-------------------
# 合并数据集
total_data = ants_data+bees_data
total_data = total_data+dogs_data
# 计算训练集和验证集的大小
total_size = len(total_data)
train_size = int(0.7 * total_size)  # 70%
val_size = total_size - train_size    # 30%

train_data_size = train_size
val_data_size = val_size

print("训练集长度:{}".format(train_data_size))
print("测试集长度:{}".format(val_data_size))
# 随机分割数据集
train_data, val_data = random_split(total_data, [train_size, val_size])

# 创建 DataLoader,分成小批量(batches),以便于进行训练和验证
train_dataloader = DataLoader(train_data, batch_size, shuffle=True) #shuffle=True可以随机打乱数据
val_dataloader = DataLoader(val_data, batch_size, shuffle=False)
# 打印数据集大小
print("训练集大小:{}".format(len(train_data)))
print("验证集大小:{}".format(len(val_data)))

#----------------------------3.创建网络模型--------------------------------
class VGG16(nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        self.model = nn.Sequential(  # 构建子类,可以减少foward代码撰写
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), #最大池化将224变成112
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 56x56
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 28x28
            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 14x14
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 7x7
        )
        # 使用AdaptiveAvgPool2d来确保输出为固定大小
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))

        # 全连接层
        self.fc1 = nn.Linear(in_features=512 * 7 * 7, out_features=4096)
        self.fc2 = nn.Linear(in_features=4096, out_features=4096)
        self.fc3 = nn.Linear(in_features=4096, out_features=3)

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.3)

    def forward(self, x):
        x = self.model(x)
        x = self.avgpool(x)  # 使用自适应平均池化
        x = x.view(x.size(0), -1)  # 拉平操作,(batch_size, num_features) 也就是(8,25088) 512*7*7 = 25088
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x

#------------------------------------4.训练加测试-----------------------

import numpy as np
import matplotlib
matplotlib.use('TkAgg')  # 或者尝试 'Qt5Agg',有这行代码会多一个弹窗显示
import matplotlib.pyplot as plt
import torch.nn as nn


def train():
    My_VGG16 = VGG16()              #调用类
    My_VGG16 = My_VGG16.to(device)

    # (1).损失函数构建
    loss_fn = nn.CrossEntropyLoss()  #计算预测值与真实标签之间的差异
    loss_fn = loss_fn.to(device)     #将模型和数据都放在同一个设备上,GPU

    # (2).优化器
    # #随机梯度下降(Stochastic Gradient Descent)优化器的一种实现。SGD 是一种常见的优化算法
    optimizer = torch.optim.SGD(My_VGG16.parameters(), lr=learning_rate)


    # 用于存储损失和准确率
    train_losses = []
    val_losses = []
    accuracies = []

    for i in range(epochs):
        loss_temp = 0  # 临时变量
        print("--------第{}轮训练开始--------".format(i + 1))

        # 训练阶段
        My_VGG16.train()  # 设置为训练模式
        #data有两个部分分别是(8,3,224,224),(8,) 一定要注意顺序
        for data in train_dataloader:
            imgs, targets = data
            imgs = imgs.to(device)
            targets = targets.to(device)
            #outputs = tensor(8,3)
            outputs = My_VGG16(imgs)
            loss = loss_fn(outputs, targets)

            # 优化器优化模型
            optimizer.zero_grad()  # 梯度清零
            loss.backward()  # 反向传播
            optimizer.step()  # 梯度更新

            loss_temp += loss.item()

        # 记录训练损失
        train_losses.append(loss_temp / len(train_dataloader))

        # 测试阶段
        My_VGG16.eval()  # 设置为评估模式
        total_test_loss = 0
        total_accuracy = 0
        with torch.no_grad():
            for data in val_dataloader:
                imgs, targets = data
                imgs = imgs.to(device)
                targets = targets.to(device)
                outputs = My_VGG16(imgs)
                loss = loss_fn(outputs, targets)

                total_test_loss += loss.item()
                #找到每个样本的预测类别,然后与真实标签进行比较
                accuracy = (outputs.argmax(1) == targets).sum().item()  # 使用.item()将Tensor转换为Python数值
                total_accuracy += accuracy   #计算正确预测的数量并累加到 total_accuracy
#-----------------------5.保存数据至.pth文件并绘图---------------------------------------------------
        # 记录验证损失和准确率
        val_losses.append(total_test_loss / len(val_dataloader))
        accuracies.append(total_accuracy / val_data_size)

        print("整体测试集上的正确率:{}".format(total_accuracy / val_data_size))
        print("整体测试集上的Loss:{}".format(total_test_loss))

    # 保存模型
    torch.save(My_VGG16, "vgg16_{}.pth".format(2))
    torch.save(My_VGG16.state_dict(), "vgg16_dict_{}.txt".format(2))
    print("模型已保存")

    # 绘制损失和准确率曲线
    epochs_range = range(1, epochs + 1)

    plt.figure(figsize=(12, 5))  #创建一个新的图形窗口,设置图形的大小

    # 训练和验证损失
    plt.subplot(1, 2, 1)  #第一个图像框
    plt.plot(epochs_range, train_losses, label='Training Loss')
    #train_losses=[1.0952595781396937, 1.085733965591148, 1.07982603708903, 1.0776626953372248, 1.0816458507820412]
    plt.plot(epochs_range, val_losses, label='Validation Loss')
    plt.title('Loss vs Epochs')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    # 准确率
    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, accuracies, label='Validation Accuracy')
    plt.title('Accuracy vs Epochs')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()

    plt.tight_layout()
    plt.show()


if __name__ == '__main__':
    parse_opt()
    train()
    # 测试模型
    # model = VGG16()
    # sample_input = torch.randn(1, 3, 224, 224)  # Batch size 为 1
    # output = model(sample_input)
    # print("Final output shape:", output.shape)  # 应输出 [1, 3]


注意本人的训练是用的gpu,不推荐用cpu跑代码训练。

检测部分代码撰写

这一部分的代码主要实现的是输入任意大小的图片,预测出它的种类。

1.导入头文件参数初始化
导入头文件,参数标准化后,读取图片,并通过transform实现图片输入格式的设置。


import torch
import torchvision
from PIL import Image
from torch import nn
from torch.nn import MaxPool2d
import argparse
from pathlib import Path

#--------------------------1.参数数据初始化-----------------------------------
def parse_opt():
    parser = argparse.ArgumentParser()  # 创建 ArgumentParser 对象
    parser.add_argument('--Dropout_Val', type=int, default=0.4, help='Dropout_Val number')  # 添加参数
    #--device "cuda:0,cuda:1" 启用多个设备
    parser.add_argument('--device', default='cuda:0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')  #--device cuda:1
    # 解析参数
    opt = parser.parse_args()
    return opt


opt = parse_opt()  # 调用解析函数
Dropout_Val = opt.Dropout_Val
device = torch.device(opt.device)


image_path = "D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\1.jpg"
image = Image.open(image_path)   #注意格式为RGBA
print(image)

image = image.convert("RGB")     #将格式变成RGB
print(image)

#确保你的图像预处理步骤(如归一化)与模型训练时使用的步骤相匹配。通常,VGG16 需要将图像归一化到 [0, 1] 范围内,
# 并可能需要减去均值和除以标准差
transform = torchvision.transforms.Compose([
    torchvision.transforms.Resize((224, 224)),
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

image = transform(image)
print(image.shape)

# 类别标签
class_names = ['ant', 'bee', 'dog']  # 根据实际情况填写

2.搭建VGG16网络结构

class VGG16(nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        self.model = nn.Sequential(  # 构建子类,可以减少foward代码撰写
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), #最大池化将224变成112
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 56x56
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 28x28
            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 14x14
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 7x7
        )
        # 使用AdaptiveAvgPool2d来确保输出为固定大小
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))

        # 全连接层
        self.fc1 = nn.Linear(in_features=512 * 7 * 7, out_features=4096)
        self.fc2 = nn.Linear(in_features=4096, out_features=4096)
        self.fc3 = nn.Linear(in_features=4096, out_features=3)

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(Dropout_Val)

    def forward(self, x):
        x = self.model(x)
        x = self.avgpool(x)  # 使用自适应平均池化
        x = x.view(x.size(0), -1)  # 拉平操作
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x

3.输出测试结果
输出测试结果需要将图片送入VGG16网络进行计算,带入训练好的权值vgg16_1.pth,得出预测结果,将预测的结果输出成概率从而计算是什么品种。

model = VGG16()
model = torch.load("vgg16_1.pth")
#模型的权重在GPU上,而输入张量在CPU上。要解决这个问题,你可以将输入张量移动到GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
image = image.to(device)
#模型的权重在CPU上,而输入张量在GPU上。要解决这个问题,你可以将输入张量移动到CPU
#model = torch.load("tudui_0.pth",map_location=torch.device('cpu'))
image = torch.reshape(image,(1,3,224,224))
model.eval()
with torch.no_grad():
    output = model(image)
predicted_index = output.argmax(1).item()  # 获取预测的索引
print(output.argmax(1))
print(output)   #输出结果预测的是第六个类别
print("Predicted index:", predicted_index)
# 输出对应的类别名称
print("Predicted class:", class_names[predicted_index])

测试输出效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其实多训练几次预测效果还是可以的。

测试整体代码

# vgg16网络测试文件,第一代测试代码,主要功能如下
# 任意输入一张图片,输出判断类型,二代与一代相比就是多了参数标准化

import torch
import torchvision
from PIL import Image
from torch import nn
from torch.nn import MaxPool2d
import argparse
from pathlib import Path

#--------------------------1.参数数据初始化-----------------------------------
def parse_opt():
    parser = argparse.ArgumentParser()  # 创建 ArgumentParser 对象
    parser.add_argument('--Dropout_Val', type=int, default=0.4, help='Dropout_Val number')  # 添加参数
    #--device "cuda:0,cuda:1" 启用多个设备
    parser.add_argument('--device', default='cuda:0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')  #--device cuda:1
    # 解析参数
    opt = parser.parse_args()
    return opt


opt = parse_opt()  # 调用解析函数
Dropout_Val = opt.Dropout_Val
device = torch.device(opt.device)


image_path = "D:\\Pycharm\\Pytorch_test\\数据集\\练手数据集\\1.jpg"
image = Image.open(image_path)   #注意格式为RGBA
print(image)

image = image.convert("RGB")     #将格式变成RGB
print(image)

#确保你的图像预处理步骤(如归一化)与模型训练时使用的步骤相匹配。通常,VGG16 需要将图像归一化到 [0, 1] 范围内,
# 并可能需要减去均值和除以标准差
transform = torchvision.transforms.Compose([
    torchvision.transforms.Resize((224, 224)),
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

image = transform(image)
print(image.shape)

# 类别标签
class_names = ['ant', 'bee', 'dog']  # 根据实际情况填写

#-------------------------------------2.搭建VGG16网络结构---------------------------------------------
class VGG16(nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        self.model = nn.Sequential(  # 构建子类,可以减少foward代码撰写
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), #最大池化将224变成112
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 56x56
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 28x28
            nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2), # 14x14
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 7x7
        )
        # 使用AdaptiveAvgPool2d来确保输出为固定大小
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7))

        # 全连接层
        self.fc1 = nn.Linear(in_features=512 * 7 * 7, out_features=4096)
        self.fc2 = nn.Linear(in_features=4096, out_features=4096)
        self.fc3 = nn.Linear(in_features=4096, out_features=3)

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(Dropout_Val)

    def forward(self, x):
        x = self.model(x)
        x = self.avgpool(x)  # 使用自适应平均池化
        x = x.view(x.size(0), -1)  # 拉平操作
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x

#---------------------------3.输出测试结果-----------------------------
model = VGG16()
model = torch.load("vgg16_1.pth")
#模型的权重在GPU上,而输入张量在CPU上。要解决这个问题,你可以将输入张量移动到GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
image = image.to(device)
#模型的权重在CPU上,而输入张量在GPU上。要解决这个问题,你可以将输入张量移动到CPU
#model = torch.load("tudui_0.pth",map_location=torch.device('cpu'))
image = torch.reshape(image,(1,3,224,224))
model.eval()
with torch.no_grad():
    output = model(image)
predicted_index = output.argmax(1).item()  # 获取预测的索引
print(output.argmax(1))
print(output)   #输出结果预测的是第六个类别
print("Predicted index:", predicted_index)
# 输出对应的类别名称
print("Predicted class:", class_names[predicted_index])

最开始训练的次数有点少,导致预测结果不理想,后来去吃饭前设置训练epochs = 500次,回来发现结果变好了一点。提高准确率的方式很多,还可以丰富训练集。注意我的权值文件权值vgg16_1.pth,可能使用的是权值vgg16_2.pth,代码我就不改了。主要是要理解以上代码。

以上就是本人的心得与总结,如有不足之处请多多包涵。
百度网盘链接代码权值及训练图片: https://pan.baidu.com/s/14xixodOGVEMK-KpE5rZTtw
提取码: dwrg

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

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

相关文章

超便携专业AI大师本带来生产力跃升,联想ThinkPad P1 AI 2024 AI元启版上市

随着AI技术在各行业的广泛应用,其实际效用愈加突出。无论是4K视频生成、建筑设计,还是仿真实验等专业领域,AI技术的支持使得过去需要数小时完成的任务如今分钟级即可完成。AI能够生成与人类创作者风格相似的内容,极大地提高了内容…

【Linux】基于驱动框架的程序编写测试

【Linux】基于驱动框架的程序编写测试 字符设备驱动工作原理☆ 驱动程序开发驱动程序开发步骤驱动代码框架驱动框架设计流程 编译与测试编译测试 参考博文: 【Linux】基于框架编写驱动代码、驱动代码编译和测试 Linux驱动(驱动程序开发、驱动框架代码编…

智能摄像头DIY教程

你要去度假,想看看家里的情况吗?你想了解人工智能和计算机视觉吗?你有 Raspberry Pi、网络摄像头和一些空闲时间吗?那么这个项目就是为你准备的! 在本文中,我们将介绍如何使用 Raspberry Pi 在 Python 中创…

Matlab|考虑阶梯式碳交易与供需灵活双响应的综合能源系统优化调度

目录 1 主要内容 目标函数 模型: 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序方法复现《考虑阶梯式碳交易与供需灵活双响应的综合能源系统优化调度》,提出了供需灵活双响应机制,供应侧引入有机朗肯循环实现热电联产机组热电输…

MongoDB入门:安装及环境变量配置

一、安装MonggoDB Windows系统安装MongoDB 1、下载MongoDB安装包 访问MongoDB官方网站,选择与Windows系统相匹配的MongoDB Community Server版本进行下载。 Download MongoDB Community Server | MongoDB 2、安装MongoDB 双击下载好的安装包文件,根…

从Midjourney到秒画:探索国产AI绘图的崛起与未来

最近,许多人在询问: 是否有优秀的国产AI绘图产品? 如果让我推荐一款AI绘图工具,那毫无疑问是Midjourney。它在AI绘图领域的地位堪比OpenAI在人工智能领域的影响力,处于领先的水平。 不过,Midjourney的使用…

[Linux]僵尸进程,孤儿进程,环境变量

希望你开心,希望你健康,希望你幸福,希望你点赞! 最后的最后,关注喵,关注喵,关注喵,大大会看到更多有趣的博客哦!!! 喵喵喵,你对我真的…

Unity 查看Inspectors组件时严重掉帧

遇到一个问题,就是运行一个脚本的时候,只要我查看它的Inspectors,就会严重掉帧。 原本是30fps,只要查看这个组件,就掉到5fps。 这还真有点像波粒二象性,一观察就会掉帧,不观察就正常。 using…

【Ubuntu】minicom安装、配置、使用以及退出

目录 1 安装 2 配置 3 使用 4 退出 minicom是一个串口通信的工具,以root权限登录系统,可用来与串口设备通信。 1 安装 sudo apt-get install minicom 2 配置 使用如下命令进入配置界面: sudo minicon -s 进入配置界面后,…

Html2OpenXml:HTML转化为OpenXml的.Net库,轻松实现Html转为Word。

推荐一个开源库,轻松实现HTML转化为OpenXml。 01 项目简介 Html2OpenXml 是一个开源.Net库,旨在将简单或复杂的HTML内容转换为OpenXml组件。 该项目始于2009年,最初是为了将用户评论转换为Word文档而设计的 随着时间的推移,Ht…

人工智能技术在电磁场与微波技术专业的应用

在人工智能与计算电磁学的融合背景下,电磁学的研究和应用正在经历一场革命。计算电磁 学是研究电磁场和电磁波在不同介质中的传播、散射和辐射等问题的学科,它在通信、雷达、无 线能量传输等领域具有广泛的应用。随着人工智能技术的发展,这一…

清美项目 vue总结

vue绑定表单验证 <el-form ref"classform" :model"classform" :rules"classRules" label-width"80px"><el-form-item label"转入班级" prop"classId"><el-select v-model"classform.classId&…

HTML流光爱心

文章目录 序号目录1HTML满屏跳动的爱心&#xff08;可写字&#xff09;2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心&#xff08;简易版&#xff09;7HTML粒子爱心8HTML蓝色动态爱心9HTML跳动的爱心&#xff08;双心版&#xff09;1…

剩余电流继电器在轨道交通地铁车站的应用

0应用背景 城市轨道交通设备复杂、量大、分布广&#xff0c;在长期持续运行的过程中&#xff0c;存在潜在的火灾风险隐患。在国内外发生的地铁火灾事件中&#xff0c;电气原因引发的火灾占比最高&#xff0c;高达37%&#xff0c;其中&#xff0c;漏电流是重要因素。地铁车站电…

【网络】手动部署内网穿透(超详细教程)

一、环境搭建 本篇文章讲的是 服务器frp转发数据的方式 frp 下载&#xff1a;https://github.com/fatedier/frp/releases/tag/v0.58.1 如果无法访问githup&#xff0c;在如下连接下载一个加速器 Watt Toolkit 官网&#xff1a;https://steampp.net/ 下载完成以后&#xff0…

京东商品详情数据接口功能介绍?API接口介绍

京东商品详情数据接口是京东开放平台提供的一组应用程序编程接口&#xff08;API&#xff09;&#xff0c;允许开发者通过编程方式获取京东商城上特定商品的详细信息。这些接口为商家、第三方开发者以及消费者提供了丰富的数据支持&#xff0c;有助于提升电商平台的运营效率、用…

ODA(Open Design Alliance)试用小记-ODA提供源码下载就完全可控了吗?

1.概述 ODA(Open Design Alliance)库架构如下&#xff1a; 产品体系如下&#xff1a; ODA的产品体系越来越壮大&#xff0c;包括主流BIM格式SDK、Viewer、Cloud、数据交换等&#xff0c;每个模块需要单独购买&#xff0c;并提供“源码服务”。 2.是否可控&#xff1f; 值得…

Tensorflow2.0

Tensorflow2.0 有深度学习基础的建议直接看class3 class1 介绍 人工智能3学派 行为主义:基于控制论&#xff0c;构建感知-动作控制系统。(控制论&#xff0c;如平衡、行走、避障等自适应控制系统) 符号主义:基于算数逻辑表达式&#xff0c;求解问题时先把问题描述为表达式…

机器学习周报(9.23-9.29)

文章目录 摘要Abstract1 自监督学习&#xff08;Self-Supervised Learning&#xff09;1.1 BERT1.1.1 Masking Input1.1.2 Next Sentence Prediction1.1.3 BERT的使用方式 1.2 Why does BERT work?1.3 Multi-lingual BERT 2 pytorch中tensor相关函数学习使用2.1 张量拼接与拆分…

【Linux】磁盘分区挂载网络配置进程【更详细,带实操】

Linux全套讲解系列&#xff0c;参考视频-B站韩顺平&#xff0c;本文的讲解更为详细 目录 一、磁盘分区挂载 1、磁盘分区机制 2、增加磁盘应用实例 3、磁盘情况查询 4、磁盘实用指令 二、网络配置 1、NAT网络原理图 2、网络配置指令 3、网络配置实例 4、主机名和host…