【Pytorch with fastai】第 15 章 :深入探讨应用程序架构

news2024/11/24 2:22:16

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

计算机视觉

cnn_learner

unet_learner

A Siamese Network

自然语言处理

Tabular

结论


我们现在处于令人兴奋的位置,我们可以完全理解我们一直用于计算机视觉、自然语言处理和表格分析的最先进模型的体系结构。在本章中,我们将填补所有关于 fastai 应用程序模型如何工作的缺失细节,并向您展示如何构建它们。

我们还将回到我们在 第 11 章中看到的用于 Siamese 网络的自定义数据预处理管道,并向您展示如何使用 fastai 库中的组件为新任务构建自定义预训练模型。

我们将从计算机视觉开始。

计算机视觉

对于计算机视觉应用程序,我们根据任务使用函数cnn_learner和 构建模型。unet_learner 在本节中,我们将探索如何构建我们在本书第一部分和第二Learner部分中使用的对象。

cnn_learner

让我们看看当我们使用该 cnn_learner函数时会发生什么。我们首先将此函数传递给用于网络主体的体系结构 。大多数时候,我们使用你已经知道的 ResNet 如何创建,所以我们不需要再深入研究了。根据需要下载预训练的权重并加载到 ResNet 中。

然后,对于迁移学习,需要对网络进行切割。这是指切掉最后一层,它只负责 ImageNet 特定的分类。事实上,我们不仅切掉了这一层,还切掉了自适应平均池化层之后的所有内容。其原因马上就会明了。由于不同的架构可能使用不同类型的池化层,甚至是完全不同种类的heads,我们不只是搜索自适应池化层来决定在哪里切割预训练模型。相反,我们有一个信息字典,用于每个模型来确定它的身体在哪里结束,它的头从哪里开始。我们称之为model_meta——这里是为了resnet50

model_meta[resnet50]
{'cut': -2,
 'split': <function fastai.vision.learner._resnet_split(m)>,
 'stats': ([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])}

身体和头部

如果我们在 的切点之前采用所有层-2,我们将得到 fastai 将保留用于迁移学习的模型部分。现在,我们戴上新的头颅。这是使用函数创建的create_head

create_head(20,2)
Sequential(
  (0): AdaptiveConcatPool2d(
    (ap): AdaptiveAvgPool2d(output_size=1)
    (mp): AdaptiveMaxPool2d(output_size=1)
  )
  (1): Flatten()
  (2): BatchNorm1d(20, eps=1e-05, momentum=0.1, affine=True)
  (3): Dropout(p=0.25, inplace=False)
  (4): Linear(in_features=20, out_features=512, bias=False)
  (5): ReLU(inplace=True)
  (6): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True)
  (7): Dropout(p=0.5, inplace=False)
  (8): Linear(in_features=512, out_features=2, bias=False)
)

使用此功能,您可以选择在最后添加多少个额外的线性层,在每个层之后使用多少 dropout,以及使用哪种池化。默认情况下,fastai 将同时应用平均池化和最大池化,并将两者连接在一起(这就是 AdaptiveConcatPool2d层)。这不是一种特别常见的方法,但它是近年来在 fastai 和其他研究实验室独立开发的,并且倾向于提供比仅使用平均池化的小改进。

fastai 与大多数库有点不同,默认情况下它在 CNN 头部添加两个线性层,而不是一个。原因是,正如我们所见,即使在将预训练模型转移到非常不同的领域时,转移学习仍然有用。然而,在这些情况下,仅仅使用一个线性层是不够的;我们发现使用两个线性层可以让迁移学习在更多情况下更快更容易地使用。

最后一个BATCHNORM

其中一个create_head值得关注的参数是bn_final. 将此设置为True将导致将 batchnorm 层添加为最后一层。这有助于帮助您的模型针对输出激活进行适当缩放。我们还没有看到这种方法在任何地方发布,但我们发现无论我们在哪里使用它,它在实践中都能很好地发挥作用。

现在让我们看看unet_learner我们在第 1 章中展示的分割问题做了什么。

unet_learner

深度学习中最有趣的架构之一是我们在第 1 章中用于分割的架构。分割是一项具有挑战性的任务,因为所需的输出实际上是图像, 或像素网格,包含每个像素的预测标签。其他任务共享类似的基本设计,例如增加图像的分辨率(超分辨率)、为黑白图像添加颜色(着色)或将照片转换为合成绘画(风格转换)——这些任务包含在 本书的在线章节中,因此请务必在阅读本章后检查一下。在每种情况下,我们都从一张图像开始,然后将其转换为另一张具有相同尺寸或​​纵横比的图像,但像素会以某种方式改变。我们将这些称为生成视觉模型

我们这样做的方法是从与我们在上一节中看到的完全相同的方法开始开发 CNN 头。例如,我们从 ResNet 开始,然后切断自适应池化层和之后的所有内容。然后我们用我们的自定义头部替换这些层,它执行生成任务。

最后一句话中有很多挥手!我们究竟如何创建生成图像的 CNN 头?如果我们从一个 224 像素的输入图像开始,那么在 ResNet 主体的末尾我们将有一个 7×7 的卷积激活网格。我们如何将其转换为 224 像素的分割蒙版?

自然地,我们用神经网络来做到这一点!所以我们需要某种层来增加 CNN 中的网格大小。一种简单的方法是将 7×7 网格中的每个像素替换为 2×2 正方形中的四个像素。这四个像素中的每一个都将具有相同的值——这被称为最近邻插值。PyTorch 提供了一个层来为我们做这件事,所以一个选择是创建一个头部,其中包含 stride-1 卷积层(以及像往常一样的 batchnorm 和 ReLU 层),中间散布着 2×2 最近邻插值层。事实上,你现在就可以试试这个!看看您是否可以创建一个像这样设计的自定义头部,并在 CamVid 分割任务中尝试。您应该会发现您得到了一些合理的结果,尽管它们不如我们第 1 章的结果好。

另一种方法是用转置卷积代替最近邻和卷积组合,也称为跨步半卷积。这与常规卷积相同,但首先在输入中的所有像素之间插入零填充。这通过图片最容易看出——图 15-1显示了我们在第 13 章讨论的出色的卷积算法论文中的图表,显示了应用于 3×3 图像的 3×3 转置卷积。

图 15-1。转置卷积(由 Vincent Dumoulin 和 Francesco Visin 提供)

如您所见,结果是增加了输入的大小。您现在可以使用 fastai 的课程来尝试一下ConvLayer;传递参数transpose=True以在您的自定义头部中创建转置卷积,而不是常规卷积。

然而,这两种方法都效果不佳。问题是我们的 7×7 网格根本没有足够的信息来创建 224×224 像素的输出。它要求每个网格单元进行大量激活,以获得足够的信息来完全重新生成输出中的每个像素。

解决方案是使用skip connections,就像在 ResNet 中一样,但是从 ResNet 主体中的激活一直跳过到架构另一侧的转置卷积的激活。如图 15-2所示,这种方法是由 Olaf Ronneberger 等人开发的。在 2015 年的论文“U-Net: Convolutional Networks for Biomedical Image Segmentation”中。虽然论文侧重于医学应用,但 U-Net 已经彻底改变了各种生成视觉模型。

图 15-2。U-Net 架构(由 Olaf Ronneberger、Philipp Fischer 和 Thomas Brox 提供)

此图显示左侧的 CNN 主体(在这种情况下,它是常规 CNN,而不是 ResNet,并且他们使用 2×2 最大池而不是 stride-2 卷积,因为这篇论文是在 ResNet 出现之前写的)和右侧的转置卷积(“up-conv”)层。额外的跳过连接显示为从左到右交叉的灰色箭头(这些有时称为 交叉连接)。你可以明白为什么它被称为U-Net了!

使用这种架构,转置卷积的输入不仅是前一层中的低分辨率网格,而且是 ResNet 头部中的高分辨率网格。这允许 U-Net 根据需要使用原始图像的所有信息。U-Nets 的一个挑战是确切的架构取决于图像大小。fastai 有一个独特的DynamicUnet类,可以根据提供的数据自动生成大小合适的架构。

现在让我们关注一个示例,在该示例中我们利用 fastai 库编写自定义模型。

A Siamese Network

让我们回到我们在 第 11 章中为孪生网络设置的输入管道。您可能还记得,它由一对图像组成,标签为True或 False,具体取决于它们是否属于同一类。

使用我们刚刚看到的内容,让我们为此任务构建一个自定义模型并对其进行训练。如何?我们将使用预训练架构并通过它传递我们的两个图像。然后我们可以连接结果并将它们发送到将返回两个预测的自定义头。在模块方面,这看起来像这样:

class SiameseModel(Module):
    def __init__(self, encoder, head):
        self.encoder,self.head = encoder,head

    def forward(self, x1, x2):
        ftrs = torch.cat([self.encoder(x1), self.encoder(x2)], dim=1)
        return self.head(ftrs)

要创建我们的编码器,我们只需要采用预训练模型并将其剪切,如前所述。该功能create_body为我们做到了;我们只需要将它传递到我们想要切割的地方。正如我们之前看到的,根据预训练模型的元数据字典,ResNet 的切割值为–2

encoder = create_body(resnet34, cut=-2)

然后我们可以创建我们的头。查看编码器告诉我们最后一层有 512 个特征,所以这个头需要接收512*2. 为什么是2?首先我们必须乘以 2,因为我们有两个图像。然后由于我们的连接池技巧,我们需要第二次乘以 2。所以我们创建头部如下:

head = create_head(512*2, 2, ps=0.5)

使用我们的编码器和头部,我们现在可以构建我们的模型:

model = SiameseModel(encoder, head)

在使用 之前Learner,我们还有两件事要定义。首先,我们必须定义我们要使用的损失函数。它是常规交叉熵,但由于我们的目标是布尔值,我们需要将它们转换为整数,否则 PyTorch 会抛出错误:

def loss_func(out, targ):
    return nn.CrossEntropyLoss()(out, targ.long())

更重要的是,为了充分利用迁移学习,我们必须定义一个自定义拆分器。拆分器是一个函数,它告诉 fastai 库如何将模型拆分为参数组。当我们进行迁移学习时,这些在幕后仅用于训练模型的头部。

这里我们需要两组参数:一组用于编码器,一组用于头部。因此,我们可以定义以下拆分器(params只是一个返回给定模块的所有参数的函数):

def siamese_splitter(model):
    return [params(model.encoder), params(model.head)]

然后我们可以Learner通过传递数据、模型、损失函数、拆分器和我们想要的任何指标来定义我们的。由于我们没有使用 fastai 的便利函数进行迁移学习(如 cnn_learner),我们必须learn.freeze手动调用。这将确保只训练最后一个参数组(在本例中为头部):

learn = Learner(dls, model, loss_func=loss_func,
                splitter=siamese_splitter, metrics=accuracy)
learn.freeze()

然后我们可以直接用通常的方法训练我们的模型:

learn.fit_one_cycle(4, 3e-3)
epochtrain_lossvaild_lossaccuracytime
00.3670150.2812420.88565600:26
10.3076880.2147210.91542600:26
20.2752210.1706150.93640100:26
30.2237710.1596330.94384300:26

现在我们解冻并微调整个模型,使用判别学习率(即身体的学习率较低,头部的学习率较高):

learn.unfreeze()
learn.fit_one_cycle(4, slice(1e-6,1e-4))
epochtrain_lossvaild_lossaccuracytime
00.2127440.1590330.94452000:35
10.2018930.1596150.94249000:35
20.2046060.1523380.94519600:36
30.2132030.1483460.94790300:36

当我们记得以相同方式训练的分类器(没有数据增强)的错误率为 7% 时,94.8% 是非常好的。

现在我们已经了解了如何创建完整的最先进的计算机视觉模型,让我们继续讨论 NLP。

自然语言处理

正如我们在第 10 章中所做的那样,将 AWD-LSTM 语言模型转换为迁移学习分类器遵循一个非常cnn_learner与我们在本章第一部分中 所做的类似的过程。在这种情况下,我们不需要“元”字典,因为我们没有这么多的体系结构来支持身体。我们需要做的就是为语言模型中的编码器选择堆叠式 RNN,这是一个单独的 PyTorch 模块。该编码器将为输入的每个单词提供激活,因为语言模型需要为每个下一个单词输出预测。

为了由此创建分类器,我们使用了ULMFiT 论文中描述为“用于文本分类的 BPTT (BPT3C)”的方法:

我们将文档分成大小为b的固定长度的批次。在每批开始时,模型用前一批的最终状态初始化;我们跟踪均值和最大池化的隐藏状态;梯度被反向传播到其隐藏状态对最终预测有贡献的批次。在实践中,我们使用可变长度的反向传播序列。

换句话说,分类器包含一个for 循环,循环遍历序列的每个批次。跨批维护状态,并存储每个批的激活。最后,我们使用与计算机视觉模型相同的平均和最大串联池化技巧——但这一次,我们不是在 CNN 网格单元上进行池化,而是在 RNN 序列上进行池化。

对于这个for循环,我们需要分批收集数据,但每个文本都需要单独处理,因为它们都有自己的标签。然而,很可能这些文本的长度并不相同,这意味着我们无法将它们全部放在同一个数组中,就像我们对语言模型所做的那样。

这就是填充的作用所在:当抓取一堆文本时,我们确定长度最大的那个;然后我们用一个叫做 的特殊标记填充较短的那些xxpad。为了避免在同一批次中包含 2,000 个标记的文本和包含 10 个标记的文本的极端情况(因此大量填充和大量浪费的计算),我们通过确保将大小相当的文本放在一起来改变随机性. 训练集的文本仍将以某种随机顺序排列(对于验证集,我们可以简单地按长度顺序对它们进行排序),但并非完全如此。

这是在创建我们的 .fastai 库时在幕后自动完成的DataLoaders

Tabular

最后,让我们来看看fastai.tabular模型。(我们不需要单独看协同过滤,因为我们已经看到这些模型只是表格 模型或使用我们之前从头开始实施的点积方法。)

这是以下forward方法TabularModel

if self.n_emb != 0:
    x = [e(x_cat[:,i]) for i,e in enumerate(self.embeds)]
    x = torch.cat(x, 1)
    x = self.emb_drop(x)
if self.n_cont != 0:
    x_cont = self.bn_cont(x_cont)
    x = torch.cat([x, x_cont], 1) if self.n_emb != 0 else x_cont
return self.layers(x)

我们不会在这里展示,因为它不是那么有趣,但会依次__init__查看每一行代码 。forward第一行只是测试是否有任何嵌入要处理——如果我们只有连续变量,我们可以跳过这一部分:

if self.n_emb != 0:

self.embeds包含嵌入矩阵,所以这得到了每个

    x = [e(x_cat[:,i]) for i,e in enumerate(self.embeds)]

并将它们连接成一个张量:

    x = torch.cat(x, 1)

然后应用dropout。您可以传递embed_p__init__更改此值:

    x = self.emb_drop(x)

现在我们测试是否有任何连续变量需要处理:

if self.n_cont != 0:

它们通过 batchnorm 层

    x_cont = self.bn_cont(x_cont)

并与嵌入激活连接,如果有的话:

    x = torch.cat([x, x_cont], 1) if self.n_emb != 0 else x_cont

最后,它通过线性层(每个层都包括 batchnorm,if use_bnisTrue和 dropout,ifps被设置为某个值或值列表):

return self.layers(x)

恭喜!现在你知道了 fastai 库中使用的每一个架构!

结论

如您所见,深度学习架构的细节现在不必吓到您了。你可以查看 fastai 和 PyTorch 的代码,看看到底发生了什么。更重要的是,试着理解为什么会这样。查看代码中引用的论文,并尝试查看代码如何与所描述的算法相匹配。

现在我们已经研究了模型的所有部分和传递给它的数据,我们可以考虑这对实际深度学习意味着什么。如果你有无限的数据、无限的内存和无限的时间,那么建议很简单:在你所有的数据上训练一个巨大的模型很长时间。但深度学习并不简单的原因是您的数据、内存和时间通常是有限的。如果您的内存或时间不足,解决方案是训练一个较小的模型。如果你不能训练足够长的时间来过度拟合,您没有利用模型的能力。

因此,第 1 步是达到可以过度拟合的程度。那么问题是如何减少过度拟合。图 15-3 显示了我们建议如何从那里确定步骤的优先级。

图 15-3。减少过度拟合的步骤

许多从业者在面对过度拟合模型时,从这张图的错误一端开始。他们的出发点是使用更小的模型或更多的正则化。使用较小的模型绝对应该是您采取的最后一步,除非训练您的模型占用了太多时间或内存。减小模型的大小会降低模型学习数据中微妙关系的能力。

相反,您的第一步应该是寻求创建更多数据。这可能涉及为您已有的数据添加更多标签,寻找您的模型可能需要解决的其他任务(或者,换一种方式思考,识别您可以建模的不同类型的标签),或创建额外的合成数据通过使用更多或不同的数据增强技术。由于 Mixup 和类似方法的发展,现在几乎所有类型的数据都可以进行有效的数据增强。

一旦您获得了您认为可以合理获取的尽可能多的数据,并通过利用您可以找到的所有标签并进行所有有意义的扩充,尽可能有效地使用它,如果您仍然过度拟合,你应该考虑使用更通用的架构。例如,添加批量归一化可能会提高泛化能力。

如果您在尽最大努力使用数据和调整架构后仍然过拟合,您可以看看正则化。一般来说,在最后一两层添加 dropout 可以很好地规范你的模型。然而,正如我们从 AWD-LSTM 的开发故事中了解到的那样,在整个模型中添加不同类型的 dropout 通常会更有帮助。一般来说,具有更多正则化的较大模型更灵活,因此比具有较少正则化的较小模型更准确。

只有在考虑了所有这些选项之后,我们才会建议您尝试使用较小版本的体系结构。

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

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

相关文章

Docker概念基本介绍以及安装

目录 一、Docker概述 1.1Docker是什么 1.2Docker和虚拟机的区别 1.3使用场景 1.4 Docker 三要素&#xff08;核心组件&#xff09; 1.5六大名称空间 1.6 Docker引擎 1.7资源控制——cgroups 1.8容器特性 1.9 容器小的架构体系 二、Docker和虚拟化的区别 三、dock…

MATLAB循环类型

MATLAB 提供以下类型的循环处理循环的要求。点击链接&#xff0c;查看个循环类型的细节&#xff1a; 循环类型描述while 循环一个给定的条件为真时重复语句或语句组。测试条件才执行循环体。for 循环执行的语句序列多次缩写管理循环变量的代码。nested 循环可以使用一个或多个…

2022年深度学习最新研究成果

一、开源深度学习编译器 二、 开源深度学习加速器 三、AITemplate引擎 四、微型机器学习框架 参考文献&#xff1a;https://arxiv.org/pdf/1510.00149.pdf 五、Contrastive Learning 对比学习综述 六、Diffusion Model 扩散模型综述 Diffusion Models: A Comprehensive Surv…

【Python百日进阶-WEB开发】Day180 - Django案例:12用户登录

文章目录十、异步发送短信验证码 - 异步方案Celery10.1 生产者消费者设计模式10.2 Celery介绍10.3 Celery介绍和使用10.3.1十一、用户账号 登录11.1 用户登录11.1.1 用户登录逻辑分析11.1.2 用户登录接口设计和定义11.1.3 用户登录后端逻辑11.2 多账号登录11.2.1 自定义用户认证…

实战三:基于LGB实现车联网大数据碰撞识别 代码+数据(非常详细可作为毕设)

项目介绍&#xff1a; 使用的数据为采集车辆信号。车辆信息非常多&#xff0c;而且用户路况信息和使用偏好千人千面&#xff0c;很难找到一种准确识别碰撞的方法&#xff0c;希望参赛者通过车联网大数据识别车辆碰撞和碰撞时间。车辆标签信息如下&#xff1a; 车号LabelCollec…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.5 SpringBoot 整合 Redis

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.5 SpringBoot 整合 Redis4.5.1 环境准备4.5.2 使用…

多系统-单点登录测试

单点登录 文章目录单点登录单点登录 单点登录全程Single Sign On&#xff08;SSO&#xff09;,在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的应用系统&#xff0c;包括单点登录和单点注销两部分&#xff0c;是目前比较流行的企业业务整合的解决方案…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.1 内置数据源

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.1 内置数据源4.1.1 现有数据层解决方案4.1.2 小结…

波束形成,通过matlab仿真不同参数的波束形成以及旁絆级

目录 1.算法概述 2.仿真效果预览 3.核心MATLAB代码预览 4.完整MATLAB程序 1.算法概述 波束成形技术&#xff08;Beam Forming&#xff0c;BF&#xff09;可分为自适应波束成形、固定波束和切换波束成形技术。固定波束即天线的方向图是固定的&#xff0c;把IS-95中的三个120…

同花顺_代码解析_技术指标_B

本文通过对同花顺中现成代码进行解析&#xff0c;用以了解同花顺相关策略设计的思想 目录 BBI BBIBOLL BETA BIAS BIASFS BOLL BOLLFS BTI BBI 多空指标 多空指标:(收盘价的M1日简单移动平均收盘价的M2日简单移动平均收盘价的M3日简单移动平均收盘价的M4日简单移动平均…

G蛋白偶联受体/激酶/离子通道——高通量筛选

GPCR Library &#xff08;含54,080 种化合物&#xff09;用于发现新的GPCR配体 G蛋白偶联受体 (GPCR)是新药研发人员关注的一类重要蛋白&#xff0c;三分之一FDA 批准药物靶向 GPCR。GPCR Library 涵盖了广泛的 GPCR 靶标&#xff0c;库中化合物具有新颖性和高度多样性的特…

C++不知算法系列之集结常规算法思想

1. 前言 数据结构和算法是程序的 2 大基础结构&#xff0c;如果说数据是程序的汽油&#xff0c;算法则就是程序的发动机。 什么是数据结构&#xff1f; 指数据之间的逻辑关系以及在计算机中的存储方式&#xff0c;数据的存储方式会影响到获取数据的便利性。 现实生活中&…

JVM和Java体系结构

前言 作为Java工程师的你曾被伤害过吗&#xff1f;你是否也遇到过这些问题&#xff1f; 运行着的线上系统突然卡死&#xff0c;系统无法访问&#xff0c;甚至直接OOMM&#xff01; 想解决线上JVM GC问题&#xff0c;但却无从下手。 新项目上线&#xff0c;对各种JVM参数设置…

【LeetCode】【简单】【4】70. 爬楼梯

题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 动态规划 参考链接&#xff1a;动态规划详解 简单来说&#xff0c;动态规划其实就是&#xff0c;给定一个问题&#xff0c;我们把它拆成一个…

贯叶连翘及其天然产物的介绍

贯叶连翘 (Hyperlcurn perforatum L.) 为藤黄科 (Guttiferae) 植物&#xff0c;又名贯叶金丝桃&#xff0c;赶山鞭&#xff0c;小对叶草、千层楼等。迄今为止&#xff0c;已有众多的活性化合物从贯叶连翘中被分离出来。贯叶连翘中不同的成分具有多重药理活性&#xff0c;如其中…

数据结构题目收录(十八)

1、若某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素&#xff0c;则采用____存储方式最节省运算时间。 A&#xff1a;单链表B&#xff1a;仅有头指针的单循环链表C&#xff1a;双链表D&#xff1a;仅有尾指针的单循环链表 解析 选项A、单链表插入…

Java_Map

字典(Map) 概述 HashMap 定义方法 public static void main(String[] args) {// HashMap<Integer, String> Map new HashMap<Integer, String>(); // 定义方法一Map<Integer, String> map new HashMap<Integer, String>(); // 定义方法二// map没…

(续)SSM整合之springmvc笔记(域对象共享数据)(P136-138)

目录 一 使用ServletAPI向request域对象共享数据 二 使用ModelAndView向request域对象共享数据 1 新建TestScopeController 2 index.html 3 书写TestScopeController 4 success.html 5 测试 三 使用Model向request域对象共享数据 1 index.html 2 TestSc…

HTNL---表格标签和列表标签

一、表格标签 对于表格我想就不用做很多细说了吧&#xff0c;Excel大家应该都知道。HTML中的表格不是用来布局页面的而是用来展示数据的。 1&#xff1a;表格标签基本使用 <table> <tr><th>姓名</th> <th>年龄</th> <td>第一</…

【Note5】macvlan,spi,rsyslog,sol

文章目录1.BMC虚拟多网口&#xff1a;macvlan是kernel提供的一种网卡虚拟化技术&#xff0c;可将网卡&#xff08;不一定是真实的物理网卡&#xff09;虚拟出多个接口&#xff0c;这网卡称为master或父接口&#xff0c;这些虚拟接口和外面环境通信都是通过父接口1.1 bridge&…