本电子书开源，欢迎 star 🌟，关注《LLM 应用开发实践笔记》

我的新书[《LangChain编程从入门到实践》](https://u.jd.com/V8pkqFY) 已经开售！推荐正在学习AI应用开发的朋友购买阅读！
[![LangChain编程从入门到实践](../../images/langchain-book.jpg "LangChain编程从入门到实践")](https://u.jd.com/V8pkqFY) ![](../images/group.png)

## Embedding 嵌入
本节对 Embedding（嵌入）概念进行介绍，同时会提到向量数据库相关知识，有助于后面的项目实现。


### 词嵌入是什么
向量是一个有方向和长度的量，可以用数学中的坐标来表示。例如，可以用二维坐标系中的向量表示一个平面上的点，也可以用三维坐标系中的向量表示一个空间中的点。在机器学习中，向量通常用于表示数据的特征。

而词嵌入是一种将文本这种离散数据映射到连续向量空间的方法，嵌入技术可以将高维的离散数据降维到低维的连续空间中，并保留数据之间的语义关系，从而方便进行机器学习和深度学习的任务。

例如：  
> "机器学习"表示为 [1,2,3]   
 "深度学习"表示为[2,3,3]  
 "英雄联盟"表示为[9,1,3]
>
> 使用余弦相似度（余弦相似度是一种用于衡量向量之间相似度的指标，可以用于词嵌入之间的相似度）在计算机中来判断文本之间的距离： 

> “机器学习”与“深度学习”的距离：![](https://private.codecogs.com/gif.latex?cos%28%5CTheta_1%20%29%3D%5Cfrac%7B1*2+2*3+3*3%7D%7B%5Csqrt%7B1%5E2+2%5E2+3%5E3%7D%5Csqrt%7B2%5E2+3%5E2+3%5E3%7D%7D%3D0.97)
> 
> "机器学习”与“英雄联盟“的距离"：![](https://private.codecogs.com/gif.latex?cos%28%5CTheta_2%20%29%3D%5Cfrac%7B1*9+2*1+3*3%7D%7B%5Csqrt%7B1%5E2+2%5E2+3%5E3%7D%5Csqrt%7B9%5E2+1%5E2+3%5E3%7D%7D%3D0.56)
> 
> “机器学习”与“深度学习”两个文本之间的余弦相似度更高，表示它们在语义上更相似。

### 词嵌入算法
词嵌入算法是指将文本数据转化为向量表示的具体算法，通常包括以下几个步骤：
- 分词：将文本划分成一个个单词或短语。
- 构建词汇表：将分词后的单词或短语建立词汇表，并为每个单词或短语赋予一个唯一的编号。
- 计算词嵌入：使用预训练的模型或自行训练的模型，将每个单词或短语映射到向量空间中。
- 计算词嵌入：将文本中每个单词或短语的向量表示取平均或加权平均，得到整个文本的向量表示。

常见的词嵌入算法包括 Word2Vec、GloVe、FastText 等。这些算法通过预训练或自行训练的方式，将单词或短语映射到低维向量空间中，从而能够在计算机中方便地处理文本数据。
### 词嵌入用途
词嵌入用于测量文本字符串的相关性，通常用于：
- 搜索（结果按与查询字符串的相关性排序）
- 聚类（其中文本字符串按相似性分组）
- 推荐（推荐具有相关文本字符串的项目）
- 异常检测（识别出相关性很小的异常值）
- 多样性测量（分析相似性分布）
- 分类（其中文本字符串按其最相似的标签分类）

### 使用词嵌入模型
* 可以使用 HuggingFace上能够处理词嵌入的开源模型，例如：[uer/sbert-base-chinese-nli](https://huggingface.co/uer/sbert-base-chinese-nli)
  ```python
  from sentence_transformers import SentenceTransformer
  model = SentenceTransformer('uer/sbert-base-chinese-nli')
  sentences = ["机器学习","深度学习","英雄联盟",]
  sentence_embeddings = model.encode(sentences)
  ```

* 使用之前介绍的 [OpenAI 词嵌入API](../01-llm/01-3.md#embeddings) 可以将文本转换为向量，OpenAI API提供了多个词嵌入模型，[这篇博客](https://openai.com/blog/new-and-improved-embedding-mode)对它们的性能进行了比较，这里是性能最好的`text-embedding-ada-002`说明：

|        模型名称        | 价格                  | 分词器      | 最大输入 token | 输出 |
| :--------------------: | --------------------- | ----------- | -------------- | ---- |
| text-embedding-ada-002 | $0.000/1k tokens | cl100k_base | 8191           | 1536 |


#### 支持词嵌入的模型
  -  [nghuyong/ernie-3.0-nano-zh](https://huggingface.co/nghuyong/ernie-3.0-nano-zh)
  -  [shibing624/text2vec-base-chinese](https://huggingface.co/shibing624/text2vec-base-chinese)
  -  [GanymedeNil/text2vec-large-chinese](https://huggingface.co/GanymedeNil/text2vec-large-chinese)
  -  [moka-ai/m3e-base](https://huggingface.co/moka-ai/m3e-base)
  -  [用于句子、文本和图像嵌入的Python库](https://github.com/UKPLab/sentence-transformers)
### 矢量数据库
* 为了快速搜索多个矢量，建议使用矢量数据库，下面是一些可选的矢量数据库：
  - [Pinecone](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases/pinecone)，一个完全托管的矢量数据库
  - [Weaviate](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases/weaviate)，一个开源的矢量搜索引擎
  - [Redis](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases/redis)作为矢量数据库
  - [Qdrant](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases/qdrant)，一个矢量搜索引擎
  - [Milvus](https://github.com/openai/openai-cookbook/blob/main/examples/vector_databases/Using_vector_databases_for_embeddings_search.ipynb)，一个为可扩展的相似性搜索而构建的矢量数据库
  - [Chroma](https://github.com/chroma-core/chroma)，Chroma是一个开源的嵌入式数据库，可以快速构建Python或JavaScript LLM应用程序。
  - [Typesense](https://typesense.org/docs/0.24.0/api/vector-search.html)，快速的开源矢量搜索引擎
  - [Zilliz](https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases/zilliz)，数据基础设施，由Milvus提供技术支持
  - [FAISS](https://github.com/facebookresearch/faiss) 是Meta开源的用于高效搜索大规模矢量数据集的库

#### 性能优化✍️：
和传统数据库一样，可以使用工程手段优化矢量数据库搜索性能，最直接的就是更新索引算法 ，对索引数据进行分区优化。

1. 平面索引（FLAT）：将向量简单地存储在一个平面结构中，最基本的向量索引方法。
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}
    - 余弦相似度（Cosine Similarity）：{% math %}sim(x,y) = \frac{x \cdot y}{\|x\| \|y\|}{% endmath %}

2. 分区索引（IVF）：将向量分配到不同的分区中，每个分区建立一个倒排索引结构，最终通过倒排索引实现相似度搜索。
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}
    - 余弦相似度（Cosine Similarity）：{% math %}sim(x,y) = \frac{x \cdot y}{\|x\| \|y\|}{% endmath %}

3. 量化索引（PQ）：将高维向量划分成若干子向量，将每个子向量量化为一个编码，最终将编码存储在倒排索引中，利用倒排索引进行相似度搜索。
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}
    - 汉明距离（Hamming Distance）：{% math %}d(x,y) = \sum_{i=1}^n (x_i \oplus y_i){% endmath %}，其中 {% math %}\oplus{% endmath %} 表示按位异或操作。

4. HNSW (Hierarchical Navigable Small World)：通过构建一棵层次化的图结构，从而实现高效的相似度搜索。
    - 内积（Inner Product）：{% math %}sim(x,y) = x \cdot y{% endmath %}
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}

5. NSG (Navigating Spreading-out Graph)：通过构建一个分层的无向图来实现快速的相似度搜索。
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}

6. Annoy (Approximate Nearest Neighbors Oh Yeah)：通过将高维空间的向量映射到低维空间，并构建一棵二叉树来实现高效的近似最近邻搜索。
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}
    - 曼哈顿距离（Manhattan Distance）：{% math %}d(x,y) = \sum_{i=1}^n |x_i - y_i|{% endmath %}

7. LSH (Locality-Sensitive Hashing)：通过使用哈希函数将高维的向量映射到低维空间，并在低维空间中比较哈希桶之间的相似度，实现高效的相似度搜索。
    - 内积（Inner Product）：{% math %}sim(x,y) = x \cdot y{% endmath %}
    - 欧式距离（Euclidean Distance）：{% math %}d(x,y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2}{% endmath %}

### 参考资源
1. [向量数据库技术鉴赏](https://www.bilibili.com/video/BV1BM4y177Dk/)：一个非常不错的讲解向量数据库科普视频