Pytorch | 从零构建Vgg对CIFAR10进行分类

Pytorch | 从零构建Vgg对CIFAR10进行分类

  • CIFAR10数据集
  • Vgg
    • 网络结构
    • 特点
    • 性能
    • 应用
    • 影响
  • Vgg结构代码详解
    • 结构代码
    • 代码详解
      • 特征提取层 _make_layers
      • 前向传播 forward
  • 训练过程和测试结果
  • 代码汇总
    • vgg.py
    • train.py
    • test.py

前面文章我们构建了AlexNet对CIFAR10进行分类:
Pytorch | 从零构建AlexNet对CIFAR10进行分类
这篇文章我们来构建Vgg.

CIFAR10数据集

CIFAR-10数据集是由加拿大高级研究所(CIFAR)收集整理的用于图像识别研究的常用数据集,基本信息如下:

  • 数据规模:该数据集包含60,000张彩色图像,分为10个不同的类别,每个类别有6,000张图像。通常将其中50,000张作为训练集,用于模型的训练;10,000张作为测试集,用于评估模型的性能。
  • 图像尺寸:所有图像的尺寸均为32×32像素,这相对较小的尺寸使得模型在处理该数据集时能够相对快速地进行训练和推理,但也增加了图像分类的难度。
  • 类别内容:涵盖了飞机(plane)、汽车(car)、鸟(bird)、猫(cat)、鹿(deer)、狗(dog)、青蛙(frog)、马(horse)、船(ship)、卡车(truck)这10个不同的类别,这些类别都是现实世界中常见的物体,具有一定的代表性。

下面是一些示例样本:
在这里插入图片描述

Vgg

VGG网络是由牛津大学视觉几何组(Visual Geometry Group)提出的一种深度卷积神经网络,在2014年的ILSVRC竞赛中获得了亚军。以下是对其详细介绍:

网络结构

  • 卷积层:VGG网络由多个卷积层组成,其卷积核大小通常为3×3。采用小卷积核的好处是可以在减少参数数量的同时,增加网络的深度,从而提高网络的表达能力。
  • 池化层:在卷积层之间穿插着池化层,通常采用最大池化,池化核大小为2×2,步长为2。池化操作可以降低特征图的分辨率,减少计算量,同时也可以提取出更具代表性的特征。
  • 全连接层:经过多个卷积和池化层后,网络将得到的特征图展开并连接到全连接层。全连接层用于对特征进行分类或回归等操作。
    在这里插入图片描述
    上图即为Vgg论文中提出的六种不同的架构,本文的 vgg.py 代码中均进行了实现.

特点

  • 结构简洁:VGG网络的结构相对简单且规整,主要由一系列的3×3卷积核和2×2池化核堆叠而成,这种简洁的结构易于理解和实现,也方便进行修改和扩展。
  • 深度较深:VGG网络通常有16或19层,是当时比较深的神经网络之一。通过增加网络的深度,能够学习到更高级的特征表示,从而提高了图像分类的准确率。
  • 小卷积核:使用3×3的小卷积核替代了传统的大卷积核,减少了参数数量,降低了计算量,同时也有助于提高网络的泛化能力。

性能

  • 在图像分类任务上表现出色:在ILSVRC竞赛等图像分类基准测试中取得了很好的成绩,证明了其在图像特征提取和分类方面的有效性。
  • 模型泛化能力较好:由于其深度和小卷积核的设计,VGG网络能够学习到具有较强泛化能力的特征,对不同的图像数据集和任务具有一定的适应性。

应用

  • 图像分类:广泛应用于各种图像分类任务,如人脸识别、物体识别、场景分类等。可以对输入图像进行分类,确定其所属的类别。
  • 目标检测:在目标检测任务中,VGG网络可以作为特征提取器,提取图像中的特征,为后续的目标定位和分类提供基础。
  • 图像分割:也可用于图像分割任务,通过对图像进行像素级的分类,将图像分割成不同的区域或物体。

影响

  • 推动了卷积神经网络的发展:VGG网络的成功激发了更多研究者对深度卷积神经网络的兴趣,推动了该领域的快速发展。
  • 为后续网络设计提供了参考:其简洁的结构和小卷积核的设计理念为后续许多卷积神经网络的设计提供了重要的参考,如ResNet等网络在一定程度上借鉴了VGG的思想。

Vgg结构代码详解

结构代码

import torch
import torch.nn as nncfg = {'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],'A-LRN' : [64, 'LRN', 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], 'C': [64, 64, 'M', 128, 128, 'M', 256, 256, 'conv1-256', 'M', 512, 512, 'conv1-512', 'M', 512, 512, 'conv1-512', 'M'], 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], 'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M']
}class Vgg(nn.Module):def __init__(self, cfg_vgg, num_classes):super(Vgg, self).__init__()self.features = self._make_layers(cfg_vgg)self.classifier = nn.Sequential(nn.Linear(512, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, num_classes))def forward(self, x):x = self.features(x)x = x.view(x.size()[0], -1)x = self.classifier(x)return xdef _make_layers(self, cfg_vgg):layers = []in_channels = 3for i in cfg[cfg_vgg]:if i == 'M':layers += [nn.MaxPool2d(kernel_size=2, stride=2)]elif i == 'LRN':layers += [nn.LocalResponseNorm(size=5, alpha=1e-4, beta=0.7, k=2)]elif i == 'conv1-256':conv2d = nn.Conv2d(in_channels, 256, kernel_size=1)layers += [conv2d, nn.ReLU(inplace=True)]in_channels = 256else:conv2d = nn.Conv2d(in_channels, i, kernel_size=3, padding=1)layers += [conv2d, nn.ReLU(inplace=True)]in_channels = ireturn nn.Sequential(*layers)

代码详解

特征提取层 _make_layers

def _make_layers(self, cfg_vgg):layers = []in_channels = 3for i in cfg[cfg_vgg]:if i == 'M':layers += [nn.MaxPool2d(kernel_size=2, stride=2)]elif i == 'LRN':layers += [nn.LocalResponseNorm(size=5, alpha=1e-4, beta=0.7, k=2)]elif i == 'conv1-256':conv2d = nn.Conv2d(in_channels, 256, kernel_size=1)layers += [conv2d, nn.ReLU(inplace=True)]in_channels = 256else:conv2d = nn.Conv2d(in_channels, i, kernel_size=3, padding=1)layers += [conv2d, nn.ReLU(inplace=True)]in_channels = ireturn nn.Sequential(*layers)
  • 初始化:首先创建一个空列表 layers,用于存储构建网络过程中依次添加的各个层,同时将初始输入通道数 in_channels 设置为3,对应RGB图像的三个通道。
  • 循环构建各层及尺寸变化分析
    • 卷积层(一般情况)
      当遇到不是 'M''LRN''conv1-256' 这些特殊标识的数字 i 时,意味着要构建一个卷积层。例如 conv2d = nn.Conv2d(in_channels, i, kernel_size=3, padding=1),这里创建了一个卷积核大小为3×3、填充为1的卷积层,输入通道数是 in_channels,输出通道数为 i
      假设输入图像尺寸为 (batch_size, 3, H, W)H 表示高度,W 表示宽度),对于3×3卷积核且填充为1的卷积操作,根据卷积运算的尺寸计算公式(输出特征图高度/宽度 = (输入特征图高度/宽度 + 2 * 填充 - 卷积核大小) / 步长 + 1),步长默认为1,经过这样的卷积层后,特征图的尺寸变化为 (batch_size, i, H, W)(因为高度和宽度在这种3×3卷积且填充为1的情况下保持不变),然后再添加 nn.ReLU(inplace=True) 激活函数层,特征图尺寸依然是 (batch_size, i, H, W),同时更新 in_channels 的值为 i,用于下一层卷积操作时确定输入通道数。
    • 1×1卷积层(conv1-256 情况)
      当遇到 'conv1-256' 时,创建一个1×1卷积层 conv2d = nn.Conv2d(in_channels, 256, kernel_size=1),它主要用于在不改变特征图尺寸的情况下改变通道数,输入通道数是当前的 in_channels,输出通道数变为256。经过这个1×1卷积层和后面的 ReLU 激活函数后,特征图尺寸仍然保持之前的大小(假设之前是 (batch_size, some_channels, H, W),现在就是 (batch_size, 256, H, W)),同时 in_channels 更新为256。
    • 池化层(M 情况)
      当遇到 'M' 标识时,添加一个最大池化层 nn.MaxPool2d(kernel_size=2, stride=2)。最大池化层的作用是对特征图进行下采样,其池化核大小为2×2,步长为2。根据池化运算的尺寸计算公式(输出特征图高度/宽度 = (输入特征图高度/宽度 - 池化核大小) / 步长 + 1),经过这样的池化操作后,特征图的尺寸在高度和宽度方向上都会减半,例如输入特征图尺寸为 (batch_size, channels, H, W),经过池化后变为 (batch_size, channels, H // 2, W // 2)
    • 局部响应归一化层(LRN 情况)
      当遇到 'LRN' 时,添加 nn.LocalResponseNorm(size=5, alpha=1e-4, beta=0.7, k=2) 层,局部响应归一化层主要用于对局部的神经元活动进行归一化处理,它不会改变特征图的尺寸大小,输入特征图尺寸是多少,输出的还是同样尺寸的特征图,只是对特征图中的数值进行了归一化操作。

最后,通过 nn.Sequential(*layers) 将构建好的所有层组合成一个顺序的网络模块并返回,这个模块就构成了VGG网络的特征提取部分,按照配置的不同,特征图在经过这一系列的卷积、池化等操作后,尺寸会逐步发生变化,最终输出的特征图将被展平后输入到全连接层进行分类处理。

前向传播 forward

def forward(self, x):x = self.features(x)x = x.view(x.size()[0], -1)x = self.classifier(x)
  • 特征提取:首先将输入 x 传入 self.features,也就是前面构建的特征提取层,让其经过一系列的卷积、池化等操作,提取出图像的特征表示。
  • 维度调整:经过特征提取层后,输出的 x 的维度形式为 (batch_size, channels, height, width),为了能够输入到全连接层中,需要将其维度进行调整,x.view(x.size()[0], -1) 这一步操作会将特征图展平成二维张量,第一维是 batch_size(批次大小),第二维是所有特征元素的数量(等于 channels * height * width)。
  • 分类预测:将展平后的特征张量 x 传入 self.classifier 全连接层部分,进行分类预测,最终返回预测的类别结果,其维度为 (batch_size, num_classes)

训练过程和测试结果

训练过程损失函数变化曲线:
在这里插入图片描述

训练过程准确率变化曲线:
在这里插入图片描述

测试结果:
在这里插入图片描述

代码汇总

项目github地址
项目结构:

|--data
|--models|--__init__.py|--vgg.py|--...
|--results
|--weights
|--train.py
|--test.py

vgg.py

import torch
import torch.nn as nncfg = {'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],'A-LRN' : [64, 'LRN', 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], 'C': [64, 64, 'M', 128, 128, 'M', 256, 256, 'conv1-256', 'M', 512, 512, 'conv1-512', 'M', 512, 512, 'conv1-512', 'M'], 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], 'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M']
}class Vgg(nn.Module):def __init__(self, cfg_vgg, num_classes):super(Vgg, self).__init__()self.features = self._make_layers(cfg_vgg)self.classifier = nn.Sequential(nn.Linear(512, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, 4096),nn.ReLU(inplace=True),nn.Dropout(),nn.Linear(4096, num_classes))def forward(self, x):x = self.features(x)x = x.view(x.size()[0], -1)x = self.classifier(x)return xdef _make_layers(self, cfg_vgg):layers = []in_channels = 3for i in cfg[cfg_vgg]:if i == 'M':layers += [nn.MaxPool2d(kernel_size=2, stride=2)]elif i == 'LRN':layers += [nn.LocalResponseNorm(size=5, alpha=1e-4, beta=0.7, k=2)]elif i == 'conv1-256':conv2d = nn.Conv2d(in_channels, 256, kernel_size=1)layers += [conv2d, nn.ReLU(inplace=True)]in_channels = 256else:conv2d = nn.Conv2d(in_channels, i, kernel_size=3, padding=1)layers += [conv2d, nn.ReLU(inplace=True)]in_channels = ireturn nn.Sequential(*layers)

train.py

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from models import *
import matplotlib.pyplot as pltimport ssl
ssl._create_default_https_context = ssl._create_unverified_context# 定义数据预处理操作
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载CIFAR10训练集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,shuffle=True, num_workers=2)# 定义设备(GPU优先,若可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 实例化模型
model_name = 'Vgg_A'
if model_name == 'AlexNet':model = AlexNet(num_classes=10).to(device)
elif model_name == 'Vgg_A':model = Vgg(cfg_vgg='A', num_classes=10).to(device)
elif model_name == 'Vgg_A-LRN':model = Vgg(cfg_vgg='A-LRN', num_classes=10).to(device)
elif model_name == 'Vgg_B':model = Vgg(cfg_vgg='B', num_classes=10).to(device)
elif model_name == 'Vgg_C':model = Vgg(cfg_vgg='C', num_classes=10).to(device)
elif model_name == 'Vgg_D':model = Vgg(cfg_vgg='D', num_classes=10).to(device)
elif model_name == 'Vgg_E':model = Vgg(cfg_vgg='E', num_classes=10).to(device)criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)# 训练轮次
epochs = 15def train(model, trainloader, criterion, optimizer, device):model.train()running_loss = 0.0correct = 0total = 0for i, data in enumerate(trainloader, 0):inputs, labels = data[0].to(device), data[1].to(device)optimizer.zero_grad()outputs = model(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()_, predicted = outputs.max(1)total += labels.size(0)correct += predicted.eq(labels).sum().item()epoch_loss = running_loss / len(trainloader)epoch_acc = 100. * correct / totalreturn epoch_loss, epoch_accif __name__ == "__main__":loss_history, acc_history = [], []for epoch in range(epochs):train_loss, train_acc = train(model, trainloader, criterion, optimizer, device)print(f'Epoch {epoch + 1}: Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%')loss_history.append(train_loss)acc_history.append(train_acc)# 保存模型权重,每5轮次保存到weights文件夹下if (epoch + 1) % 5 == 0:torch.save(model.state_dict(), f'weights/{model_name}_epoch_{epoch + 1}.pth')# 绘制损失曲线plt.plot(range(1, epochs+1), loss_history, label='Loss', marker='o')plt.xlabel('Epoch')plt.ylabel('Loss')plt.title('Training Loss Curve')plt.legend()plt.savefig(f'results\\{model_name}_train_loss_curve.png')plt.close()# 绘制准确率曲线plt.plot(range(1, epochs+1), acc_history, label='Accuracy', marker='o')plt.xlabel('Epoch')plt.ylabel('Accuracy (%)')plt.title('Training Accuracy Curve')plt.legend()plt.savefig(f'results\\{model_name}_train_acc_curve.png')plt.close()

test.py

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from models import *import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 定义数据预处理操作
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.491, 0.482, 0.446), (0.247, 0.243, 0.261))])# 加载CIFAR10测试集
testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=128,shuffle=False, num_workers=2)# 定义设备(GPU优先,若可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 实例化模型
# 实例化模型
model_name = 'Vgg_A'
if model_name == 'AlexNet':model = AlexNet(num_classes=10).to(device)
elif model_name == 'Vgg_A':model = Vgg(cfg_vgg='A', num_classes=10).to(device)
elif model_name == 'Vgg_A-LRN':model = Vgg(cfg_vgg='A-LRN', num_classes=10).to(device)
elif model_name == 'Vgg_B':model = Vgg(cfg_vgg='B', num_classes=10).to(device)
elif model_name == 'Vgg_C':model = Vgg(cfg_vgg='C', num_classes=10).to(device)
elif model_name == 'Vgg_D':model = Vgg(cfg_vgg='D', num_classes=10).to(device)
elif model_name == 'Vgg_E':model = Vgg(cfg_vgg='E', num_classes=10).to(device)
criterion = nn.CrossEntropyLoss()# 加载模型权重
weights_path = f"weights/{model_name}_epoch_15.pth"  
model.load_state_dict(torch.load(weights_path, map_location=device))def test(model, testloader, criterion, device):model.eval()running_loss = 0.0correct = 0total = 0with torch.no_grad():for data in testloader:inputs, labels = data[0].to(device), data[1].to(device)outputs = model(inputs)loss = criterion(outputs, labels)running_loss += loss.item()_, predicted = outputs.max(1)total += labels.size(0)correct += predicted.eq(labels).sum().item()epoch_loss = running_loss / len(testloader)epoch_acc = 100. * correct / totalreturn epoch_loss, epoch_accif __name__ == "__main__":test_loss, test_acc = test(model, testloader, criterion, device)print(f"================{model_name} Test================")print(f"Load Model Weights From: {weights_path}")print(f'Test Loss: {test_loss:.4f}, Test Acc: {test_acc:.2f}%')

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

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

相关文章

Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络

前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

SAP ABAP-日期格式问题 SAP内部错误,反序列化JSON字符串时发生异常 值 20241215 不是根据 ABAP 的 XML 格式的有效日期

SAP ABAP-日期格式问题 SAP内部错误,反序列化JSON字符串时发生异常 值 20241215 不是根据 ABAP 的 XML 格式的有效日期 在SAP内部用 YYYYMMDD没有问题 外部传入参数

sql server索引优化语句

第一步 建一个测试表 --create table TestUsers --( -- Id int primary key identity(1,1), -- Username varchar(30) not null, -- Password varchar(10) not null, -- CreateDateTime datetime not null --)第二步 插入100w数据 大概1分钟执行时间 ----插入数据…

day-21 内核链表以及栈

1.昨日作业 1.删除指定节点 找到删除就完事了,双向可以停在删除处。 /***************************** 功能:删除指定结点(通过姓名)* 参数:phead;oldname; * 返回:成功0,失-1&…

我在广州学 Mysql 系列——有关 Mysql 函数的练习

ℹ️大家好,我是LXJ,通过Mysql各种函数的相关学习,本文将通过一些练习来巩固Mysql的函数~~ 明天就冬至啦,提前大家冬至快乐!!“差不多冬至,一早一晚还是有雨”~ 复习:👉《…

第3节 测试套件数据驱动

创建Excel、 CSV测试数据 1. 从主菜单中选择 File > New > Test Data。将显示新的测试数据对话框。输入测试数据的名称并选择数据类型作为Excel File/ CSV File 。单击OK。 2. 浏览到要导入Katalon Studio的Excel File, 选择Excel中的sheetName,或者CSV文件…

tomato靶场攻略

前提:kali和tomato的连接方式都为net模式 tomato的默认网络连接方式为桥接模式,导入前注意修改,将tomato.ova的镜像导入虚拟机中 出现此页面则表示导入成功,打开kali虚拟机终端,切换为root权限 arp-scan -l 浏览器访…

【数据安全】如何保证其安全

数据安全风险 数字经济时代,数据已成为重要的生产要素。智慧城市、智慧政务的建设,正以数据为核心,推动城市管理的智能化和公共服务的优化。然而,公共数据开放共享与隐私保护之间的矛盾日益凸显,如何在确保数据安全的…

day5,数据结构,单向,双向,循环链表

1】思维导图 2】完成单向循环链表的所有操作 【创建、判空、尾插、遍历、尾删、销毁】 创建: LooplinkPtr caerte() {LooplinkPtr h(LooplinkPtr)malloc(sizeof(Looplink));if(NULLh){printf("创建失败\n");return NULL;}h->len0;h->data0;h->…

CH340系列芯片驱动电路·CH340系列芯片驱动!!!

目录 CH340基础知识 CH340常见类型 CH340引脚功能讲解 CH340驱动电路 CH340系列芯片数据手册 编写不易,仅供学习,请勿搬运,感谢理解 常见元器件驱动电路文章专栏连接 LM7805系列降压芯片驱动电路降压芯片驱动电路详解-CSDN博客 ME62…

【机器学习案列】使用随机森林(RF)进行白葡萄酒质量预测

🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

Reactor

文章目录 正确的理解发送double free问题 1.把我们的reactor进行拆分2.链接管理3.Reactor的理论 listensock只需要设置_recv_cb,而其他sock,读,写,异常 所以今天写nullptr其实就不太对,添加为空就没办法去响应事件 获…

React,Antd实现文本输入框话题添加及删除的完整功能解决方案

最终效果就是实现该输入框: 添加话题时,话题自动插入到输入框前面多文本输入框左侧间距为话题的宽度多行文本时,第二行紧接开头渲染删除文本时,如果删除到话题,再次删除,话题被删除 首先构造div结构 cons…

【机器人】ATM 用于策略学习的任意点轨迹建模 RSS 2024 | 论文精读

文章提出了一种新的框架,名为Any-point Trajectory Modeling (ATM) ,称为任意点轨迹建模。 用于从视频中预测任意点的未来轨迹,从而在最少动作标签数据的情况下,学习稳健的视觉运动策略。 图中展示了三个案例,打开柜子…

Android 搭建AIDL Client和Server端,双向通信

一、背景 使用AIDL,搭建Client和Server端,实现跨进程通讯,即两个应用之间可以相互通讯。这里列举AIDL实现的方式和需注意的细节,并附上源码。 二、实现方式 2.1 定义AIDL需要的接口,名字为xxx.aidl,Client和Server端 AIDL接口的包名和aidl文件必须一致&#xff0c…

【VUE】14、VUE项目如何自动识别服务端是否发布了新版本

今天介绍的是通过轮询的方式去检测服务端是否发布了新版本,从而提醒客户刷新页面,提升用户体验。 1、实现思路 使用轮询的方式获取项目中 index.html 文件。查询文件引入的 JS 文件是否有更新( Vue 每次打包后会生成新的引入文件&#xff0…

空天地遥感数据识别与计算--数据分析如何助力农林牧渔、城市发展、地质灾害监测等行业革新

在科技飞速发展的时代,遥感数据的精准分析已经成为推动各行业智能决策的关键工具。从无人机监测农田到卫星数据支持气候研究,空天地遥感数据正以前所未有的方式为科研和商业带来深刻变革。然而,对于许多专业人士而言,如何高效地处…

多智能体/多机器人网络中的图论法

一、引言 1、网络科学至今受到广泛关注的原因: (1)大量的学科(尤其生物及材料科学)需要对元素间相互作用在多层级系统中所扮演的角色有更深层次的理解; (2)科技的发展促进了综合网…

python数据分析:介绍pandas库的数据类型Series和DataFrame

安装pandas pip install pandas -i https://mirrors.aliyun.com/pypi/simple/ 使用pandas 直接导入即可 import pandas as pd pandas的数据结构 pandas提供了两种主要的数据结构:Series 和 DataFrame,类似于python提供list列表,dict字典,…

Python:枚举(包含例题字符计数,反倍数,洁净数,扫雷)

一.枚举是什么 枚举:通过逐个尝试所有可能的值或组合来解决问题的方法。 将问题空间划分为一系列离散的状态,并通过遍历这些状态来寻找解决方案。 二.枚举流程 1.确定解空间(一维,二维等) 2.确定空间边界&#xff…