终于,BERT的替代品出现了:ModernBERT 隆重登场
这个博客文章介绍了 ModernBERT,这是一个最先进的仅编码器模型系列,代表了对旧版编码器的全面改进,具有 8192 的序列长度、更好的下游性能和更快的处理速度。
ModernBERT 可作为任何类似 BERT 的模型的即插即用替代品,提供 base (139M 参数) 和 large (395M 参数) 两种模型大小。
点击查看如何使用 `transformers` 使用这些模型
ModernBERT 将包含在 transformers
的 v4.48.0 版本中。在此之前,它需要从 main 安装 transformers:
pip install git+https://github.com/huggingface/transformers.git
由于 ModernBERT 是一个掩码语言模型(MLM),你可以使用 fill-mask 管道或通过 AutoModelForMaskedLM 加载它。要将 ModernBERT 用于分类、检索或 QA 等下游任务,请按照标准的 BERT 微调方法进行微调。
⚠️ 如果你的 GPU 支持,我们建议使用 ModernBERT 和 Flash Attention 2 以达到最高效率。为此,请如下所示安装 Flash Attention,然后像平常一样使用模型:
pip install flash-attn
使用 AutoModelForMaskedLM:
from transformers import AutoTokenizer, AutoModelForMaskedLM
model_id = "answerdotai/ModernBERT-base"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForMaskedLM.from_pretrained(model_id)
text = "The capital of France is [MASK]."
inputs = tokenizer(text, return_tensors="pt")
outputs = model(**inputs)
# To get predictions for the mask:
masked_index = inputs["input_ids"][0].tolist().index(tokenizer.mask_token_id)
predicted_token_id = outputs.logits[0, masked_index].argmax(axis=-1)
predicted_token = tokenizer.decode(predicted_token_id)
print("Predicted token:", predicted_token)
# Predicted token: Paris
使用管道:
import torch
from transformers import pipeline
from pprint import pprint
pipe = pipeline(
"fill-mask",
model="answerdotai/ModernBERT-base",
torch_dtype=torch.bfloat16,
)
input_text = "He walked to the [MASK]."
results = pipe(input_text)
pprint(results)
注意: 与一些早期的 BERT 模型不同,ModernBERT 不使用 token type IDs。大多数下游用法与 Hugging Face Hub 上的标准 BERT 模型相同,只是你可以省略 token_type_ids 参数。
引言
BERT 发布于 2018 年(在 AI 年代里已经是几千年前了!),但它至今仍被广泛使用:事实上,它目前是 Hugging Face Hub 上下载量第二大的模型,每月下载量超过 6800 万次,仅次于另一个为检索微调的编码器模型。这是因为它仅编码器架构使其非常适合每天出现的各种实际问题,如检索(例如 RAG)、分类(例如内容审核)和实体提取(例如用于隐私和法规遵从)。
终于,6 年后,我们有了替代品!今天,我们 Answer.AI 和 LightOn (以及朋友们!) 正在发布 ModernBERT。ModernBERT 是一个新的模型系列,在 速度 和 准确性 方面都优于 BERT 及其同类产品。该模型借鉴了近年来大型语言模型(LLM)的数十项进展,并将其应用于 BERT 式模型,包括对架构和训练过程的更新。
我们期望看到 ModernBERT 成为现在部署仅编码器模型的众多应用中的新标准,例如在 RAG 管道(检索增强生成)和推荐系统中。
除了速度更快、更准确之外,ModernBERT 还将上下文长度增加到 8k 个 token(而大多数编码器仅为 512 个),并且是第一个在其训练数据中包含大量代码的仅编码器模型。这些功能开辟了以前无法通过开放模型访问的新应用领域,例如大规模代码搜索、新的 IDE 功能以及基于全文检索而非小块的新型检索管道。
但是,为了解释我们到底做了什么,让我们先退一步,看看我们从哪里来。
仅解码器模型
最近 LLM 的高调进展都集中在 GPT、Llama 和 Claude 等模型上。这些是仅解码器模型或生成模型。它们生成类人内容的能力催生了惊人的新 GenAI 应用领域,如生成的艺术和交互式聊天。这些引人注目的应用吸引了大量投资,资助了蓬勃发展的研究,并导致了快速的技术进步。我们所做的,本质上是将这些进展移植回仅编码器模型。
为什么?因为许多实际应用需要一个精简且强大的模型!而且它不需要是一个生成模型。
更直白地说,仅解码器模型对于许多工作来说太大、太慢、太私有且太贵。考虑到最初的 GPT-1 是一个拥有 1.17 亿参数的模型。Llama 3.1 模型,相比之下,有 4050 亿个参数,其技术报告描述的数据合成和管理方法过于复杂和昂贵,大多数公司都无法复制。因此,要使用诸如 ChatGPT 之类的模型,你需要付出一定的费用并等待几秒钟,才能从你无法控制的重型服务器获得 API 回复。
当然,这些巨大的生成模型的开放能力意味着你可以勉强将它们用于非生成或判别性任务,例如分类。这是因为你可以用简单的英语描述分类任务,然后 ... 只需让模型进行分类即可。但是,虽然此工作流程非常适合原型设计,但一旦你投入批量生产,就不想支付原型价格。
围绕 GenAI 的流行热潮掩盖了仅编码器模型的作用。这些是实际语言处理的骨干,这些模型实际上正在许多科学和商业应用中用于此类工作负载。
仅编码器模型
仅编码器模型的输出是一个数值列表(一个嵌入向量)。你可以说,编码器模型不是用文本回答,而是将它的“答案”以这种压缩的数字形式编码。该向量是模型输入的压缩表示,这就是为什么仅编码器模型有时被称为表示模型的原因。
虽然仅解码器模型(如 GPT)可以完成仅编码器模型(如 BERT)的工作,但它们受到关键约束的限制:由于它们是生成模型,它们在数学上是“不允许” “偷看”后面的 token。它们只能向后看。这与仅编码器模型形成对比,后者被训练为每个 token 都可以向前和向后(双向)查看。它们是为此而构建的,这使得它们在完成工作时非常高效。
基本上,像 OpenAI 的 O1 这样的前沿模型就像一辆法拉利 SF-23。这显然是工程的胜利,旨在赢得比赛,这就是我们谈论它的原因。但是,仅仅更换轮胎就需要一支特殊的维修人员,而且你无法自己购买一辆。相比之下,BERT 模型就像一辆本田思域。它也是工程的胜利,但更微妙,因为它的设计是为了经济实惠、省油、可靠且非常有用。这就是为什么它们绝对无处不在。
你可以通过多种方式看到这一点。
支持生成模型:了解表示模型(仅编码器)普及的一种方法是注意它们如何经常与仅解码器模型结合使用,以创建一个安全高效的系统。
一个明显的例子是 RAG。该系统不是依赖于 LLM 训练到模型参数中的知识,而是使用文档存储来为 LLM 提供与查询相关的信息。但这当然只是推迟了问题。如果 LLM 不知道哪些文档与查询相关,那么系统就需要其他过程来选择这些文档?它需要一个足够快速且便宜的模型,以便可以用于编码使 LLM 有用所需的大量信息。该模型通常是类似 BERT 的仅编码器模型。
另一个例子是监督架构,其中可能会使用廉价的分类器来确保生成的文本不违反内容安全要求。
简而言之,无论何时你在部署中看到仅解码器模型,都有合理的可能性,仅编码器模型也是系统的一部分。但反之则不然。
基于编码器的系统:在 GPT 出现之前,社交媒体和 Netflix 等平台上就有内容推荐。这些场所、搜索和其他地方都有广告定位。还有垃圾邮件检测、滥用检测等内容分类。这些系统不是建立在生成模型之上,而是建立在像仅编码器模型这样的表示模型之上。所有这些系统仍然存在,并且仍然以巨大的规模运行。想象一下,每秒钟在世界各地定位多少广告!
下载量:在 HuggingFace 上,RoBERTa,一种领先的基于 BERT 的模型,其下载量超过 HuggingFace 上最流行的 10 个 LLM 的总和。事实上,目前仅编码器模型的每月下载量总计超过 10 亿次,几乎是仅解码器模型每月 3.97 亿次下载量的三倍。事实上,由 ModernBERT 等仅编码器 “基础模型” 组成的 fill-mask 模型类别,准备好为其他下游应用进行微调,是所有模型类别中下载量最多的模型类别。
推理成本:以上表明,在每次推理的基础上,仅编码器模型的每年推理次数远多于仅解码器或生成模型。一个有趣的例子是 FineWeb-Edu,其中必须对超过 15 万亿个 token 执行基于模型的质量过滤。FineWeb-Edu 团队选择使用仅解码器模型 Llama-3-70b-Instruct 生成注释,并使用 微调的基于 BERT 的模型 执行大部分过滤。这种过滤花费了 6,000 个 H100 小时,按照 HuggingFace 推理点 的价格为 10 美元/小时计算,总计 60,000 美元。另一方面,即使使用 Google Gemini Flash 及其 0.075 美元/百万个 token 的低推理成本 这种最低成本选项,将 15 万亿个 token 提供给流行的仅解码器模型,也将花费超过一百万美元!
性能
概述
以下是 ModernBERT 和其他模型在各种任务中的准确性快照,这些任务是通过标准学术基准衡量的 - 正如你所看到的,ModernBERT 是唯一一个在每个类别中都名列前茅的模型,这使得它成为你可以用于所有基于编码器任务的一个模型:
如果你曾经在 Kaggle 上参加过 NLP 竞赛,那么你会知道 DeBERTaV3 多年来一直是冠军的选择。但现在不是了:ModernBERT 不仅是第一个在 GLUE 上击败 DeBERTaV3 的 base 大小模型,而且它的内存使用量还不到 DeBERTa 的 1/5。
当然,ModernBERT 速度很快。它的速度是 DeBERTa 的 两倍 - 事实上,在输入长度混合的更常见情况下,速度高达 4 倍。其长上下文推理速度几乎是 NomicBERT 和 GTE-en-MLM 等其他高质量模型的 3 倍。
ModernBERT 的上下文长度为 8,192 个 token,比大多数现有编码器大 16 倍 以上。这对于 RAG 管道至关重要,在 RAG 管道中,小上下文通常会使块太小而无法进行语义理解。ModernBERT 也是 ColBERT 的最先进长上下文检索器,比其他长上下文模型高 9 个百分点。更令人印象深刻的是:这个非常快速训练的模型,只是简单地调整为与其他骨干网进行比较,在长上下文任务上的性能甚至优于广泛使用的检索模型!
对于代码检索,ModernBERT 是独一无二的。没有真正可以与之比较的,因为以前从未有过像这样在大量代码数据上训练的编码器模型。例如,在 StackOverflow-QA 数据集 (SQA) 上,这是一个混合了代码和自然语言的混合数据集,ModernBERT 的专门代码理解和长上下文能力使其成为唯一在此任务中得分超过 80 的骨干网络。
这意味着整个新的应用程序很可能会基于此功能构建。例如,想象一下一个 AI 连接的 IDE,它使用 ModernBERT 嵌入索引了整个企业的代码库,从而提供跨所有存储库的快速长上下文相关代码检索。或者是一个代码聊天服务,它描述了一个集成数十个独立项目的应用程序功能是如何工作的。
与主流模型相比,ModernBERT 在检索、自然语言理解和代码检索这三大任务类别中表现更好。虽然它在某一方面(自然语言理解)略逊于 DeBERTaV3,但它的速度快了很多倍。请注意,ModernBERT 与任何其他基础模型一样,只能开箱即用地执行掩码词预测。为了能够执行其他任务,应像这些样板中所做的那样对基础模型进行微调。
与专业模型相比,ModernBERT 在大多数任务中都具有可比性或优越性。此外,ModernBERT 在大多数任务中的速度都快于大多数模型,并且可以处理高达 8,192 个 token 的输入,这比主流模型长 16 倍。
效率
以下是在 NVIDIA RTX 4090 上 ModernBERT 和其他解码器模型的内存(最大批大小,BS)和推理(每秒数千个 token)效率结果:
你可能首先注意到的是,我们是在经济实惠的消费级 GPU 上分析效率,而不是最新的、难以获得的炒作硬件。首先,ModernBERT 的重点是实用性,而不是炒作。
作为此重点的一部分,这也意味着我们确保 ModernBERT 适用于现实世界的应用,而不仅仅是基准测试。这类模型通常仅在它们最擅长的那个确切大小(它们的最大上下文长度)上进行测试。这就是表中 “fixed” 列显示的内容。但在现实世界中,输入大小各不相同,因此这是我们努力优化的性能 - “variable” 列。正如你所看到的,对于可变长度的输入,ModernBERT 比所有其他模型都要快得多。
对于我们认为将成为未来最有价值和最重要的应用程序基础的长上下文输入,ModernBERT 比下一个最快的模型快 2-3 倍。而且,再次从 “实用性” 维度来看:ModernBERT 不需要额外的繁重的 “xformers” 依赖项,而只需要现在普遍存在的 Flash Attention 作为依赖项。
此外,由于 ModernBERT 的效率,它可以比几乎任何其他模型使用更大的批大小,并且可以在更小、更便宜的 GPU 上有效使用。特别是基础大小的效率可能会使直接在浏览器、手机等上运行的新应用程序成为可能。
为什么 ModernBERT,嗯,是现代的?
现在,我们已经说明了为什么我们应该更加关注编码器模型。作为可靠、不被赏识的干将,自 2018 年的 BERT 以来,它们的更新非常少!
更令人惊讶的是:自从 RoBERTa 以来,还没有一个编码器能在不进行权衡的情况下提供全面的改进(花哨地称为 “帕累托改进”):DeBERTaV3 在 GLUE 和分类性能方面更好,但牺牲了效率和检索。其他模型,例如 AlBERT,或较新的模型(如 GTE-en-MLM),都在某些方面改进了原始 BERT 和 RoBERTa,但在其他方面有所退步。
但是,自从该二人组最初发布以来,我们已经了解了大量关于如何构建更好的语言模型的信息。如果你曾经使用过 LLM,你就会非常清楚:虽然它们在编码器世界中很少见,但帕累托改进在解码器领域是常态,模型不断在各方面变得更好。而且,正如我们现在都了解到的:模型的改进只是一部分魔法,而主要是工程。
因此,(希望名称恰当的)ModernBERT 项目的目标非常简单:将这种现代工程引入编码器模型。我们通过三种核心方式做到了这一点:
- 一个现代化的 Transformer 架构
- 特别关注效率
- 现代数据规模和来源
迎接新的 Transformer,与旧的 Transformer 相同
Transformer 架构已成为主流,并被当今绝大多数模型使用。但是,重要的是要记住,不是一个而是许多Transformer。它们的主要共同点是它们深信注意力确实是你所需要的一切,因此围绕注意力机制构建各种改进。
ModernBERT 从 Transformer++(由 Mamba 创造的)中获得了巨大的灵感,Llama2 系列模型 首次使用了该模型。也就是说,我们将旧的 BERT 式构建块替换为其改进的等效项,即我们:
使用 “旋转位置嵌入” (RoPE) 替换旧的位置编码:这使得模型更擅长理解单词之间的相对位置,并允许我们扩展到更长的序列长度。
将旧的 MLP 层换成 GeGLU 层,改进原始 BERT 的 GeLU 激活函数。
通过删除不必要的偏差项来简化架构,使我们能够更有效地利用参数预算。
在嵌入后添加额外的归一化层,这有助于稳定训练。
为赛道升级本田思域
我们已经介绍了这一点:编码器不是法拉利,ModernBERT 也不例外。但是,这并不意味着它不能快速运行。当你上高速公路时,你通常不会去将你的汽车换成赛车,而是希望你日常可靠的汽车能够舒适地达到速度限制。
事实上,对于我们上面提到的所有应用案例,速度至关重要。编码器在必须处理大量数据的使用中非常受欢迎,即使是很小的速度增量也能很快累积起来,或者在延迟非常重要的情况下,例如 RAG。在许多情况下,编码器甚至在 CPU 上运行,如果我们希望在合理的时间内获得结果,效率就更为重要。
与研究中的大多数事物一样,我们站在巨人的肩膀上构建,并大量利用 Flash Attention 2 的速度改进。我们的效率改进依赖于三个关键组件:交替注意力,以提高处理效率;取消填充和序列打包,以减少计算浪费;以及 硬件感知模型设计,以最大限度地利用硬件。
全局和局部注意力
ModernBERT 最有影响力的功能之一是交替 注意力,而不是完全全局注意力。从技术上讲,这意味着我们的注意力机制仅每 3 层关注完整输入(全局注意力),而所有其他层都使用滑动窗口,其中每个 token 仅关注最接近自身的 128 个 token(局部注意力)。由于注意力的计算复杂性随着每个额外 token 的增加而急剧膨胀,这意味着 ModernBERT 可以比任何其他模型更快地处理长输入序列。
实际上,它看起来像这样:
从概念上讲,之所以会这样工作的原因非常简单:想象一下你在读书。对于你阅读的每一句话,你是否需要完全了解整个情节才能理解大部分内容(完全全局注意力)?还是只要你偶尔回顾一下它对主要情节的意义(全局注意力),对当前章节的认识就足够了(局部注意力)?在绝大多数情况下,是后者。
取消填充和序列打包
为 ModernBERT 的效率做出贡献的另一个核心机制是它用于取消填充和序列打包。
为了能够在同一批次中处理多个序列,编码器模型要求它们具有相同的长度,以便它们可以执行并行计算。传统上,我们依赖于 填充 来实现这一点:找出哪个句子最长,并添加无意义的 token(填充 token)来填充所有其他序列。
虽然填充解决了问题,但它并没有优雅地解决:大量的计算最终花费和浪费在填充 token 上,而填充 token 不会贡献任何语义信息。
取消填充 解决了这个问题:我们不是保留这些填充 token,而是删除它们,并将它们连接到批大小为 1 的小批次中,避免所有不必要的计算。如果你使用的是 Flash Attention,那么我们实现的取消填充速度甚至比以前的方法更快,以前的方法严重依赖于在序列通过模型时取消填充和重新填充序列:我们通过引入我们自己实现的取消填充进一步迈出了一步,该实现严重依赖于 Flash Attention 的 RoPE 支持中的最新进展。这使 ModernBERT 只需取消填充一次,并可以在处理后选择重新填充序列,从而比以前的方法快 10-20%。
为了进一步加快预训练速度,取消填充在我们的模型中拥有良好的表现,因为我们将其与 序列打包 结合使用。此处的序列打包是逻辑上的下一步:当我们正在将输入连接成一个序列时,而 GPU 非常擅长并行化,我们希望最大限度地提高我们可以从单个前向模型传递中提取的计算效率。为此,我们使用贪婪算法将各个序列分组到与模型最大输入长度尽可能接近的连接序列中。
注意硬件
最后,ModernBERT 效率的第三个方面是硬件设计。
我们试图平衡先前研究强调的两个见解:
深层且窄层与宽层且浅层:研究表明,具有较窄层的更深层模型通常比具有较少、更宽层的浅层模型性能更好。然而,这是一把双刃剑:模型越深,并行化程度越低,因此,在相同的参数计数下,运行速度越慢。
硬件效率:模型维度需要与 GPU 硬件良好对齐才能实现最大性能,并且不同的目标 GPU 会产生不同的约束。
遗憾的是,没有神奇的方法可以使模型在各种 GPU 上都能以类似的方式良好运行,但有一个优秀的指南: 与硬件共同设计模型架构的案例,其中详细介绍了针对给定 GPU 优化模型架构的方法。我们提出了一个启发式方法,以将它们的方法扩展到 GPU 篮子,同时遵守一组给定的约束。从逻辑上讲,第一步是定义上述约束,在我们的案例中:
将我们的目标 GPU 定义为常见的推理 GPU (RTX 3090/4090、A10、T4、L4)
大致将我们的目标模型大小定义为 ModernBERT-Base 的 130 到 150 百万个参数,以及 ModernBERT-Large 的 350 到 420 个参数。
最终嵌入大小必须与原始 BERT 的维度(base 为 768,large 为 1024)匹配,以最大限度地提高向后兼容性
设置在 GPU 篮子中通用的性能约束
之后,我们通过受约束的网格搜索尝试了多种模型设计,改变了层计数和层宽度。一旦我们确定了似乎最有效的形状,我们就确认了我们的启发式方法与实际 GPU 性能相匹配,并确定了最终的模型设计。
训练
def data(): return ['text', 'bad_text', 'math', 'code']
想象一下这个场景,但是用数据代替开发者
编码器一直落后的另一个重要方面是训练数据。这通常被理解为仅意味着训练数据的 规模,但这实际上并非如此:之前的编码器,例如 DeBERTaV3,经过了足够长时间的训练,它们甚至可能已经突破了万亿 token 的规模!
问题实际上是训练数据的 多样性:许多较旧的模型在有限的语料库上进行训练,这些语料库通常由维基百科和维基书籍组成。这些数据组合非常明显地是 单一文本模态:它们只包含高质量的自然文本。
相比之下,ModernBERT 是在来自各种英语来源的数据上训练的,包括 Web 文档、代码和科学文章。它是在 2 万亿个 token 上训练的,其中大多数是唯一的,而不是之前编码器中常见的 20 到 40 次重复。
这方面的影响立即可见:在所有现有的开源编码器中,ModernBERT 在编程相关任务中独树一帜。我们特别感兴趣的是,这将在改进编程助手方面带来哪些下游用途。
流程
我们坚持原始 BERT 的训练方法,并进行了一些受后续工作启发的细微升级:我们删除了下一句预测目标,因为随后表明添加该目标会增加开销,并且没有明显的收益,并将掩码率从 15% 提高到 30%。
这两个模型都使用 三阶段流程 进行训练。首先,我们在 1024 的序列长度上训练 1.7T 个 token。然后,我们采用长上下文适应阶段,在 8192 的序列长度上训练 250B 个 token,同时通过降低批大小来使每个批次看到的 token 总数或多或少保持一致。最后,我们按照 ProLong 强调的长上下文扩展理想组合,对 500 亿个以不同方式采样的 token 执行退火。
分三个阶段进行训练是我们确保我们的模型在各个方面都表现良好的方法,这反映在其结果中:它在长上下文任务中具有竞争力,而不会影响其处理短上下文的能力……
……但它还有另一个好处:在前两个阶段,一旦预热阶段完成,我们就使用恒定的学习率进行训练,并且仅在最后的 500 亿个 token 上执行学习率衰减,遵循梯形(或预热-稳定-衰减)学习率。此外:受 Pythia 的启发,我们将发布来自这些稳定阶段的每一个单一的即时中间检查点。我们这样做的主要原因是支持未来的研究和应用:任何人都可以从我们的任何预衰减检查点重新开始训练,并根据其预期用途对领域适当的数据执行退火!
诀窍,一切都取决于诀窍!
如果你已经看到这篇文章的这一步,你可能已经习惯了:当然,我们也使用技巧来加快这里的速度。确切地说,我们有两个主要技巧。
让我们从第一个开始,这很常见:由于初始训练步骤是更新随机权重,我们采用批大小预热:我们从较小的批大小开始,以便相同数量的 token 更频繁地更新模型权重,然后逐渐将批大小增加到最终训练大小。这显着加快了模型训练的初始阶段,在该阶段,模型会学习对语言的最基本理解。
第二个技巧更为罕见:通过平铺为更大的模型尺寸进行权重初始化,灵感来自微软的 Phi 系列模型。这基于以下认识:当我们有一个非常好的(如果允许我们这么说的话)ModernBERT-base 权重集就在那里时,为什么还要用随机数初始化 ModernBERT-large 的初始权重呢?
事实上,事实证明,在 ModernBERT-large 中平铺 ModernBERT-base 的权重比从随机权重初始化效果更好。它还具有与批大小预热良好叠加的额外好处,从而实现更快的初始训练。
结论
在这篇博客文章中,我们介绍了 ModernBERT 模型,这是一系列新的最先进的、小型且高效的仅编码器模型,最终使 BERT 获得了急需的重新启动机会。
ModernBERT 证明了可以通过现代方法改进仅编码器模型。它们继续在某些任务上提供非常强大的性能,提供极具吸引力的大小/性能比。
最重要的是,我们非常期待看到社区会提出哪些创造性使用这些模型的方式!为了鼓励这一点,我们将在 2025 年 1 月 10 日之前公开征集演示:前 5 个最佳演示将添加到这篇文章的展示部分,并获得 100 美元(或当地货币等值)的亚马逊礼品卡,以及 6 个月的 HuggingFace Pro 订阅!如果你需要一个开始的提示,这里有一个我们想到的演示:代码相似性 HF 空间!请记住,这是一个编码器模型,因此所有最酷的下游应用程序可能都需要某种形式的微调(在真实数据或也许是解码器模型合成数据上)。值得庆幸的是,那里有很多很酷的框架来支持微调编码器:🤗Transformers 本身用于各种任务(包括分类)、GliNER 用于零样本命名实体识别,或 Sentence-Transformers 用于检索和相似性任务!