Keras深度学习框架实战(3):EfficientNet实现stanford dog分类

1、通过EfficientNet进行微调以实现图像分类概述

通过EfficientNet进行微调以实现图像分类,是一个使用EfficientNet作为预训练模型,并通过微调(fine-tuning)来适应特定图像分类任务的过程。一下是对相关重要术语的解释。

  • EfficientNet:这是一个高效的卷积神经网络(CNN)架构,旨在通过统一缩放网络深度、宽度和分辨率等维度来优化性能和效率。EfficientNet有多个预训练的变体,如EfficientNet-B0到EfficientNet-B8,它们在多个图像分类任务上取得了很好的效果。
  • 微调(Fine-tuning):在深度学习中,微调通常涉及使用预训练模型(如EfficientNet)的权重作为起点,并在特定数据集上进一步训练该模型。微调可以加速训练过程,因为它利用了预训练模型在大量数据上学到的通用特征。
  • 图像分类:这是计算机视觉中的一个基本任务,涉及将输入图像分配给预定义的一组类别中的一个。例如,一个图像分类模型可能被训练来识别图像中的对象(如猫、狗、汽车等)或场景(如森林、海滩、城市等)。

1.1 EfficientNet简介

EfficientNet,最初由Tan和Le在2019年提出,是目前最高效的模型之一(即在ImageNet和常见的图像分类迁移学习任务上达到最先进的准确率时所需的浮点运算量(FLOPs)最少)。

最小的基本模型类似于MnasNet,后者通过显著更小的模型达到了接近最高水平的性能。通过引入一种启发式的方法来缩放模型,EfficientNet提供了一系列模型(B0到B7),这些模型在各种规模上代表了效率和准确性的良好组合。这种缩放启发式方法(复合缩放,详情参见Tan和Le,2019)允许以效率为导向的基本模型(B0)在每个尺度上超越其他模型,同时避免了广泛的超参数网格搜索。

模型的最新更新总结可以在这里找到,其中应用了各种增强方案和半监督学习方法来进一步提高模型在ImageNet上的性能。这些模型的扩展可以通过更新权重而无需改变模型架构来使用。

1.2 EfficientNet的B0到B7变体

(如果你只对使用这些模型感兴趣,可以跳过本节中关于“复合缩放”的详细解释)

基于原始论文,人们可能会认为EfficientNet是一个通过任意选择论文中Eq.(3)中的缩放因子来创建的连续模型家族。然而,分辨率、深度和宽度的选择也受到许多因素的限制:

分辨率:分辨率如果不能被8、16等整除,会导致某些层边界附近的零填充,从而浪费计算资源。这尤其适用于模型的较小变体,因此B0和B1的输入分辨率被选为224和240。

深度和宽度:EfficientNet的构建块要求通道大小是8的倍数。

资源限制:当深度和宽度可以继续增加时,内存限制可能会限制分辨率。在这种情况下,增加深度和/或宽度但保持分辨率仍然可以提高性能。

因此,EfficientNet模型的每个变体的深度、宽度和分辨率都是精心挑选的,并已被证明能产生良好的结果,尽管它们可能与复合缩放公式显著不同。因此,Keras实现(详细如下)仅提供这8个模型,B0到B7,而不是允许任意选择宽度/深度/分辨率参数。

1.3 Keras中的EfficientNet实现

自Keras v2.3起,已经内置了EfficientNet B0到B7的实现。要使用EfficientNetB0对ImageNet的1000类图像进行分类,可以运行:

from tensorflow.keras.applications import EfficientNetB0
model = EfficientNetB0(weights='imagenet')

这个模型接受形状为(224, 224, 3)的输入图像,输入数据的范围应该在[0, 255]之间。标准化是模型的一部分。

由于在ImageNet上训练EfficientNet需要大量的资源和一些不属于模型架构本身的技巧,因此Keras实现默认加载了通过AutoAugment训练获得的预训练权重。

对于B0到B7的基础模型,输入形状是不同的。以下是每个模型所需的输入形状列表:
以下是B0到B7基础模型的分辨率的表格翻译:

基础模型分辨率
EfficientNetB0224
EfficientNetB1240
EfficientNetB2260
EfficientNetB3300
EfficientNetB4380
EfficientNetB5456
EfficientNetB6528
EfficientNetB7600

当模型用于迁移学习时,Keras实现提供了一个选项来移除顶层:

model = EfficientNetB0(include_top=False, weights='imagenet')

这个选项排除了最后一层的Dense层,该层将倒数第二层的1280个特征转化为对1000个ImageNet类别的预测。通过用自定义层替换顶层,可以在迁移学习的工作流程中将EfficientNet用作特征提取器。

在模型构造函数中另一个值得注意的参数是drop_connect_rate,它控制用于随机深度的dropout率。这个参数在微调时作为额外正则化的切换器,但不会影响已加载的权重。例如,当需要更强的正则化时,可以尝试:

model = EfficientNetB0(weights='imagenet', drop_connect_rate=0.4)

默认值为0.2 。

2、数据准备

本文将演示将EfficientNetB0用于斯坦福狗数据集。

EfficientNet能够处理广泛的图像分类任务。这使其成为迁移学习的良好模型。作为一个端到端的例子,我们将展示如何在斯坦福狗数据集上使用预训练的EfficientNetB0。
斯坦福狗数据集的下载地址如下:
http://vision.stanford.edu/aditya86/ImageNetDogs/

2.1 设置

import numpy as np
import tensorflow_datasets as tfds
import tensorflow as tf  # For tf.data
import matplotlib.pyplot as plt
import keras
from keras import layers
from keras.applications import EfficientNetB0# IMG_SIZE is determined by EfficientNet model choice
IMG_SIZE = 224
BATCH_SIZE = 64

2.2 加载数据

本文的演示从tensorflow_datasets(以下简称TFDS)加载数据。Stanford Dogs数据集在TFDS中作为stanford_dogs提供。它包含20,580张图像,这些图像属于120个狗品种类别(其中12,000张用于训练,8,580张用于测试)。

只需更改下面的dataset_name,您还可以尝试将此笔记本用于TFDS中的其他数据集,如cifar10、cifar100、food101等。当图像远小于EfficientNet的输入大小时,我们可以简单地对输入图像进行上采样。Tan和Le在2019年的研究表明,即使输入图像保持较小,增加分辨率的迁移学习结果也会更好。

dataset_name = "stanford_dogs"
(ds_train, ds_test), ds_info = tfds.load(dataset_name, split=["train", "test"], with_info=True, as_supervised=True
)
NUM_CLASSES = ds_info.features["label"].num_classes

当数据集包含各种大小的图像时,我们需要将它们调整为统一的大小。Stanford Dogs数据集仅包含至少200x200像素大小的图像。在这里,我们将图像调整为EfficientNet所需的输入大小。

ize = (IMG_SIZE, IMG_SIZE)
ds_train = ds_train.map(lambda image, label: (tf.image.resize(image, size), label))
ds_test = ds_test.map(lambda image, label: (tf.image.resize(image, size), label))

2.3 图像可视化

以下是展示前9张图像及其标签的代码:

import tensorflow as tf
import tensorflow_datasets as tfds
import matplotlib.pyplot as plt# 加载Stanford Dogs数据集
dataset, info = tfds.load('stanford_dogs', with_info=True, split='train[:1%]')  # 只加载一小部分数据进行可视化# 获取类别名称
class_names = info.features['label'].names# 从数据集中提取图像和标签
images, labels = next(iter(dataset.batch(9))).numpy()# 绘制图像
fig, axs = plt.subplots(3, 3, figsize=(12, 12))# 遍历前9个图像
for i, ax in enumerate(axs.flatten()):ax.imshow(images[i] / 255.0)  # 归一化到[0, 1]范围ax.set_title(class_names[labels[i]])ax.axis('off')plt.show()

请注意,程序添加了.numpy()来从TensorFlow的数据集中提取NumPy数组,以便于绘图。程序还将图像归一化到[0, 1]的范围,因为matplotlib的imshow函数期望图像数据在这个范围内。此外,程序中使用了split='train[:1%]'来仅加载一小部分训练数据,以便快速可视化。如果程序员想要查看更多图像,可以更改这个切片操作来加载更多的数据。
在这里插入图片描述

2.4 数据增强

程序可以使用预处理层(preprocessing layers)API来进行图像增强。

在训练深度学习模型时,数据增强是一种常见的技术,它通过应用随机变换来增加训练数据的多样性,从而提高模型的泛化能力。在TensorFlow中,可以使用Keras的预处理层(preprocessing layers)API来实现各种图像增强技术,如随机旋转、裁剪、缩放、翻转等。这些增强操作可以在训练过程中实时应用于输入图像,以生成新的、变化多样的训练样本。

img_augmentation_layers = [layers.RandomRotation(factor=0.15),layers.RandomTranslation(height_factor=0.1, width_factor=0.1),layers.RandomFlip(),layers.RandomContrast(factor=0.1),
]def img_augmentation(images):for layer in img_augmentation_layers:images = layer(images)return images

这个Sequential模型对象既可以作为我们稍后构建的模型的一部分,也可以作为函数来预处理输入模型之前的数据。将它们用作函数可以方便地可视化给定图像的增强结果。在这里,程序绘制了给定图像经过增强后的9个示例结果。

这意味着程序员可以创建一个预处理序列(Sequential model),该序列定义了一系列图像增强步骤。然后,可以将这个预处理序列作为模型的一部分,也可以将其作为独立的函数来预处理图像,以便在将图像输入到模型之前进行可视化或检查。将增强步骤作为函数应用,程序员可以很容易地看到不同的增强效果是如何影响原始图像的。

for image, label in ds_train.take(1):for i in range(9):ax = plt.subplot(3, 3, i + 1)aug_img = img_augmentation(np.expand_dims(image.numpy(), axis=0))aug_img = np.array(aug_img)plt.imshow(aug_img[0].astype("uint8"))plt.title("{}".format(format_label(label)))plt.axis("off")

在这里插入图片描述

2.5 准备输入数据

一旦我们验证了输入数据和增强功能都工作正常,就准备训练用的数据集。输入数据会被调整到统一的大小(IMG_SIZE)。标签将被编码为独热(也称为分类)编码。数据集将被批量处理。

注意:在某些情况下,使用prefetchAUTOTUNE可能会提高性能,但这取决于环境和所使用的具体数据集。请参阅这个指南以获取更多关于数据管道性能的信息。

在TensorFlow中,通常会使用tf.data API来准备数据,并利用诸如map, batch, prefetch, 和 shuffle等方法来提高数据处理效率。此外,当进行图像分类任务时,使用tf.keras.utils.to_categorical来将整数标签转换为独热编码也是很常见的做法。对于图像大小调整,可以使用tf.image.resize函数。

# One-hot / categorical encoding
def input_preprocess_train(image, label):image = img_augmentation(image)label = tf.one_hot(label, NUM_CLASSES)return image, labeldef input_preprocess_test(image, label):label = tf.one_hot(label, NUM_CLASSES)return image, labelds_train = ds_train.map(input_preprocess_train, num_parallel_calls=tf.data.AUTOTUNE)
ds_train = ds_train.batch(batch_size=BATCH_SIZE, drop_remainder=True)
ds_train = ds_train.prefetch(tf.data.AUTOTUNE)ds_test = ds_test.map(input_preprocess_test, num_parallel_calls=tf.data.AUTOTUNE)
ds_test = ds_test.batch(batch_size=BATCH_SIZE, drop_remainder=True)

3、从零开始进行模型训练

我们从头开始构建一个具有120个输出类别的EfficientNetB0模型:

注意:准确率会增长得非常慢,并且可能会出现过拟合。

EfficientNet是一种预训练的深度学习模型架构,它在多个图像分类任务上都取得了很好的效果。然而,当从头开始训练EfficientNet模型时,由于需要从头学习所有参数,因此训练过程可能会相对较长,并且可能会因为训练数据不足而出现过拟合现象。为了缓解这个问题,可以使用数据增强、正则化技术(如Dropout、L2正则化等)和早停(early stopping)等方法来减少过拟合的风险。同时,也可以使用预训练的EfficientNet模型作为特征提取器,并在此基础上进行微调(fine-tuning),以加快训练速度并提高模型的性能。

model = EfficientNetB0(include_top=True,weights=None,classes=NUM_CLASSES,input_shape=(IMG_SIZE, IMG_SIZE, 3),
)
model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])model.summary()epochs = 40  # @param {type: "slider", min:10, max:100}
hist = model.fit(ds_train, epochs=epochs, validation_data=ds_test)

以下表格包含了模型的所有层:

Layer (type)Output ShapeParam #Connected to
input_layer(None, 224, 224, 3)0-
Rescaling(None, 224, 224, 3)0input_layer[0][0]
Normalization(None, 224, 224, 3)7rescaling[0][0]
stem_conv_pad(None, 225, 225, 3)0normalization[0][0]
stem_conv (Conv2D)(None, 112, 112, 32)864stem_conv_pad[0][0]
stem_bn(None, 112, 112, 32)128stem_conv[0][0]
stem_activation(None, 112, 112, 32)0stem_bn[0][0]
block1a_dwconv(None, 112, 112, 32)288stem_activation[0][0]
block1a_bn(None, 112, 112, 32)128block1a_dwconv[0][0]
block1a_activation(None, 112, 112, 32)0block1a_bn[0][0]
block6c_add (Add)(None, 7, 7, 192)0block6c_drop[0][0], block6b_add[0][0]
block6d_expand_conv(None, 7, 7, 1152)221,184block6c_add[0][0]
block6d_expand_bn(None, 7, 7, 1152)4,608block6d_expand_conv[0][0]
block6d_expand_activation(None, 7, 7, 1152)0block6d_expand_bn[0][0]
block6d_dwconv(None, 7, 7, 1152)28,800block6d_expand_activation[0][0]
block6d_bn(None, 7, 7, 1152)4,608block6d_dwconv[0][0]
block6d_activation(None, 7, 7, 1152)0block6d_bn[0][0]
block6d_se_squeeze(None, 1152)0block6d_activation[0][0]
block6d_se_reshape(None, 1, 1, 1152)0block6d_se_squeeze[0][0]
block6d_se_reduce(None, 1, 1, 48)55,344block6d_se_reshape[0][0]
block6d_se_expand(None, 1, 1, 1152)56,448block6d_se_reduce[0][0]
block6d_se_excite(None, 7, 7, 1152)0block6d_activation[0][0], block6d_se_expand[0][0]
block6d_project_conv(None, 7, 7, 192)221,184block6d_se_excite[0][0]
block6d_project_bn(None, 7, 7, 192)768block6d_project_conv[0][0]
block6d_drop(None, 7, 7, 192)0block6d_project_bn[0][0]
block6d_add (Add)(None, 7, 7, 192)0block6d_drop[0][0], block6c_add[0][0]
block7a_expand_conv(None, 7, 7, 1152)221,184block6d_add[0][0]
block7a_expand_bn(None, 7, 7, 1152)4,608block7a_expand_conv[0][0]
block7a_expand_activation(None, 7, 7, 1152)0block7a_expand_bn[0][0]
block7a_dwconv(None, 7, 7, 1152)10,368block7a_expand_activation[0][0]
block7a_bn(None, 7, 7, 1152)4,608block7a_dwconv[0][0]
block7a_activation(None, 7, 7, 1152)0block7a_bn[0][0]
block7a_se_squeeze(None, 1152)0block7a_activation[0][0]
block7a_se_reshape(None, 1, 1, 1152)0block7a_se_squeeze[0][0]
block7a_se_reduce(None, 1, 1, 48)55,344block7a_se_reshape[0][0]
block7a_se_expand(None, 1, 1, 1152)56,448block7a_se_reduce[0][0]
block7a_se_excite(None, 7, 7, 1152)0block7a_activation[0][0], block7a_se_expand[0][0]
block7a_project_conv(None, 7, 7, 320)368,640block7a_se_excite[0][0]
block7a_project_bn(None, 7, 7, 320)1,280block7a_project_conv[0][0]
top_conv (Conv2D)(None, 7, 7, 1280)409,600block7a_project_bn[0][0]
top_bn(None, 7, 7, 1280)5,120top_conv[0][0]
top_activation(None, 7, 7, 1280)0top_bn[0][0]
avg_pool (GlobalAveragePooling2D)(None, 1280)0top_activation[0][0]
top_dropout(None, 1280)0avg_pool[0][0]
predictions (Dense)(None, 120)153,720top_dropout[0][0]

请注意,"…" 表示中间层已被省略,以避免冗长。如果需要中间层的详细信息,请告知我,我将提供完整的数据。

import matplotlib.pyplot as plt训练模型的速度相对较快。这可能让人误以为可以轻松地从头开始在任何想要的数据集上训练EfficientNet。然而,在较小的数据集上训练EfficientNet,尤其是像CIFAR-100这样分辨率较低的数据集,会面临过拟合的重大挑战。因此,从头开始训练需要非常谨慎地选择超参数,并且很难找到合适的正则化方法。这也会在资源上提出更高的要求。绘制训练和验证准确率图表可以清楚地看到,验证准确率停滞在一个较低的值上。这段文字主要强调了从头开始训练EfficientNet模型可能面临的挑战,包括过拟合问题、超参数选择的难度以及资源需求的增加。同时,通过绘制训练和验证准确率图表,可以直观地看到模型在验证集上的表现并不理想,准确率提升有限。
def plot_hist(hist):plt.plot(hist.history["accuracy"])plt.plot(hist.history["val_accuracy"])plt.title("model accuracy")plt.ylabel("accuracy")plt.xlabel("epoch")plt.legend(["train", "validation"], loc="upper left")plt.show()plot_hist(hist)

在这里插入图片描述

4、从预训练权重进行迁移学习

为改善第三节从零开始训练模型的不足,在这里,我们使用在ImageNet上预训练的权重来初始化模型,并在我们自己的数据集上对其进行微调。

def build_model(num_classes):inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3))model = EfficientNetB0(include_top=False, input_tensor=inputs, weights="imagenet")# Freeze the pretrained weightsmodel.trainable = False# Rebuild topx = layers.GlobalAveragePooling2D(name="avg_pool")(model.output)x = layers.BatchNormalization()(x)top_dropout_rate = 0.2x = layers.Dropout(top_dropout_rate, name="top_dropout")(x)outputs = layers.Dense(num_classes, activation="softmax", name="pred")(x)# Compilemodel = keras.Model(inputs, outputs, name="EfficientNet")optimizer = keras.optimizers.Adam(learning_rate=1e-2)model.compile(optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"])return model

4.1 训练顶层

迁移学习的第一步是冻结所有层并仅训练顶层。在这一步中,可以使用相对较大的学习率(如1e-2)。请注意,验证准确率和损失通常会比训练准确率和损失要好。这是因为正则化很强,它只会抑制训练时的指标。

请注意,收敛可能需要多达50个epoch,具体取决于所选的学习率。如果未应用图像增强层,验证准确率可能只能达到约60%。

model = build_model(num_classes=NUM_CLASSES)epochs = 25  # @param {type: "slider", min:8, max:80}
hist = model.fit(ds_train, epochs=epochs, validation_data=ds_test)
plot_hist(hist)

在这里插入图片描述

4.2 解冻其它层

第二步是解冻一定数量的层并使用较小的学习率来训练模型。在这个例子中,我们展示了如何解冻所有层,但具体取决于数据集,可能只解冻所有层的一部分会更合适。

当使用预训练模型进行特征提取的效果足够好时,这一步在验证准确率上的提升会非常有限。在我们的例子中,我们只看到了很小的提升,因为ImageNet预训练已经让模型接触到了大量的狗的图片。

另一方面,当我们在与ImageNet差异更大的数据集上使用预训练权重时,这个微调步骤可能至关重要,因为特征提取器也需要做出相当大的调整。如果选择CIFAR-100数据集作为例子,这种情况可以得到体现,其中微调可以将EfficientNetB0的验证准确率提升约10%,达到80%以上。

关于模型冻结/解冻的一个旁注:设置模型的trainable属性将同时设置该模型所属的所有层的trainable属性。只有当层本身和包含它的模型都是可训练的,该层才是可训练的。因此,当我们需要部分冻结/解冻一个模型时,我们需要确保模型的trainable属性被设置为True。

def unfreeze_model(model):# We unfreeze the top 20 layers while leaving BatchNorm layers frozenfor layer in model.layers[-20:]:if not isinstance(layer, layers.BatchNormalization):layer.trainable = Trueoptimizer = keras.optimizers.Adam(learning_rate=1e-5)model.compile(optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"])unfreeze_model(model)epochs = 4  # @param {type: "slider", min:4, max:10}
hist = model.fit(ds_train, epochs=epochs, validation_data=ds_test)
plot_hist(hist)

在这里插入图片描述

4.3 微调EfficientNet要点

在解冻层时:

  • BatchNormalization层需要保持冻结(更多细节)。如果它们也被设置为可训练的,解冻后的第一个epoch将显著降低准确率。

  • 在某些情况下,只解冻部分层而不是全部层可能更有益。当使用更大的模型如B7时,这样做将大大加快微调过程。

  • 每个块需要全部打开或关闭。这是因为架构包括从每个块的第一层到最后一层的快捷连接。不尊重块也会显著损害最终性能。

利用EfficientNet的其他提示:

  • EfficientNet的较大变体并不保证性能的提升,特别是对于数据量较少或类别较少的任务。在这种情况下,选择的EfficientNet变体越大,调整超参数就越困难。

  • **EMA(指数移动平均)**在从头开始训练EfficientNet时非常有用,但在迁移学习中帮助不大。

  • 不要使用原始论文中的RMSprop设置进行迁移学习。迁移学习中的动量和学习率太高。它很容易破坏预训练权重并使损失爆炸。一个简单的检查方法是看同一epoch后损失(作为分类交叉熵)是否显著大于log(NUM_CLASSES)。如果是,那么初始学习率/动量太高。

  • 较小的批处理大小有利于验证准确率,可能是因为有效地提供了正则化。

5、总结

今天我们对EfficientNet进行了深入的讨论,以下是对讨论的总结:

EfficientNet概述

EfficientNet是一个高效且可扩展的卷积神经网络模型家族,它通过系统地平衡网络深度、宽度和分辨率,以较小的模型尺寸实现了出色的性能。EfficientNet模型在多个基准测试中均取得了显著的性能提升,包括ImageNet分类、CIFAR-100分类以及COCO目标检测等。

EfficientNet的特点

  • 系统平衡:EfficientNet通过一种复合缩放方法,同时缩放网络的深度、宽度和分辨率,以在给定计算资源下实现最佳性能。

  • 高效性:EfficientNet使用了MobileNetV2中的MBConv(Mobile inverted bottleneck convolution)作为其构建块,结合了深度可分离卷积和Squeeze-and-Excitation模块,从而实现了高效计算和参数利用。

  • 可扩展性:EfficientNet具有多个不同规模的变体,包括EfficientNetB0到EfficientNetB8,这些变体在模型大小和性能之间提供了灵活的权衡。

迁移学习与微调EfficientNet

  • 冻结与解冻层:在迁移学习中,首先冻结所有层并仅训练顶层是一种常见做法。随着训练的进行,可以逐步解冻更多层以进行微调。然而,需要注意的是,在解冻过程中,BatchNormalization层应保持冻结以避免性能下降。

  • 学习率调整:在微调过程中,学习率的选择至关重要。初始阶段可以使用较大的学习率以加速训练,但随后应逐渐减小学习率以进行更精细的调整。

  • 数据增强:在迁移学习中,数据增强对于提高模型的泛化能力非常重要。通过使用如随机裁剪、翻转、旋转等数据增强技术,可以增加模型的鲁棒性。

使用EfficientNet的注意事项

  • 模型大小与任务匹配:较大的EfficientNet变体并不总是能保证更好的性能,特别是在数据较少或类别较少的任务中。因此,在选择模型时,需要根据具体任务和数据集来权衡模型大小和性能。

  • 优化器与学习率设置:对于迁移学习任务,不建议使用原始论文中的RMSprop优化器和较高的学习率。相反,使用更稳定的优化器(如Adam或SGD)和较低的学习率通常更为合适。

  • 正则化技术:在微调过程中,可以考虑使用正则化技术(如Dropout、Weight Decay等)来防止过拟合。这些技术可以帮助模型在训练过程中更好地泛化到未见过的数据。

EfficientNet是一个强大且灵活的卷积神经网络模型家族,适用于各种计算机视觉任务。通过合理的迁移学习策略和微调技巧,可以充分发挥EfficientNet的性能优势。

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

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

相关文章

Flutter-自定义可展开文本控件

Flutter 在移动开发中,常常需要处理一些长文本显示的场景,如何优雅地展示这些文本并允许用户展开和收起是一个常见的需求。在本文中,我将分享如何使用Flutter实现一个可展开和收起的文本控件。 效果 我们将实现一个可展开和收起的文本控件…

yolov10模块

yolov10模块 1 C2f2 C2fCIB2.1 CIB2.2 RepVGGDW 3 PSA4 SCDown5 v10Detect 论文代码:https://github.com/THU-MIG/yolov10 论文链接:https://arxiv.org/abs/2405.14458 Conv是Conv2dBNSiLU PW是Pointwise Convolution(逐点卷积) DW是Depthwise Convolut…

【SQL学习进阶】从入门到高级应用【企业真题】

文章目录 第一题第二题第三题第四题第五题第六题第七题第八题第九题MySQL行转列使用case whengroup by完成 第十题 🌈你好呀!我是 山顶风景独好 💕欢迎来到我的博客,很高兴能够在这里和您见面! 💕希望您在这…

疫情物资捐赠和分配系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,机构管理,用户管理,发放管理,物资管理 前台账户功能包括:系统首页,个人中心,物资论坛,公告信息…

STM32作业设计

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

彻底卸载Windows Defender

概述 卸载Windows Defender的方法有很多,如修改注册表、组策略,执行脚本等等,这些方法操作过于繁琐和复杂,不适合小白,今天带来一款强大的卸载工具,只需要以管理员身份运行该软件即可,不用其他操…

禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》Chapter 6插图

禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》 Chapter 6插图

Stable Diffusion详细教程

目录 🐋引言 🐋Stable Diffusion基本概念 🦈潜在扩散模型 🦈图像生成原理 🐋Stable Diffusion安装部署 🦈环境要求 🦈安装步骤 🐋Stable Diffusion阶段 🦈准备阶…

PHP 页面报错Warning</b>: Cannot modify header information - headers already sent by

先给出解决方案再解释,如果急着用就不用看解释了。 解决方案一:保存php文件编码为utf-8无BOM码,具体操作可以用notepad等编辑器完成,把 sesstion_start() 放在文档所有输出(包括html标签和php的输出语句,具…

ch4网络层---计算机网络期末复习(持续更新中)

网络层概述 将分组从发送方主机传送到接收方主机 发送方将运输层数据段封装成分组 接收方将分组解封装后将数据段递交给运输层网络层协议存在于每台主机和路由器上 路由器检查所有经过它的IP分组的分组头 注意路由器只有3层(网络层、链路层、物理层) 网络层提供的服务 一…

Java筑基-集合[Set、Map、List、Stack、Queue]

这里写目录标题 一、Collection接口结构图二、Set集合1、常用方法 三、List集合1、List集合常用方法2、代码案例 四、Stack集合1、方法2、代码展示 五、Queue集合1、常用的方法2、代码展示 六、Map集合1、基本概念2、常用方法3、代码展示 一、Collection接口结构图 二、Set集合…

小熊家务帮day8-day9 客户管理模块2 (用户定位,地址簿,实名认证,银行卡信息上传等功能)

客户管理模块 0.用户定位功能0.1 需求0.2 接口分析0.3 接口开发Controller层开发Service层开发 1.我的地址簿功能1.1 需求1.2 数据库设计1.3 新增地址簿1.3.1 接口设计1.3.2 接口开发Controller层开发Service层开发测试功能 1.4 地址簿查询1.4.1 接口设计1.4.2 接口开发Control…

Caliburn.Micro框架学习笔记——多页面处理案例

在聊这个之前,我们先来看一个静态类 在 Caliburn.Micro 中,ViewLocator 是一个用于查找和关联视图与视图模型的静态类。默认情况下,它根据约定(命名约定或其他规则)自动找到与视图模型相对应的视图。然而,…

C语言 | Leetcode C语言题解之第126题单词接龙II

题目: 题解: char** list; int** back; int* backSize;// DFS uses backtrack information to construct results void dfs(char*** res, int* rSize, int** rCSizes, int* ans, int last, int retlevel) {int i ans[last];if (i 0) {res[*rSize] (c…

实验报告 GUI窗体和标签

实验目的: 理解AWT和Swing工具包 掌握窗体和组件的创建方法。 实验要求: (1)掌握创建GUI窗体的操作 (2)掌握标签的使用 (3)代码应遵循Java编程规范,包含恰当的注释…

网上蛋糕售卖店管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,店员管理,用户管理,商品管理,基础数据管理 前台账户功能包括:系统首页,个人中心,公告信息,商品…

2024医美如何做抖音医美抖音号,本地团购、短视频直播双ip爆品引流,实操落地课

课程下载:https://download.csdn.net/download/m0_66047725/89307619 更多资源下载:关注我。 课程内容: 01-0-序.mp4 02-01-账号定位.mp4 03-02-误区.mp4 04-03-五件套.mp4 05-04-文案怎么来.mp4 06-05-对标怎么弄.mp4 07-06-人设怎…

进程与线程(四)

进程与线程(四) 基于System V IPC对象的进程间通信机制SystemV IPC引入查看Linux系统中IPC工具的方式查看所有IPC工具命令:ipcs 查看指定的IPC工具key值获取方法:ftok()函数 消息队列消息队列的特征:消息队列的操作打开…

处理多对一的映射关系

1、级联属性赋值 Emp实体类中设置Dept 属性并生成get、set方法,生成toString方法,但是构造方法不必包含此属性 在EmpMapper.xml文件中: <resultMap id="empAndDeptResultMapOne" type="Emp"><id property="eid" column="eid…

Python知识点5---字符串的使用

提前说一点&#xff1a;如果你是专注于Python开发&#xff0c;那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了&#xff0c;而如果你和作者一样只是操作其他技术的Python API那就足够了。 Python的字符串在使用上和其他语言的差别不大&#xff0c;常规操作都…