qinghuan
← 理解模型与输入 / 全部笔记

单元 05 / 理解模型与输入

P0 · 必会主线L1 · 基础

用向量和相似度建立 Embedding 直觉

先用一组数字亲手算相似度,再理解文本向量如何用于查找相关资料。

本篇目录 · 按需要跳转

这篇重点学什么

本篇重点:先核对查询和文档的编码模型版本、归一化方式与索引是否兼容;换模型或迁移索引后检查检索结果,再用标注样例确定并检验相似度阈值。

开始读正文 ↓
学过相近内容?查看哪些可以跳过

两条路线有些基础相同。已经学会的部分可以跳过,另一条路线只用来补你当前需要的内容。

向量与相似度

共同基础点积、范数、余弦与稳定计算统一以数学篇为主读;掌握后不重复做同类数值练习。

另一条路线补充什么?

推导并计算 L1 / L2 / L∞ 范数、点积和余弦,再用相近数相减的例子检查浮点抵消误差,学习稳定计算。

读完这篇,你能做什么

能手算并运行余弦示例,解释为什么跨模型向量不能只凭维度相同就混用。

学习理由、准备要求与知识点

为什么学它

检索会比较向量有多接近。理解距离分数在表示什么,才能选对索引,也避免把“相似”误当成“事实可靠”。

先读这些内容

知识点清单

  • 标量、向量与矩阵
  • 维度与对应位置
  • 点积
  • 向量长度与零向量
  • 余弦相似度
  • 相似度与概率区别
  • Embedding 表示
  • 模型版本与编码兼容

运行前先确认完整算法使用 Java 17 标准库;JDK 源码基线为 OpenJDK 17 GA。示例是内存中的机制模型,外部服务接入需要另外配置和验证。

01先弄懂它是什么

想让程序比较学习笔记是否相关,可以先把文本表示为一组数字,再计算这些表示之间的关系。一个数叫标量,固定位置的一组数叫向量,有行列的数表叫矩阵。向量有多少维,并不天然对应多少个知识点;它能做什么取决于训练方式,单个坐标通常也不能直接翻译成某个词或清楚的含义。

Embedding 会把文本变成固定维度的向量,查询和文档必须采用互相兼容的编码方式。两个模型即使都输出同样长的数组,它们每个位置代表的坐标体系也可能不同。更换模型或预处理方式后,通常应重新生成旧向量,或按版本分开索引;程序能完成点积运算,不代表新查询与旧文档的比较仍有意义。

复习已学内容:需要时展开讲解和代码

点积把相同位置相乘再求和,向量长度是平方和开根号,余弦相似度用点积除以两个长度。它主要比较方向,忽略整体正比例缩放;因此[1,2]与[2,4]余弦为一。相似度不是概率,也不是事实支持程度,仍需用任务标注校准检索阈值。

文中这几个词,先认一认

点积
把两个同维向量的对应数相乘,再把结果相加。它同时受到向量方向和长度的影响。
范数
按规定计算向量有多大的方法,本篇使用由勾股定理推广出的欧氏长度。
零向量
每个位置都是零的向量。它没有方向,因此不能直接代入余弦相似度公式。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

复习已学内容:需要时展开讲解和代码

手算[1,0]与[0,1]:点积为零,长度都为一,余弦为零;与[-1,0]则为负一。负值表示该表示空间中的相反方向,不应直接解释为自然语言逻辑否定。哪些相似度对应相关资料,要通过同一模型、相同任务上的标注数据观察。

直接计算平方可能溢出。例如Double.MAX_VALUE的平方变成无穷,而两个相同巨大向量理论余弦仍是一。算法先分别除以各自最大绝对坐标,再计算点积与长度;因为正比例缩放在余弦分子分母中相互抵消,数学结果保持不变,同时中间数值更稳定。

计算相似度前,先检查向量维度、零向量,以及 NaN 或无穷这类非有限值。NaN 会让比较排序失常,无穷可能让归一化无效,不能等搜不到结果再查。正常浮点计算可能产生很小的舍入误差,最后可以把结果限制在 [-1,1];但这一步不能用来掩盖输入非法或维度错误。

正在加载流程图…

图中状态只描述本篇可执行机制;业务调用仍需由外层应用负责。
查看流程图源码(Mermaid)
flowchart LR
  T["文本与编码版本"] --> E["兼容 Embedding"]
  E --> C["维度及有限值检查"]
  C --> S["最大绝对值缩放"]
  S --> D["点积和范数"]
  D --> R["相似度与标注验收"]

按顺序看,每一步做了什么

  1. 固定编码模型、维度、预处理与版本标识。
  2. 逐向量检查维度、非有限值和零向量。
  3. 按最大绝对值缩放,再计算点积及长度。
  4. 得到余弦后按相关标注评估排序与阈值。
  5. 保存来源版本,模型切换时重建或隔离索引。

03对照实现代码

OpenJDK 17 Double 源码 (新标签页)↗

OpenJDK 17 GA,jdk-17+35 · Double.isFinite

下方为原创 Java 教学片段,调用已核验的 JDK API 说明局部机制;不是该类原样源码,也不实现神经网络。完整程序见算法模块。

核心机制代码 · java
for (double value : vector) {
    // NaN 与正负无穷会破坏距离和排序,进入检索前明确拒绝。
    if (!Double.isFinite(value)) throw new IllegalArgumentException("non-finite coordinate");
}
// 输入有效只证明数值可计算,不证明向量来自兼容模型。

Double.isFinite区分有限数与NaN、无穷,是数值输入校验的真实JDK入口。零是有限数,所以零向量还需单独检查。

浮点数的范围、精度和特殊值是表示的属性,与Embedding模型优劣无关。输入校验应在公共边界执行,避免某个异常坐标污染整个候选排序。

Java代码负责可验证的向量算术,没有实现Embedding网络训练。真正的编码器如何学习表示,应阅读Embedding课程与模型原始资料;本题只是应用层相似度桥梁。

本例缩放减少平方溢出的风险,但浮点误差仍存在。判断数值测试使用容差,不能要求所有数学等价表达式在所有机器上产生逐位相同的结果。

代码先调用谁,再调用谁

  1. 读取向量及模型元数据
  2. Double.isFinite校验坐标
  3. 寻找最大绝对坐标
  4. 归一缩放后累计内积与平方和
  5. 输出有边界的余弦值

04动手写一个例子

完成主读数学篇的稳定余弦实现后,可以跳过这里的数值算法复习。余弦为负不说明两段文字互相矛盾,相似度也不是答案正确的概率;阈值要用对应模型和任务的标注数据确定并检查。接着检查编码版本与索引迁移;下方批量近邻和量化可按需深入。

复习已学内容:需要时展开讲解和代码

数值稳定的余弦相似度

输入两个等长非空有限实数向量,输出余弦;零向量与非法输入拒绝。Java17完整程序内置容差断言,编译Main.java后执行java Main。

输入 1
[1,2]与[2,4]
输出 1
1
输入 2
[1,0]与[0,1]
输出 2
0

先找两个向量各自最大绝对值ma、mb。若任意最大值为零,则对应向量没有方向,不能通过加一个任意极小常数悄悄定义业务相似度。

将每个坐标分别除以ma或mb,新坐标绝对值不超过一。余弦中的ma和mb在分子、分母抵消,因此缩放保留方向。累计ab、aa、bb,再计算ab/sqrt(aa)/sqrt(bb)。

分两次除法避免先把两个长度相乘引入额外溢出。最后处理舍入误差,测试包括极大坐标和极小非零坐标,确认稳定性策略确实覆盖了容易失败的输入。

为什么这样写是对的处理前i维后,ab是缩放向量前缀点积,aa与bb是对应前缀平方和;完整向量正比例缩放不改变理论余弦。

时间 O(d)额外空间 O(1)

d为维度,两次线性扫描,只有固定数量累加器;输入数组不被修改。此复杂度不包括生成Embedding的神经网络推理。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

Main.java · 完整可运行程序 · Java
public class Main {
    static double cosine(double[] a,double[] b){
        if(a==null||b==null||a.length==0||a.length!=b.length)throw new IllegalArgumentException("dimension");
        double ma=0,mb=0;
        for(int i=0;i<a.length;i++){
            if(!Double.isFinite(a[i])||!Double.isFinite(b[i]))throw new IllegalArgumentException("non-finite");
            ma=Math.max(ma,Math.abs(a[i]));mb=Math.max(mb,Math.abs(b[i]));
        }
        if(ma==0||mb==0)throw new IllegalArgumentException("zero vector"); // 零向量没有方向。
        double ab=0,aa=0,bb=0;
        for(int i=0;i<a.length;i++){
            double x=a[i]/ma,y=b[i]/mb; // 正比例缩放避免巨大坐标先平方溢出。
            ab+=x*y;aa+=x*x;bb+=y*y;
        }
        double value=ab/Math.sqrt(aa)/Math.sqrt(bb);
        return Math.max(-1,Math.min(1,value)); // 只修正有限计算中的微小舍入越界。
    }
    static void near(double expected,double got){if(Math.abs(expected-got)>1e-12)throw new AssertionError(got);}
    static void bad(double[] a,double[] b){try{cosine(a,b);throw new AssertionError();}catch(IllegalArgumentException expected){}}
    public static void main(String[] args){
        near(1,cosine(new double[]{1,2},new double[]{2,4}));
        near(0,cosine(new double[]{1,0},new double[]{0,1}));
        near(-1,cosine(new double[]{1},new double[]{-1}));
        near(1,cosine(new double[]{Double.MAX_VALUE},new double[]{Double.MIN_VALUE}));
        bad(new double[]{0},new double[]{1});bad(new double[]{Double.NaN},new double[]{1});
        bad(new double[]{1},new double[]{1,2});
        System.out.println("PASS 7 tests");
    }
}

这些特殊情况也要试一试

  • 零向量不定义余弦,需显式拒绝或由业务规定独立状态。
  • 同维度不同模型仍不兼容,算法无法从数字推断模型版本。
  • 极大和极小坐标使用缩放,比较结果用数值容差。
进阶选读 · 后续再读:批量近邻与向量量化

场景百万级向量需要减少存储和比较成本。

做法先建立精确搜索作为质量基线,再评估量化与近似索引;量化改变坐标精度,应比较召回与相关排序,不能只验证文件变小。

复杂度精确逐查询O(nd),量化或近似索引的查询成本取决于结构和参数,不保证对任意数据均为O(log n)。

好处与代价低精度和近似搜索节省资源,但可能改变近邻;高风险资料需保留版本和可复核的精确比较通路。

05项目里怎么用

检索入库时应记录模型标识、维度、归一化方式、原文片段ID及版本。查询端核对这些配置,发现不一致应停止或路由到匹配索引。只比较数组长度会让错误结果悄悄出现,这比直接抛维度异常更难定位。

阈值应由实际任务定义。课程标题很短时,相似度分布可能与长篇笔记不同;换语言、换模型或换语料后,原阈值不能直接照搬。准备相关、不相关、相似措辞但事实相反的样本,分别检查漏检与误检,再决定是否增加重排。

向量相似度只帮助选候选,引用是否支持答案还要读原文。两个段落可能主题相似但版本结论相反;对外输出时保留来源、版本与适用条件,而不是展示一个相似度数字就宣称“可信度百分之九十”。

容易踩的坑

  • 把余弦相似度乘一百显示为正确概率。
  • 混用维度相同但来源模型不同的向量。
  • 零向量加极小常数后默默继续排序。
  • 直接平方巨大坐标导致无穷与NaN传播。

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
余弦强调方向,适合已按该指标训练的表示忽略长度信息,零向量需处理语义检索的常见配置
点积保留方向与长度,计算直接长度可能主导排序模型训练明确使用点积
欧氏距离几何含义直观受尺度与归一化影响编码和索引采用一致欧氏配置

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01余弦一代表什么?

展开参考答案与推导

非零向量同方向;它不是文本必然等义、事实相同或概率百分之百的保证。

02相同维度为什么还不够?

展开参考答案与推导

坐标体系来自具体编码模型及预处理;不同模型的同一位置不必表示兼容信息。

03缩放为什么不改变余弦?

展开参考答案与推导

向量乘正数时,点积与两个长度的乘积获得相同缩放因子,分子分母相消。

换个情况,还会处理吗

01更换Embedding后召回突然下降怎么办?

展开参考答案与推导

核对查询与语料是否同时迁移、模型维度和归一化是否一致,再用固定标注集比较;不要先凭感觉调整阈值。

02相似度很高却引用错误版本怎么办?

展开参考答案与推导

增加版本与权限过滤,检查原文支持;语义相似不能替代版本约束和断言核对。

03排序出现NaN,应在哪里处理?

展开参考答案与推导

在向量进入计算或索引前拒绝非有限坐标,并检查零向量和溢出;不要让比较器把异常值当普通分数。

写代码试一试

01计算反方向向量

输入 / 条件
[2,0]与[-3,0]
输出 / 预期
-1

提示 点积为负。

展开参考答案与推导

点积为-6,长度乘积为6,余弦为-1;不据此推断自然语言逻辑矛盾。

02如何增加对称性测试?

输入 / 条件
a=[1,2],b=[3,4]
输出 / 预期
cos(a,b)约等于cos(b,a)

提示 使用容差比较。

展开参考答案与推导

交换输入应保留点积和范数乘积;若差异超容差,检查维度索引或错误的单侧归一化。

03为什么不能把编码耗时计入O(d)结论?

输入 / 条件
文本生成向量后计算余弦
输出 / 预期
O(d)只覆盖已给定数组

提示 区分接口边界。

展开参考答案与推导

神经网络编码有独立的结构、序列长度和硬件成本;本题输入已是向量,因此不对模型推理给出线性维度承诺。

读完之后,独立完成

课后作业:自己做一遍

本篇共 10 道作业,先完成 8 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

能手算并运行余弦示例,解释为什么跨模型向量不能只凭维度相同就混用。

本篇重点加练

相似度的业务含义依赖编码版本,重点验证分数解释与升级后的可比性。

  • 相似度与概率区别
  • 模型版本与编码兼容

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    用三篇笔记各含四个特征的例子区分标量、向量和矩阵;交换其中两个特征位置,解释比较前还需核对哪些契约。

    提交内容
    一份独立书面回答或标注图,包含自造例子、概念边界及判断依据。
    怎样算完成
    • 逐项回应题目要求,区分事实、假设与个人判断。
    • 能用自己的例子解释适用条件,记录尚未弄懂的点。

    练到的知识点标量、向量与矩阵 · 维度与对应位置

  2. 作业 02代码推演

    对向量[2,-1,3]、[0,4,1]和零向量手算点积与长度,保留推导和输入约束,自行规定无效输入的返回或异常方式。

    提交内容
    手工推演记录,写明输入约定、中间状态、最终判断;可运行时附程序对照记录。
    怎样算完成
    • 推演包含中间过程,数据形状、单位、顺序或状态前后一致。
    • 主动检查至少一个边界或异常条件,能解释手算与运行差异。

    练到的知识点点积 · 向量长度与零向量

  3. 作业 03原理问答

    为两份编码配置建立比较资格表,区分相同维度、相同模型、相同归一化与相同阈值这几种声明各需要什么证据。

    提交内容
    概念或合同说明、关系图与自造案例,明确事实、假设和适用条件。
    怎样算完成
    • 关键概念有明确边界,能用案例检验声明。
    • 未获证据支持的假设单独标注。

    练到的知识点相似度与概率区别 · 模型版本与编码兼容

综合应用

  1. 作业 04面试场景

    两组不同模型给相同文本对打出0.76和0.88的相似度。设计验证方案判断是否能共用阈值,并说明分数能够支持哪些业务结论。

    提交内容
    场景分析与决策记录,包含信息缺口、约束、备选方案和验证计划。
    怎样算完成
    • 方案回应给定约束,列出至少一种替代方案及代价。
    • 每项关键结论说明所需证据,不能核实的部分明确标注。

    练到的知识点余弦相似度 · 相似度与概率区别

  2. 作业 05编程实践

    为笔记向量记录增加编码模型、维度、版本与来源,写一个兼容性校验器,测试同维不同版本、旧文档新查询和缺失元数据。

    提交内容
    可复现的最小程序或实验材料、自写测试、环境说明与运行记录;题目允许纸面实验时提交完整案例表。
    怎样算完成
    • 提交物按说明可以重现;测试覆盖正常、边界与失败场景。
    • 运行证据与断言能核验声明的行为,注明模拟与真实执行范围。

    练到的知识点Embedding 表示 · 模型版本与编码兼容

  3. 作业 06排错实验

    用微型语料模拟索引升级,混入一个旧版本向量并观察检索结果;记录发现不兼容、重建、切换和回滚的证据。

    提交内容
    最小故障输入、复现步骤、观测记录、定位依据及回归用例。
    怎样算完成
    • 其他人可按记录复现故障,并区分现象与已验证原因。
    • 定位过程有观测依据,回归用例可以再次暴露原问题。

    练到的知识点维度与对应位置 · 余弦相似度 · 模型版本与编码兼容

  4. 作业 07编程实践

    编写离线检索结果审计器,输入文档版本、查询编码版本、相似度与人工相关标签;混入高分不相关和跨版本记录,检验哪些记录可用于阈值评价。

    提交内容
    可复现的小程序或题目允许的离线实验、自写测试、输入和运行记录。
    怎样算完成
    • 输入、环境和运行过程可重现,测试覆盖声明的重点条件。
    • 验证目标由合同确定,模拟结果不冒充真实服务证据。

    练到的知识点相似度与概率区别 · 模型版本与编码兼容

  5. 作业 08面试场景

    切换模型后平均相似度提高但用户投诉增多。提出一个区分分数尺度变化与实际质量变化的小样本调查,明确不能直接跨版本合并的统计量。

    提交内容
    场景评审记录,包含约束、备选方案、未决信息和验证计划。
    怎样算完成
    • 方案回应题目约束,取舍能对应到可核验的事实。
    • 给出失败或条件变化时的检查方式。

    练到的知识点相似度与概率区别 · 模型版本与编码兼容

进阶挑战 · 掌握前置后选做

  1. 作业 09设计评审

    在掌握标注与检索评测后,用带人工相关性标签的小样本比较阈值策略,报告样本范围和不能推广到其他模型的限制。

    提交内容
    设计评审文档或对照实验报告,包含前置条件、方案取舍、验收证据与尚未验证的范围。
    怎样算完成
    • 比较使用一致条件与事先声明的标准,记录失败与未知结果。
    • 说明方案的适用前提、未覆盖风险及后续验证方法。

    练到的知识点向量长度与零向量 · 相似度与概率区别 · Embedding 表示

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 10思维拓展 · 反例构造

    构造“维度相同且余弦值更大,回答就更可信”的反例案例,给出需要补充核查的编码与业务证据。

    提交内容
    具体反例、原主张的适用前提、复现或逻辑证明及结论边界。
    怎样算完成
    • 反例满足原主张所声称的前提,确实针对其结论。
    • 说明一个反例能推翻什么、不能进一步证明什么。

    练到的知识点相似度与概率区别 · 模型版本与编码兼容

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗