【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术QueSearch
【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术Que2Search
目录
文章目录
- 【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术Que2Search
- 目录
- 0. 论文信息
- 1. 研究背景:
- 2. 技术背景和发展历史:
- 3. 算法建模
- 3.1 模型架构
- 3.1.1 双塔与分类
- 3.2 训练
- 3.3 课程训练
- 3.4 评估
- 3.5 加快模型推理
- 3.6 不同模态的融合
- 3.7 模型可解释性
- 4. 系统架构
- 5. 消融研究
- 6. 部署经验
- 6.1 基于嵌入的检索
- 6.2 检索没有万能钥匙
- 6.3 精度、召回率、CPU使用量和延迟
- 6.4 优化连续分页
- 6.5 搜索排序
- 6.6 失败的教训
- 7. 结论
- 文章主要参考文献:
- 后记
0. 论文信息

本文要介绍的这篇论文是Facebook发表在KDD 2021的ADS Track的论文。通常情况下,论文所描述的问题和解决方案,与公司实际业务中遇到的现实问题有密切关联。这篇论文介绍了“Que2Search”,这是Facebook部署的一个查询和产品理解系统(如下图1),用于搜索功能。该系统通过多任务和多模态学习方法来训练查询和产品表示,实现了超过 5% 的绝对离线相关性改进和超过 4% 的在线参与度提升。
ACM KDD(Knowledge Discovery and Data Mining)会议是数据科学、数据挖掘和人工智能领域内的重要国际会议。KDD ADS(Applied Data Science, ADS) Track专注于数据科学的实际应用,征集描述数据挖掘、数据分析、数据科学和应用机器学习在现实世界中的实际应用解决方案和系统的论文。它强调解决或推进对现实世界中部署数据科学技术相关问题的理解。

1. 研究背景:
随着电子商务的兴起,用户对搜索结果的准确性和相关性的要求越来越高。Facebook Marketplace作为一个全球性的在线购物平台,需要提供高质量的搜索体验,以帮助用户快速找到他们想要的产品。
传统上,搜索引擎基于各种术语匹配方法,但这些方法在处理自然语言和多模态数据时存在局限性。
Note: 传统的信息检索系统,通常是基于关键词检索匹配(也称为Term-based matching),优势在于NLP相关技术相对成熟,底层引擎做倒排索引匹配计算飞快,可以实现在亿级别商品数据集下秒级別响应返回。
最近,Huang等人将基于嵌入的检索(Embedding-based Retrieval)引入Facebook搜索:作者们使用基于字符n-gram的稀疏特征来表示文本特征,并依赖宽和深神经网络来构建查询和文档的嵌入表示。这种方法缺乏自然语言的表示,并且由于高计算需求,没有采用基于BERT的模型。在我们之前的工作中,我们构建了最先进的图像识别系统用于产品图像理解。除了产品图像之外,其他文本和分类产品特征,如标题、产品描述、位置和产品类别,也提供了有价值的信息。
Note: 从这里描述来看,Facebook的检索,本身是已经有一套Emebddng的向量化检索,不过没有太强高层次语义信息,只是包含n-gram的稀疏特征,那么可能存在检索的语义相关性不足。
Note: 同时,论文作者们有图像视觉特征embedding的方法用于图像识别的研究经验。不过,没有将文本等多模态信息进行融合。
本文介绍了Que2Search,这是一个查询Query到产品(电商术语: Product, Item 均为商品/产品)相似性模型,它提供了一种建模方法,考虑了全面的多模态特征,并利用XLM编码器对文本特征进行编码。Que2Search在弱监督数据集上进行训练,并与Facebook之前的基线相比,实现了最先进的产品表示性能。Que2Search已被部署到Facebook Marketplace搜索中;它支持基于嵌入的查询到产品检索,并用作排名特征。Que2Search在Facebook Marketplace规模上运行,支持多语言并满足严格的延迟约束。
在构建Que2Search时,作者认为面临了多个挑战:
- 产品描述的噪声:卖方提供的产品描述质量参差不齐。在许多情况下,市场产品的属性缺失或拼写错误。
- 国际化支持:希望构建一个能够在Facebook Marketplace启用的许多语言中表现良好的模型。
- 有效处理多模态性:需要将所有多模态特征(如产品图像和文本信息)有效地整合到一个模型中。
- 严格的延迟约束:需要满足搜索引擎的严格延迟约束,这在使用计算成本较高的基于Transformer的语言模型时尤为具有挑战性。
Note: 在电商场景,尤其是跨境电商场景,产品描述通常是多语言的,标题质量因发布商家质量参差不齐。
Note: 采用XLM方案时,如果不做模型简化和底层推理加速,普遍面临推理延迟问题。
最新的语言理解技术来自基于BERT的Transformer语言模型。在业界,扩展基于BERT的模型并提高推理延迟是一个众所周知的挑战。推理时间对于搜索问题至关重要,我们需要实时地为自由形式的文本查询提供嵌入表示。我们使用2层XLM编码器来转换查询文本,并在产品方面,我们采用多语言方法,使用XLM-R编码器来编码产品的文本字段。我们分享了我们在搜索场景中扩展XLM/XLM-R模型的经验,以在CPU上在1.5毫秒内运行。Que2Search通过引入几种建模技术,在生产应用中展示了收益,包括多阶段课程学习、多模态处理和多任务学习,这些技术共同优化了查询到产品检索任务和产品分类任务。
2. 技术背景和发展历史:
与这篇论文密切相关的研究工作,主要集中在两部分: 基于嵌入的检索和多模态建模。
- Embedding-based Retrieval:传统的基于孪生(Siamese)网络的方法,用于建模成对(pairwise)关系,并且可以有效应用生产应用中的检索。 这种方法提供了一种独立于查询对象嵌入并将其存储在搜索索引中的方法。
Note: Siamese network,孪生网络, 左右网络结构一样,仅是输入不同。
Note: 这里说的孪生网络提供了一种独立于查询对象嵌入并将其存储在搜索索引中的方法,应该是通过训练该网络,可以得到商品得embedding表征,该表征可以直接用于构建向量化召回索引。
最近,一些工业界的工作调查了使用孪生网络和宽与深架构的组合在各种应用中进行语义检索的方法。例如,Yang等人训练了两个塔的神经网络来驱动商业移动应用商店的检索,并引入了混合负样本采样,除了批次内随机负样本外,还从搜索索引中采取随机负样本。Huang等人为Facebook搜索引入了宽与深网络的方法,他们使用了基于字符n-gram的一组稀疏特征来建模文本特征,并且主要处理单模态数据。最近有几项工作使用先进的基于BERT的模型进行搜索应用,例如Guo等人提出了在排名框架内使用深度网络,并使用几种优化来减少计算问题,例如为文档使用较小的模型,以及在服务之前预先计算文档方面的嵌入。
Note: 对于embedding-based retrieval, 相关的研究主要集中在: 负样本采样, 网络结构优化,输入模态信息扩充。
为此, 本文分享了我们利用基于Transformer的语言模型进行文本特征处理的经验,并描述了实用的多模态训练技术,将文本、视觉和分类模态融合在一起。 从本质上来说,本文所提方法实际也是一种Embedding-based Retreival方法。
- 多模态学习:近年来,作者们[4, 15, 18]一直在研究文本和视觉领域的多模态表示解决方案,并在研究数据集上取得了最先进的结果。许多论文使用早期融合在两塔中,这排除了独立部署查询塔模型和文档塔模型的可能性。本文重点介绍双塔Siamese网络与后期融合,其中两塔可以独立计算。
Note: 这篇文章重点 介绍双塔Siamese网络与后期融合,并且特别提出两塔可以独立计算。独立计算的好处,隐含意思是可以并行加速。
- 自然语言处理: 随着BERT[10]的突破,自然语言理解的质量达到了一个新的水平。由于transformers的高计算需求,各种公司探索了BERT优化,包括[11, 21, 22]。我们应用了[11]中的一些类似方法,并讨论了我们为提高生产推理延迟而采取的其他方法。最近,XLM-R(Conneau等人[7])将BERT方法适应于多语言应用。我们使用XLM-R转换文本特征,并使用搜索日志数据进行微调,我们在第3节中分享了我们的解决方案。
Note: 对于BERT,这篇论文作者使用XLM-R转换文本特征,并用搜索日志进行微调。通常预训练的BERT都需要结合实际业务数据进行微调,以提升模型效果。
3. 算法建模

在本节中,我们首先描述模型架构,然后介绍输入特征。接着,我们描述了如何训练和评估模型,包括一个课程训练计划,通过使用不同的损失函数而不改变训练数据收集过程,将更难的负例输入模型。我们还讨论了在多模态训练中遇到的挑战以及我们如何克服它们的解决方案。最后,我们描述了如何使用机器学习可解释性技术来理解模型并识别改进领域。
3.1 模型架构
模型架构如图2所示。
Note: 由于是召回模型,整体结构还是双塔模型,只是在Query Tower和 Document Tower两部分有专门的优化改进。
Note: 有创新的是: 如何对不同模态信息的融合,以及对于Document Tower输出的新增loss约束
对于查询塔,我们使用三个输入特征: 字符三元组[14] Char-3-gram, 多分类特征;搜索者的国家Country,一个单一分类特征;以及原始查询文本Query Text,这是一个文本特征。对于字符三元组,我们对查询的3个字符滑动窗口应用哈希函数(例如MurmurHash),从而得到查询的哈希ID列表。然后我们将哈希ID列表传递给一个EmbeddingBag [24],该函数学习每个哈希ID的嵌入,并执行求和池化以提供每个查询的固定大小表示。我们同样对搜索者国家的单一分类特征应用EmbeddingBag [24]以学习国家表示。我们对原始查询文本应用2层XLM [17]编码器。我们采用最后一层的[CLS]标记的表示作为编码器表示,并将其投影到期望的维度以获得查询的XLM表示。然后我们根据注意力权重融合这三个表示以获得查询的最终表示(Query Embedding)。
Note: 对于三个不同类型的特征,经过处理后得到三个Embedding向量,然后用Attention (Q, K,V)做融合。这里分别是哪个做Q, K, V呢?
对于文档塔,我们的输入特征包括产品标题、描述和图像。我们使用一个共享的6层XLM-R [7]编码器来处理所有文本字段(如标题和描述);编码器的共享有助于提高批量召回@1(见表3)。我们同样对标题和描述的字符三元组多分类特征应用EmbeddingBag [24],如前一段所述。对于每个文档,附加了可变数量的图像。我们取每个附加图像的预训练图像表示(Bell等人[2]),应用一个共享的MLP层和深度集(Zaheer等人[29])融合以获得图像通道表示。与查询塔一样,我们然后使用学习到的注意力权重融合来自不同特征通道的表示以获得文档的最终表示。我们尝试了不同的后期融合技术来融合不同特征通道的表示,例如连接融合(即,连接所有编码器输出,然后跟一个MLP层)和简单的注意力融合(基于学习到的通道权重的通道表示的加权和,如公式1所示,其中∥表示连接)。简单的注意力融合与连接融合相比,显示出更好的指标性能。

Note: 这里有一个细节需要注意,对于Query塔使用的是2层的XLM, 对于Document塔则是使用6层。实际,由于Query通常是需要每次请求到来时,都需要计算一次,因此模型不能太复杂,因此用相对简化的。此外,还有一个原因,在实际应用中,query的长度相对较短,使用相对浅层的XLM对效果损失也不大。
我们使用PyTorch和几个下游库,如Facebook的PyText、Fairseq和多模态框架(MMF)来实现模型。我们使用7e-4的学习率,批量大小为768,并使用Adam优化器。然而,由于我们使用预训练的XLM/XLM-R模型,我们对XLM模块实现了不同的学习率2e-4 - 这意味着XLM模块内的所有参数将与其余参数有不同的学习率。这使我们在验证集上的ROC AUC增加了1%(表4中提到的变化XLM LR)。我们使用了标准的正则化技术,如dropout(dropout率为0.1)和1.0的梯度裁剪。我们使用验证集上的ROC AUC指标进行早期停止,训练3个epochs周期。
Note: 这里提到对于预训练的XLM/XLM-R在后面微调的时候使用的学习率与其他模块的学习率不一样。并且这个学习率要比其他模块的学习率要小。一种考虑估计是避免XLM/XLM-R在微调时,由于其他模块都没有初始化好,出现较大的调整,偏离原始模型参数。如果是这样,那么很自然地引出一个问题: 是否可以先冻结这部分模块,单独训练其他模块,最后再一起微调?
3.1.1 双塔与分类
我们还将双塔模型结构扩展到文档塔中,增加了一个额外的分类任务,如图2右上角所示。对于每个文档,我们从去标识化和聚合的Marketplace搜索日志数据中收集了一组估计与该文档相关的文本查询。我们保留最常见的45k个查询。我们将此问题视为多标签多类分类任务,并使用多标签交叉熵,其中每个正目标设置为1/k,如果文档有k个正标签[19]。我们在第5节中比较了这种多任务架构与普通双塔模型的结果。
Note: 这里对于document塔的输出,直接引入一个MLP做分类,一方面原因是约束学习得到的document embedding具有一定的鉴别能力。
Note: 这里也引出两个新问题: 1) ,如何对文档打标,人力资源消耗情况?2) 如何融合新增的这个分类loss与主目标loss?
3.2 训练
训练数据: 在传统的监督学习中,我们需要训练数据来包含正面和负面的例子。然而,在我们的训练设置中,我们只采用正面的训练(查询,文档)对,并自动从批次中的其他文档中获得负面例子。通过搜索日志可以收集正面的(查询,文档)对。具体来说,我们使用类似于[2, 25]中的数据收集模式:我们通过以下事件序列创建文档-查询对的数据集:(1) 用户搜索查询,(2) 点击产品,(3) 向市场卖家发送消息,以及 (4) 卖家回复。如果所有4个事件在短时间内(例如24小时)发生,则用户很可能找到了他们想要的东西,因此将该查询视为与文档相关。我们无法访问消息内容,只知道用户与卖家进行了互动。我们称这些用户互动的序列为in-conversation [25]。
批次负样本:我们使用了几种方法来生成负样本。为了解释批次负样本的工作原理,我们将解释在单个批次训练期间发生了什么,批量大小为B。在每个批次中,我们获得查询嵌入张量 { q i } i = 1 B \{q_i \}_{i=1}^{B} {qi}i=1B,嵌入维度为 D D D,同样我们也获得文档嵌入张量 { d j } j = 1 B \{d_j \}^B_{j=1} {dj}j=1B,大小相同。然后我们计算余弦相似度矩阵 { c o s ( q i , d j ) } i , j = 1 B \{cos(q_i,d_j) \}_{i,j=1}^B {cos(qi,dj)}i,j=1B。余弦相似度矩阵表示批次内每一对可能的查询文档相似度,矩阵的行属于查询,列属于文档。我们将这视为多类分类问题,类别数为B,文档 d i d_i di是查询 q i q_i qi的真类别(如图4所示的绿色网格),而其他文档 d j , j ≠ i d_j,j ≠ i dj,j=i是q_i的负例。我们使用缩放的多类交叉熵损失(公式2)来优化我们的网络。在训练期间,我们发现有一个比例尺的损失对于收敛很重要。在我们的用例中,我们选择了15到20之间的比例尺。
L i = − log exp ( s ⋅ cos ( q i , d i ) ) ∑ j = 1 B exp ( s ⋅ cos ( q i , d j ) ) 其中s表示比例尺 ( 2 ) L_i = -\log \frac{\exp(s \cdot \cos(q_i,d_i))}{\sum^B_{j=1} \exp(s \cdot \cos(q_i,d_j))} \text{ 其中s表示比例尺} \quad (2) Li=−log∑j=1Bexp(s⋅cos(qi,dj))exp(s⋅cos(qi,di)) 其中s表示比例尺(2)
我们还尝试了对称缩放交叉熵损失(公式3)。这个损失函数优化了查询到文档的检索,也优化了文档到查询的检索。虽然这个损失在我们本文讨论的查询到文档双塔模型中既没有提高也没有降低整体系统指标,但在我们训练文档到文档双塔模型的不同用例中,这种对称损失在评估数据集上显示出2%的ROC AUC改进。
L i = − 1 2 ( log exp ( s ⋅ cos ( q i , d i ) ) ∑ j = 1 B exp ( s ⋅ cos ( q i , d j ) ) + log exp ( s ⋅ cos ( q i , d i ) ) ∑ j = 1 B exp ( s ⋅ cos ( q j , d i ) ) ) ( 3 ) L_i = -\frac{1}{2} \left( \log \frac{\exp(s \cdot \cos(q_i,d_i))}{\sum^B_{j=1} \exp(s \cdot \cos(q_i,d_j))} + \log \frac{\exp(s \cdot \cos(q_i,d_i))}{\sum^B_{j=1} \exp(s \cdot \cos(q_j,d_i))} \right) \quad (3) Li=−21(log∑j=1Bexp(s⋅cos(qi,dj))exp(s⋅cos(qi,di))+log∑j=1Bexp(s⋅cos(qj,di))exp(s⋅cos(qi,di)))(3)
3.3 课程训练
除了在第3.2节中描述的批内负例采样外,我们还设计了一个课程训练计划,其中双塔模型在第二阶段训练中被输入更难的负例,并在验证集上实现了超过1%的ROC AUC绝对增益(见表4)。
在训练的第一阶段,我们使用批内负例训练模型,并在验证集上的ROC AUC指标上通过早期停止让模型收敛。我们希望在第二阶段训练中给模型输入“更难的”负例。虽然传统上这意味着要运行一个单独的数据管道来找到难的负例,并将这个数据集输入模型,我们建议使用批次内的负例:我们像通常一样得到形状为(B * B)的余弦相似度矩阵。对于每一行,我们现在提取除了对角线分数之外的最高分数作为硬负样本;我们将其列索引表示为 n q i = arg max B j ≠ i c o s ( q i , d j ) n^{qi} = \argmax_B^{j≠i}cos(q_i,_dj) nqi=Bargmaxj=icos(qi,dj)。这将有助于以格式(qi,di,dnqi)生成训练样本。我们探索了缩放的二元交叉熵损失(公式4)和边际排名损失(公式5),并观察到边际在0.1到0.2之间的边际排名损失效果最好。批内硬负例采样还减少了在寻找硬负例时额外的CPU使用以及维护另一个离线数据流水线的成本。最初,使用更难负例的课程训练效果不佳。通过实验,我们发现确保第一阶段训练收敛后再开始第二阶段训练至关重要
。此外,我们观察到在边际排名损失3中,reduction=sum比reduction=mean效果更好。
L i = − log ( σ ( s ⋅ cos ( q i , d i ) ) ) + log ( 1 − σ ( s ⋅ cos ( q i , d n q i ) ) ) ( 4 ) L_i = -\log(\sigma(s \cdot \cos(q_i,d_i))) + \log(1 - \sigma(s \cdot \cos(q_i,d_nq_i))) \quad (4) Li=−log(σ(s⋅cos(qi,di)))+log(1−σ(s⋅cos(qi,dnqi)))(4)
L i = max ( 0 , − [ cos ( q i , d i ) − cos ( q i , d n q i ) ] + m a r g i n ) ( 5 ) L_i = \max(0, -[\cos(q_i, d_i) - \cos(q_i,d_nq_i)] + margin) \quad (5) Li=max(0,−[cos(qi,di)−cos(qi,dnqi)]+margin)(5)
图3是多阶段训练的ROC AUC评估指标曲线之一,我们可以看到在第二阶段训练中ROC AUC的增长。


Note: 一开始使用困难负样本进行课程学习没有取得收益。通过实验发现,问题的关键在于必须先保证第一阶段收敛 ,然后才能开始执行第二阶段的课程学习 。除此之外,对所有样本对求总体的margin rank loss损失时,使用sum 来做reduce比用mean更好。
3.4 评估
在通过在线流量进行A/B测试之前,我们离线评估我们的模型候选,并基于批量召回@K、ROC AUC和KNN召回率和精确度选择最佳模型。
批量召回@K:此度量标准在训练期间容易计算,并且是模型优化最接近的指标,使我们能够快速迭代建模思路。
ROC AUC:在Facebook Marketplace,我们定期收集人类评级数据以评估搜索引擎的质量。人类评级评估是在公开可见的产品上进行的,查询数据在评估程序开始之前被去标识化和聚合。给定一组预先选定的搜索查询,通常有成千上万个,我们抓取搜索引擎的结果并发送给人类评估员进行评估:1表示满足查询搜索意图的相关文档,0表示不相关的。这为我们提供了一个带有标签的(查询,文档)对的评估数据集。有了推断出的查询和文档嵌入的余弦相似度作为分数,我们计算ROC AUC。ROC AUC在训练期间用作早期停止的验证指标,并且在模型训练后用作离线评估指标。人类评级数据帮助我们评估对相关性和搜索质量的潜在影响。
KNN召回@K:另一方面,我们通过KNN召回和精确度评估对在线参与度的潜在影响。KNN召回@K是一个离线评估指标,用于在训练模型后计算评估数据集。我们使用数周的在线参与数据(定义在3.2节中的in-conversation)来训练我们的模型,并在将来看不见的日子的数据上进行评估:给定一个经过训练的双塔模型和一个评估数据集,推断查询嵌入和文档嵌入。给定查询嵌入,我们使用Faiss(Johnson等人[16])K最近邻来检索文档嵌入空间中与N个相似的文档,并检查原始查询的真文档是否在检索到的前K个文档中。我们观察到KNN召回@K指标与基于嵌入的检索的在线性能密切相关。
3.5 加快模型推理
在Facebook,我们的系统需要处理大量的QPS并以闪电般的速度为我们庞大的用户群提供服务。搜索查询由用户实时请求,我们期望在几百毫秒内返回结果。查询侧模型需要足够快以满足系统要求。因此,我们对各种变压器架构进行了实验,以处理延迟和准确性之间的权衡。我们对查询塔中的两个XLM模型进行了实验:一个有2层,4个注意力头和128个句子嵌入维度,另一个有3个编码器层,4个注意力头和256个句子嵌入维度。我们发现前者的P99延迟为1.5ms,而后者的P99延迟为3.5ms,而性能提升很小。我们将查询序列长度限制在Marketplace搜索中所有查询长度的99百分位。我们使用了0.1的dropout和3倍句子嵌入维度的前馈嵌入维度。我们为所有实验使用了一个大小为150k的SentencePiece词汇表。文档塔模型对执行速度的要求较低,因为文档侧嵌入可以离线预先计算,或者在文档创建后近实时计算,然后输入到搜索系统后端。我们在第4节中分享了更多关于服务系统优化的细节。我们还利用Torch即时编译(JIT)来加速我们的推理。
3.6 不同模态的融合
我们尝试了各种方法来混合不同的模态,例如文本模态和图像模态。默认方法是简单的注意力融合,如公式1所述。我们称这种方法为基线模态处理。梯度混合[26]是一种优化技术,用于在多模态设置中平衡不同模态,并减少对主导模态的过度拟合。在梯度混合中,我们训练M+1个模型:M个单模态模型和一个完整的多模态模型,其中M是模态的数量。我们为每个模型计算损失,并以估计的权重作为最终损失的加权和。背后的直觉是让模型即使在模态不出现时也能学习,使模型对稀疏输入特征更加稳健。我们通过一次保持一个塔具有全部模态,将梯度混合扩展到双塔架构。例如在图2中,我们说查询塔有m=2个模态:查询和国家;文档塔有n=3个模态:标题、描述和图像。然后我们得到以下公式化的m+n+1个损失,其中 ψ i 1 1 m {ψ^1_i}^m_1 ψi11m和 { ψ j 2 } 1 n \{ψ^2_j\}^n_1 {ψj2}1n分别是左塔和右塔的单模态深度网络,⊕表示融合操作:
L m u l t i = L ( c o s ( ψ 1 1 ⊕ . . . ⊕ ψ 1 m , ψ 2 1 ⊕ . . . ⊕ ψ 2 n ) ) L_{multi} = L(cos(ψ1_1 ⊕ ... ⊕ ψ1_m,ψ2_1 ⊕ ... ⊕ ψ2_n)) Lmulti=L(cos(ψ11⊕...⊕ψ1m,ψ21⊕...⊕ψ2n))
L 1 i = L ( c o s ( ψ 1 i , ψ 2 1 ⊕ . . . ⊕ ψ 2 n ) ) i ∈ [1,m] L_{1i} = L(cos(ψ1_i ,ψ2_1 ⊕ ... ⊕ ψ2_n)) \text{ i ∈ [1,m]} L1i=L(cos(ψ1i,ψ21⊕...⊕ψ2n)) i ∈ [1,m]
L 2 j = L ( c o s ( ψ 1 1 ⊕ . . . ⊕ ψ 1 m , ψ 2 j ) ) j ∈ [1,n] L_{2j} = L(cos(ψ1_1 ⊕ ... ⊕ ψ1_m,ψ2_j )) \text{ j ∈ [1,n]} L2j=L(cos(ψ11⊕...⊕ψ1m,ψ2j)) j ∈ [1,n]
模态是一个松散定义的术语。我们可以将每个输入特征通道视为一个单独的模态。那样的话,在图2中,查询塔有m=3个模态,文档塔有n=5个模态。我们也可以将相关特征组合在一起作为一个模态。例如,如果产品描述特征缺失,那么原始文本和描述字符三元组都不出现。我们可以将这两个特征组合在一起。然后,在文档塔中,我们只有n=3个模态:标题、描述和图像。我们在表1中分享了我们在梯度混合上的实验结果。我们没有观察到使用个体特征通道的梯度混合在离线指标上有统计上的显著改进,并且观察到分组特征梯度混合在ROC AUC上提供了+0.91%的性能提升,梯度混合仅在训练期间应用,并且在推理期间我们像往常一样运行完整的多模态模型。
3.7 模型可解释性
3.7.1 XLM编码器是否为查询塔增加价值? 一个常问的问题是,对于查询塔是否有用XLM编码器。有人可能会认为查询通常是短语,对于Marketplace来说,查询是头部沉重的(大部分流量由一小套查询主导)。直观地说,字符三元组可能已经足够代表查询了。我们使用注意力权重来探索这一问题的答案。在查询塔中,我们在查询字符三元组通道和查询XLM编码器通道之间有一个注意力机制。注意力权重(总和为1)在公式1中制定。我们提取这些注意力权重,并尝试解释模型更关注哪个特征。我们发现XLM编码器的平均注意力权重为0.64,而EmbeddingBagEncoder为0.36。当我们将注意力权重作为查询长度的函数进行绘制时,我们发现模型在查询长度小于5个字符时更关注字符三元组,但XLM编码器的权重在更长的查询中占主导地位。


3.7.2 特征重要性。 我们认为双塔模型更像是一个“搜索”问题,而不是一个“分类”问题。因此,我们提出了一种不同的方法来计算双塔模型的特征重要性,即在保持另一个不变的情况下,在塔内计算特征重要性。我们使用一种称为特征消融的特征重要性技术 - 其中每个输入特征被替换为基线(零张量,或来自其他随机训练点的特征值),并计算输出的变化。我们在表2中展示了特征重要性结果。正如我们所看到的,XLM在两个塔的所有文本特征中占主导地位。另一方面,字符三元组特征也提供了非常显著的增益。这与我们在第3.7节中的研究一致,即字符三元组特征对短查询的贡献最大。在文档塔中,我们使用了来自GrokNet [2]的预训练图像嵌入,这是Facebook最先进的图像识别系统。从表2中我们可以看到,图像模态为模型提供了重要的价值。此外,我们使用了深度集 [29] 融合多个产品的图像,它特别对于像车库销售这样在一个列表中出售多个产品的产品提供了额外的好处。

4. 系统架构
我们现在描述了Que2Search的系统架构,这是Facebook部署的大规模产品理解系统,用于Marketplace。Que2Search已投入生产,并旨在实时创建的产品上运行。模型推理在称为Predictor的机器云中计算。Predictor提供了部署模型的功能,并提供了一个API来调用模型与一组输入特征。Predictor是一个云服务,可以根据需要进行扩展。给定对Predictor的调用,查询嵌入在1.5毫秒的P99内计算,文档嵌入在7毫秒内计算。我们使用Unicorn存储产品嵌入,连同其他产品索引,用于搜索检索和排名。文档侧模型服务。在产品创建和更新时,将对Predictor进行异步调用,以生成其产品嵌入,并将嵌入向量实时更新到搜索索引中,以准备产品进行检索和排名。换句话说,当搜索查询发出时,产品嵌入已经被预先计算并索引,这使得在许多产品候选中计算查询产品相似度变得可行。此外,Unicorn进一步聚类和量化[16]嵌入空间以加速计算。我们的基础设施还支持向后填充旧产品的嵌入。在部署了新模型之后,通常在24小时内将嵌入回填到足够数量的文档的搜索索引中,以进行实验。查询时间模型服务。在推理时,查询流程遵循调用我们的后端自然语言理解服务(NLP服务)的路径,该服务实时调用Predictor服务。NLP服务将嵌入缓存在内存中以加快服务响应速度,并节省Predictor计算。然后查询嵌入被传递给Unicorn搜索引擎后端进行产品检索。Unicorn搜索引擎能够进行近似最近邻(ANN)检索和布尔约束,这意味着我们可以,例如,要求检索与查询最相关/匹配的产品,同时要求产品在用户指定位置的50英里半径内,以及其他传统术语匹配表达式。结果然后传回几个排名阶段。我们在所有排名阶段使用两塔模型产生的余弦相似度分数作为排名特征。
5. 消融研究
我们进行了线下消融研究,以确认每一步的有效性。我们的基线模型[14]使用深度和宽度架构,并在产品的文本属性上应用字符三元组特征;该模型已在生产中部署,并取得了生产收益。在表3中,我们分享了比较新模型和功能添加的消融研究结果。我们看到,添加基于XLM/XLM-R的原始文本特征转换器显著提高了批量Recall@1(参见第3.4节)。类似的影响来自图像特征,我们应用了多达五个产品图像的深度集合融合[29]。文本转换器和图像特征每个都提供了独立的值;文本转换器和图像特征的联合引入使批量Recall@1相对提高了9.23%。与连接融合相比,塔模态上的简单注意力融合(公式1)提高了0.15%;在产品文本字段上使用共享XLM-R编码器提供了额外的0.18%改进(见表3)。Que2Search双塔模型还在人类评级数据集上显著提高了ROC AUC。评估结果如表4所示。图像和文本特征都提供了额外的价值,其中原始文本的XLM编码器贡献最大,ROC AUC绝对提高了2.1%。第二大贡献者来自课程训练,ROC AUC提高了0.8%绝对值(参见第3.3节)。XLM模块与其他模型部分的不同学习率(参见第3.1节)有助于进一步提高质量,相对提高了1%。在文档塔中添加分类任务头(参见第3.1节)在ROC AUC上提供了0.61%的相对改进。使用分组特征的梯度混合(参见第3.7节)使模型对模态缺失的情况更加鲁棒,并在ROC AUC上提供了额外的0.91%相对改进。总之,Que2Search比现有的Facebook产品理解系统在绝对ROC AUC上提高了5%以上。

6. 部署经验
我们在Facebook Marketplace上部署了Que2Search,每天处理数以亿计的产品搜索。此外,我们通过将其作为Facebook Marketplace的Embedding-based Retrieval和排名栈的一个有机组成部分进行集成,通过各种优化在精度、召回率、CPU使用量和延迟方面实现了可扩展性。在线A/B测试表明,Que2Search使搜索产品参与度增加了4%以上,其中约2.8%来自语义检索,1.24%来自排名。表5显示了不同技术对搜索产品参与度增益的更详细分解。
6.1 基于嵌入的检索
随着Que2Search的引入,我们能够使用Unicorn的原生支持进行更多基于语义的检索(EBR)。更具体地说,如第4节所述,我们将查询塔模型部署到NLP服务以实时生成查询嵌入,并将产品塔模型部署在异步更新路径上,以便在产品创建和更新时更新Unicorn索引。当用户发出产品搜索时,其搜索查询将被转换为嵌入向量,以在产品嵌入空间中执行ANN搜索,使用Unicorn的NN-operator检索余弦距离在阈值内的产品作为结果。
6.2 检索没有万能钥匙
注意到我们将Que2Search作为整个Marketplace搜索检索的一个有机组成部分进行部署,而不是将其作为唯一的搜索检索手段。整个检索过程的其他关键组件包括与ANN搜索语义并行的传统基于标记的检索,以及其他可能的约束,如位置、社交[13]和产品类别。这是使用Unicorn的查询语言[8]无缝完成的(即,ANN和基于标记的检索在OR下组织,约束被分组到一个高级别的AND中)。从经验上,我们发现将EBR视为解决语义匹配的组件是有益的,而不是将其视为解决所有检索问题的唯一解决方案。以下是几个原因:
-(1)尽管Que2Search具有位置感知能力,但在ANN搜索中很难指定明确的位置约束,这对于像Facebook Marketplace这样的本地商务搜索引擎至关重要
- (2)ANN搜索无法处理用户选定的过滤器,这是任何商务搜索引擎的关键功能
- (3)由于性能问题,ANN搜索很难做到穷尽,而基于标记的检索提供了一种廉价的替代方案,可以更全面地覆盖简单案例。
6.3 精度、召回率、CPU使用量和延迟
在部署Que2Search时,需要优化的维度包括精度、召回率、CPU使用量和延迟。在Unicorn的NN-operator方面,它们可以转化为两个超参数 - n_prob和raduis。由于Unicorn采用聚类和产品量化方法[14, 16],要扫描的簇的数量决定了ANN搜索的穷尽程度,由n_prob控制。通常,扫描的簇越多,精度越高,但CPU使用量也越大。当扫描到一个产品时,会计算其与查询向量的余弦距离。然后raduis参数控制阈值以确定是否检索此产品。尽管可以使用在线A/B测试对超参数进行网格搜索,但我们希望最小化用户影响,尤其是在参数选择不当时会降低用户体验。因此,我们使用黄金查询集来模拟检索,以减少在线A/B测试前的合理良好候选数量。黄金集包括使用分层抽样的搜索查询对Marketplace搜索进行抓取的搜索结果。我们还要求评估员判断结果是否相关。有了这个黄金集,我们可以模拟使用不同的raduis的检索,并通过检查返回的相关数量来确定检索的侵略性。然而,在线A/B测试仍然至关重要,因为除了需要衡量真正的用户参与度外,有时CPU使用量和延迟很难准确模拟,因为它们与用户行为密切相关。

6.4 优化连续分页
关于用户行为如何影响CPU使用量度量的一个关键观察是围绕连续分页的,这是指用户不断滚动搜索结果页面以触发更多检索请求,以便可以展示更多产品。显然,如果用户对顶部结果(等同于,第一个检索请求提供了很好的精度)感到满意,我们将节省CPU使用量,因为任何随后的请求将不需要被触发。然而,我们也知道,用户对第一个请求的延迟非常敏感 - 如果在一段时间内没有显示结果,整个搜索可能会被放弃。这意味着我们不能为了精度而使第一个请求过于昂贵。总结来说,连续分页可以作为权衡精度、召回率和CPU使用量的杠杆,但受到延迟等限制的约束。有了这个观察,我们为第一个请求启用了保守的n_prob和raduis配置的EBR,以便我们可以保持对延迟的高门槛,并使随后请求中的EBR更加昂贵,以进一步优化参与度。基于在线A/B测试结果,我们发现这种动态ANN配置与始终保持相同配置相比,产生了相同的参与度增益,但大大减少了CPU使用周期和延迟。实际上,如果没有这种优化,我们可能无法在生产中启用Que2Search,因为延迟问题。
Note: 首页请求,翻页不触发查询,需要结合查询缓存技术。
Note: 这里一个有价值的经验: 作者采用了动态ANN参数的方式,第一个请求的时候关注性能,弱化准召,可以调整n_prob,radius实现;后续翻页的请求的时候,则可以在一定时延约束下,更关注准召。
6.5 搜索排序
根据表4,我们知道双塔模型的查询-产品余弦相似度分数是人类评级数据集的良好预测指标。鉴于在检索阶段就可以得到相似度分数,我们也将其暴露给搜索排序以进一步利用Que2Search。Facebook Marketplace的搜索排名遵循两阶段计划,在该计划中,轻量级GBDT排序器在各个索引服务器实例上运行,以从每个分片中选择最佳结果,更昂贵的DLRM类模型用于进一步选择最佳结果。为了在排序中利用Que2Search,我们简单地将分数添加到两个阶段的排序中。我们检查了特征重要性,并发现余弦距离特征在两个阶段都是最重要的,并且在线A/B测试表明,这种简单的特征添加产生了统计上显著的参与度改进,如表5所示。
Note: 向量化召回的算分应用到后续的排序链路,提升排序模型效果。
6.6 失败的教训
部署Que2Search以提高搜索质量是一个试错过程,我们遇到了很多最初看起来违反直觉的现象。精确度很重要:精确度显然在各种机器学习任务中都非常重要,但我们不能仅用召回率作为搜索检索设置中的唯一标准,这一点不太直观。换句话说,我们发现放宽ANN搜索的阈值可能会产生更糟的结果,尽管理论上,它不应该影响检索到的总的好结果的数量。进一步分析表明,这是由于检索和排名模型之间的不一致性 - 我们的排名模型无法处理通过更宽松的阈值检索到的噪声结果。提高多阶段模型之间的一致性本身是一个研究课题[5, 28],我们不会在这里详细说明。最终,我们只是进行了在线和离线的参数扫描,以确定最佳阈值。相关性不够:提高检索和排名模型之间一致性的一个简单方法是简单地使用双塔模型的余弦相似度作为排名分数。这种方法的一个好处是,它保证了所有由于更宽松的阈值而检索到的结果都可以排在底部。实际上,我们通过将基于GBDT的第一阶段模型替换为余弦相似度进行了实验,尽管我们发现NDCG的相关性有了显著提高,但在线参与度却大幅下降。这可能是因为双塔模型是被训练来优化查询-产品相似性,而不是优化参与度,而GBDT模型更注重参与度。尽管可以将参与度特征添加到双塔模型中,但这不是一个微不足道的扩展,因为我们的许多重要参与度特征是基于搜索者和产品信号的手工调整的密集特征,这违反了双塔模型的独立/后期融合属性。
Note: 在召回环节,离线的Precision同样很重要 。召排不一致性,对最终的转化效果有影响。
Note: 做召回模型,只保证相关性远远不够,需要关注多种指标!!!
7. 结论
我们介绍了构建一个全面的查询和产品理解系统Que2Search的方法。我们介绍了多任务和多模
态训练的创新思想,以学习查询和产品表示。通过Que2Search,我们实现了超过5%的绝对离线相关性改进和超过4%的在线参与度提升。我们分享了在搜索用例中调整和部署基于BERT的查询理解模型的经验,并实现了99百分位的1.5毫秒推理时间。
文章主要参考文献:
- Huang et al. [14]:介绍了Facebook搜索中的Embedding-based Retrieval方法。
- Lample and Conneau [17]:提出了XLM编码器,用于跨语言的文本特征转换。
- Conneau et al. [7]:XLM-R,适应BERT方法于多语言应用。
- Bell et al. [2]:提出了GrokNet,用于产品图像理解。
- Zaheer et al. [29]:提出了Deep Sets,用于处理多模态数据。
后记
如果您对我的博客内容感兴趣,欢迎三连击(点赞,关注和评论),我将持续为您带来计算机人工智能前沿技术(尤其是AI相关的大语言模型,深度学习,计算机视觉相关方向)最新学术论文及工程实践方面的内容分享,助力您更快更准更系统地了解 AI前沿技术。
相关文章:
【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术QueSearch
【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术Que2Search 目录 文章目录 【电商搜索】现代工业级电商搜索技术-Facebook语义搜索技术Que2Search目录0. 论文信息1. 研究背景:2. 技术背景和发展历史:3. 算法建模3.1 模型架构3.1.1 双塔与分类 …...
海滨体育馆管理系统:SpringBoot实现技巧与案例
2系统关键技术 2.1JAVA技术 Java是一种非常常用的编程语言,在全球编程语言排行版上总是前三。在方兴未艾的计算机技术发展历程中,Java的身影无处不在,并且拥有旺盛的生命力。Java的跨平台能力十分强大,只需一次编译,任…...
个人计算机与网络的安全
关于 wifi 大家都知道 wifi 已经使用了 wpa3 非常安全 但很多人不知道 pin 和 wps 这两项有漏洞 我发现很多用户都简单设置了这两项 他们的设置 使他们的网络出现了漏洞 关于 国产的 linux 老实说全是漏洞 默认开启 很多服务 但初始化的设置都有漏洞 关于 系统安全 老…...
AIGC教程:如何用Stable Diffusion+ControlNet做角色设计?
前言 对于生成型AI的画图能力,尤其是AI画美女的能力,相信同行们已经有了充分的了解。然而,对于游戏开发者而言,仅仅是漂亮的二维图片实际上很难直接用于角色设计,因为,除了设计风格之外,角色设…...
5V继电器模块详解(STM32)
目录 一、介绍 二、模块原理 1.原理图 2.引脚描述 3.工作原理介绍 三、程序设计 main.c文件 relay.h文件 relay.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 继电器(Relay),也称电驿,是一种电子控制器件,它具有控制系统…...
探究Spring的单例设计模式--单例Bean
Spring的单例设计模式 在Spring框架中,单例设计模式是一种常见且重要的设计模式,主要用于确保在应用程序的生命周期中仅创建一个特定的Bean实例 一、什么是单例设计模式? 单例设计模式是一种创建型设计模式,确保一个类只有一个…...
js基础速成-Set、Map
集合(Set) 集合是元素的集合,只能包含唯一元素 创建一个空集合 const companies new Set() console.log(companies)Set(0) {}从数组创建集合 const languages [英语,芬兰语,英语,法语,西班牙语,英语,法语, ]const setOfLanguages new …...
手机软件何时统一——桥接模式
文章目录 手机软件何时统一——桥接模式凭什么你的游戏我不能玩紧耦合的程序演化合成/聚合复用原则松耦合的程序桥接模式桥接模式基本代码 手机软件何时统一——桥接模式 凭什么你的游戏我不能玩 时间:5月31日20点 地点:大鸟房间 人物…...
【Nacos 架构 原理】服务发现模块之Nacos注册中心服务数据模型
文章目录 服务(Service)和服务实例(Instance)定义服务服务元数据定义实例实例元数据持久化属性 集群定义集群 生命周期服务的生命周期实例的生命周期集群的生命周期元数据的生命周期 服务(Service)和服务实…...
基于微信小程序爱心领养小程序设计与实现(源码+参考文档+定制开发)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
【数据库】 MongoDB 用户分配新的角色和权限
在 MongoDB 中,可以通过简单的命令为用户分配新的角色和权限。这对于调整用户的访问能力和管理数据库安全至关重要。以下是如何为用户分配新的角色和权限的详细步骤。 1. 使用 MongoDB Shell 分配角色 1.1 修改用户角色 要为现有用户分配新的角色,可以…...
加速 Python for 循环
在 Python 编程中,for 循环是开发者常用的工具之一,但它的执行速度经常让人感到不满。幸运的是,有许多方法可以显著提高 for 循环的效率。 本文将介绍几种简单而高效的优化技巧,帮助你加速Python for 循环,速度提升从…...
计算机毕业设计 基于Python国潮男装微博评论数据分析系统的设计与实现 Django+Vue 前后端分离 附源码 讲解 文档
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...
React 表单与事件
React 表单与事件 React 是一个用于构建用户界面的 JavaScript 库,它通过组件化的方式来提高开发效率和代码的可维护性。在 React 应用中,表单和事件处理是核心功能之一,它们允许用户与应用程序进行交互。本文将深入探讨 React 中的表单处理…...
Appium独立测试自动化初始化脚本
1、查看环境初始化参数 确保appium已经开起来了,设置ip ,并点击启动 打开夜神模拟器,点击工具--设置 最下面的版本说明,双击进去 版本号这里再去单击。 直到进入到开发者模式。 可能我们不是开发者模式打开的状态,所以软件访问模…...
Nginx反向代理配置支持websocket
一、官方文档 WebSocket proxying 为了将客户端和服务器之间的连接从HTTP/1.1转换为WebSocket,使用了HTTP/1.1中可用的协议切换机制(RFC 2616: Hypertext Transfer Protocol – HTTP/1.1)。 然而,这里有一个微妙之处:由于“升级”…...
C# 游戏引擎中的协程
前言 书接上回,我谈到了Unity中的协程的重要性,虽然协程不是游戏开发“必要的”,但是它可以在很多地方发挥优势。 为了在Godot找回熟悉的Unity协程开发手感,不得不自己做一个协程系统,幸运的是,有了Unity的…...
如何封装微信小程序中的图片上传功能
文章目录 前言一、需求分析与设计思路二、上传图片功能封装三、页面调用示例四、功能改进与扩展4.1 压缩图片4.2 上传进度4.3 重试机制 五、总结 前言 在微信小程序开发中,图片上传功能是一个十分常见的需求,不管是社交分享、商城中的商品图片上传&…...
被问界/理想赶超!奔驰CEO再度“出马”,寻找中国外援
来自中国车企的全方位、持续施压,让大部分外资车企开始寻求更多的本地化合作来实现技术升级。传统豪华品牌也同样如此。 本周,知情人士透露,梅赛德斯奔驰首席执行官Ola Kllenius计划再次访问中国,目的是进一步寻求和扩大与本地技术…...
魔改xjar支持springboot3,
jar包加密方案xjar, 不支持springboot3。这个发个魔改文章希望大家支持 最近公司需要将项目部署在第三方服务器,于是就有了jar包加密的需求,了解了下目前加密方案现况如下: 混淆方案,就是在代码中添加大量伪代码,以便隐藏业务代…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
