利用Embedding优化搜索功能

我们继续用Gemini学习LLM编程之旅。

Embedding是一种自然语言处理 (NLP) 技术,可将文本转换为数值向量。Embedding捕获语义含义和上下文,从而导致具有相似含义的文本具有更接近的Embedding。例如,句子“我带我的狗去看兽医”和“我带我的猫去看兽医”在向量空间中的Embedding会比较接近,因为它们都描述了相似的上下文。

Gemini 接口中的嵌入服务(models/embedding-001)可为单词、短语和句子生成embedding,然后生成的embedding可用于 NLP 任务,例如语义搜索、文本分类和聚类等。

本期文章参考徐文浩老师的《语义检索,利用Embedding优化你的搜索功能》(https://time.geekbang.org/column/article/644795) ,学习如何用embedding优化搜索,即通过语义来搜索(在给定一段输入文本的情况下检索语义相似的文本),而非传统的基于关键词分词的搜索。徐文浩老师的课程中用OpenAI的text-davinci-003模型生成淘宝产品标题,用text-embedding-ada-002生成Embedding。

由于gemini-pro对中文支持不好,models/embedding-001甚至完全不支持中文,我们会用gemini-pro生成Amazon的英文产品标题,用models/embedding-001生成Embedding。

一开始没留意谷歌的models/embedding-001不支持中文(文档没提),只是奇怪计算出来的余弦相似度很多一样,还以为自己的代码错了。后面用代码具体测试,才发现它生成的中文embedding一模一样。

human = genai.embed_content(model="models/embedding-001", content='人类', task_type="retrieval_query",
)['embedding']
mouse = genai.embed_content(model="models/embedding-001", content='鼠标', task_type="retrieval_query",
)['embedding']# 比较两个嵌入向量列表是否相同
embeddings_are_equal = human == mouse
print(f"人类和鼠标的嵌入向量是否相同: {embeddings_are_equal}")

得到的结果是:

人类和鼠标的嵌入向量是否相同: True

设置环境

先参考《免费使用谷歌Gemini模型学习LLM编程》(https://juejin.cn/post/7315009908302331923)获取API Key,设置环境变量。

import osimport google.generativeai as genai
import pandas as pd
from dotenv import find_dotenv, load_dotenv
import numpy as np_ = load_dotenv(find_dotenv())  # read local .env fileGOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)model = genai.GenerativeModel("gemini-pro")

通过Gemini Pro生成实验数据

一般可以从网上下载或者直接用机器学习软件包自带的数据集来做实验,现在大语言模型来了,我们也可以直接让大语言模型帮我们生成实验数据。只要不是用于训练大模型,直接使用没有任何问题。

(参考字节跳动账户被封禁)

data = model.generate_content(" Please generate 50 product titles from Amazon, each about 50 characters long. The category is 3C digital products. The title often contains some promotional information, one per line.").text
digital_product_names = data.strip().split('\n')
digital_df = pd.DataFrame({'product_name': digital_product_names})
digital_df.product_name = digital_df.product_name.apply(lambda x: x.split('.')[1].strip())
digital_df.head()

让gemini-pro生成amazon的3C类别的产品标题,按行分隔,并加载到panda的DataFrame。注意,大模型返回的结果里面带了标号,通过这行代码去掉了:digital_df.product_name = digital_df.product_name.apply(lambda x: x.split('.')[1].strip())

输出结果:

All-in-One 4K UHD Smart TV with Built-in Streaming Apps and Voice Control
High-Performance Gaming PC with RGB Lighting and Liquid Cooling
Ultra-Thin Laptop with Long Battery Life and Fast Charging
Premium Noise-Canceling Headphones with Hi-Res Audio and Multipoint Connectivity
Powerful Wireless Router with Mesh Technology for Whole-Home Coverage

为了让实验数据多样化,再让模型生成女士的衣服、箱包类的商品标题。

clothes_data = model.generate_content("""Please generate 50 product titles from Amazon, each about 50 characters long. The category is women’s clothing, bags, etc. The title often contains some promotional information, one per line.""").text
clothes_product_names = clothes_data.strip().split('\n')
clothes_df = pd.DataFrame({'product_name': clothes_product_names})
clothes_df.product_name = clothes_df.product_name.apply(lambda x: x.split('.')[1].strip())
clothes_df.head()

输出结果:

Women’s Lightweight Summer Maxi Dress with Pockets: Flattering Flowy Beach Style
Crossbody Bag for Women: Durable Leather with Adjustable Strap and Multiple Compartments
“Women’s Designer Handbag Set: Tote, Crossbody, and Clutch Bag in One”
Vegan Leather Backpack for Women: Convertible to Tote or Shoulder Bag
Women’s Casual Shift Dress with Ruffle Sleeves: Comfortable and Stylish

再把两个DataFrame拼接起来作为我们最终的实验数据:

df = pd.concat([digital_df, clothes_df], axis=0)
df = df.reset_index(drop=True)
df

输出结果:
在这里插入图片描述

通过 Embedding 进行语义搜索

传统的搜索主要有两种:一种是数据库,不管是关系型数据库还是NoSQL数据库,都提供like类型的搜索,这种方式是直接查找某个字段是否包含某个关键词;另外一种是像ElasticSearch之类的全文检索,通过倒排索引的方式,先建立关键词到具体文档的映射,在搜索的时候,匹配上关键词,再返回对应的文档列表。

这两种方式具体到细节,当然都有很多可以优化的地方,但是本质上,都是通过关键词匹配,你的关键词选错了,就找不到对应的商品、文档等。所以,很多电商的商家,在将产品上架的时候,会在系统允许的范围内将商品的标题填上各种各样的关键词,目的就是为了尽可能匹配上关键词,获得更多搜索的流量。后台也会提供类似“买家搜索词”之类的工具,方便商家了解用户可能用什么关键词来搜索。这种“人工”智能比较费人工,但是关键词堆多了,也能有比较好的效果。

这个策略最大的缺点就是如果产品没有包含那个关键词,即使是同义词,语义上很接近,也搜索不到。

现在有了Embedding 的接口,就可以把一段文本的语义表示成一段向量。而向量之间是可以计算距离的。我们可以把用户的搜索也通过 Embedding 接口变成向量,然后把它和所有的商品的标题向量计算一下点积或者余弦距离,找出离我们搜索词最近的几个向量。最近的几个向量,其实就是语义和这个商品相似的,而并不需要相同的关键词。

下面来看一下具体的步骤。

保存文档的Embedding

首先,我们要把随机生成的所有商品标题,都计算出它们的Embedding,然后保存下来。

product_names_list = df.product_name.tolist()
#Note: Specifying a title for RETRIEVAL_DOCUMENT provides better quality embeddings for retrieval.
embeddings = (genai.embed_content(model="models/embedding-001", content=product_names_list, task_type="retrieval_document", title="amazon product names"))['embedding']
print(f"embeddings len:{len(embeddings)}")
df["embedding"] = embeddings
df.to_parquet("data/amazon_product_title.parquet", index=False)
df.to_csv("data/amazon_product.csv", index=False)
df

注意谷歌生成embedding和其他的不太一样,有一个task_type,可能是针对不同的应用场景做了优化。

这里我们是用来做文档检索,而且现在是建立文档的“数据库”,所以task_type设置为retrieval_document,针对retrieval_document,还可以设置一个可选的title;等下查询的时候task_type要设为retrieval_query。

谷歌的models/embedding-001生成embedding还是很方便的,传入字符串列表就可以直接生成向量列表。

Task TypeDescription
RETRIEVAL_QUERYSpecifies the given text is a query in a search/retrieval setting.
RETRIEVAL_DOCUMENTSpecifies the given text is a document in a search/retrieval setting.
SEMANTIC_SIMILARITYSpecifies the given text will be used for Semantic Textual Similarity (STS).
CLASSIFICATIONSpecifies that the embeddings will be used for classification.
CLUSTERINGSpecifies that the embeddings will be used for clustering.

搜索产品

接下来我们定义一个 search_product 的搜索函数,接受三个参数:df 代表用于搜索的数据源, query 代表用于搜索的搜索词,n 代表搜索返回多少条记录。

而这个函数做了这三件事情:

  • 调用谷歌的接口将搜索词转换成 Embedding。
  • 将这个 Embedding 和 DataFrame 里的每一个 Embedding 都计算一下点积。
  • 根据点积去排序,返回点积最大的 n 个标题。

点积的值可以在 -1 和 1 之间(包含 -1 和 1)。如果两个向量之间的点积为 1,则这两个向量的方向相同。如果点积值为 0,则这些向量彼此正交或不相关。最后,如果点积为 -1,则向量指向相反方向并且彼此不相似。

def search_product(df, query, n=5, pprint=True):product_embedding = genai.embed_content(model="models/embedding-001", content=query, task_type="retrieval_query")['embedding']df["dot_products"] = df.embedding.apply(lambda x: np.dot(x, product_embedding))results = (df.sort_values("dot_products", ascending=False).head(n).loc[:, ["product_name", "dot_products"]])if pprint:for index, row in results.iterrows():print(f"{row['product_name']} - dot_products: {row['dot_products']}")return resultsresults = search_product(df, "elegant dress for summer for beach vacations", n=3)

我们搜索 elegant dress for summer for beach vacations 得到结果:

Women's Lightweight Summer Maxi Dress with Pockets: Flattering Flowy Beach Style - dot_products: 0.6860313467181551
Women's Mini Dress: Sexy and Elegant, Perfect for a Night Out - dot_products: 0.6560099266011095
Women's Floral Print Wrap Dress: Flattering Design for Any Occasion - dot_products: 0.6244976428038793

搜索英文的结果还可以。

注意事项

在实际项目中使用这种方式优化的时候,或者是使用向量数据库来找匹配的文档的时候,要注意的是,返回的文档只是库里和你的查询最相似的。但是即使相差十万八千里,向量的距离也不太可能是0或者-1。以我们这次实验的数据为例,“elegant dress for summer for beach vacations”和”VR Gaming Controller with Haptic Feedback and Motion Controls”是最不相关的,点积也有0.415979671305287。所以,可能要根据具体的项目,低于某个数值就认为是不相干的,就不要返回了。

参考

  1. https://time.geekbang.org/column/article/644795
  2. https://ai.google.dev/docs/embeddings_guide?hl=en
  3. https://ai.google.dev/examples/doc_search_emb

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/317111.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023.12.27 关于 Redis 数据类型 List 常用命令

目录 List 类型基本概念 List 类型特点 List 操作命令 LPUSH LPUSHX RPUSH RPUSHX LRANGE LPOP RPOP LINDEX LINSERT LREM LTRIM LSET 阻塞版本的命令 阻塞版本 和 非阻塞版本的区别 BLPOP & BRPOP List 类型基本概念 Redis 中的列表(list&am…

LobeChat:搭建你的私人 GPT!

前端训练营:1v1私教,终身辅导计划,帮你拿到满意的 offer。 已帮助数百位同学拿到了中大厂 offer。欢迎来撩~~~~~~~~ Hello,大家好,我是 Sunday。 之前有同学问我:“老师,我想要搭建一个个人的 …

老师的职业特点是什么

老师,这个职业看似平凡,却承载着塑造下一代的重任。不仅传授知识,更是引导学生成为自己期望的人。 具备扎实的专业知识。这不仅是教好课的基础,也是赢得学生尊敬的关键。时刻保持对所教专业的热爱和关注,通过自己的经验…

Android 串口协议

前言 本协议是 Android 应用端与主控板之间的通信协议,是串行通信协议。 协议要求同一时间只能有两个通讯端点在相互通讯,采用小端传输数据。 硬件层基于RS485协议,采取半双工,一主多从的通讯模式。Android定义为主机&#xff0c…

声明式管理方(yaml)文件

声明式管理方(yaml)文件: 1、适合对资源的修改操作 2、声明式管理依赖于yaml文件,所有的内容都在yaml文件当中。 3、编辑好的yaml文件需要依靠陈述是还是要依靠陈述式的命令发布到k8s集群当中 create只能创建,不能更新。从指定yaml文件中读取配置&#…

C++Qt6 哈夫曼编码求解 数据结构课程设计 | JorbanS

一、 问题描述 在进行程序设计时,通常给每一个字符标记一个单独的代码来表示一组字符,即编码。在进行二进制编码时,假设所有的代码都等长,那么表示 n 个不同的字符需要 位,称为等长编码。如果每个字符的使用频率相等&…

【计算机毕业设计】SSM实现的在线农产品商城

项目介绍 本项目分为前后台,且有普通用户与管理员两种角色。 用户角色包含以下功能: 用户登录,查看首页,按分类查看商品,查看新闻资讯,查看关于我们,查看商品详情,加入购物车,查看我的订单,提交订单,添加收获地址,支付订单等功能。 管理员角色包含以…

SD-WAN组网方式详解

企业网络的演进势不可挡,对于高效、可靠的网络连接需求日益增加。SD-WAN(软件定义广域网)作为一项创新的网络技术,备受企业青睐并得到广泛应用。SD-WAN提供了多种灵活的组网方式,以满足企业多样化的需求和不同的网络环…

算法28:力扣64题,最小路径和------------样本模型

题目: 给定一个二维数组matrix,一个人必须从左上角出发,最后到达右下角 。沿途只可以向下或者向右走,沿途的数字都累加就是距离累加和 * 返回累加和最小值 思路: 1. 既然是给定二维数组matrix,那么二维数…

Vue - 多行文本“展开、收起”功能

TextClamp 使用 js 实现文本展开、收起,并非纯 CSS 实现。 Props: fontSize:Number,默认:14lines:Number,默认:1lineHeight:Number,默认:20 F…

获取网页信息

每次copy & paste总是很麻烦,现在有点问题,先记录下来。 需求:获取url 里Feature list,并输出表格形式 可以用Convert curl commands to code:得到get请求的header,cookie等 import requests import…

天然药物,到2028年市场规模将达到 3082亿美元

天然药物,也称为草药或传统药物,是指将植物、矿物和动物产品等天然物质用于药用目的。近年来,人们对天然药物作为传统药物的替代品越来越感兴趣,这导致了天然药物市场的增长。全球天然药物市场: 全球天然药物市场预计从…