导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • 7.search
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • 1. 什么是 Sentence Transformers?
  • 2. 环境准备
    • 2.1 检查 Python 版本
    • 2.2 安装 Sentence Transformers
    • 2.3 验证安装
  • 3. 核心概念
    • 3.1 模型(Model)
    • 3.2 编码(Encoding)
    • 3.3 相似度(Similarity)
    • 3.4 嵌入向量(Embedding)
  • 4. 快速体验:加载模型与编码句子
  • 5. 批量编码:处理多个句子
    • 5.1 批量编码示例
    • 5.2 编码选项
  • 6. 相似度计算:找出相似的文本
    • 6.1 余弦相似度
    • 6.2 找到最相似的句子
  • 7. 语义搜索:构建一个简单的搜索系统
    • 7.1 基本语义搜索
    • 7.2 改进版语义搜索
  • 8. 性能优化:批量处理与设备选择
    • 8.1 批量编码优化
    • 8.2 GPU 加速
  • 9. 实战案例:文档推荐系统
  • 10. 使用中文模型
  • 11. 常见问题与排查
    • 11.1 模型下载慢或失败
    • 11.2 内存不足
    • 11.3 中文效果不理想
    • 11.4 处理大量数据时的优化
    • 11.5 版本兼容问题
  • 12. 参考

1. 什么是 Sentence Transformers? #

Sentence Transformers 是一个 Python 库,可以将句子、段落甚至整个文档转换成固定长度的数字向量(Embedding)。这些向量能够捕捉文本的语义信息,相似的文本会产生相似的向量,所以通过比较向量就能找到语义相似的内容。

为什么要用 Sentence Transformers?

想象一下,你想在一个包含数千篇文档的知识库中找到"如何学习编程?"相关的文档。传统的关键词搜索可能会找不到,因为文档中可能用的是"如何入门编程"或"编程学习指南"等不同的表述。但 Sentence Transformers 能理解这些不同的表达方式在语义上是相似的,从而找到相关内容。

主要应用场景:

  • 语义搜索:根据意思搜索,而不仅仅是关键词匹配
  • 相似度计算:判断两段文本是否相似
  • 文档聚类:将相似的文档分组
  • 智能推荐:推荐相似的内容
  • 问答系统:找到与问题最相关的答案

前置知识补充:

  • 嵌入(Embedding):将文本转换成数字向量的过程。向量是一串数字,比如 [0.1, 0.5, -0.3, 0.8, ...]。相似的文本会有相似的向量。
  • 余弦相似度:衡量两个向量相似程度的指标,范围通常是 -1 到 1,值越接近 1 表示越相似。
  • Transformer:一种深度学习模型架构,Sentence Transformers 基于此构建。不需要深入了解原理,只需要知道它能很好地理解文本语义即可。

2. 环境准备 #

在学习 Sentence Transformers 之前,需要确保你的 Python 环境已经准备好。Sentence Transformers 支持 Python 3.8 及以上版本,并且会自动安装 PyTorch 作为底层框架。

2.1 检查 Python 版本 #

在安装之前,先检查一下你的 Python 版本是否符合要求。

# Windows PowerShell:查看 Python 版本
python --version
# macOS 终端:查看 Python 版本
python3 --version

2.2 安装 Sentence Transformers #

Sentence Transformers 依赖 PyTorch,安装时会自动安装 PyTorch(CPU 版本)。如果需要 GPU 支持,需要先单独安装支持 GPU 的 PyTorch。

# 说明:Windows PowerShell 安装 Sentence Transformers
# 先升级 pip 到最新版本,确保能正常安装依赖
python -m pip install --upgrade pip
# 安装 Sentence Transformers(会自动安装 PyTorch CPU 版)
python -m pip install sentence-transformers
# 说明:macOS / Linux 终端安装 Sentence Transformers
# 先升级 pip 到最新版本
python3 -m pip install --upgrade pip
# 安装 Sentence Transformers(会自动安装 PyTorch CPU 版)
python3 -m pip install sentence-transformers

网络加速提示:如果从 PyPI 下载较慢,可以使用国内镜像:

  • Windows: python -m pip install sentence-transformers -i https://pypi.tuna.tsinghua.edu.cn/simple
  • macOS: python3 -m pip install sentence-transformers -i https://pypi.tuna.tsinghua.edu.cn/simple

2.3 验证安装 #

安装完成后,验证一下是否安装成功。

# -*- coding: utf-8 -*-
# 说明:验证 Sentence Transformers 是否安装成功

# 说明:导入 SentenceTransformer 类
from sentence_transformers import SentenceTransformer

# 说明:尝试加载一个轻量级模型进行测试
# "all-MiniLM-L6-v2" 是一个小型的通用模型,适合快速测试
# 首次运行时会自动下载模型(可能需要一些时间)
print("正在加载模型进行测试...")
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:对一个简单的句子进行编码测试
sentence = "这是一个测试句子"
embedding = model.encode(sentence)

# 说明:检查嵌入向量的形状
print(f"安装成功!嵌入向量维度:{embedding.shape}")
print(f"前 5 个维度值:{embedding[:5]}")

# 说明:如果没有报错并输出了维度信息,说明安装成功

3. 核心概念 #

在使用 Sentence Transformers 之前,理解几个核心概念会让后续学习更顺利。

3.1 模型(Model) #

模型是用来将文本转换为向量的工具。Sentence Transformers 提供了多种预训练模型:

  • 通用模型:如 all-MiniLM-L6-v2,适用于大多数场景
  • 多语言模型:支持多种语言
  • 专业领域模型:针对特定领域优化

3.2 编码(Encoding) #

编码是将文本转换为向量的过程。输入文本(字符串),输出向量(数字数组)。

3.3 相似度(Similarity) #

通过比较向量之间的距离或相似度,可以判断文本的相似程度。常用的方法是余弦相似度。

3.4 嵌入向量(Embedding) #

嵌入向量是文本的数字表示,通常是一个固定长度的数组。向量的每个维度代表文本的某个特征。

4. 快速体验:加载模型与编码句子 #

让我们通过一个最简单的例子来快速体验 Sentence Transformers 的强大功能。

# -*- coding: utf-8 -*-
# 说明:快速体验 Sentence Transformers 的基本用法

# 说明:导入 SentenceTransformer 类
from sentence_transformers import SentenceTransformer

# 说明:加载一个预训练的模型
# "all-MiniLM-L6-v2" 是一个轻量级的通用模型,具有以下特点:
# - 向量维度:384
# - 速度快,适合学习和测试
# - 支持多种语言(包括中文)
# 首次加载时会自动下载模型(可能需要几分钟)
print("正在加载模型 'all-MiniLM-L6-v2'...")
model = SentenceTransformer("all-MiniLM-L6-v2")
print("模型加载完成!")

# 说明:准备一个示例句子
# 这可以是任何文本,比如一句话、一段话等
sentence = "自然语言处理可以帮助我们理解文本含义"

# 说明:对句子进行编码,将文本转换为向量
# encode() 方法接受字符串或字符串列表
# 返回的是 numpy 数组(numpy.ndarray)
embedding = model.encode(sentence)

# 说明:查看嵌入向量的信息
# shape 属性显示向量的维度(这里应该是 (384,) 表示 384 维向量)
print(f"\n嵌入向量维度:{embedding.shape}")

# 说明:打印向量的前 5 个数值
# 向量中的每个数值都是浮点数,通常在 -1 到 1 之间
print(f"前 5 个维度值:{embedding[:5]}")

# 说明:打印向量的类型
print(f"向量类型:{type(embedding)}")

# 说明:查看向量中数值的范围
import numpy as np
print(f"向量数值范围:[{np.min(embedding):.4f}, {np.max(embedding):.4f}]")

运行上面的代码,你会看到:

  • 模型下载和加载过程(首次运行)
  • 嵌入向量的维度信息(应该是 384)
  • 向量的前几个数值

5. 批量编码:处理多个句子 #

在实际应用中,我们通常需要同时处理多条文本。Sentence Transformers 支持批量编码,可以一次性处理多个句子,效率更高。

5.1 批量编码示例 #

# -*- coding: utf-8 -*-
# 说明:演示如何批量编码多个句子

# 说明:导入 SentenceTransformer 类
from sentence_transformers import SentenceTransformer

# 说明:加载模型
# 可以重用同一个模型对象,不需要重复加载
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备多条句子
# 这是一个字符串列表,每个元素是一个句子
# 在实际场景中,这些句子可能来自文件、数据库或用户输入
sentences = [
    "深度学习是人工智能的重要分支",
    "我喜欢在周末读技术博客",
    "今天的天气非常适合散步",
    "Python 是数据科学常用语言"
]

# 说明:批量编码所有句子
# encode() 方法可以接受字符串列表
# 返回的是一个二维 numpy 数组:
# - 行数 = 句子数量(这里是 4)
# - 列数 = 向量维度(这里是 384)
embeddings = model.encode(sentences)

# 说明:查看嵌入矩阵的形状
# shape 返回 (行数, 列数),即 (句子数, 向量维度)
print(f"嵌入矩阵形状:{embeddings.shape}")
print(f"句子数量:{embeddings.shape[0]}")
print(f"向量维度:{embeddings.shape[1]}")

# 说明:访问第一条句子的向量
print(f"\n第一条句子的向量(前 5 维):{embeddings[0][:5]}")

# 说明:遍历所有句子的向量
print("\n所有句子的向量:")
for i, sentence in enumerate(sentences):
    print(f"{i+1}. '{sentence}' -> 向量维度:{embeddings[i].shape}")

5.2 编码选项 #

encode() 方法支持多种参数,可以优化编码过程。

# -*- coding: utf-8 -*-
# 说明:演示 encode() 方法的常用参数

# 说明:导入 SentenceTransformer
from sentence_transformers import SentenceTransformer

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备测试句子
sentences = [
    "这是一个测试句子",
    "这是另一个测试句子"
]

# 说明:选项1:转换为张量(Tensor)格式
# convert_to_tensor=True 将结果转换为 PyTorch 张量
# 使用张量可以提高后续计算的性能
embeddings_tensor = model.encode(sentences, convert_to_tensor=True)
print(f"张量格式:{type(embeddings_tensor)}")
print(f"张量形状:{embeddings_tensor.shape}")

# 说明:选项2:显示进度条
# show_progress_bar=True 在处理大量数据时显示进度
# 这对于处理成千上万条文本很有用
embeddings_with_progress = model.encode(
    sentences,
    show_progress_bar=True
)

# 说明:选项3:批量大小控制
# batch_size 控制每次处理多少条文本
# 较大的 batch_size 可以提高速度,但需要更多内存
# 默认值通常是 32,可以根据你的硬件调整
embeddings_batch = model.encode(
    sentences,
    batch_size=16,  # 每次处理 16 条
    show_progress_bar=True
)

# 说明:选项4:转换为 numpy 数组(默认行为)
# convert_to_numpy=True 是默认值,返回 numpy 数组
embeddings_numpy = model.encode(
    sentences,
    convert_to_numpy=True  # 显式指定(默认值)
)
print(f"\nNumPy 格式:{type(embeddings_numpy)}")

6. 相似度计算:找出相似的文本 #

得到文本的向量表示后,我们可以通过计算向量之间的相似度来判断文本的相似程度。

6.1 余弦相似度 #

余弦相似度是最常用的相似度计算方法,值域通常在 -1 到 1 之间,值越接近 1 表示越相似。

# -*- coding: utf-8 -*-
# 说明:演示如何计算文本之间的相似度

# 说明:导入 SentenceTransformer 和 util 工具
# util 模块提供了相似度计算的便捷函数
from sentence_transformers import SentenceTransformer, util

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备示例句子
# 这些句子中有相似的(关于机器学习),也有不相关的(关于披萨)
sentences = [
    "我爱机器学习",
    "机器学习非常有趣",
    "我今天吃了披萨"
]

# 说明:编码所有句子
# convert_to_tensor=True 转换为张量格式,计算相似度时更快
embeddings = model.encode(sentences, convert_to_tensor=True)

# 说明:计算所有句子之间的余弦相似度矩阵
# cos_sim() 函数计算两个向量或向量组之间的余弦相似度
# 返回一个相似度矩阵,矩阵中的每个元素表示对应句子对的相似度
similarity_matrix = util.cos_sim(embeddings, embeddings)

# 说明:打印相似度矩阵
# 矩阵是对称的,对角线上的值都是 1(句子与自己的相似度)
print("相似度矩阵:")
print(similarity_matrix)

# 说明:逐对显示句子之间的相似度
print("\n句子之间的相似度:")
for i in range(len(sentences)):
    for j in range(i + 1, len(sentences)):
        # 说明:从张量中提取标量值
        # .item() 方法将张量中的单个值转换为 Python 的数值类型
        score = similarity_matrix[i][j].item()
        print(f"'{sentences[i]}' vs '{sentences[j]}' -> 相似度:{score:.4f}")

6.2 找到最相似的句子 #

在多个句子中,找到与给定句子最相似的一个或多个句子。

# -*- coding: utf-8 -*-
# 说明:演示如何找到最相似的句子

# 说明:导入必要的库
from sentence_transformers import SentenceTransformer, util
import numpy as np

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备候选句子库
candidate_sentences = [
    "机器学习是人工智能的重要分支",
    "我喜欢在周末读技术博客",
    "深度学习依靠神经网络",
    "Python 是数据科学常用语言",
    "今天的天气非常适合散步"
]

# 说明:准备查询句子
query = "人工智能和机器学习"

# 说明:编码查询句子和所有候选句子
query_embedding = model.encode(query, convert_to_tensor=True)
candidate_embeddings = model.encode(candidate_sentences, convert_to_tensor=True)

# 说明:计算查询句子与所有候选句子的相似度
# cos_sim() 的第一个参数是查询向量,第二个参数是候选向量列表
# 返回的结果中 [0] 表示第一个查询的结果(因为我们只有一个查询)
similarities = util.cos_sim(query_embedding, candidate_embeddings)[0]

# 说明:找到相似度最高的句子
# 使用 argmax 找到相似度最大的索引
best_match_idx = np.argmax(similarities.cpu().numpy())
best_score = similarities[best_match_idx].item()

print(f"查询:'{query}'")
print(f"\n最相似的句子:")
print(f"  - {candidate_sentences[best_match_idx]}")
print(f"  - 相似度:{best_score:.4f}")

# 说明:找到相似度最高的前 3 个句子
top_k = 3
# 说明:使用 argsort 对相似度进行排序,取最大的 k 个
top_indices = np.argsort(-similarities.cpu().numpy())[:top_k]

print(f"\n相似度最高的前 {top_k} 个句子:")
for idx, rank in enumerate(top_indices, 1):
    score = similarities[rank].item()
    print(f"{idx}. '{candidate_sentences[rank]}' (相似度:{score:.4f})")

余弦相似度说明:余弦相似度衡量的是两个向量在方向上的相似程度,而不是距离。值域范围是 -1 到 1:

  • 1:完全相同或语义完全一致
  • 接近 1:非常相似
  • 0:不相关
  • -1:完全相反

7. 语义搜索:构建一个简单的搜索系统 #

语义搜索可以根据意思搜索,而不仅仅是关键词匹配。下面我们构建一个简单的语义搜索系统。

7.1 基本语义搜索 #

# -*- coding: utf-8 -*-
# 说明:演示如何构建一个简单的语义搜索系统

# 说明:导入必要的库
from sentence_transformers import SentenceTransformer, util
import numpy as np

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备语料库(待搜索的文档集合)
# 在实际应用中,这些可能来自数据库、文件或 API
corpus = [
    "猫咪在沙发上睡觉",
    "狗狗在草地上奔跑",
    "程序员正在调试代码",
    "美食博主在分享菜谱",
    "学生正在准备考试"
]

# 说明:准备查询文本(用户的问题)
query = "写代码的人在干什么"

# 说明:编码语料库和查询
# 使用 convert_to_tensor=True 可以提高后续计算速度
corpus_embeddings = model.encode(corpus, convert_to_tensor=True)
query_embedding = model.encode(query, convert_to_tensor=True)

# 说明:计算查询与语料库中每个文档的余弦相似度
# cos_sim() 返回一个张量,[0] 表示第一个查询的结果
cos_scores = util.cos_sim(query_embedding, corpus_embeddings)[0]

# 说明:找到相似度最高的前 top_k 个结果
top_k = min(3, len(corpus))  # 确保不超过语料库的大小

# 说明:使用 argpartition 高效地找到前 top_k 个最大的值
# argpartition 比完整排序更快,因为我们只需要前 k 个
top_results = np.argpartition(-cos_scores.cpu().numpy(), range(top_k))[:top_k]

# 说明:对结果按相似度降序排序
# 先按相似度排序,然后取前 top_k 个
top_results_sorted = sorted(top_results, key=lambda x: cos_scores[x].item(), reverse=True)

# 说明:打印搜索结果
print(f"查询:'{query}'\n")
print(f"找到 {len(top_results_sorted)} 个最相关的结果:\n")
for idx, result_idx in enumerate(top_results_sorted, 1):
    score = cos_scores[result_idx].item()
    print(f"{idx}. {corpus[result_idx]}")
    print(f"   相似度:{score:.4f}\n")

7.2 改进版语义搜索 #

下面是一个更完善的语义搜索示例,包含更好的结果展示和错误处理。

# -*- coding: utf-8 -*-
# 说明:改进版语义搜索,包含更好的结果展示

# 说明:导入必要的库
from sentence_transformers import SentenceTransformer, util
import numpy as np

# 说明:定义语义搜索函数
def semantic_search(query: str, corpus: list, model, top_k: int = 3):
    """
    执行语义搜索

    参数:
        query (str): 查询文本
        corpus (list): 待搜索的文档列表
        model: SentenceTransformer 模型
        top_k (int): 返回最相关的结果数量

    返回:
        list: 包含 (文档, 相似度分数) 的列表
    """
    # 说明:检查输入是否为空
    if not query or not corpus:
        print("错误:查询文本和语料库不能为空")
        return []

    # 说明:编码查询和语料库
    query_embedding = model.encode(query, convert_to_tensor=True)
    corpus_embeddings = model.encode(corpus, convert_to_tensor=True)

    # 说明:计算相似度
    cos_scores = util.cos_sim(query_embedding, corpus_embeddings)[0]

    # 说明:找到前 top_k 个最相似的结果
    top_k = min(top_k, len(corpus))
    top_indices = np.argsort(-cos_scores.cpu().numpy())[:top_k]

    # 说明:构造结果列表
    results = []
    for idx in top_indices:
        score = cos_scores[idx].item()
        results.append({
            "document": corpus[idx],
            "score": score,
            "rank": len(results) + 1
        })

    return results

# 说明:主程序入口
if __name__ == "__main__":
    # 说明:加载模型
    print("正在加载模型...")
    model = SentenceTransformer("all-MiniLM-L6-v2")
    print("模型加载完成!\n")

    # 说明:准备语料库
    corpus = [
        "猫咪在沙发上睡觉",
        "狗狗在草地上奔跑",
        "程序员正在调试代码",
        "美食博主在分享菜谱",
        "学生正在准备考试"
    ]

    # 说明:执行搜索
    query = "写代码的人在干什么"
    results = semantic_search(query, corpus, model, top_k=3)

    # 说明:显示结果
    print(f"查询:'{query}'\n")
    print("搜索结果:")
    print("-" * 60)
    for result in results:
        print(f"排名 {result['rank']}: {result['document']}")
        print(f"相似度:{result['score']:.4f}")
        print("-" * 60)

8. 性能优化:批量处理与设备选择 #

当需要处理大量文本时,可以使用批处理和 GPU 加速来提高效率。

8.1 批量编码优化 #

# -*- coding: utf-8 -*-
# 说明:演示如何使用批量处理提高编码效率

# 说明:导入必要的库
import time
from sentence_transformers import SentenceTransformer

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:生成大量测试文本
# 在实际场景中,这些文本可能来自文件或数据库
texts = [f"这是第 {i} 条示例文本,用于测试批量编码性能" for i in range(100)]

# 说明:方法1:使用默认批量大小(通常是 32)
print("方法1:使用默认批量大小")
start_time = time.time()
embeddings_default = model.encode(
    texts,
    show_progress_bar=True  # 显示进度条
)
time_default = time.time() - start_time
print(f"耗时:{time_default:.2f} 秒\n")

# 说明:方法2:指定较大的批量大小
# 较大的 batch_size 可以提高速度,但需要更多内存
print("方法2:使用较大的批量大小(batch_size=64)")
start_time = time.time()
embeddings_large_batch = model.encode(
    texts,
    batch_size=64,  # 每次处理 64 条文本
    show_progress_bar=True
)
time_large = time.time() - start_time
print(f"耗时:{time_large:.2f} 秒\n")

# 说明:方法3:指定较小的批量大小(适合内存较小的情况)
print("方法3:使用较小的批量大小(batch_size=8)")
start_time = time.time()
embeddings_small_batch = model.encode(
    texts,
    batch_size=8,  # 每次处理 8 条文本
    show_progress_bar=True
)
time_small = time.time() - start_time
print(f"耗时:{time_small:.2f} 秒\n")

# 说明:比较结果是否一致
print(f"结果一致性检查:")
print(f"默认批量 vs 大批量:{embeddings_default.shape == embeddings_large_batch.shape}")
print(f"默认批量 vs 小批量:{embeddings_default.shape == embeddings_small_batch.shape}")

8.2 GPU 加速 #

如果有 GPU 可用,可以显著提高处理速度。

# -*- coding: utf-8 -*-
# 说明:演示如何使用 GPU 加速(如果有可用的 GPU)

# 说明:导入 torch 库用于检测 GPU
import torch
from sentence_transformers import SentenceTransformer

# 说明:检测是否有可用的 GPU
# cuda.is_available() 返回 True 表示系统有可用的 NVIDIA GPU
# 注意:macOS 上的 M 系列芯片使用 MPS(Metal Performance Shaders),不是 CUDA
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"当前使用设备:{device}")

# 说明:如果没有 GPU,会使用 CPU(速度会慢一些,但功能完全正常)
if device == "cpu":
    print("未检测到 GPU,将使用 CPU 运行(速度较慢)")

# 说明:加载模型到指定设备
# device 参数指定模型运行在哪个设备上
model = SentenceTransformer("all-MiniLM-L6-v2", device=device)

# 说明:准备测试文本
texts = [f"示例文本 {i}" for i in range(100)]

# 说明:编码时指定设备
# device 参数确保编码过程在指定设备上执行
embeddings = model.encode(
    texts,
    batch_size=32,
    show_progress_bar=True,
    device=device  # 明确指定设备
)

print(f"\n编码完成,得到 {embeddings.shape[0]} 个嵌入向量")
print(f"每个向量维度:{embeddings.shape[1]}")

9. 实战案例:文档推荐系统 #

下面是一个完整的实战案例,封装成一个文档推荐类,可以根据查询找到最相似的文档。

# -*- coding: utf-8 -*-
# 说明:实战案例 - 文档推荐系统
# 封装一个完整的文档相似度推荐类

# 说明:导入必要的库
from sentence_transformers import SentenceTransformer, util
import numpy as np

# 说明:定义文档推荐类
class DocumentRecommender:
    """
    文档推荐系统

    功能:
    - 添加文档到系统
    - 根据查询文本推荐最相似的文档
    """

    # 说明:初始化方法
    def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
        """
        初始化文档推荐系统

        参数:
            model_name (str): 要使用的模型名称,默认为 "all-MiniLM-L6-v2"
        """
        # 说明:加载模型
        print(f"正在加载模型:{model_name}...")
        self.model = SentenceTransformer(model_name)
        print("模型加载完成!")

        # 说明:初始化文档列表和嵌入向量
        self.documents = []  # 存储文档文本
        self.doc_embeddings = None  # 存储文档的嵌入向量

    # 说明:添加文档的方法
    def add_documents(self, docs):
        """
        添加文档到推荐系统

        参数:
            docs (list): 文档列表,每个元素是一个字符串
        """
        # 说明:将新文档添加到文档列表
        self.documents.extend(docs)

        # 说明:对所有文档重新编码
        # 每次添加新文档后,需要重新编码所有文档(包括新添加的)
        print(f"正在编码 {len(self.documents)} 条文档...")
        self.doc_embeddings = self.model.encode(
            self.documents,
            convert_to_tensor=True,  # 使用张量格式,提高计算速度
            show_progress_bar=True
        )
        print("文档编码完成!")

    # 说明:推荐方法
    def recommend(self, query: str, top_k: int = 3):
        """
        根据查询文本推荐最相似的文档

        参数:
            query (str): 查询文本
            top_k (int): 返回最相似的文档数量,默认为 3

        返回:
            list: 包含 (文档, 相似度分数) 的元组列表
        """
        # 说明:检查是否有文档
        if not self.documents:
            print("错误:系统中没有文档,请先添加文档")
            return []

        # 说明:编码查询文本
        query_emb = self.model.encode(query, convert_to_tensor=True)

        # 说明:计算查询与所有文档的相似度
        # cos_sim() 返回一个张量,[0] 表示第一个查询的结果
        scores = util.cos_sim(query_emb, self.doc_embeddings)[0]

        # 说明:找到相似度最高的 top_k 个文档
        # argsort 对相似度进行排序,取最大的 top_k 个
        # 使用负数是因为 argsort 默认是升序,我们要降序
        top_indices = np.argsort(-scores.cpu().numpy())[:top_k]

        # 说明:构造结果列表
        # 每个元素是一个元组:(文档文本, 相似度分数)
        results = []
        for idx in top_indices:
            doc = self.documents[idx]
            score = float(scores[idx])  # 将张量中的值转换为 Python 浮点数
            results.append((doc, score))

        return results

    # 说明:获取统计信息
    def get_stats(self):
        """
        获取系统的统计信息

        返回:
            dict: 包含文档数量的字典
        """
        return {
            "文档数量": len(self.documents)
        }


# 说明:主程序入口,演示如何使用
if __name__ == "__main__":
    # 说明:创建推荐系统实例
    rec = DocumentRecommender()

    # 说明:添加文档到系统
    docs = [
        "机器学习是人工智能的一个分支",
        "我喜欢做饭并分享美食",
        "Python 是数据分析常用语言",
        "深度学习依靠神经网络"
    ]
    rec.add_documents(docs)

    # 说明:执行查询和推荐
    query = "如何入门深度学习"
    print(f"\n查询:'{query}'")
    print("\n推荐结果:")
    print("-" * 60)

    results = rec.recommend(query, top_k=2)
    for idx, (doc, score) in enumerate(results, 1):
        print(f"{idx}. {doc}")
        print(f"   相似度:{score:.4f}")
        print("-" * 60)

    # 说明:显示统计信息
    print(f"\n系统统计:{rec.get_stats()}")

10. 使用中文模型 #

默认模型对中文的支持可能不够好。如果需要更好的中文效果,可以使用专门针对中文优化的模型。

# -*- coding: utf-8 -*-
# 说明:演示如何使用中文优化的模型

# 说明:导入 SentenceTransformer
from sentence_transformers import SentenceTransformer, util

# 说明:加载中文优化的模型
# "shibing624/text2vec-base-chinese" 是一个专门针对中文优化的模型
# 首次运行时会自动下载模型
print("正在加载中文模型...")
model = SentenceTransformer("shibing624/text2vec-base-chinese")
print("模型加载完成!")

# 说明:准备中文测试文本
chinese_texts = [
    "机器学习是人工智能的重要分支",
    "深度学习依靠神经网络",
    "自然语言处理可以理解文本语义",
    "计算机视觉可以识别图像内容"
]

# 说明:编码中文文本
embeddings = model.encode(chinese_texts, show_progress_bar=True)

# 说明:测试相似度
query = "人工智能和机器学习"
query_embedding = model.encode(query, convert_to_tensor=True)
corpus_embeddings = model.encode(chinese_texts, convert_to_tensor=True)

# 说明:计算相似度
scores = util.cos_sim(query_embedding, corpus_embeddings)[0]

# 说明:显示结果
print(f"\n查询:'{query}'")
print("\n相似度结果:")
for text, score in zip(chinese_texts, scores):
    print(f"  {text}: {score:.4f}")

模型选择建议:

  • 通用场景:使用 all-MiniLM-L6-v2(速度快,效果不错)
  • 中文场景:使用 shibing624/text2vec-base-chinese(中文效果更好)
  • 多语言场景:使用 paraphrase-multilingual-MiniLM-L12-v2(支持多种语言)

11. 常见问题与排查 #

在使用 Sentence Transformers 时,可能会遇到一些常见问题。本节提供解决方案。

11.1 模型下载慢或失败 #

问题:首次加载模型时下载速度很慢或失败。

解决方案:

# -*- coding: utf-8 -*-
# 说明:演示如何使用镜像站点加速模型下载

# 说明:设置环境变量使用镜像站点
# 在加载模型之前设置这个环境变量
import os
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'

# 说明:然后再加载模型
from sentence_transformers import SentenceTransformer

# 说明:现在加载模型会使用镜像站点
model = SentenceTransformer("all-MiniLM-L6-v2")
print("模型加载完成!")

或者使用命令行的方式:

# Windows PowerShell:设置环境变量后运行 Python 脚本
$env:HF_ENDPOINT="https://hf-mirror.com"
python your_script.py
# macOS / Linux 终端:设置环境变量后运行 Python 脚本
export HF_ENDPOINT=https://hf-mirror.com
python3 your_script.py

11.2 内存不足 #

问题:处理大量文本时内存不足。

解决方案:

# -*- coding: utf-8 -*-
# 说明:演示如何通过减小批量大小来降低内存使用

# 说明:导入 SentenceTransformer
from sentence_transformers import SentenceTransformer

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备大量文本
texts = [f"文本 {i}" for i in range(10000)]

# 说明:使用较小的批量大小
# 较小的 batch_size 会使用更少的内存,但可能稍微慢一些
embeddings = model.encode(
    texts,
    batch_size=8,  # 减小批量大小(默认可能是 32)
    show_progress_bar=True
)

print(f"编码完成,共 {embeddings.shape[0]} 条文本")

11.3 中文效果不理想 #

问题:使用默认模型处理中文时,效果不够好。

解决方案:使用专门的中文模型(参考第 10 节)。

11.4 处理大量数据时的优化 #

问题:处理成千上万条文本时速度慢。

解决方案:

# -*- coding: utf-8 -*-
# 说明:演示如何优化大量数据的处理

# 说明:导入必要的库
from sentence_transformers import SentenceTransformer
import time

# 说明:加载模型
model = SentenceTransformer("all-MiniLM-L6-v2")

# 说明:准备大量数据
large_texts = [f"这是第 {i} 条文本,用于测试性能优化" for i in range(1000)]

# 说明:优化策略1:增大批量大小(如果内存允许)
print("策略1:增大批量大小")
start = time.time()
embeddings1 = model.encode(
    large_texts,
    batch_size=64,  # 增大批量大小
    show_progress_bar=True
)
time1 = time.time() - start
print(f"耗时:{time1:.2f} 秒\n")

# 说明:优化策略2:使用多线程(如果 CPU 核心数足够)
print("策略2:使用多线程编码")
start = time.time()
embeddings2 = model.encode(
    large_texts,
    batch_size=32,
    show_progress_bar=True,
    # 注意:某些版本的 sentence-transformers 可能不支持此参数
    # 请根据实际情况调整
)
time2 = time.time() - start
print(f"耗时:{time2:.2f} 秒")

# 说明:策略3:缓存已编码的结果
# 如果某些文档不会改变,可以先编码并保存,避免重复计算
print("\n策略3:缓存编码结果")
# 这里只是示例,实际使用时可以将结果保存到文件或数据库
# 例如:使用 numpy.save() 保存,使用 numpy.load() 加载

11.5 版本兼容问题 #

问题:不同库版本之间可能不兼容。

解决方案:

# Windows PowerShell:升级到最新稳定版本
python -m pip install --upgrade torch transformers sentence-transformers
# macOS / Linux 终端:升级到最新稳定版本
python3 -m pip install --upgrade torch transformers sentence-transformers

12. 参考 #

  • Sentence Transformers 官方文档
  • Hugging Face Model Hub

访问验证

请输入访问令牌

Token不正确,请重新输入