cocoindex 语义检索实战:从《Attention Is All You Need》到 Transformer 架构与 LanceDB 向量索引
cocoindex 语义检索实战从《Attention Is All You Need》到 Transformer 架构与 LanceDB 向量索引【免费下载链接】cocoindexIncremental engine for long horizon agents Star if you like it!项目地址: https://gitcode.com/GitHub_Trending/co/cocoindex《Attention Is All You Need》是 2017 年提出 Transformer 架构的开创性论文也是本仓库text_embedding_lancedb示例中随附的markdown_files/样例文档之一用于验证Markdown 分块 → 本地向量化 → 增量写入 LanceDB的完整语义检索链路。阅读本文你将掌握 Transformer 的核心组件缩放点积注意力、多头注意力、位置编码的数学原理与设计动机并看到这篇论文如何作为真实语料在 cocoindex 中以增量、免服务器的方式被切分、嵌入并检索。一、论文背景为什么需要抛弃循环与卷积论文开篇指出当时主流的序列转换模型sequence transduction models都基于复杂的循环神经网络RNN或卷积神经网络CNN由编码器encoder和解码器decoder构成最优秀的模型还要在编码器与解码器之间接入注意力机制。循环模型按符号位置逐步分解计算每一步的隐藏状态 hₜ 依赖于前一隐藏状态 hₜ₋₁ 与当前位置输入。这种内在的顺序性导致训练样本内无法并行化序列越长内存约束越明显批处理能力越差。虽然因子分解技巧与条件计算能改善效率但顺序计算的本质约束依然存在。注意力机制已被广泛用于建模输入/输出序列中任意距离的依赖关系但在绝大多数模型中它只是循环网络的附属品。论文提出Transformer——完全基于注意力机制、彻底摒弃循环与卷积的架构从而获得显著更高的并行度并能在 8 块 P100 GPU 上仅训练 12 小时就刷新翻译质量纪录。这也是后续所有基于 Attention 的大语言模型如 GPT、BERT的架构源头。二、模型架构总览编码器–解码器与残差子层Transformer 沿用标准的编码器–解码器结构编码器把输入符号序列 (x₁,…,xₙ) 映射为连续表示 z(z₁,…,zₙ)解码器据此自回归地逐个生成输出符号 (y₁,…,yₘ)——生成下一个符号时把此前已生成的符号作为额外输入。2.1 编码器堆栈编码器由N6 个相同层堆叠而成每层包含两个子层多头自注意力子层multi-head self-attention逐位置的按位全连接前馈网络position-wise feed-forward network每个子层外都套一层残差连接加层归一化LayerNorm输出 LayerNorm(x Sublayer(x))为便于残差相加模型中所有子层以及嵌入层都输出维度 d_model 512。2.2 解码器堆栈解码器同样由 N6 个相同层构成在编码器两个子层之外额外插入第三个子层对编码器堆栈输出做多头注意力即 encoder-decoder attention。解码器内的自注意力子层经过**掩码masking**处理禁止位置 attend 到其后续位置再加上输出嵌入右移一位的设计共同保证位置 i 的预测只能依赖位置小于 i 的已知输出从而保留自回归性质。三、注意力机制三种用法的数学内核注意力函数可描述为把查询query与一组键值对key-value pairs映射到输出输出是值的加权和权重由查询与对应键的兼容性函数计算得出。3.1 缩放点积注意力Scaled Dot-Product Attention查询与键的维度为 d_k值的维度为 d_v。实际计算时查询、键、值分别打包为矩阵 Q、K、V输出为Attention(Q, K, V) softmax(QKᵀ / √d_k) · V与加性注意力相比点积注意力可用高度优化的矩阵乘法实现更快、更省显存。论文还解释了缩放因子 √d_k 的由来当 d_k 较大时点积的模长随之增大若 q、k 各分量均值为 0、方差为 1则点积方差为 d_k会把 softmax 推向梯度极小区域除以 √d_k 恰好抵消这一效应。3.2 多头注意力Multi-Head Attention与其用 d_model 维的键、值、查询做单一注意力不如把它们线性投影 h 次每次使用不同的可学习投影矩阵投影到 d_k、d_k、d_v 维并行执行 h 个注意力将输出拼接后再投影回最终值MultiHead(Q, K, V) Concat(head₁, …, head_h) · Wᴼ headᵢ Attention(QWᵢQ, KWᵢK, VWᵢV)其中 WᵢQ ∈ R^(d_model×d_k)、WᵢK ∈ R^(d_model×d_k)、WᵢV ∈ R^(d_model×d_v)、Wᴼ ∈ R^(h·d_v×d_model)。多头机制让模型能在不同表示子空间、不同位置上联合注意论文采用 h8 个头每个头 d_k d_v d_model/h 64。由于每个头的维度降低总计算成本与单头全维度注意力相当。3.3 注意力的三种用法Transformer 以三种方式使用多头注意力编码器–解码器注意力查询来自上一层解码器键与值来自编码器输出使解码器每个位置都能 attend 输入序列的所有位置编码器自注意力键、值、查询都来自编码器上一层输出每个位置可 attend 前一层所有位置解码器自注意力每个位置可 attend 解码器内到自身为止的所有位置为保持自回归性将非法连接的 softmax 输入置为 −∞即掩码。四、位置级前馈网络与嵌入设计4.1 按位前馈网络每个编码/解码层都包含一个逐位置独立应用、且各位置共享参数的全连接前馈网络由两次线性变换夹一次 ReLU 激活组成FFN(x) max(0, xW₁ b₁)W₂ b₂等价于两次核大小为 1 的卷积。输入输出维度为 d_model 512内层维度 d_ff 2048参数随层不同而不同。4.2 嵌入与 Softmax与其他序列转换模型类似使用可学习嵌入把输入/输出 token 转为 d_model 维向量用可学习的线性变换加 softmax 把解码器输出转为下一个 token 的概率分布。论文将两个嵌入层与 pre-softmax 线性变换共享同一权重矩阵并在嵌入层中把权重乘以 √d_model。4.3 位置编码模型不含循环与卷积必须注入序列的位置信息。做法是把与嵌入同维d_model的位置编码加到编码器与解码器堆栈底部的输入嵌入上。论文选用不同频率的正弦/余弦函数PE(pos, 2i) sin(pos / 10000^(2i/d_model)) PE(pos, 2i1) cos(pos / 10000^(2i/d_model))波长从 2π 到 10000·2π 呈几何级数。正弦版本的优点在于对任意固定偏移 kPE(posk) 可表示为 PE(pos) 的线性函数模型易于学习按相对位置注意同时可能外推到训练中未见过的更长序列。消融实验Table 3 行 E表明它与可学习位置嵌入的效果几乎一致。五、为什么用自注意力复杂度与路径长度论文从三个维度对比自注意力、循环层与卷积层见表 1 归纳层类型每层复杂度顺序操作数最大路径长度自注意力O(n²·d)O(1)O(1)循环O(n·d²)O(n)O(n)卷积O(k·n·d²)O(1)O(log_k(n))受限自注意力O(r·n·d)O(1)O(n/r)关键结论路径长度自注意力用常数级顺序操作连接所有位置而循环层需要 O(n) 顺序操作信号传播路径越短长距离依赖越容易学习复杂度当序列长度 n 小于表示维度 d机器翻译中句子表示常见的情形时自注意力比循环层更快处理超长序列时可把自注意力限制到邻域 r路径长度变为 O(n/r)卷积的代价核宽 kn 的卷积层要堆叠 O(n/k) 层才能连通所有位置分隔卷积虽可把复杂度降到 O(k·n·dn·d²)但即便 kn其复杂度也等于自注意力 按位前馈的组合——这正是论文采用的方案可解释性自注意力还有额外好处——注意力头会分化出不同职责部分头表现出与句法、语义结构相关的行为论文附录 Fig. 3–5 展示了长距离依赖追踪与指代消解的例子。六、训练机制数据、硬件、优化器与正则化6.1 训练数据与批处理英德WMT 2014 英德数据集约 450 万句对字节对编码BPE共享词表约 37000 token英法更大的 WMT 2014 英法数据集3600 万句32000 word-piece 词表句对按近似序列长度分批每批约含 25000 源 token 与 25000 目标 token。6.2 硬件与训练进度单机 8 块 NVIDIA P100 GPU。base 模型每步约 0.4 秒共训练 100,000 步约 12 小时big 模型每步 1.0 秒训练 300,000 步约 3.5 天。6.3 优化器使用 Adam 优化器β₁0.9、β₂0.98、ε10⁻⁹学习率按公式动态调整lrate d_model^(-0.5) · min(step_num^(-0.5), step_num · warmup_steps^(-1.5))即前 warmup_steps4000步线性上升之后按步数平方根的倒数衰减。6.4 正则化三种正则化手段残差 Dropout每个子层输出在加入子层输入并归一化之前应用 dropout编码/解码堆栈中嵌入 位置编码之和也做 dropoutbase 模型 P_drop 0.1标签平滑Label Smoothingϵ_ls 0.1虽然会略微伤害困惑度模型变得更不确定但能提升准确率与 BLEU。七、实验结果翻译、消融与语法解析7.1 机器翻译WMT 2014 英德big 模型 BLEU 28.4比此前最优含集成模型高 2.0 BLEUbase 模型也以远低于竞品的训练成本超越所有已发表模型与集成WMT 2014 英法big 模型 BLEU 41.8训练成本不足此前最优单模型的 1/4英法版本 dropout 用 0.1 而非 0.3推理细节base 平均最后 5 个 checkpoint每 10 分钟写一次big 平均最后 20 个束搜索 beam size4、长度惩罚 α0.6最大输出长度设为输入长度50。7.2 消融实验Table 3newstest2013 开发集行 A头数h8d_kd_v64最优单头比最优差 0.9 BLEU头数过多质量也下降行 B键维度 d_k缩小 d_k 损害质量说明确定兼容性并非易事行 C/D规模与正则更大的模型更好dropout 对防过拟合非常有效行 E位置编码可学习位置嵌入与正弦版结果几乎一致big 模型配置N6、d_model1024、d_ff4096、h16、P_drop0.3、训练 300K 步参数 2.13 亿。7.3 英语成分句法分析用 4 层、d_model1024 的 Transformer 在 Penn Treebank WSJ 约 4 万句上训练WSJ Section 23 的 F1 达到 91.3仅 WSJ与 92.7半监督约 1700 万句。结果表明即便没有针对任务调参Transformer 也超越了除 RNN Grammar 外所有此前模型甚至在仅 4 万句训练数据下就胜过 Berkeley Parser。八、仓库落地让这篇论文成为可检索的向量索引在 cocoindex 仓库中这篇论文被放在 examples/text_embedding_lancedb/markdown_files/1706.03762v7.md作为text_embedding_lancedb示例的测试语料之一同目录还有1810.04805v2.md、rfc8259.md。其完整流水线定义在 examples/text_embedding_lancedb/main.py实现读 Markdown → 递归分块 → 本地嵌入 → 增量写入 LanceDB8.1 连接与生命周期免服务器的 LanceDBcoco.lifespan async def coco_lifespan(builder: coco.EnvironmentBuilder) - AsyncIterator[None]: conn await lancedb.connect_async(LANCEDB_URI) # ./lancedb_data磁盘上的目录 builder.provide(LANCE_DB, conn) builder.provide(EMBEDDER, SentenceTransformerEmbedder(EMBED_MODEL)) yieldLANCEDB_URI ./lancedb_data只是磁盘路径无需启动任何数据库服务connect_async是lancedb.connect_async()的薄封装见 python/cocoindex/connectors/lancedb/_target.py 中connect_async的实现。嵌入模型默认使用sentence-transformers/all-MiniLM-L6-v2本地运行、无需 API key。8.2 主流程挂载表目标、遍历 Markdown、增量映射coco.fn async def app_main(sourcedir: pathlib.Path) - None: target_table await lancedb.mount_table_target( LANCE_DB, table_nameTABLE_NAME, table_schemaawait lancedb.TableSchema.from_class( DocEmbedding, primary_key[id] ), ) files localfs.walk_dir(sourcedir, recursiveTrue, path_matcherPatternFilePathMatcher(included_patterns[**/*.md]), liveTrue) await coco.mount_each(process_file, files.items(), target_table)mount_table_target是table_target()coco.mount_target()的语法糖见 python/cocoindex/connectors/lancedb/_target.py 中mount_table_target创建并托管表、基于主键做幂等 upsert、文件消失时清理孤儿行TableSchema.from_class(DocEmbedding, primary_key[id])从 dataclass 自动推导列与 PyArrow 类型映射Python 标量类型按_LEAF_TYPE_MAPPINGS映射int→int64、float→float64、str→stringAnnotated[NDArray, EMBEDDER]字段通过嵌入器的向量 schema 映射为定长 float32 向量列pa.list_(pa.float32(), list_sizedim)PatternFilePathMatcher(included_patterns[**/*.md])只收录 MarkdownliveTrue开启源监听配合cocoindex update -L main即可持续跟随文件变化。8.3 分块与嵌入论文全文如何变成向量coco.fn(memoTrue) async def process_file(file: FileLike, table: lancedb.TableTarget[DocEmbedding]) - None: text await file.read_text() chunks _splitter.split( text, chunk_size2000, chunk_overlap500, languagemarkdown ) id_gen IdGenerator() await coco.map(process_chunk, chunks, file.file_path.path, id_gen, table)RecursiveSplitterpython/cocoindex/ops/text.py按chunk_size2000字节、chunk_overlap500、languagemarkdown递归切分相邻块保留重叠以缓解语义截断coco.fn(memoTrue)使未变化的文件直接跳过每块的行id由id_gen.next_id(chunk.text)基于块文本派生因此只有内容真正变化的块才会被重新嵌入与 upsertSentenceTransformerEmbedderpython/cocoindex/ops/sentence_transformers.py内部以coco.fn.as_async(batchingTrue, runnercoco.GPU, max_batch_size64)批量编码默认归一化为单位向量以便余弦相似度单文本调用会被引擎自动聚合成批同时实现__coco_vector_schema__()向表 schema 提供维度信息。process_chunk把文件名、块起止字符偏移、块文本与嵌入向量声明为一行。8.4 行级写入的底层保障在 python/cocoindex/connectors/lancedb/_target.py 的_RowHandler中upsert 通过table.merge_insert(主键).when_matched_update_all().when_not_matched_insert_all()实现删除按主键条件执行table.delete(...)写入行数累积到阈值后还会依据table.stats()决策性地执行table.optimize()处理小文件碎片、删除文件与未索引行尾相关行为由 python/tests/connectors/test_lancedb_target.py 中的test_row_handler_optimizes_for_small_fragments、test_row_handler_optimizes_for_deletion_files等用例验证。8.5 查询同一模型、同一连接、余弦打分async def query_once(conn, embedder, query_text, *, top_kTOP_K) - None: query_vec await embedder.embed(query_text) table await conn.open_table(TABLE_NAME) search await table.search(query_vec, vector_column_nameembedding) results await search.limit(top_k).to_list() for r in results: score 1.0 - r[_distance] print(f[{score:.3f}] {r[filename]})查询端复用同一个SentenceTransformerEmbedder与连接保证索引/搜索表示一致分数由 LanceDB 返回的距离换算为相似度。例如执行python main.py what is self-attention?即可在论文分块中按语义召回最相近的段落——即便查询词与原文不共享字面词。九、运行示例示例依赖见 examples/text_embedding_lancedb/pyproject.tomlcocoindex[sentence_transformers,lancedb]1.0.7Python ≥3.11。一键建索引与实时监听pip install -e . cocoindex update main # 一次性追赶扫描、同步、退出 cocoindex update -L main # live 模式持续监听文件变化./lancedb_data/目录首次运行自动创建想重置只需删除该目录后重新运行。也可在app_main中自行补充target_table.declare_vector_index(columnembedding, metriccosine)与declare_fts_index(columntext)来声明向量/全文索引两者都由表目标的 attachment 状态机托管见_VectorIndexHandler与_FtsIndexHandler。结语《Attention Is All You Need》以纯注意力、无循环、无卷积的简洁设计奠定了现代深度学习架构的基础而在 cocoindex 中这篇经典论文本身又成为端到端语义检索的鲜活语料——text_embedding_lancedb示例证明从 Transformer 论文的分块嵌入到 LanceDB 的增量 upsert、孤儿清理与索引托管再到用同一嵌入模型的语义查询整个文档 → 向量 → 检索链路可以完全本地化、增量化和免服务器地运行在纯异步 Python 之上。【免费下载链接】cocoindexIncremental engine for long horizon agents Star if you like it!项目地址: https://gitcode.com/GitHub_Trending/co/cocoindex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考