2025-04-16 15:56:09
随着大型语言模型 (LLM) 的兴起,Embedding 成为了一个备受关注的热词。那什么是 embedding 呢?embedding 中文翻译过来是嵌入
,本质上就是一个向量,类似[0.1,0.8,0.6….]这样的形式,在很多文章里,我们可以看到嵌入向量(embedding vector)这样的描述。
Embedding 将所有模型的输入(例如文本或图片)转化为一系列数字向量表示,相似的输入在向量空间中的距离也更接近。近期备受关注的 RAG (Retrieval Augmented Generation) 技术,正是将所有私有知识内容转化为向量,并将用户提出的问题也转化为向量,然后检索出与问题最相似的知识片段,再将这些知识传递给大型模型,使其整合生成完整的答案。
在 embedding 技术广泛应用之前,One-Hot 编码曾是主流方法。如果和我们比较熟悉的 One-Hot 对比起来理解,直观上看,embedding 可以理解为对 One-Hot 编码进行平滑处理的结果,而 One-Hot 编码则类似于对 Embedding 进行最大池化操作。与 One-Hot 的自定义编码方式不同,embedding 其实是在大模型训练过程中,通过对比学习的方式生成的模型输出。这个过程可以看作是 LLM 训练的一个关键组成部分,这也就解释了一个常见问题,在配置本地的大模型客户端的时候,会要求选择 embedding 的模型,这个模型可以和 LLM 保持一个系列,也可以选择其他公司的模型,但一定要保证模型输出的向量维度和 LLM 适配。
对于我们输入大模型的 prompt,首先会被拆分成独立的 token,然后每一个 token会通过一个 embedding 模型得到对应的 embedding,每个 embedding 是一个高维的向量,比如 OpenAI 的 text-embedding-3-small
模型生成的就是 1536 维的向量,不同模型生成 embedding 的方式和维度都不相同,但是每个 embedding 向量都包含了对应 token 的语法和语义的特征。
常用的 embedding 模型性能可以通过https://huggingface.co/spaces/mteb/leaderboard 查到。
使用 OpenAI 原生 SDK
from openai import OpenAI
import os
api_key=os.environ.get("OPENAI_API_KEY")
client = OpenAI(
api_key= api_key,
base_url = "https://burn.hair/v1"
)
input = "This is a test document."
query_result = client.embeddings.create(model="text-embedding-3-small", input=input)
print(query_result.data[0].embedding)
使用 LangChain 的 SDK
def get_openai_embedding():
# 获取当前脚本所在的目录
current_dir = Path(__file__).resolve().parent
# 获取上一层目录的路径(项目根目录)
project_root = current_dir.parent
# 加载项目根目录中的 .env 文件
env_path = project_root / '.env'
load_dotenv(dotenv_path=env_path)
# 创建并返回 OpenAIEmbeddings 实例
return OpenAIEmbeddings(
openai_api_key=os.environ.get("OPENAI_API_KEY"),
model="text-embedding-3-small",
base_url='https://burn.hair/v1'
)
statement = "I am happy"
embedding = get_openai_embedding()
print(embedding.embed_query(statement))
一些开源的模型也可以通过 Hugging Face平台的 SDK 或 API 的方式使用
def generate_embedding(text: str) -> list[float]:
embedding_url = "https://api-inference.huggingface.co/pipeline/feature-extraction/sentence-transformers/all-MiniLM-L6-v2"
response = requests.post(
embedding_url,
headers={"Authorization": f"Bearer {config.hg_token}"},
json={"inputs": text})
if response.status_code != 200:
raise ValueError(f"Request failed with status code {response.status_code}: {response.text}")
return response.json()
a= "i am a boy"
print(generate_embedding(a))
2025-04-08 16:03:03
MongoDB 作为非结构化数据库的代表,在作为存储载体的数据库家族中一直重要的位置,但是在实际的业务场景中,尤其是互联网业务中,大多数都是 MySQL 或者 PostgresSQL 这种结构化的数据库作为主要技术选型,MongoDB稍显小众。
不过随着 LLM 的火爆,向量数据库作为 RAG 的后端存储,开始频繁出现在各种技术文章里,主流的向量数据如下图所示,在这张表中其实还落了 MongoDB,虽然性能上它不是最强的,但作为个人开发和测试的场景完全不输表中的数据库。
MongoDB 作为一个开源的数据,诞生于 2009 年,与传统的关系型数据库不同,MongoDB 使用 BSON格式来存储数据,每个文档都是一个独立的存储单元,可以灵活地存储嵌套结构,这一点在 IoT 领域体现得尤为明显,一个硬件设备升级之后,可能多了很多功能,但是在存储上,我们只需要在json 结构的数据字段里增添对应的功能名称即可,而不需要更改整个数据表的结构。
MongoDB Atlas是由 MongoDB 公司提供的一个全托管的数据库云服务(Database-as-a-Service, DBaaS),旨在简化数据库的部署和管理,更重要的是每个 Atlas 项目提供一个免费集群,不用绑卡且永不过期,可以说现在 Atlas 的使用就像本地使用 SQLite 数据库一样简单。
数据库最常用的功能就是查询了,Atlas 一共有 3 中检索方式,分别是Index Search、Vector Search、Atlas Search。
Index Search 是最传统的数据库检索方式,可以通过建立索引快速检索符合条件的文档,而无需遍历整个数据库表格。MongoDB Atlas提供了多种索引类型:单字段索引、复合字段索引、文字索引、地理空间索引等。
Vector Search 是随着 LLM 的发展,用于处理高维数据结构而设计的查询,用于相似性搜索的场景。通常情况下,将数据(文本、声音、图像)转化为嵌入式向量(embedding),然后存储在 MongoDB 中。当需要查询的时候,根据用户的输入数据的embedding 计算和其最为相似的向量并返回。
Atlas Search 是基于Apache Lucene的全文检索引擎,整合在 MongoDB Atlas 中,专门为大量文本数据和复杂查询设计。
常用的MongoDB 的数据格式如下:
String:适合存放文字 Number:用于数字数据 Date:记录日期和时间 Boolean:用于是/否的选项 Array:存放在多个相关项目的列表 Object:用于复杂的资料结构
MongoDB与 SQL的语句对照可以参考下表
Mongo | SQL |
---|---|
Database | Database |
Collection | Table |
Document | Raw Data |
连接 Atlas 云上数据库
import os
from dotenv import load_dotenv
from pathlib import Path
# 获取当前脚本所在的目录
current_dir = Path(__file__).resolve().parent
# 获取上一层目录的路径
parent_dir = current_dir.parent
# 加载上一层目录中的 .env 文件
env_path = parent_dir / '.env'
load_dotenv(dotenv_path=env_path)
client = MongoClient(os.getenv("MONGODB_ATLAS_URL"))
# 测试联通性
try:
client.admin.command('ping')
print("Pinged your deployment. You successfully connected to MongoDB!")
except Exception as e:
print(e)
插入数据
data = {
"text":"hello world.",
"embedding": [0.123, 0.234, 0.345],
}
collection.insert_one(data)
查找
# 查找
data = collection.find_one({"text":"hello world."})
print("----回傳 dictionary----")
print(f"类型:{type(data)}")
print(data["text"])
2025-03-21 19:49:29
长沙,一座一座既熟悉又陌生的城市,高中的课本里有他,“屈贾谊于长沙,非无圣主;窜梁鸿于海曲,岂乏明时?”,儿时的歌曲里有他,“浏阳河,弯过了几道弯”;小时候爱看的《快乐大本营》拍摄地也在长沙,长大了爱看的纪录片《守护解放西》拍摄地也在长沙。长沙,一直若隐若现的出现在过去的生活中,如今有机会来到长沙,让长沙真正从文字走进了生活。
本以为3 月份的长沙会是春光艳艳,刚出黄花机场看了眼手机天气,30 度的数字着实让我大吃一惊,松雅湖公园里人头攒动,湖边有捞鱼的小孩,也有摆出各种姿势照相的青年;草坪上有搭帐篷野餐的家人,也有放风筝的孩子;湖边的步道上有喝着饮料悠闲散步的情侣,也有骑着自行车说笑打闹的学生,租自行车的地方甚至排起了长队。湖的一角有一个瞭望台,一群孩子上上下下不知在玩着什么游戏,湖的中心还有个小岛,本想登岛去看一看,但很可惜小岛还没有对外开放。
回酒店的路上,看着窗外的景色,感觉长沙的马路好宽,大多都是双向 8 车道,而长沙的住宅楼又好高,基本都是二三十层,而且楼间距感觉很窄,一楼基本上都是各种商户,晚上转到一个比较老旧的住宅区,整个小区的主干道各种街边小吃,像是一个小型夜市,感觉长沙的棋牌室很多,没走几步就能看到一个,再有就是奶茶店、零食店和水果店,也是没走几步就能看到一个。
这次到长沙还认识了 2 种新的食物,一个叫凉薯,脆脆甜甜很好吃,看摆盘一开始以为是萝卜;一个叫捆鸡,感觉就是卤制的豆皮,味道一般。长沙的米感觉很好吃,比较硬有嚼劲,更重要的是米饭可以无限续。
最后还要说一下长沙的天气,除了第一天到长沙看到了太阳,随后的几天都是阴天或是雨天,就像歌中所唱“风带着潮湿,水在讲故事”,把“雾都”这个称号颁给 3 月份的长沙一点也不为过。我在长沙天气晴,我在长沙雨不停。