当前位置: 首页 > news >正文

从零实现深度学习框架——Transformer从菜鸟到高手(二)

引言

💡本文为🔗[从零实现深度学习框架]系列文章内部限免文章,更多限免文章见 🔗专栏目录。

本着“凡我不能创造的,我就不能理解”的思想,系列文章会基于纯Python和NumPy从零创建自己的类PyTorch深度学习框架。

上篇文章中我们介绍了多头注意力,本文我们来了解Transformer Encoder模块剩下的组件,即残差连接、层归一化和前馈网络层。

Transformer架构

image-20230731105136951

图1. Transformer架构图

它也是一个encoder-decoder架构,左边是encoder,右边是decoder。我们先来看下它们内部的构件(从下到上)。

  • Encoder
    • Input Embedding:输入嵌入层
    • Positional Encoding:位置编码
    • Encoder Transformer Block:由于Encoder和Decoder的Block不同,这里区分来展开。
      • Multi-Head Attention:多头注意力
      • Add: 残差连接
      • (Layer) Norm:层归一化
      • (Position-wise) Feed Forward:位置级前馈网络
      • 上面是一个Block包含的内容,由于设计成了输入和输出的维度一致,因此可以堆叠N个。
  • Decoder
    • Output Embedding:输出嵌入层
    • Positional Encoding:位置编码
    • Decoder Transformer Block
      • Masked Multi-Head Attention:掩码多头注意力
      • Add: 残差连接
      • (Layer) Norm:层归一化
      • Multi-Head Attention:多头注意力
      • (Position-wise) Feed Forward:位置级前馈网络
      • 上面是一个Block包含的内容,由于设计成了输入和输出的维度一致,因此可以堆叠N个。
    • Linear:线性映射层
    • Softmax:输出概率

再回顾一下Transformer架构,已经实现的用红色标出。

残差连接

残差连接(residual connection,skip residual,也称为残差块)其实很简单,如下图所示:

image-20230821165356635

图2. 残差连接示意图

x \pmb x x为网络层的输入,该网络层包含非线性激活函数,记为 F ( x ) F(\pmb x) F(x),用公式描述的话就是:
y = x + F ( x ) (1) \pmb y = \pmb x + F(\pmb x) \tag 1 y=x+F(x)(1)
y \pmb y y是该网络层的输出,它作为第二个网络层的输入。有点像LSTM中的门控思想,输入 x \pmb x x没有被遗忘。

一般网络层数越深,模型的表达能力越强,性能也就越好。但随着网络的加深,也带来了很多问题,比如梯度消失、梯度爆炸。

image-20230821170459985

图3. ResNet-56,有无残差连接损失平面的区别,来自论文Visualizing the Loss Landscape of Neural Nets

可以看出来,增加了残差连接后,损失平面更加平滑,没有那么多局部极小值。直观地看,有了残差连接了, x \pmb x x的信息可以直接传递到下一层,哪怕中间 F ( x ) F(\pmb x) F(x)是一个非常深的网络,只要它能学到将自己的梯度设成很小,不影响 x \pmb x x梯度的传递即可。

还有一些研究(Residual networks behave like ensembles of relatively shallow networks)表明,深层的残差网络可以看成是不同浅层网络的集成。

残差连接实现起来非常简单,就是公式 ( 1 ) (1) (1)的代码化:

x = x + layer(x)

层归一化

层归一化想要解决一个问题,这个问题在Batch Normalization的论文中有详细的描述(参考小节中有论文笔记,建议阅读原论文),即深层网络中内部结点在训练过程中分布的变化(Internal Covariate Shift,ICS,内部协变量偏移)问题。

如果神经网络的输入都保持同一分布,比如高斯分布,那么网络的收敛速度会快得多。但如果不做处理的话,这很难实现。由于低层参数的变化(梯度更新),会导致每层输入的分布也会在训练期间变化。

考虑有sigmoid激活函数 z = g ( W u + b ) z=g(Wu+b) z=g(Wu+b)的网络层,其中 u u u是该层的输入; W W W b b b是可学习的参数,且 g ( x ) = 1 1 + exp ⁡ ( − x ) g(x) = \frac{1}{1 +\exp(-x)} g(x)=1+exp(x)1。随着 ∣ x ∣ |x| x增加, g ′ ( x ) g^\prime (x) g(x)趋向于 0 0 0。这意味着对于 x = W u + b x = Wu+b x=Wu+b 中除了绝对值较小的维度之外的所有维度,流向 u u u的梯度将消失,导致模型训练缓慢。然而,因为 x x x也被 W , b W,b W,b和所有后续层的参数影响,在训练期间改变这些参数值也可能将 x x x的很多维度移动到非线性上的饱和区域(见下图红线位置),减缓收敛速度。这种影响还会随着网络层数的加深而增强。实际中,该饱和和梯度消失问题通常通过使用ReLU激活单元来解决,并且需要小心地初始化,以及小的学习率,这也会导致训练过慢。

image-20230821172911227

图4. Sigmoid导函数图像

批归一化首先被提出来通过在深度神经网络中包含额外的归一化阶段来减少训练时间。批归一化通过使用训练数据中每个批次输入的均值和标准差来归一化每个输入。它需要计算累加输入统计量的移动平均值。在具有固定深度的网络中,可以简单地为每个隐藏层单独存储这些统计数据。针对的是同一个批次内所有数据的同一个特征。

然而批归一化并不适用于处理NLP任务的RNN(Transformer)中,循环神经元的累加输入通常会随着序列的长度而变化,而且循环神经元的需要计算的次数是不固定的(与序列长度有关)。

通常在NLP中一个批次内的序列长度各有不同,所以需要进行填充,存在很多填充token。如果使用批归一化,则容易受到长短不一中填充token的影响,造成训练不稳定。而且需要为序列中每个时间步计算和存储单独的统计量,如果测试序列不任何训练序列都要长,那么这也会是一个问题。

而层归一化针对的是批次内的单个序列样本,通过计算单个训练样本中一层的所有神经元(特征)的输入的均值和方差来归一化。没有对批量大小的限制,因此也可以应用到批大小为 1 1 1的在线学习。

批归一化是不同训练数据之间对单个隐藏单元(神经元,特征)的归一化,层归一化是单个训练数据对同一层所有隐藏单元(特征)之间的归一化。对比见下图:

img

图5. 层归一化和批归一化的对比,来自参考文章How does Layer Normalization work?

如上图右所示,批归一化针对批次内的所有数据的单个特征(Feature);层归一化针对批次内的单个样本的所有特征,它们都包含所有时间步。

说了这么多,那么具体是如何计算层归一化的呢?
y = x − E [ x ] Var [ x ] + ϵ ⋅ γ + β (2) \pmb y = \frac{\pmb x -E[\pmb x]}{\sqrt{\text{Var}[\pmb x] + \epsilon}} \cdot \pmb\gamma + \pmb\beta \tag 2 y=Var[x]+ϵ xE[x]γ+β(2)
x \pmb x x是归一化层的输入; y \pmb y y是归一化层的输出(归一化的结果);

γ \pmb \gamma γ β \pmb \beta β是为归一化层每个神经元(特征)分配的一个自适应的缩放和平移参数。这些参数和原始模型一起学习,可以恢复网络的表示。通过设置 γ ( k ) = Var [ x ( k ) ] \gamma^{(k)} = \sqrt{\text{Var}[\pmb x^{(k)}]} γ(k)=Var[x(k)] β ( k ) = E [ x ( k ) ] \beta^{(k)}=E[\pmb x^{(k)}] β(k)=E[x(k)],可以会输入恢复成原来的激活值,如果模型认为有必要的话;

ϵ \epsilon ϵ是一个很小的值,防止除零。

class LayerNorm(Module):def __init__(self, features: int, eps: float = 1e-6):"""Args:features: 特征个数eps:"""super().__init__()self.gamma = Parameter(Tensor.ones(features))self.beta = Parameter(Tensor.zeros(features))self.eps = epsdef forward(self, x: Tensor) -> Tensor:"""Args:x: (batch_size, input_len, emb_size)Returns:"""mean = x.mean(-1, keepdims=True)std = x.std(-1, keepdims=True)return self.gamma * (x - mean) / (std + self.eps) + self.beta

FFN

Position-wise Feed Forward(FFN),逐位置的前馈网络,其实就是一个全连接前馈网络。

它一个简单的两层全连接神经网络,不是将整个嵌入序列处理成单个向量,而是独立地处理每个位置的嵌入。所以称为position-wise前馈网络层。也可以看为核大小为1的一维卷积。

目的是把输入投影到特定的空间,再投影回输入维度。

class PositionWiseFeedForward(nn.Module):'''实现FFN网路'''def __init__(self, d_model, d_ff, dropout=0.1):"""Args:d_model: 模型大小d_ff: FF层的大小,2dropout:"""super().__init__()# 将输入转换为d_ff维度self.linear1 = nn.Linear(d_model, d_ff)# 将d_ff转换回d_modelself.linear2 = nn.Linear(d_ff, d_model)self.dropout = nn.Dropout(dropout)def forward(self, x):# 经过三次变换: 线性->非线性->线性return self.dropout(self.linear2(F.relu(self.linear1(x))))

我们继续之前的例子:

import numpy as npembed_dim = 512
vocab_size = 5000num_heads = 8
# 第二个样本包含两个填充
input_data = Tensor(np.array([[1, 2, 3, 4, 5], [6, 7, 8, 0, 0]])
)
# batch_size = 2
# seq_len = 5
batch_size, seq_len = input_data.shapeembedding = nn.Embedding(vocab_size, embed_dim)
# 模拟嵌入层
input_embeds = embedding(input_data)mask = generate_mask(input_data)attn = MultiHeadAttention(embed_dim=embed_dim, num_heads=num_heads)ffn = PositionWiseFeedForward(embed_dim, d_ff=2048)# 编码器中,query、key、value来自同一个嵌入
values = attn(input_embeds, input_embeds, input_embeds, mask)
print(values.shape)
output = ffn(values)
print(values.shape)
(2, 5, 512)
(2, 5, 512)

可以看到FFN并没有改变输入的维度,下面通过全1输入证明它对每个位置的作用是一样的:

input_data = Tensor.ones(2, 5)
ffn = PositionWiseFeedForward(5, d_ff=2048)
ffn.eval() # 抑制dropout
print(ffn(input_data))
Tensor(
[[-2.9619  1.7855  2.1681 -1.176  -0.2168][-2.9619  1.7855  2.1681 -1.176  -0.2168]], requires_grad=True)

可以看到,由于这两个输入是完全一样的,它们经过这个FFN得到的结果也是一样的,这里要取消dropout,否则可能(被dropout后)不一样了。可以理解为这种线性变换同等地应用到输入的每个位置上。

Post-LN v.s. Pre-LN

在Transformer中应用层归一化时,通常有两种选择。即Post-Layer Normalization(Post-LN)和Pre-Layer Normalization(Pre-LN)。

所谓Post-LN,即原始的Transformer将层归一化放置在残差连接之间,这被称为是Post-Layer Normalization(Post-LN)的做法。

image-20230822162159047

图6. 两种LN的对比

见上图中的Post-LN,可以看到层归一化在两个残差连接(块)中间;而Pre-LN的设计下层归一化在残差连接内部。

Post-LN也是Transformer的默认做法。但这种方式很从零开始训练,把层归一化放到残差块之间,接近输出层的参数的梯度往往较大。然后在那些梯度上使用较大的学习率会使得训练不稳定。通常需要用到学习率预热(warm-up)技巧,在训练开始时学习率需要设成一个极小的值,然后在一些迭代后逐步增加,最后再持续衰减。同时还需要小心地初始化模型的参数。

将层归一化放到残差连接的范围内,即Pre-LN。这在训练期间往往更加稳定,收敛更快,并且通常不需要任何学习率预热。在论文ON LAYER NORMALIZATION IN THE TRANSFORMER ARCHITECTURE中有详细的介绍,也可以看后面的参考文章,论文作者说使用Pre-LN的方式可以安全地移除学习预热阶段。其实如果仔细看的话,输入流有可能直接流向最后的输出,残差连接内的网络层相当于不存在了,对于梯度也是一样。

但Pre-LN真的是又快又好吗?最近人们认为,Pre-LN虽然更容易训练,但最终效果往往不如Post-LN好。

我们来看Pre-LN对应的公式:
x t + 1 = x t + F ( Norm ( x t ) ) (3) \pmb x_{t+1} = \pmb x_t +F(\text{Norm}(\pmb x_t)) \tag 3 xt+1=xt+F(Norm(xt))(3)
这里 Norm \text{Norm} Norm代表层归一化; F F F表示另一个网络层,比如多头注意力或FFN,括号内的参数表示作为输入。

对它从 t + 2 t+2 t+2处展开有:
x t + 2 = x t + 1 + F ( Norm ( x t + 1 ) ) = x t + F ( Norm ( x t ) ) + F ( Norm ( x t + 1 ) ) \begin{aligned} \pmb x_{t+2} &= \pmb x_{t+1} +F(\text{Norm}(\pmb x_{t+1})) \\ &= \pmb x_t + F(\text{Norm}(\pmb x_{t})) +F(\text{Norm}(\pmb x_{t+1})) \end{aligned} xt+2=xt+1+F(Norm(xt+1))=xt+F(Norm(xt))+F(Norm(xt+1))
实际上是增加了网络的"宽度",网络的深度并没有增加。因为当 t t t较大时, x t + 1 \pmb x_{t+1} xt+1 x t \pmb x_t xt的差别是很小的。

怎么理解网络的宽度呢?下图就可以很好地理解:

image-20230822170910021

图6 网络的宽度和深度

这样我们了解了实现Encoder Transformer Block的所有细节。实现起来就不难了,我们这里支持Post-LN和Pre-LN二选一。

class TransformerEncoderLayer(nn.Module):def __init__(self, d_model: int, num_heads: int, dim_feedforward: int = 2048, dropout: float = 0.1,norm_first: bool = False):"""Args:d_model: 输入的特征个数num_heads: 多头个数dim_feedforward: FFN中的扩张的维度大小,通常会比d_model要大dropout:norm_first: 为True记为Pre-LN;默认为False对应的Post-LN。"""super().__init__()self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model)self.attn = MultiHeadAttention(d_model, num_heads, dropout)self.feed_forward = PositionWiseFeedForward(d_model, dim_feedforward, dropout)self.dropout1 = nn.Dropout(dropout)self.dropout2 = nn.Dropout(dropout)self.norm_first = norm_firstdef forward(self, src: Tensor, src_mask: Tensor = None) -> Tensor:x = srcif self.norm_first:# 层归一化x = self.norm1(x)# 多头注意力 -> 残差连接x = x + self.dropout1(self.attn(x, x, x, src_mask))# 层归一化 -> FFN -> 残差连接x = x + self.dropout2(self.feed_forward(self.norm2(x)))else:# 多头注意力 -> 残差连接 -> 层归一化x = self.norm1(x + self.dropout1(self.attn(x, x, x, src_mask)))x = self.norm2(x + self.dropout2(self.feed_forward(x)))return x

我们测试一下前向传播:

import numpy as npembed_dim = 512
vocab_size = 5000num_heads = 8
# 第二个样本包含两个填充
input_data = Tensor(np.array([[1, 2, 3, 4, 5], [6, 7, 8, 0, 0]])
)
# batch_size = 2
# seq_len = 5
batch_size, seq_len = input_data.shapeembedding = nn.Embedding(vocab_size, embed_dim)
# 模拟嵌入层
input_embeds = embedding(input_data)mask = generate_mask(input_data)encoder_layer = TransformerEncoderLayer(embed_dim, num_heads,dim_feedforward=2048)
output = encoder_layer(input_embeds, mask)
print(output.shape)
(2, 5, 512)

参考

  1. [论文翻译]Attention Is All You Need
  2. The Annotated Transformer
  3. Speech and Language Processing
  4. The Illustrated Transformer
  5. [论文笔记]Batch Normalization
  6. [论文笔记]Layer Normalization
  7. [论文笔记]ON LAYER NORMALIZATION IN THE TRANSFORMER ARCHITECTURE
  8. How does Layer Normalization work?

相关文章:

从零实现深度学习框架——Transformer从菜鸟到高手(二)

引言 💡本文为🔗[从零实现深度学习框架]系列文章内部限免文章,更多限免文章见 🔗专栏目录。 本着“凡我不能创造的,我就不能理解”的思想,系列文章会基于纯Python和NumPy从零创建自己的类PyTorch深度学习框…...

docker监控平台FAST OS DOCKER --1

感觉这个是目前好用的中文平台,暂为v1吧 拉取镜像 docker pull wangbinxingkong/fast运行镜像 docker run --name fastos --restart always -p 18091:8081 -p 18092:8082 -e TZ"Asia/Shanghai" -d -v /var/run/docker.sock:/var/run/docker.sock -v /e…...

SpringBoot2.0集成WebSocket

<!-- websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> 新建配置类 import org.springframework.boot.autoconfigure.condition.Cond…...

Vue的Ajax请求-axios、前后端分离练习

Vue的Ajax请求 axios简介 ​ Axios&#xff0c;是Web数据交互方式&#xff0c;是一个基于promise [5]的网络请求库&#xff0c;作用于node.js和浏览器中&#xff0c;它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.js http模块, 而在…...

Spring源码深度解析三 (MVC)

书接上回 10.MVC 流程&源码剖析 * 问题1&#xff1a;Spring和SpringMVC整合使用时&#xff0c;会创建一个容器还是两个容器&#xff08;父子容器&#xff1f;&#xff09; * 问题2&#xff1a;DispatcherServlet初始化过程中做了什么&#xff1f; * 问题3&#xff1a;请求…...

API接口漏洞利用及防御

API是不同软件系统之间进行数据交互和通信的一种方式。API接口漏洞指的是在API的设计、开发或实现过程中存在的安全漏洞&#xff0c;可能导致恶意攻击者利用这些漏洞来获取未授权的访问、篡改数据、拒绝服务等恶意行为。 1.API接口漏洞简介 API&#xff08;Application Progr…...

解决Spring mvc + JDK17@Resource无法使用的情况

问题描述 我在使用jdk17进行Spring mvc开发时发现 Resource用不了了。 原因 因为JDK版本升级的改动&#xff0c;在Jdk9~17环境下&#xff0c;搭建Springboot项目&#xff0c;会出现原有Resource&#xff08;javax.annotation.Resource&#xff09;不存在的问题&#xff0c;导…...

页面禁用鼠标右键,禁用F12打开开发者工具!!!

文章目录 问题分析方法一方法二方法二问题 今天在浏览博主文章时发现无法复制页面上的内容,也无法F12打开开发者工具,更用不了鼠标右键,于是上网找了原因并亲测可用 分析 方法一 将 <body> 改成 <body oncontextmenu=self.event.returnValue=false>方法二 …...

Android中使用JT808协议进行车载终端通信的实现和优化

JT808是一种在中国广泛应用的车载终端通信协议&#xff0c;用于车辆与监控中心之间的数据通信。下面是关于Android平台上使用JT808协议进行通信的一般步骤和注意事项&#xff1a; 协议了解&#xff1a;首先&#xff0c;您需要详细了解JT808协议的规范和定义。该协议包含了通信消…...

导出pdf

该方法导出的pdf大小是A4纸的尺寸&#xff0c;如果大于1页需要根据元素高度进行截断的话&#xff0c;页面元素需要加 class ergodic-dom&#xff0c;方法里面会获取ergodic-dom元素&#xff0c;对元素高度和A4高度做比较&#xff0c;如果大于A4高度&#xff0c;会塞一个空白元素…...

【考研数学】线形代数第三章——向量 | 基本概念、向量组的相关性与线性表示

文章目录 引言一、向量的概念与运算1.1 基本概念1.2 向量运算的性质 二、向量组的相关性与线性表示2.1 理论背景2.2 相关性与线性表示基本概念2.3 向量组相关性与线性表示的性质 引言 向量是线性代数的重点和难点。向量是矩阵&#xff0c;同时矩阵又是由向量构成的&#xff0c…...

温故知新之:接口和抽象类有什么区别?

本文以下内容基于 JDK 8 版本。 1、接口介绍 接口是 Java 语言中的一个抽象类型&#xff0c;用于定义对象的公共行为。它的创建关键字是 interface&#xff0c;在接口的实现中可以定义方法和常量&#xff0c;其普通方法是不能有具体的代码实现的&#xff0c;而在 JDK 8 之后&…...

回归预测 | MATLAB实现SSA-RF麻雀搜索优化算法优化随机森林算法多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SSA-RF麻雀搜索优化算法优化随机森林算法多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SSA-RF麻雀搜索优化算法优化随机森林算法多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;…...

文旅景区vr体验馆游乐场vr项目是什么

我们知道现在很多的景区或者游玩的地方&#xff0c;以及学校、科技馆、科普馆、商场或公园或街镇&#xff0c;都会建一些关于游玩以及科普学习的项目。从而增加学习氛围或者带动人流量等等。这样的形式&#xff0c;还是有很好的效果呈现。 普乐蛙VR体验馆案例 下面是普乐蛙做的…...

Vue Element upload组件和Iview upload 组件上传文件

今天要分享的是使用这俩个UI组件库的upload组件分别实现调用组件本身的上传方法实现和后台交互。接下来就是开车的时间&#xff0c;请坐稳扶好~ 一、element upload组件传送门 1、html文件 <el-upload ref"uploadRef" :action"uploadUrl" :data"…...

无涯教程-PHP - File 函数

文件系统功能用于访问和操纵文件系统&#xff0c;PHP为您提供了操纵文件的所有功能。 运行时配置 这些功能的行为受php.ini中的设置影响。 NameDefaultChangeableChangelogallow_url_fopen"1"PHP_INI_ALLPHP_INI_ALL in PHP < 4.3.4. PHP_INI_SYSTEM in PHP &l…...

elasticsearch 常用查询 7.4 版本

Elasticsearch 常用查询 match&#xff1a;全文查询exists&#xff1a;查询存在的字段must_not&#xff1a;查询不存在的字段ids&#xff1a;跟据id查询prefix&#xff1a;前缀查询range: 查询范围term&#xff1a;精准查询terms&#xff1a;多术语查询 本文基于es 7.4版本文档…...

ChatGpt 从入门到精通

相关资源下载地址: 基于ChatGPT的国际中文语法教学辅助应用的探讨.pdf 生成式人工智能技术对教育领域的影响-关于ChatGPT的专访.pdf 电子-从ChatGPT热议看大模型潜力.pdf 从图灵测试到ChatGPT——人机对话的里程碑及启示.pdf 正文 ChatGPT 是一种强大的自然语言处理模型&…...

vscode远程调试

安装ssh 在vscode扩展插件搜索remote-ssh安装 如果连接失败&#xff0c;出现 Resolver error: Error: XHR failedscode 报错&#xff0c;可以看这篇帖子vscode ssh: Resolver error: Error: XHR failedscode错误_阿伟跑呀的博客-CSDN博客 添加好后点击左上角的加号&#xff0…...

Vue3 数据响应式原理

核心&#xff1a; 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等… 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作 const userData {name: "John",age: 12 };let proxyUser new Proxy(use…...

2023.08.20 学习周报

文章目录 摘要文献阅读1.题目2.现有问题3.解决方案4.本文贡献5.方法5.1 利用长短期记忆网络学习时空演化特征5.2 构建用于气象辅助信息编码的堆叠自编码器5.3 使用多任务学习发现全市通用模式5.4 模型 6. 实验6.1 数据集6.2 实验设置6.3 实验结果 7.结论8.展望 大气污染物传输总…...

软件测试技术之单元测试—工程师 Style 的测试方法(2)

怎么写单元测试&#xff1f; JUnit 简介 基本上每种语言和框架都有不错的单元测试框架和工具&#xff0c;例如 Java 的 JUnit、Scala 的 ScalaTest、Python的 unittest、JavaScript 的 Jest 等。上面的例子都是基于 JUnit 的&#xff0c;我们下面就简单介绍下 JUnit。 JUnit…...

项目中超图 for openlayer和超图for cesium同时引入的问题

一个项目中同时用到了超图的openlayer和cesium版本&#xff0c;首先我是外部引入的超图的开发包&#xff0c;你要是通过npm导入的那就没关系了。 <script type"text/javascript" src"/static/openlayer/supermap/ol/iclient-ol.min.js"></script&…...

3D与沉浸式技术,如何助力企业数字化转型?

说起3D&#xff0c;估计许多读者朋友会在第一时间想起《阿凡达》系列和《侏罗纪公园》系列电影大作。每一帧细节纤毫毕现的逼真画面&#xff0c;让观众几乎分不清虚拟与现实&#xff0c;完全沉浸在导演打造的视觉盛宴中。 事实上&#xff0c;除了大家所熟知的3D影视动画之外&am…...

excel vba 将多张数据表的内容合并到一张数据表

功能描述&#xff1a; 一个Excel文件有很多个 样式相同 的数据表&#xff0c; 需要将多张数据表的内容合并到一张数据表里。 vba实现代码如下&#xff1a; Attribute VB_Name "NewMacros" Option Explicit Public Const Const_OutSheetName As String "V…...

接口和抽象类的区别?解析接口和抽象类的特点和用法

接口和抽象类的区别&#xff1f;解析接口和抽象类的特点和用法 引言 在面向对象编程中&#xff0c;接口和抽象类是两个非常重要的概念。它们都可以用于定义一组相关的方法&#xff0c;但在实际使用中有一些差异。本文将探讨接口和抽象类的区别&#xff0c;并通过示例代码和测…...

vscode-vue项目格式化

一、插件要求 Prettier Vetur 二、配置文件 {"workbench.startupEditor": "newUntitledFile","files.autoSave": "off", // 关闭文件自动保存&#xff0c;避免开发时候页面变化"editor.tabSize": 2, // tab距离"ve…...

SAP MM学习笔记26- SAP中 振替转记(转移过账)和 在库转送(库存转储)1- 移动Type间振替转记

SAP 中在库移动 不仅有入库&#xff08;GR&#xff09;&#xff0c;出库&#xff08;GI&#xff09;&#xff0c;也可以是单纯内部的转记或转送。 1&#xff0c;振替转记&#xff08;转移过账&#xff09; 2&#xff0c;在库转送&#xff08;库存转储&#xff09; 1&#xff…...

SAP SPL(Special Ledger)之注释行项目-Noted Items

财务凭证过账里常见的SPL特殊总账标识根据业务主要有三种&#xff0c;BoE-billing of exchange: 汇票业务&#xff0c;包括商业汇票和银行汇票&#xff1b;Down Payment&#xff0c;预付款业务&#xff0c;包括供应商和客户预付款和申请&#xff1b;其它&#xff0c;一般是保证…...

学习平台助力职场发展与提升

近年来&#xff0c;随着互联网技术的发展&#xff0c;学习平台逐渐成为了职场发展和提升的必备工具。学习平台通过提供丰富的课程内容、灵活的学习时间和个性化的学习路径&#xff0c;帮助职场人士更好地提升自己的技能和知识储备&#xff0c;为职场发展打下坚实的基础。 学习…...

有没有免费格式转换工具推荐?PDF转化为PPT的方法

在当今职场生活中&#xff0c;掌握文件格式转换技能变得异常重要。将PDF文档转换为PPT格式可以在演讲、报告等场合更好地展示和传达信息&#xff0c;为我们的专业形象增添亮点&#xff0c;接下来我们可以一起来看一下“有没有免费格式转换工具推荐?PDF转化为PPT的方法”相关的…...

【LeetCode-经典面试150题-day12】

20.有效的括号 题意&#xff1a; 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括…...

TCP机制-延迟应答,捎带应答

在看本篇博客前推荐先看TCP中窗口和滑动窗口的含义以及流量控制 延迟应答和捎带应答都是TCP用于提高网络传输效率的机制 延迟应答 当发送端发送数据给接收端了以后&#xff0c;按道理接收端的内核会立即返回ACK&#xff08;应答报文&#xff09;给发送端&#xff0c;而且ACK&a…...

【Redis从头学-8】Redis中的ZSet数据类型实战场景之用户积分榜

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Re…...

Springboot内嵌SQLite配置使用

版本号 MacOS Apple M1 | Jdk17 | Maven 3.8.5 | SpringBoot 2.6.9 | SQLite 3.42.0.0 pom.xml <dependencies><dependency><groupId>org.xerial</groupId><artifactId>sqlite-jdbc</artifactId><version>3.42.0.0</version&g…...

【微服务学习笔记】认识微服务

【微服务学习笔记】认识微服务 单体架构 分布式架构 微服务架构 SpringCloud 服务拆分和注意事项 服务拆分的案例demo 各个服务之间的数据库都是相互独立的&#xff0c;你不能直接访问对方的数据库&#xff0c;只能从一个服务像另外一个服务发起远程调用 在订单模块的服务中 …...

基于Android R快速编译recovery-ramdisk.img

Android默认没有单编recovery-ramdisk.img的命令&#xff0c;我们可以自己修改Makefile实现 修改&#xff1a;build/core/Makefile 添加&#xff1a; .PHONY: recovery-ramdisk-nodeps recovery-ramdisk-nodeps: $(MKBOOTFS) | $(COMPRESSION_COMMAND_DEPS)echo "make …...

Redis分布式缓存

分布式缓存 -- 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题&#xff1a; 1.Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化 AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#x…...

最大公约数和最小公倍数

最大公约数&#xff1a; 概念&#xff1a; 公约数中最大的称为最大公约数。 对任意的若干个正整数&#xff0c;1总是它们的公因数。 公约数与公倍数相反&#xff0c;就是既是A的约数同时也是B的约数的数&#xff0c;12和15的公约数有1&#xff0c;3&#xff0c;最大公约数就是…...

数据结构——二叉搜索树(附带C++实现版本)

文章目录 二叉搜索树概念 二叉树的实际应用二叉树模拟实现存储结构二叉搜索树构成二叉搜索树的查找插入操作中序遍历二叉树的删除循环(利用左子树最右节点&#xff09;递归(利用右子树根节点) 二叉树拷贝二叉树资源的销毁 二叉树实现完整代码总结 二叉搜索树 概念 二叉搜索树…...

C++(3)C++对C的扩展Extension

类型增强 1、类型更加严格 不初始化&#xff0c;无法通过编译&#xff1b;C不初始化&#xff0c;则随机赋值 #include <iostream> #include <stdlib.h>int main() {const int a 100; //真正的const,无法修改 // int *p &a; 报错const int *p…...

在vscode(idea)使用GitHub账号、Copilot异常

在idea使用GitHub账号、Copilot异常 登录GitHub显示 Invalid authentication data.Connection refused: connect或者副驾驶显示 Failed to initiate the GitHub login process. Please try again.一般网上的方法推荐使用token登录&#xff0c;或者降级副驾驶 经过研究&#x…...

新的后端渲染:服务器驱动UI

通过API发送UI是一种彻底的新方法&#xff0c;将改变传统的UI开发。 一项正在改变我们对用户界面 (UI) 的看法的技术是通过 API 发送 UI&#xff0c;也称为服务器驱动UI。这种方法提供了新水平的活力和灵活性&#xff0c;正在改变 UI 开发的传统范例。 服务器驱动 UI 不仅仅是…...

Postman如何做接口自动化测试?

前言 什么是自动化测试 把人对软件的测试行为转化为由机器执行测试行为的一种实践。 例如GUI自动化测试&#xff0c;模拟人去操作软件界面&#xff0c;把人从简单重复的劳动中解放出来。 本质是用代码去测试另一段代码&#xff0c;属于一种软件开发工作&#xff0c;已经开发完…...

excel文本函数篇2

本期主要介绍LEN、FIND、SEARCH以及后面加B的情况&#xff1a; &#xff08;1&#xff09;后缀没有B&#xff1a;一个字节代表一个中文字符 &#xff08;2&#xff09;后缀有B&#xff1a;两个字节代表一个中文字符 1、LEN(text)&#xff1a;返回文本字符串中的字符个数 2、…...

【MyBatis】动态SQL > 重点:${...}和#{...}与resultMap和resultType的区别

目录 一、MyBatis动态sql 1.1 动态sql的作用 1.2 动态sql作用论证 1.2.1 条件判断&#xff1a;<if> 1.2.2 循环迭代&#xff1a;<foreach> 1.2.3 SQL片段重用 1.2.4 动态条件组合&#xff1a;<choose><when><otherwise> 1.2.5 <where…...

什么是BEM命名规范?为什么要使用BEM命名规范?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ BEM命名规范⭐ 为什么使用BEM命名规范&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为…...

JavaScript:交集和差集的应用场景

在集合A和集合B中&#xff0c;属于集合A&#xff0c;同时也属于集合B的元素组成的集合&#xff0c;就是交集。 在A中所有不属于集合B元素&#xff0c;组合成集合&#xff0c;就是差集。 那么在平时的开发中&#xff0c;如何使用差集和交集来解决问题呢&#xff1f; 现在有这…...

达梦数据库表空间创建和管理

概述 本文将介绍在达梦数据库如何创建和管理表空间。 1.创建表空间 1.1表空间个数限制 理论上最多允许有65535个表空间&#xff0c;但用户允许创建的表空间 ID 取值范围为0~32767&#xff0c; 超过 32767 的只允许系统使用&#xff0c;ID 由系统自动分配&#xff0c;ID不能…...

三、MySQL 数据库安装集

一、CentOS—YUM 1. MySQL—卸载 # 1、查看存在的MySQL。 rpm -qa | grep -i mysql rpm -qa | grep mysql# 2、删除存在的MySQL。 rpm -e –-nodeps 包名# 3、查找存在的MySQL目录。 find / -name mysql# 4、删除存在的MySQL目录。 rm -rf 目录# 5、删除存在的MySQL配置文件。…...