Embedding基础
向量嵌入(Vector Embedding)
解决的问题
传统应用对于语义搜索的局限
传统应用,搜索功能基本是基于不同的索引方式加上精确匹配和排序算法等实现的,本质是对文本的精确匹配,这种方法对于关键字搜索很合适,但是对于语义搜索非常弱
例如,搜索“小狗”,只能得到带有“小狗”关键字的相关结果,而得不到“金毛”、“柯基”等结果。所以传统应用需要人为的将“小狗”和“金毛”等词搭上特征标签进行关联,这样才能实现语义搜索。
但如果还需要处理非机构的数据(图像、音频、视频等),就会发现特征数量开始快速膨胀。比如一张图片有颜色、形状、纹理、边缘、对象、场景等特征,难以人为处理。
Vector Embedding
Vector Embedding 是由 AI 模型生成的,模型会根据自己的算法生成高维度的向量数据。代表着数据的不同特征,这些特征代表了数据的不同维度。例如,对于文本,这些特征可能包括词汇、语法、语义、情感、情绪、主题、上下文等。对于音频,这些特征可能包括音调、节奏、音高、音色、音量、语音、音乐等。
特征和向量
人类是通过识别不同事物之间不同的特征来识别种类的,例如不同种类的小狗。
假设通过体型这一个特征来对小狗进行排序,则可以得到一个体型特征的一维坐标和对应的数值
然而单靠一个特征不能完全区分,像照片中哈士奇、金毛和拉布拉多的体型就非常接近,如果继续观察其他特征,比如毛发的长短
这样每只狗对应一个二维坐标点,我们就能轻易的将哈士奇、金毛和拉布拉多区分开来,在这种情况下,只要特征足够多,就能够将所有的狗区分开来,最后就能得到一个高维的坐标系,世间万物都可以用一个多维坐标系来表示,它们都在一个高维的特征空间中对应着一个坐标点。
相似性测量(Similarity Measurement)
相似性测量即计算两个向量之间的距离,根据距离远近来判断它们的相似度
欧几里得距离(Euclidean Distance)
欧几里得距离算法的优点是可以反映向量的绝对距离,适用于需要考虑向量长度的相似性计算
余弦相似度(Cosine Similarity)
余弦相似度(Cosine Similarity)是一种用于衡量两个向量之间相似度的指标。它通过计算两个向 量夹角的余弦值来判断它们的相似程度。其值介于 -1 和 1 之间,通常用于文本分析和推荐系统等 领域。
- 点积计算:计算两个向量的点积,即各个对应元素相乘再求和
- 范数计算:计算每个向量的范数,通常是欧几里得范数,即元素的平方和的平方根
- 带入公式:将点积和范数代入公式,得到余弦相似度
相似性搜索(Similarity Search)
如果想要在一个海量的数据中找到和某个向量最相似的向量,需要对数据库中的每个向量进行一次比较计算,但这样的计算量是非常巨大的,所以需要一种高效的算法来解决这个问题。
高效的搜索算法有很多,其主要思想是通过两种方式提高搜索效率:
- 减少向量大小:通过降维或减少表示向量值的长度。
- 缩小搜索范围:可以通过聚类或将向量组织成基于树形、图形结构来实现,并限制搜索范围仅在最接近的簇中进行,或者通过最相似的分支进行过滤。
K-Means
在保存向量数据后,先对向量数据先进行聚类。例如下图在二维坐标系中,划定了 4 个聚类中心,然后将每个向量分配到最近的聚类中心,经过聚类算法不断调整聚类中心位置,这样就可以将向量数据分成 4 个簇。每次搜索时,只需要先判搜索向量属于哪个簇,然后再在这一个簇中进行搜索,这样就从 4 个簇的搜索范围减少到了 1 个簇,大大减少了搜索的范围。
工作原理:
- 初始化:选择
个随机点作为初始聚类中心。 - 分配:将每个向量分配给离它最近的聚类中心,形成
个簇。 - 更新:重新计算每个簇的聚类中心,即簇内所有向量的平均值。
- 迭代:重复“分配”和“更新”步骤,直到聚类中心不再变化或达到最大迭代次数。
优点:通过将向量聚类,可以在进行相似性搜索时只需计算查询向量与每个聚类中心之间的距离,而不是与所有向量计算距离。这大大减少了计算量,提高了搜索效率。这种方法在处理大规模数据时尤其有效,可以显著加快搜索速度。
缺点:在搜索的时候,如果搜索的内容正好处于两个分类区域的中间,就很有可能遗漏掉最相似的向量。
FAISS 有对其进行优化,对相邻聚类再进行一次比较
Hierarchical Navigable Small Worlds (HNSW)
通过构建树或者构建图的方式来实现近似最近邻搜索。这种方法的基本思想是每次将向量加到数据库中的时候,就先找到与它最相邻的向量,然后将它们连接起来,这样就构成了一个图。当需要搜索的时候,就可以从图中的某个节点开始,不断的进行最相邻搜索和最短路径计算,直到找到最相似的向量。
HNSW 算法是一种经典的空间换时间的算法,它的搜索质量和搜索速度都比较高,但是它的内存开销也比较大,因为不仅需要将所有的向量都存储在内存中。还需要维护一个图的结构,也同样需要存储。所以这类算法需要根据实际的场景来选择。
简单实践
from openai import OpenAI
from numpy import dot
from numpy.linalg import norm
client = OpenAI()
# 获取文本的embedding
def get_embedding(text: str):
response = client.embeddings.create(
input=text,
model="text-embedding-3-small"
)
return response.data[0].embedding
# 计算余弦相似度
def cosine_similarity(vec1, vec2):
return dot(vec1, vec2) / (norm(vec1) * norm(vec2))
def search_documents(query: str, documents: list[str]):
# 数生成查询字符串的嵌入向量
query_embedding = get_embedding(query)
# 对每个文档调用 get_embedding 函数生成文档的嵌入向量,存储在 document_embeddings 列表中
document_embeddinds = [get_embedding(doc) for doc in documents]
# 计算查询嵌入向量与每个文档嵌入向量之间的余弦相似度,存储在 similarities 列表中
similarities = [cosine_similarity(query_embedding, doc_embedding) for doc_embedding in document_embeddinds]
# 找到相似度最高的文档的索引 most_similar_index
most_similar_index = similarities.index(max(similarities))
# 返回相似度最高的文档和相似度得分
return documents[most_similar_index], max(similarities)
# 测试
documents = [
"鲨鱼是蓝色的",
"今天北京天气明朗",
"今天广东天气阴天",
"猫有九条命",
]
query = "今天北京天气怎么样?"
result, similarity = search_documents(query, documents)
print(f"最相似的文档: {result}") # 最相似的文档: 今天北京天气明朗
print(f"相似度: {similarity}") # 相似度: 0.7851910403695268
langchain 对接 embedding models
Embeddings
类是一个专为与文本嵌入模型进行交互而设计的类。 有许多嵌入模型提供商(如OpenAI、Cohere、Hugging Face等)- 这个类旨在为它们提供一个标准接口。
基本Embeddings
类提供两个方法:
embed_documents
:接受多个文本作为输入,嵌入内容,返回一个浮点数列表的列表embed_query
:接受单个文本,查询和已嵌入内容相似的向量
embedding models
# OpenAIEmbeddings
from langchain_openai import OpenAIEmbeddings
embedding_model = OpenAIEmbeddings()
# huggingface
from langchain_huggingface import HuggingFaceEmbeddings
embedding_model = HuggingFaceEmbeddings(
model_name="Alibaba-NLP/gte-Qwen2-7B-instruct",
model_kwargs={"device": "cpu"}
)
embed_documents
embedding = embedding_model.embed_documents(
[
'鲨鱼是蓝色的',
'今天北京天气明朗',
'今天广东天气阴天',
'猫有九条命',
]
)
print(len(embedding), len(embedding[0])) # 4 1536(OpenAIEmbeddings有1536个维度)
embed_query
embedded_query = embedding_model.embed_query('今天北京天气怎么样?')
print(embedded_query[:2]) # [0.011546866036951542, -0.02446899563074112]