前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >文本嵌入,语义搜索与sentence-transformers库

文本嵌入,语义搜索与sentence-transformers库

作者头像
杜逸先
发布2023-12-24 09:59:07
4120
发布2023-12-24 09:59:07
举报

近期在研究开源的 rust 实现的向量数据库 qdrant。顾名思义,向量数据是用于存储和查询向量的数据库,而向量本质上是一个多维空间中的点。如果要用向量数据库处理文本数据,就需要将文本转换为向量表示,机器学习术语叫做文本嵌入(Text Embedding)。

传统的文本嵌入方法是基于统计的,比如 TF-IDF,Word2Vec 等。随着 transformer 架构的出现和发展,基于 transformer 的文本嵌入方法也越来越流行,并且在很多任务上取得了很好的效果。sentence-transformers 就是一个基于 transformer 的文本嵌入工具包,可以用于生成句子的向量表示。

安装 sentence-transformers

安装 pytorch

可以前往 pytorch 官网 根据自己的环境选择合适的安装方式,我的设备是 RTX 2060s 的 Windows PC,安装了 windows 的 cuda 版本。

代码语言:javascript
复制
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

安装好 pytorch 后可以验证一下。

代码语言:javascript
复制
import torch

# Check if PyTorch is installed
print("PyTorch version:", torch.__version__)

# Check if CUDA is available
print("CUDA available:", torch.cuda.is_available())


x = torch.rand(5, 3)
print(x)

安装 sentence-transformers

sentence-transformers 可以直接使用 pip 安装。

代码语言:javascript
复制
pip3 install sentence-transformers

使用 sentence-transformers

sentence-transformers 提供了很多预训练模型,可以直接使用。我们这里使用的是 paraphrase-multilingual-MiniLM-L12-v2 模型,支持多语言,模型尺寸也比较大(480M)。只处理英文文本的话,可以使用 all-MiniLM-L6-v2 模型(80M)。

代码语言:javascript
复制
from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")

#Sentences are encoded by calling model.encode()
emb1 = model.encode("This is a red cat with a hat.")
emb2 = model.encode("Have you seen my red cat?")

cos_sim = util.cos_sim(emb1, emb2)
print("Cosine-Similarity:", cos_sim) # Cosine-Similarity: tensor([[0.7097]])

上述代码中,我们使用 sentence-transformers 加载了 paraphrase-multilingual-MiniLM-L12-v2 模型,并使用该模型将两个句子转换为向量表示,然后计算了两个向量的余弦相似度。

余弦相似度是一个常用的相似度度量方法,其值域为 [-1, 1],值越大表示两个向量越相似。其他的相似度度量方法还有欧氏距离、曼哈顿距离等。

我们还可以使用中文文本进行测试。

代码语言:javascript
复制
zh_sentences = ["湖南的省会是长沙", "长沙是湖南的首府", "长沙的特色小吃有臭豆腐", "绍兴的臭豆腐也很有名"]

results = []
for a, b in itertools.combinations(zh_sentences, 2):
    emb_a = model.encode(a)
    emb_b = model.encode(b)
    cos_sim = util.cos_sim(emb_a, emb_b)
    results.append((a, b, cos_sim))

results = sorted(results, key=lambda x: x[2], reverse=True)

for a, b, cos_sim in results:
    print(f"{a} {b} {cos_sim}")

输出结果如下:

代码语言:javascript
复制
湖南的省会是长沙 长沙是湖南的首府 tensor([[0.9138]])
长沙的特色小吃有臭豆腐 绍兴的臭豆腐也很有名 tensor([[0.8868]])
湖南的省会是长沙 长沙的特色小吃有臭豆腐 tensor([[0.3102]])
湖南的省会是长沙 绍兴的臭豆腐也很有名 tensor([[0.2694]])
长沙是湖南的首府 长沙的特色小吃有臭豆腐 tensor([[0.2276]])
长沙是湖南的首府 绍兴的臭豆腐也很有名 tensor([[0.1900]])

可以看到相似度最高的一组是“湖南的省会是长沙”和“长沙是湖南的首府”,这两句本质是同一个意思。第二高的一组是“长沙的特色小吃有臭豆腐”和“绍兴的臭豆腐也很有名”,分别说明了两个以臭豆腐闻名的城市。剩下的几组相关性就很低了,尤其是最后一组“长沙是湖南的首府”和“绍兴的臭豆腐也很有名”,这两句话也确实没什么关联。

使用 sentence-transformers 进行语义搜索

通过比较不同向量间的余弦相似度,我们可以找到最相似的向量,这就是语义搜索的基本原理。下面是一个来自 sentence-transformers 官方文档的例子。

代码语言:javascript
复制
embedder = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")

# Corpus with example sentences
corpus = ['A man is eating food.',
          'A man is eating a piece of bread.',
          'The girl is carrying a baby.',
          'A man is riding a horse.',
          'A woman is playing violin.',
          'Two men pushed carts through the woods.',
          'A man is riding a white horse on an enclosed ground.',
          'A monkey is playing drums.',
          'A cheetah is running behind its prey.'
          ]
corpus_embeddings = embedder.encode(corpus, convert_to_tensor=True)

# Query sentences:
queries = ['A man is eating pasta.', 'Someone in a gorilla costume is playing a set of drums.', 'A cheetah chases prey on across a field.']


# Find the closest 5 sentences of the corpus for each query sentence based on cosine similarity
top_k = min(5, len(corpus))
for query in queries:
    query_embedding = embedder.encode(query, convert_to_tensor=True)

    # We use cosine-similarity and torch.topk to find the highest 5 scores
    cos_scores = util.cos_sim(query_embedding, corpus_embeddings)[0]
    top_results = torch.topk(cos_scores, k=top_k)

    print("\n\n======================\n\n")
    print("Query:", query)
    print("\nTop 5 most similar sentences in corpus:")

    for score, idx in zip(top_results[0], top_results[1]):
        print(corpus[idx], "(Score: {:.4f})".format(score))

这里贴出第一个查询的输出。

代码语言:javascript
复制
Query: A man is eating pasta.

Top 5 most similar sentences in corpus:
A man is eating food. (Score: 0.6734)
A man is eating a piece of bread. (Score: 0.4269)
A man is riding a horse. (Score: 0.2086)
A man is riding a white horse on an enclosed ground. (Score: 0.1020)
A cheetah is running behind its prey. (Score: 0.0566)

排名最高的两句都是关于“吃”的,看来模型确实一定程度上识别到了句子的含义。把”eating food”与”eating pasta”看作从属关系的话,”eating a piece of bread”与原始查询为并列关系,得分低一点也是合理的。

事实上,sentence-transformers 还提供了 utils.semantic_search 函数,简化了语义搜索的过程。可以使用一些中文文本来测试一下。

代码语言:javascript
复制
facts = [
    "张三今年二十岁。",
    "张三今年一百斤。",
    "李四和娜娜是一对情侣。",
    "王五是一名医生。",
    "李四和兰兰是一对兄妹",
    "小明喜欢吃水果。",
    "小红会弹钢琴。",
    "刘老师教数学。",
    "这个城市有很多高楼大厦。",
]

queries = ["张三今年几岁?", "李四的女朋友是谁?", "谁是医生?", "小明喜欢吃什么?", "谁会弹钢琴?", "谁教数学?", "这个城市有什么?"]

embedder = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")

corpus_embeddings = embedder.encode(facts, convert_to_tensor=True)
query_embeddings = embedder.encode(queries, convert_to_tensor=True)

corpus_embeddings = util.normalize_embeddings(corpus_embeddings)
query_embeddings = util.normalize_embeddings(query_embeddings)

hits = util.semantic_search(
    query_embeddings, corpus_embeddings, score_function=util.dot_score
)

for i, (closest, *_) in enumerate(hits):
    print("Query:", queries[i], "Answer:", facts[closest["corpus_id"]])

输出结果如下:

代码语言:javascript
复制
Query: 张三今年几岁? Answer: 张三今年二十岁。
Query: 李四的女朋友是谁? Answer: 李四和娜娜是一对情侣。
Query: 谁是医生? Answer: 王五是一名医生。
Query: 小明喜欢吃什么? Answer: 小明喜欢吃水果。
Query: 谁会弹钢琴? Answer: 小红会弹钢琴。
Query: 谁教数学? Answer: 刘老师教数学。
Query: 这个城市有什么? Answer: 这个城市有很多高楼大厦。

可以看到,语义搜索的结果还是比较准确的,并且模型正确识别出了在“情侣”、“兄妹”两个关系中,“女朋友”与前者更接近。

总结

sentence-transformers 是一个非常好用的文本嵌入工具包,可以用于生成句子的向量表示,也可以用于语义搜索。sentence-transformers 还提供了很多预训练模型,可以根据自己的需求选择合适的模型。

本文代码中的所有向量数据都是存在内存中的,可以使用多种方式持久化向量数据,比如存储到 JSON 文件中,或者存储到关系型数据库中。不过为了更好的查询性能,我们可以使用专门的向量数据库,比如 qdrant,这也是后续文章的主题。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装 sentence-transformers
    • 安装 pytorch
      • 安装 sentence-transformers
      • 使用 sentence-transformers
        • 使用 sentence-transformers 进行语义搜索
        • 总结
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档