第J3-1周:DenseNet算法 实现乳腺癌识别

news2024/11/16 3:17:03

目录

  • 一、理论基础
    • 1.前言
    • 2.设计理念
    • 3.网络结构
    • 4.与其他算法效果对比
  • 二、 前期准备
    • 1. 设置GPU
    • 2. 导入数据
    • 3. 划分数据集
  • 三、搭建网络模型
    • 1. DenseLayer模块
    • 2. DenseBlock模块
    • 3. Transition模块
    • 4. 构建DenseNet
    • 5. 构建densenet121
  • 四、 训练模型
    • 1. 编写训练函数
    • 2. 编写测试函数
    • 3. 正式训练
  • 五、 结果可视化
    • 1. Loss与Accuracy图

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊|接辅导、项目定制

一、理论基础

1.前言

在计算机视觉领域,卷积神经网络(CNN)已经成为最主流的方法,比如GoogLenet,VGG-16,Incepetion等模型。CNN史上的一个里程碑事件是ResNet模型的出现,ResNet可以训练出更深的CNN模型,从而实现更高的准确度。ResNet模型的核心是通过建立前面层与后面层之间的“短路连接”(shortcuts,skip connection),进而训练出更深的CNN网络。
今天我们要介绍的是DenseNet模型,它的基本思路与ResNet一致,但是它建立的是前面所有层与后面层的密集连接(dense connection),它的名称也是由此而来。DenseNet的另一大特色是通过特征在channel上的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能,DenseNet也因此斩获CVPR 2017的最佳论文奖。
在这里插入图片描述

2.设计理念

相比ResNet,DenseNet提出了一个更激进的密集连接机制:即互相连接所有的层,具体来说就是每个层都会接受其前面所有层作为其额外的输入。
图1为ResNet网络的残差连接机制,作为对比,图2为DenseNet的密集连接机制。可以看到,ResNet是每个层与前面的某层(一般是2~4层)短路连接在一起,连接方式是通过元素相加。而在DenseNet中,每个层都会与前面所有层在channel维度上连接(concat)在一起(即元素叠加),并作为下一层的输入。
对于一个 L L L层的网络,DenseNet共包含 L ( L + 1 ) 2 \frac{L(L+1)}{2} 2L(L+1)个连接,相比ResNet,这是一种密集连接。而且DenseNet是直接concat来自不同层的特征图,这可以实现特征重用,提升效率,这一特点是DenseNet与ResNet最主要的区别。
在这里插入图片描述
●输入和输出的公式是 X l = H l ( X l − 1 ) X_l = H_l(X_{l-1}) Xl=Hl(Xl1),其中 H l H_l Hl是一个组合函数,通常包括 BN、ReLU、Pooling、Conv操作, X l − 1 X_{l-1} Xl1是第 l l l层输入的特征图, X l X_{l} Xl是第 l l l层输出的特征图。在这里插入图片描述
● ResNet是跨层相加,输入和输出的公式是 X l = H l ( X l − 1 ) + X l − 1 X_l = H_l(X_{l-1})+X_{l-1} Xl=Hl(Xl1)+Xl1
在这里插入图片描述

3.网络结构

具体介绍网络的具体实现细节如图4所示。
在这里插入图片描述
CNN网络一般要经过Pooling或者stride>1的Conv来降低特征图的大小,而DenseNet的密集连接方式需要特征图大小保持一致。为了解决这个问题,DenseNet网络中使用DenseBlock+Transition的结构,其中DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。而Transition层是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。图5给出了DenseNet的网路结构,它共包含4个DenseBlock,各个DenseBlock之间通过Transition层连接在一起。
在这里插入图片描述
在DenseBlock中,各个层的特征图大小一致,可以在channel维度上连接。DenseBlock中的非线性组合函数 H ( ⋅ ) H(\cdot) H()的是 BN+ReLU+3x3 Conv 的结构,如图6所示。另外值得注意的一点是,与ResNet不同,所有DenseBlock中各个层卷积之后均输出 k k k个特征图,即得到的特征图的channel数为 k k k,或者说采用 k k k个卷积核。 k k k在DenseNet称为growth rate,这是一个超参数。一般情况下使用较小的 k k k(比如12),就可以得到较佳的性能。假定输入层的特征图的channel数为 k 0 k_0 k0,那么 l l l层输入的channel数为 k 0 + k ( 1 , 2 , . . . , l − 1 ) k_0+k_{(1,2,...,l-1)} k0+k(1,2,...,l1),因此随着层数增加,尽管 k k k设定得较小,DenseBlock的输入会非常多,不过这是由于特征重用所造成的,每个层仅有 k k k个特征是自己独有的。
在这里插入图片描述
由于后面层的输入会非常大,DenseBlock内部可以采用bottleneck层来减少计算量,主要是原有的结构中增加1x1 Conv,如图7所示,即BN+ReLU+1x1 Conv+BN+ReLU+3x3 Conv,称为DenseNet-B结构。其中1x1 Conv得到 4 k 4k 4k个特征图它起到的作用是降低特征数量,从而提升计算效率。
在这里插入图片描述
对于Transition层,它主要是连接两个相邻的DenseBlock,并且降低特征图大小。Transition层包括一个1x1的卷积和2x2的AvgPooling,结构为BN+ReLU+1x1Conv+2x2AvgPooling。另外,Transition层可以起到压缩模型的作用。假定Transition层的上接DenseBlock得到的特征图channels数为 m m m,Transition层可以产生 ⌊ θ m ⌋ \lfloor\theta m\rfloor θm个特征(通过卷积层),其中 θ ∈ ( 0 , 1 ] \theta \in (0,1] θ(0,1]是压缩系数(compression rate)。当 θ = 1 \theta=1 θ=1时,特征个数经过Transition层没有变化,即无压缩,而当压缩系数小于1时,这种结构称为DenseNet-C,文中使用 θ = 0.5 \theta=0.5 θ=0.5。对于使用bottleneck层的DenseBlock结构和压缩系数小于1的Transition组合结构称为DenseNet-BC。

对于ImageNet数据集,图片输入大小为 224 × 224 224\times 224 224×224,网络结构采用包含4个DenseBlock的DenseNet-BC,其首先是一个stride=2的7x7卷积层,然后是一个stride=2的3x3 MaxPooling层,后面才进入DenseBlock。ImageNet数据集所采用的网络配置如表1所示:

在这里插入图片描述

4.与其他算法效果对比

在这里插入图片描述

二、 前期准备

🏡 我的环境:
● 语言环境:Python3.8
● 编译器:Jupyter Lab
● 深度学习环境:Pytorch
○ torch1.12.1+cu113
○ torchvision
0.13.1+cu113

1. 设置GPU

如果设备上支持GPU就使用GPU,否则使用CPU

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warnings

warnings.filterwarnings("ignore")             #忽略警告信息

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device

2. 导入数据

import os,PIL,random,pathlib

data_dir = './J3-data/'
data_dir = pathlib.Path(data_dir)

data_paths  = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[1] for path in data_paths]
classeNames
# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    # transforms.RandomHorizontalFlip(), # 随机水平翻转
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])

test_transform = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])

total_data = datasets.ImageFolder(data_dir,transform=train_transforms)
total_data
Dataset ImageFolder
    Number of datapoints: 13403
    Root location: J3-data
    StandardTransform
Transform: Compose(
               Resize(size=[224, 224], interpolation=bilinear, max_size=None, antialias=None)
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           )

3. 划分数据集

train_size = int(0.8 * len(total_data))
test_size  = len(total_data) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
train_dataset, test_dataset
batch_size = 32

train_dl = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)
test_dl = torch.utils.data.DataLoader(test_dataset,
                                          batch_size=batch_size,
                                          shuffle=True)
for X, y in test_dl:
    print("Shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break

三、搭建网络模型

from collections import OrderedDict
import torch
import torch.nn as nn
import torch.nn.functional as F

1. DenseLayer模块

self.add_module() 函数用于向类中添加一个子模块。

在这段代码中,self.add_module(‘name’, module) 被用于将不同的模块添加到 DenseLayer 类中。这些模块可以是任何继承自 nn.Module 的子类,例如批标准化层 (nn.BatchNorm2d)、ReLU 激活函数 (nn.ReLU)、卷积层 (nn.Conv2d) 等。

该函数的参数包括一个字符串 ‘name’,用于给该模块命名,以及一个模块实例 module,用于表示要添加的模块对象。在代码中,每个模块都按顺序添加到 DenseLayer 类中。

通过使用 self.add_module() 函数,这些子模块被存储在 DenseLayer 类的内部,成为该类的属性。这样,在类的其他方法中,可以通过引用这些属性来访问和操作这些模块,例如在 forward() 方法中使用 super().forward(x) 调用父类模块的前向传播方法。

总之,self.add_module() 函数的作用是将子模块添加到类中,并为这些模块提供属性名以便后续引用和操作。

class DenseLayer(nn.Sequential):
    def __init__(self, in_channel, growth_rate, bn_size, drop_rate):
        super(DenseLayer, self).__init__()
        self.add_module('norm1', nn.BatchNorm2d(in_channel))
        self.add_module('relu1', nn.ReLU(inplace=True))
        self.add_module('conv1', nn.Conv2d(in_channel, bn_size*growth_rate,
                                           kernel_size=1, stride=1, bias=False))
        self.add_module('norm2', nn.BatchNorm2d(bn_size*growth_rate))
        self.add_module('relu2', nn.ReLU(inplace=True))
        self.add_module('conv2', nn.Conv2d(bn_size*growth_rate, growth_rate,
                                           kernel_size=3, stride=1, padding=1, bias=False))
        self.drop_rate = drop_rate
    
    def forward(self, x):
        new_feature = super(DenseLayer, self).forward(x)
        if self.drop_rate>0:
            new_feature = F.dropout(new_feature, p=self.drop_rate, training=self.training)
        return torch.cat([x, new_feature], 1)

2. DenseBlock模块

''' DenseBlock '''
class DenseBlock(nn.Sequential):
    def __init__(self, num_layers, in_channel, bn_size, growth_rate, drop_rate):
        super(DenseBlock, self).__init__()
        for i in range(num_layers):
            layer = DenseLayer(in_channel+i*growth_rate, growth_rate, bn_size, drop_rate)
            self.add_module('denselayer%d'%(i+1,), layer)

3. Transition模块

''' Transition layer between two adjacent DenseBlock '''
class Transition(nn.Sequential):
    def __init__(self, in_channel, out_channel):
        super(Transition, self).__init__()
        self.add_module('norm', nn.BatchNorm2d(in_channel))
        self.add_module('relu', nn.ReLU(inplace=True))
        self.add_module('conv', nn.Conv2d(in_channel, out_channel,
                                          kernel_size=1, stride=1, bias=False))
        self.add_module('pool', nn.AvgPool2d(2, stride=2))

4. 构建DenseNet

nn.Sequential是PyTorch中的一个模型容器,它按照给定的顺序依次执行一系列的神经网络模块(layers)。在构建神经网络时,我们可以使用nn.Sequential来简化代码。

OrderedDict是Python中的一种有序字典数据结构,它保留了元素添加的顺序。在神经网络中,我们可以使用OrderedDict来指定模型的层次结构。

class DenseNet(nn.Module):
    def __init__(self, growth_rate=32, block_config=(6,12,24,16), init_channel=64, 
                 bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=1000):
        '''
        :param growth_rate: (int) number of filters used in DenseLayer, `k` in the paper
        :param block_config: (list of 4 ints) number of layers in eatch DenseBlock
        :param init_channel: (int) number of filters in the first Conv2d
        :param bn_size: (int) the factor using in the bottleneck layer
        :param compression_rate: (float) the compression rate used in Transition Layer
        :param drop_rate: (float) the drop rate after each DenseLayer
        :param num_classes: (int) 待分类的类别数
        '''
        super(DenseNet, self).__init__()
        # first Conv2d
        self.features = nn.Sequential(OrderedDict([
            ('conv0', nn.Conv2d(3, init_channel, kernel_size=7, stride=2, padding=3, bias=False)),
            ('norm0', nn.BatchNorm2d(init_channel)),
            ('relu0', nn.ReLU(inplace=True)),
            ('pool0', nn.MaxPool2d(3, stride=2, padding=1))
        ]))
        
        # DenseBlock
        num_features = init_channel
        for i, num_layers in enumerate(block_config):
            block = DenseBlock(num_layers, num_features, bn_size, growth_rate, drop_rate)
            self.features.add_module('denseblock%d'%(i+1), block)
            num_features += num_layers*growth_rate
            if i != len(block_config)-1:
                transition = Transition(num_features, int(num_features*compression_rate))
                self.features.add_module('transition%d'%(i+1), transition)
                num_features = int(num_features*compression_rate)
                
        # final BN+ReLU
        self.features.add_module('norm5', nn.BatchNorm2d(num_features))
        self.features.add_module('relu5', nn.ReLU(inplace=True))
        # 分类层
        self.classifier = nn.Linear(num_features, num_classes)
        
        # 参数初始化
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.bias, 0)
                nn.init.constant_(m.weight, 1)
            elif isinstance(m, nn.Linear):
                nn.init.constant_(m.bias, 0)
    
    def forward(self, x):
        x = self.features(x)
        x = F.avg_pool2d(x, 7, stride=1).view(x.size(0), -1)
        x = self.classifier(x)
        return x

5. 构建densenet121

device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

densenet121 = DenseNet(init_channel=64,
                       growth_rate=32,
                       block_config=(6,12,24,16),
                       num_classes=len(classeNames))  

model = densenet121.to(device)
model

四、 训练模型

1. 编写训练函数

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小
    num_batches = len(dataloader)   # 批次数目, (size/batch_size,向上取整)

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    
    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)
        
        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新
        
        # 记录acc与loss
        train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()
            
    train_acc  /= size
    train_loss /= num_batches

    return train_acc, train_loss

2. 编写测试函数

测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器

def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小
    num_batches = len(dataloader)          # 批次数目, (size/batch_size,向上取整)
    test_loss, test_acc = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            target_pred = model(imgs)
            loss        = loss_fn(target_pred, target)
            
            test_loss += loss.item()
            test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()

    test_acc  /= size
    test_loss /= num_batches

    return test_acc, test_loss

3. 正式训练

import copy

optimizer  = torch.optim.Adam(model.parameters(), lr= 1e-4)
loss_fn    = nn.CrossEntropyLoss() # 创建损失函数

epochs     = 20

train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []

best_acc = 0    # 设置一个最佳准确率,作为最佳模型的判别指标

for epoch in range(epochs):
    
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    # 保存最佳模型到 best_model
    if epoch_test_acc > best_acc:
        best_acc   = epoch_test_acc
        best_model = copy.deepcopy(model)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    # 获取当前的学习率
    lr = optimizer.state_dict()['param_groups'][0]['lr']
    
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, 
                          epoch_test_acc*100, epoch_test_loss, lr))
    
# 保存最佳模型到文件中
PATH = './best_model.pth'  # 保存的参数文件名
torch.save(best_model.state_dict(), PATH)

print('Done')

五、 结果可视化

1. Loss与Accuracy图

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

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

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

相关文章

I/O多路复用+高性能网络模式

前言: 本篇文章将介绍客户端-服务端之间从最简单的Socket模型到I/O多路复用的模式演变过程,并介绍Reactor和Proactor两种高性能网络模式 文章内容摘自:小林Coding I/O多路复用高性能网络模式 . 传统Socket模型传统Socket模型的性能瓶颈多进程…

【Java基础学习打卡12】Java入门程序

目录 前言一、Java程序开发运行流程二、Java程序源代码编写三、Java程序源代码编译四、Java程序运行五、Java入门程序问题总结 前言 本文首先介绍Java程序开发运行基础流程,然后先进行程序源代码编写,然后对Java程序代码进行编译,最后要运行…

Python学习笔记(1)--环境搭建,开发工具PyCharm 安装及初步使用

传送门>B站黑马python入门教程 目录 1.Python环境安装搭建安装python基础包验证安装文件 2.hello world3.开发工具PyCharm 安装及初步使用安装基础设置 1.Python环境安装搭建 安装python基础包 首先,打开python 官网 https://www.python.org/ 下载windows版 下载后进行安装 …

Triton教程 --- 解耦后端和模型

Triton教程 — 解耦后端和模型 Triton系列教程: 快速开始利用Triton部署你自己的模型Triton架构模型仓库存储代理模型设置优化动态批处理速率限制器模型管理自定义算子 解耦后端和模型 Triton 可以支持为一个请求发送多个响应或为一个请求发送零个响应的后端和模型。 解耦的…

论文笔记--Prompt Consistency for Zero-Shot Task Generalization

论文笔记--Prompt Consistency for Zero-Shot Task Generalization 1. 文章简介2. 文章概括3 文章重点技术3.1 Prompt-based zero-shot task generalization3.2 Prompt Consistency Training3.3 如何防止遗忘和退化? 4. 文章亮点5. 原文传送门 1. 文章简介 标题&am…

【numpy模块上}——数据分析01

目录索引 介绍:用处与特点:构成:导包:创建数组: numpy常用方法:常用属性查看:*获取秩的大小:**获取数组形状:**获取元素个数:**获取元素类型:**获…

行为型设计模式10-解释器模式

🧑‍💻作者:猫十二懿 ❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github 🎉公众号:猫十二懿 解释器模式 1、解释器模式介绍 解释器模式(Interpreter Pattern)是一种行为设…

Kafka系列之:对源连接器的的Exactly-Once支持

Kafka系列之:对源连接器的的Exactly-Once支持 一、背景二、目标三、公共接口四、连接器 API 扩展五、REST API验证六、新指标七、计划变更八、任务计数记录九、重新平衡的准备十、源任务启动十一、领导者访问配置主题十二、用于隔离事务生产者的管理 API十三、解决了…

论文阅读 - SegFormer

文章目录 1 概述2 模型说明2.1 总体结构2.2 Hierarchical Transformer Encoder2.3 Lightweight All-MLP Decoder 3 SegFormer和SETR的比较参考资料 1 概述 图像分割任务和图像分类任务是非常相关的,前者是像素级别的分类,后者是图像级别的分类。基于分类…

不到3000块,搭建IT人的实验平台!性能可媲美服务器!

作为IT从业者,特别是运维这个岗位,没有自己的实验平台真的特别难受,那么如何搭建自己的实验平台呢?这是我最近思考并付诸实践的一个事情,最终找到了自己觉得比较可以的方案。 01 我的需求是什么? 大内存容量…

TypeScript——类(class)

ES6 中类的用法 下面我们先回顾一下 ES6 中类的用法,更详细的介绍可以参考 ECMAScript 6 入门 - Class 属性和方法 使用 class 定义类,使用 constructor 定义构造函数。 通过new生成新实例的时候,会自动调用构造函数。 class Person{con…

leetcode877. 石子游戏(动态规划-java)

石子游戏 leetcode877. 石子游戏题目描述暴力递归代码演示 动态规划 动态规划专题: leetcode877. 石子游戏 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/stone-game 题目描述 Alice 和 Bob 用几堆石子在做游戏。一共有…

HTTP与Fiddler使用

HTTP与Fiddler使用 HTTP与Fiddler使用FidderHTTP的报文结构:其他请求头User-agentReferer和OrigincookieHTTP状态码 HTTP与Fiddler使用 HTTP协议是使用十分广泛的应用层协议,也是一个可以由程序员进行设置的一个协议。该协议的结构规定了浏览器与万维网…

【C++】通讯录的基本实现,附有源码分享

目录 1、运行环境 2、系统实现功能 2.1菜单功能 2.2退出通讯录功能 2.3添加联系人功能 2.4显示联系人功能 2.5删除联系人功能 2.6查找联系人功能 2.7修改联系人功能 2.8清空联系人功能 2.9动态扩容功能 2.10选择优化功能 2.11文件操作 3、源码分享 1、运行环境 …

【备战秋招】每日一题:2023.04.26-华为OD机式-第三题-MC方块

在线评测链接:P1231 题目内容 MC最新版本更新了一种特殊的方块,幽匿催发体。这种方块能够吸收生物死亡掉落的经验并感染周围方块,使其变成幽匿块。Steve想要以此为基础尝试搭建一个经验仓库,他来到了创造超平坦模式,在只有草方块…

【微信小程序开发】第 7 课 - 小程序的常用组件

欢迎来到博主 Apeiron 的博客,祝您旅程愉快 ! 时止则止,时行则行。动静不失其时,其道光明。 目录 1、缘起 2、小程序中组件的分类 3、常用的视图容器类组件 3.1、view 组件 3.2、scroll - view 组件 3.3、swiper 和 swiper…

blfs:为lfs虚拟机增加桌面02

参考书籍: BLFS11.3 LFS11.3(这里面有软件安装的详细说明) 树莓派Linux操作系统移植(这里面有桌面系统的脉络梳理) 参考视频 https://www.youtube.com/watch?vcavxyXBgJ6Q&listPLyc5xVO2uDsBK_3VZOek8ICsxewOO4DU…

Vue3 网络请求——axios 高级用法之 axios 拦截器实战与并发请求

文章目录 📋前言🎯关于拦截器🎯项目创建🎯代码分析🎯补充:并发请求🧩axios.all() 和 Promise.all() 的区别 📝最后 📋前言 Axios 是一个流行的基于 Promise 的 HTTP 客户…

机器学习中的多分类问题

文章标题:机器学习中的多分类问题 机器学习中的分类问题可以大致分为二分类和多分类两种。在二分类问题中,模型需要将输入数据分为两类;而在多分类问题中,模型需要将输入数据分为多个类别。本文将介绍机器学习中的多分类问题及其…

C语言指针类型,8个例子给你讲明白

0.问题 知乎上回答了一个粉丝问题, 结果这兄弟又连续问了几个问题: 好吧,帮人帮到底,送佛送到西!给你讲彻底点吧! 1. int va; 这是一个整型变量,32位CPU的话,占有32个bite 2. in…