深入理解 Transformers 中的 JinaEmbeddingsV3:基于 XLM-RoBERTa 的多语言多任务文本嵌入模型
Jina-Embeddings-v3 是一个面向检索、分类、聚类、文本相似度等多种 NLP 场景的多语言文本嵌入模型。它在 🤗 Transformers 仓库中由 JinaEmbeddingsV3 系列类实现(模型发布论文日期为 2024-09-16,贡献进 Transformers 的时间为 2026-03-19),其架构以 XLM-RoBERTa 为基础,用旋转位置编码(RoPE)替换绝对位置编码以支持最长 8192 token 的输入,并内置 5 个任务专用 LoRA Adapter。读完本文,你将掌握通过 Pipeline、AutoModel 提取嵌入的两种标准做法,理解 5 类任务 Adapter 的适用场景与加载切换方法,并能结合源码弄清其注意力实现、位置编码细节与各 Head 类的职责。
模型核心特性一览
从官方模型文档可知,JinaEmbeddingsV3 的核心技术特点集中在三点:
- 多语言、多任务:单个模型同时服务检索、分类、聚类、相似度计算等多种下游任务,用于推断与训练均适用;
- 基于 XLM-RoBERTa 架构:词表与骨干网络继承自 XLM-RoBERTa;
- RoPE + 长序列:以旋转位置编码(Rotary Position Embeddings)替代绝对位置嵌入,从而把输入长度扩展至 8192 tokens;
- 5 个内置 Task-Specific LoRA Adapter:在不显著增加推理延迟的前提下,按任务切换得到专用的文本嵌入。
这些能力都可以在 JinaEmbeddingsV3 模型文档 中找到依据;模型权重归档在 jinaai 组织下,Transformers 官方推荐使用的 Hub 检查点为 jinaai/jina-embeddings-v3-hf。
架构设计:从源码理解它是“如何长成这样”的
JinaEmbeddingsV3 的实现位于 src/transformers/models/jina_embeddings_v3/ 目录,其中包含了配置、模型定义与“模块化源文件”(modular):
- configuration_jina_embeddings_v3.py:
JinaEmbeddingsV3Config定义; - modeling_jina_embeddings_v3.py:全部模型类与层的自动生成实现(文件头标注“由 modular 自动生成,勿手动编辑”);
- modular_jina_embeddings_v3.py:真正的人类可读源码,被 CI 校验后生成上面的 modeling 文件。
从 modular 源码的 import 语句可以看出它的“血统”——它复用了多个现有模型的构件:
| 构件 | 来源 | 在本模型中的角色 |
|---|---|---|
XLMRobertaConfig / XLMRobertaPreTrainedModel 等 |
XLM-RoBERTa | 骨干与各类任务 Head 的基座 |
XLMRobertaEmbeddings |
XLM-RoBERTa | 词嵌入 + token_type 嵌入 + LayerNorm + Dropout |
LlamaRotaryEmbedding、LlamaAttention、apply_rotary_pos_emb |
Llama | 旋转位置编码与多头注意力 |
GPTNeoXLayer |
GPT-NeoX | 残差块层结构 |
CLIPMLP |
CLIP | 前馈网络(MLP) |
为什么可以支持 8192 token?——RoPE 对绝对位置编码的替换
在标准 XLM-RoBERTa 的 embedding 里,位置信息来自可学习的 position_embeddings 表。而 modular_jina_embeddings_v3.py 中 JinaEmbeddingsV3Embeddings.__init__ 明确执行了两件事:
del self.padding_idx
del self.position_embeddings
即删除了 padding 索引与绝对位置嵌入,同时把 create_position_ids_from_input_ids、create_position_ids_from_inputs_embeds 两个方法置为 AttributeError("Not needed for JinaEmbeddingsV3")。位置信息改由 JinaEmbeddingsV3RotaryEmbedding(继承自 LlamaRotaryEmbedding)在注意力计算前实时生成。
旋转位置编码在 modeling_jina_embeddings_v3.py 中的 compute_default_rope_parameters 中计算逆频率:
base = config.rope_parameters["rope_theta"]
dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float) / dim))
对应的 RoPE 超参 default_theta = 20000.0 记录在 configuration_jina_embeddings_v3.py 中。由于 RoPE 是“按位置实时外推”的旋转插值,不需要学习一个固定长度的位置嵌入表,模型因而能自然支持更长的输入序列。
双向注意力与层结构
JinaEmbeddingsV3 是一个双向(encoder)嵌入模型,不是 decoder 式的因果模型:
- 在
JinaEmbeddingsV3Attention中self.is_causal = False; - 在模型前向中,attention mask 由
create_bidirectional_mask生成,保证每个 token 都能看到上下文两侧; - 注意力投影(q/k/v/o)全部带 bias,head 维度为
hidden_size // num_attention_heads(默认 1024/16 = 64),缩放因子scaling = head_dim ** -0.5。
注意力实现通过 ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward) 动态分发。结合 JinaEmbeddingsV3PreTrainedModel 中声明的能力标志 _supports_flash_attn = True、_supports_sdpa = True、_supports_flex_attn = True、_supports_attention_backend = True,可以确认该模型原生支持 Flash Attention、SDPA、Flex Attention 等多种加速注意力后端(文档页顶部的 PyTorch / FlashAttention / SDPA 徽标也印证了这一点),并支持梯度检查点(supports_gradient_checkpointing = True)。
每个 JinaEmbeddingsV3Layer 的前向路径是:残差 + 注意力输出 → post_attention_dropout → post_attention_layernorm → 残差 + MLP → post_mlp_dropout → post_mlp_layernorm,即 LayerNorm 位于每个残差加法之后,而非经典 Transformer 的 pre-norm 位置;同时设置了 post_attention_dropout / post_mlp_dropout 两处 Dropout。这一点可在 modeling_jina_embeddings_v3.py 的 JinaEmbeddingsV3Layer.forward 中直接核对。
嵌入输出形态
JinaEmbeddingsV3Model.forward 返回 BaseModelOutputWithPooling:
last_hidden_state:形状为(batch_size, sequence_length, hidden_size)的全部 token 隐状态;pooler_output:取序列首 token(<s>)过一层dense + tanh的池化结果(对应JinaEmbeddingsV3Pooler)。
值得注意的是:官方文档明确要求生成句级/段级高质量嵌入时,不要直接用首 token 池化,而要自行做 mean pooling(详见下文),这正是 LoRA 任务嵌入的标准后处理。
快速上手:三种方式提取嵌入特征
模型文档给出了通过 Pipeline 与 AutoModel 提取嵌入的示例,两者都使用 jinaai/jina-embeddings-v3-hf 检查点。仓库集成测试 test_modeling_jina_embeddings_v3.py 也以同一 model_id 和提示句 "Jina Embeddings V3 is great for semantic search." 断言了输出形状为 (1, 17, 1024),说明该检查点单条输入会产出与模型 hidden_size = 1024 一致的特征维度。
方式一:Pipeline(feature-extraction)
from transformers import pipeline
pipeline = pipeline(
task="feature-extraction",
model="jinaai/jina-embeddings-v3-hf",
)
# Returns a list of lists containing the embeddings for each token
embeddings = pipeline("Jina Embeddings V3 is great for semantic search.")
feature-extraction 任务是 Transformers 通用嵌入管线的标准入口。PipelineTesterMixin 在测试文件中将 feature-extraction 映射到 JinaEmbeddingsV3Model,同时也映射了 fill-mask(JinaEmbeddingsV3ForMaskedLM)、text-classification / zero-shot(JinaEmbeddingsV3ForSequenceClassification)、token-classification(JinaEmbeddingsV3ForTokenClassification),说明该模型可无缝接入这些现成管线。
方式二:AutoModel + AutoTokenizer
import torch
from transformers import AutoModel, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("jinaai/jina-embeddings-v3-hf")
model = AutoModel.from_pretrained("jinaai/jina-embeddings-v3-hf", device_map="auto")
prompt = "Jina Embeddings V3 is great for semantic search."
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model(**inputs)
# The base AutoModel returns the raw hidden states for all tokens
last_hidden_states = outputs.last_hidden_state
print(f"Features shape: {last_hidden_states.shape}")
device_map="auto" 交给 Transformers/accelerate 自动安排设备;outputs.last_hidden_state 是逐 token 的原始隐状态(默认上下文长度下形状如 (1, seq_len, 1024))。
方式三:命令行
模型文档在说明中同样提到可以“从命令行”完成特征提取。除上述 Python API 外,你可以在终端中复用仓库示例目录下的脚本逻辑,例如以 examples/pytorch/ 中相关示例脚本为基础封装批处理任务;更直接的做法是把上面两段代码保存为脚本后以 python your_script.py 运行(具体 CLI 入口取决于你安装的 Transformers 版本与配套命令行工具)。
Task-Specific LoRA Adapters:一个模型,五种任务语义
JinaEmbeddingsV3 的招牌特性是任务专用 LoRA Adapter:不必为每个任务加载一整份不同的模型权重,而是共享同一骨干、仅切换轻量的 LoRA 适配层,即可让嵌入空间针对特定任务“定向塑造”。
5 类任务 Adapter 及用途
官方文档列出了以下任务语义(注意文档中的“点分/连字符”命名是语义名称,实际加载时对应 Hub 上的子目录名,见下文示例):
retrieval.query(子目录retrieval_query):用于非对称检索中的“查询”侧嵌入(如搜索引擎的 query);retrieval.passage(子目录retrieval_passage):用于非对称检索中的“段落/文档”侧嵌入(如被检索的语料);separation:用于聚类与重排(re-ranking)应用的嵌入;classification:用于分类任务的嵌入;text-matching(子目录text_matching):用于衡量两段文本相似度的任务,例如语义文本相似度(STS)或对称检索。
句嵌入必须配合 Mean Pooling
要产出高质量的句子/段落级嵌入,需要对模型输出的 token 嵌入做 mean pooling:把所有 token 的嵌入取平均,并且通过 attention mask 把 padding token 屏蔽在求和之外。
完整示例:为检索查询任务生成句嵌入
以下代码完整取自官方文档,展示如何在 AutoModel API 中加载 retrieval_query Adapter、执行 mean pooling 并做 L2 归一化:
import torch
import torch.nn.functional as F
from transformers import AutoModel, AutoTokenizer
def mean_pooling(model_output, attention_mask):
# First element of model_output contains all token embeddings
token_embeddings = model_output[0]
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
# Sum the embeddings and divide by the number of non-padding tokens
sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1)
sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
return sum_embeddings / sum_mask
sentences = [
"How is the weather today?",
"What is the current weather like today?"
]
tokenizer = AutoTokenizer.from_pretrained("jinaai/jina-embeddings-v3-hf")
model = AutoModel.from_pretrained("jinaai/jina-embeddings-v3-hf", device_map="auto")
encoded_input = tokenizer(sentences, padding=True, truncation=True, return_tensors="pt").to(model.device)
# Set up the adapter mask for your specific task
task = 'retrieval_query' # Can be any of (retrieval_passage, separation, classification, text_matching) depending on the use-case.
model.load_adapter("jinaai/jina-embeddings-v3-hf", adapter_name=task, adapter_kwargs={"subfolder": task})
model.set_adapter(task)
with torch.no_grad():
model_output = model(**encoded_input)
embeddings = mean_pooling(model_output, encoded_input["attention_mask"])
embeddings = F.normalize(embeddings, p=2, dim=1)
print(embeddings.shape)
# Output: torch.Size([2, 1024])
几个容易踩坑的细节,均可以对照代码验证:
- 任务名即 Hub 子目录名:
load_adapter(..., adapter_kwargs={"subfolder": task})表示 Adapter 权重存放在同一模型仓库下名为retrieval_query(或retrieval_passage、separation、classification、text_matching)的子目录中; load_adapter与set_adapter缺一不可:前者把 Adapter 权重挂载进模型,后者激活它;集成测试test_inference_retrieval_query_adapter、test_inference_retrieval_passage_adapter、test_inference_separation_adapter、test_inference_classification_adapter、test_inference_text_matching_adapter(见 test_modeling_jina_embeddings_v3.py)逐一验证了这 5 个任务的加载路径与输出数值,可用作回归基准;- padding/truncation 必须开启:
mean_pooling依赖attention_mask屏蔽 padding,因此 tokenizer 需要padding=True, truncation=True; - 归一化是可选的惯例:
F.normalize(embeddings, p=2, dim=1)对嵌入做 L2 归一化,使余弦相似度计算与内积/点积检索在数值上等价,是向量检索实践的常见步骤; - 无任务时直接取逐 token 隐状态:基础
AutoModel前向(不做任何 Adapter)返回的就是逐 token 原始隐状态,未经过任务定向;一旦需要可复现且可对比的“检索查询 vs 文档”嵌入空间,就应各自加载对应 Adapter。
JinaEmbeddingsV3Config 默认超参数速查
configuration_jina_embeddings_v3.py 定义了 JinaEmbeddingsV3Config(model_type = "jina_embeddings_v3"),其类级默认值如下,可与 jinaai/jina-embeddings-v3-hf 检查点的 config.json 对照:
| 参数 | 默认值 | 含义 |
|---|---|---|
vocab_size |
250002 | 词表大小(含特殊 token) |
hidden_size |
1024 | 隐层维度(也即嵌入维度) |
num_hidden_layers |
24 | Transformer 层数 |
num_attention_heads |
16 | 注意力头数(单头 64 维) |
intermediate_size |
4096 | MLP 中间层维度 |
hidden_act |
"gelu" |
激活函数 |
hidden_dropout_prob |
0.1 | 隐层 Dropout |
attention_probs_dropout_prob |
0.1 | 注意力 Dropout |
max_position_embeddings |
8194 | RoPE 支持的最大序列位置(对应 8192 token + 首尾特殊位置) |
type_vocab_size |
1 | segment 类型数(文档注明该参数≥2 时才能传入 token_type_ids) |
initializer_range |
0.02 | 权重初始化范围 |
layer_norm_eps |
1e-5 | LayerNorm 的 eps |
pad_token_id / bos_token_id / eos_token_id |
1 / 0 / 2 | 特殊 token id |
use_cache |
True |
是否启用 KV cache |
tie_word_embeddings |
True |
MLM Head 与词嵌入是否共享权重 |
default_theta |
20000.0 | RoPE 基础频率 rope_theta |
rope_parameters |
None(默认按 default) |
高级 RoPE 参数(rope_type/rope_theta 等),实现于 JinaEmbeddingsV3RotaryEmbedding |
注意配置类同时把 add_cross_attention、is_decoder 声明为 AttributeError(),这是架构层面的强约束:JinaEmbeddingsV3 是纯双向编码器,不能当作带交叉注意力的 decoder 使用。从测试看,JinaEmbeddingsV3ModelTest.test_model_rope_scaling_from_config 针对 linear/dynamic/yarn 缩放被 @unittest.skip("Model doesn't support scaling - due to non-RoPE related reasons") 跳过,说明从源码层面该模型暂不支持对 RoPE 的额外 length scaling 配置。
模型 API 家族:五个公开类
官方文档以 autodoc 形式收录了以下类(全部位于 modeling_jina_embeddings_v3.py):
JinaEmbeddingsV3Config:模型配置类,见上文默认值表;JinaEmbeddingsV3Model:裸骨干模型,输出逐 token 隐状态与(可选)首 token 池化结果,是最常用于“提取嵌入”的入口;JinaEmbeddingsV3ForMaskedLM:在骨干上叠JinaEmbeddingsV3LMHead(dense → GELU → LayerNorm → decoder 线性层)用于掩码语言建模。测试断言其 logits 形状为(batch_size, seq_length, vocab_size);它同时设置了_tied_weights_keys,把lm_head.decoder与roberta.embeddings.word_embeddings的权重做 tied(与tie_word_embeddings=True呼应);JinaEmbeddingsV3ForSequenceClassification:分类 Head(取首 token 特征后接多层/输出层),输出(batch_size, num_labels);JinaEmbeddingsV3ForTokenClassification:序列标注 Head,输出(batch_size, seq_length, num_labels);JinaEmbeddingsV3ForQuestionAnswering:抽取式问答 Head,输出start_logits/end_logits,形状均为(batch_size, seq_length)。
从 modular_jina_embeddings_v3.py 的类定义可以确认,这四个“For 任务”类分别继承自 XLMRobertaForMaskedLM、XLMRobertaForSequenceClassification、XLMRobertaForTokenClassification、XLMRobertaForQuestionAnswering,因此其输入输出约定与 XLM-RoBERTa 家族保持一致,使用门槛很低。
各任务 Head 的 forward 参数约定
JinaEmbeddingsV3Model.forward(input_ids, attention_mask, token_type_ids, position_ids, inputs_embeds):input_ids与inputs_embeds必须二选一且只能选一(否则抛ValueError);JinaEmbeddingsV3ForMaskedLM额外支持labels(形状同input_ids,取值[-100, 0, …, config.vocab_size],-100表示忽略该位置);JinaEmbeddingsV3ForSequenceClassification额外支持labels(整型标签);JinaEmbeddingsV3ForTokenClassification额外支持逐 token 的labels;JinaEmbeddingsV3ForQuestionAnswering额外支持start_positions/end_positions。
在仓库中验证:测试与模型结构佐证
如果你希望在本仓库中亲手复现与验证本文结论,最直接的两个入口是:
- 结构阅读:modular_jina_embeddings_v3.py 是模型的“人类可读”源,逐类阅读其 import 与
del/raise AttributeError行为,就能快速定位它与 XLM-RoBERTa、Llama、GPT-NeoX、CLIP 的差异点;生成的 modeling_jina_embeddings_v3.py 则为实际运行使用的实现; - 运行测试:test_modeling_jina_embeddings_v3.py 中的
JinaEmbeddingsV3ModelIntegrationTest提供了 6 个@slow集成用例:一个“无 Adapter”基线 + 5 个 Adapter(retrieval_query/retrieval_passage/separation/classification/text_matching)用例,它们不仅验证输出形状(1, 17, 1024),还通过torch.testing.assert_close对若干位置的浮点值做rtol=1e-4级别的数值对齐。若你在本地修改了推理逻辑,这些用例可作为“数值是否发生变化”的判据。
普通(非 slow)的单元测试则由 JinaEmbeddingsV3ModelTester 生成小型随机配置(如 hidden_size=16、num_hidden_layers=2、seq_length=7),覆盖模型、MLM、QA、序列分类、Token 分类五个前向的形状正确性,并复用 ModelTesterMixin / PipelineTesterMixin 的统一校验逻辑。
小结
围绕 JinaEmbeddingsV3,本文梳理了从“官方模型文档一句话”到“可落地代码”的完整链路:它是一个 XLM-RoBERTa 血统的多语言嵌入模型,通过 RoPE 换取最长 8192 token 的输入能力;通过内置 5 个任务 LoRA Adapter 让“单模型 + 轻量切换”即可覆盖查询/文档检索、聚类重排、分类与语义匹配等场景;在 Transformers 中它有完整的配置类、骨干模型与 4 类任务 Head,同时支持 FlashAttention/SDPA/Flex Attention 等加速后端。实践中最关键的三步是:用 AutoModel 加载并切到对应任务 Adapter → 对逐 token 隐状态做带 mask 的 mean pooling → 视场景做 L2 归一化。后续在多语言 RAG、语义搜索或文本聚类项目中,你可以直接复用文档中的示例代码,并结合本仓库源码按需深入排查细节。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0630
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00