Skip to content

博客

向量归一化和相似度

在机器学习和深度学习中,对嵌入向量进行归一化(Normalization)至关重要,它能带来多方面的益处,尤其是在涉及到相似度计算、模型训练和数据表示时。归一化通常指的是将向量的长度(或范数)缩放为单位长度(通常为 L2 范数等于 1)。以下是归一化的主要必要性:

  • 避免特征尺度差异的影响: 不同的特征或模型生成的原始嵌入向量可能具有不同的尺度和范围。例如,一个模型的嵌入向量的数值可能在 -1 到 1 之间,而另一个模型的可能在 -100 到 100 之间。如果不进行归一化,在计算向量之间的距离或相似度时(如欧氏距离、点积),尺度较大的向量会占据主导地位,导致模型更关注这些尺度较大的特征,而忽略了尺度较小的特征中可能包含的重要信息。
  • 确保每个特征的贡献相等: 归一化可以将所有嵌入向量的元素缩放到相似的尺度,使得每个维度在距离和相似度计算中具有更公平的权重,从而提高模型的鲁棒性和泛化能力。
  • 提高余弦相似度的准确性: 余弦相似度是衡量两个向量方向之间夹角的余弦值,常用于计算文本、图像等嵌入的语义相似性。当向量未归一化时,向量的长度(magnitude)会影响点积的值,从而影响余弦相似度的结果。归一化后,向量的长度变为 1,此时余弦相似度就直接等于向量的点积,能够更准确地反映向量方向上的相似程度。
  • 改善基于距离的度量: 对于依赖欧氏距离等距离度量的算法(如 K-近邻、K-均值聚类),归一化可以确保距离的计算不会被尺度较大的特征所主导,从而得到更符合语义或特征实际分布的结果。
  • 避免梯度爆炸或消失: 在神经网络训练中,如果输入的特征尺度过大或过小,可能导致梯度在反向传播过程中变得非常大(梯度爆炸)或非常小(梯度消失),从而影响模型的收敛速度和训练稳定性。归一化可以将输入特征限制在合理的范围内,有助于缓解这些问题。
  • 加快收敛速度: 当特征具有相似的尺度时,梯度下降等优化算法可以更有效地找到最优解,因为模型不需要在不同尺度的特征空间中来回震荡。
  • 减少模型对异常值的敏感性: 某些归一化方法(如 Z-score 归一化)可以降低异常值对模型的影响,使模型更加稳定。
  • 提升模型在不同数据集上的泛化能力: 通过统一特征的尺度,归一化可以帮助模型学习到更通用的模式,而不是过度依赖于特定数据集的尺度分布。

方便不同嵌入模型的比较和集成:

Section titled “方便不同嵌入模型的比较和集成:”
  • 统一不同模型的输出尺度: 不同的嵌入模型可能产生具有不同尺度范围的向量。归一化可以将它们的输出统一到一个共同的尺度,使得可以直接比较或组合来自不同模型的嵌入,从而进行更复杂的分析或构建更强大的系统。

总结

归一化是处理嵌入向量的一个关键步骤,它可以确保相似度计算的准确性,优化模型训练过程,提高模型性能和泛化能力,并方便不同嵌入之间的比较和集成。在大多数使用嵌入向量的机器学习和深度学习应用中,都强烈建议进行归一化处理。最常用的归一化方法是 L2 归一化,它将向量的长度缩放为单位长度。

相似度量用于衡量向量之间的相似性。选择合适的距离度量有助于显著提高分类和聚类性能。向量的相似度量有:余弦相似度(Cosine Similarity)、内积(Inner Product, IP)、欧氏距离(Euclidean Distance)、曼哈顿距离(Mahalanobis Distance)、余弦距离(Cosine Distance)、汉明距离(Hamming Distance)、Jaccard 相似度(Jaccard Similarity)、BM25 相似度(BM25 Similarity)等等。

Cosine(A, B) = (A · B) / (||A|| * ||B||)。它衡量的是两个向量方向上的一致性,忽略了向量的长度(模长)。它的取值范围是 [-1, 1],越接近 1 表示方向越一致。 例如,两个正比向量的余弦相似度为1,两个正交向量的余弦相似度为0,两个相反向量的余弦相似度为-1,余弦越大,两个向量之间的夹角越小,说明这两个向量之间的相似度越高。 用 1 减去它们的余弦相似度,就可以得到两个向量之间的余弦距离。

IP(A, B) = A · B。它计算的是两个向量的点积。这个值同时受到向量方向和向量模长的影响。如果两个向量方向一致,它们的模长越大,内积通常也越大。IP 的取值范围没有固定界限(取决于向量模长)。

3、同样的数据,内积 (IP) 能查到,但是余弦相似度查询不到

Section titled “3、同样的数据,内积 (IP) 能查到,但是余弦相似度查询不到”

因为内积查询需要计算两个向量的点积,而余弦相似度只需要计算两个向量的余弦值。

$~$

  • 如果向量是归一化的(即模长为1,||A|| = 1||B|| = 1),那么Cosine(A, B) = A · B = IP(A, B)。在这种情况下,使用余弦相似度和内积进行检索,得到的结果排序应该是完全相同的。

  • 如果向量不是归一化的,那么Cosine(A, B) = (A · B) / (||A|| * ||B||),而IP(A, B) = A · B。在这种情况下,使用余弦相似度和内积进行检索,得到的结果排序可能不同。


具体到这个现象来说:


  • 内积 (IP) 成功: 这很可能意味着你想要查找的那个目标数据向量,与你的查询向量之间的点积 (A · B) 相对较高。这个高点积可能是因为它们的方向比较接近,也可能是因为一个或两个向量的模长(长度)非常大,即使方向不是最接近的,较大的模长也导致了较高的内积得分,使其排在了前面。
  • 余弦相似度 (Cosine) 失败: 这表明,虽然目标向量和查询向量的内积可能较高(被 IP 检索到),但当除以各自的模长进行归一化计算后 ((A · B) / (||A|| * ||B||)),得到的余弦相似度得分反而不高。可能有其他向量,虽然它们的模长较小(导致 IP 得分不如目标向量),但它们与查询向量的方向更一致(角度更小),因此具有更高的余弦相似度得分,把真正的目标挤出了 top-k 结果。

总结来说可能原因是:


  • 向量未归一化: 这是最主要的原因。gte-Qwen2-7B-instruct 或你使用的其他嵌入模型产生的向量可能其模长不为 1。
  • 模长的影响: 能够被 IP 检索到的目标数据,很可能与查询向量的点积较大,这其中较大的模长起到了重要作用,弥补了方向上可能不是绝对最优的劣势。
  • 方向 vs. 模长: 其他数据可能在方向上与查询向量更接近(余弦相似度更高),但由于模长较小,其内积得分不如目标数据高。

取一个由模型产生的向量 v,计算其 L2 范数(模长):norm = np.linalg.norm(v)。如果 norm 不接近 1,那么向量就没有归一化。


检查向量是否归一化,可以使用以下代码:
from llama_index.embeddings.ollama import OllamaEmbedding
import numpy as np
def is_normalized(vector):
return np.isclose(np.linalg.norm(vector)1.0
def normalize(vector):
if is_normalized(vector):
return vector
return vector / np.linalg.norm(vector)
def normalize_vectors(vectors):
return [normalize(vector) for vector in vectors]
embedd = OllamaEmbedding(
# model_name="Embedding-GGUF/gte-Qwen2-1.5B-instruct-Q4_K_M-GGUF",
model_name="modelscope.cn/Embedding-GGUF/gte-Qwen2-7B-instruct-Q4_K_M-GGUF:latest",
base_url="http://localhost:11434",
normalize=True
)
embedding = embedd.get_text_embedding("hello")
print(f"向量是否归一化: {is_normalized(embedding)}")

RAG vs 模型微调

RAG (Retrieval-Augmented Generation),中文可以理解为检索增强生成,是一种通过从外部知识库检索信息来增强大型语言模型(LLM)能力的AI框架。


简单来说,传统的 LLM 是基于其训练数据来生成文本的,而 RAG 则在此基础上增加了一个步骤:当用户提出问题或指令时,RAG会先从外部的信息源(比如网页、数据库、文档等)中搜索相关的知识,然后将这些检索到的信息与用户的问题一起提供给 LLM,让 LLM 在生成答案时能够参考这些外部信息


RAG的主要步骤可以概括为:


  1. 检索 (Retrieval): 接收用户的查询,并从外部知识库中找到与之相关的信息。这通常涉及到信息检索技术,例如关键词搜索、向量搜索等。
  2. 增强 (Augmentation): 将检索到的相关信息与用户的原始查询结合起来,形成一个更丰富的输入(通常称为提示,Prompt),提供给 LLM。
  3. 生成 (Generation): LLM 接收到增强后的输入,并基于这些信息生成最终的回答或文本。

使用RAG的好处包括:


  1. 获取最新的信息: LLM的训练数据通常是静态的,而RAG可以实时地从外部获取最新的信息,从而生成更准确、及时的回答。
  2. 减少“幻觉”: 通过引用外部的可靠信息来源,RAG可以降低LLM生成不真实或不相关信息的风险。
  3. 提高透明度: 在某些情况下,RAG可以提供生成答案所依据的外部信息来源,帮助用户验证答案的可靠性。
  4. 降低计算成本: 相比于重新训练整个LLM来使其掌握新知识,RAG的成本通常更低。
  5. 适应特定领域: RAG可以连接到特定的知识库,例如企业内部文档或专业领域的数据库,从而使LLM在这些特定领域内提供更专业的回答。

RAG的一些应用场景包括:


  1. 智能客服: 能够根据最新的产品信息、FAQ等知识库回答用户的问题。
  2. 问答系统: 能够从大量的文档或数据中检索并生成针对特定问题的答案。
  3. 内容创作: 能够基于最新的研究报告或新闻资讯生成文章或报告。

总而言之,RAG通过将LLM的生成能力与信息检索系统的知识获取能力相结合,使得AI能够生成更准确、更可靠、更具上下文相关性的文本。

模型微调 (Fine-tuning) 则是指在预训练好的大型语言模型的基础上,使用一个较小但更 específico 的数据集进行额外的训练,以使模型更好地适应特定的任务或领域。这个过程会调整模型内部的权重和参数,使其学习到目标任务的特定模式和知识。


模型微调相较于RAG,在难度和不利方面主要体现在以下几点:


难度:


  1. 数据准备和标注: 模型微调通常需要一个高质量、与目标任务高度相关的标注数据集。收集、清洗和标注这些数据可能非常耗时、昂贵且困难,尤其是在某些专业领域。RAG主要依赖于构建和维护一个可搜索的知识库,对数据的标注要求通常较低。
  2. 专业知识: 微调需要对自然语言处理(NLP)、深度学习以及模型架构有一定的理解,才能选择合适的微调策略、调整超参数并评估模型性能。RAG的实现则更侧重于信息检索系统的构建和与LLM的集成。
  3. 实验和调优: 微调过程可能需要多次实验才能找到最佳的模型配置和超参数,以避免过拟合或欠拟合,并获得理想的性能。RAG的调优主要集中在检索策略和生成提示的优化上。
  4. 计算资源: 对大型LLM进行微调需要大量的计算资源(GPU/TPU)和时间,尤其是在处理大型数据集时。RAG在推理阶段的计算成本主要在于检索和生成答案,通常低于重新训练整个模型。

不利的地方:


  1. 知识更新困难: 微调后的模型所学习的知识是基于微调数据集的静态快照。如果需要模型掌握新的信息,必须重新进行微调,这既耗时又耗费资源。RAG可以通过更新外部知识库来轻松地引入和利用最新的信息。
  2. 泛化能力下降风险: 如果微调数据集过小或不够多样化,微调后的模型可能会过拟合于特定任务,导致在未见过的数据上的泛化能力下降。RAG通过利用外部知识库,模型仍然可以依赖其预训练的通用知识。
  3. 透明度和可解释性较低: 微调改变了模型内部的权重和参数,使得模型学习到的知识和决策过程更加难以解释和追溯。RAG在生成答案时可以提供检索到的外部证据,提高了透明度和可信度。
  4. 模型偏移 (Model Drift): 随着时间的推移,如果目标任务或数据的分布发生变化,微调后的模型性能可能会逐渐下降,需要定期维护和重新微调。RAG可以通过更新知识库来适应变化,但检索系统的有效性也可能受到数据质量的影响。
  5. 难以处理开放领域或不断演变的信息: 由于微调依赖于静态数据集,它不太适合需要处理不断变化或涉及广泛领域知识的任务。RAG通过实时检索外部信息,更适合这类场景。
特征RAG (检索增强生成)模型微调 (Fine-tuning)
核心思想实时检索外部知识并融入生成过程,增强模型对新知识的利用通过额外的训练调整模型内部参数,使其适应特定任务或领域
知识来源外部知识库 (实时或近实时)微调数据集 (静态)
更新知识相对容易,更新外部知识库即可需要重新进行微调,成本较高
适用场景需要处理最新信息、知识密集型、特定领域但知识 постоянно 更新的场景需要模型学习特定风格、术语、或执行特定任务,且知识相对稳定的场景
数据需求构建高质量的外部知识库和高效的检索系统需要高质量、标注良好的微调数据集 (通常比预训练数据小)
计算成本检索过程有计算成本,但通常低于重新训练整个模型微调过程需要一定的计算资源,尤其对于大型模型
灵活性较高,可以灵活更换和扩展知识库一旦微调完成,模型的知识和能力就相对固定
透明度可以提供检索到的证据,提高回答的可信度模型内部知识难以追溯,透明度较低
减少“幻觉”通过引用外部知识,有助于减少模型生成不实信息可以通过高质量的微调数据减少“幻觉”,但并非完全消除
实现难度需要构建信息检索系统和融合机制需要准备微调数据、选择合适的微调策略和超参数

总结来说:


  1. 选择 RAG 的情况: 当你需要模型能够回答关于最新信息、特定领域知识(这些知识可能会频繁更新)的问题,并且希望答案有外部证据支持时。RAG不需要改变模型本身,而是通过外部信息来增强其能力。
  2. 选择模型微调的情况: 当你需要模型学习特定的语言风格、理解特定领域的术语、或者在某个特定任务上获得更好的性能,并且你有相关的标注数据可以用来训练模型时。微调会改变模型内部的知识和行为方式。

在实践中,RAG和模型微调有时也可以结合使用,以充分发挥各自的优势。例如,可以先对模型进行微调,使其具备更好的领域理解能力,然后再使用RAG来增强其获取最新信息和生成更准确答案的能力。

GGUF格式和LLM量化类型

LLM GGUF 格式是一种用于存储大型语言模型(LLM)的文件格式,特别是那些与 Llama.cpp 库一起使用的模型。GGUF 是 “GG Ultra Fast” 的缩写,它旨在提供一种高效且可移植的方式来存储和加载 LLM。

GGUF 格式的关键特性包括:

  • 高效性: GGUF 格式的设计考虑了性能,允许 Llama.cpp 快速加载和处理模型数据。

  • 可移植性: GGUF 文件可以在不同的硬件和操作系统之间共享,从而提高了 LLM 的可用性。

  • 灵活性: GGUF 格式支持各种模型架构和数据类型,使其能够适应不断发展的 LLM 领域。

  • 向后兼容性: GGUF 格式设计为向后兼容,这意味着新版本的 Llama.cpp 仍然可以读取旧版本的 GGUF 文件。

总而言之,GGUF 格式是一种专门为 LLM 设计的存储格式,它强调效率、可移植性和灵活性。它在 Llama.cpp 生态系统中被广泛使用,并帮助实现了各种设备上 LLM 的高效部署。

Hugging Face 平台为使用 Llama.cpp 进行模型转换、量化和托管提供了各种在线工具:

GGUF-my-repo space:用于转换为 GGUF 格式,并将模型权重量化为更小的尺寸。

GGUF-my-LoRA space:用于将 LoRA 适配器转换为 GGUF 格式。

GGUF-editor space:用于在浏览器中编辑 GGUF 元数据。

Inference Endpoints:用于在云中直接托管 Llama.cpp 。

在 LLM 的 GGUF 格式中,Quantization 参数用于指定模型的量化方式。量化是一种模型压缩技术,通过降低模型参数的精度,来减少模型的大小和内存占用,同时提高推理速度。以下是 Quantization 参数中各种取值的含义:

  • q2_K:

    • 2 位量化,是最小的量化方式,可以实现最小的模型大小和最快的推理速度。
    • 但精度损失最大,适用于对精度要求不高的场景。
  • q3_K_M:

    • 3 位量化,精度比 q2_K 稍高,速度稍慢。
    • 适用于对精度有一定要求的场景。
  • q4_0:

    • 4 位量化,是常用的量化方式,可以在模型大小、速度和精度之间取得较好的平衡。
    • 适用于大多数场景。
  • q4_K_M:

    • 4 位量化,精度比 q4_0 稍高,速度稍慢。
    • 适用于对精度有较高要求的场景。
  • q5_0:

    • 5 位量化,精度比 q4 系列更高,速度稍慢。
    • 适用于需要更高精度的场景。
  • q5_K_M:

    • 5 位量化,精度比 q5_0 更高,速度稍慢。
    • 适用于对精度有更高要求的场景。
  • q6_K:

    • 6 位量化,精度较高,速度较慢。
    • 适用于对精度要求很高的场景。
  • q8_0: - 8 位量化,精度最高,速度最慢。- 适用于对精度要求极高的场景。

总结:

  • q 表示量化,后面的数字表示量化的位数。
  • K 和 M 表示不同的量化方法,它们在精度和速度之间有所权衡。
  • 量化位数越低,模型越小,速度越快,但精度越低。
  • 量化位数越高,模型越大,速度越慢,但精度越高。

Llama.cpp vs Ollama

Llama.cppOllama 都是在本地运行大型语言模型(LLMs)的工具,但它们的设计目标和使用方式有所不同。以下是它们之间的主要区别:

  1. 设计目标:
  • Llama.cpp:
    • Llama.cpp 是一个用 C++ 编写的库,专注于在消费级硬件(尤其是 CPU)上实现高性能的 LLM 推理。
    • 它的目标是提供一个轻量级、高效的解决方案,允许开发者在本地运行 LLMs,而无需依赖强大的 GPU。
    • Llama.cpp 的核心是提供一个高效的运行LLM的工具,可以理解为是一个底层的工具。
  • Ollama:
    • Ollama 则是一个更高级别的工具,旨在简化在本地运行 LLMs 的过程。
    • 它使用 Llama.cpp 作为其后端之一,提供了一个易于使用的命令行界面和 API,允许用户快速下载、运行和管理 LLMs,而无需深入了解底层细节。
    • Ollama 的核心是提供一个简易的LLM运行,下载,管理的工具,可以理解为是一个上层的工具。
  1. 使用方式:
  • Llama.cpp:
    • Llama.cpp 主要是一个库,开发者可以将其集成到自己的应用程序中。
    • 它也提供了一些命令行工具,但主要用于测试和演示。
    • Llama.cpp 需要用户对命令行和模型文件有一定的了解。
  • Ollama:
    • Ollama 提供了一个更友好的用户体验,用户可以通过简单的命令下载和运行 LLMs。
    • 它还提供了一个 API,允许开发者将 LLMs 集成到自己的应用程序中。
    • Ollama 简化了 LLMs 的安装和运行过程,降低了使用门槛。
  1. 功能:
  • Llama.cpp:
    • Llama.cpp 专注于提供高性能的 LLM 推理,支持多种量化和优化技术。
    • 它对底层硬件进行了优化,以实现最佳性能。
  • Ollama:
    • Ollama 除了提供 LLM 推理功能外,还提供了模型管理、API 服务等功能。
    • 它旨在提供一个完整的 LLM 运行环境。
  1. 总结:
  • 如果您是开发者,需要将 LLMs 集成到自己的应用程序中,并且对性能有较高要求,那么 Llama.cpp 可能更适合您。
  • 如果您是普通用户,希望快速、方便地在本地运行 LLMs,那么 Ollama 可能更适合您。
  • 简单来说,Llama.cpp 是一个更底层的工具,而 Ollama 则是一个更上层的工具。

GraphRAG vs AnythingLLM

  • 核心理念: GraphRAG 是一种检索增强生成(RAG)技术,它利用知识图谱来增强大型语言模型(LLM)的性能。

  • 工作原理:

    • 它从知识图谱中检索相关信息,并将其作为上下文提供给 LLM。这有助于 LLM 更准确、更全面地回答问题,尤其是在需要复杂推理或涉及实体关系时。
  • 优势:

    • 增强了 LLM 的事实准确性和知识覆盖面。
    • 提高了处理复杂查询和推理的能力。
    • 能够更好地理解实体之间的关系。
  • 应用场景:

    • 金融分析、医疗保健、法律等需要精确知识和推理的领域。
    • 在处理具有复杂关系的数据时,例如社交网络分析。
  • 特点:

    • 通过知识图谱来提高数据的关系性和准确性。
    • 需要构建和维护知识图谱。
  • 核心理念: Anything LLM 是一种开源平台,旨在简化 LLM 与各种数据源的集成。

  • 工作原理:

    • 它允许用户连接各种数据源(如文档、网站、数据库等)。
    • 然后,它使用 RAG 技术从这些数据源中检索相关信息,并将其提供给 LLM。
  • 优势:

    • 易于使用,无需编码即可连接数据源。
    • 支持多种数据源,具有很强的灵活性。
    • 开源,允许用户自定义和扩展。
  • 应用场景:

    • 构建问答系统、聊天机器人、文档摘要等。
    • 在需要从多个数据源检索信息的场景中。
  • 特点:

    • 可以连接多种数据源。
    • 拥有友好的UI界面,容易上手。
    • 开源项目,具有活跃的社区。
  • 主要区别

    • GraphRAG 的重点是利用知识图谱,而 Anything LLM 的重点是简化数据源集成。
    • GraphRAG更侧重于提高LLM的推理能力,而AnythingLLM更侧重于提高LLM的信息检索能力。
    • GraphRAG通常需要构建知识图谱,而AnythingLLM则更加的通用,可以连接多种数据源。
  • 总结

    • 如果需要利用知识图谱来提高 LLM 的推理能力,GraphRAG 可能是一个不错的选择。
    • 如果需要一个易于使用的平台来连接各种数据源,Anything LLM 可能更适合。