7 Embedding文本向量化
Embedding文本向量化是一种将非结构化文本转化为低维、连续数值向量的技术,旨在通过数学方式捕捉文本的语义、语法或特征信息,从而让机器更高效地处理语言任务。其核心思想源于流形假设(Manifold Hypothesis),即认为高维原始数据(如文本)实际隐含于低维流形中,通过映射到低维空间可实现语义可分性。
7.1 基础知识
原始文本到模型可输入向量的转化需要经过一下3个过程:
文本(原始形式)——> 分词(Token)——>Token索引化——>向量嵌入(Embedding)
7.1.1 分词(Tokenization)
-
基于单词的分词(Word-level)
将文本按空格或标点切分为完整单词(如英文)或独立词汇(如中文“苹果”)。优点是语义完整,但词表规模庞大且易出现未登录词问题
-
基于字符的分词(Character-level)
以单个字符为最小单元(如中文“苹”和“果”)。词表规模小(仅需数千字符),但丢失词汇边界信息,导致模型难以捕捉长距离语义
-
基于子词的分词(Subword-level):
平衡前两者,通过算法(如BPE、WordPiece)将罕见词拆分为更小单元。例如,“unbelievable”在BERT中被拆为[“un”, “##believable”],其中“##”表示该子词属于前续词。这种方法既能处理未登录词问题,又保留部分语义信息。
7.1.2 Token索引化
通过分词,将词转化为唯一整数索引,可以得到一张词表。以BERT为例,BERT的词表通常包含约30,522个子词,覆盖高频词汇和常见组合,并且添加了一些特殊Token,其词表形式如下:
{"[PAD]": 0, "我": 1, "吃了": 2, "苹果": 3, "[CLS]":4,"[SEP]":5}
7.1.3 向量嵌入
BERT的嵌入层包含一个可学习的Embedding矩阵,尺寸为[Vocab Size × Hidden Size]
(如30,522×768),该矩阵通过预训练(如掩码语言模型任务)学习每个Token的语义表示。原始文本在分词且索引化后,根据Token索引化后的ID从Embedding矩阵中提取向量,并与位置(Position Embeddings)、段落(Segment Embeddings)嵌入相加,得到最终输入表示。例如:
·输入Token ID=2(“苹果”)会从矩阵中取出对应的768维向量。
BERT模型的最大输入长度(token上限)通常为512个token,超出部分会被丢弃。常见扩展方法如下:
- 滑动窗口分割:将长文本划分为多个512-token的片段分别处理(如使用
stride
参数覆盖上下文),但可能丢失全局信息。 - 专用模型:如BigBird和Longformer通过稀疏注意力机制支持更长序列(例如4096 tokens),但需重新预训练且与BERT架构不同。
- BELT方法:基于现有BERT模型,通过分段处理与池化技术间接支持长文本,保留预训练权重的同时提升处理能力。
7.2 langchain的向量嵌入
安装阿里云百炼的客户端依赖:
pip install -q langchain_community
pip install -q dashscope
输入一句话为什么得到固定长度的一维向量?
因为BERT在输入序列的起始位置强制添加特殊标记[CLS]
,其对应的最后一层隐藏状态被用作整个句子的综合表示。
def qen_embedding():os.environ.setdefault("DASHSCOPE_API_KEY", load_key("DASHSCOPE_API_KEY"))from langchain_community.embeddings import DashScopeEmbeddingsembedding_model = DashScopeEmbeddings(model="text-embedding-v1")text = "This is a test query."query_result = embedding_model.embed_query(text)print(len(query_result)) # 获得一个1536维度的向量,不同模型的向量长度可能不同
------------------------------------------------
1536
7.3 余弦相似度计算
安装依赖:
pip install -q scikit-learn
pip install numpy
text1与text2的语义相似度较高,所以余弦相似度大,得分越高。
def cosine_similarity():os.environ.setdefault("DASHSCOPE_API_KEY", load_key("DASHSCOPE_API_KEY"))from langchain_community.embeddings import DashScopeEmbeddingsembedding_model = DashScopeEmbeddings(model="text-embedding-v1")text1 = "我喜欢吃苹果"text2 = "我喜欢吃香蕉"text3 = "太阳从东边升起"import numpy as npembedding1 = np.array(embedding_model.embed_query(text1)).reshape(1, -1)embedding2 = np.array(embedding_model.embed_query(text2)).reshape(1, -1)embedding3 = np.array(embedding_model.embed_query(text3)).reshape(1, -1)from sklearn.metrics.pairwise import cosine_similaritysimilarity12 = cosine_similarity(embedding1, embedding2)[0][0]similarity13 = cosine_similarity(embedding1, embedding3)[0][0]print(f"\"{text1}\" 与 \"{text2}\" 相似度:{similarity12:.4f}")print(f"\"{text1}\" 与 \"{text3}\" 相似度:{similarity13:.4f}")
-----------------------------------------------------------------------------------------------
"我喜欢吃苹果" 与 "我喜欢吃香蕉" 相似度:0.6128
"我喜欢吃苹果" 与 "太阳从东边升起" 相似度:0.0797
7.4 向量数据持久化
既然已经实现了文本向量化转换,自然需要一款工具来完成向量数据的持久化存储,并支持向量相似度检索。这类工具被统称为向量数据库。
在 LangChain 框架中,集成了丰富的向量数据库组件,具体集成列表可查阅官网文档《LangChain 集成的 Vector Store》。接下来,以 Redis为例,演示向量数据的核心操作流程。
注意: 默认的 Redis 社区版不支持向量数据存储,需额外安装redisearch
模块才能启用向量存储功能
通过 Docker 容器部署集成redisearch
模块的 Redis 服务,具体部署指令如下:
docker run -p 6379:6379 redis/redis-stack-server:latest
持久化案例展示如下,未来对于用户提出的任何问题,都可以在向量数据库中快速检索出和用户问题语义最接近的文本。但是这里需要注意,这里只是检索出可能认为最相关的文本,并不能完全保证用户提出的问题,最终的答案就是这个最相关的文本。基于LangChain框架的良好设计,未来如果想要切换到其他的向量数据库,只需要修改
vector_store的实现类即可,业务代码几乎不需要改动。
def qwen_embedding_save():os.environ.setdefault("DASHSCOPE_API_KEY", load_key("DASHSCOPE_API_KEY"))embedding_model = DashScopeEmbeddings(model="text-embedding-v1")from langchain_redis import RedisConfig, RedisVectorStoreredis_config = RedisConfig(index_name="my_index",redis_url="redis://127.17.0.2:6379/0",distance_metric="COSINE",embedding_dimensions=1536)vector_store = RedisVectorStore(embeddings=embedding_model, config=redis_config)vector_store.add_texts(["我喜欢吃苹果", "我不喜欢吃香蕉", "太阳从东边升起"])retireval_result = vector_store.similarity_search("我喜欢吃什么?", k=3<