AlexNet模型搭建(三部曲_2)

news2024/11/14 13:42:19

文章目录

  • 1模型介绍
  • 2 模型搭建
  • 3 模型训练
  • 4 模型预测

猫狗二分类,模型简单,训练精度并不高。数据集下载:<https://aistudio.baidu.com/datasetdetail/26884> 百度飞浆上找的大小只有60多M

1模型介绍

AlexNet是一个卷积神经网络的名字,最初是与CUDA一起使用GPU支持运行的,AlexNet是2012年ImageNet竞赛冠军获得者Alex Krizhevsky设计的。该网络的错误率与前一届冠军相比减小了10%以上,比亚军高出10.8个百分点。AlexNet是由多伦多大学SuperVision组设计的,由Alex Krizhevsky, Geoffrey Hinton和Ilya Sutskever组成。

AlexNet模型共有5个卷积层,3个全连接层,前两个卷积层和第五个卷积层有pool池化层,其他两个卷积层没有池化。
AlexNet介绍:https://www.jiqizhixin.com/graph/technologies/a0955638-affc-472d-80b0-f645a4fd4604
在这里插入图片描述
计算过程举例:
卷积操作后的输出尺寸可以通过以下公式计算
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode=‘zeros’, device=None, dtype=None)
官方介绍 https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html#torch.nn.Conv2d
在这里插入图片描述
一般默认dilation=1,简化之后:
在这里插入图片描述
在 PyTorch 中,卷积操作会按照上述公式计算输出特征图的尺寸,并且会使用 floor 函数来向下取整。padding 和 stride 参数在卷积计算中会影响最终的输出维度。

例如
输入的一批张量的维度为 (64, 3, 224, 224),这个张量代表一个批量大小为 64 的图片数据集合,每张图片有 3 个通道(RGB ),每个通道的分辨率【图片大H和W】为 224x224。经过该nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2)卷积操作,输出张量是什么样的情况?
在这里插入图片描述
输出张量的形状将是 (64, 96, 55, 55)。其中:
批量大小仍然是 64;
输出通道数变为 96;【上图是借助两个GPU运算,分两个out_c=48,合起来就是out_c=96】
每个通道的高和宽均为 55x55。

最大池化操作后的输出尺寸可以通过以下公式计算
官方文档:https://pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html#torch.nn.MaxPool2d
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
在这里插入图片描述
带入默认值简化之后如下:【注意:向下取整符号】
在这里插入图片描述
将上一步卷积后的输出张量 (64, 96, 55, 55),传递给最大池化层 nn.MaxPool2d(kernel_size=3, stride=2) 池化操作后输出结果是什么?
在这里插入图片描述
输出张量的形状将是 (64, 96, 27, 27)。其中:
批量大小仍然是 64;
输出通道数仍然是 96;
每个通道的高和宽现在是 27x27。

2 模型搭建

import torch
from torch import nn


class AlexNet(nn.Module):
    def __init__(self, num_classes=2):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2),  # [None, 3, 224, 224] --> [None, 96, 55, 55]
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2),  # [None, 96, 55, 55] --> [None, 96, 27, 27]
            nn.Conv2d(96, 256, kernel_size=5, padding=2),  # [None, 96, 27, 27] --> [None, 256, 27, 27]
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2),  # [None, 256, 27, 27] --> [None, 256, 13, 13]
            nn.Conv2d(256, 384, kernel_size=3, padding=1),  # [None, 256, 27, 27] --> [None, 384, 13, 13]
            nn.ReLU(),
            nn.Conv2d(384, 384, kernel_size=3, padding=1),  # [None, 384, 13, 13] --> [None, 384, 13, 13]
            nn.ReLU(),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),  # [None, 384, 13, 13] --> [None, 256, 13, 13]
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2)  # [None, 256, 13, 13] --> [None, 256, 6, 6]
        )

        self.classifier = nn.Sequential(
            nn.Dropout(p=0.2),
            nn.Linear(256 * 6 * 6, 2048),
            nn.ReLU(),
            nn.Dropout(p=0.2),
            nn.Linear(2048, 2048),
            nn.ReLU(),
            nn.Linear(2048, num_classes)
        )

    def forward(self, inputs):
        x = self.features(inputs)
        x = torch.flatten(x, start_dim=1)
        outputs = self.classifier(x)
        return outputs

3 模型训练

train_dataset = ImageFolder(ROOT_TRAIN, transform=train_transform)的作用是使用 PyTorch 提供的 ImageFolder 类来加载指定目录 【例如目录:ROOT_TRAIN】中的图像数据,并应用数据预处理或增强变换 (train_transform)。【专门用于加载文件夹格式的数据集。】
它要求数据集的组织形式为:【每个子文件夹的名称 (class1, class2, 等等) 代表一个类别,文件夹中的所有图片都属于该类别。】
ROOT_TRAIN/
├── class1/
│ ├── img1.jpg
│ ├── img2.jpg
│ └── …
├── class2/
│ ├── img1.jpg
│ ├── img2.jpg
│ └── …
└── …
ImageFolder 会自动为每个类别生成一个标签(从0开始的整数),并将其与相应的图像关联。例如,如果目录下有两个子文件夹 class1 和 class2,则 class1 中的所有图像的标签为 0,class2 中的图像标签为 1。

为什么说这个呢,因为我遇到问题了,还困扰了我好一会儿

我是在colab上跑的,第一次用这个ImageFolder函数,也不太知道详情,我第一的数据集是这样的,就是猫狗未分文件夹,统一放在train或者val中的。
data/
├── train/
│ ├── cat.jpg
│ ├── dog.jpg
│ └── …
├── val/
│ ├── cat.jpg
│ ├── dog.jpg
│ └── …
└── …
在这里插入图片描述
后边了解之后,就将train和val下的猫狗分类放入对应的文件夹
data/
├── train/
│ ├── cat/
│ │ ├── cat.jpg
│ │ └── …
│ ├── dog/
│ │ ├── dog.jpg
│ │ └── …
│ └── …
├── val/
│ ├── cat/
│ │ ├── cat.jpg
│ │ └── …
│ ├── dog/
│ │ ├── dog.jpg
│ │ └── …
│ └── …
在这里插入图片描述

这里介绍两个自定义工具类:
SortUtil.py
将数据集从样式
在这里插入图片描述
随机分割组装成第二种格式
在这里插入图片描述

import os
from shutil import copy
import random

def make_dir(file):
    if not os.path.exists(file):
        os.makedirs(file)

# 获取data_class文件夹下所有文件夹名(即需要分类的类名)
file_path = '../data_class'  # ../data_class 是数据文件目录
flower_class = [cla for cla in os.listdir(file_path)]
# 创建 训练集train 文件夹,并由类名在其目录下创建5个子目录
make_dir('../data/train')
for cla in flower_class:
    make_dir('../data/train/' + cla)

# 创建 验证集val 文件夹,并由类名在其目录下创建子目录
make_dir('../data/val')
for cla in flower_class:
    make_dir('../data/val/' + cla)

# 划分比例,训练集 : 验证集 = 7 : 3
split_rate = 0.3

# 遍历所有类别的全部图像并按比例分成训练集和验证集
for cla in flower_class:
    cla_path = file_path + '/' + cla + '/'  # 某一类别的子目录
    images = os.listdir(cla_path)  # iamges 列表存储了该目录下所有图像的名称
    num = len(images)
    eval_index = random.sample(images, k=int(num * split_rate))  # 从images列表中随机抽取 k 个图像名称
    for index, image in enumerate(images):
        # eval_index 中保存验证集val的图像名称
        if image in eval_index:
            image_path = cla_path + image
            new_path = '../data/val/' + cla
            copy(image_path, new_path)  # 将选中的图像复制到新路径

        # 其余的图像保存在训练集train中
        else:
            image_path = cla_path + image
            new_path = '../data/train/' + cla
            copy(image_path, new_path)
        print("\r[{}] processing [{}/{}]".format(cla, index + 1, num), end="")  # processing bar
    print()

print("processing done!")

ZipExtract.py 【zip文件解压缩】
这里是因为我用了colab 去跑模型,然后遇到了一些问题。https://colab.google/

目前使用的感受:
弊端1:GPU有时候没有资源,抢不到,只能用cpu
弊端2:上传的文件他不能保存,当你使用的文本和服务器断联后,再进去,文件就没了
弊端3:上传数据很慢,下载也是

原由:我需要将自己的数据集上传到colab上面,但是直接上传文件中的图片,大几万张,比较乱还比较慢。我就想到能不能上传个压缩包,上传之后,发现他没有解压选项,所以就有了这个工具类,去解压zip数据。

import zipfile
import os

def unzip_file(zip_file_path, extract_to_dir):
    """
    解压 ZIP 文件到指定目录。

    :param zip_file_path: ZIP 文件的路径
    :param extract_to_dir: 目标解压目录
    """
    # 检查目标目录是否存在,如果不存在则创建
    if not os.path.exists(extract_to_dir):
        os.makedirs(extract_to_dir)

    # 打开 ZIP 文件
    with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
        # 解压所有内容到目标目录
        zip_ref.extractall(extract_to_dir)
        print(f"解压缩完成,文件解压到: {extract_to_dir}")


# 你的 ZIP 文件路径
zip_file_path = './example'
# 你希望解压到的目录
extract_to_dir = './output'
unzip_file(zip_file_path, extract_to_dir)

模型训练搭建

import torch
from torch import nn
from AlexNet import AlexNet
from torch.optim import lr_scheduler
import os
from datetime import datetime
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader

import matplotlib.pyplot as plt

# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

ROOT_TRAIN = r'../data/train'
ROOT_TEST = r'../data/val'

# 将图像RGB三个通道的像素值分别减去0.5,再除以0.5.从而将所有的像素值固定在[-1,1]范围内
normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
train_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.RandomVerticalFlip(),  # 随机垂直旋转
    transforms.ToTensor(),  # 将0-255范围内的像素转为0-1范围内的tensor
    normalize])

val_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    normalize])

train_dataset = ImageFolder(ROOT_TRAIN, transform=train_transform)
val_dataset = ImageFolder(ROOT_TEST, transform=val_transform)


train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=64, shuffle=True)

# 如果显卡可用,则用显卡进行训练
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("当前设备{}".format(device))

# 调用net里面的定义的网络模型, 如果GPU可用则将模型转到GPU
my_nn = AlexNet(num_classes=2).to(device)

# 定义损失函数(交叉熵损失)
loss_fn = nn.CrossEntropyLoss().to(device)

# 定义优化器(SGD)
optimizer = torch.optim.SGD(my_nn.parameters(), lr=0.01, momentum=0.9)

# 学习率衰减 ,每十轮衰减一次 ,衰减为原来的0.1
lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)


# 定义训练函数
def train_net(dataloader, model, loss_fn, optim, lr_scheduler):
    model.train()
    # 一轮次的损失值、一轮次的正确率、一轮次被分为几个批次
    epoch_loss, epoch_acc, batch_count = 0.0, 0.0, 0
    # 学习率衰减的几轮
    cur_epoch = lr_scheduler.last_epoch + 1
    print("当前为第{}轮次,此时的学习率为{}".format(cur_epoch, optim.param_groups[0]['lr']))
    for data in dataloader:
        img, tag = data
        # 将数据移动到设备上【cpu或者gpu,看支持情况】
        img, tag = img.to(device), tag.to(device)
        # 记录该批次模型训练输出结果
        output = model(img)
        # 记录该批次的损失值
        batch_loss = loss_fn(output, tag)
        # 对 output 张量的每一行取最大值和对应的索引。_ 保存最大值,pred 保存最大值的索引,也就是预测的类别。
        pred_num, pred = torch.max(output, axis=1)
        # 将正确的预测数量除以总的样本数量,得到当前批次的准确率
        cur_acc = torch.sum(tag == pred) / output.shape[0]
        # 梯度清零,pytorch框架需要手动调用
        optim.zero_grad()
        # 反向传播计算梯度
        batch_loss.backward()
        # 更新模型参数
        optim.step()
        # 累加批次损失值
        epoch_loss += batch_loss.item()
        # 累加批次正确率
        epoch_acc += cur_acc.item()
        # 记录总批次数
        batch_count += 1
    """
    lr_scheduler.step() 有内部机制来管理学习率的调整,有一个内部计数器来跟踪训练进度。
    每次调用 step() 方法时,StepLR 会检查当前的 epoch 数量是否达到了 step_size 的倍数。
    如果是,它会按照设定的 gamma 参数更新学习率;如果不是,它不会调整学习率。
    """
    # 每个epoch结束后,更新学习率
    lr_scheduler.step()
    print("Train_Avg_Batch_Loss:{} ".format(epoch_loss / batch_count))
    print("Train_Avg_Batch_Acc: {}".format(epoch_acc / batch_count))
    return epoch_loss / batch_count, epoch_acc / batch_count


# 定义模型测试方法
def test_net(dataloader, model, loss_fn):
    model.eval()
    # 一轮次的损失值、一轮次的正确率、一轮次被分为几个批次
    epoch_loss, epoch_acc, batch_count = 0.0, 0.0, 0
    # 禁用梯度计算,节省内存和加速计算【在test阶段,是不需要梯度计算的】
    with torch.no_grad():
        for data in dataloader:
            img, tag = data
            # 将数据移动到设备上【cpu或者gpu,看支持情况】
            img, tag = img.to(device), tag.to(device)
            # 记录该批次模型训练输出结果
            output = model(img)
            # 记录该批次的损失值
            batch_loss = loss_fn(output, tag)
            # 对 output 张量的每一行取最大值和对应的索引。pred_num存最大值,pred 保存最大值的索引,也就是预测的类别。
            pred_num, pred = torch.max(output, axis=1)
            # 将正确的预测数量除以总的样本数量,得到当前批次的准确率
            cur_acc = torch.sum(tag == pred) / output.shape[0]
            # 累加批次损失值
            epoch_loss += batch_loss.item()
            # 累加批次正确率
            epoch_acc += cur_acc.item()
            # 记录总批次数
            batch_count += 1
        print("Test_Avg_Batch_Loss:{} ".format(epoch_loss / batch_count))
        print("Test_Avg_Batch_Acc: {}".format(epoch_acc / batch_count))
    # 返回批次正确率
    return epoch_loss / batch_count, epoch_acc / batch_count


# 画图函数
def matplot_loss(train_loss, val_loss):
    plt.plot(train_loss, label='train_loss')
    plt.plot(val_loss, label='val_loss')
    plt.legend(loc='best')
    plt.ylabel('loss', fontsize=12)
    plt.xlabel('epoch', fontsize=12)
    plt.title("训练集和验证集loss值对比图")
    plt.show()


def matplot_acc(train_acc, val_acc):
    plt.plot(train_acc, label='train_acc')
    plt.plot(val_acc, label='val_acc')
    plt.legend(loc='best')
    plt.ylabel('acc', fontsize=12)
    plt.xlabel('epoch', fontsize=12)
    plt.title("训练集和验证集精确度值对比图")
    plt.show()


# 开始训练
loss_train = []
acc_train = []
loss_val = []
acc_val = []

"""
只保存两个模型:
    1、测试结果最好的那一个模型
    2、最后的那一个模型
"""
# 开始训练模型
epoch = 25
max_acc = 0.0
# 计时
start_time = datetime.now()
print("当前训练模型是AlexNet,猫狗二分类,预定训练轮次-{}".format(epoch))
for t in range(epoch):
    print("-----第{}轮训练开始-----".format(t + 1))
    train_loss, train_acc = train_net(train_dataloader, my_nn, loss_fn, optimizer, lr_scheduler)
    val_loss, val_acc = test_net(val_dataloader, my_nn, loss_fn)

    loss_train.append(train_loss)
    acc_train.append(train_acc)
    loss_val.append(val_loss)
    acc_val.append(val_acc)

    # 保存最好的模型权重文件
    if val_acc > max_acc:
        folder = '../save_models'
        if not os.path.exists(folder):
            os.mkdir('../save_models')
        max_acc = val_acc
        print(f'save best model,第{t + 1}轮')
        torch.save(my_nn.state_dict(), '../save_models/best_model.pth')
    # 保存最后的权重模型文件
    if t == epoch - 1:
        torch.save(my_nn.state_dict(), '../save_models/last_model.pth')
        print("save last ok!")
print('Done!')
end_time = datetime.now()
print("start_time:{}".format(start_time))
print("end_time:{}".format(end_time))
print("{}训练总用时:{}".format(device, end_time - start_time))
print("Done!")
matplot_loss(loss_train, loss_val)
matplot_acc(acc_train, acc_val)

问题1:处理方式对比

transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

Resize(256): 将图像的较长边调整为256像素。
CenterCrop(224): 从调整后的图像中中心裁剪224x224的区域。
ToTensor(): 将图像从[0, 255]范围内的像素值转换为[0, 1]范围内的浮点数,并将图像转换为PyTorch的Tensor格式。
Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]): 使用ImageNet数据集的均值和标准差对图像进行归一化。

train_transform = transforms.Compose([
transforms.Resize((224, 224)), # 裁剪为224x224
transforms.RandomVerticalFlip(), # 随机垂直翻转
transforms.ToTensor(), # 将0-255范围内的像素转为0-1范围内的tensor
normalize
])

Resize((224, 224)): 将图像调整为224x224的大小(这与标准处理中的CenterCrop不同,可能会影响图像的内容)。
RandomVerticalFlip(): 在训练时随机进行垂直翻转,这是一种数据增强方法,有助于提高模型的泛化能力。
ToTensor(): 将图像从[0, 255]范围内的像素值转换为[0, 1]范围内的浮点数,并将图像转换为PyTorch的Tensor格式。
Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]): 将图像像素值范围从[0, 1]调整到[-1, 1]。【更适合与自定义数据集配合使用】

问题2:归一化计算方式

transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
ransforms.Normalize(mean=[0.5, 0.5, 0.5], std= [0.5, 0.5, 0.5])

首先,需要知道ToTensor()的处理结果。
在这里插入图片描述
然后,是Normalize(mean, std)是如何处理计算的。
在这里插入图片描述
以 transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])为例。
在这里插入图片描述
计算下线【pixel=0】,计算上线【pixel=1】。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

问题3:在深度学习中,normalize函数的作用是什么,有他没他会对训练造成什么影响?

在深度学习中,normalize函数通常用于将数据的数值范围调整到一个标准范围(例如0到1,或者均值为0,标准差为1)。
其主要作用包括:

  1. 加速收敛:将输入数据归一化到相同的尺度可以帮助加速训练过程。梯度下降法在训练时会更稳定,减少了训练过程中出现的梯度消失或梯度爆炸的问题。
  2. 提高模型稳定性:通过归一化,模型在训练时可以更稳定地更新权重,因为不同特征的数值范围被标准化到类似的尺度,这样模型可以更容易地学习到有用的模式。
  3. 避免特征间的不平衡:如果不同特征的尺度差异很大,某些特征可能会对模型的训练产生不成比例的影响。归一化可以确保所有特征在训练过程中对模型的贡献更加均衡。

如果不进行归一化,训练可能会遇到以下问题:

  • 训练速度慢:由于特征的尺度不同,模型可能需要更长的时间来收敛,因为每个特征的更新幅度不同。
  • 收敛不稳定:特征值范围差异大可能导致梯度在训练过程中变化剧烈,增加了梯度消失或爆炸的风险。
  • 模型性能下降:特征不均衡可能导致模型无法有效地学习所有特征的有用信息,从而影响最终的预测性能。

问题4:如果我的数据训练集上都加上了normalize,那么我在模型训练结束之后的测试集,是不是也应该加上相同的normalize处理,不加会怎么样,或者加的normalize参数和训练集的不一样会怎么样?

在进行模型评估或测试时,测试集也应该进行相同的归一化处理。归一化是数据预处理的一部分,保持训练集和测试集的一致性非常重要。
测试集也要进行与训练集相同的归一化
如果测试数据的归一化方式与训练数据不一致或没有归一化,模型的输出将无法反映真实性能,测试结果将不可比。这样会导致测试结果失真,因为模型已经适应了训练数据的特定尺度。模型在训练时是基于归一化后的数据进行学习的,因此在测试时也必须使用相同的归一化标准。

4 模型预测

import torch
from AlexNet import AlexNet
from torch.autograd import Variable
from torchvision import datasets, transforms
from torchvision.transforms import ToPILImage
from torchvision.datasets import ImageFolder
import matplotlib.pyplot as plt

ROOT_TEST = r'../data2/train'


val_transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

val_dataset = ImageFolder(ROOT_TEST, transform=val_transform)
print(len(val_dataset))

# 如果显卡可用,则用显卡进行训练
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("当前设备{}".format(device))

# 调用net里面的定义的网络模型, 如果GPU可用则将模型转到GPU
model = AlexNet().to(device)

# 加载模型train.py里面训练的模型
model.load_state_dict(torch.load('../save_models/best_model.pth', weights_only=True))

# 获取预测结果
classes = ['cat', 'dog']

# 把tensor转成Image,方便可视化
show = ToPILImage()

# 进入验证阶段
model.eval()
arr = [1500, 1123, 45, 36, 78, 50, 1999, 1998]
# 对val_dataset里面的照片进行推理验证
for i in arr:
    x, y = val_dataset[i][0], val_dataset[i][1]
    # show(x).show()
    img = show(x)
    # 使用 matplotlib 显示图像并设置标题
    plt.imshow(img)
    plt.title(i)
    plt.axis('off')  # 如果不想显示坐标轴,可以关闭
    plt.show()
    x = Variable(torch.unsqueeze(x, dim=0).float(), requires_grad=False).to(device)
    with torch.no_grad():
        pred = model(x)
        # print(pred)
        predicted, actual = classes[torch.argmax(pred[0])], classes[y]
        print(f'Predicted: "{predicted}", Actual: "{actual}"')

在这里插入图片描述

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

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

相关文章

Linux命令(基础面试可用,都是自己觉得平时使用多的)

1.cat 参数&#xff1a;-n&#xff1a;显示行号-s&#xff1a;压缩连续的空行&#xff0c;只显示一个空行2.chattr 改变文件属性 语法&#xff1a;chattr [-RV] [/-/<属性>][文件或目录] 属性&#xff1a;a&#xff1a;让文件或目录仅供附加用途i&#xff1a;不得任意更…

MediaPipe人体姿态、手指关键点检测

MediaPipe人体姿态、手指关键点检测 文章目录 MediaPipe人体姿态、手指关键点检测前言一、手指关键点检测二、姿态检测三、3D物体案例检测案例 前言 Mediapipe是google的一个开源项目&#xff0c;用于构建机器学习管道。   提供了16个预训练模型的案例&#xff1a;人脸检测、…

基于级联深度学习算法的前列腺病灶检测在双参数MRI中的评估| 文献速递-基于深度学习的乳房、前列腺疾病诊断系统

Title 题目 Evaluation of a Cascaded Deep Learning–based Algorithm for Prostate Lesion Detection at Biparametric MRI 基于级联深度学习算法的前列腺病灶检测在双参数MRI中的评估 Background 背景 Multiparametric MRI (mpMRI) improves prostate cancer (PCa) de…

从西安出发,走向世界——西安国际数字影像产业园跻身全国十大产业园区行列

西安&#xff0c;作为中国历史文化名城&#xff0c;不仅以其丰富的历史遗产闻名于世&#xff0c;还逐渐成为现代科技和产业发展的新高地。产业园区成为推动经济增长和创新发展的重要引擎&#xff0c;西安国际数字影像产业园&#xff0c;正怀揣着雄心壮志&#xff0c;向着全国十…

idea git拉取代码can‘t update

idea有时候创建的新分支&#xff0c;提交以后却无法拉去代码&#xff0c;提示如下错误信息&#xff1a; cant update No tracked branch configured for branch 在idea的“Terminal”窗口中输入如下命令即可 git branch --set-upstream-to origin 找不到Terminal的可以参考下图…

ESP32S3 IDF 对 16路输入输出芯片MCP23017做了个简单的测试

这次还是使用了idf老版本4.4.7&#xff0c;上次用了5.3&#xff0c;感觉不好用&#xff0c;官方的MCP23017芯片是英文版&#xff0c;真的很难读明白&#xff0c;可能是我英语水平不够吧。先看看每个寄存器的功能&#xff1a; IODIRA 和 IODIRB: 输入/输出方向寄存器 IPOLA 和 I…

B端界面升级就是升级颜值,错了,这样想就片面啦。

在B端应用的发展中&#xff0c;界面升级是非常重要的一环。然而&#xff0c;界面级不仅仅是为了提升外观颜值&#xff0c;还需要关注用户体验、功能增强和效率提升等方面。 虽然美观的界面可以吸引用户的眼球&#xff0c;但如果功能不完善&#xff0c;用户可能会选择其他产品。…

LVS负载均衡(twenty-six day)

一、LVS &#xff08;一&#xff09;什么是LVS linux virtural server的简称&#xff0c;也就是linxu虚拟机服务器&#xff0c;这是一个由章文岩博士发起的开源项目&#xff0c;官网是http://www.linuxvirtualserver.org,现在lvs已经是linux内核标准的-部分&#xff0c;使用lv…

vue 实现上浮气泡效果

一、 效果 二、代码 1.用 li 来做每个气泡 <div class"dataSea_middle_bottom"><ul><liv-for"(item,index) in keyBusiness":key"index"class"pops animal"><p class"fb">{{ item.name}}</p>…

Java面试题(一)----Java基础

基础 Java中和equals有什么区别&#xff1f; 一个是运算符&#xff0c;一个是方法。 如果比较的对象是基本数据类型&#xff0c;则比较数值是否相等&#xff1b;如果比较的是引用数据类型&#xff0c;则比较的是对象的内存地址是否相等。 因为Java只有值传递&#xff0c;对于…

SQL Zoo 9-.Window functions

以下数据均来自SQL Zoo 1.Show the lastName, party and votes for the constituency S14000024 in 2017.&#xff08;显示2017年选区“S14000024”的姓氏、政党和选票&#xff09; SELECT lastName, party, votesFROM geWHERE constituency S14000024 AND yr 2017 ORDER BY…

开启IDEA打开新项目时窗口提示

1.背景 实际开发中很多时候,我们会同时打开多个项目,或者项目切换, 这时候有2中情况,打开新窗口或者在当前窗口打开(即:关闭当前窗口,打开新窗口) 具体是那种情况,要根据实际场景,因此我们希望可以弹框提示: 是打开新窗口,还是在当前窗口打开 具体设置如下: 2.步骤 3.测试…

中国科技统计年鉴,数据覆盖1991-2022年多年份

基本信息. 数据名称: 中国科技统计年鉴 数据格式: excel 数据时间: 1991-2022年 数据几何类型: xlsx 数据坐标系: WGS84 数据来源&#xff1a;国家统计局 数据预览&#xff1a; 数据可视化.

zabbix“专家坐诊”第251期问答

问题一 Q&#xff1a;zabbix模板自带的监控项很多都不需要&#xff0c;也不用删除&#xff0c;就是怎么让他们都不展示出来 A&#xff1a;禁用掉 Q&#xff1a;还是在的&#xff0c;我想要就看不见&#xff0c;不是不启用&#xff0c;想要效果跟删除一样&#xff0c;看不见&am…

我在高职教STM32——I2C通信之SHT20传感器(1)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课件分…

go-zero中基本配置及获取参数

一、使用配置文件启动项目 1、在项目的etc文件夹下分别创建开发环境和测试环境的配置文件,这里简单点使用不同的端口 2、配置Makefile文件启动命令来启动不同配置文件 runDev:go run users.go -f etc/application-dev.yml runProd:go run users.go -f etc/application-prod.ym…

RC电路(三):零点和极点

一、零极点定义 零点和极点是在自动控制原理中用于描述系统特性的概念。‌ 零点&#xff08;Zero&#xff09;&#xff1a;‌在传递函数的分子多项式等于零的解。即当系统的输入信号等于零时&#xff0c;‌输出信号不为零的情况下&#xff0c;‌输入信号与输出信号相等的点。‌…

人工智能系统测试生命周期详解之测试数据准备

前面的文章里我们已经整体介绍过了人工智能测试的生命周期&#xff0c;它需要经历测试需求的分析、测试环境的准备、数据的准备与验证、测试的执行预分析以及上线后的监控这样一个过程。前面的文章已经为大家介绍了人工智能系统测试生命周期的“需求分析”环节和“测试环境准备…

SPSS-主成分分析实践

相信各位小伙伴都知道主成分分析的原理&#xff0c;我们今天用SPSS来实现一下主成分分析 主成分分析步骤 对原来的全部指标进行标准化&#xff0c;以消除变量在水平和量纲的影响根据标准化的数据矩阵求出相关系数矩阵求出协方差矩阵的特征根和特征向量确定主成分&#xff0c;…

DRM(Direct Rendering Manager)直接渲染管理

DRM是Linux 内核的一个子系统&#xff0c;负责与现代显卡的GPU进行交互。DRM 公开了一个API (libdrm)&#xff0c;用户空间程序可以使用该API 向 GPU 发送命令和数据并执行诸如配置显示器模式设置之类的操作。DRM 最初是作为X 服务器直接渲染基础架构的内核空间组件开发的&…