原件、证据身份与引用位置
本课构建一个能检索图文资料、引用原件回答并在缺证时拒答的完整小系统。读者会基础Python、向量和概率;矩阵运算、softmax与交叉熵将在使用前展开。项目使用自己编写的七份工坊文档和二十四张真实PNG几何图,并真实训练小型图文编码器。这里没有远程语言模型或预训练CLIP,所以“生成”阶段是公开的抽取与模板,不假装获得任意语言理解。
第一条合同是原件必须存在。文档先作为UTF-8 Markdown文件落盘,再由导入器重新读取;图片是三十二乘三十二RGB PNG,导入器从磁盘解码像素。文件扩展名、尺寸、通道、相对路径和SHA256写入清单。不能预先手写“红色小圆形”证据表,再让系统只搜索这张表,却称为自动多模态导入。训练caption可以由合成定义提供,但入库图像属性必须重新由像素测量。
将证据单位定义为一个可定位、可验证且有有限范围的对象。文档单位是主题下的一条源字段行,图片单位是实际图像及前景包围框。每个单位保存稳定ID、原件相对路径、原件散列、模态、可检索文本和来源位置。ID表示档案中的身份,散列表示当前内容版本,两者不能互相替代。改文件名可以保持字节内容,改一句文字也可以保留原名;引用必须同时说明身份与版本。
项目文档采用明确的主题、字段、值格式,例如主题是借阅台,开放时间这一行给出实际时间文字。导入器逐行切分冒号,记录字段与值,主题从首行继承。这个格式是原创资料的结构,不是对自然散文的通用信息抽取模型。回答阶段读取的值确实来自原文件行,而不是代码里额外保存的答案表。自然PDF、扫描件、表格跨页和OCR错误在本项目没有实现,不得从几份干净文本推出其处理能力。
位置采用Unicode码点偏移,起点包含、终点不包含,同时保存从一开始的行号。Python字符串索引按码点而非UTF-8字节,中文常用字通常占三个UTF-8字节,所以两套偏移不能混写。换行算一个字符,读取时也要固定换行处理。引用校验先比对原件散列,再截取保存的区间,要求它与“字段、冒号、值”的原始行一致;这让错误偏移能够被独立发现。
算例一:中文行的字符偏移与字节偏移
设文本第一行是“主题:借阅台”再换行,其中六个可见字符加一个换行,共七个码点;第二行从偏移七开始。若第二行是“开放时间:周三。”再换行,八个可见字符加换行,共九个码点,所以区间为左闭右开七到十六。截取该区间能取回完整第二行。若用UTF-8字节位置,前六个中文字和标点各三字节再加换行,起点应为十九,而不是七;混用会指向乱码或错误文字。
这项手算应与实际解释器复核,因为可见字符里可能存在组合字符或不同标点。我们不把“用户看起来一个字”当所有Unicode字符串的计数规则。项目使用固定原创常用中文和全角冒号,但声明offset_unit为Unicode code points。未来若页面把空白归一化、合并段落或转换标点,必须保留从规范文本到原件的映射,不能对处理后文本的偏移直接声称原始PDF页内坐标。
算例二:散列相同与引用正确是两个条件
假设原件包含开放时间与归还要求两行,索引散列确实匹配当前文件,却误把归还要求的偏移附在开放时间答案上。散列校验通过,但区间内容校验失败,应拒绝回答或重新入库。反过来,原文件只改了另一行,旧区间恰好还能截出相同开放时间,内容比对可能通过,散列仍显示版本改变。两条件同时满足才能确认引用来自索引承诺的版本和位置,不能任选一个好看的检查。
切分颗粒度决定检索和引用的精度。整份文档作为一块通常含更多主题噪声,检索命中后也难定位具体答案;太碎又可能把主题与数值拆开。项目每个字段行继承文档主题形成检索文本,原件span仍只指该行,所以“主题来自首行”也是一项公开上下文依赖。若把主题换错,原行值仍可能真实,却回答了错误对象。后续回答器要求查询同时包含正确主题和字段,避免只凭相似时间词取答案。
图片证据的可回查范围不同。前景包围框由像素阈值自动得出,测得颜色均值、面积、宽高和填充率再生成短描述。引用给原PNG路径、散列及像素框,校验时重新跑同一测量规则。它能够支持受控域内“测得红色、大小、形状”的说法,不能支持材质、重量、用途或真实对象身份。学习编码器给相似向量只负责检索排序,更不会让这些未观测属性突然变成事实。
训练监督和答案证据必须分开。二十个图文对的caption用来训练;四个颜色、形状、大小组合被留出,所有基本词仍在训练出现。评价标签保存为单独文件,只在评价器读取。入库器通过图片目录遍历,不读取caption清单或评价查询表;测试会删除这两个表,要求索引条目完全相同。这样既有真实学习,又防止把训练标签偷渡成自动视觉证明。
原创文档当然由人写出事实,这与人工把图片结论塞给检索器不同。这里文档本身就是来源,系统的职责是忠实读取并给位置;它不保证来源陈述在现实世界一定为真。例如两份测试台记录给出相冲突开放时间,系统应暴露冲突,而不能因为二者都“来自文档”就同时当唯一答案。证据支持只保证在指定来源中可找到陈述,事实可信度还需要来源权威、时间与外部核验。
导入错误要分类保存。无法解码、尺寸模式不符、没有足够前景、缺少主题行、字段分隔不合法,都应报错或隔离,不默默产生空证据。当前实现严格接受小RGB图和已声明字段文档,未实现任意格式回退。这种窄合同使每步可检查;扩展格式时需要新的解析器、位置映射及失败样本,不把“能打开一个文件”当全部内容被正确理解。
路径也属于证据完整性。索引只允许根目录内相对路径,验证时解析实际路径并检查它仍处于输入根目录,拒绝向上跳到其他文件。散列只能验证指定内容,不能自动提供访问权限或来源权威。项目是本地离线教学原件,没有部署多用户权限;若接入真实知识库,检索前后都需保留权限过滤,不能先把无权资料放上下文再希望回答器不引用。
最终数据档案应能回答“系统实际看了什么”。原件清单、导入后的单位、训练清单和评价清单各有明确用途;代码中哪个函数读取哪类文件可直接追踪。下一章从这些真实单位建立词法索引,再到可学习向量,不跳过来源层。强检索器若拿错版本或错span,回答仍不可信;反之,来源正确也不保证相关性,二者必须分别验证。
text='主题:借阅台\n开放时间:周三。\n'
lines=text.splitlines(keepends=True)
start=len(lines[0]);end=start+len(lines[1])
assert start==7 and end==16
assert len(lines[0].encode('utf-8'))==19
print(start,end,text[start:end])TRY IT YOURSELF
练习一:ID与版本
同名文档改了开放时间,ID保留但SHA256改变,旧引用应该继续使用吗?若只改文件名而字节不变,又需更新什么?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习二:引用颗粒度
为什么每行字段继承主题可以改善检索,又会带来额外依赖?要怎样验证回答没有借到另一个主题的同名字段?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习三:视觉标签泄漏
入库器读取generated-caption.json直接作为每张图片的描述,训练与评价都非常准确,为什么仍不证明图像理解?给出删除文件测试。