实习日志三之大模型开发

在开始正文之前主包先自我检讨一下,由于上周考试去了,更新文档比较晚了,望大家见谅。

大模型开发基础知识之RAG

RAG简介

检索生成增强(Retrieval-Augmented Generation,简称RAG),是一种信息检索与生成式 AI相结合的先进技术架构。

RAG工作流程

  • 第一步文件预处理(离线状态)
    a.进行数据清洗。因为我们收集的数据往往具备噪音,它会影响到正常的学习效果,因此我们需要去除这些无用信息。
    b.进行文档分割。大模型存在上下文限制,需要将清洗好的数据分成合适大小的chunks
    c.向量化。机器只能理解机器语言而不是我们的自然语言,所以需要把文本映射到高维的向量上,也就是数值数组。可能很难理解,没关系,后面我会使用例子来介绍向量化的概念。
    d.构建索引。向量数据无法直接高效检索,需要用专门的向量数据库构建索引,相当于给向量数据建一个 “语义检索目录”,支持快速的相似度匹配。
    至此完成了预处理所有过程,其实不难看出,这个阶段是为了能够快速检索到干净数据的。
  • 第二步查询处理(在线状态)
    a.查询改写。用户原有的问题可能有点模糊,或者口语化,通过这一步骤转换为专业的具体的问题,比如user询问它的功能是什么,这时候需要查询上下文,找到“它”的定义是“同源策略”,然后问题就变成了同源策略的功能是什么。
    b.问题向量化。使用和向量化相同的嵌入模型,将user提出的问题也向量化。
    c.相似度搜索。对于user提出的问题向量,向量数据库通过计算它与其他向量的相似度(通常使用余弦相似度、欧氏距离,这两个具体原理我没看相关文档,只是知道有这么个原理,建议读者有余力可以看看这两个的原理),最后找到相似度最高的模块。
    d.结果排序。对于检索到的文本块进行二次排序,排序依据有——相似度分数,文档发布时间等等。
    这个阶段是为了找到那个最准确的文本块的。
  • 第三步生成回答(在线状态)
    a.上下文过滤。排序后的文本块很可能存在冗余重复内容,这个步骤用来精简上下文,减少token,也减少噪音。
    b.上下文构建。将过滤后的文本块和user原始问题拼接成一个完整的输入内容,这个内容会成为大模型生成回答的依据。
    c.提示工程。设计合理的提示词模版,引导大模型基于给定的上下文回答问题,而不是凭空捏造,很重要的一点——提示词需要明确角色,任务和输出要求。
    d.模型生成。将构建好的提示词输入到大模型中,模型结合自身参数知识和外部上下文,生成准确、有依据的回答。
    显然,这里是为了找到那个准确回答的。

RAG之向量化

在开始正文之前,先引一个文档,学习向量化的文档,跟人觉得写到比较通俗易懂——【超详细教程】一文读懂向量化原理,收藏级大模型入门指南!

  • 向量化核心概念
    将非结构化的文本语义信息,通过数学方法映射到一个高维向量空间,生成一串有序的数值数组(即向量 / Embedding)。在这个向量空间中,向量之间的距离越近,代表对应的文本语义越相似。这种映射不是简单的字符转数字,而是对文本核心含义的数字化表达,说实话,这段概念如果初学的话很抽象,不妨让我找一个例子来介绍这门技术。
  • 苹果的例子
    我们假设一个苹果有三个特征——[是否为水果,酸甜度,果皮颜色](这里说的是水果而非手机,它当然可以有很多特征,这里只是为了让大家理解,就选了三个,后面设置也是,只是为了理解而非实际使用),同时我们将三个特征设置的很简单:
    如果是水果输出1,否则0。酸甜度偏甜为1,偏酸为-1,中间态为0。果皮颜色偏红为1,偏绿为-1,其他为0。
    对于如下情景:红甜苹果=[1,1,1],青酸苹果=[1,-1,-1],香蕉=[1,1,0],石头=[0,0,0],通过欧式距离计算,前两者的距离更近更相似,所以向量化可以用来识别语义相似性。
    至此应该对向量化的概念有些许认识了,如果后面有用到更深的内容,我会补充,先有一个理解和认识即可。

HNSW索引查询

学习的过程中参考了一篇前辈的文章,自认为写的非常专业和学术化——向量检索和RAG全流程解析

  • 核心原理
    HNSW(Hierarchical Navigable Small World, 层次化可导航小世界)用层次化图结构代替传统树/聚类结构,通过上层快速导航和下层精准匹配实现高效检索,具体过程可拆解为索引构建和检索过程。没事我知道你看着这段话特别抽象,后面会给大家举出例子。
  • 基础概念
    小世界网络特性: 图中任意两个节点直接最短路径极短(六度分隔理论——你认识一个陌生人中间至多6个人),且节点具有“高聚类系数”(相邻节点也易相连,一个节点的邻居们大概率互相也为邻居)。层次化图结构,顶层(最高的Layer)是稀疏图,节点连接少,用于快速导航到目标区域。底层也是Layer 0为稠密图,节点多,在目标区域内精确匹配候选向量。每个节点存在于部分层中(如一个节点可能同时在Layer 2、Layer 1、Layer 0中),层数由“指数分布”随机决定,高层节点稀疏,底层节点稠密。
  • 索引构建过程
    1.确定节点层数。根据指数分布   p ( l ) = ( 1 − p ) l × p \ p(l) = (1-p)^l \times p  p(l)=(1p)l×p其中l为最大层级,p为分布概率,一般取p=0.9。比如l=3就说明它存在于Layer 3~Layer 0中。但是l取得越大概率越小,也符合高层节点稀疏的特性。
    2.初始化当前节点。从顶层的入口节点开始(通常为第一个节点),逐层向下处理。如果new_node位于已有层,则从起始节点开始处理,如果不是已有层,则先新建一个更高层,在从起始节点开始处理。
    3.在每层寻找候选节点。对于Layer l,从选取的L层到0层,通过局部导航找到距离new_node最近的(e_f)个候选节点,e_f为自己设置的连接数。贪心策略—— 从当前层的起始节点出发,只找 “当前节点的邻居里离 v 更近的节点”,一步步迭代,直到找不到更近的节点为止,不是全局对比所有节点;候选数 e_f:每层最终只选 e_f 个离 v 最近的节点作为候选。
    4.建立连接。在Layer l层中将new_node与候选节点们建立双向连接,然后删除“冗余连接”,其中每层会有一个连接参数M,按照距离远近将e_f个候选节点排序,并确保只保留<=M个候选节点。
    5.更新入口节点。若L大于当前最高层级,则更新L为最高层,且设置new_node为入口节点。
  • 相关例子说明
    • 场景1:第一个node插入。
      比如我们插入的第一个节点为红富士苹果,然后随机生成的L=2,这样初始化层级为Layer 2~Layer 0,由于其他层都没有node所以其他步骤不会有任何更新,最后更新最高层和入口节点。
    • 场景2:插入new_node且new_node层级大于入口node层级。
      假设红富士为L=2,青苹果为L=1,new_node为阿克苏苹果,e_f=2&M=2,这样阿克苏会建立和红富士和青苹果的连接,且最高层及其入口node分别为L=3和阿克苏苹果。但是Layer 2~Layer 0入口依然是红富士。
    • 场景3:插入new_node且不更新最高层。
      假设新苹果叫为嘎啦苹果,这里设置L=1,这样在L=3和L=2的层级它只会导航而不会建立分身。Layer 1~Layer 0就会寻找候选。
    • ps : L=l是指Layer l~Layer 0都会有这个node的分身。然后它在每一层的连接是相互独立的,举个简单的例子——可以把同一节点的不同层级分身,理解成同一个人在不同社交圈的身份:
      你在 “公司高管圈”(高层)的朋友,都是和工作强相关的核心伙伴(数量少,精准对接);
      你在 “小区邻居圈”(底层)的朋友,是日常相处的邻居(数量多,覆盖面广);
      所以每个层级之间相互独立。

检索过程(查询匹配)

  • 顶层导航:从顶层入口节点开始,在当前层( l )(从最高层到Layer 1)中,通过“贪心搜索”(每次跳转至距离( q )最近的邻居节点)找到距离( q )最近的候选节点( c ),将( c )作为下一层的起始节点;
  • 底层精准匹配:在Layer 0(稠密图)中,以步骤1得到的( c )为起点,扩大候选范围(如寻找( e_s )个候选节点,( e_s )为检索时的候选数参数),计算这些候选节点与( q )的距离;
  • 筛选Top-K:对Layer 0中所有候选节点按距离排序,取前K个作为最终检索结果。
  • ps :个人感觉这段文字其实挺好理解,就是有些地方还是有些模棱两可,所以依然是场景例子帮助理解。
  • 相关例子说明:
    假设当前网络为:
    顶层:Layer 3,顶层入口节点:阿克苏苹果(L=3,Layer 3 唯一节点)
    各层节点分布:
    Layer 3:阿克苏
    Layer 2:阿克苏、红富士
    Layer 1:阿克苏、红富士、青苹果、嘎啦
    Layer 0(稠密底层):阿克苏、红富士、青苹果、嘎啦、冰糖心(所有水果都在)
    查找的相关内容为:
    查询向量 q:“适合做沙拉的甜口苹果”(语义和红富士、冰糖心最接近)
    目标:检索 Top-K=2 个最相似向量
    检索候选数 e_s=3(底层扩大候选范围时,最多找 3 个节点)
    过程为:从Layer 3~Layer 1,由于阿克苏没有邻居,所以计算距离d=0.25(假设的)然后到Layer 2然后找阿克苏的邻居,计算红富士 d=0.1然后更新当前node为红富士,然后没有邻居了,继续向下寻找,计算Layer 1中红富士邻居嘎啦和青苹果,都不如d=0.1小;至此第一步导航完成。
    然后进入第二步,这样进入Layer 0的起始节点为红富士,但是我们设置e_s=3(候选节点数)避免遗漏,这样从红富士开始找邻居以及邻居的邻居,可能会超过3个,但是没关系,我们可以进行排序后筛选。
    最后,把底层所有候选节点按 “与 q 的距离从小到大” 排序,取前 K 个,就是最终的检索结果。

HNSW优势

搜索速度快,适配高维特征向量、搜索准确度高。

ps:这周之后应该会更新后端开发的内容,大模型可能会再往后延一延。但是肯定会继续更新大模型的。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐