尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从分词到向量化:大语言模型核心预处理流程的Node.js实践

从分词到向量化:大语言模型核心预处理流程的Node.js实践 1. 项目概述为什么我们要关心“分词”和“向量化”如果你最近在折腾大语言模型不管是想自己部署一个本地模型玩玩还是想基于API开发个智能应用大概率会频繁遇到两个词Tokenization分词/令牌化和Embedding嵌入/向量化。它们听起来很技术但其实是所有大模型理解人类语言的基石。简单来说模型没法直接读懂“你好世界”这串字符它需要先把这句话“切”成模型能认识的小块Tokenization再把这些小块转换成模型能“计算”的数字Embedding。这个过程就像你要教一个完全不懂中文的外星人读古诗。你不能直接把《静夜思》的全文塞给它它看不懂。你得先教它认识“床”、“前”、“明”、“月”、“光”这些最基本的字或词这就是分词。然后光认识字还不够你还需要告诉它“月亮”和“明镜”、“思乡”和“忧愁”在意思上是相近的这种“意思上的远近”你需要用一套复杂的数学坐标来表示这就是向量化。模型最终处理和生成的就是这一串串的数字坐标。所以搞懂从Tokenization到Embedding的完整流水线绝不是纸上谈兵。它能帮你精准控制成本几乎所有按Token收费的API如OpenAI其计费基础就是分词后的Token数量。理解分词规则你才能准确预估调用开销甚至优化你的提示词Prompt来省钱。高效调试模型当模型输出一些匪夷所思的结果时问题可能出在分词阶段。一个生僻词被切分成奇怪的片段会导致模型无法正确理解其含义。构建高级应用如果你想做语义搜索、文本分类、聚类分析核心就是获取高质量的文本向量Embedding。自己动手实现这个流程能让你更自由地定制和优化。深入理解模型原理这是打开大模型黑盒的第一把钥匙让你从“魔法调用者”转变为“原理明白人”。本文将用Node.js作为实践工具带你把这两个核心环节彻底拆解清楚。选择Node.js是因为它生态丰富、上手快非常适合快速构建原型和深入理解算法而不是仅仅停留在理论层面。我们会从一段简单的文本开始亲手实现一个简化的分词器然后探索如何将分词结果转化为有意义的向量最后串联成完整的流程。你会发现这背后的思想远比代码本身更精彩。2. 核心环节一Tokenization分词/令牌化深度解析2.1 分词的底层逻辑与常见策略为什么非要分词因为计算机包括神经网络最擅长处理的是结构化的、固定长度的数字输入。自然语言是高度非结构化的变长序列。分词的核心目标就是将连续、复杂的字符序列切割成离散的、模型词汇表Vocabulary中存在的子单元。主流的分词策略主要有三种词级别分词最简单直观按空格或标点将文本切分成独立的单词。例如“I love coding” - [“I”, “love”, “coding”]。优点易于理解每个Token携带丰富的语义信息。缺点词汇表会极其庞大需要覆盖所有单词的各种形式无法处理未登录词OOV对于中文、日文等没有空格分隔的语言基本无效。字符级别分词将文本切分成单个字符。例如“hello” - [“h”, “e”, “l”, “l”, “o”]。优点词汇表极小英文字母符号不过几百个彻底解决OOV问题。缺点每个Token的语义信息非常稀薄模型需要从零学习字符组合成词的规律训练效率低序列长度极大。子词级别分词这是当今大模型如GPT系列、BERT、T5的绝对主流。它介于词和字符之间旨在找到一种平衡。其核心思想是将频繁出现的连续字符序列作为一个Token而不常见的词则被拆分成更小的、可重用的子词单元。例如“unhappily” 可能被拆成 [“un”, “happ”, “ily”]。其中 “un” 表示否定“happ” 是 “happy” 的词干“ily” 是副词后缀。这样“happ” 这个Token可以在 “happy”, “happiness”, “happily” 中复用极大地压缩了词汇表并让模型能推理出未知词的语义。目前最流行的子词分词算法是Byte-Pair Encoding (BPE)及其变种如WordPiece, SentencePiece。BPE的原理是从一个基础字符词汇表开始统计语料中相邻符号对的频率将最高频的符号对合并成一个新的符号不断迭代直到达到预设的词汇表大小。2.2 用Node.js实现一个简易BPE分词器理论说再多不如动手。我们来用Node.js实现一个极度简化但核心逻辑完整的BPE分词器。我们会使用一个微型语料库来演示。// 1. 准备初始词汇表和语料 function initializeVocab(text) { // 将文本转换成字符数组并添加特殊的结束符 “/w” 来标记词尾 const words text.toLowerCase().split(/\s/).map(word word.split().join( ) /w); // 初始词汇表是所有基本字符加上结束符 const vocab new Set(); words.forEach(word { word.split( ).forEach(char vocab.add(char)); }); // 统计初始的词频以空格分隔的符号序列为单位 const frequencies {}; words.forEach(word { const symbols word.split( ); for (let i 0; i symbols.length; i) { const token symbols[i]; frequencies[token] (frequencies[token] || 0) 1; } // 同时统计相邻符号对的出现次数这是BPE合并的基础 for (let i 0; i symbols.length - 1; i) { const pair symbols[i] symbols[i 1]; frequencies[pair] (frequencies[pair] || 0) 1; } }); return { words, vocab: Array.from(vocab), frequencies }; } // 2. 核心合并函数 function getMostFrequentPair(frequencies, vocab) { let maxFreq 0; let bestPair null; // 遍历频率字典找出当前最高频的、且可以合并的符号对 for (const [pair, freq] of Object.entries(frequencies)) { const symbols pair.split( ); if (symbols.length ! 2) continue; // 确保是配对 // 检查这两个符号是否都在当前词汇表中即是否已经是基础单元 if (vocab.includes(symbols[0]) vocab.includes(symbols[1])) { if (freq maxFreq) { maxFreq freq; bestPair pair; } } } return bestPair ? { pair: bestPair, freq: maxFreq } : null; } function mergePair(pair, words, frequencies, vocab) { const [a, b] pair.split( ); const newToken a b; // 例如将 “l” 和 “o” 合并成 “lo” // 更新词汇表 vocab.push(newToken); // 更新words中的符号序列将所有相邻的 (a, b) 替换为 newToken const newWords []; words.forEach(word { const symbols word.split( ); let newSymbols []; let i 0; while (i symbols.length) { if (i symbols.length - 1 symbols[i] a symbols[i 1] b) { newSymbols.push(newToken); i 2; } else { newSymbols.push(symbols[i]); i 1; } } newWords.push(newSymbols.join( )); }); // 重要更新频率统计。这是一个简化处理实际BPE实现中频率更新更复杂。 // 这里我们清空并重新统计以保持演示清晰。 const newFrequencies {}; newWords.forEach(word { const symbols word.split( ); symbols.forEach(sym { newFrequencies[sym] (newFrequencies[sym] || 0) 1; }); for (let i 0; i symbols.length - 1; i) { const p symbols[i] symbols[i 1]; newFrequencies[p] (newFrequencies[p] || 0) 1; } }); return { newWords, newFrequencies, newVocab: vocab }; } // 3. 执行BPE训练 function trainBPE(text, numMerges 10) { let { words, vocab, frequencies } initializeVocab(text); console.log(初始词汇表:, vocab); console.log(初始词序列示例:, words[0]); for (let merge 0; merge numMerges; merge) { const pairInfo getMostFrequentPair(frequencies, vocab); if (!pairInfo) break; console.log(\n第 ${merge 1} 次合并: 合并 ${pairInfo.pair} 频率 ${pairInfo.freq}); const result mergePair(pairInfo.pair, words, frequencies, vocab); words result.newWords; frequencies result.newFrequencies; vocab result.newVocab; console.log(新词汇表大小: ${vocab.length}); console.log(新词序列示例: ${words[0]}); } return { finalVocab: vocab, finalWords: words }; } // 测试 const corpus low lower lowest new newer newest; // 微小语料 const { finalVocab, finalWords } trainBPE(corpus, 5); console.log(\n 最终结果 ); console.log(最终词汇表:, finalVocab); console.log(编码后的序列:); finalWords.forEach((w, i) console.log( ${corpus.split(/\s/)[i]} - ${w}));实操心得与注意事项真实世界的复杂性我们实现的这个版本是高度简化的。生产级的BPE如tiktoken库中的会处理字节级编码解决所有Unicode字符、更高效的频率统计和合并算法并保存合并规则Merge Rules用于后续编码。结束符/w的重要性它用于区分词边界。例如“eat”和“eater”中的“eat”部分如果没有结束符模型无法区分它们是同一个子词还是不同词的一部分。加上/w后“eat ”和“eat”就是两个不同的Token。词汇表大小的权衡numMerges合并次数直接决定词汇表大小。太大会导致词汇表膨胀失去子词压缩的优势太小则会导致序列过长增加计算负担。通常需要在大规模语料上统计确定。中文分词的差异对于中文BPE的初始单元通常是单个汉字。因为汉字本身是语素文字携带一定语义。所以中文BPE训练后词汇表里会包含高频词如“中国”、“经济”和常用字。运行上面的代码你会看到词汇表如何从单个字符逐步生长并形成像“low”、“er”、“est”这样的可复用子词。这就是大模型“认识”世界的第一步。3. 核心环节二Embedding向量化/嵌入的本质探究3.1 从One-Hot到分布式表示为什么需要向量分词之后我们得到了一串Token ID每个Token在词汇表中的索引。最原始的表示方法是One-Hot Encoding假设词汇表有5万个词每个词就是一个长度为5万的向量只有对应索引位置是1其余全是0。这种方法问题巨大维度灾难向量维度等于词汇表大小动辄数万维计算和存储开销无法承受。语义鸿沟任意两个词的向量都是正交的点积为0无法表达“猫”和“狗”在语义上比“猫”和“飞机”更接近的关系。Embedding就是为了解决这些问题而生的。它的核心思想是学习一个低维、稠密的实数向量来表示每个Token使得语义相似的Token在向量空间中的位置距离也相近。这个向量空间通常被称为“嵌入空间”。例如在一个训练好的嵌入空间中vector(“国王”) - vector(“男人”) vector(“女人”)的结果向量会非常接近vector(“女王”)。这就是著名的“词向量类比”特性证明了嵌入空间捕获了语义和语法关系。3.2 Embedding层是如何工作的在神经网络尤其是Transformer中Embedding通常是一个可训练的查找表Lookup Table也称为Embedding Layer。结构它是一个大小为[vocab_size, embedding_dim]的矩阵。vocab_size是词汇表大小如50000embedding_dim是嵌入向量的维度如768、1024等远小于词汇表大小。过程输入是一个Token ID整数例如token_id 1234。在Embedding矩阵中查找第1234行。这一行就是一个长度为embedding_dim的浮点数向量即该Token的嵌入向量。训练这个矩阵的参数在模型训练初期是随机初始化的。通过模型在大量文本数据上进行“完形填空”如BERT的MLM任务或“预测下一个词”如GPT的LM任务等训练根据预测误差反向传播不断调整这个矩阵中的每一个数字。最终语义和语法上相关的Token其对应的向量会在训练过程中被“推”到相近的位置。3.3 在Node.js中模拟Embedding查找与简单运算虽然训练一个高质量的Embedding层需要海量数据和计算资源但我们可以用Node.js模拟其使用过程并直观感受向量运算的魔力。假设我们已经有一个训练好的、极小的词向量表这里手动构造以便理解// 模拟一个微型的嵌入矩阵 [vocab_size6, embedding_dim4] // 词汇表: [“king”, “queen”, “man”, “woman”, “computer”, “data”] const embeddingMatrix { “king”: [ 0.8, 0.2, -0.1, 0.5], “queen”: [ 0.7, 0.3, -0.2, 0.6], “man”: [ 0.1, 0.8, 0.4, -0.3], “woman”: [ 0.2, 0.9, 0.3, -0.2], “computer”:[ -0.5, -0.1, 0.9, 0.7], “data”: [ -0.6, 0.0, 0.8, 0.6] }; function getEmbedding(token) { return embeddingMatrix[token] || Array(4).fill(0); // 返回零向量处理未知词 } function vectorAdd(v1, v2) { return v1.map((val, idx) val v2[idx]); } function vectorSubtract(v1, v2) { return v1.map((val, idx) val - v2[idx]); } function cosineSimilarity(v1, v2) { const dotProduct v1.reduce((sum, val, idx) sum val * v2[idx], 0); const norm1 Math.sqrt(v1.reduce((sum, val) sum val * val, 0)); const norm2 Math.sqrt(v2.reduce((sum, val) sum val * val, 0)); return dotProduct / (norm1 * norm2); } // 1. 基础查找 console.log(“‘king’的嵌入向量:”, getEmbedding(“king”)); // 2. 经典类比运算: king - man woman ≈ queen const resultVector vectorAdd( vectorSubtract(getEmbedding(“king”), getEmbedding(“man”)), getEmbedding(“woman”) ); console.log(“\n计算 ‘king - man woman’ 的结果向量:”, resultVector); // 3. 在词汇表中寻找与结果向量最相似的词 console.log(“\n寻找最相似的词:”); let bestMatch { token: null, similarity: -Infinity }; for (const [token, vec] of Object.entries(embeddingMatrix)) { const sim cosineSimilarity(resultVector, vec); console.log( 与 “${token}” 的余弦相似度: ${sim.toFixed(4)}); if (sim bestMatch.similarity) { bestMatch { token, similarity: sim }; } } console.log(\n最匹配的词是: “${bestMatch.token}” (相似度: ${bestMatch.similarity.toFixed(4)})); // 4. 计算语义相关性 console.log(“\n语义相关性对比:”); console.log(“man” 与 “woman”: ${cosineSimilarity(getEmbedding(“man”), getEmbedding(“woman”)).toFixed(4)}); console.log(“king” 与 “queen”: ${cosineSimilarity(getEmbedding(“king”), getEmbedding(“queen”)).toFixed(4)}); console.log(“computer” 与 “data”: ${cosineSimilarity(getEmbedding(“computer”), getEmbedding(“data”)).toFixed(4)}); console.log(“king” 与 “computer”: ${cosineSimilarity(getEmbedding(“king”), getEmbedding(“computer”)).toFixed(4)});运行这段代码你会看到即使在这个手工构造的微型示例中“king - man woman”的结果向量与“queen”的向量相似度最高。同时“man”和“woman”、“computer”和“data”之间的相似度也远高于“king”和“computer”。这直观地展示了Embedding空间如何编码语义关系。关键点解析余弦相似度是衡量向量方向相似度的常用指标范围在[-1, 1]之间值越大越相似。它比欧氏距离更能抵抗向量长度范数的影响更适合衡量语义相似性。维度embedding_dim的意义维度越高理论上能捕获更细粒度和更复杂的语义、语法特征。但并非越高越好过高的维度会增加计算量并可能导致过拟合。768或1024维是当前大模型的常见选择。位置编码的补充在Transformer中单纯的Token Embedding会丢失词序信息。因此需要额外加上Positional Encoding位置编码来告诉模型每个Token在序列中的位置。这才是完整的输入表示输入 Token Embedding Positional Encoding。4. 完整流程串联从原始文本到向量序列现在我们把Tokenization和Embedding两个环节串联起来形成一个完整的文本预处理流水线。我们将使用一个在Node.js中可用的、相对轻量级的库来演示接近生产环境的流程。这里选择gpt-3-encoder用于GPT-2的分词和tensorflow/tfjs用于模拟Embedding查找进行演示。# 初始化项目并安装依赖 npm init -y npm install gpt-3-encoder tensorflow/tfjsconst { encode, decode } require(‘gpt-3-encoder’); const tf require(‘tensorflow/tfjs’); // 注意在Node.js中运行TF.js需要安装‘tensorflow/tfjs-node’以获得原生后端性能此处为演示用CPU后端。 // 步骤1: Tokenization - 使用一个预训练的分词器 const text “从Tokenization到Embedding理解大语言模型的核心预处理步骤。”; console.log(“原始文本:”, text); const encodedTokens encode(text); console.log(“\n1. 分词后Token IDs:”, encodedTokens); console.log(“Token数量:”, encodedTokens.length); // 我们可以解码回来看看对于BPE解码是无损的 const decodedText decode(encodedTokens); console.log(“解码回文本:”, decodedText); // 应与原文本一致 // 步骤2: 模拟Embedding查找 // 假设我们有一个虚拟的嵌入矩阵。在实际模型中这是一个从文件加载的、训练好的权重。 const vocabSize 50257; // GPT-2的词汇表大小 const embeddingDim 768; // 常见的嵌入维度 // 创建一个随机初始化的嵌入矩阵模拟未训练的权重 // 注意这里为了演示我们只创建一个小批次所需的矩阵切片。 const embeddingMatrix tf.randomNormal([vocabSize, embeddingDim]); // 根据Token IDs获取对应的嵌入向量 function getEmbeddingsForTokens(tokenIds) { // tokenIds 是一个普通数组如 [123, 456, 789] // tf.gather 从embeddingMatrix中收集指定行的向量 const tokenTensor tf.tensor1d(tokenIds, ‘int32’); const embeddings tf.gather(embeddingMatrix, tokenTensor); return embeddings; // 形状为 [tokenIds.length, embeddingDim] } // 获取我们文本的嵌入向量 const tokenEmbeddings getEmbeddingsForTokens(encodedTokens); console.log(“\n2. 嵌入向量形状:”, tokenEmbeddings.shape); // 应为 [token数量, 768] // 步骤3: 模拟添加位置编码 // 这里我们使用Transformer论文中的正弦余弦位置编码公式 function getPositionalEncoding(maxLen, dModel) { const pe tf.buffer([maxLen, dModel]); for (let pos 0; pos maxLen; pos) { for (let i 0; i dModel; i) { const angle pos / Math.pow(10000, (2 * (i / 2)) / dModel); if (i % 2 0) { pe.set(Math.sin(angle), pos, i); } else { pe.set(Math.cos(angle), pos, i); } } } return pe.toTensor(); } const seqLen encodedTokens.length; const positionalEncodings getPositionalEncoding(seqLen, embeddingDim); console.log(“\n3. 位置编码形状:”, positionalEncodings.shape); // 应为 [token数量, 768] // 步骤4: 组合成最终的输入表示 // 输入 Token Embedding Positional Encoding const modelInput tf.add(tokenEmbeddings, positionalEncodings); console.log(“\n4. 模型最终输入形状:”, modelInput.shape); // [token数量, 768] console.log(“第一个Token的最终输入向量前10维:”, modelInput.slice([0,0], [1, 10]).arraySync()); // 步骤5: 清理内存在TensorFlow.js中很重要 tokenEmbeddings.dispose(); positionalEncodings.dispose(); modelInput.dispose(); embeddingMatrix.dispose(); console.log(“\n 流程完成 ); console.log(文本“${text}”已被成功转换为一个形状为 [${seqLen}, ${embeddingDim}] 的数值张量可供Transformer模型处理。);流程详解与避坑指南分词器选择我们使用了gpt-3-encoder它封装了GPT-2的BPE分词逻辑。对于不同的模型如BERT、T5必须使用其对应的专用分词器如tokenizers库提供的BertTokenizer因为它们的词汇表和合并规则是不同的。混用会导致灾难性后果。嵌入矩阵在真实场景中嵌入矩阵是模型权重的一部分需要从预训练模型文件如.bin、.h5、.safetensors中加载。我们这里用随机数模拟。加载大矩阵时要注意内存。位置编码我们实现了原始Transformer的绝对位置编码。现代大模型如GPT-3、LLaMA可能使用更高效的旋转位置编码其目的是让模型能更好地捕捉相对位置关系。但核心思想不变为序列注入顺序信息。张量内存管理使用tf.dispose()或tf.tidy()及时清理中间张量在长时间运行或处理大批量数据时至关重要可以避免内存泄漏。批处理上述流程是针对单个序列的。实际训练或推理时需要处理一个批次Batch的序列。这涉及到填充Padding和注意力掩码Attention Mask来统一序列长度并忽略填充部分这是另一个关键步骤。5. 实战应用场景与高级话题探讨理解了基础流程我们来看看它在实际项目中的应用以及一些更深入的话题。5.1 应用场景一精准计算API调用成本假设你使用OpenAI的GPT-4 API其定价是每1000个输入Token和输出Token收费一定金额。你可以利用分词器来精确计算每次请求的Token数。const { encode } require(‘gpt-3-encoder’); // 对于OpenAI模型应使用tiktoken function estimateOpenAICost(prompt, model‘gpt-4’, completionTokensEstimate500) { // 注意OpenAI实际使用tiktoken此处用gpt-3-encoder近似演示 const promptTokens encode(prompt).length; const totalTokens promptTokens completionTokensEstimate; // 假设价格此为示例请以OpenAI官网最新价格为准 const pricePer1K 0.03; // 假设GPT-4输入$0.03/1K tokens const cost (totalTokens / 1000) * pricePer1K; console.log(提示词Token数: ${promptTokens}); console.log(预估总Token数: ${totalTokens}); console.log(预估成本: $${cost.toFixed(4)}); return { promptTokens, totalTokens, cost }; } const myPrompt 请将以下英文翻译成中文并保持技术文档的严谨性 “The transformer architecture relies heavily on self-attention mechanisms to weigh the importance of different tokens in the input sequence.”; estimateOpenAICost(myPrompt);避坑技巧不同模型的分词器不同。gpt-3-encoder适用于GPT-2/3而GPT-4使用cl100k_base编码器。在Python中使用tiktoken库是官方推荐做法。在Node.js中可以寻找对应的移植或通过API预计算。5.2 应用场景二构建语义搜索系统这是Embedding最经典的应用。核心思想将文档库中的所有文本和查询文本都转换为向量然后计算查询向量与所有文档向量的余弦相似度返回最相似的文档。// 伪代码/概念演示 class SimpleSemanticSearch { constructor() { this.documents []; this.embeddings null; // 假设这里存储所有文档的向量 this.model null; // 假设这里加载了一个生成Embedding的模型如sentence-transformers的MiniLM } async addDocument(text) { this.documents.push(text); // 在实际中这里应调用Embedding模型生成text的向量 // const embedding await this.model.encode(text); // 并将embedding存入this.embeddings } async search(query, topK5) { // 1. 生成查询语句的向量 // const queryEmbedding await this.model.encode(query); // 2. 计算查询向量与所有文档向量的余弦相似度 // const similarities this.embeddings.map(docVec cosineSimilarity(queryEmbedding, docVec)); // 3. 按相似度排序返回Top K的文档索引和分数 // const rankedIndices similarities.map((sim, idx) ({idx, sim})).sort((a,b) b.sim - a.sim).slice(0, topK); // 4. 返回结果 // return rankedIndices.map(({idx, sim}) ({ document: this.documents[idx], score: sim })); console.log(语义搜索: “${query}”); console.log((此处需接入真实的Embedding生成模型如通过ONNX Runtime运行MiniLM)); } } // 使用示例 // const searcher new SimpleSemanticSearch(); // await searcher.addDocument(“机器学习是人工智能的一个分支。”); // await searcher.addDocument(“深度学习利用神经网络进行特征学习。”); // const results await searcher.search(“什么是神经网络”); // console.log(results);实操心得Embedding模型的选择对于语义搜索通常使用经过对比学习训练的专用模型如all-MiniLM-L6-v2它们生成的向量在语义相似度任务上比通用语言模型的Embedding效果更好。向量数据库当文档数量巨大时百万级以上逐一遍历计算余弦相似度是不可行的。需要使用向量数据库如Milvus, Pinecone, Weaviate, Qdrant进行高效的近似最近邻搜索。混合搜索将基于关键词的传统搜索如BM25和语义搜索结合往往能取得更鲁棒的效果。5.3 高级话题分词与Embedding的局限性与前沿分词的局限性语言偏见BPE等基于统计的分词器在训练语料上频率高的组合会优先合并这可能放大语料中的社会文化偏见。上下文无关同一个词在不同语境下可能有不同含义如“苹果”公司 vs “苹果”水果但标准的分词器会给出相同的Token ID。区分多义词义是后续模型如Transformer需要完成的任务。处理数字和代码数字“12345”可能被切分成[“12” “345”]或[“123” “45”]这种任意性会影响模型对数学和代码的理解。一些专门为代码训练的模型如Codex会采用不同的分词策略。Embedding的演进上下文相关的Embedding传统的Word2Vec、GloVe是静态的每个词只有一个向量。而像BERT这样的模型会根据上下文动态生成每个Token的表示。例如“bank”在“river bank”和“bank account”中会有不同的向量。这被称为Contextual Embedding是当前的主流。句子/段落级Embedding如何将一组Token的向量聚合起来形成一个句子或文档的单一向量表示常用方法有取平均、取最后一个Token的向量在自回归模型中、或者使用专门的[CLS]标记的向量在BERT中。更先进的方法如BERTScore或使用Sentence Transformers进行有监督的句子表示学习。多模态EmbeddingCLIP模型将图像和文本映射到同一个向量空间使得用文本搜索图像成为可能。这展示了Embedding思想从NLP向CV等领域的强大扩展性。理解从Tokenization到Embedding的流程是驾驭大语言模型的必备基础。它不再是神秘的黑盒而是一套有章可循、可观测、可干预的数据转换管道。当你下次调用API或微调模型时不妨想想你输入的文本正经历着怎样的“分词”与“向量化”之旅这或许能帮你更好地理解模型的反馈甚至设计出更巧妙的提示。在Node.js这样的环境中亲手实现简化版是固化这一理解的最佳方式。
返回列表