
在自然语言处理领域中,文本嵌入一直是把文本转换成计算机能够理解的数值向量的关键技术,也是很多上层任务的基础,通常我们会看到Text Embedding的关键词进行描述。而 Sentence-BERT(SBERT)系列里的 MiniLM 类轻量模型,更是凭借体积小、速度快、效果稳的特点,成为了工业界落地的首选方案。前段时间在给智能体做意图识别相关工作时,最先使用的是经典的 all-MiniLM 模型,但实际跑下来效果并不理想,很多相近意图容易混淆,识别精度达不到预期。后来抱着试一试的心态换成了 paraphrase-multilingual-MiniLM,结果提升非常明显,语义区分度、跨句式表达的匹配度都好了一大截。
也正是这次实践中的直观对比,让我对这两款轻量化嵌入模型产生了浓厚兴趣。接下来我就从实际使用场景出发,对它们做一次深度体验与评测,看看二者在结构、性能、适用场景上到底有哪些差异,以及为什么在意图识别这类任务上,多语言版本会带来如此显著的效果提升。

文本嵌入也常被称为句子嵌入、语义向量,是NLP的基础技术,其核心目标是将非结构化的文本(如单词、句子、段落)映射到低维稠密的数值向量空间,使得向量的距离能够反映文本间的语义相似度。
在传统的 NLP 任务中,文本以离散的符号形式存在,如one-hot编码,这种表示方式存在维度爆炸、语义信息缺失的问题,例如 "苹果"(水果)和 "苹果"(公司)的one-hot向量完全相同。而文本嵌入通过深度学习模型学习语义特征,解决了这一痛点:
BERT是一种自然语言处理预训练模型,它的核心特点可以概括为:
详细可参考:《构建AI智能体:大语言模型BERT:原理、应用结合日常场景实践全面解析》
SBERT是BERT变体模型,专门针对句子嵌入优化。传统BERT模型虽然能捕捉上下文语义,但直接使用 [CLS] token 作为句子表示的效果较差,且计算句子对相似度时需要对所有句子对进行前向传播,效率极低。
SBERT 的核心改进:
BERT与 SBERT 的关系:
简单总结:
MiniLM 是轻量级 BERT 变体,核心是通过模型压缩技术(知识蒸馏)在保持性能的前提下大幅减小模型体积、提升推理速度:

模型名称 | 核心特征 | 参数量 | 适用场景 |
|---|---|---|---|
paraphrase-multilingual-MiniLM-L12-v2 | 多语言支持(100 + 语言)、12 层 Transformer、均值池化 | ~120M | 跨语言文本匹配、多语言语义检索 |
all-MiniLM-L6-v2 | 单语言(英语优先)、6 层 Transformer、均值池化 | ~80M | 英文文本相似度计算、轻量化语义检索 |
池化是将 Transformer 输出的序列特征([CLS], token1, token2, ..., tokenN)转化为单一句子向量的操作,MiniLM 主要使用均值池化(Mean Pooling):
3.1.1 padding token的说明
我们需要先理解为什么要用 Padding,以及它长什么样;
- 1. 怎么理解 Padding
计算机处理数据喜欢整齐划一,比如矩阵运算。但在自然语言中,句子长短不一:
为了让模型能一次性批量处理(Batch Processing),我们会把短的句子强行拉长,补上特殊的标记,直到所有句子长度一致,比如都补齐到10个字,这些补上去的标记就是 Padding。
- 2. 什么是“非Padding”Token
假设我们设定最大长度为10,使用 [PAD] 作为填充标记:
位置 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
原始句子 | 今 | 天 | 天 | 气 | 真 | 不 | 错 | - | - | - |
输入模型 | 今 | 天 | 天 | 气 | 真 | 不 | 错 | [PAD] | [PAD] | [PAD] |
类型 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
- 3. 模型中的标记实现
在代码中,如SBERT模型通常会返回一个 attention_mask,即注意力掩码来标记哪些是真实的。
“非 padding”就是去掉了那些为了凑数而填进去的无效占位符,只保留真正有意义的字词。在计算句子向量时,必须排除它们,否则句子的语义表示会被污染。
用于衡量两个向量的语义相似度,取值范围 [-1, 1],值越大表示语义越相似:

在加载模型时,device_map="auto"会自动将模型层分配到 CPU/GPU 上,适合显存有限的场景:
MiniLM的核心是简化版的 Transformer 编码器,以下是 all-MiniLM-L6-v2 的完整结构:
输入层 → 词嵌入层(Token Embedding) → 位置嵌入层(Position Embedding) → 6层Transformer编码器 → 均值池化层 → 向量归一化 → 输出句子嵌入

输入文本经过以下步骤转化为模型可接受的格式:
每个 Transformer 层包含两个核心模块:
- 1. 多头自注意力(Multi-Head Self-Attention):
- 2. 前馈网络(Feed-Forward Network):
paraphrase-multilingual-MiniLM-L12-v2是经过特殊涉及的多语言模型,该模型在 MiniLM 基础上增加了多语言支持,核心改进:
MiniLM 模型的加载与推理流程:

步骤详解:
- 1. 模型下载(snapshot_download):
- 2. Tokenizer 加载:
- 3. 模型加载:
- 4. 文本编码与推理:
评估 MiniLM 模型的核心维度包括:
all-MiniLM-L6-v2是英文场景的最优轻量选择:
2.1 优势:
2.2 劣势:
2.3 适用场景:
paraphrase-multilingual-MiniLM-L12-v2是多语言场景的首选:
3.1 优势:

3.2 劣势:
3.3 适用场景:
通过定量计算对比“多语言模型paraphrase-multilingual-MiniLM-L12-v2“与“纯英文模型all-MiniLM-L6-v2”在处理中英文任务时的性能差异,验证模型选型对跨语言语义理解的重要性。
import torch
import numpy as np
from scipy.spatial.distance import cosine
from modelscope.hub.snapshot_download import snapshot_download
from transformers import AutoTokenizer, AutoModel
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 配置参数
CACHE_DIR = "D:\\modelscope\\hub"
# 定义两个模型ID
MODEL_IDS = {
"multilingual": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
"english": "sentence-transformers/all-MiniLM-L6-v2"
}
# 设置数据类型(半精度,减少显存占用)
TORCH_DTYPE = torch.float16
# 设置设备(优先GPU)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
def load_model_and_tokenizer(model_id, cache_dir):
"""
加载模型和分词器
:param model_id: 模型ID
:param cache_dir: 缓存目录
:return: tokenizer, model
"""
# 下载模型到本地
local_path = snapshot_download(model_id, cache_dir=cache_dir)
# 加载分词器
tokenizer = AutoTokenizer.from_pretrained(
local_path,
trust_remote_code=True
)
# 加载模型(修正:使用AutoModel而非AutoModelForCausalLM)
model = AutoModel.from_pretrained(
local_path,
trust_remote_code=True,
torch_dtype=TORCH_DTYPE,
device_map="auto" # 自动分配设备
)
# 设置模型为评估模式
model.eval()
return tokenizer, model
def mean_pooling(model_output, attention_mask):
"""
均值池化函数:生成句子嵌入
:param model_output: 模型输出
:param attention_mask: 注意力掩码
:return: 归一化后的句子嵌入
"""
# 获取token嵌入
token_embeddings = model_output[0] # 第一个元素是last_hidden_state
# 扩展掩码维度,匹配token嵌入
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).to(TORCH_DTYPE)
# 计算均值池化(忽略padding部分)
sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, dim=1)
sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9) # 避免除以0
mean_embeddings = sum_embeddings / sum_mask
# L2归一化
mean_embeddings = torch.nn.functional.normalize(mean_embeddings, p=2, dim=1)
return mean_embeddings
def get_sentence_embedding(text, tokenizer, model):
"""
获取单句的嵌入向量
:param text: 输入文本
:param tokenizer: 分词器
:param model: 模型
:return: 句子嵌入向量(numpy数组)
"""
# 文本编码
encoded_input = tokenizer(
text,
padding=True,
truncation=True,
max_length=512,
return_tensors="pt"
).to(DEVICE)
# 前向传播(不计算梯度,提升速度)
with torch.no_grad():
model_output = model(**encoded_input)
# 均值池化生成句子嵌入
sentence_embedding = mean_pooling(model_output, encoded_input['attention_mask'])
# 转换为numpy数组
return sentence_embedding.cpu().numpy()[0]
def calculate_similarity(emb1, emb2):
"""
计算两个嵌入向量的余弦相似度
:param emb1: 向量1
:param emb2: 向量2
:return: 余弦相似度值
"""
return 1 - cosine(emb1, emb2)
# ------------------------------
# 主执行流程
# ------------------------------
if __name__ == "__main__":
# 测试文本集(包含多语言)
test_texts = {
"english1": "The quick brown fox jumps over the lazy dog",
"english2": "A fast brown fox leaps over a sleepy dog",
"english3": "I love natural language processing",
"chinese1": "快速的棕色狐狸跳过懒狗",
"chinese2": "一只敏捷的棕色狐狸越过一只懒狗",
"chinese3": "我喜欢自然语言处理"
}
# 存储结果
results = {}
# 加载并测试两个模型
for model_name, model_id in MODEL_IDS.items():
print(f"\n=== 加载模型:{model_name} ({model_id}) ===")
tokenizer, model = load_model_and_tokenizer(model_id, CACHE_DIR)
# 计算所有文本的嵌入
embeddings = {}
for text_name, text in test_texts.items():
emb = get_sentence_embedding(text, tokenizer, model)
embeddings[text_name] = emb
print(f" 已生成 {text_name} 的嵌入,向量长度:{len(emb)}")
# 计算相似度矩阵
text_names = list(test_texts.keys())
similarity_matrix = np.zeros((len(text_names), len(text_names)))
for i, name1 in enumerate(text_names):
for j, name2 in enumerate(text_names):
similarity_matrix[i, j] = calculate_similarity(
embeddings[name1],
embeddings[name2]
)
# 存储结果
results[model_name] = {
"embeddings": embeddings,
"similarity_matrix": similarity_matrix,
"text_names": text_names
}
# 打印相似度矩阵
print(f"\n{model_name} 模型相似度矩阵:")
print(similarity_matrix.round(3))
# ------------------------------
# 可视化相似度矩阵
# ------------------------------
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# 绘制多语言模型相似度矩阵
sns.heatmap(
results["multilingual"]["similarity_matrix"],
annot=True,
fmt=".3f",
cmap="Blues",
xticklabels=results["multilingual"]["text_names"],
yticklabels=results["multilingual"]["text_names"],
ax=axes[0]
)
axes[0].set_title("paraphrase-multilingual-MiniLM-L12-v2 相似度矩阵", fontsize=12)
axes[0].set_xlabel("文本", fontsize=10)
axes[0].set_ylabel("文本", fontsize=10)
# 绘制英文模型相似度矩阵
sns.heatmap(
results["english"]["similarity_matrix"],
annot=True,
fmt=".3f",
cmap="Blues",
xticklabels=results["english"]["text_names"],
yticklabels=results["english"]["text_names"],
ax=axes[1]
)
axes[1].set_title("all-MiniLM-L6-v2 相似度矩阵", fontsize=12)
axes[1].set_xlabel("文本", fontsize=10)
axes[1].set_ylabel("文本", fontsize=10)
# 保存图片
plt.tight_layout()
plt.savefig("model_similarity_matrix.png", dpi=300, bbox_inches="tight")
plt.show()
# ------------------------------
# 性能对比分析
# ------------------------------
print("\n=== 性能对比分析 ===")
# 提取关键相似度值
en_similarity_multilingual = calculate_similarity(
results["multilingual"]["embeddings"]["english1"],
results["multilingual"]["embeddings"]["english2"]
)
en_similarity_english = calculate_similarity(
results["english"]["embeddings"]["english1"],
results["english"]["embeddings"]["english2"]
)
cn_similarity_multilingual = calculate_similarity(
results["multilingual"]["embeddings"]["chinese1"],
results["multilingual"]["embeddings"]["chinese2"]
)
cn_similarity_english = calculate_similarity(
results["english"]["embeddings"]["chinese1"],
results["english"]["embeddings"]["chinese2"]
)
print(f"英文相似句相似度 - 多语言模型:{en_similarity_multilingual:.4f}")
print(f"英文相似句相似度 - 英文模型:{en_similarity_english:.4f}")
print(f"中文相似句相似度 - 多语言模型:{cn_similarity_multilingual:.4f}")
print(f"中文相似句相似度 - 英文模型:{cn_similarity_english:.4f}")
# 性能对比可视化
fig, ax = plt.subplots(figsize=(10, 6))
categories = ['英文相似句', '中文相似句']
multilingual_scores = [en_similarity_multilingual, cn_similarity_multilingual]
english_scores = [en_similarity_english, cn_similarity_english]
x = np.arange(len(categories))
width = 0.35
ax.bar(x - width/2, multilingual_scores, width, label='paraphrase-multilingual-MiniLM-L12-v2')
ax.bar(x + width/2, english_scores, width, label='all-MiniLM-L6-v2')
ax.set_title('模型相似度性能对比', fontsize=14)
ax.set_ylabel('余弦相似度', fontsize=12)
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.legend()
ax.set_ylim(0, 1)
# 添加数值标签
for i, v in enumerate(multilingual_scores):
ax.text(i - width/2, v + 0.02, f'{v:.4f}', ha='center')
for i, v in enumerate(english_scores):
ax.text(i + width/2, v + 0.02, f'{v:.4f}', ha='center')
plt.tight_layout()
plt.savefig("model_performance_comparison.png", dpi=300, bbox_inches="tight")
plt.show()重点说明:
输出结果:
=== 加载模型:multilingual (sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2) === 已生成 english1 的嵌入,向量长度:384 已生成 english2 的嵌入,向量长度:384 已生成 english3 的嵌入,向量长度:384 已生成 chinese1 的嵌入,向量长度:384 已生成 chinese2 的嵌入,向量长度:384 已生成 chinese3 的嵌入,向量长度:384 multilingual 模型相似度矩阵: [[1. 0.935 0.039 0.867 0.857 0.021] [0.935 1. 0.039 0.856 0.819 0.01 ] [0.039 0.039 1. 0.045 0.066 0.905] [0.867 0.856 0.045 1. 0.876 0.044] [0.857 0.819 0.066 0.876 1. 0.091] [0.021 0.01 0.905 0.044 0.091 1. ]] === 加载模型:english (sentence-transformers/all-MiniLM-L6-v2) === 已生成 english1 的嵌入,向量长度:384 已生成 english2 的嵌入,向量长度:384 已生成 english3 的嵌入,向量长度:384 已生成 chinese1 的嵌入,向量长度:384 已生成 chinese2 的嵌入,向量长度:384 已生成 chinese3 的嵌入,向量长度:384 english 模型相似度矩阵: [[ 1. 0.848 0.126 0.005 0.049 -0.01 ] [ 0.848 1. 0.11 0.002 0.045 -0.033] [ 0.126 0.11 1. 0.015 0.014 0.07 ] [ 0.005 0.002 0.015 1. 0.774 0.657] [ 0.049 0.045 0.014 0.774 1. 0.594] [-0.01 -0.033 0.07 0.657 0.594 1. ]]

两个模型的相似度矩阵热力图:
=== 性能对比分析 === 英文相似句相似度 - 多语言模型:0.9346 英文相似句相似度 - 英文模型:0.8477 中文相似句相似度 - 多语言模型:0.8765 中文相似句相似度 - 英文模型:0.7744

性能对比分析说明:
在以上示例中添加以下代码可测试模型的推理速度:
import time
def test_inference_speed(model_name, model_id, cache_dir, test_texts, repeat=100):
"""
测试模型推理速度
:param model_name: 模型名称
:param model_id: 模型ID
:param cache_dir: 缓存目录
:param test_texts: 测试文本列表
:param repeat: 重复次数
:return: 平均推理时间(秒/句)
"""
tokenizer, model = load_model_and_tokenizer(model_id, cache_dir)
# 预热
for text in test_texts:
get_sentence_embedding(text, tokenizer, model)
# 正式测试
start_time = time.time()
for _ in range(repeat):
for text in test_texts:
get_sentence_embedding(text, tokenizer, model)
end_time = time.time()
total_time = end_time - start_time
avg_time_per_sentence = total_time / (repeat * len(test_texts))
sentences_per_second = 1 / avg_time_per_sentence
print(f"\n{model_name} 推理速度测试:")
print(f" 总耗时:{total_time:.2f} 秒({repeat}次重复)")
print(f" 平均每句耗时:{avg_time_per_sentence:.4f} 秒")
print(f" 每秒处理句子数:{sentences_per_second:.1f}")
return avg_time_per_sentence, sentences_per_second
# 运行速度测试
if __name__ == "__main__":
test_texts_list = list(test_texts.values())
multilingual_speed = test_inference_speed(
"multilingual", MODEL_IDS["multilingual"], CACHE_DIR, test_texts_list
)
english_speed = test_inference_speed(
"english", MODEL_IDS["english"], CACHE_DIR, test_texts_list
)
# 速度对比可视化
fig, ax = plt.subplots(figsize=(8, 6))
models = ['paraphrase-multilingual-MiniLM-L12-v2', 'all-MiniLM-L6-v2']
speeds = [multilingual_speed[1], english_speed[1]]
ax.bar(models, speeds, color=['blue', 'green'])
ax.set_title('模型推理速度对比', fontsize=14)
ax.set_ylabel('处理速度(句子/秒)', fontsize=12)
ax.set_ylim(0, max(speeds) * 1.1)
# 添加数值标签
for i, v in enumerate(speeds):
ax.text(i, v + 5, f'{v:.1f}', ha='center')
plt.xticks(rotation=15)
plt.tight_layout()
plt.savefig("model_speed_comparison.png", dpi=300, bbox_inches="tight")
plt.show()输出结果:
multilingual 推理速度测试: 总耗时:10.54 秒(100次重复) 平均每句耗时:0.0176 秒 每秒处理句子数:56.9 english 推理速度测试: 总耗时:5.76 秒(100次重复) 平均每句耗时:0.0096 秒 每秒处理句子数:104.2

不同硬件条件下的推理速度对比:

基于大模型的部署成本高、推理速度慢、显存占用大、使用成本高的核心问题,MiniLM 模型可以作为大模型的轻量级替代方案,在大模型调用前作为大模型的前置处理模块,通常在实际应用中,MiniLM 常与大模型配合使用,形成 "轻量模型 + 大模型" 的混合架构,面对一些常规场景,做文本的筛选或分类处理:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。