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

news2024/12/23 5:46:08

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

  • CIFAR10数据集
  • MobileNet
    • 设计理念
    • 网络结构
    • 技术优势
    • 应用领域
  • MobileNet结构代码详解
    • 结构代码
    • 代码详解
      • DepthwiseSeparableConv 类
        • 初始化方法
        • 前向传播 forward 方法
      • MobileNet 类
        • 初始化方法
        • 前向传播 forward 方法
  • 训练过程和测试结果
  • 代码汇总
    • mobilenet.py
    • train.py
    • test.py

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

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个不同的类别,这些类别都是现实世界中常见的物体,具有一定的代表性。

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

MobileNet

MobileNet是由谷歌在2017年提出的一种轻量级卷积神经网络,主要用于移动端和嵌入式设备等资源受限的环境中进行图像识别和分类任务,以下是对其的详细介绍:

设计理念

  • 深度可分离卷积:其核心创新是采用了深度可分离卷积(Depthwise Separable Convolution)来替代传统的卷积操作。深度可分离卷积将标准卷积分解为一个深度卷积(Depthwise Convolution)和一个逐点卷积(Pointwise Convolution),大大减少了计算量和模型参数,同时保持了较好的性能。

网络结构

  • 标准卷积层:输入层为3通道的彩色图像,首先经过一个普通的卷积层conv1,将通道数从3变为32,同时进行了步长为2的下采样操作,以减小图像尺寸。
  • 深度可分离卷积层:包含了一系列的深度可分离卷积层dsconv1dsconv13,这些层按照一定的规律进行排列,通道数逐渐增加,同时通过不同的步长进行下采样,以提取不同层次的特征。
  • 池化层和全连接层:在深度可分离卷积层之后,通过一个自适应平均池化层avgpool将特征图转换为1x1的大小,然后通过一个全连接层fc将特征映射到指定的类别数,完成分类任务。

技术优势

  • 模型轻量化:通过深度可分离卷积的使用,大大减少了模型的参数量和计算量,使得模型更加轻量化,适合在移动设备和嵌入式设备上运行。
  • 计算效率高:由于减少了计算量,MobileNet在推理时具有较高的计算效率,可以快速地对图像进行分类和识别,满足实时性要求较高的应用场景。
  • 性能表现较好:尽管模型轻量化,但MobileNet在图像识别任务上仍然具有较好的性能表现,能够在保持较高准确率的同时,大大降低模型的复杂度。

应用领域

  • 移动端视觉任务:广泛应用于各种移动端设备,如智能手机、平板电脑等,用于图像分类、目标检测、人脸识别等视觉任务。
  • 嵌入式设备视觉:在嵌入式设备,如智能摄像头、自动驾驶汽车等领域,MobileNet可以为这些设备提供高效的视觉处理能力,实现实时的图像分析和决策。
  • 物联网视觉应用:在物联网设备中,MobileNet可以帮助实现对图像数据的快速处理和分析,为智能家居、智能安防等应用提供支持。

MobileNet结构代码详解

结构代码

import torch
import torch.nn as nn

class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False):
        super(DepthwiseSeparableConv, self).__init__()
        self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size, stride, padding, groups=in_channels, bias=bias)
        self.bn1 = nn.BatchNorm2d(in_channels)
        self.relu1 = nn.ReLU6(inplace=True)
        self.pointwise = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=bias)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.relu2 = nn.ReLU6(inplace=True)

    def forward(self, x):
        out = self.depthwise(x)
        out = self.bn1(out)
        out = self.relu1(out)

        out = self.pointwise(out)
        out = self.bn2(out)
        out = self.relu2(out)

        return out
    
class MobileNet(nn.Module):
    def __init__(self, num_classes):
        super(MobileNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.relu = nn.ReLU6(inplace=True)

        self.dsconv1 = DepthwiseSeparableConv(32, 64, stride=1)
        self.dsconv2 = DepthwiseSeparableConv(64, 128, stride=2)
        self.dsconv3 = DepthwiseSeparableConv(128, 128, stride=1)
        self.dsconv4 = DepthwiseSeparableConv(128, 256, stride=2)
        self.dsconv5 = DepthwiseSeparableConv(256, 256, stride=1)
        self.dsconv6 = DepthwiseSeparableConv(256, 512, stride=2)

        self.dsconv7 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv8 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv9 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv10 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv11 = DepthwiseSeparableConv(512, 512, stride=1)

        self.dsconv12 = DepthwiseSeparableConv(512, 1024, stride=2)
        self.dsconv13 = DepthwiseSeparableConv(1024, 1024, stride=1)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.dsconv1(out)
        out = self.dsconv2(out)
        out = self.dsconv3(out)
        out = self.dsconv4(out)
        out = self.dsconv5(out)
        out = self.dsconv6(out)

        out = self.dsconv7(out)
        out = self.dsconv8(out)
        out = self.dsconv9(out)
        out = self.dsconv10(out)
        out = self.dsconv11(out)

        out = self.dsconv12(out)
        out = self.dsconv13(out)

        out = self.avgpool(out)
        out = out.view(out.size(0), -1)
        out = self.fc(out)

        return out

代码详解

以下是对上述代码的详细解释:

DepthwiseSeparableConv 类

这是一个自定义的深度可分离卷积层类,继承自 nn.Module

初始化方法
  • 参数说明
    • in_channels:输入通道数,指定输入数据的通道数量。
    • out_channels:输出通道数,即卷积操作后输出特征图的通道数量。
    • kernel_size:卷积核大小,默认为3,用于定义卷积操作中卷积核的尺寸。
    • stride:步长,默认为1,控制卷积核在输入特征图上滑动的步长。
    • padding:填充大小,默认为1,在输入特征图周围添加的填充像素数量,以保持特征图尺寸在卷积过程中合适变化。
    • bias:是否使用偏置,默认为 False,决定卷积层是否添加偏置项。
  • 构建的层及作用
    • self.depthwise:这是一个深度卷积层(nn.Conv2d),通过设置 groups=in_channels,实现了深度可分离卷积中的深度卷积部分,它对每个输入通道分别进行卷积操作,有效地减少了计算量。
    • self.bn1:批归一化层(nn.BatchNorm2d),用于对深度卷积后的输出进行归一化处理,加速模型收敛并提升模型的泛化能力。
    • self.relu1:激活函数层(nn.ReLU6),采用 ReLU6 激活函数(输出值限定在0到6之间),并且设置 inplace=True,意味着直接在输入的张量上进行修改,节省内存空间,增加非线性特性。
    • self.pointwise:逐点卷积层(nn.Conv2d),卷积核大小为1,用于将深度卷积后的特征图在通道维度上进行融合,改变通道数到指定的 out_channels
    • self.bn2:又是一个批归一化层,对逐点卷积后的输出进行归一化处理。
    • self.relu2:同样是 ReLU6 激活函数层,进一步增加非线性,处理逐点卷积归一化后的结果。
前向传播 forward 方法

定义了数据在该层的前向传播过程:

  • 首先将输入 x 通过深度卷积层 self.depthwise 进行深度卷积操作,得到输出特征图。
  • 然后将深度卷积的输出依次经过批归一化层 self.bn1 和激活函数层 self.relu1
  • 接着把经过处理后的特征图通过逐点卷积层 self.pointwise 进行逐点卷积,改变通道数等特征。
  • 最后再经过批归一化层 self.bn2 和激活函数层 self.relu2,并返回最终的输出结果。

MobileNet 类

这是定义的 MobileNet 网络模型类,同样继承自 nn.Module

初始化方法
  • 参数说明
    • num_classes:分类的类别数量,用于最后全连接层输出对应类别数的预测结果。
  • 构建的层及作用
    • self.conv1:普通的二维卷积层(nn.Conv2d),输入通道数为3(通常对应RGB图像的三个通道),输出通道数为32,卷积核大小为3,步长为2,用于对输入图像进行初步的特征提取和下采样,减少特征图尺寸同时增加通道数。
    • self.bn1:批归一化层,对 conv1 卷积后的输出进行归一化。
    • self.relu:激活函数层,采用 ReLU6 激活函数给特征图增加非线性。
    • 一系列的 self.dsconv 层(从 dsconv1dsconv13):都是前面定义的深度可分离卷积层 DepthwiseSeparableConv 的实例,它们逐步对特征图进行更精细的特征提取、通道变换以及下采样等操作,不同的 dsconv 层有着不同的输入输出通道数以及步长设置,以此构建出 MobileNet 网络的主体结构,不断提取和融合特征,逐步降低特征图尺寸并增加通道数来获取更高级、更抽象的特征表示。
    • self.avgpool:自适应平均池化层(nn.AdaptiveAvgPool2d),将输入特征图转换为指定大小 (1, 1) 的输出,起到全局平均池化的作用,进一步压缩特征图信息,同时保持特征图的维度一致性,方便后续全连接层处理。
    • self.fc:全连接层(nn.Linear),输入维度为1024(与前面网络结构最终输出的特征维度对应),输出维度为 num_classes,用于将经过前面卷积和池化等操作得到的特征向量映射到对应类别数量的预测分数上,实现分类任务。
前向传播 forward 方法

定义了 MobileNet 模型整体的前向传播流程:

  • 首先将输入 x 通过 conv1 进行初始卷积、bn1 进行归一化以及 relu 激活。
  • 然后依次通过各个深度可分离卷积层(dsconv1dsconv13),逐步提取和变换特征。
  • 接着经过自适应平均池化层 self.avgpool,将特征图压缩为 (1, 1) 大小。
  • 再通过 out.view(out.size(0), -1) 操作将特征图展平为一维向量(其中 out.size(0) 表示批量大小,-1 表示自动计算剩余维度大小使其展平)。
  • 最后将展平后的特征向量通过全连接层 self.fc 得到最终的分类预测结果并返回。

训练过程和测试结果

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

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

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

代码汇总

项目github地址
项目结构:

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

mobilenet.py

import torch
import torch.nn as nn

class DepthwiseSeparableConv(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False):
        super(DepthwiseSeparableConv, self).__init__()
        self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size, stride, padding, groups=in_channels, bias=bias)
        self.bn1 = nn.BatchNorm2d(in_channels)
        self.relu1 = nn.ReLU6(inplace=True)
        self.pointwise = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=bias)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.relu2 = nn.ReLU6(inplace=True)

    def forward(self, x):
        out = self.depthwise(x)
        out = self.bn1(out)
        out = self.relu1(out)

        out = self.pointwise(out)
        out = self.bn2(out)
        out = self.relu2(out)

        return out
    
class MobileNet(nn.Module):
    def __init__(self, num_classes):
        super(MobileNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.relu = nn.ReLU6(inplace=True)

        self.dsconv1 = DepthwiseSeparableConv(32, 64, stride=1)
        self.dsconv2 = DepthwiseSeparableConv(64, 128, stride=2)
        self.dsconv3 = DepthwiseSeparableConv(128, 128, stride=1)
        self.dsconv4 = DepthwiseSeparableConv(128, 256, stride=2)
        self.dsconv5 = DepthwiseSeparableConv(256, 256, stride=1)
        self.dsconv6 = DepthwiseSeparableConv(256, 512, stride=2)

        self.dsconv7 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv8 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv9 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv10 = DepthwiseSeparableConv(512, 512, stride=1)
        self.dsconv11 = DepthwiseSeparableConv(512, 512, stride=1)

        self.dsconv12 = DepthwiseSeparableConv(512, 1024, stride=2)
        self.dsconv13 = DepthwiseSeparableConv(1024, 1024, stride=1)

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(1024, num_classes)

    def forward(self, x):
        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.dsconv1(out)
        out = self.dsconv2(out)
        out = self.dsconv3(out)
        out = self.dsconv4(out)
        out = self.dsconv5(out)
        out = self.dsconv6(out)

        out = self.dsconv7(out)
        out = self.dsconv8(out)
        out = self.dsconv9(out)
        out = self.dsconv10(out)
        out = self.dsconv11(out)

        out = self.dsconv12(out)
        out = self.dsconv13(out)

        out = self.avgpool(out)
        out = out.view(out.size(0), -1)
        out = self.fc(out)

        return out

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 plt

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训练集
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 = 'MobileNet'
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)
elif model_name == 'GoogleNet':
    model = GoogleNet(num_classes=10).to(device)
elif model_name == 'ResNet18':
    model = ResNet18(num_classes=10).to(device)
elif model_name == 'ResNet34':
    model = ResNet34(num_classes=10).to(device)
elif model_name == 'ResNet50':
    model = ResNet50(num_classes=10).to(device)
elif model_name == 'ResNet101':
    model = ResNet101(num_classes=10).to(device)
elif model_name == 'ResNet152':
    model = ResNet152(num_classes=10).to(device)
elif model_name == 'MobileNet':
    model = MobileNet(num_classes=10).to(device)

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练轮次
epochs = 15

def train(model, trainloader, criterion, optimizer, device):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0
    for 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 / total
    return epoch_loss, epoch_acc

if __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 = 'MobileNet'
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)
elif model_name == 'GoogleNet':
    model = GoogleNet(num_classes=10).to(device)
elif model_name == 'ResNet18':
    model = ResNet18(num_classes=10).to(device)
elif model_name == 'ResNet34':
    model = ResNet34(num_classes=10).to(device)
elif model_name == 'ResNet50':
    model = ResNet50(num_classes=10).to(device)
elif model_name == 'ResNet101':
    model = ResNet101(num_classes=10).to(device)
elif model_name == 'ResNet152':
    model = ResNet152(num_classes=10).to(device)
elif model_name == 'MobileNet':
    model = MobileNet(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.0
    correct = 0
    total = 0
    with 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 / total
    return epoch_loss, epoch_acc

if __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.coloradmin.cn/o/2264081.html

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

相关文章

支付域——清结算系统体系

摘要 本文深入探讨了支付清算的基础知识和跨机构清算原理,涵盖了组织、账户、支付工具和系统的基础,支付流程的模型,以及支付清算的全局实现。文章还详细介绍了支付机构的五大业务和支付系统的总架构,并通过案例分析了支付清算的…

javaEE-线程的常用方法-4

目录 一.start():启动一个线程 调用start()方法 start()方法只能调用一次: java中的API: start()和run()的区别: 二.中断一个线程 中断线程方法1:引入标志位 中断线程方法2:调⽤interrupt()⽅法 抛出的异常: 三.等待一个线程 join() 四、获取线程引用 五…

uniapp小案例---趣味打字坤

当点击输入框时出现小鸡打字 当输入框失焦时打字鸡沉下去 原图自取 这里运用了一个三元 :class"isActive?active:"&#xff0c;当聚焦时isActivetrue从而让class绑定&#xff0c;当失焦时isActivefalse <template><view class"out"><inp…

JS使用random随机数实现简单的四则算数验证

1.效果图 2.代码实现 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</ti…

vue3项目结合Echarts实现甘特图(可拖拽、选中等操作)

效果图&#xff1a; 图一&#xff1a;选中操作 图二&#xff1a;上下左右拖拽操作 本案例在echarts​​​​​​​示例机场航班甘特图的基础上修改​​​​​​​ 封装ganttEcharts组件&#xff0c;测试数据 airport-schedule.jsonganttEcharts代码: 直接复制粘贴可测​​​​…

【已解决】黑马点评项目jmeter高并发测试中用户数据的生成

具体实现见此篇文章的第3章 运行 test 程序后&#xff0c;生成以下用户名 以下文件名改成自己的地址 成功

VScode 查看linux 内核代码

0&#xff0c;安装c.c 1&#xff0c;查看linux 目录下的linux代码&#xff0c;安装remote ssh 2&#xff0c; 输入服务器IP 3 选择服务器为linux

【游戏设计原理】21 - 解谜游戏的设计

你想象一下&#xff0c;刚坐下准备玩游戏&#xff0c;想着“今天得挑战一下我的智商极限&#xff01;”可结果碰上一个谜题&#xff0c;傻眼了&#xff0c;心里默念&#xff1a;“这啥玩意儿&#xff1f;这游戏是在玩我吗&#xff1f;”如果这个谜题太简单了&#xff0c;你可能…

解析交通事故报告:利用 PDF、AI 与数据标准化技术构建智能分析系统

在交通事故处理中&#xff0c;数据的准确性与完整性至关重要。传统上&#xff0c;交通事故报告通常以 PDF 格式呈现&#xff0c;这使得手动提取数据成为一项繁琐且容易出错的任务。随着人工智能与数据处理技术的发展&#xff0c;如何自动化这一过程并提升数据质量&#xff0c;成…

基于Python+Vue开发的体育用品商城管理系统,实习作品,期末作业

项目简介 该项目是基于PythonVue开发的体育用品商城管理系统&#xff08;前后端分离&#xff09;&#xff0c;这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能&#xff0c;同时锻炼他们的项目设计与开发能力。通过学习基于Python的体…

7.C语言 宏(Macro) 宏定义,宏函数

目录 宏定义 宏函数 1.注释事项 2.注意事项 宏(Macro)用法 常量定义 简单函数实现 类型检查 条件编译 宏函数计算参数个数 宏定义进行类型转换 宏定义进行位操作 宏定义进行断言 总结 宏定义 #include "stdio.h" #include "string.h" #incl…

【LeetCode】906、超级回文数

【LeetCode】906、超级回文数 文章目录 一、通过数据量猜解法 枚举 数学 回文1.1 通过数据量猜解法 枚举 数学 回文1.2 多语言解法 二、打表法 一、通过数据量猜解法 枚举 数学 回文 1.1 通过数据量猜解法 枚举 数学 回文 减小数据规模: 先构成回文, 再平方, 再判断是否是范围…

SpringBoot的创建方式

SpringBoot创建的五种方式 1.通过Springboot官网链接下载 注意SpringBoot项目的封装方式默认为Jar 需要查看一下&#xff0c;自己的Maven版本是否正确 创建成功 2.通过 aliyun官网链接下载 修改服务路径为阿里云链接 创建成功 3.通过Springboot官网下载 点击&#xff0c;拉到最…

Android Studio AI助手---Gemini

从金丝雀频道下载最新版 Android Studio&#xff0c;以利用所有这些新功能&#xff0c;并继续阅读以了解新增内容。 Gemini 现在可以编写、重构和记录 Android 代码 Gemini 不仅仅是提供指导。它可以编辑您的代码&#xff0c;帮助您快速从原型转向实现&#xff0c;实现常见的…

物理信息神经网络(PINN)八课时教案

物理信息神经网络&#xff08;PINN&#xff09;八课时教案 第一课&#xff1a;物理信息神经网络概述 1.1 PINN的定义与背景 物理信息神经网络&#xff08;Physics-Informed Neural Networks&#xff0c;简称PINN&#xff09;是一种将物理定律融入神经网络训练过程中的先进方…

双臂机器人

目录 一、双臂机器人简介 二、双臂机器人系统的组成 三、双臂机器人面临的主要挑战 3.1 协调与协同控制问题 3.2 力控制与柔顺性问题 3.3 路径规划与轨迹优化问题 3.4 感知与环境交互 3.5 人机协作问题 3.6 能源与效率问题 3.7 稳定性与可靠性问题 四、双臂机器人…

日期区间选择器插件的操作流程

我们知道&#xff0c;在开发过程中&#xff0c;为了能够在规定时间内完成项目&#xff0c;有时候我们都会使用插件来大大提高我们的开发效率&#xff0c;有些插件是可以直接拿来用&#xff0c;但是有些插件拿过来之后是需要进行修改&#xff0c;在使用插件的时候还有很多的注意…

以ATTCK为例构建网络安全知识图

ATT&CK&#xff08;Adversarial Tactics, Techniques, and Common Knowledge &#xff09;是一个攻击行为知识库和模型&#xff0c;主要应用于评估攻防能力覆盖、APT情报分析、威胁狩猎及攻击模拟等领域。本文简单介绍ATT&CK相关的背景概念&#xff0c;并探讨通过ATT&a…

“年轻科技旗舰”爱玛A7 Plus正式发布,全国售价4999元

12月18日&#xff0c;备受行业瞩目的“A7上场 一路超神”爱玛旗舰新品发布会在爱玛台州智造工厂盛大举行。 作为年末“压轴产品”的“两轮豪华轿跑”爱玛A7Plus重磅上场&#xff0c;以“快、稳、帅、炫、智、爽”六大超神技惊艳四座&#xff0c;不仅践行了爱玛科技的精品战略&…

精通Redis(一)

目录 1.NoSQL 非关系型数据库 2.Redis 3.Redis的java客户端 4.Jedis 4.1Jedis快速入门 4.2Jedis连接池及使用 5.SpringDataRedis和RedisTemplate 1.NoSQL 非关系型数据库 基础篇-02.初始Redis-认识NoSQL_哔哩哔哩_bilibili NoSQL与SQL的区别就在于SQL是结构化的、关联…