ELECTRA:作为判别器而非生成器的预训练文本编码器
摘要
诸如BERT之类的掩码语言建模(MLM)预训练方法通过将某些标记替换为[MASK]来破坏输入,然后训练模型以重建原始标记。尽管这些方法在下游自然语言处理(NLP)任务中表现良好,但它们通常需要大量的计算资源才能有效。作为替代方案,我们提出了一种更高效的预训练任务,称为替换标记检测。与掩码输入不同,我们的方法通过将某些标记替换为从一个小型生成器网络中采样的合理替代标记来破坏输入。然后,我们不是训练一个预测被破坏标记原始身份的生成模型,而是训练一个判别模型,预测被破坏输入中的每个标记是否被生成器样本替换。大量实验表明,这种新的预训练任务比MLM更高效,因为该任务针对所有输入标记而不仅仅是掩码的一小部分子集。因此,在相同的模型大小、数据和计算资源下,我们的方法学习到的上下文表示显著优于BERT。对于小型模型,这种优势尤为明显;例如,我们在一个GPU上训练了4天的模型,在GLUE自然语言理解基准测试中优于GPT(GPT使用了30倍的计算资源)。我们的方法在大规模场景下也表现良好,在使用不到1/4计算资源的情况下,其性能与RoBERTa和XLNet相当,而在使用相同计算资源时则优于它们。
1 引言
当前最先进的语言表示学习方法可以被视为学习去噪自编码器(Vincent等,2008)。它们从未标记的输入序列中选择一小部分(通常为15%),掩码这些标记的身份(例如BERT;Devlin等(2019))或对这些标记的注意力(例如XLNet;Yang等(2019)),然后训练网络以恢复原始输入。尽管由于学习双向表示,这些掩码语言建模(MLM)方法比传统的语言模型预训练更有效,但它们需要大量的计算成本,因为网络仅从每个示例的15%的标记中学习。
作为替代方案,我们提出了替换标记检测,这是一种预训练任务,模型在其中学习区分真实输入标记与合理但合成的替代标记。与掩码不同,我们的方法通过将某些标记替换为从提议分布中采样的样本来破坏输入,该分布通常是一个小型掩码语言模型的输出。这种破坏过程解决了BERT中的一个不匹配问题(尽管XLNet中没有),即在预训练期间网络看到人工的[MASK]标记,但在下游任务微调时却不会看到。然后,我们将网络预训练为一个判别器,预测每个标记是原始标记还是替换标记。相比之下,MLM将网络训练为一个生成器,预测被破坏标记的原始身份。我们的判别任务的一个关键优势是,模型从所有输入标记中学习,而不仅仅是从掩码的一小部分子集中学习,从而使其计算效率更高。尽管我们的方法让人联想到训练生成对抗网络(GAN)的判别器,但我们的方法并不具有对抗性,因为生成破坏标记的生成器是通过最大似然训练的,这是由于将GAN应用于文本的困难(Caccia等,2018)。
我们将我们的方法称为ELECTRA1,意为“高效学习一个准确分类标记替换的编码器”。与之前的工作一样,我们将其应用于预训练Transformer文本编码器(Vaswani等,2017),这些编码器可以在下游任务上进行微调。通过一系列消融实验,我们表明,从所有输入位置学习使得ELECTRA的训练速度比BERT快得多。我们还表明,ELECTRA在完全训练后在下游任务上实现了更高的准确性。
当前大多数预训练方法需要大量计算资源才能有效,这引发了对其成本和可访问性的担忧。由于更多的计算资源几乎总是会带来更好的下游任务准确性,我们认为预训练方法的一个重要考虑因素应该是计算效率以及绝对的下游性能。从这个角度来看,我们训练了不同规模的ELECTRA模型,并评估了它们的下游性能与计算需求的关系。特别是,我们在GLUE自然语言理解基准测试(Wang等,2019)和SQuAD问答基准测试(Rajpurkar等,2016)上进行了实验。在相同的模型大小、数据和计算资源下,ELECTRA显著优于基于MLM的方法,如BERT和XLNet(见图1)。例如,我们构建了一个ELECTRA-Small模型,可以在1个GPU上训练4天。2 ELECTRA-Small在GLUE上比同等规模的BERT模型高出5分,甚至优于更大的GPT模型(Radford等,2018)。我们的方法在大规模场景下也表现良好,我们训练的ELECTRA-Large模型在性能上与RoBERTa(Liu等,2019)和XLNet(Yang等,2019)相当,尽管参数更少且使用了1/4的计算资源进行训练。进一步训练ELECTRA-Large会得到一个更强大的模型,在GLUE上优于ALBERT(Lan等,2019),并在SQuAD 2.0上创造了新的最先进水平。综上所述,我们的结果表明,区分真实数据与具有挑战性的负样本的判别任务比现有的生成方法在语言表示学习中更具计算效率和参数效率。
2 方法
我们首先描述替换标记检测预训练任务;图2提供了其概览。我们在第3.2节中提出并评估了针对该方法的若干建模改进。
我们的方法训练两个神经网络:生成器 G G G和判别器 D D D。每个网络主要由一个编码器(例如Transformer网络)组成,该编码器将输入标记序列 x = [ x 1 , . . . , x n ] x = [x_1, ..., x_n] x=[x1,...,xn]映射为上下文向量表示序列 h ( x ˙ ) = [ h 1 , . . . , h n ] h(\dot{x}) = [h_1, ..., h_n] h(x˙)=[h1,...,hn]。对于给定位置 t t t(在我们的情况下,仅限 x t = [ x_t = [ xt=[MASK]的位置),生成器通过softmax层输出生成特定标记 x t x_t xt的概率:
p G ( x t ∣ x ) = exp ( e ( x t ) T h G ( x ) t ) / ∑ x ′ exp ( e ( x ′ ) T h G ( x ) t ) p_{G}(x_{t}|\boldsymbol{x})=\exp\left(e(x_{t})^{T}h_{G}(\boldsymbol{x})_{t}\right)/\sum_{x^{\prime}}\exp\left(e(x^{\prime})^{T}h_{G}(\boldsymbol{x})_{t}\right) pG(xt∣x)=exp(e(xt)ThG(x)t)/x′∑exp(e(x′)ThG(x)t)
其中 e e e表示标记嵌入。对于给定位置 t t t,判别器通过sigmoid输出层预测标记 x t x_t xt是否为“真实”标记,即它来自数据而不是生成器分布:
D ( x , t ) = s i g m o i d ( w T h D ( x ) t ) D(\boldsymbol{x},t)=\mathrm{sigmoid}(w^Th_D(\boldsymbol{x})_t) D(x,t)=sigmoid(wThD(x)t)
生成器被训练用于执行掩码语言建模(MLM)。给定输入 x = [ x 1 , x ~ 2 , . . . , x n ] x = [x_1, \tilde{x}_2, ..., x_n] x=[x1,x~2,...,xn],MLM首先随机选择一组位置(1到 n n n之间的整数)进行掩码 m = [ m 1 , . . . , m k ] \boldsymbol{m} = [m_1, ..., m_k] m=[m1,...,mk]。所选位置的标记被替换为[MASK]标记,我们将其表示为 x masked = REPLACE ( x , m x^\text{masked} = \text{REPLACE}(\boldsymbol{x}, \boldsymbol{m} xmasked=REPLACE(x,m, [MASK])。然后,生成器学习预测被掩码标记的原始身份。判别器被训练用于区分数据中的标记与由生成器样本替换的标记。更具体地说,我们通过用生成器样本替换被掩码的标记来创建损坏的示例 x c o r r u p t x^\mathrm{corrupt} xcorrupt,并训练判别器预测 x corrupt x^\text{corrupt} xcorrupt中哪些标记与原始输入 x x x匹配。形式上,模型输入根据以下方式构建:
m i ∼ u n i f { 1 , n } f o r i = 1 t o k x m a s k e d = R E P L A C E ( x , m , [ M A S ) x ^ i ∼ p G ( x i ∣ x m a s k e d ) f o r i ∈ m x c o r r u p t = R E P L A C E ( x , m , x ^ ) m_{i}\sim\mathrm{unif}\{1,n\}\mathrm{~for~}i=1\mathrm{~to~}kx^{\mathrm{masked}}=\mathrm{REPLACE}(x,m,\:[\mathrm{~MAS})\\\hat{x}_{i}\sim p_{G}(x_{i}|x^{\mathrm{masked}})\mathrm{~for~}i\in mx^{\mathrm{corrupt}}=\mathrm{REPLACE}(x,m,\hat{x}) mi∼unif{1,n} for i=1 to kxmasked=REPLACE(x,m,[ MAS)x^i∼pG(xi∣xmasked) for i∈mxcorrupt=REPLACE(x,m,x^)
损失函数为:
L M L M ( x , θ G ) = E ( ∑ i ∈ m − log p G ( x i ∣ x m a s k e d ) ) L D i s c ( x , θ D ) = E ( ∑ t = 1 n − 1 ( x t c o r n p t = x t ) log D ( x c o r r u p t , t ) − 1 ( x t c o r r u p t ≠ x t ) log ( 1 − D ( x c o r r u p t , t ) ) ) \begin{aligned}&\mathcal{L}_{\mathrm{MLM}}(x,\theta_{G})=\mathbb{E}\left(\sum_{i\in m}-\log p_{G}(x_{i}|x^{\mathrm{masked}})\right)\\&\mathcal{L}_{\mathrm{Disc}}(x,\theta_{D})=\mathbb{E}\left(\sum_{t=1}^{n}-1(x_{t}^{\mathrm{cornpt}}=x_{t})\log D(x^{\mathrm{corrupt}},t)-1(x_{t}^{\mathrm{corrupt}}\neq x_{t})\log(1-D(x^{\mathrm{corrupt}},t))\right)\end{aligned} LMLM(x,θG)=E(i∈m∑−logpG(xi∣xmasked))LDisc(x,θD)=E(t=1∑n−1(xtcornpt=xt)logD(xcorrupt,t)−1(xtcorrupt=xt)log(1−D(xcorrupt,t)))
尽管与生成对抗网络(GAN)的训练目标相似,但有几个关键区别。首先,如果生成器恰好生成了正确的标记,则该标记被视为“真实”而非“虚假”;我们发现这种形式化在下游任务中适度提高了结果。更重要的是,生成器是通过最大似然训练的,而不是通过对抗训练来欺骗判别器。对抗训练生成器具有挑战性,因为无法通过从生成器采样进行反向传播。尽管我们尝试通过使用强化学习训练生成器来规避此问题(见附录F),但其表现不如最大似然训练。最后,我们没有像典型的GAN那样向生成器提供噪声向量作为输入。
我们在大型原始文本语料库 X X X上最小化组合损失:
min θ G , θ D ∑ x ∈ X L MLM ( x , θ G ) + λ L Disc ( x , θ D ) \min_{\theta_G,\theta_D}\sum_{x\in X}\mathcal{L}_{\text{MLM}}(x,\theta_G)+\lambda\mathcal{L}_{\text{Disc}}(x,\theta_D) θG,θDminx∈X∑LMLM(x,θG)+λLDisc(x,θD)
我们通过单一样本近似损失中的期望值。我们不会通过生成器反向传播判别器损失(实际上,由于采样步骤,我们无法这样做)。预训练后,我们丢弃生成器,并在下游任务上微调判别器。
3 实验
3.1 实验设置
我们在通用语言理解评估(GLUE)基准测试(Wang等,2019)和斯坦福问答数据集(SQuAD)(Rajpurkar等,2016)上进行了评估。GLUE包含多种任务,涵盖文本蕴含(RTE和MNLI)、问答蕴含(QNLI)、释义(MRPC)、问题释义(QQP)、文本相似性(STS)、情感分析(SST)和语言可接受性(CoLA)。有关GLUE任务的更多详细信息,请参见附录C。我们的评估指标为:STS使用Spearman相关性,CoLA使用Matthews相关性,其他GLUE任务使用准确率;我们通常报告所有任务的平均分数。对于SQuAD,我们评估了1.1版本(模型选择回答问题的文本片段)和2.0版本(某些问题无法通过文章回答)。我们使用标准的评估指标:精确匹配(EM)和F1分数。在大多数实验中,我们使用与BERT相同的数据进行预训练,这些数据包括来自Wikipedia和BooksCorpus(Zhu等,2015)的33亿个标记。然而,对于我们的Large模型,我们使用了XLNet(Yang等,2019)的数据进行预训练,该数据集通过包含来自ClueWeb(Callan等,2009)、CommonCrawl和Gigaword(Parker等,2011)的数据,将BERT数据集扩展到330亿个标记。所有的预训练和评估都在英文数据上进行,尽管我们认为未来将我们的方法应用于多语言数据会很有趣。
我们的模型架构和大多数超参数与BERT相同。对于GLUE的微调,我们在ELECTRA之上添加了简单的线性分类器。对于SQuAD,我们在ELECTRA之上添加了XLNet的问答模块,该模块比BERT的稍微复杂一些,因为它联合而非独立预测起始和结束位置,并为SQuAD 2.0添加了“可回答性”分类器。我们的一些评估数据集较小,这意味着微调模型的准确率可能会因随机种子而有显著差异。因此,我们报告了从同一预训练检查点进行的10次微调运行的中位数结果。除非另有说明,结果均基于开发集。更多训练细节和超参数值请参见附录。
3.2 模型扩展
我们通过提出并评估模型的几种扩展来改进我们的方法。除非另有说明,这些实验使用与BERT-Base相同的模型大小和训练数据。
权重共享
我们建议通过共享生成器和判别器之间的权重来提高预训练的效率。如果生成器和判别器大小相同,则可以共享所有Transformer权重。然而,我们发现使用较小的生成器更高效,在这种情况下,我们仅共享生成器和判别器的嵌入(包括标记嵌入和位置嵌入)。在这种情况下,我们使用与判别器隐藏状态大小相同的嵌入。生成器的“输入”和“输出”标记嵌入始终像BERT一样绑定。我们比较了生成器与判别器大小相同时的权重绑定策略。我们训练这些模型50万步。GLUE分数分别为:无权重绑定83.6,绑定标记嵌入84.3,绑定所有权重84.4。
我们假设ELECTRA受益于绑定标记嵌入,因为掩码语言建模在学习这些表示方面特别有效:虽然判别器仅更新输入中存在或由生成器采样的标记,但生成器的softmax对词汇表的密集更新会更新所有标记嵌入。另一方面,绑定所有权重几乎没有带来改进,同时带来了生成器和判别器必须大小相同的显著缺点。基于这些发现,我们在本文的进一步实验中使用绑定嵌入。
较小的生成器
如果生成器和判别器大小相同,训练ELECTRA每一步的计算量大约是仅使用掩码语言建模的两倍。我们建议使用较小的生成器以减少这一因素。具体来说,我们通过减小层大小来缩小模型,同时保持其他超参数不变。我们还探索使用一种极其简单的“单字”生成器,根据训练语料库中的频率采样虚假标记。不同大小的生成器和判别器的GLUE分数如图3左侧所示。所有模型均训练50万步,这对较小的生成器在计算上是不利的,因为它们每一步所需的计算量较少。然而,我们发现当生成器大小为判别器的1/4到1/2时,模型表现最佳。我们推测,生成器过强可能会给判别器带来过于困难的任务,从而阻碍其有效学习。特别是,判别器可能不得不使用其许多参数来建模生成器,而不是实际的数据分布。本文的进一步实验使用给定判别器大小下找到的最佳生成器大小。
训练算法
最后,我们探索了ELECTRA的其他训练算法,尽管这些算法最终并未改善结果。提出的训练目标联合训练生成器和判别器。我们尝试使用以下两阶段训练过程:
- 仅使用 L MLM L_{\text{MLM}} LMLM训练生成器 n n n步。
- 使用生成器的权重初始化判别器的权重。然后使用 L Disc L_{\text{Disc}} LDisc训练判别器 n n n步,同时冻结生成器的权重。
请注意,此过程中的权重初始化要求生成器和判别器大小相同。我们发现,如果没有权重初始化,判别器有时可能完全无法学习到多数类以外的内容,可能是因为生成器在训练初期领先判别器太多。另一方面,联合训练自然地为判别器提供了一种课程,其中生成器从弱开始,但在训练过程中逐渐变强。我们还探索了像GAN一样对抗训练生成器,使用强化学习来适应从生成器采样的离散操作。详见附录F。
结果如图3右侧所示。在两阶段训练中,从生成目标切换到判别目标后,下游任务性能显著提高,但最终并未超过联合训练。尽管仍然优于BERT,我们发现对抗训练的表现不如最大似然训练。进一步分析表明,这种差距是由对抗训练的两个问题引起的。
首先,对抗生成器在掩码语言建模方面表现较差;其掩码语言建模的准确率为58%,而MLE训练的生成器为65%。我们认为较差的准确率主要是由于在生成文本的大动作空间中,强化学习的样本效率较低。其次,对抗训练的生成器产生的输出分布熵较低,大部分概率质量集中在单个标记上,这意味着生成器样本的多样性不足。这两个问题在之前的文本GAN研究中都有观察到(Caccia等,2018)。
3.3 小型模型
本研究的一个目标是提高预训练的效率,因此我们开发了一个可以在单个GPU上快速训练的小型模型。我们从BERT-Base的超参数出发,缩短了序列长度(从512减少到128),减少了批量大小(从256减少到128),降低了模型的隐藏层维度(从768减少到256),并使用了更小的词嵌入(从768减少到128)。为了进行公平比较,我们还使用相同的超参数训练了一个BERT-Small模型。我们将BERT-Small训练了150万步,使其使用的训练浮点运算量(FLOPs)与训练了100万步的ELECTRA-Small相同。除了BERT,我们还与两种计算资源需求较低的基于语言建模的预训练方法进行了比较:ELMo(Peters等,2018)和GPT(Radford等,2018)。我们还展示了与BERT-Base相当的ELECTRA-Base模型的结果。结果如表1所示。更多结果(包括使用更多计算资源训练的更强的小型和基础模型)请参见附录D。ELECTRA-Small在其规模下表现非常出色,其GLUE得分高于其他使用更多计算资源和参数的方法。例如,它的得分比可比的BERT-Small模型高出5分,甚至超过了更大的GPT模型。ELECTRA-Small的训练基本达到了收敛,即使训练时间更短(少至6小时),模型仍然能够取得合理的性能。虽然从更大的预训练Transformer模型中蒸馏出的小型模型也能取得良好的GLUE得分(Sun等,2019b;Jiao等,2019),但这些模型首先需要消耗大量计算资源来预训练更大的教师模型。结果还展示了ELECTRA在中等规模下的优势;我们的ELECTRA-Base模型显著优于BERT-Base,甚至超过了BERT-Large(其GLUE得分为84.0)。我们希望ELECTRA能够在相对较少的计算资源下取得强劲结果的能力,能够拓宽开发和应用预训练模型在自然语言处理中的可及性。
3.4 大型模型
我们训练了大型ELECTRA模型,以衡量替换标记检测预训练任务在当前最先进的预训练Transformer大规模下的有效性。我们的ELECTRA-Large模型与BERT-Large的规模相同,但训练时间更长。具体来说,我们训练了一个40万步的模型(ELECTRA-400K;大约是RoBERTa预训练计算量的1/4)和一个175万步的模型(ELECTRA-1.75M;计算量与RoBERTa相似)。我们使用了批量大小为2048和XLNet的预训练数据。需要注意的是,尽管XLNet的数据与用于训练RoBERTa的数据相似,但比较并不完全直接。作为基线,我们使用与ELECTRA-400K相同的超参数和训练时间训练了自己的BERT-Large模型。
GLUE开发集上的结果如表2所示。ELECTRA-400K的表现与RoBERTa和XLNet相当。然而,训练ELECTRA-400K所需的计算量不到RoBERTa和XLNet的1/4,这表明ELECTRA的样本效率优势在大规模下依然存在。更长时间训练的ELECTRA(ELECTRA-1.75M)在大多数GLUE任务上得分更高,同时仍然需要更少的预训练计算量。令人惊讶的是,我们的基线BERT模型得分明显低于RoBERTa-100K,这表明我们的模型可能会受益于更多的超参数调整或使用RoBERTa的训练数据。ELECTRA的优势在GLUE测试集上依然存在(见表3),尽管由于模型使用的额外技巧,这些比较并不完全直接(见附录B)。
SQuAD上的结果如表4所示。与GLUE结果一致,在相同的计算资源下,ELECTRA的得分优于基于掩码语言建模的方法。例如,ELECTRA-400K优于RoBERTa-100k和我们的BERT基线,它们使用了相似的预训练计算量。尽管ELECTRA-400K使用的计算量不到RoBERTa-500K的1/4,但其表现与RoBERTa-500K相当。不出所料,更长时间训练的ELECTRA进一步提高了结果:ELECTRA-1.75M在SQuAD 2.0基准测试中得分高于之前的模型。
ELECTRA-Base也取得了强劲的结果,得分显著优于BERT-Base和XLNet-Base,甚至在大多数指标上超过了BERT-Large。ELECTRA在SQuAD 2.0上的表现通常优于1.1版本。或许替换标记检测任务(模型需要区分真实标记与合理的伪造标记)特别适用于SQuAD 2.0的可回答性分类任务,因为模型必须区分可回答的问题与伪造的不可回答的问题。
3.5 效率分析
我们提出,将训练目标限定在一小部分标记上会导致掩码语言建模效率低下。然而,这一点并不完全显而易见。毕竟,尽管模型只预测少量掩码标记,但它仍然接收了大量的输入标记。为了更好地理解ELECTRA的优势来源,我们比较了一系列其他预训练目标,这些目标被设计为BERT和ELECTRA之间的“过渡阶梯”。
- ELECTRA 15%:该模型与ELECTRA相同,只是判别器损失仅来自输入中被掩码的15%的标记。换句话说,判别器损失 L D i s c L_{Disc} LDisc中的求和范围是 i ∈ m i \in m i∈m,而不是从1到 n n n。
- 替换MLM:该目标与掩码语言建模相同,只是被掩码的标记不是用[MASK]替换,而是用生成器模型生成的标记替换。该目标测试ELECTRA的增益在多大程度上来自于解决了预训练期间暴露给模型[MASK]标记但在微调期间不暴露的差异问题。
- 全标记MLM:与替换MLM类似,掩码标记被生成器样本替换。此外,模型预测输入中所有标记的身份,而不仅仅是被掩码的标记。我们发现,通过使用显式复制机制训练该模型可以提高结果,该机制使用sigmoid层为每个标记输出复制概率 D D D。模型的输出分布将 D D D权重放在输入标记上,加上 1 − D 1-D 1−D乘以MLM softmax的输出。该模型本质上是BERT和ELECTRA的结合。需要注意的是,如果没有生成器替换,模型会简单地学会从词汇表中预测[MASK]标记,并复制其他标记的输入。
结果如表5所示。首先,我们发现ELECTRA的显著优势来自于损失函数定义在所有输入标记上,而不仅仅是一个子集:ELECTRA 15%的表现远不如ELECTRA。其次,我们发现BERT的性能因预训练和微调之间[MASK]标记的不匹配而受到轻微影响,因为替换MLM的表现略优于BERT。我们注意到,BERT(包括我们的实现)已经包含了一种技巧来缓解预训练/微调差异:掩码标记在10%的情况下被替换为随机标记,并在10%的情况下保持不变。
然而,我们的结果表明,这些简单的启发式方法不足以完全解决问题。最后,我们发现全标记MLM(一种对所有标记而不是子集进行预测的生成模型)缩小了BERT和ELECTRA之间的大部分差距。总体而言,这些结果表明,ELECTRA的大部分改进可以归因于从所有标记中学习,而较小部分可以归因于缓解了预训练和微调之间的不匹配。
ELECTRA相对于全标记MLM的改进表明,ELECTRA的增益不仅仅来自于更快的训练。我们通过比较不同模型大小的BERT和ELECTRA进一步研究了这一点(见图4左)。我们发现,随着模型变小,ELECTRA的增益变得更大。小型模型完全训练到收敛(见图4右),表明ELECTRA在完全训练后比BERT实现了更高的下游准确性。我们推测,ELECTRA比BERT更具参数效率,因为它不必对每个位置可能标记的完整分布进行建模,但我们认为需要更多分析来完全解释ELECTRA的参数效率。
4 相关工作
NLP中的自监督预训练
自监督学习已被用于学习词表示(Collobert等,2011;Pennington等,2014),以及最近通过语言建模等目标学习词的上下文表示(Dai & Le,2015;Peters等,2018;Howard & Ruder,2018)。BERT(Devlin等,2019)在掩码语言建模任务上预训练了一个大型Transformer(Vaswani等,2017)。BERT有许多扩展版本。例如,MASS(Song等,2019)和UniLM(Dong等,2019)通过添加自回归生成训练目标将BERT扩展到生成任务。ERNIE(Sun等,2019a)和SpanBERT(Joshi等,2019)通过掩码连续的标记序列来改进跨度表示。这一想法可能与ELECTRA互补;我们认为将ELECTRA的生成器改为自回归并添加“替换跨度检测”任务会很有趣。XLNet(Yang等,2019)没有掩码输入标记,而是掩码注意力权重,使得输入序列以随机顺序自回归生成。然而,这种方法与BERT存在相同的低效问题,因为XLNet仅以这种方式生成15%的输入标记。与ELECTRA类似,XLNet可能通过不需要[MASK]标记来缓解BERT的预训练-微调差异,尽管这一点并不完全明确,因为XLNet在预训练期间使用两个“流”的注意力机制,而在微调期间只使用一个。最近,TinyBERT(Jiao等,2019)和MobileBERT(Sun等,2019b)等模型表明,BERT可以有效地蒸馏为更小的模型。相比之下,我们更关注预训练速度而不是推理速度,因此我们从零开始训练ELECTRA-Small。
生成对抗网络(GANs)
GANs(Goodfellow等,2014)在生成高质量合成数据方面非常有效。Radford等(2016)提出在下游任务中使用GAN的判别器,这与我们的方法类似。GANs已被应用于文本数据(Yu等,2017;Zhang等,2017),尽管最先进的方法仍然落后于标准的极大似然训练(Caccia等,2018;Tevet等,2018)。虽然我们没有使用对抗学习,但我们的生成器特别让人联想到MaskGAN(Fedus等,2018),它训练生成器填充从输入中删除的标记。
对比学习
广义上,对比学习方法将观察到的数据点与虚构的负样本区分开来。它们已被应用于多种模态,包括文本(Smith & Eisner,2005)、图像(Chopra等,2005)和视频(Wang & Gupta,2015;Sermanet等,2017)数据。常见的方法是学习嵌入空间,其中相关的数据点相似(Saunshi等,2019),或者对真实数据点进行排序的模型优于负样本(Collobert等,2011;Bordes等,2013)。ELECTRA与噪声对比估计(NCE)(Gutmann & Hyvarinen,2010)特别相关,后者也训练一个二元分类器来区分真实和伪造的数据点。Word2Vec(Mikolov等,2013)是NLP中最早的预训练方法之一,使用了对比学习。事实上,ELECTRA可以看作是带有负采样的连续词袋模型(CBOW)的大规模扩展版本。CBOW也根据上下文预测输入标记,而负采样将学习任务重新表述为关于输入标记是来自数据还是提议分布的二元分类任务。然而,CBOW使用词袋向量编码器而不是Transformer,并使用从单字标记频率派生的简单提议分布,而不是学习的生成器。
5 结论
我们提出了替换标记检测,这是一种用于语言表示学习的新自监督任务。其核心思想是训练一个文本编码器来区分输入标记与由小型生成器网络生成的高质量负样本。与掩码语言建模相比,我们的预训练目标计算效率更高,并在下游任务中表现出更好的性能。即使使用相对较少的计算资源,它也能很好地工作,我们希望这将使开发和应用预训练文本编码器对计算资源较少的研究人员和从业者更加容易。我们还希望未来更多关于NLP预训练的工作能够同时考虑效率和绝对性能,并像我们一样在报告评估指标时同时报告计算使用量和参数数量。
A 预训练细节
以下细节适用于我们的ELECTRA模型和BERT基线模型。我们大多使用与BERT相同的超参数。我们将判别器目标在损失中的权重λ设置为50。我们使用动态token掩码,掩码位置在训练过程中动态决定,而不是在预处理阶段确定。此外,我们没有使用原始BERT论文中提出的下一句预测目标,因为最近的研究表明它不会提高分数(Yang等,2019;Liu等,2019)。对于我们的ELECTRA-Large模型,我们使用了更高的掩码比例(25%而不是15%),因为我们注意到生成器在15%的掩码比例下达到了很高的准确率,导致替换的token非常少。我们在早期实验中为Base和Small模型从[1e-4, 2e-4, 3e-4, 5e-4]中搜索了最佳学习率,并从[1, 10, 20, 50, 100]中选择了λ。除此之外,我们没有进行第3.2节实验之外的超参数调优。完整的超参数集列在表6中。
B 微调细节
对于Large模型,我们大多使用了Clark等(2019)的超参数。然而,在注意到RoBERTa(Liu等,2019)使用了更多的训练轮数(最多10轮而不是3轮)后,我们为每个任务从[10, 3]中搜索了最佳的训练轮数。对于SQuAD,我们将训练轮数减少到2轮,以与BERT和RoBERTa保持一致。对于Base模型,我们从[3e-5, 5e-5, 1e-4, 1.5e-4]中搜索了学习率,并从[0.9, 0.8, 0.7]中搜索了分层学习率衰减,其他超参数与Large模型相同。我们发现Small模型受益于更大的学习率,并从[1e-4, 2e-4, 3e-4, 5e-3]中搜索了最佳学习率。除了训练轮数外,我们对所有任务使用了相同的超参数。相比之下,之前关于GLUE的研究,如BERT、XLNet和RoBERTa,分别为每个任务搜索了最佳超参数。我们预计,如果我们进行类似的额外超参数搜索,我们的结果会略有提升。完整的超参数集列在表7中。
遵循BERT的做法,我们没有在开发集结果中展示WNLI GLUE任务的结果,因为即使使用标准的微调分类方法,也很难击败多数分类器。对于GLUE测试集结果,我们应用了许多GLUE排行榜提交(包括RoBERTa(Liu等,2019)、XLNet(Yang等,2019)和ALBERT(Lan等,2019))使用的标准技巧。具体来说:
- 对于RTE和STS,我们使用中间任务训练(Phang等,2018),从已在MNLI上微调的ELECTRA检查点开始。对于RTE,我们发现将其与较低的学习率2e-5结合使用是有帮助的。
WNLI任务
对于WNLI任务,我们遵循了Liu等(2019)中描述的技巧,即使用规则提取代词的候选先行词,并训练模型为正确的先行词赋予高分。然而,与Liu等(2019)不同的是,我们的评分函数并非基于掩码语言模型(MLM)概率。相反,我们对ELECTRA的判别器进行微调,使其在正确的先行词替换代词时,为正确先行词的token赋予高分。例如,对于Winograd模式句子“奖杯无法放入行李箱,因为它太大了”,我们训练判别器使其在“奖杯无法放入行李箱,因为奖杯太大了”中为“奖杯”赋予高分,而在“奖杯无法放入行李箱,因为行李箱太大了”中为“行李箱”赋予低分。
模型集成
对于每个任务,我们从相同的预训练检查点初始化,使用不同的随机种子微调了30个模型,并集成了其中表现最好的10个模型。
尽管这些技巧确实提高了分数,但它们使得科学对比变得更加困难,因为这些技巧需要额外的工作来实现,需要大量的计算资源,并且由于不同论文对这些技巧的实现方式不同,结果之间的可比性降低。因此,我们还在表8中报告了ELECTRA-1.75M的结果,其中唯一的技巧是开发集模型选择(从10个模型中选择最佳模型),这是BERT用于报告结果的设置。
SQuAD 2.0测试集提交
对于SQuAD 2.0测试集的提交,我们从相同的预训练检查点微调了20个模型,并提交了开发集分数最高的模型。
GLUE任务详情
以下是GLUE基准任务的进一步细节:
- CoLA:语言可接受性语料库(Warstadt等,2018)。任务是判断给定句子是否符合语法。数据集包含8.5k个来自语言学理论书籍和期刊文章的训练样本。
- SST:斯坦福情感树库(Socher等,2013)。任务是判断句子的情感是积极还是消极。数据集包含67k个来自电影评论的训练样本。
- MRPC:微软研究释义语料库(Dolan & Brockett,2005)。任务是预测两个句子是否语义等价。数据集包含3.7k个来自在线新闻源的训练样本。
- STS:语义文本相似度(Cer等,2017)。任务是预测两个句子在1-5范围内的语义相似度。数据集包含5.8k个来自新闻标题、视频和图像描述以及自然语言推理数据的训练样本。
- QQP:Quora问题对(Iyer等,2017)。任务是判断一对问题是否语义等价。数据集包含364k个来自问答社区Quora的训练样本。
- MNLI:多体裁自然语言推理(Williams等,2018)。给定一个前提句子和一个假设句子,任务是预测前提是否蕴含假设、与假设矛盾或两者都不是。数据集包含393k个来自十个不同来源的训练样本。
- QNLI:问题自然语言推理;基于SQuAD构建(Rajpurkar等,2016)。任务是预测上下文句子是否包含问题句子的答案。数据集包含108k个来自维基百科的训练样本。
- RTE:文本蕴含识别(Giampiccolo等,2007)。给定一个前提句子和一个假设句子,任务是预测前提是否蕴含假设。数据集包含2.5k个来自一系列年度文本蕴含挑战赛的训练样本。
GLUE测试集上的进一步结果
我们在表8中报告了ELECTRA-Base和ELECTRA-Small在GLUE测试集上的结果。此外,我们通过使用XLNet数据(而非wikibooks)并训练更长时间(4e6训练步数)来进一步探索Base和Small模型的极限;这些模型在表中称为ELECTRA-Base++和ELECTRA-Small++。对于ELECTRA-Small++,我们还将序列长度增加到512;其他超参数与表6中列出的相同。最后,表中还包含了未使用附录B中描述的技巧的ELECTRA-1.75M的结果。与论文中的开发集结果一致,ELECTRA-Base在平均分数上优于BERT-Large,而ELECTRA-Small优于GPT。不出所料,++模型的表现更好。Small模型的分数甚至接近TinyBERT(Jiao等,2019)和MobileBERT(Sun等,2019b)。这些模型通过复杂的蒸馏过程从BERT-Base中学习,而我们的ELECTRA模型则是从头训练的。鉴于BERT蒸馏的成功,我们相信通过蒸馏ELECTRA可以构建更强大的小型预训练模型。ELECTRA在CoLA任务上表现尤为突出。在CoLA中,目标是区分符合语法的句子和不符合语法的句子,这与ELECTRA预训练任务中识别虚假token的任务非常接近,这或许解释了ELECTRA在该任务上的优势。
E 计算FLOPs
我们选择以浮点运算次数(FLOPs)来衡量计算量,因为这是一种与特定硬件、底层优化等无关的度量方式。然而,值得注意的是,在某些情况下,忽略硬件细节可能是一个缺点,因为以硬件为中心的优化可能是模型设计的关键部分。例如,ALBERT(Lan等,2019)通过权重绑定减少了TPU工作节点之间的通信开销,从而获得了加速。我们使用了TensorFlow的FLOP计数功能,并通过手动计算验证了结果。我们做了以下假设:
-
“操作”是指数学运算,而不是机器指令。例如,指数运算(exp)和加法运算(add)都算作一次操作,尽管实际上指数运算可能更慢。我们认为这一假设不会显著改变计算量的估计,因为矩阵乘法占据了大多数模型的主要计算量。同样,我们将矩阵乘法计算为2 ∗ m ∗ n FLOPs,而不是m ∗ n(如果考虑融合乘加操作的话)。
-
反向传播的计算量与前向传播相同。这一假设并不完全准确(例如,对于softmax交叉熵损失,反向传播更快),但重要的是,对于矩阵乘法而言,前向传播和反向传播的FLOPs确实是相同的,而矩阵乘法占据了大部分计算量。
-
我们假设“稠密”嵌入查找(即与one-hot向量相乘)。实际上,稀疏嵌入查找比常数时间慢得多;在某些硬件加速器上,稠密操作实际上比稀疏查找更快。
通过以上假设,我们能够以FLOPs为单位对模型的计算量进行标准化评估,从而在不同模型之间进行公平的比较。
F 对抗训练
在这里,我们详细介绍了尝试使用对抗训练来训练生成器,而不是使用最大似然估计的方法。具体来说,我们训练生成器 G G G以最大化判别器损失 L D i s c \mathcal{L}_\mathrm{Disc} LDisc。由于我们的判别器与GAN(生成对抗网络)中的判别器并不完全相同(参见第2节的讨论),这种方法实际上是**对抗对比估计(Adversarial Contrastive Estimation, Bose等,2018)**的一个实例,而不是生成对抗训练。由于生成器的离散采样,无法通过判别器反向传播来对抗训练生成器(例如,像在图像上训练的GAN那样),因此我们改用强化学习。
我们的生成器与大多数文本生成模型不同,它是非自回归的:预测是独立进行的。换句话说,生成器不是通过一系列动作生成每个token,而是通过一个巨大的动作同时生成所有token,其中动作的概率分解为每个token生成概率的乘积。为了处理这种巨大的动作空间,我们做了以下简化假设:判别器的预测 D ( x corrupt , t ) D(x^\text{corrupt},t) D(xcorrupt,t)仅依赖于token x t x_t xt和未被替换的token { x i : i ∉ m } \{x_i:i\not\in\boldsymbol{m}\} {xi:i∈m},即它不依赖于其他生成的token { x ^ i : i ∈ m ∧ i ≠ t } \{\hat{x}_i:i\in\boldsymbol{m}\wedge i\neq t\} {x^i:i∈m∧i=t}。这个假设并不算太糟糕,因为被替换的token数量相对较少,并且它大大简化了使用强化学习时的信用分配问题。在符号上,我们通过(略微滥用符号)将判别器预测生成token x ^ t \hat{x}_t x^t是否等于原始token x t x_t xt表示为 D ( x ^ t ∣ x masked ) D(\hat{x}_t|x^\text{masked}) D(x^t∣xmasked),其中 x masked x^\text{masked} xmasked是掩码上下文。这一假设的一个有用结果是,对于未被替换的token ( D ( x t ∣ x masked ) (D(x_t|\boldsymbol{x}^\text{masked}) (D(xt∣xmasked),其中 t ∉ m ) t\not\in\boldsymbol{m}) t∈m),其判别器分数与 p G p_G pG无关,因为我们假设它不依赖于任何被替换的token。因此,在训练 G ~ \tilde{G} G~以最大化 L ^ D i s c \hat{\mathcal{L}}_\mathrm{Disc} L^Disc时,可以忽略这些token。
在训练过程中,我们试图找到:
arg max θ G L D i s c = arg max θ G E x , m , x ^ ( ∑ t = 1 n − 1 ( x t c o r r u p t = x t ) log D ( x c o r r u p t , t ) − 1 ( x t c o r r u p t ≠ x t ) log ( 1 − D ( x c o r r u p t , t ) ) ) \begin{array} { r l } & { \underset { \theta _ { G } } { \arg \operatorname* { m a x } } \, \mathcal { L } _ { \mathrm { D i s c } } = \underset { \theta _ { G } } { \arg \operatorname* { m a x } } \, \underset { x , m , \hat { x } } { \mathbb { E } } \left( \sum _ { t = 1 } ^ { n } - \mathbb { 1 } ( x _ { t } ^ { \mathrm { c o r r u p t } } = x _ { t } ) \log D ( x ^ { \mathrm { c o r r u p t } } , t ) - \right. } \\ & { \left. \qquad \qquad \qquad \qquad \qquad \qquad \mathbb { 1 } ( x _ { t } ^ { \mathrm { c o r r u p t } } \neq x _ { t } ) \log ( 1 - D ( x ^ { \mathrm { c o r r u p t } } , t ) ) \right) } \end{array} θGargmaxLDisc=θGargmaxx,m,x^E(∑t=1n−1(xtcorrupt=xt)logD(xcorrupt,t)−1(xtcorrupt=xt)log(1−D(xcorrupt,t)))
利用简化假设,我们通过以下公式近似上述目标:
E x , m , x ^ ( ∑ t ∈ m − 1 ( x ^ t = x t ) log D ( x ^ ∣ x m a s k e d ) − 1 ( x ^ t ≠ x t ) log ( 1 − D ( x ^ ∣ x m a s k e d ) ) ) = E x , m ∑ t ∈ m E x ^ t ∼ p G R ( x ^ t , x ) \mathbb{E}_{x,m,\hat{x}}\left(\sum_{t\in m}-1(\hat{x}_{t}=x_{t})\log D(\hat{x}|x^{\mathrm{masked}})-1(\hat{x}_{t}\neq x_{t})\log(1-D(\hat{x}|x^{\mathrm{masked}}))\right)=\mathbb{E}_{x,m}\sum_{t\in m}\mathbb{E}_{\hat{x}_{t}\sim p_{G}}R(\hat{x}_{t},x) Ex,m,x^(t∈m∑−1(x^t=xt)logD(x^∣xmasked)−1(x^t=xt)log(1−D(x^∣xmasked)))=Ex,mt∈m∑Ex^t∼pGR(x^t,x)
其中:
R ( x ^ t , x ) = { − log D ( x ^ t ∣ x m a s k e d ) i f x ^ t = x t − log ( 1 − D ( x ^ t ∣ x m a s k e d ) ) o t h e r w i s e R(\hat{x}_{t},\boldsymbol{x})=\begin{cases}-\log D(\hat{x}_{t}|\boldsymbol{x}^{\mathrm{masked}})&\mathrm{if~}\hat{x}_{t}=x_{t}\\-\log(1-D(\hat{x}_{t}|\boldsymbol{x}^{\mathrm{masked}}))&\mathrm{otherwise}\end{cases} R(x^t,x)={−logD(x^t∣xmasked)−log(1−D(x^t∣xmasked))if x^t=xtotherwise
简而言之,简化假设使我们能够将损失分解为单个生成token的损失。由于无法通过 x ^ \hat{x} x^的离散采样进行反向传播,因此我们无法直接使用梯度上升法找到 arg max θ G \arg \max_{\theta_G} argmaxθG。相反,我们使用策略梯度强化学习(Williams, 1992)。具体来说,我们使用REINFORCE梯度:
∇ θ G L D i s c ≈ E ∑ x , m ∑ t ∈ m E x ^ t ∼ p G ∇ θ g log p G ( x ^ t ∣ x m a s k e d ) [ R ( x ^ t , x ) − b ( x m a s k e d , t ) ] \nabla_{\theta_{G}}\mathcal{L}_{\mathrm{Disc}}\approx\mathbb{E}\sum_{x,m}\sum_{t\in m}\mathbb{E}_{\hat{x}_{t}\sim p_{G}}\nabla_{\theta_{g}}\log p_{G}(\hat{x}_{t}|x^{\mathrm{masked}})[R(\hat{x}_{t},x)-b(x^{\mathrm{masked}},t)] ∇θGLDisc≈Ex,m∑t∈m∑Ex^t∼pG∇θglogpG(x^t∣xmasked)[R(x^t,x)−b(xmasked,t)]
其中, b b b是一个通过学习得到的基线函数,其实现为 b ( x masked , t ) = − log b(x^\text{masked},t)=-\log b(xmasked,t)=−logsigmoid ( w T h G ( x masked ) t ) (w^Th_G(\boldsymbol{x}^\text{masked})_t) (wThG(xmasked)t),其中 h G ( x masked ) h_G(\boldsymbol{x}^\text{masked}) hG(xmasked)是生成器的Transformer编码器的输出。基线函数通过交叉熵损失进行训练,以匹配对应位置的奖励。我们通过单样本近似期望,并使用梯度上升法学习 θ G \theta_G θG。
尽管没有收到关于哪些生成的token是正确的明确反馈,但我们发现对抗训练能够生成一个相当准确的生成器(对于一个256隐藏层大小的生成器,对抗训练的生成器在掩码语言建模任务中达到了58%的准确率,而相同大小的最大似然估计(MLE)生成器的准确率为65%)。然而,在下游任务中,使用这种生成器并没有比使用MLE训练的生成器带来改进(参见主论文中图B的右侧部分)。
L D i s c = − ∑ x ∈ v o c a b ( ( 1 − p m a s k ) p d a t a ( x ∣ c ) log D ( x , c ) + //unmasked token p m a s k p d a t a ( x ∣ c ) p G ( x ∣ c ) log D ( x , c ) + //generator samples correct token p m a s k ( 1 − p d a t a ( x ∣ c ) ) p G ( x ∣ c ) log ( 1 − D ( x , c ) ) ) //generator samples incorrect token \begin{aligned} \mathcal{L}_{\mathrm{Disc}}= & -\sum_{x\in\mathrm{vocab}}\Big((1-p_{\mathrm{mask}})p_{\mathrm{data}}(x|c)\log D(x,c)+ &\qquad{\text{//unmasked token}}\\ & p_{\mathrm{mask}}p_{\mathrm{data}}(x|c)p_{G}(x|c)\log D(x,c)+ &\qquad{\text{//generator samples correct token}}\\ & p_{\mathrm{mask}}(1-p_{\mathrm{data}}(x|c))p_{G}(x|c)\log(1-D(x,c))\Big) &\qquad{\text{//generator samples incorrect token}} \end{aligned} LDisc=−x∈vocab∑((1−pmask)pdata(x∣c)logD(x,c)+pmaskpdata(x∣c)pG(x∣c)logD(x,c)+pmask(1−pdata(x∣c))pG(x∣c)log(1−D(x,c)))//unmasked token//generator samples correct token//generator samples incorrect token
对这个损失函数关于 D 求解临界点显示,对于一个固定的生成器,最优的鉴别器是
D ( x , c ) = p d a t a ( x ∣ c ) ( a + p G ( x ∣ c ) ) / ( a p d a t a ( x ∣ c ) + p G ( x ∣ c ) ) D(x,c)=p_\mathrm{data}(x|c)(a+p_G(x|c))/(ap_\mathrm{data}(x|c)+p_G(x|c)) D(x,c)=pdata(x∣c)(a+pG(x∣c))/(apdata(x∣c)+pG(x∣c))
这意味着
p d a t a ( x ∣ c ) = D ( x , c ) p G ( x ∣ c ) / ( a ( 1 − D ( x , c ) ) + p G ( x ∣ c ) ) p_{\mathrm{data}}(x|c)=D(x,c)p_{G}(x|c)/(a(1-D(x,c))+p_{G}(x|c)) pdata(x∣c)=D(x,c)pG(x∣c)/(a(1−D(x,c))+pG(x∣c))
其中, a = ( 1 − p m a s k ) / p m a s k a=(1-p_{\mathrm{mask}})/p_{\mathrm{mask}} a=(1−pmask)/pmask 表示每个被掩码的标记对应的未掩码标记的数量。我们可以利用这个表达式来评估ELECTRA作为一个掩码语言模型的表现,通过选择 arg max x ∈ 词汇表 D ( x , c ) p G ( x ∣ c ) / ( a ( 1 − D ( x , c ) ) + p G ( x ∣ c ) ) \arg\max_{x\in\text{词汇表}}D(x,c)p_G(x|c)/(a(1-D(x,c))+p_G(x|c)) argmaxx∈词汇表D(x,c)pG(x∣c)/(a(1−D(x,c))+pG(x∣c))作为模型在给定上下文下的预测。实际上,对整个词汇表进行选择是非常耗时的,因此我们改为在生成器预测的前100个结果中选取argmax。使用这种方法,我们在Wikipedia+BooksCorpus数据集上比较了ELECTRA-Base和BERT-Base。我们发现,在掩码语言建模任务上,BERT略优于ELECTRA(准确率分别为77.9%和75.5%)。这可能是因为在这种评估方案下,假设存在一个最优的判别器(这显然远非正确)损害了ELECTRA的准确性。然而,或许并不太令人惊讶的是,像BERT这样专门为生成任务训练的模型在生成任务上表现更好,而像ELECTRA这样具有判别目标的模型在微调判别任务时表现更佳。我们认为,比较BERT和ELECTRA的MLM预测可能是未来工作中揭示ELECTRA和BERT编码器差异的一种有趣方式。
H 负面结果
我们简要描述了一些在初步实验中看起来不太有前景的想法:
-
我们最初尝试通过策略性地掩码标记来提高BERT的效率(例如,更频繁地掩码较罕见的标记,或者训练一个模型来猜测如果某些标记被掩码,BERT是否难以预测它们)。这些方法相比常规BERT仅带来了相当小的加速效果。
-
鉴于ELECTRA似乎(在某种程度上)受益于使用较弱的生成器(见第3.2节),我们尝试提高生成器输出softmax的温度,或者禁止生成器采样正确的标记。然而,这些方法均未改善结果。
-
我们尝试添加一个句子级别的对比目标。在这个任务中,我们保留了20%的输入句子不变,而不是用生成器对其进行噪声处理。然后,我们在模型中添加了一个预测头,用于预测整个输入是否被破坏。令人惊讶的是,这略微降低了在下游任务上的得分。
相关文章:
ELECTRA:作为判别器而非生成器的预训练文本编码器
摘要 诸如BERT之类的掩码语言建模(MLM)预训练方法通过将某些标记替换为[MASK]来破坏输入,然后训练模型以重建原始标记。尽管这些方法在下游自然语言处理(NLP)任务中表现良好,但它们通常需要大量的计算资源…...
图论——最小生成树
最小生成树 给定一个无向图,在图中选择若干条边把图的所有节点连起来。要求边长之和最小。在图论中,叫做求最小生成树。 prim算法 prim 算法采用的是一种贪心的策略。 每次将离连通部分的最近的点和点对应的边加入的连通部分,连通部分逐渐扩大…...
【Linux-网络】初识计算机网络 Socket套接字 TCP/UDP协议(包含Socket编程实战)
🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 道阻且长,行则将至 目录 📚一、初识计算机网络 📖 背景 📖 网络协议 🔖OSI七层…...
三数之和(15)
15. 三数之和 - 力扣(LeetCode) 可以一起总结的题目:三角形的最大周长(976)-CSDN博客 解法: class Solution { public:vector<vector<int>> threeSum(vector<int>& nums) {vector…...
6 Flink 状态管理
6 Flink 状态管理 1. State-Keyed State2. State-Operator State3. Broadcast State 我们前面写的 wordcount 的例子,没有包含状态管理。如果一个task在处理过程中挂掉了,那么它在内存中的状态都会丢失,所有的数据都需要重新计算。从容错和消…...
物联网 STM32【源代码形式-使用以太网】连接OneNet IOT从云产品开发到底层MQTT实现,APP控制 【保姆级零基础搭建】
物联网(IoT)是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器等装置与技术,实时采集并连接任何需要监控、连接、互动的物体或过程,实现对物品和过程的智能化感知、识别和管理。物联网的核心功能包括数据采集与监…...
elasticsearch8.15 高可用集群搭建(含认证Kibana)
文章目录 1.资源配置2.系统参数优化3.JDK17安装4.下载&安装ES 8.155.生成ES的证书(用于ES节点之间进行安全数据传输)6.修改ES 相关配置文件7.创建es用户并启动8.配置ES的账号和密码(用于ES服务端和客户端)9.下载和安装Kibana10.编辑Kibana配置文件11.启动Kiabana12.访问Kia…...
如何实现滑动网格的功能
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverList组件相关的内容,本章回中将介绍SliverGrid组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverGrid组件是一种网格类组件,主要用来…...
DBASE DBF数据库文件解析
基于Java实现DBase DBF文件的解析和显示 JDK19编译运行,实现了数据库字段和数据解析显示。 首先解析数据库文件头代码 byte bytes[] Files.readAllBytes(Paths.get(file));BinaryBufferArray bis new BinaryBufferArray(bytes);DBF dbf new DBF();dbf.VersionN…...
linux中统计文件中特定单词或字符串的出现次数
在 Linux 中,可以使用 grep 和 wc 命令组合来统计一个文件中特定单词或字符串的个数。假设想统计文件 example.txt 中字符串 “example_string” 出现的次数,可以使用以下命令: grep -o -w example_string example.txt | wc -l这里是每个选项…...
视觉状态空间模型(VMamba)的解读
在计算机视觉领域,设计计算高效的网络架构一直是研究的热点。今天,我想和大家分享一篇发表在 NIPS 2024 上的论文——VMamba:Visual State Space Model,这篇论文提出了一种新的视觉骨干网络,具有线性时间复杂度&#x…...
几种K8s运维管理平台对比说明
目录 深入体验**结论**对比分析表格**1. 功能对比****2. 用户界面****3. 多租户支持****4. DevOps支持** 细对比分析1. **Kuboard**2. **xkube**3. **KubeSphere**4. **Dashboard****对比总结** 深入体验 KuboardxkubeKubeSphereDashboard 结论 如果您需要一个功能全面且适合…...
nodejs:js-mdict 的下载、安装、测试、build
js-mdict 项目的目录结构:js-mdict 项目教程 js-mdict 下载地址: js-mdict-master.zip 先解压到 D:\Source\ js-mdict 6.0.2 用了 ts (TypeScript) 和 Jest,增加了应用开发的难度,因为先要了解 ts 和 Jest。 参阅:测试与开发&a…...
Vue3 表单:全面解析与最佳实践
Vue3 表单:全面解析与最佳实践 引言 随着前端技术的发展,Vue.js 已经成为最受欢迎的前端框架之一。Vue3 作为 Vue.js 的最新版本,带来了许多改进和新的特性。其中,表单处理是 Vue 应用中不可或缺的一部分。本文将全面解析 Vue3 …...
JavaWeb入门-请求响应(Day3)
(一)请求响应概述 请求(HttpServletRequest):获取请求数据 响应(HttpServletResponse):设置响应数据 BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器就可访问,应用程序的逻辑和数据都存储在服务端(维护方便,响应速度一般) CS架构:Client/ser…...
【单层神经网络】基于MXNet库简化实现线性回归
写在前面 同最开始的两篇文章 完整程序及注释 导入使用的库# 基本 from mxnet import autograd, nd, gluon # 模型、网络 from mxnet.gluon import nn from mxnet import init # 学习 from mxnet.gluon import loss as gloss # 数据集 from mxnet.gluon…...
一元函数微积分的几何应用:二维平面光滑曲线的曲率公式
文章目录 前言曲率和曲率半径的定义曲率计算公式参数方程形式直角坐标显式方程形式极坐标形式向量形式 前言 本文将介绍二维平面光滑曲线的曲率定义以及不同形式的曲率及曲率半径公式的推导。 曲率和曲率半径的定义 (关于二维平面光滑曲线的定义以及弧长公式请参…...
编程题-最接近的三数之和
题目: 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 解法一(排序双指针): 题目要求找…...
【LLM-agent】(task4)搜索引擎Agent
note 新增工具:搜索引擎Agent 文章目录 note一、搜索引擎AgentReference 一、搜索引擎Agent import os from dotenv import load_dotenv# 加载环境变量 load_dotenv() # 初始化变量 base_url None chat_model None api_key None# 使用with语句打开文件…...
string类详解
为什么学习string类? 1.1 C语言中的字符串 C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想…...
【含文档+PPT+源码】基于微信小程序农家乐美食餐厅预约推广系统
项目介绍 本课程演示的是一款基于微信小程序农家乐美食餐厅预约推广系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 …...
享元模式——C++实现
目录 1. 享元模式简介 2. 代码示例 1. 享元模式简介 享元模式是一种结构型模式。 享元模式用于缓存共享对象,降低内存消耗。共享对象相同的部分,避免创建大量相同的对象,减少内存占用。 享元模式需要将对象分成内部状态和外部状态两个部分…...
《苍穹外卖》项目学习记录-Day11订单统计
根据起始时间和结束时间,先把begin放入集合中用while循环当begin不等于end的时候,让begin加一天,这样酒吧把这个区间内的时间放到List集合。 查询每天的订单总数也就是查询的时间段是大于当天的开始时间(0点0分0秒)小…...
Python3 OS模块中的文件/目录方法说明十六
一. 简介 前面文章简单学习了 Python3 中 OS模块中的文件/目录的部分函数。 本文继续来学习 OS 模块中文件、目录的操作方法:os.unlink() 方法、os.utime()方法。 二. Python3 OS模块中的文件/目录方法 1. os.unlink() 方法 os.unlink() 方法用于删除文件,如果文…...
(二)QT——按钮小程序
目录 前言 按钮小程序 1、步骤 2、代码示例 3、多个按钮 ①信号与槽的一对一 ②多对一(多个信号连接到同一个槽) ③一对多(一个信号连接到多个槽) 结论 前言 按钮小程序 Qt 按钮程序通常包含 三个核心文件: m…...
图论——spfa判负环
负环 图 G G G中存在一个回路,该回路边权之和为负数,称之为负环。 spfa求负环 方法1:统计每个点入队次数, 如果某个点入队n次, 说明存在负环。 证明:一个点入队n次,即被更新了n次。一个点每次被更新时所对应最短路的边数一定是…...
96,【4】 buuctf web [BJDCTF2020]EzPHP
进入靶场 查看源代码 GFXEIM3YFZYGQ4A 一看就是编码后的 1nD3x.php 访问 得到源代码 <?php // 高亮显示当前 PHP 文件的源代码,用于调试或展示代码结构 highlight_file(__FILE__); // 关闭所有 PHP 错误报告,防止错误信息泄露可能的安全漏洞 erro…...
Rust 的基本类型有哪些,他们存在堆上还是栈上,是否可以COPY?
Rust 的基本类型主要包括以下几类: 1. 整数类型(Integer) Rust 提供了有符号和无符号的整数类型: 有符号整数(i8, i16, i32, i64, i128, isize)无符号整数(u8, u16, u32, u64, u128, usize&a…...
函数与递归
函数与递归 声明或者定义应该在使用之前(不单单针对于函数) 函数对全局变量做出的改变还是不会随着函数结束而消失的 函数声明在main函数里面也是可以的 引用变量和引用实体的变化是一样的 传址调用比传值调用效率高 重载函数->编译器会根据传递…...
UE5 蓝图学习计划 - Day 11:材质与特效
在游戏开发中,材质(Material)与特效(VFX) 是提升视觉体验的关键元素。Unreal Engine 5 提供了强大的 材质系统 和 粒子系统(Niagara),让开发者可以通过蓝图控制 动态材质、光效变化、…...
DeepSeek 详细使用教程
1. 简介 DeepSeek 是一款基于人工智能技术的多功能工具,旨在帮助用户高效处理和分析数据、生成内容、解答问题、进行语言翻译等。无论是学术研究、商业分析还是日常使用,DeepSeek 都能提供强大的支持。本教程将详细介绍 DeepSeek 的各项功能及使用方法。…...
低代码系统-产品架构案例介绍、炎黄盈动-易鲸云(十二)
易鲸云作为炎黄盈动新推出的产品,在定位上为低零代码产品。 开发层 表单引擎 表单设计器,包括设计和渲染 流程引擎 流程设计,包括设计和渲染,需要说明的是:采用国际标准BPMN2.0,可以全球通用 视图引擎 视图…...
【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.27 线性代数王国:矩阵分解实战指南
1.27 线性代数王国:矩阵分解实战指南 #mermaid-svg-JWrp2JAP9qkdS2A7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JWrp2JAP9qkdS2A7 .error-icon{fill:#552222;}#mermaid-svg-JWrp2JAP9qkdS2A7 .erro…...
【C语言】动态内存管理
1、为什么存在动态内存分配?2、动态内存管理函数介绍(1)malloc(2)free(3)calloc(4)realloc 3、常见动态内存错误(1)使用free释放动态内存开辟的一…...
Cocoa和Cocoa Touch是什么语言写成的?什么是Cocoa?编程语言中什么是框架?为什么苹果公司Cocoa类库有不少NS前缀?Swift编程语言?
Cocoa和Cocoa Touch是什么语言写成的? 二者主要都是用Objective-C语言编写而成的。 什么是Cocoa? Cocoa是苹果操作系统macOS和iOS上的应用程序开发框架集合,核心语言是Objective-C编程语言,在移动平台被称为Cocoa Touch,Cocoa包含多个子框架…...
Qt Creator 中使用 vcpkg
Qt Creator 中使用 vcpkg Qt Creator 是一个跨平台的轻量级 IDE,做 Qt 程序开发的同学们肯定对这个 IDE 都比较属于。这个 IDE 虽然没有 Visual Stdio 功能那么强,但是由于和 Qt 集成的比较深,用来开发 Qt 程序还是很顺手的。 早期…...
Python GUI 开发 | PySide6 PyQt6 学习手册
本文是个 Python GUI 开发的目录,方便读者系统性学习的,笔者后续会满满填充此目录中的内容,感兴趣的小伙伴可以关注一手。(主要是偏向 PySide6 方向的) 0x01:PySide6 & PyQt6 基础入门 0x0101ÿ…...
Xposed-Hook
配置 Xposed 模块的 AndroidManifest.xml: <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"your.package.name"><applicationandr…...
鸿蒙物流项目之基础结构
目录: 1、项目结构2、三种包的区别和使用场景3、静态资源的导入4、颜色样式设置5、修改项目名称和图标6、静态包基础目录7、组件的抽离8、在功能模块包里面引用静态资源包的组件 1、项目结构 2、三种包的区别和使用场景 3、静态资源的导入 放在har包中,那…...
CSS 中调整元素大小的全面指南
CSS 中调整元素大小的全面指南 1. 原始尺寸(固有尺寸)示例代码:图像的固有尺寸 2. 设置具体的尺寸示例代码:设置固定宽度和高度 3. 使用百分比示例代码:使用百分比设置宽度 4. 使用百分比作为外边距和内边距示例代码&a…...
文字投影效果
大家好,我是喝西瓜汁的兔叽,今天给大家分享一个常见的文字投影效果。 效果展示 我们来实现一个这样的文字效果。 思路分析 这样的效果如何实现的呢? 实际上是两组相同的文字,叠合在一块,只不过对应的css不同罢了。 首先&…...
【Redis】set 和 zset 类型的介绍和常用命令
1. set 1.1 介绍 set 类型和 list 不同的是,存储的元素是无序的,并且元素不允许重复,Redis 除了支持集合内的增删查改操作,还支持多个集合取交集,并集,差集 1.2 常用命令 命令 介绍 时间复杂度 sadd …...
Clion开发STM32时使用stlink下载程序与Debug调试
一、下载程序 先创建一个文件夹: 命名:stlink.cfg 写入以下代码: # choose st-link/j-link/dap-link etc. #adapter driver cmsis-dap #transport select swdsource [find interface/stlink.cfg]transport select hla_swdsource [find target/stm32f4x.…...
HarmonyOS:给您的应用添加通知
一、通知介绍 通知旨在让用户以合适的方式及时获得有用的新消息,帮助用户高效地处理任务。应用可以通过通知接口发送通知消息,用户可以通过通知栏查看通知内容,也可以点击通知来打开应用,通知主要有以下使用场景: 显示…...
scrape登录(js逆向)
url:链接 首先登录抓包 可以 看到token进行了加密 可以直接去全局搜索token 可以看到在这里进行了加密 进入encode,处理from后进行加密 首先这一段复制js,缺什么补什么, code cb_utob function(e) {if (e.length < 2) {var r e.cha…...
后台管理系统通用页面抽离=>高阶组件+配置文件+hooks
目录结构 配置文件和通用页面组件 content.config.ts const contentConfig {pageName: "role",header: {title: "角色列表",btnText: "新建角色"},propsList: [{ type: "selection", label: "选择", width: "80px&q…...
RDP协议详解
以下内容包含对 RDP(Remote Desktop Protocol,远程桌面协议)及其开源实现 FreeRDP 的较为系统、深入的讲解,涵盖协议概要、历史沿革、核心原理、安全机制、安装与使用方法、扩展与未来发展趋势等方面, --- ## 一、引…...
Spring Boot - 数据库集成06 - 集成ElasticSearch
Spring boot 集成 ElasticSearch 文章目录 Spring boot 集成 ElasticSearch一:前置工作1:项目搭建和依赖导入2:客户端连接相关构建3:实体类相关注解配置说明 二:客户端client相关操作说明1:检索流程1.1&…...
虚幻UE5手机安卓Android Studio开发设置2025
一、下载Android Studio历史版本 步骤1:虚幻4.27、5.0、5.1、5.2官方要求Andrd Studio 4.0版本; 5.3、5.4、5.5官方要求的版本为Android Studio Flamingo | 2022.2.1 Patch 2 May 24, 2023 虚幻官网查看对应Andrd Studiob下载版本: https:/…...
Flutter开发环境配置
下载 Flutter SDK 下载地址:https://docs.flutter.cn/get-started/install M1/M2芯片选择带arm64字样的Flutter SDK。 解压 cd /Applications unzip ~/Downloads/flutter_macos_arm64_3.27.3-stable.zip执行 /Applications/flutter/bin/flutterManage your Flut…...