AI 基础科普:从神经网络、LLM 到训练与推理
AI基础 - 丨渍丨
一篇面向入门者的 AI 基础科普,梳理了神经网络输入层、隐藏层、输出层与权重、激活函数、反向传播的关系,并解释梯度消失与梯度爆炸的成因。文章还介绍 LLM 按 token 续写、上下文窗口与多模态编码器,区分预训练、SFT、RLHF、DPO 及 LoRA、QLoRA 微调,并说明推理阶段的 KV Cache 与投机解码。
什么是 AI
AI(Artificial Intelligence,人工智能)可以先理解成一组能力,让计算机在某些任务上表现出「类似智能」的行为。
机器学习
| 学习方式 | 有没有标准答案 | 典型场景 |
|---|---|---|
| 监督学习 | 有 | 垃圾邮件识别、房价预测、图像分类 |
| 无监督学习 | 没有 | 用户分群、异常检测、数据压缩 |
| 强化学习 | 延迟的奖励信号 | 游戏 AI、机器人控制、推荐系统 |
深度学习
深度学习的核心是神经网络(Neural Network)。这个名字来源于生物学——人脑里有大约 860 亿个神经元,互相连接形成网络。人工神经网络是一种大大简化的数学模拟,但基本思想很像:很多简单的计算单元连在一起,通过层层传递,最终输出一个复杂判断。
不要被「神经」两个字吓到。你可以把神经网络想象成一个工厂流水线,每一层工人负责一道工序,层层加工,最后产出结果。
输入层(Input Layer):接收原始数据。如果是图像,输入层的每个节点对应一个像素点的亮度或颜色值。如果是文本,输入的是每个词对应的数字编码。
隐藏层(Hidden Layer):真正干活的地方。这一层的每个「神经元」都会对上一层的输出做加权求和,再通过一个激活函数决定是否「开火」——把信号往下传递。
你可以把隐藏层理解为流水线的中间工序。第一层可能识别"这里有条横线",第二层识别"横线加竖线像是个耳朵",第三层识别"耳朵加圆脑袋大概率是猫"。层数越多,能识别的模式越抽象、越复杂。
输出层(Output Layer):给出最终判断。猫狗分类任务里,输出层有两个节点,分别输出"是猫的概率"和"是狗的概率"。哪个概率高,模型就选哪个。
权重:每个连接的「重要性分数」
层与层之间的每条连线,都有一个数值叫权重(Weight)。它决定上一层某个信号对下一层某个神经元的影响有多大。
打个比方:判断一个人会不会违约,你考虑了三个特征——收入、负债率、学历。你觉得收入最重要,给它的权重是 0.6;负债率次之,权重 0.3;学历相对次要,权重 0.1。神经网络里的权重就是这个逻辑,只不过它有成千上万个特征和连接,权重的值是训练过程中自动调整出来的,而不是人定的。
训练神经网络的过程,本质上就是不断调整这些权重的过程。调对了,模型输出就准;调错了,就继续改。算法用来调权重的方法叫反向传播(Backpropagation):模型做完一题,对照答案算出差多少,然后从输出层往回一层一层地「追责」,告诉每一层该往哪个方向改。
激活函数:决定要不要传递信号
如果神经元只是简单地把输入加起来,那多层和一层没什么区别——多层嵌套后仍然是线性运算,学不到复杂规律。
激活函数(Activation Function)的作用就是打破这种线性。它像一个开关:输入信号够强,就输出一个非零值;不够强,就输出零或接近零。
ReLU(修正线性单元)是最常用的激活函数之一:输入大于零,原样输出;输入小于等于零,输出零。就像公司审批流程——预算超过门槛才往上递,不够门槛直接驳回。
这种非线性让神经网络能拟合各种弯曲、复杂的关系,而不是只能画直线。
梯度消失与梯度爆炸:深层网络的「走极端」问题¶
层数多了之后,有一个训练上的大麻烦——梯度消失与梯度爆炸。
反向传播时,梯度要从输出层一层一层往回传。如果每层都乘以一个小于 1 的系数,传到最前面几层时梯度就接近零了,权重根本得不到更新,这就是梯度消失。反过来,如果每层乘以一个大于 1 的系数,梯度会越乘越大,变成天文数字,权重一下子蹦飞了,这就是梯度爆炸。
「深度」指的是神经网络的隐藏层很多。传统神经网络可能只有 1~2 个隐藏层,而深度学习模型动辄几十层、上百层,甚至上千层。
层数多带来的好处是:模型能学习不同抽象层次的特征。
以图像识别为例:
- 第 1~2 层:识别边缘、颜色块、简单纹理
- 第 3~5 层:识别眼睛、耳朵、轮子、翅膀等基本部件
- 第 6~10 层:识别「猫脸」「汽车轮廓」「建筑结构」等整体概念
- 更深层:理解场景关系,比如「一只猫趴在沙发上」
什么是 LLM
LM(Large Language Model,大语言模型)可以先理解成一台巨大的语言续写机器。它根据上下文预测后面最可能接什么,再一个 token 一个 token 地写下去。

一句话在模型身体里走一圈
Token 文本的切片单位。模型处理文字时,会沿着 token 一步步往前走。例子,人工智能可能被切成「人工」和「智能」。
参数 模型内部可调整的数字。训练就是反复调整这些数字,让模型越来越会预测。参数更像一大片被训练出来的语言肌肉,别把它想成一条条知识卡片。
上下文窗口 模型一次能看到多少信息。窗口越大,它能同时参考的前文、文件、代码越多。窗口变大,信息容量会变宽;抓错重点、漏看细节的情况仍然会发生。
多模态 AI
多模态 AI(Multimodal AI)就是让 AI 同时处理文字、图片、语音和视频,把不同形式的信息放到同一张理解地图里。
编码器(Encoder)。编码器就是把原始信息转换成向量表示的模块。图像编码器负责把图片变成向量,音频编码器负责把声音变成向量,文本编码器负责把文字变成向量。模型真正处理的是这些转换后的表示——它从来没有直接「看到」图片或「听到」声音。
模型、数据、训练与推理
模型(Model)像一套会计算的规则,数据(Data)是它看过的材料,训练(Training)负责把规则调出来,推理(Inference)就是训练好的模型真正开始回答问题。
模型:一套会计算的复杂规则
可以先理解成一个数学函数:你给它输入,它给你输出。
text 输入:一张猫的图片 → [模型] → 输出:猫,置信度 99% 输入:一段中文句子 → [模型] → 输出:对应英文翻译 输入:请解释量子计算 → [模型] → 输出:一段解释文字
参数是什么
模型内部有很多参数(Parameter)。你可以把参数想成一堆旋钮。训练前,旋钮的状态基本是随机的;训练中,系统会不断调整这些旋钮;训练后,旋钮组合就变成了模型的能力。
数据:模型真正吃进去的东西
数据(Data)是模型学习的原料。
对于大语言模型来说,最常见的数据是文本和代码;对于多模态模型,还会加入图片、音频、视频。数据进入模型前,通常会先被处理成 token、图像块、音频特征等形式。
训练:预测、犯错、改参数
训练(Training)就是让模型从数据中学习规律,并不断调整参数。
对语言模型来说,最基础的训练任务很朴素:预测下一个 token。
比如给模型一句话:
text 深圳今天的天气很
模型会猜下一个 token 可能是「热」「好」「潮湿」。训练系统会拿模型预测和真实文本对比,算出误差,再通过反向传播(Backpropagation)调整参数。
预训练、后训练、微调:三件事别混
预训练:先学通用能力
预训练(Pre-training)像通识教育。模型先读海量文本、代码和多模态材料,学语言规律、常识、知识关联和基础推理能力。
预训练得到的模型叫基座模型(Base Model)。基座模型通常会续写文本,但未必适合直接聊天。你问它「帮我总结这篇文章」,它可能顺着你的话继续补一篇文章,没有按你的要求给摘要。
监督微调(Supervised Fine-Tuning,SFT)像看标准答案。人类准备一批「指令 → 理想回答」样本,让模型学习应该怎样回应用户。
RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)是让模型从人类偏好里学习。
InstructGPT 论文给过一个经典流程:
- 收集标注员写的 prompt 和用户通过 API 提交的 prompt;
- 让标注员写出理想回答,用来做监督微调;
- 对同一个问题生成多个回答,让人类排序;
- 用这些偏好排序继续训练模型。
RLHF 很强,但工程上比较复杂。后来出现了 DPO(Direct Preference Optimization,直接偏好优化)。DPO 论文把传统 RLHF 里「训练奖励模型 + 强化学习优化」的流程,改成直接用偏好数据优化语言模型,目标是更简单、更稳定、更轻量。
LoRA 和 QLoRA:普通人更常听到的微调
很多企业说「我们基于开源模型微调了一个行业模型」,大概率没有从零训练,只是在已有基座模型上做参数高效微调。
LoRA(Low-Rank Adaptation,低秩适配)就是常见方法之一。LoRA 论文的做法是冻结原模型权重,只训练额外加入的小矩阵。论文摘要里提到,对 GPT-3 175B 这类规模模型,LoRA 相比全量微调可以把可训练参数减少 10,000 倍,GPU 显存需求降低 3 倍。
QLoRA(Quantized LoRA,量化 LoRA)进一步把大模型以 4-bit 形式量化,再训练 LoRA 适配器。QLoRA 论文提到,它能把 65B 参数模型微调压到单张 48GB GPU 上,还提出了 NF4、double quantization、paged optimizers 等技巧。
这就是为什么普通团队也能「微调大模型」。它们通常没有重新训练整个模型,只是在已有模型旁边加了一层便宜得多的适配器。
推理:模型真正开始干活
| 维度 | 训练 | 推理 |
|---|---|---|
| 目标 | 学习规律,调整参数 | 使用规律,生成结果 |
| 参数 | 会更新 | 通常固定 |
| 成本 | 一次投入巨大 | 按调用持续消耗 |
| 时间尺度 | 几天到几个月 | 毫秒到分钟 |
| 典型场景 | 训练 GPT、LLaMA、DeepSeek | 用户聊天、代码补全、RAG 问答 |
工程上有很多推理优化方法。
比如 KV Cache。模型生成第 100 个 token 时,不想把前 99 个 token 的注意力结果全部重算一遍,于是会缓存前面的 Key/Value。这样速度更快,但显存压力也会变大。
还有 投机解码(Speculative Decoding)。Speculative Sampling 论文的做法是让一个更快的小模型先草拟几个 token,再让大模型并行验证。论文在 Chinchilla 70B 上报告了 2–2.5 倍解码加速,而且不修改目标模型本身。
最小示例:教一个模型认识「猫」¶
用一个小例子把这几个概念串起来。
假设你要做一个识别猫的模型。
| 环节 | 在这个例子里是什么 |
|---|---|
| 模型 | 一个能处理图片的神经网络 |
| 数据 | 很多猫、狗、兔子、鸟的照片 |
| 标签 | 每张图对应的答案,比如「猫」或「狗」 |
| 训练 | 模型猜答案,猜错就调参数 |
| 推理 | 看到一张新照片,判断里面是不是猫 |
一个大模型从训练到上线,大概会经历什么
Token、Embedding 与上下文窗口
Token(词元)可以先理解成模型处理文本时使用的「文本颗粒」。它可能是一个字、一个词、一个词根、一个标点,也可能是一段字节。
Embedding(嵌入 / 向量表示)做的事,就是把文本、图片、音频或其他对象变成一串数字。语义相近的内容,在向量空间里距离更近;语义差很远的内容,距离更远。
Embedding 能做什么
常见用途可以这样看:
| 场景 | 怎么用 embedding |
|---|---|
| 语义搜索 | 把问题和文档都转成向量,找距离最近的文档 |
| 聚类 | 把相似评论、工单、用户反馈自动归类 |
| 推荐 | 找和用户已读内容相近的文章或商品 |
| 去重 | 找语义重复但文字不同的内容 |
| 分类 | 用文本向量和标签向量的相似度判断类别 |
| RAG | 先检索相关片段,再交给大模型回答 |
上下文窗口:模型一次能看的工作台 上下文窗口(Context Window)可以理解成模型一次生成答案时能参考的 token 总容量。
text 上下文窗口 ≈ 系统提示词 + 历史对话 + 当前输入 + 工具结果 + 本轮输出
三者关系:从文本到回答
Token、Embedding、上下文窗口不是三块孤立知识。它们正好对应模型处理信息的三个层面。
Prompt、上下文和记忆
Prompt负责说明这次任务,上下文决定模型当前能看到什么,记忆负责把长期信息重新带回现场。
模型回答时,会把当前可见的上下文一起拿来判断。
温度与采样参数
温度、Top-k、Top-p决定模型从候选 token 里怎么选下一个 token。它们不负责增加知识,负责控制输出时的随机性、稳定性和多样性。
模型每次生成下一个 token 时,会先给大量候选 token 打分,再把分数变成概率。解码策略负责回答一个很具体的问题:下一步到底选谁?
可以先记一句人话:Prompt 决定模型往哪儿走,采样参数决定它走得稳不稳、散不散。
模型生成文字时,到底在选什么
LLM 是一类自回归语言模型。它每次只做一件事:根据当前上下文预测下一个 token。
在 Hugging Face 的 generation strategies 文档里,decoding strategy 被定义为决定模型如何选择下一个生成 token 的规则。最简单的规则叫 Greedy Search(贪心搜索):每一步都选概率最高的 token。
这听起来很合理。
问题也从这里开始。
贪心每一步都选最可能的词,短答案通常很稳,长文本却容易变得机械、重复。Hugging Face 文档里直接提醒:greedy search 适合较短、创意优先级不高的任务,生成长序列时可能开始重复。Hugging Face 的经典文章 How to generate text 也展示过 GPT-2 生成时反复出现同一句话的例子。
这就是采样参数存在的原因。它们让模型不要只盯着概率最高的那个 token,而是在一个可控范围内保留变化。
Temperature:控制随机性,不控制事实性
它只改变模型从已有概率分布里怎么选 token。事实任务里,低温通常更稳;创意任务里,高温通常更有变化。可如果模型没见过、上下文没给、检索没查到,温度再低也可能编。
Top-k:只从前 k 个里抽
Top-k sampling 的规则很直接:模型把所有候选 token 按概率排序,只保留前 k 个,其他候选概率设为 0,再从剩下的候选里抽样。
Top-p:按累计概率动态截断
Top-p sampling 也叫 Nucleus Sampling(核采样)。它不固定保留几个候选,会保留一个「累计概率达到 p 的最小候选集合」。
Top-p 的好处是动态。模型很确定时,候选池会自动变小。模型不确定时,候选池会自动变大。
Top-k 和 Top-p 怎么配
一种路线是只调一个主旋钮。
Anthropic 文档把 top_p 和 top_k 都标为高级用法,普通场景优先用 temperature 会更直观。很多产品界面也只露出 temperature,因为它最好理解。
另一种路线是组合使用。
Hugging Face 的 How to generate text 给过组合示例:top_k=50 加 top_p=0.95。这样 Top-k 先限制最低质量的长尾,Top-p 再根据累计概率动态收缩候选池。
两种路线都能用。新手建议按这个顺序来:
- 先用默认参数跑一遍;
- 输出太跳,先降 temperature;
- 低温后仍有怪词,再收紧 top_p 或 top_k;
- 只改一个参数,记录变化;
- 找到稳定范围后再微调。
别一口气把 temperature、top_p、top_k 全拧到极端值。那样很难判断到底是哪一个参数带来了变化。
| 任务 | Temperature | Top-p | Top-k | 说明 |
|---|---|---|---|---|
| 分类、抽取、固定标签 | 0 - 0.2 | 默认或偏低 | 默认或偏低 | 目标是稳定,不追求花样 |
| 代码生成、配置生成 | 0 - 0.3 | 0.7 - 0.9 | 默认或 20 - 50 | 先保证一致性,再看是否需要变化 |
| 事实问答、资料总结 | 0.2 - 0.5 | 0.8 - 0.95 | 默认 | 温度低一点,重点靠上下文和引用约束 |
| 翻译、改写 | 0.3 - 0.7 | 0.8 - 0.95 | 默认 | 保留语义,允许措辞变化 |
| 日常聊天 | 0.5 - 0.9 | 0.9 - 0.95 | 默认或 40 - 50 | 需要自然度和一点变化 |
| 标题、口号、命名 | 0.8 - 1.2 | 0.9 - 0.98 | 40 - 100 | 多生成几个候选更重要 |
| 诗歌、故事、脑洞 | 0.9 以上 | 0.95 左右 | 50 以上 | 接受少量跑飞,靠人工挑选 |
一个简单决策流程
幻觉
| 层次 | 怎么判断 | 例子 |
|---|---|---|
| 与现实冲突 | 现实世界里查不到或事实相反 | 编造不存在的公司收购 |
| 与材料冲突 | 用户给的文档里没有,甚至相反 | 总结合同时新增了一条付款条款 |
| 无法验证 | 看起来合理,但找不到来源 | 捏造论文、链接、采访原话 |
开发者可以这样做
| 层面 | 做法 | 适合场景 |
|---|---|---|
| 数据 | 建可靠知识库,清理过期和重复资料 | 企业问答、客服 |
| 检索 | 评估召回率、重排效果和引用命中 | RAG 系统 |
| 生成 | 限制回答范围,要求引用证据 | 事实问答 |
| 输出 | 做格式校验、来源校验、事实核查 | 报告、公告、代码 |
| 权限 | 高风险工具调用前人工确认 | Agent、自动化工作流 |
| 评测 | 建幻觉测试集和回归测试 | 产品迭代 |
| 监控 | 记录用户反馈和错误案例 | 线上系统 |
普通用户可以这样做
| 做法 | Prompt 写法 | 作用 |
|---|---|---|
| 允许它停下来 | 如果资料不足,请说「无法确认」 | 减少硬猜 |
| 限定来源 | 只基于我提供的材料回答 | 避免混入外部印象 |
| 先要证据 | 先列出原文引用,再回答 | 让结论有支点 |
| 要求标注不确定 | 把无法确认的信息单独列出 | 防止混进正文 |
| 降低温度 | 事实任务用低 temperature | 让输出更稳 |
| 多跑几次对比 | 同一问题生成 3 次,看差异 | 找出不稳定点 |
| 关键内容人工核查 | 对数字、引用、结论逐条查源 | 防止错用 |
Prompt 到底是什么
Prompt(提示词)是你输入给 AI 模型的文本,用来告诉模型你想让它做什么。
更准确地说,Prompt 是一种输入设计(Input Design)——你不是在"跟 AI 聊天",而是在给 AI 下发一份任务说明书。模型根据说明书里的信息,生成对应的输出。
为什么 Prompt 很重要
同一个模型,不同的 Prompt,结果可以天差地别。
我们用 GPT-4o(OpenAI 2024 年发布的多模态大模型)做一个对比实验。需求都是一样的:解释什么是 RAG。
Prompt 版本 A(随意型):
RAG 是什么?
模型可能会给出一篇 500 字的技术解释,充满术语,对小白很不友好。因为它不知道你的背景,只能按"最通用的方式"回答。
Prompt 版本 B(结构化型):
角色:你是面向小白的技术科普作者。 任务:解释 RAG(Retrieval-Augmented Generation,检索增强生成)这个概念。 要求: 1. 先说明它解决什么问题 2. 用一个生活化的比喻来解释原理 3. 列出 1 个常见使用场景 4. 控制在 200 字以内
这次的输出会清晰很多:有场景、有比喻、有结构、有字数控制。模型知道"读者是谁""该用什么风格""多长算够",所以不会乱发挥。
这就是 Prompt 的价值:它不改变模型本身的能力,但能让模型把能力用在对的地方。
Prompt 的核心组件
角色(Role):你希望模型扮演谁
任务(Task):你要模型做什么
约束(Constraint):不能做什么、必须满足什么
输出格式(Output Format):结果长什么样
示例(Example):给模型看"好的回答"长什么样
最小模板:直接套用
如果你记不住那么多理论,直接用这个模板填空就行:
text 角色:你是一位[具体身份]。 任务:请[具体动作][对象/内容]。 背景:[补充必要的上下文信息,如读者是谁、使用场景是什么] 约束: 1. [第一条限制,如长度、风格] 2. [第二条限制,如必须包含/不能出现] 3. [第三条限制] 输出格式:[段落/列表/表格/JSON/Markdown 等]
text 角色:你是一位高中物理老师。 任务:请用牛顿三定律解释为什么坐公交车没座位时要抓紧扶手。 背景:面向高一学生,他们刚学完牛顿定律但还不习惯联系生活。 约束: 1. 用通俗中文,不要直接用公式符号(可以用文字描述公式含义) 2. 不超过 300 字 3. 举一个公交车急刹车的具体例子 输出格式:一段完整的讲解文字,分成"现象—原理—结论"三部分。
Prompt 模板
Prompt 模板(Prompt Template) 是一种带有占位符(placeholder)的 Prompt 结构,你把固定部分写好,把变化部分留空,每次使用时填入具体内容即可。
简单记法:
- 太长不想看 → 总结
- 这样说不太合适 → 改写
- 这是哪一类 → 分类
- 把XX挑出来 → 抽取
LLM 的三个天然局限
要理解为什么需要 RAG,先要理解 LLM 本身有哪些做不到的事:
局限 1:知识有截止日期
LLM 的训练数据有一个截止时间。GPT-4o 的知识截止于 2023 年 10 月左右。如果你问「2025 年发布的 iPhone 17 有什么新功能」,它不可能知道,因为训练时这台手机还没发布。
局限 2:看不到私有数据
你的公司文档、产品手册、用户协议、内部知识库——LLM 一概不知。它的训练数据来自互联网公开内容,不包含任何非公开信息。
局限 3:不知道的它会「编」
当 LLM 被问到不知道的问题时,它倾向于自信地给出看起来合理的错误答案。这种现象叫幻觉(Hallucination)。不是模型故意撒谎,而是它的工作机制就是「根据上下文预测最合理的下一个词」。如果没有相关信息,它会预测一个「听起来靠谱」的答案。
RAG 通过提供真实的参考资料来缓解这三个问题:用检索获取最新信息、用私有知识库补充模型盲区、用原文减少编造空间。
文档切分
文档切分(Chunking)是把长文本切成小片段的过程——切得好,检索才能找得准;切得不好,后面的所有优化都救不回来。
切分的「粗细」直接影响检索效果:
| 粒度 | 典型大小 | 优点 | 缺点 |
|---|---|---|---|
| 粗粒度 | 按章节或段落 | 上下文完整,不会切断关键语句 | 可能包含无关信息,检索精度下降 |
| 中粒度 | 固定字符数(如 300~500) | 统一,便于批量处理 | 可能从句子中间切断 |
| 细粒度 | 按句子或语义段落 | 精确匹配用户问题 | 缺少上下文,信息可能不完整 |
什么是向量
向量是一组浮点数,比如 [0.023, -0.157, 0.892, ...](通常 256~3072 维)。每个维度代表文本在某个潜在「语义方向」上的分量。
相似度计算
余弦相似度(Cosine Similarity)¶
衡量两个向量在方向上的夹角大小,不考虑向量长度(模长)。
cos(a, b) = (a · b) / (|a| × |b|)
| 取值范围 | 含义 |
|---|---|
| 1 | 方向完全一致(语义最接近) |
| 0 | 正交,不相关 |
| -1 | 方向完全相反(语义最远) |
Python 实现:
```python import numpy as np
def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
vec_a = np.array([0.2, -0.1, 0.5]) vec_b = np.array([0.3, -0.2, 0.4]) print(f"余弦相似度:{cosine_similarity(vec_a, vec_b):.4f}") ```
适用场景:语义搜索、文本相似度判断、信息检索。这是 RAG 中最常用的指标,因为它只关心方向(语义倾向),不受文本长度影响。
OpenAI embeddings 已经归一化
OpenAI 的 text-embedding-3-small 和 text-embedding-3-large 输出默认是 L2 归一化的(模长为 1)。此时余弦相似度等价于点积,可以省去分母计算。
点积相似度(Dot Product / Inner Product)¶
直接计算两个向量的内积:
dot(a, b) = a₁b₁ + a₂b₂ + ... + aₙbₙ
```python def dot_product(a, b): return np.dot(a, b)
print(f"点积:{dot_product(vec_a, vec_b):.4f}") ```
| 特点 | 说明 |
|---|---|
| 与余弦的关系 | 当向量已 L2 归一化时,点积 = 余弦相似度 |
| 未归一化时 | 同时受方向和模长影响——模长长的向量更容易得高分 |
| 取值范围 | 无固定边界,取决于向量维度和数值范围 |
适用场景: - 向量已归一化时:直接替代余弦相似度,计算更快(省一次模长运算) - 向量未归一化时:适合对「高频/长文档」有偏好的场景(如长文本比短文本更值得被检索到)
欧氏距离(Euclidean Distance / L2)¶
衡量两个向量在多维空间中的直线距离。距离越小越相似。
L2(a, b) = √[(a₁ - b₁)² + (a₂ - b₂)² + ... + (aₙ - bₙ)²]
```python def euclidean_distance(a, b): return np.linalg.norm(a - b)
def euclidean_similarity(a, b): """将距离转为相似度分数,方便统一比较""" return 1 / (1 + euclidean_distance(a, b)) ```
与余弦相似度的关键区别:
| 维度 | 余弦相似度 | 欧氏距离 |
|---|---|---|
| 关注点 | 方向夹角 | 绝对距离 |
| 受向量长度影响 | 否 | 是 |
| 举例 | (1,0) 与 (10,0) 的 cos=1,完全相同 | (1,0) 与 (10,0) 的 L2=9,差异很大 |
适用场景:当向量长度本身携带语义信息时(比如向量的模长代表「信息量」或「置信度」),欧氏距离能同时捕捉方向和大小的差异。
曼哈顿距离(Manhattan Distance / L1)¶
计算两个向量在各维度上的绝对差之和:
L1(a, b) = |a₁ - b₁| + |a₂ - b₂| + ... + |aₙ - bₙ|
python def manhattan_distance(a, b): return np.sum(np.abs(a - b))
与欧氏距离对比:曼哈顿距离对单个维度的异常差异不那么敏感(不取平方)。在高维空间中,曼哈顿距离有时比欧氏距离更稳定(缓解「维度灾难」)。但 Embedding 检索中较少直接使用曼哈顿距离。
各指标对比总结¶
```python import numpy as np
def compare_metrics(a, b): """一次性输出多种相似度指标""" cos = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) dot = np.dot(a, b) l2 = np.linalg.norm(a - b) l1 = np.sum(np.abs(a - b))
return {
"余弦相似度": cos,
"点积": dot,
"欧氏距离": l2,
"欧氏相似度": 1 / (1 + l2),
"曼哈顿距离": l1,
}v1 = np.array([0.2, -0.1, 0.5]) v2 = np.array([0.3, -0.2, 0.4]) v3 = np.array([-0.5, 0.8, 0.1])
for name, val in compare_metrics(v1, v2).items(): print(f"{name}: {val:.4f}") ```
在 RAG 实践中,余弦相似度是默认选择,其次是点积(向量归一化后等效于余弦)。欧氏距离作为补充,适合向量长度携带信息的场景。向量数据库(FAISS、Milvus 等)通常同时支持多种距离度量,可以在建索引时通过参数指定。
向量数据库
| 工具 | 类型 | 规模 | 特点 |
|---|---|---|---|
| Chroma | 本地嵌入式 | 小规模 | 简单易用,适合原型 |
| FAISS | 本地库 | 中大规模 | Meta 开源,性能好 |
| Milvus | 分布式 | 大规模 | 云原生,功能完整 |
| Weaviate | 分布式 | 中大规模 | 原生支持多种 Embedding 模型 |
| Qdrant | 分布式 | 中大规模 | Rust 实现,性能优异 |
| PostgreSQL + pgvector | 扩展 | 中规模 | 如果你已经在用 Postgres |
检索的基本流程
| 维度 | 语义检索(Semantic / Dense) | 关键词检索(Lexical / Sparse) |
|---|---|---|
| 原理 | 用 Embedding 模型理解语义 | 用 BM25/TF-IDF 匹配关键词 |
| 优势 | 理解同义词、近义表达 | 精确匹配专有名词(如产品型号) |
| 劣势 | 对罕见词和专有名词敏感度低 | 不理解语义,「苹果」搜不到「iPhone」 |
| 场景 | 开放域问答、多轮对话 | 搜索文档标题、代码片段 |
| 工具 | 向量数据库 | Elasticsearch、Whoosh |
实践中,很多系统采用混合检索(Hybrid Search / Multi-Recall)——同时用语义检索和关键词检索,分别取 Top-K,然后合并去重。混合检索往往比单一方式更鲁棒。
评估检索有两个来自信息检索的经典指标:
- 精确率(Precision,查准率):返回的 K 个结果中,有多少是真正相关的
- 召回率(Recall,查全率):知识库中所有相关文档,被检索回来的占比
重排
重排(Rerank)是对初步检索结果进行二次排序的过程,用更精确的模型将最相关的文档片段排在前面。
重排的本质是用精度换时间:把需要高精度的运算限制在少量候选项上。
第一阶段(检索):用 Embedding 模型快速召回 Top-50 ~ Top-100
第二阶段(重排):用交叉编码器(Cross-Encoder)对每个候选逐条打分,只保留 Top-3 ~ Top-5
重排的工作原理
输入是问题和文档拼接后的文本对,输出是 0~1 之间的相关度分数。模型能同时看到问题中的每个词和文档中的每个词,捕捉它们之间的交互关系。这就是为什么交叉编码器比双编码器精度更高的原因。
生成
生成阶段的任务是:把资料、问题和指令精确地编排在一起,让 LLM 知道自己的角色、知道哪些是事实依据、知道什么情况下该说「不知道」。
一个标准的 RAG Prompt 包含三部分:
- 指令(Instruction):告诉 LLM 它的角色和任务
- 资料(Context):检索到的文档片段
- 问题(Question):用户的原问题
来源:博客园 · cnblogs.com