Base LLM | 从 NLP 到 LLM 的算法全栈教程 第一天

NLP 概述

在工程实践中,NLP 通常可以概括为自然语言理解(Natural Language Understanding,NLU)与自然语言生成(Natural Language Generation,NLG)两类能力。

|--------------------------------|--------------------------------|---------------------------|----------------------------------------------|
| 任务 | 是什么 | 有什么用 | 举例 |
| 文本分类 (Text Classification) | 给一段文本自动分配一个或多个预定义的标签 | 信息组织与过滤;入门最广泛的任务之一 | 情感分析;垃圾邮件过滤;新闻分类 |
| 命名实体识别 (NER) | 从文本中找出并分类关键实体,如人名、地名、组织、时间、产品等 | 将非结构化文本转为结构化信息,是信息抽取的关键一步 | "马云""1999年""杭州""阿里巴巴"等实体识别 |
| 关系抽取 (Relation Extraction) | 在识别实体的基础上判断实体间的语义关系 | 构建知识图谱,深化文本理解 | 创始人(马云, 阿里巴巴);创办于(阿里巴巴, 1999年) |
| 机器翻译 (Machine Translation) | 自动将一种自然语言翻译成另一种 | 消除语言隔阂,促进全球交流 | Attention is all you need → 注意力机制就是你所需要的一切 |
| 文本摘要 (Text Summarization) | 将长文本压缩为简短摘要,保留核心信息 | 快速获取要点,节省阅读时间 | 新闻摘要;会议纪要 |
| 问答系统 (Question Answering) | 针对问题给出精准、简洁的答案 | 高效信息获取,是智能客服/搜索的核心能力 | "珠穆朗玛峰多高?→ 8848.86米";"我的订单何时到?→ 预计明天下午 3 点前" |
| 文本生成 (Text Generation) | 根据输入(关键词、数据、图片等)自动生成文本 | 内容创作、人机交互、报告自动化 | AI 写作;代码生成 |
| 对话系统 (Dialogue System) | 模拟多轮对话,理解上下文并作出恰当回应 | 智能助理、情感陪伴、客服等交互式应用 | 连续对话、记忆上下文的应答 |

第一节 初级分词技术

一、基于规则与词典

基于规则与词典 的分词方法是 NLP 领域最早期的技术方案,它的核心依赖于一部大型词典一套匹配规则jieba 的默认模式实现的就是这种方式。它会先基于一个前缀词典(Trie 树) ,高效地构建出一个包含句子中所有可能词语组合的有向无环图(DAG)。接着,通过动态规划算法寻找一条概率最大的路径,作为最终分词结果。

数值稳定性问题:在实际工程中,将大量小于 1 的概率值直接相乘,很容易导致结果趋近于 0,造成浮点数下溢 ,无法比较路径优劣。为此,jieba 采用了对数概率 技术,利用对数函数 log 的性质,将概率的累乘 转换为 log 概率的累加

jieba 还引入了动态规划 的思想。它会从句子的末尾 开始,从后向前 递推计算到每个位置的最优切分路径及其 log 概率之和,并记录下来。最终,从句子开头出发,根据记录好的最优路径信息,就能反推出整个句子的最优分词结果。

我们可以进行人工干预 ,通过自定义词典 把新词加入词表,"教会"jieba认识它。

通过自定义词典,能很方便地解决 OOV 的问题,让分词结果符合预期,这在处理特定业务领域文本时尤其有用。当向jieba中添加自定义词(如"奔波儿灞")但不指定词频 时,jieba会采取一种自动的方式来估算其词频。它会首先尝试对这个新词进行分词(例如,默认会切成 ["奔", "波", "儿", "灞"]),然后基于这些组成部分的基础词频计算出一个概率。最后,给"奔波儿灞"这个整体赋予一个略高于其组成部分概率之积的词频。这样,在后续的分词计算中,模型就会更倾向于将"奔波儿灞"视为一个完整的词,从而实现了"强制"分词的效果。

精确模式工作流程

(1)文本预处理与分块 (cut 方法)

在处理复杂的中文句子前,首先需要"把肉和骨头分开"。cut 函数作为总调度,利用正则表达式 re_han_default 将整个句子切分成连续的汉字区块(如"我在梦里收到清华大学录取通知书")和非汉字部分(如标点、数字、英文)。非汉字部分直接输出,只有汉字部分才会进入后续的分词流程。

2)构建有向无环图 (get_DAG 方法)

接下来是"绘制地图",也就是要找出所有可能的走法。系统会扫描句子,看每个字能组成哪些词。

  • 从"我"(第 0 字)开始,查词典发现能组成"我"(到第 1 字结束)。
  • ...
  • 当扫描到"清"(第 6 字)时,查词典发现能组成"清"、"清华"、"清华大学"等。
  • 以此类推...

这样,字和字之间就建立起了各种连接(边)。因为我们只能从前向后走,不会回头,也不会绕圈子,所以这张由字(节点)和词(边)构成的网络图,就被称为有向无环图get_DAG 方法的作用就是生成这样一张包含所有潜在分词路径的"地图"。

(3)计算最优路径 (calc 方法)

地图构建完成后,需要计算哪条路径的概率最大。calc 方法采用动态规划 的思想,为了避免重复计算,它从句子的末尾向前反向计算 。例如,先看最后一个字"书",确定其最佳路径;再看倒数第二个字"知",判断是连着"书"走概率高,还是自己单独走概率高;以此类推直到句首"我"。在这个过程中,算法会计算每个候选词的路径"分数"(由当前词的 log 概率与该词之后剩余句子的最优 log 概率相加而成),并将从每个位置出发的总分最高词语及其结束位置记录在 route 表中。

4)从路由表中重建结果 (__cut_DAG_NO_HMM 方法)

最后一步就是根据计算好的路线输出结果。__cut_DAG_NO_HMM 方法根据 route 表中记录的"路标"进行路径回溯。例如,从起点"我"开始,最佳路标指向下一个位置,因此切分出 ['我']。随着处理的推进,当遍历到"清"字时,最佳路标指示直接跳过 4 个字(因为"清华大学"作为整体的概率更高),于是切分出 ['清华大学']。最终,系统结合所有切分结果,输出 ['我', '在', '梦里', '收到', '清华大学', '录取', '通知书']

二、统计学习时代的方法

为了解决对人工词典的过度依赖,研究者们转向了统计学习。主要思想是把分词看作一个序列标注 问题,即为句子中的每个字打上一个位置标签。例如,我们可以用 B (Begin) 表示词的开始,M (Middle) 表示词的中间,E (End) 表示词的结束,S (Single) 表示单字成词。在这种标注体系下,"我爱北京"会被标注为 S S B E,通过这种方式,分词任务就转化为了寻找字序列对应的最合理标签序列的问题。

隐马尔可夫模型(HMM) 就是解决这类问题的经典生成式模型。它能学习到字与标签之间的对应关系(发射概率)以及标签与标签之间的转移关系(转移概率)。jieba 就利用了 HMM 来识别词典中不存在的 OOV 2。当基于词典的图算法在句子中遇到无法切分的、连续的未登录字串时,就会调用 HMM 模块对该局部子句进行二次分词。这种方法的优点是能够发现词典外的新词,在一定程度上缓解了 OOV 问题。但缺点也显而易见,HMM 的两个核心假设(观测独立假设和齐次马尔可夫假设)过于严格,限制了模型利用更丰富上下文特征的能力,所以在处理复杂的歧义场景时,它的效果往往不如后续的 CRF 等模型。

HMM 对未登录词的识别

前面的 __cut_DAG_NO_HMM 方法是纯粹的"词典派",它完全信赖词典,遇到未收录的词(如"直聘")时,往往只能将其切碎成单字("直"、"聘")。为了修正这种行为,默认开启的 __cut_DAG 方法采用了**"单字缓冲,二次加工"的混合策略。它在遍历动态规划生成的路径时,不会立即输出结果,而是设置一个 缓冲区**。凡是路径上的单字 ,都先扔进缓冲区攒着;一旦遇到多字词 或句子结束,就说明一段由单字组成的"未登录区域"结束了。此时,jieba 会调用 HMM 模型对缓冲区里的字符串进行二次分词 ,试图用统计规律把这些被切碎的字重新"粘"回成词。这也解释了为什么 jieba 的默认分词策略能够识别很多词典里没有的新词。

词性标注

除了分词,jieba 还提供了词性标注功能。采用了词典查询与隐马尔可夫模型相结合 的混合策略,来识别出每个词语的语法属性(名词、动词、形容词等)。这需要使用jieba.posseg模块。由于在前面已经通过 jieba.load_userdict() 加载了包含"奔波儿灞"的词典,所以 jieba 已经能够正确地将其切分出来。但是,因为初始词典未提供词性,jieba 会给它一个默认的、不一定准确的词性(如下面的x)。

三、从"分词"到"分块",BERT与GPT

随着深度学习,特别是 BERTGPT 等大规模预训练模型的兴起,传统意义上"将句子切分成标准词语"的分词范式有了重大改变。现代 NLP 模型更倾向于采用**"无分词"或"弱分词"的策略,将文本处理成更基础的、数据驱动的单元,主要分为 字粒度子词粒度**两种流派。

(1)字粒度分词

BERT 模型为代表,在处理中文时,它的分词策略就是字粒度 ,也就是直接将每个汉字视为一个独立的 Token。这种策略有效解决了 OOV 问题,因为常用汉字的数量是有限的,模型可以轻松构建一个全覆盖的"字表",摆脱对庞大词典的依赖。但是,它的代价也显而易见。首先是词汇语义的丢失 ,像"博物馆"这样的词被拆散为三个独立的字后,模型需要消耗更多资源去重新学习它们之间的组合关系;其次,相较于词,字的序列长度会显著增加,这加大了模型的计算负担。

(2)子词分词

GPT 系列为代表的大语言模型,则采用了更灵活的子词(Subword) 切分方案,其中最主流的算法是 BPE(BytePair Encoding) 3。BPE 的思路是在原始语料上迭代合并高频相邻 字节对(或字符对)成一个新的、更大的单元,并将其加入词表。例如,如果 "deep" 和 "er" 经常相邻出现,BPE 就会将它们合并,最终可能将 "deeper" 作为一个整体加入词表。这种方案在"词"和"字"之间取得了有效平衡,高频词被完整保留,低频词或新词则被拆解为更小的有意义单元(如字或字节组合),既保持了信息完整性又有效解决了 OOV 问题,同时还能通过控制合并次数来限制词表大小。

第二节 词向量表示

核心任务就是要弥合自然语言(符号世界)与数学模型(向量空间)之间的鸿沟 。我们需要一种系统性的方法,将分词后得到的词元序列(如 ["国足", "爱", "吃", "海参"]),整体转换成模型能够处理的一个或一组有意义的数字。这个将符号转换为数字的过程,统称为词向量表示 (Word Representation)

一、离散表示

独热编码(One-Hot Encoding)

哑编码的优点 是实现简单,能够清晰地将词语区分开。至于它的缺点 主要体现在两方面,一是维度灾难, 数据极其稀疏,浪费计算和存储资源;二是语义鸿沟 ,任意两个不同词的独热向量都是正交 的(点积为 0)。这是因为点积是通过将两个向量的对应元素相乘再求和来计算的。例如"国王"与"女王"的距离和"国王"与"香蕉"的距离完全一样,严重丢失了语义信息。

词袋模型(Bag-of-Words, BoW)

词袋模型 的基本思想是忽略文本中的词序和语法,将其仅仅视作一个装满词的"袋子" ,用袋子中每个词出现的统计量 来表示整个文档。它的实现过程可以理解为将文档中所有词的独热向量相加,得到一个最终的向量。这个向量的维度等于词典大小,每一维的值代表了对应词语在文档中的出现频次。

词袋模型向量中每一维的值可以采用不同策略。最直接的方式是使用频数 ,也就是单词在文档中出现的次数,但这会导致长文章的计数值普遍偏高。为缓解文档长度的影响,可以使用频率 ,即用单词次数除以文档总词数(词频)。另一种方式是二进制 ,仅关注单词是否出现,若出现记为 1,否则为 0,忽略具体出现次数。

词袋模型的主要优点 是实现简单,尤其在文本分类 等任务上表现不错,因为这类任务的核心往往在于判断"文档里有什么词",而非"词与词之间如何关联",所以即使丢失了词序也能取得较好效果。但它的缺点 也很明显,首先是丢失词序 ,导致如"我 爱 你"和"你 爱 我"的词袋表示完全相同,无法区分语义差异;其次是未考虑词的重要性 ,像"的"、"是"这类在所有文档中都频繁出现的停用词会获得很高频次,却对区分文档主题贡献寥寥,反而形成干扰。

TF-IDF

TF-IDF 主要由两部分组成,第一部分是词频(Term Frequency, TF) ,用于衡量一个词在当前文档中出现的频繁程度。它的计算方式主要有两种,一是直接使用原始频数, 二是采用归一化频率 通过除以文档总词数来消除长文档带来的偏差。

第二部分是逆文档频率(Inverse Document Frequency, IDF),用于衡量一个词的"稀有"程度或"信息量"。常用平滑版本:log(料库中的总文档数/料库中的总文档数+1)

TF-IDF 更是衡量用户查询词与网页内容相关性的核心指标之一,但是丢失词序。

N-gram 模型

心基于马尔可夫假设 ,也就是认为一个词出现的概率只取决于它前面 N-1 个词 。这种简化大大降低了建模的复杂度,并且由于它基于前文预测后续内容,常被视为生成式 AI 的雏形。根据依赖的前文长度不同,N-gram 模型可分为多种类型,其中 Unigram(1-gram) 和词袋模型一样,假设每个词独立且不依赖前文;Bigram(2-gram) 只依赖前 1 个词,例如看到"喜欢"预测"玩"的概率;而 Trigram(3-gram) 则依赖前 2 个词,例如根据"喜欢 玩"来预测"GTA6"的概率。

虽然 N-gram 找回了词序,但它付出了巨大的代价,这也是它被神经网络取代的原因。挑战之一是指数级爆炸 ,如果词典有 10,000 个词,Bigram 就有 108 种组合,Trigram 则高达 1012 种,这种维度灾难 是传统计算机无法承受的。随之而来的是数据稀疏问题,绝大多数词的组合(如"大象 骑着 张三 驾驶 飞机")在语料中永远不会出现,导致概率为 0。

二、序号化表示

序号化 ,也称整数编码 ,是将分词后的词元序列 转换为深度学习模型能够处理的整数序列核心步骤。其过程如下:

(1)构建词典 :与 One-Hot 类似,首先从训练语料中构建一个词典。但在深度学习中,这个词典通常是字级别 的(如 BERT),或是子词级别的(如 GPT),而不是词级别的。

(2)增加特殊词元 :在词典中加入一些有特殊功能的 Token,至少包括 [PAD](Padding)和 [UNK](Unknown)。[PAD] 的 ID 通常为 0,用于将短句子填充 至同一批次内的最长长度,以满足批处理需求;[UNK] 的 ID 通常为 1,用于表示所有词典中未出现过 的词。根据任务需求,还可能加入 [CLS](分类)、[SEP](分隔)等其他特殊词元。

(3)ID 映射 :将文本序列中的每个词元(字/子词)直接映射为其在词典中的整数 ID

在实践中,很少从零开始为自己的小数据集构建词典。更常见的做法是,直接使用像 BERTGPT 这类预训练模型官方提供的词典文件(vocab.txt) 。这些词典通常包含了数万个字、子词、符号等,是在海量通用语料上构建的,覆盖面非常广。例如,Google 的中文 BERT 模型词典 vocab.txt 中就包含了约 21128 个词元,其中不仅有常用汉字,还包括了英文字母、数字、标点及 [PAD]/[UNK] 等特殊符号。

对这句子进行分词 ,并根据词典查找每个词元对应的 ID 。由于"钱"字不在词典中,将其映射为 [UNK] 的 ID 1;

为了将这三个长短不一的序列组成一个矩阵 ,我们需要以最长的序列(句子1,长度为5)为基准,使用 [PAD] 的 ID 0 对其他短序列进行填充 (Padding);

最终,我们得到一个 3x5整数矩阵,这个矩阵就是喂给深度学习模型的最终输入。

第三节 从主题模型到 Word2Vec

为了获取词向量之间的关系,研究者们探索了不同的技术路径。其中一条是基于全局文档统计主题模型 。另一条是后来居上、并成为主流的、基于局部上下文预测神经网络模型 。作为早期探索的代表,主题模型 利用全局统计初步实现了语义捕捉,而随后的 Word2Vec 则通过全新的局部预测范式,真正释放了分布式表示的强大威力。

一、主题模型

SVD 矩阵分解

(1)构建"词-文档"矩阵 :首先,我们需要以整个语料库为基础,构建一个巨大的词-文档矩阵 X 。这个矩阵的每一行代表一个词,每一列代表一篇文档,矩阵中 X(i,j) 的值是词 i 在文档 j 中的重要性权重,可以使用 TF-IDF 值来填充。这个矩阵通常是巨大且高度稀疏的。

(2)矩阵分解 :从线性代数的角度看,这个巨大的稀疏矩阵 X 可以被近似分解为两个更小的、更稠密的矩阵的乘积。最常用的分解技术之一是 奇异值分解(SVD)

Xm×n≈Wtopic,m×k×Htopic,k×n

  • Xm×n 是原始的词-文档矩阵, m 是词典大小, n 是文档数量。
  • k 是一个远小于 m 和 n 的超参数,代表期望发现的潜在主题数量
  • Wtopic 表示 "词-主题矩阵"。它的每一行,都是一个 k 维的稠密向量,表示一个词与 k 个主题的关联度。
  • Htopic 表示 "文档-主题矩阵"。它的每一列,都是一个 k 维的稠密向量,表示一篇文档在 k 个主题上的分布。

(3)获取词向量 :分解完成后,我们真正关心的是 "词-主题"矩阵 Wtopic 。这个矩阵的每一行 ,正是我们需要的词向量。它将原来 m 维的 One-Hot 编码,降维到了 k 维,而且还是一个稠密向量,每个维度都代表了与某个主题的关联强度。这个矩阵还蕴含了语义信息。如果两个词(如"CPU"和"GPU")经常在描述"硬件"这个主题的文档中共同出现,那么SVD分解的结果会使它们在对应"硬件"主题的那个维度上都有很高的值,从而使它们的最终词向量在空间上非常接近。

主题模型(如其更广为人知的名字 LSA, Latent Semantic Analysis 1)通过对全局的"词-文档"共现矩阵进行分解,成功地将词语映射到了一个低维的"主题空间",得到了能够表达语义的稠密词向量,但它也存在明显的局限性。比如对一个大型语料库进行 SVD 分解,计算量和内存开销都极大,导致计算代价高昂;其次,它依赖的是全局的、粗粒度的文档级别共现信息,忽略了词语在句子中的局部上下文和词序信息,使得它难以捕捉更精细的语义关系;而且这种"先统计,再分解"的流程,很难与现代的深度学习模型进行端到端的联合训练,难以集成。

二、Word2Vec

Word2Vec 通常被认为是一种浅层神经网络模型(Shallow Neural Network) 。它的"浅层"体现在网络结构的简单性上,因为它移除了 传统神经概率语言模型(NNLM)中计算昂贵的非线性隐藏层,直接将投影层与输出层相连。

从数学上看,将一个单词的 ID 转换为其稠密向量的过程,在概念上可以分解为三步。首先输入一个代表单词的 ID(例如 3);接着进行哑编码,将 ID 3 转换为一个维度等于词典大小 |V| 的高维稀疏向量(例如 [0,0,0,1,0,...],其中只有第 3 个位置为 1);最后进行矩阵乘法,用这个 One-Hot 向量去乘以一个巨大的、可学习的参数矩阵 Win (尺寸为 |V|×D),该矩阵即为最终的词向量查询表。由于 One-Hot 向量只有一个位置是 1,这个矩阵乘法的结果等效于直接从矩阵 Win 中 "抽取"出索引为 3 的那一行

实践中:根据输入的单词 ID,直接从 Win 矩阵中获取对应的行向量。理解这里的关键在于,这个参数矩阵 Win 本身就是学习的目标 。它被随机初始化,并在后续的训练过程中,通过 CBOW 或 Skip-gram 这样的 预测任务 不断地被优化和调整。

CBOW模型:根据上下文词来预测中心词

**Skip-gram:**根据中心词预测上下文,它将一个预测任务,分解成了多个独立的子任务。

滑动窗口的直观理解:

关键在于滑动窗口机制如何生成大量高度重叠的训练样本。假设有一个很长的句子,并设窗口大小为 k=7(中心词左右各 7 个词),通过在文本上滑动该窗口可以生成大量训练样本。

面对这两个拥有几乎相同上下文却对应不同目标词的样本,模型的目标看似矛盾,因为既要调整参数使第一个样本的上下文向量成功预测出 w8,又要让几乎完全一样的第二个样本上下文向量也能成功预测出 w9。为了同时达成这两个目的,优化算法(如梯度下降)会找到一个"捷径",也就是当 w8 和 w9 的词向量本身就足够接近时,模型就能用一个相似的上下文向量同时很好地预测出它们俩

固定词向量,和上下文无关,静态查询表。

第四节 基于 Gensim 的词向量实战

Gensim 时,会遇到几个概念:

  • 语料库 :这是 Gensim 处理的主要对象,可以简单理解为训练数据集 。分词后的文档通常表示为 list[list[str]];用于 TF-IDF、LDA 等模型的标准 BoW 语料库是包含稀疏向量的可迭代对象,每篇文档表示为 [(token_id, frequency), ...]。例如 [["我", "爱", "吃", "海参"], ["国足", "惨败", "泰国"]] 中每个子列表代表一篇独立的文档。
  • 词典 :这是一个将词语(token)映射到唯一整数ID的词汇表。在使用词袋模型之前,必须先根据整个语料库构建一个词典。
  • 向量 :在 Gensim 中,一篇文档最终会被转换成一个数学向量。例如,使用词袋模型时,一篇文档 ["我", "爱", "我"] 可能会被表示为 [(0, 2), (1, 1)]
  • 稀疏向量 :这是 Gensim 为了节省内存而采用的一种高效表示法。对于像 One-Hot 或词袋模型这样维度巨大且绝大多数值为0的向量,Gensim 不会存储所有0。例如,一个词袋向量 [2, 1, 0, 0, ... , 0] 会被表示成 [(0, 2), (1, 1)],仅记录非零项的索引和值,极大地减少了存储开销。
  • 模型 :在 Gensim 中,模型是一个用于实现向量转换 的算法。例如,TfidfModel 可以将一个由词频构成的词袋向量,转换为一个由TF-IDF权重构成的向量。
相关推荐
一拳不是超人2 小时前
龙虾🦞(OpenClaw) 本地部署体验:是真变革还是旧酒装新瓶?
前端·人工智能·程序员
云境筑桃源哇2 小时前
2026年科研软件分类及主流工具汇总
人工智能·分类·数据挖掘
LS_learner2 小时前
OpenCode 的 skills 网站相关信息
人工智能
东离与糖宝2 小时前
告别AI投毒!Java后端实现大模型prompt过滤与敏感信息拦截实战
java·人工智能
FluxMelodySun2 小时前
机器学习(二十二) 原型聚类:k均值算法、学习向量量化、高斯混合聚类
人工智能·机器学习·聚类
xiami_world2 小时前
6款思维导图软件深度评测:协作、AI能力与工具选型对比
人工智能·ai·信息可视化·思维导图
qq_454245032 小时前
AI自改进的复杂度边界
人工智能
新缸中之脑2 小时前
Chrome 146:终结专用AI浏览器?
前端·人工智能·chrome