由于本项目的辩论数据集以中文为主,因此决定采用M3E文本向量模型。
目录
M3E模型原理
M3E 使用 in-batch 负采样的对比学习方式在句对数据集进行训练。
m3e利用对比学习的方式在大规模句对数据集上进行训练,学习将相似的句子映射到向量空间中的相近位置,将不相似的句子映射到远离的位置。对于异质文本检索任务,m3e将查询文本和候选文档分别编码为向量表示,然后计算两个向量的相似度分数(如余弦相似度),根据相似度分数从候选文档中检索出与查询最相关的文档。m3e使用了指令数据集进行训练,使得在对文本编码时可以遵从指令,提高检索效果。
一旦增强实例被编码并投影到嵌入空间中,就会应用对比学习目标。目标是最大化正对(来自同一样本的实例)之间的一致性,并最小化负对(来自不同样本的实例)之间的一致性。这鼓励模型将相似的实例拉得更近,同时将不同的实例推开。实例之间的相似度通常使用距离度量来测量,例如欧几里德距离或余弦相似度。该模型经过训练,可以最小化嵌入空间中正对之间的距离并最大化负对之间的距离。
m3e采用in-batch负采样的方式进行对比学习。具体来说,在一个batch内,将相似的句子对作为正样本,将不相似的句子对作为负样本,通过最大化正样本相似度和最小化负样本相似度的方式进行训练。
M3E模型下载
我们首先需要下载m3e-base模型,由于国内网络限制可能无法直接连接Hugging Face官方服务器(huggingface.co),因此我们采用镜像解决方案:
1. 配置镜像源(关键!)
在PowerShell中运行:
# 设置镜像源环境变量
$env:HF_ENDPOINT = "https://hf-mirror.com"
# 验证变量是否生效
echo $env:HF_ENDPOINT # 应该输出 https://hf-mirror.com
2. 强制使用镜像站下载
# 清理缓存(重要!)
Remove-Item -Recurse -Force ~\.cache\huggingface\hub
# 执行带镜像源的下载
huggingface-cli download --resume-download moka-ai/m3e-base `
--local-dir ./m3e-base ` # 模型下载路径(可更改)
--cache-dir ./hf_cache ` # 缓存路径(可更改)
--force-download
3. 验证下载结果
# 检查文件结构
Get-ChildItem ./m3e-base -Recurse -File | Select-Object Name
# 应包含以下核心文件:
# config.json
# pytorch_model.bin
# tokenizer_config.json
# special_tokens_map.json
或者直接可以在左侧文件结构中找到新增的两个文件夹 hf_cache 和 m3e-base,其中 m3e-base 文件夹中包含了上述几个核心文件:
M3E模型实现文本向量化
m3e-base模型下载完成后,我们尝试使用该模型进行文本向量化,参考代码如下:
import torch
from transformers import AutoModel, AutoTokenizer
# 初始化
model = AutoModel.from_pretrained("./m3e-base", trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained("./m3e-base", trust_remote_code=True)
# 输入文本(任意文本即可)
text = "自然语言处理技术"
# 生成向量函数
def get_embedding(text):
inputs = tokenizer(text, return_tensors="pt")
with torch.no_grad():
outputs = model(**inputs)
return outputs.last_hidden_state.mean(dim=1).cpu().numpy()
vec = get_embedding(text)
print(vec)
注意,初始化部分的模型路径要与之前下载的路径保持一致。
我们对输入文本text调用向量生成函数,打印查看最终生成的向量结果vec,如下图所示:
至此我们完成了文本向量化。
文本相似度测试
我们可以添加如下代码进行文本语义相似度的简单测试:
from sklearn.metrics.pairwise import cosine_similarity
text1 = "这个多少钱"
text2 = "这个什么价格"
text3 = "这个给我吧"
vec1 = get_embedding(text1)
vec2 = get_embedding(text2)
vec3 = get_embedding(text3)
similarity1 = cosine_similarity(vec1, vec2)[0][0]
print(f"1&2相似度: {similarity1:.4f}") # 示例输出: 0.7823
similarity2 = cosine_similarity(vec1, vec3)[0][0]
print(f"1&3相似度: {similarity2:.4f}")
similarity3 = cosine_similarity(vec2, vec3)[0][0]
print(f"2&3相似度: {similarity3:.4f}")
运行结果如下:
不难发现,1&2两句话语义更为相似,而3与1、2的语义并不相似,因此1&2的相似度更高,其余间的相似度较低,测试结果十分准确。
另外我们还可以用自己编写的欧氏距离函数进行测试,代码如下:
similarity1 = euclidean(vec1, vec2)
print(f"1&2欧氏距离: {similarity1:.4f}")
similarity2 = euclidean(vec1, vec3)
print(f"1&3欧氏距离: {similarity2:.4f}")
similarity3 = euclidean(vec2, vec3)
print(f"2&3欧氏距离: {similarity3:.4f}")
运行结果如下:
同样不难发现,1&2 语义相似度高,欧氏距离小;1&3 与 2&3 语义相似度低,欧式距离大。测试结果也十分准确。