☰
自然语言处理学习路线:从词向量到Transformer的NLP进阶笔记
2026/10/11 22:44:24 网站建设 项目流程

最近系统刷完了吴恩达老师的 NLP 系列课程,结合平时在做文本分类、信息抽取、评论情感分析等项目中的落地经验,我整理了一份从入门到进阶的 NLP 学习笔记。

很多同学学 NLP 时会遇到同一个困惑:资料太多,不知道从哪里开始;要么直接上手 HuggingFace 跑模型,原理完全不理解;要么只啃理论,代码能力跟不上。吴恩达这套 NLP 课程的优势在于,它把整个知识体系拆成了从传统方法到深度学习的递进路线,每个模块都配有可运行的 Python 实验,非常适合用来建立完整的 NLP 知识骨架。

这篇文章会把课程的骨架、核心算法、配套代码和常见的“坑位”一起串起来。无论你是刚接触自然语言处理的新手,还是已经在做 NLP 项目但缺少系统梳理的开发者,都能从里面找到需要的内容。

1. NLP 是什么,为什么需要系统学习

1.1 NLP 要解决的核心问题

自然语言处理(Natural Language Processing,简称 NLP)是让计算机理解、处理和生成人类语言的技术。它不是一个单一算法,而是一整个技术栈,覆盖了很多子任务:

  • 文本分类:垃圾邮件识别、评论情感分析、新闻分类。
  • 信息抽取:从文本中抽取实体、关系、事件。
  • 机器翻译:中英翻译、多语言翻译。
  • 问答系统:基于文档的问答、知识库问答。
  • 文本生成:摘要、对话、写作辅助。
  • 语音识别后的文本处理:分词、纠错、语义理解。

这些任务有一个共同难点:人类语言充满歧义、省略、指代和上下文依赖。比如“苹果好吃”和“苹果发布了新手机”,同一个词在不同的上下文里含义完全不同。NLP 的核心工作,就是找到一种方式,让模型能够捕捉这些上下文信息。

1.2 规则、统计与深度学习的演进

NLP 的技术路线大体经历了三个阶段:

阶段代表方法特点
规则时代人工编写语法规则、词典可解释性强,但覆盖不了语言多样性
统计时代N-gram、HMM、CRF、SVM、朴素贝叶斯靠人工设计特征,泛化能力有限
深度学习时代RNN、LSTM、Seq2Seq、Transformer、BERT自动学习特征,效果大幅提升

吴恩达这套 NLP 课程的设计思路,恰好就是按照这个演进顺序展开的。它不是一上来就讲大模型,而是先从词向量、朴素贝叶斯、逻辑回归这些基础组件讲起,再一步步过渡到 RNN、注意力机制、Transformer。这种讲法最大的好处是:你能清楚知道每个模型解决什么问题、为什么后来会被新模型替代。

1.3 为什么推荐跟着课程体系走一遍

现在很多初学者一上来就微调 BERT,虽然能用,但遇到效果不好时往往不知道如何优化,因为不清楚模型内部发生什么。而吴恩达课程强调“用 Python 从零实现关键算法”,比如手写逻辑回归、手写反向传播、实现注意力得分计算。这些基本功在工程排查中非常有用。

另外,这套课程覆盖了 NLP 开发全链路:数据预处理、特征表示、模型训练、评估调优,不是只讲论文模型。跟着走一遍,对项目落地也很有帮助。

2. 课程体系拆解与学习路线规划

2.1 吴恩达 NLP 系列课程的主要内容

这里以 deeplearning.ai 的自然语言处理专项课程为参考,整个体系通常被划分为四个大的模块:

  1. 文本分类与向量空间学习词频表示、TF-IDF、词向量、余弦相似度,以及朴素贝叶斯、逻辑回归在文本分类中的应用。

  2. 概率模型与语言模型学习 N-gram 语言模型、概率计算、文本生成,以及词性标注、隐马尔可夫模型(HMM)等经典序列标注方法。

  3. 序列模型学习 RNN、LSTM、GRU 的原理与实现,掌握如何用序列模型处理文本生成、命名实体识别等任务。

  4. 注意力模型与 Transformer学习 Seq2Seq 中的注意力机制、Transformer 架构,并进一步理解 BERT 等预训练语言模型的应用。

不同版本课程在章节安排上可能略有差异,但主线基本一致。学习时不用纠结版本号,重点是抓住这条“从词向量到注意力机制”的主线。

2.2 适合哪些人学习

  • 刚入门 NLP 的同学:课程从最基础的文本表示讲起,不需要很强的机器学习基础。
  • 会用框架但不懂原理的开发者:课程里的手写实现可以帮你补上原理短板。
  • 准备做 NLP 工程项目的工程师:文本预处理、模型评估、误差分析等章节贴近真实项目。

如果你已经熟练使用 HuggingFace 微调各种大模型,那么这套课程的前半部分可以快速过一遍,重点看注意力机制和 Transformer 的原理推导。

2.3 推荐的学习节奏

以“边看边写代码”为原则,这里给出一份 12 周的学习计划表,你可以根据实际情况调整:

周次学习内容动手任务
第 1 周文本表示:词袋、TF-IDF用 sklearn 实现文本向量化
第 2 周朴素贝叶斯、逻辑回归实现一个垃圾邮件分类器
第 3 周词向量与 Word2Vec训练小型中文词向量并做相似度分析
第 4 周向量空间与相似度检索实现一个简单问答检索
第 5 周N-gram 语言模型用 N-gram 生成中文短句
第 6 周HMM 与词性标注完成一个简单词性标注实验
第 7 周RNN 原理与代码用 NumPy 实现前向传播
第 8 周LSTM、GRU用 Keras 完成文本分类
第 9 周Seq2Seq 模型实现日期格式转换等小任务
第 10 周注意力机制手动实现注意力得分计算
第 11 周Transformer 架构理解多头自注意力代码
第 12 周预训练模型 BERT使用 HuggingFace 微调一个分类模型

看课和写代码的时间最好保持 1:2,甚至写代码的时间更多。只看不写,两周后基本会忘掉。

3. 核心知识点拆解:从词向量到注意力

这一节把课程中最核心的内容做一次提炼,每部分都会解释原理、适用场景和常见误区。

3.1 文本表示:从词袋到词向量

计算机无法直接处理文字,所以第一步是把文本变成数字。最朴素的方式是词袋模型(Bag of Words):

  • 统计每个词在文档中出现的次数。
  • 忽略词序,只保留词频信息。
# 文件路径:demo_bow.py from sklearn.feature_extraction.text import CountVectorizer corpus = [ "我喜欢自然语言处理", "自然语言处理是人工智能的方向" ] vectorizer = CountVectorizer(token_pattern=r"(?u)\b\w+\b") X = vectorizer.fit_transform(corpus) print(vectorizer.get_feature_names_out()) print(X.toarray())

输出结果中,每一行代表一个文档,每一列代表一个词,值是该词在文档中出现的次数。

但词袋模型有两个明显问题:

  1. 高频词(如“的”“是”)会被赋予过高的权重,但它们往往没有实际区分度。
  2. 丢失了词序信息,“我喜欢你”和“你喜欢我”在词袋表示下完全相同。

为了解决第一个问题,出现了 TF-IDF。它通过“词频”乘以“逆文档频率”来降低普遍出现词的权重。为了解决第二个问题,就需要引入词向量。

3.2 Word2Vec 与词向量的核心思想

Word2Vec 的核心思想是:一个词的含义由它周围的词决定。也就是“分布假说”。

Word2Vec 有两种训练方式:

  • CBOW:用上下文词预测中心词。
  • Skip-gram:用中心词预测上下文词。

训练完成后,每个词对应一个低维稠密向量。语义相近的词在向量空间中距离更近,“国王 - 男人 + 女人 ≈ 女王”这类类比关系也往往能成立。

# 文件路径:demo_word2vec.py from gensim.models import Word2Vec sentences = [ ["自然语言处理", "是", "人工智能", "的", "重要", "方向"], ["我", "喜欢", "研究", "自然语言处理"], ["词向量", "是", "自然语言处理", "的", "基础"] ] # 按实际 gensim 版本调整参数 model = Word2Vec(sentences, vector_size=64, window=3, min_count=1, sg=1) vector = model.wv["自然语言处理"] print(vector.shape) similar_words = model.wv.most_similar("自然语言处理", topn=3) print(similar_words)

这里vector_size是词向量维度,window是上下文窗口大小,sg=1表示使用 Skip-gram 方式训练。在项目里,如果语料只有几万条,词向量效果一般不会太好;如果语料达到百万级,词向量的语义效果会明显提升。

3.3 RNN 与 LSTM:处理序列信息

词向量解决了词的表示问题,但没有解决“顺序”问题。对于“我不喜欢这家餐厅”和“这家餐厅我不喜欢”,词袋或词向量平均都无法体现否定词对情感的影响。

RNN(循环神经网络)让模型在处理当前词时,把前一个时刻的隐藏状态一起输入进来。这样模型就具备了“记忆”能力。

RNN 的数学表达可以简化为:

h_t = tanh(W_h * h_{t-1} + W_x * x_t + b)

其中h_t是当前时刻的隐藏状态,x_t是当前时刻的输入。

但标准 RNN 存在梯度消失问题,难以捕捉长距离依赖。LSTM 通过引入“门控机制”来解决这个问题:

  • 遗忘门:决定记住多少旧信息。
  • 输入门:决定把多少新信息写入记忆。
  • 输出门:决定输出多少记忆信息。

在实际项目中,LSTM 是处理序列数据的稳妥选择,但它的训练速度比 RNN 慢,而且很难并行化。这也是后来 Transformer 兴起的原因之一。

3.4 注意力机制与 Transformer

Seq2Seq 模型在编码器和解码器之间只通过一个固定长度的向量传递信息。句子太长时,这个向量装不下所有信息,效果会下降。注意力机制的核心改进是:解码每个词时,让模型“回头看”编码器输出的所有隐藏状态,并按照相关性加权求和。

注意力得分的计算通常有几种方式:

  • 点积注意力:score = q · k
  • 缩放点积注意力:score = q · k / sqrt(d_k)
  • 加性注意力:score = W * tanh(q + k)

Transformer 则更进一步,完全抛弃了循环结构,使用“自注意力”机制来处理序列。自注意力的意思是:句子中的每个词都与句子中的其他词计算相关性权重,从而直接建模全局依赖。

# 文件路径:demo_attention.py import numpy as np def scaled_dot_product_attention(query, key, value): d_k = key.shape[-1] scores = np.dot(query, key.T) / np.sqrt(d_k) weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True)) weights /= weights.sum(axis=-1, keepdims=True) return np.dot(weights, value) # 模拟三个词的向量表示 q = np.random.randn(3, 8) k = np.random.randn(3, 8) v = np.random.randn(3, 8) output = scaled_dot_product_attention(q, k, v) print(output.shape)

在吴恩达课程中,这部分会有更完整的推导。理解注意力机制之后,再学习 BERT、GPT 等预训练模型就容易很多:它们本质上都是基于 Transformer 的不同训练目标和结构变体。

4. 完整实战:从中文语料清洗到情感分类

前面讲了大量概念,下面我们把知识点串起来,完成一个完整的 NLP 项目。项目目标:训练一个中文文本情感分类模型,流程覆盖“语料准备 → 数据清洗 → 特征表示 → 模型训练 → 效果评估”。

4.1 项目结构与数据准备

先创建项目目录:

nlp_sentiment_demo/ ├── data/ │ ├── raw.txt ├── src/ │ ├── clean.py │ ├── train_traditional.py │ ├── train_lstm.py ├── requirements.txt

在requirements.txt中写入依赖:

jieba==0.42.1 scikit-learn==1.3.0 pandas==2.0.0 tensorflow==2.13.0

注意:依赖版本需要结合你的 Python 环境调整,这里以示例环境为准。如果安装的是更高版本,API 基本兼容,但个别参数可能需要微调。

为了方便演示,这里使用一段很小的示例数据,不要期待它达到生产级效果。真实项目中,数据量至少要在几万条以上。

# 文件路径:src/clean.py import re import jieba raw_texts = [ ("这个电影太精彩了,演员演技在线,强烈推荐", 1), ("剧情拖沓,看了半小时就想睡觉", 0), ("画面很美,但故事逻辑有很大问题", 0), ("非常喜欢这种轻松搞笑的风格", 1), ("一般般,没有宣传中那么好", 0), ] def clean_text(text: str) -> str: # 只保留中文字符和英文字母 text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9]", " ", str(text)) # 分词并用空格连接 words = jieba.lcut(text) return " ".join(words) def build_dataset(): texts, labels = [], [] for text, label in raw_texts: texts.append(clean_text(text)) labels.append(label) return texts, labels if __name__ == "__main__": texts, labels = build_dataset() for t, l in zip(texts, labels): print(t, "->", l)

这里做了两件事:

  • 正则清洗:去掉标点符号、特殊字符,避免噪声进入模型。
  • 中文分词:jieba 把句子切成词,方便后续向量化。

4.2 使用 TF-IDF + 朴素贝叶斯搭建基线模型

第一个模型选择逻辑回归或朴素贝叶斯。这类模型训练快、可解释性强,适合作为项目的 baseline。后续如果深度学习模型效果不理想,也可以先回到这个基线做对比。

# 文件路径:src/train_traditional.py from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.pipeline import Pipeline from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from clean import build_dataset texts, labels = build_dataset() X_train, X_test, y_train, y_test = train_test_split( texts, labels, test_size=0.2, random_state=42, stratify=labels ) pipeline = Pipeline([ ("tfidf", TfidfVectorizer(analyzer="char", ngram_range=(1, 2))), ("clf", MultinomialNB()), ]) pipeline.fit(X_train, y_train) y_pred = pipeline.predict(X_test) print(classification_report(y_test, y_pred))

这里有一个细节:analyzer="char"表示按字符级别做向量化,ngram_range=(1, 2)表示同时保留单个字符和相邻两个字符的信息。在中文短文本任务里,字符级别的 n-gram 通常比纯词级别更稳定,因为它不会受到分词错误的干扰。

4.3 使用 Embedding + LSTM 搭建深度模型

基线模型跑通之后,再用 LSTM 做一版深度模型,体验一下从“特征工程”到“自动特征学习”的转变。

# 文件路径:src/train_lstm.py from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense from sklearn.model_selection import train_test_split from clean import build_dataset texts, labels = build_dataset() X_train, X_test, y_train, y_test = train_test_split( texts, labels, test_size=0.2, random_state=42, stratify=labels ) VOCAB_SIZE = 1000 MAX_LEN = 20 EMBED_DIM = 64 tokenizer = Tokenizer(num_words=VOCAB_SIZE) tokenizer.fit_on_texts(X_train) X_train_seq = tokenizer.texts_to_sequences(X_train) X_test_seq = tokenizer.texts_to_sequences(X_test) X_train_pad = pad_sequences(X_train_seq, maxlen=MAX_LEN, padding="post") X_test_pad = pad_sequences(X_test_seq, maxlen=MAX_LEN, padding="post") model = Sequential([ Embedding(input_dim=VOCAB_SIZE, output_dim=EMBED_DIM, input_length=MAX_LEN), LSTM(units=32), Dense(units=1, activation="sigmoid") ]) model.compile( optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"] ) model.fit( X_train_pad, y_train, epochs=10, batch_size=16, validation_split=0.2, verbose=1 ) loss, acc = model.evaluate(X_test_pad, y_test, verbose=0) print(f"Test accuracy: {acc:.4f}")

这个代码里有几个容易出错的地方:

  • Tokenizer的num_words并不是“实际词典大小”,而是“保留最高频词的数量”。设置太小时,低频词会被丢弃。
  • pad_sequences的padding="post"表示不到最大长度的序列在后面补 0。
  • Embedding 层接收的是词的索引,不是词向量。索引 0 通常留给 padding,所以词典大小要预留一个位置。

4.4 模型评估与预测

评估不能只看准确率。当样本不平衡时,准确率会骗人。比如 95% 的样本都是负样本,模型全预测为负样本也能有 95% 的准确率。所以分类任务至少要同时看精确率、召回率和 F1。

# 继续在 train_lstm.py 中追加 import numpy as np probabilities = model.predict(X_test_pad) predictions = (probabilities >= 0.5).astype(int) from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score print(f"Accuracy: {accuracy_score(y_test, predictions):.4f}") print(f"Precision: {precision_score(y_test, predictions):.4f}") print(f"Recall: {recall_score(y_test, predictions):.4f}") print(f"F1: {f1_score(y_test, predictions):.4f}")

如果出现“准确率高但召回率低”的情况,通常表示模型偏向预测多数类,需要调整阈值或使用加权损失函数。

4.5 预测一条新样本

模型训练完成后,可以封装一个简单预测函数:

# 文件路径:src/predict.py import re import jieba from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.sequence import pad_sequences import pickle def preprocess_new_text(text: str) -> str: text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9]", " ", str(text)) return " ".join(jieba.lcut(text)) def predict_sentiment(model_path, tokenizer_path, text): model = load_model(model_path) with open(tokenizer_path, "rb") as f: tokenizer = pickle.load(f) clean = preprocess_new_text(text) seq = tokenizer.texts_to_sequences([clean]) padded = pad_sequences(seq, maxlen=20, padding="post") prob = model.predict(padded)[0][0] return prob if __name__ == "__main__": result = predict_sentiment( "sentiment_model.h5", "tokenizer.pkl", "电影还不错,但结局有点仓促" ) print(f"Positive probability: {result:.4f}")

这个函数展示了真实工程中常用的模型部署方式:把模型和 Tokenizer 序列化保存,预测时重新加载。注意Tokenizer必须在训练集上 fit,不能在预测时才新建。

5. 常见问题与排查思路

5.1 高频问题对照表

问题现象常见原因解决思路
中文文本向量化结果全是 0没有正确分词,或词表与文本不匹配先打印预处理后的文本,确认分词结果
模型准确率很低数据量太少 / 类别不平衡 / 数据噪声大扩充数据,检查样本分布,清洗数据
LSTM 训练很慢序列过长、Embedding 维度太高限制 max_len,减小 embedding 维度
模型过拟合数据量小、模型参数多、训练轮数多增加 dropout,减小 LSTM 隐层单元,使用早停
测试集效果好,线上效果差训练集和线上数据分布不一致采集更多线上真实数据,做领域适配
出现 out of vocabulary 词词典太小,或者使用按词切分设置更大词表,或改用字符级特征
预测结果全是某一类样本不平衡 / 阈值设置不合理调整阈值,使用 class_weight

5.2 一个典型问题排查过程

比如训练完成后,发现所有测试样本都被预测为“正向”。排查思路如下:

  1. 检查 labels 是否打错,正向和负向的 label 是否颠倒。
  2. 打印验证集上的预测概率分布,看模型是否输出接近 0.5 的值。
  3. 统计训练集中正负样本比例。如果正样本占 90%,模型很可能学成“无脑预测正类”。
  4. 在model.fit中加入class_weight,给少数类更高权重。
  5. 如果问题依旧,回到传统模型,用 TF-IDF + 逻辑回归训练,观察特征权重是否合理。

6. 最佳实践与工程建议

6.1 数据质量优先于模型复杂度

做 NLP 项目时,数据决定效果上限,模型只是逼近这个上限。课程里常见的是处理好的干净语料,但生产环境里 80% 的时间都要花在数据上。

具体来说,要重视以下几步:

  • 去重:重复样本会导致训练集和验证集重叠,评估分数虚高。
  • 清洗:统一大小写、去特殊字符、处理 emoji 和 URL。
  • 标签一致性检查:人工抽检 100 条标注,计算标注一致性。
  • 切分策略:按时间切分,或按用户切分,避免数据泄露。

6.2 构建高质量中文 NLP 语料库

中文语料库构建有几个容易踩坑的点:

  • 分词不只是“把句子切成词”。领域不同,词典差异很大。金融文本里的“多头”“空头”和日常用语含义完全不一样。
  • 不要盲目使用网上下载的停用词表,很多业务关键词会被误删。
  • 如果做实体识别,标注规范比标注数量更重要。标注不一致会直接导致模型学错。

一个常见的清洗流程是:

  1. 原始数据 → 去HTML标签 → 去URL → 去重复标点。
  2. 繁体转简体。
  3. 根据业务规则去掉无效字段。
  4. 人工抽检,记录清洗规则。
  5. 切分训练/验证/测试集。

6.3 模型选型建议

不同任务适合不同模型,不必一上来就上大模型:

场景推荐方案
短文本分类、垃圾过滤TF-IDF + 逻辑回归 / 朴素贝叶斯
中等规模数据集上的序列标注BiLSTM + CRF
大规模文本理解预训练模型微调,如 BERT 系列
长文本分类分段处理 + 模型集成,或直接使用长文本模型
低资源场景数据增强、预训练模型 + PEFT 微调

其实很多时候,用朴素贝叶斯、逻辑回归就能达到生产可用效果。直接上大模型反而会引入部署复杂度和推理成本。

6.4 训练与评估注意事项

  • 设定固定随机种子,保证实验可复现。
  • 使用验证集做模型选择,测试集只能评估一次。
  • 保存每次实验的配置和评估结果,方便回溯。
  • 深度学习模型的初始学习率非常重要。可以先跑 10 个 batch 观察 loss 变化,再调整策略。
  • 文本分类中,如果类别差异大,优先关注 macro-F1,而不是准确率。

7. 总结与下一步学习路线

这套课程学下来,你至少应该掌握以下能力:

  • 理解文本从字符到向量的完整转换过程。
  • 掌握朴素贝叶斯、逻辑回归在 NLP 中的应用。
  • 理解 RNN、LSTM 的动机和局限。
  • 理解注意力机制和 Transformer 的基本原理。
  • 能独立完成一个包含数据清洗、模型训练、评估的中文 NLP 小项目。

如果这些目标已经达成,下一步可以沿着两个方向继续深入:

  1. 预训练模型方向:学习 HuggingFace Transformers 库,掌握 BERT 微调、提示学习、LoRA 等高效微调方法。
  2. 工程落地方向:学习模型部署、推理优化、数据回流、在线学习,了解模型上线后的监控与迭代闭环。

如果还想补充理论深度,也可以配合《动手学深度学习》中的 NLP 章节一起看。吴恩达课程的代码风格偏算法实现,而工程项目更考验数据清洗和调优能力,两者结合效果最好。

NLP 是一个典型的“入门容易、精通难”的领域。跟着课程体系走一遍,再亲手完成一两个完整的实战项目,遇到问题能自己定位根因,这时候才算真正入了门。建议收藏这份笔记,学习过程中随时回来对照知识点查漏补缺。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询