CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
机器学习入门/课时/NLP 与 Transformer
RawBlame
课时目录12 节

机器学习入门

  1. 01机器学习是什么
  2. 02数学基础
  3. 03Python 与数据栈
  4. 04数据预处理
  5. 05监督学习
  6. 06无监督学习
  7. 07模型评估
  8. 08Scikit-learn 工作流
  9. 09深度学习基础
  10. 10NLP 与 Transformer
  11. 11TensorFlow 与 PyTorch
  12. 12综合练习

NLP 与 Transformer

Tokenization、Embedding、Attention。

35 分钟

NLP 与 Transformer

这一课你将能做到

  1. 用 jieba 分词 + TF-IDF + 余弦相似度,做出一个「相似题推荐」原型
  2. 理解词向量的来源——「分布假设」如何把词变成可计算的向量
  3. 用 NumPy 手算一遍 self-attention,彻底拆掉 Transformer 的神秘感
  4. 说清多头注意力、位置编码、BERT 与 GPT 的区别

为什么要学

本站每天都有用户搜题、刷题,系统要回答两个问题:「这道题和哪道题相似」「这道题讲的是什么知识点」。前几课的模型只会处理表格数字,而文本是高维、离散、带语序的信号。

NLP 方法近十年经历三段演进:词袋(数频率)→ 词向量(数语义)→ Transformer(数关系)。本课沿这条线各走一步,最后手算一次 attention——公式只有三行,但写过一遍和背十遍是两回事。

先建立图像

self-attention 的一句话:每个词提出「查询 Q」,和全句每个词的「键 K」对暗号,
按匹配度(softmax 权重)加权融合它们的「值 V」——

输出_i = Σ_j  softmax(QKᵀ/√d)ᵢⱼ · Vⱼ

「括号匹配」这句话里,「括号」会把注意力更多地分给「匹配」。

跟着做

步骤 1:分词 + TF-IDF,找最相似题

# pip install jieba scikit-learn
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

STOP = {"如何", "用", "实现", "求", "与"}
def tokenize(t):
    return [w for w in jieba.cut(t) if w.strip() and w not in STOP]

titles = ["用栈实现括号匹配校验", "用队列做任务调度与消息缓冲",
          "二叉树的层序遍历", "滑动窗口找无重复字符的最长子串"]
M = TfidfVectorizer(tokenizer=tokenize).fit_transform(titles)
sim = cosine_similarity(M)

for i in range(len(titles)):
    j = sorted(range(len(titles)), key=lambda k: -sim[i, k])[1]
    print(f"{titles[i]} → 最相似:{titles[j]}({sim[i, j]:.2f})")

「栈」和「队列」的语境高度重叠,得分自然领先——这是纯词频方法的诚实结果。

步骤 2:词向量从哪来?转置一下就懂

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import TruncatedSVD

cv = CountVectorizer(tokenizer=tokenize, binary=True)
words_docs = cv.fit_transform(titles).toarray().T   # 词 × 文档矩阵

svd = TruncatedSVD(n_components=2, random_state=42)
word_vec = svd.fit_transform(words_docs)            # 每词一个 2 维向量
for w, v in zip(cv.get_feature_names_out(), word_vec):
    print(f"{w:>4}  {v.round(2)}")

「栈」「队列」常出现在相似语境,向量就挨得近——这就是分布假设。Word2Vec 把它训练得更充分,BERT 则用深层网络生成随上下文变化的动态向量。

步骤 3:NumPy 手算 attention

import numpy as np

rng = np.random.default_rng(0)
d, T = 4, 3                                  # 词向量维度 4,句子 3 个词
X  = rng.normal(0, 1, (T, d))
Wq, Wk, Wv = (rng.normal(0, 0.5, (d, d)) for _ in range(3))

Q, K, V = X @ Wq, X @ Wk, X @ Wv
scores = Q @ K.T / np.sqrt(d)                # 除 √d 防点积过大
A = np.exp(scores - scores.max(axis=1, keepdims=True))
A /= A.sum(axis=1, keepdims=True)            # softmax:每行和为 1
out = A @ V

print(A.round(3), "\n每行和:", A.sum(axis=1).round(3))

A[i, j] 是第 i 个词分给第 j 个词的注意力权重,每行和恒为 1;out 的第 i 行就是加权融合全句后的新表示——每个词都「看过」了上下文。

步骤 4:认识完整架构

Transformer 编码器块(堆 N 层):
输入 + 位置编码 → 多头自注意力 → 残差 & 层归一化 → 前馈网络 → 残差 & 层归一化
  • 位置编码:attention 天生无序,必须显式注入词的位置;
  • 多头:多组 Q/K/V 并行,各自捕捉不同模式(语法、指代、主题);
  • BERT vs GPT:BERT 是双向编码器,擅长理解类任务(分类、相似度);GPT 是单向解码器,擅长生成。两者共享「预训练 + 微调」范式。

讲清楚要点

  • 词袋 → 词向量的跃迁:TF-IDF 得到的是稀疏的「词频指纹」,词向量是稠密的「语义坐标」,后者能算词与词的相似度。
  • attention 的三重价值:长距离依赖不再被距离稀释;矩阵运算可并行(对比 RNN 逐词串行);权重矩阵天然可视化,具备一定可解释性。
  • 中文与 subword:英文有天然空格,中文要分词;BERT/GPT 类模型按字或 BPE 子词切分,从机制上回避了分词错误传播。
  • 预训练 + 微调:大模型在海量语料上学通用语言能力,你只需几百条标注样本微调头部,这是现代 NLP 的标准打开方式。

你可能会懵的地方

Q1:为什么点积要除 √d? 维度越高点积方差越大,分数过大会让 softmax 饱和成 one-hot,梯度接近零。除 √d 把方差拉回常数级,训练才稳定。

Q2:BERT 和 GPT 到底差在哪? 训练目标和方向:BERT 双向掩码预测,看得见上下文左右,适合理解;GPT 单向预测下一个词,适合生成。选型按任务定,别拿 BERT 硬写文章。

Q3:需要手写 Transformer 吗? 生产中不需要,用 transformers 库加载预训练模型即可。本课手算的目的只有一个:让公式从「背的」变成「摸过的」。

Q4:中文任务还要 jieba 分词吗? 传统流程(TF-IDF)需要;BERT 类模型按字/子词切分,不需要。但分词在关键词抽取、搜索场景依然有用。

Q5:数据很少时用微调还是特征抽取? 几百条样本用特征抽取:冻结 BERT,取 [CLS] 向量接一个逻辑回归;数据多且领域差异大(医疗、法律)再整体微调。

错法对照

错法后果正确做法
用原始词频算相似度常见词主导结果TF-IDF 加权
attention 忘 softmax权重不可比、无归一每行 softmax,和为 1
手算时忘除 √dsoftmax 饱和、梯度消失scores / np.sqrt(d)
拿 BERT 做文本生成架构与任务错配生成任务用 GPT 类解码器
小数据从头训练语言模型效果远差于预训练预训练 + 微调/特征抽取

卡点急救

  1. ModuleNotFoundError: jieba → pip install jieba。
  2. TfidfVectorizer 报 empty vocabulary → tokenizer 被停用词滤空了,先 print 检查分词结果。
  3. TruncatedSVD 报 n_components 过大 → 不能超过特征数,用 min(2, len(vocab)-1) 兜底。

面试常问

Q1:讲一下 self-attention 的计算过程? 输入经三个投影得到 Q/K/V;分数 = QKᵀ/√d,按行 softmax 得权重,再加权求和 V。每个词的输出都是全句信息的加权融合。

Q2:BERT 和 GPT 的区别? 双向编码 vs 单向解码、掩码语言模型 vs 下一词预测、理解 vs 生成,二者是同一 Transformer 架构在不同训练目标下的分支。

Q3:词向量为什么能表达语义? 分布假设:语义由上下文分布决定。语境相似的词在向量空间中距离近,于是「 king - man ≈ queen - woman」这类代数关系得以成立。

自测三问

  1. softmax 之后注意力矩阵每一行的和是多少?(恰好 1)
  2. 为什么必须有位置编码?(attention 对输入顺序不敏感)
  3. 余弦相似度的取值范围?([-1, 1],TF-IDF 文本通常落在 0~1)

小结一句话

NLP 的三段演进各解决一层问题:词袋数频率、词向量数语义、attention 数关系——Transformer 把「让每个词看全句」机制化。

延伸

  • 官方文档:PyTorch Transformer 教程
  • 下一课预告:PyTorch、TensorFlow、JAX、XGBoost 各守哪块阵地?再给训练好的模型搭一个线上推理接口。
加载本节练习…
← 上一课深度学习基础下一课 →TensorFlow 与 PyTorch