资讯中心

cocoindex 语义检索实战:从 BERT 论文精读到向量索引构建

📅 2026/10/25 19:14:45
cocoindex 语义检索实战:从 BERT 论文精读到向量索引构建
cocoindex 语义检索实战从 BERT 论文精读到向量索引构建【免费下载链接】cocoindexIncremental engine for long horizon agents Star if you like it!项目地址: https://gitcode.com/GitHub_Trending/co/cocoindex本篇技术指南以 cocoindex 仓库中text_embedding示例的语料——BERT 经典论文1810.04805v2.md为核心文本完整梳理 BERT 的模型架构、预训练任务Masked LM 与 Next Sentence Prediction、微调范式与消融实验结论并结合 text_embedding 示例 的 Rust/Python 管道讲解如何把这篇论文按 Markdown 分块、向量化并存入 Postgres/pgvector 做语义检索。读完你将同时掌握 BERT 的技术原理与一套可复制的论文语料 → 可搜索向量索引的增量管道实现。BERT 论文在 cocoindex 示例中的角色text_embedding示例Rust 版位于 examples/rust/text_embeddingPython 版位于 examples/text_embedding的输入语料是markdown_files/目录下的三份经典文档1810.04805v2.mdBERT 论文本文主体1706.03762v7.mdAttention Is All You Needrfc8259.mdJSON 规范管道的工作方式为遍历**/*.md文件 → 按 Markdown 语义递归分块 → 用all-MiniLM-L6-v2本地嵌入 → 写入 Postgres/pgvector。因此理解这篇论文正文也是在理解这套向量索引里被检索的知识本体。BERT 核心思想深度双向的预训练表示BERTBidirectional Encoder Representations from Transformers由 Google AI Language 团队提出针对此前语言表示模型的根本局限——标准语言模型是单向的。OpenAI GPT 采用从左到右的架构每个 token 在自注意力层中只能关注其左侧的 tokenELMo 则是独立训练的从左到右与从右到左两个 LM 的浅层拼接。两者都不是在所有层同时基于左右上下文联合建模的深度双向表示。BERT 的关键设计是用Masked Language ModelMLM预训练目标来解除单向性约束随机遮蔽一部分输入 token让模型仅依据上下文预测被遮蔽词的原始词表 id。MLM 灵感源自 Cloze 任务Taylor, 1953它使表示能够融合左右上下文从而预训练出深度双向的 Transformer。此外BERT 还引入Next Sentence PredictionNSP任务联合预训练文本对表示。论文的三大贡献可概括为证明双向预训练对语言表示的重要性——对比 GPT单向 LM与 ELMo浅层双向拼接证明预训练表示能显著减少对精心设计的任务专属架构的依赖BERT 是首个在大量句子级与 token 级任务上超越众多任务专属架构的基于微调的代表模型在 11 个 NLP 任务上刷新当时的最优水平。模型架构与两种规格BERT 的模型架构是基于 Vaswani 等人2017的多层双向 Transformer 编码器。论文用三个超参描述模型层数 L、隐藏维度 H、自注意力头数 A前馈网络维度固定为 4H。两种主要规格如下规格L层数H隐藏维度A注意力头数总参数前馈维度BERT_BASE1276812110M3072BERT_LARGE24102416340M4096BERT_BASE 特意选择了与 OpenAI GPT 相同的模型尺寸以便公平对比关键差异在于BERT 使用双向自注意力而 GPT 的 Transformer 使用受限自注意力每个 token 只能关注左侧上下文。论文同时指出双向 Transformer 在文献中常被称为Transformer 编码器而仅看左侧上下文的版本被称为Transformer 解码器。BERT 的另一个显著特征是跨任务统一架构预训练架构与下游微调架构几乎无差别除输出层外同一套预训练参数被用来初始化不同下游任务的模型。输入/输出表示BERT 的输入表示需要无歧义地表达单个句子与句子对如 ⟨Question, Answer⟩。其中句子可以是任意连续文本片段而非语言学意义上的句子序列指输入 BERT 的 token 序列可以是单句或打包的两句。使用 WordPiece 词表30,000 token每个序列的首个 token 固定为特殊分类 token[CLS]其最终隐藏状态 C ∈ R^H 用作分类任务的聚合序列表示句子对打包进单一序列用特殊 token[SEP]分隔并为每个 token 附加学习到的 segment embedding 以标识其属于句子 A 还是 B单个 token 的输入表示由 token embedding、segment embedding 与 position embedding 三者求和构成。预训练两个无监督任务Task #1Masked LMMLM标准条件语言模型只能从左到右或从右到左训练因为双向条件化会让每个词间接看到自己模型在多层级上下文中可以平凡地预测目标词。BERT 的做法是随机遮蔽一定比例的输入 token再预测被遮蔽 token。实验中每个序列随机遮蔽 15% 的 WordPiece token与去噪自编码器不同BERT 只预测被遮蔽的词而非重建整个输入。由于[MASK]token 在微调阶段不会出现纯遮蔽会造成预训练与微调之间的不匹配。为此训练数据生成器选出 15% 的 token 位置后按如下混合策略替换80% 概率替换为[MASK]tokenmy dog is hairy → my dog is [MASK]10% 概率替换为随机词my dog is hairy → my dog is apple10% 概率保持不变my dog is hairy → my dog is hairy目的是让表示偏向实际观察到的词随后用 T_i 以交叉熵损失预测原始 token。这一策略的优势在于Transformer 编码器不知道哪些词会被要求预测、哪些被随机词替换因此被迫对所有输入 token 保持分布式的上下文表示。由于随机替换只发生在 1.5% 的 token 上15% × 10%不会损害模型的语言理解能力。相比标准语言模型训练MLM 每批只对 15% 的 token 做预测收敛需要更多预训练步数。Task #2Next Sentence PredictionNSP问答QA与自然语言推理NLI等任务依赖对两个句子之间关系的理解这是语言建模无法直接捕获的。BERT 为此预训练一个二分类的 NSP 任务选择句子 A 与 B 构造预训练样本时50% 情况下 B 是 A 的真实后续句标注IsNext50% 情况下 B 是语料中的随机句子标注NotNext[CLS]的表示 C 用于 NSP 预测。论文报告最终模型在 NSP 上达到 97%–98% 的准确率并指出 C 在未经微调时并非有意义的句子表示因为它只按 NSP 目标训练。NSP 对 QA 和 NLI 任务非常有益。论文给出的两个 NSP 示例Input [CLS] the man went to [MASK] store [SEP] he bought a gallon [MASK] milk [SEP] Label IsNextInput [CLS] the man [MASK] to the store [SEP] penguin [MASK] are flight ##less birds [SEP] Label NotNext预训练数据与训练细节预训练语料为 BooksCorpus8 亿词与英文 Wikipedia25 亿词仅抽取文本段落忽略列表、表格与标题。论文强调必须使用文档级语料而非 Billion Word Benchmark 这类打乱的句子级语料以提取长连续序列。训练配置附录 A.2每个训练序列采样两段文本即两个句子通常比单句长组合长度 ≤ 512 token句子 A 用 A embedding句子 B 用 B embedding批量大小 256 序列256 × 512 128,000 token/批训练 1,000,000 步约等于在 33 亿词语料上跑 40 个 epoch优化器 Adam学习率 1e-4β1 0.9β2 0.999L2 权重衰减 0.01前 10,000 步学习率 warmup之后线性衰减所有层 dropout 概率 0.1激活函数用 gelu跟随 OpenAI GPT而非标准 relu训练损失为平均 MLM 似然与平均 NSP 似然之和BERT_BASE 在 4 个 Cloud TPUPod 配置共 16 个 TPU 芯片上训练BERT_LARGE 在 16 个 Cloud TPU64 芯片上训练每次预训练约 4 天由于注意力复杂度随序列长度呈二次增长90% 的训练步使用 128 序列长度其余 10% 使用 512 以学习位置嵌入。微调范式微调非常直接Transformer 的自注意力机制使 BERT 能建模单文本或文本对的下游任务只需替换适当的输入与输出。对于文本对任务BERT 用自注意力统一了独立编码 双向交叉注意力两个阶段——对拼接后的文本对做自注意力天然包含了两个句子间的双向交叉注意力。输入侧预训练中的句子 A/B 分别对应释义中的句子对、蕴含中的假设-前提对、问答中的问题-段落对、以及文本分类/序列标注中的退化 text-∅ 对。输出侧token 级任务序列标注、问答使用 token 表示喂入输出层分类任务蕴含、情感分析使用[CLS]表示 C 喂入输出层。与预训练相比微调相对廉价论文所有结果可在单个 Cloud TPU 上最多 1 小时复现如 SQuAD 模型约 30 分钟达到 Dev F1 91.0或在 GPU 上数小时。微调超参数附录 A.3微调时大部分超参与预训练一致dropout 始终为 0.1仅在批量大小、学习率与 epoch 数上不同。论文发现以下范围在全部任务上表现良好批量大小16、32学习率Adam5e-5、3e-5、2e-5epoch 数2、3、4大数据集10 万 标注样本对超参数选择远没有小数据集敏感。微调通常很快合理的做法是对上述参数做穷举搜索并在开发集上选择最优模型。实验11 个 NLP 任务GLUEGLUE 是多样的自然语言理解任务集合。微调时用[CLS]的最终隐藏向量 C 作为聚合表示唯一新增参数是分类层权重 W ∈ R^(K×H)K 为标签数以 log(softmax(C·W^T)) 计算标准分类损失。表 1 为 GLUE 测试集结果F1 用于 QQP 与 MRPCSpearman 相关系数用于 STS-B其余为准确率平均列排除有问题的 WNLISystemMNLI-(m/mm)QQPQNLISST-2CoLASTS-BMRPCRTEAverage训练样本数392k363k108k67k8.5k5.7k3.5k2.5k-Pre-OpenAI SOTA80.6/80.166.182.393.235.081.086.061.774.0BiLSTMELMoAttn76.4/76.164.879.890.436.073.384.956.871.0OpenAI GPT82.1/81.470.387.491.345.480.082.356.075.1BERT_BASE84.6/83.471.290.593.552.185.888.966.479.6BERT_LARGE86.7/85.972.192.794.960.586.589.370.182.1所有 GLUE 任务使用批量大小 32、微调 3 个 epoch从 5e-5、4e-3、3e-5、2e-5 中按 Dev 集选择最优学习率。BERT_LARGE 在小数据集上微调偶有不稳因此做多次随机重启相同预训练 checkpoint不同数据打乱与分类层初始化并选 Dev 最优。BERT_BASE 与 BERT_LARGE 相比此前最优系统分别获得 4.5% 与 7.0% 的平均准确率提升在最大的 GLUE 任务 MNLI 上获得 4.6% 的绝对提升。BERT_LARGE 在所有任务上显著优于 BERT_BASE尤其是训练数据极少的任务。SQuAD v1.1SQuAD v1.1 含 10 万个人工标注问答对。输入将问题与段落打包为单一序列问题用 A embedding段落用 B embedding。微调仅引入起始向量 S ∈ R^H 与结束向量 E ∈ R^H词 i 作为答案起点的概率为 softmax(S·T_i)终点同理候选片段 (i, j) 的得分为 S·T_i E·T_j取 j ≥ i 的最大得分片段作为预测。微调 3 个 epoch、学习率 5e-5、批量大小 32。表 2 的结果中BERT 最优系统先微调 TriviaQA 再做 SQuAD 数据增强在集成上超过当时榜首系统 1.5 F1、单模型 1.3 F1单模型 BERT 甚至超过当时的 top 集成系统的 F1SystemDev EMDev F1Test EMTest F1Human--82.391.2#1 Ensemble - nlnet--86.091.7#2 Ensemble - QANet--84.590.5BiDAFELMo (Single)-85.6-85.8R.M. Reader (Ensemble)81.287.982.388.5BERT_BASE (Single)80.888.5--BERT_LARGE (Single)84.190.9--BERT_LARGE (Ensemble)85.891.8--BERT_LARGE (Sgl.TriviaQA)84.291.185.191.8BERT_LARGE (Ens.TriviaQA)86.292.287.493.2SQuAD v2.0SQuAD 2.0 允许段落中不存在答案更贴近现实。BERT 的扩展方式很简洁把无答案问题视为答案起止都在[CLS]token 的片段将概率空间扩展到包含[CLS]位置预测时比较无答案片段的得分 s_null S·C E·C 与最佳非空片段得分 ŝ_ij当 ŝ_ij s_null τ 时预测非空答案τ 在 Dev 集上按最大化 F1 选取。未使用 TriviaQA 数据微调 2 个 epoch、学习率 5e-5、批量大小 48相比此前最优系统获得 5.1 F1 的提升SystemDev EMDev F1Test EMTest F1Human86.389.086.989.5#1 Single - MIR-MRC (F-Net)--74.878.0#2 Single - nlnet--74.277.1unet (Ensemble)--71.474.9SLQA (Single)--71.474.4BERT_LARGE (Single)78.781.980.083.1SWAGSWAG 数据集含 11.3 万句子对补全样本用于评估接地常识推理给定句子从四个选项中选出最合理的延续。微调时构造四个输入序列每个为给定句子A与一个可能延续B的拼接唯一新增参数是向量与[CLS]表示 C 的点积得分经 softmax 归一化。微调 3 个 epoch、学习率 2e-5、批量大小 16。BERT_LARGE 相比作者的 ESIMELMo 基线提升 27.1%比 OpenAI GPT 高 8.3%SystemDevTestESIMGloVe51.952.7ESIMELMo59.159.2OpenAI GPT-78.0BERT_BASE81.6-BERT_LARGE86.686.3消融研究什么在起作用预训练任务的效果论文用与 BERT_BASE 完全相同的预训练数据、微调方案与超参数评估两种预训练目标No NSP有 MLM、无 NSP 的双向模型与LTR No NSP标准从左到右 LM微调时同样施加左向约束以避免预训练/微调不匹配可比作使用更大数据集、BERT 输入表示与微调方案的 OpenAI GPT。表 5 显示移除 NSP 在 QNLI、MNLI 与 SQuAD 1.1 上显著损害性能LTR 模型在所有任务上都比 MLM 模型差在 MRPC 与 SQuAD 上大幅下降TasksMNLI-m (Acc)QNLI (Acc)MRPC (Acc)SST-2 (Acc)SQuAD (F1)BERT_BASE84.488.486.792.788.5No NSP83.984.986.592.687.9LTR No NSP82.184.377.592.177.8 BiLSTM82.184.175.791.684.9在 LTR 模型上加随机初始化的 BiLSTM 能显著改善 SQuAD但仍远差于预训练双向模型且对 GLUE 任务有害。论文也讨论了 ELMo 式的分别训练 LTR 与 RTL 模型再拼接方案一是成本翻倍二是对 QA 不直观RTL 模型无法让答案条件化在问题上三是严格弱于深度双向模型后者每一层都能同时使用左右上下文。模型规模的影响论文训练了不同层数、隐藏单元与注意力头数的 BERT 模型其余超参与流程相同。表 6 显示更大的模型在四个数据集上带来严格一致的准确率提升即使 MRPC 只有 3,600 个标注样本、且与预训练任务差异显著。对比Vaswani 等人最大的 Transformer 为 (L6, H1024, A16)、编码器 1 亿参数文献中最大的 Transformer 为 (L64, H512, A2)、2.35 亿参数而 BERT_BASE 有 1.1 亿、BERT_LARGE 有 3.4 亿参数#L#H#ALM (ppl)MNLI-mMRPCSST-23768125.8477.979.888.46768125.2480.682.290.7676834.6881.984.891.312768123.9984.486.792.9121024163.5485.786.993.3241024163.2386.687.893.7论文指出这是首次令人信服地证明只要模型被充分预训练将模型规模扩展到极端水平也能在小规模任务上带来大幅提升。此前 Peters 等人的工作对增大预训练 bi-LM 规模呈现混合结果Melamud 等人仅顺带提及隐藏维度从 200 增至 600 有帮助、增至 1000 无进一步提升——两者都是特征式方法论文推测当模型直接在下游任务上微调且仅随机初始化极少量附加参数时任务模型能受益于更大、更具表达力的预训练表示即便下游数据非常小。特征式方法与微调式方法的对比并非所有任务都能用 Transformer 编码器架构简单表达且预先计算训练数据的昂贵表示、再在其上跑廉价模型实验有巨大的计算收益。论文在 CoNLL-2003 NER 上比较两种方式特征式方法使用保留大小写的 WordPiece 模型提取一个或多个层的激活不微调任何参数作为输入喂给随机初始化的两层 768 维 BiLSTM 与分类层token 级分类器使用首个子 token 的表示。表 7 显示 BERT_LARGE 与当时最先进方法竞争力相当最佳特征式方法拼接预训练 Transformer 顶部四层的 token 表示仅比微调整个模型低 0.3 F1证明 BERT 对微调与特征式两种方式都有效SystemDev F1Test F1ELMo (Peters et al., 2018a)95.792.2CVT (Clark et al., 2018)-92.6CSE (Akbik et al., 2018)-93.1BERT_LARGE微调96.692.8BERT_BASE微调96.492.4Embeddings特征式91.0-Second-to-Last Hidden特征式95.6-Last Hidden特征式94.9-Weighted Sum Last Four Hidden特征式95.9-Concat Last Four Hidden特征式96.1-Weighted Sum All 12 Layers特征式95.5-训练步数与遮蔽策略的消融附录 C.1 回答了两个问题其一BERT 是否真的需要如此大量的预训练128,000 词/批 × 1,000,000 步答案是需要——BERT_BASE 用 1M 步相比 500k 步在 MNLI 上多获得约 1.0% 的准确率。其二MLM 预训练是否因每批只预测 15% 的词而比 LTR 收敛更慢答案是略慢但从绝对准确率看 MLM 几乎立刻开始超越 LTR。附录 C.2 对遮蔽策略做了消融MASK 表示替换为[MASK]SAME 表示保持不变RND 表示替换为随机 token左半部分为各策略概率右半部分为 Dev 结果特征式方法拼接 BERT 最后 4 层即 5.3 节的最优做法。表 8 显示微调对不同的遮蔽策略出人意料地稳健但只用 MASK 策略在特征式 NER 上有问题而只用 RND 策略明显更差Masking RatesDev Set ResultsMASKSAMERNDMNLI Fine-tuneNER Fine-tuneNER Feature-based80%10%10%84.295.494.9100%0%0%84.394.994.080%0%20%84.195.294.680%20%0%84.495.294.70%20%80%83.794.894.60%0%100%83.694.994.6与 ELMo、OpenAI GPT 的系统性对比三类模型的本质差异BERT 是双向 TransformerOpenAI GPT 是左到右 TransformerELMo 是独立训练的左右 LSTM 的拼接以生成特征。三者中只有 BERT 的表示在所有层同时基于左右上下文联合条件化且 BERT 与 GPT 是微调式方法ELMo 是特征式方法。BERT 在设计中刻意尽量贴近 GPT 以做最小化对比但仍有几处训练差异GPT 只在 BooksCorpus8 亿词上训练BERT 额外使用 Wikipedia25 亿词GPT 的[SEP]与[CLS]只在微调时引入BERT 在预训练期间就学习[SEP]、[CLS]与句子 A/B 嵌入GPT 以 32,000 词/批训练 1M 步BERT 以 128,000 词/批训练 1M 步GPT 所有微调统一用 5e-5 学习率BERT 按任务在开发集上选择最优学习率。5.1 节的消融证明绝大多数改进来自两个预训练任务及其带来的双向性。从论文精读到可检索的向量索引理解了 BERT 论文的内容之后回到 cocoindex 场景这篇论文连同 Attention 论文与 RFC 文档正是text_embedding示例的检索语料。Rust 示例的 main.rs 中default_sourcedir()指向markdown_files/索引流程为遍历walk_items(sourcedir, [**/*.md])递归收集 Markdown 文件Python 版用localfs.walk_dirPatternFilePathMatcher(included_patterns[**/*.md])分块RecursiveSplitter::new()配合RecursiveChunkConfig其中chunk_size: 2000、chunk_overlap: 500、language: Some(markdown)。配置字段定义见 rust/sdk/cocoindex/src/ops/text.rschunk_size为目标块大小字节、min_chunk_size默认取 chunk_size 的一半、chunk_overlap为相邻块重叠、language指定语法感知切分的语言Python 版等价写法为_splitter.split(text, chunk_size2000, chunk_overlap500, languagemarkdown)嵌入SentenceTransformerEmbedder加载sentence-transformers/all-MiniLM-L6-v2384 维embed_batch批量生成向量并复用同一模型做查询向量保证索引与检索一致入库postgres::mount_table_target在coco_examplesschema 下建doc_embeddings表embedding列类型为vector(384)并通过declare_vector_index声明 HNSW 向量索引process_file标注#[cocoindex::function(memo)]未变化的文件被 memo 跳过被删除/编辑文件的块由托管目标自动 reconcile 删除孤儿行检索query子命令将查询文本嵌入后执行SELECT filename, text, embedding $1::vector AS distance ... ORDER BY distance ASC LIMIT $2用 pgvector 余弦距离取 TOP_K5并按1.0 - distance打印相似度得分。因此What is bidirectional self-attention? 这类问题即使与原文不共享关键词也能通过语义向量命中 BERT 论文中关于 MLM 双向性的段落——这正是本文所讲解的 BERT 概念在工程管道中的落地形态。结论BERT 论文的核心贡献是把语言模型的预训练从深度单向架构推广到深度双向架构通过 MLM 解除单向性限制、以 NSP 捕获句子关系使同一套预训练模型仅靠一个额外输出层就能在句子级与 token 级任务上全面达到当时的顶尖水平。对 cocoindex 用户而言这篇论文不仅是 NLP 发展史上的里程碑文献也是text_embedding示例中可直接检索的高质量知识语料——从分块参数到 pgvector 索引整个语义检索管道在仓库中均有完整可运行的参考实现。【免费下载链接】cocoindexIncremental engine for long horizon agents Star if you like it!项目地址: https://gitcode.com/GitHub_Trending/co/cocoindex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案