NLP学习笔记02:文本表示方法——从词袋模型到 BERT

NLP学习笔记02:文本表示方法------从词袋模型到 BERT

作者:Ye Shun

日期:2026-04-15

一、前言

在自然语言处理(NLP)中,文本本身是非结构化数据,计算机并不能直接理解一句话的含义。无论是文本分类、情感分析、信息检索,还是问答系统、机器翻译和大语言模型,第一步都离不开一个核心问题:怎样把文本变成计算机可以处理的数值形式。

这就是文本表示(Text Representation)要解决的问题。

简单来说,文本表示的目标是把词、句子或文档映射为向量,让模型可以在"数值空间"中进行计算。文本表示方法的发展大致经历了这样一条路线:

  1. 传统统计方法:词袋模型、TF-IDF、N-gram
  2. 静态词向量方法:Word2Vec、GloVe、FastText
  3. 上下文感知表示:ELMo、BERT 及其变体
  4. 句向量与文档向量:Doc2Vec、Sentence-BERT、主题模型

这篇笔记会按照这条演进路径展开,帮助我们理解为什么 NLP 会从"统计特征工程"逐步走向"预训练表示学习"。

二、传统文本表示

传统方法的特点是实现简单、可解释性较强、训练成本低,但通常难以捕捉深层语义。

1. 词袋模型(Bag of Words)

词袋模型是最基础的文本表示方法之一。它把一段文本看作一个无序的词汇集合,只关心某个词是否出现、出现了几次,而不关心词语顺序和语法结构。

基本思想
  • 先构建整个语料的词汇表
  • 再统计每篇文档中每个词出现的次数
  • 最终把每篇文档表示成一个高维稀疏向量

比如有一个小语料:

python 复制代码
corpus = [
    "This is the first document.",
    "This document is the second document.",
    "And this is the third one.",
    "Is this the first document?"
]

使用 CountVectorizer 构建词袋特征:

python 复制代码
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)

print(vectorizer.get_feature_names_out())
print(X.toarray())

这里输出的每一行都对应一篇文档,每一列对应词汇表中的一个词,数值则表示词频。

优点
  • 实现简单,容易理解
  • 训练和计算速度快
  • 在小规模数据和基线任务中仍然有效
缺点
  • 忽略词序和上下文
  • 不能表达语义相似性
  • 维度通常很高,而且向量非常稀疏
  • 无法处理一词多义和同义表达

换句话说,词袋模型能回答"这个词出现了没有",却很难回答"这个词在这里是什么意思"。

2. TF-IDF

TF-IDF(Term Frequency-Inverse Document Frequency)可以看作是对词袋模型的加权改进。它不仅考虑一个词在当前文档中出现得多不多,还考虑这个词在整个语料里是否过于常见。

核心思想
  • 如果一个词在某篇文档中频繁出现,它可能很重要
  • 但如果这个词在所有文档中都频繁出现,它的区分能力就会下降

因此,TF-IDF 用下面的思想来衡量词的重要性:

  • TF:词在当前文档中出现的频率
  • IDF:词在整个语料中的稀有程度
  • TF-IDF = TF × IDF

常见公式为:

text 复制代码
TF(t, d) = 词 t 在文档 d 中出现次数 / 文档 d 总词数
IDF(t) = log(文档总数 / 包含词 t 的文档数)
TF-IDF(t, d) = TF(t, d) × IDF(t)

代码示例如下:

python 复制代码
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vectorizer = TfidfVectorizer()
X_tfidf = tfidf_vectorizer.fit_transform(corpus)

print(tfidf_vectorizer.get_feature_names_out())
print(X_tfidf.toarray())
优点
  • 比原始词频更能突出"有区分度"的词
  • 在文本分类、检索等任务中常作为强基线
  • 不需要复杂训练,使用方便
缺点
  • 本质上仍是稀疏统计特征
  • 仍然无法建模词序和上下文
  • 不能直接表示语义关系

TF-IDF 很适合"关键词重要性"场景,但不擅长深层语义理解。

3. N-gram 模型

词袋模型忽略了顺序信息,而 N-gram 模型试图部分弥补这个问题。它不只看单个词,还考虑连续的 n 个词组合。

常见类型包括:

  • Unigram:单个词
  • Bigram:两个连续词
  • Trigram:三个连续词

例如:

  • natural language
  • language processing
  • natural language processing

代码示例如下:

python 复制代码
from sklearn.feature_extraction.text import CountVectorizer

bigram_vectorizer = CountVectorizer(ngram_range=(2, 2))
X_bigram = bigram_vectorizer.fit_transform(corpus)

print(bigram_vectorizer.get_feature_names_out())
优点
  • 能捕捉局部词序信息
  • 能表示短语和固定搭配
  • 对某些分类任务和检索任务很有帮助
缺点
  • 特征维度膨胀更严重
  • 数据稀疏问题更明显
  • 仍然无法处理长距离依赖

可以把 N-gram 看成是"比词袋多了一点顺序信息",但还远远称不上真正的语义建模。

三、词向量表示

传统统计方法的局限很明显:它们通常只能表示"词出现了几次",却不能表示"词和词之间有多像"。词向量方法的出现,正是为了让词语拥有可以学习的连续分布式表示。

1. Word2Vec

Word2Vec 是 Google 在 2013 年提出的经典词向量模型。它的核心思想很简单:一个词的含义,可以从它出现的上下文中学习出来。

Word2Vec 主要有两种架构:

  • CBOW(Continuous Bag of Words):根据上下文预测中心词
  • Skip-gram:根据中心词预测上下文

代码示例如下:

python 复制代码
from gensim.models import Word2Vec

sentences = [["cat", "say", "meow"], ["dog", "say", "woof"]]
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4)

vector = model.wv["cat"]
similar_words = model.wv.most_similar("cat")
特点
  • 得到的是低维稠密向量,通常 50 到 300 维
  • 可以在一定程度上捕捉语义和语法关系
  • 支持向量运算,如 king - man + woman ≈ queen
局限
  • 每个词只有一个固定向量
  • 无法区分一词多义
  • 对上下文变化不敏感

也就是说,在 Word2Vec 中,"bank"无论出现在"river bank"还是"bank account"中,向量通常是一样的。

2. GloVe

GloVe(Global Vectors for Word Representation)是 Stanford 提出的词向量方法。它结合了局部上下文窗口和全局共现统计信息。

核心思想
  • 先统计词与词的共现矩阵
  • 再通过优化目标学习词向量
  • 希望两个词向量的点积可以反映它们共现次数的对数

可以简单理解为:

  • Word2Vec 更偏"预测式"
  • GloVe 更偏"统计式 + 表示学习"

两者比较如下:

特性 Word2Vec GloVe
训练方式 局部窗口预测 全局共现统计
计算效率 较高 较低
小数据集表现 较好 一般
大数据集表现 更好

在工程中,很多时候会直接使用预训练好的 GloVe 向量,而不是自己从头训练。

3. FastText

FastText 是 Facebook 提出的词向量模型,它在 Word2Vec 的基础上进一步考虑了子词(subword)信息。

核心特点
  • 把一个词表示为多个字符 n-gram 的组合
  • 能较好处理未登录词(OOV)
  • 对词形变化丰富的语言更友好

例如单词 apple 可以被拆成多个子串,再共同组成词向量。这意味着即便模型从未见过完整单词,也可能根据子词信息构造出一个近似向量。

代码示例如下:

python 复制代码
from gensim.models import FastText

sentences = [["cat", "say", "meow"], ["dog", "say", "woof"]]
model = FastText(sentences, vector_size=100, window=5, min_count=1, workers=4)

vector = model.wv["unseenword"]
优点
  • 更擅长处理 OOV 问题
  • 对拼写变化和词形变化更鲁棒
  • 在很多语言任务上表现稳定
局限
  • 仍然是静态词向量
  • 上下文相关性仍然有限

四、上下文感知的表示

静态词向量的最大问题在于:同一个词,不管放在什么句子里,向量都不变。但现实语言中,很多词的含义高度依赖上下文。

为了解决这个问题,NLP 开始进入"上下文表示"阶段。

1. ELMo

ELMo(Embeddings from Language Models)是较早的重要上下文相关词表示方法之一。

核心特点
  • 基于双向 LSTM 语言模型
  • 一个词的表示取决于整句上下文
  • 可以利用不同层表示不同粒度的信息

这意味着同一个词在不同句子中会得到不同的向量表示。相比 Word2Vec 这类静态词向量,ELMo 明显更接近真实语言理解。

2. BERT 及其变体

BERT(Bidirectional Encoder Representations from Transformers)是 Google 在 2018 年提出的预训练语言模型,也是现代 NLP 的重要里程碑。

关键创新
  • 使用 Transformer 架构
  • 使用双向上下文建模
  • 使用掩码语言模型(MLM)训练目标
  • 原始版本中还包含下一句预测(NSP)任务

常见变体包括:

  • RoBERTa:优化训练策略
  • DistilBERT:更轻量、更快
  • ALBERT:通过参数共享压缩模型

代码示例如下:

python 复制代码
from transformers import BertTokenizer, BertModel

tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
model = BertModel.from_pretrained("bert-base-uncased")

inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
outputs = model(**inputs)
last_hidden_states = outputs.last_hidden_state

last_hidden_state 中保存了每个 token 的上下文表示,它已经不是简单的词频或静态词向量,而是融合了整句信息的深层表示。

3. 预训练 + 微调范式

现代 NLP 很多模型的基本流程都是:

  1. 在海量通用语料上做预训练
  2. 在具体任务数据上做微调

这种范式带来的好处是:

  • 能利用大规模数据学习通用语言知识
  • 能较快迁移到下游任务
  • 大幅减少手工特征工程工作

常见模型演进可以简单概括为:

模型 发布时间 主要特点
Word2Vec 2013 静态词向量
GloVe 2014 全局统计 + 局部窗口
ELMo 2018 双向 LSTM,上下文相关
BERT 2018 Transformer,双向上下文
GPT-3 2020 大规模生成式 Transformer

从这里也能看出,文本表示的发展趋势,就是从"静态特征"不断走向"动态语义表示"。

五、文档级表示

前面讨论的大多数方法主要关注词级别表示,但在很多实际任务中,我们更关心的是整句话、整段文本甚至整篇文档的表示方式。

1. Doc2Vec

Doc2Vec 可以看作是 Word2Vec 在文档级表示上的扩展。它不只学习词向量,也学习文档本身的向量。

主要有两种形式:

  • PV-DM(Distributed Memory):类似 CBOW,引入文档 ID
  • PV-DBOW(Distributed Bag of Words):类似 Skip-gram

代码示例如下:

python 复制代码
from gensim.models import Doc2Vec
from gensim.models.doc2vec import TaggedDocument

documents = [TaggedDocument(doc.split(), [i]) for i, doc in enumerate(corpus)]
model = Doc2Vec(documents, vector_size=100, window=5, min_count=1, workers=4)

vector = model.infer_vector(["new", "document", "text"])

Doc2Vec 在早期文档分类和聚类任务中较常见,但现在很多场景会更多使用基于预训练模型的句向量或文档向量。

2. 句向量与文档向量

如果我们已经有词向量,最直接的方法就是把一个句子中所有词向量取平均,得到句向量。

常见方法包括:

  • 平均法:直接平均所有词向量
  • SIF:对高频词降权后再平均
  • BERT 句向量:使用 [CLS] 或平均 token 向量

在实践中,Sentence-BERT 是非常常见的句向量工具:

python 复制代码
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")
sentences = ["This is an example sentence", "Each sentence is converted"]
embeddings = model.encode(sentences)

句向量常用于:

  • 语义相似度计算
  • 文本检索
  • 问答匹配
  • 聚类和推荐

3. 主题模型(LDA)

LDA(Latent Dirichlet Allocation)是一种经典的无监督主题模型。虽然它不属于现代神经网络表示学习方法,但在文档级表示里依然值得了解。

基本思想
  • 一篇文档可以看成多个主题的混合
  • 每个主题可以看成若干词语的概率分布
  • 通过概率推断学习文档和主题之间的关系

代码示例如下:

python 复制代码
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)

lda = LatentDirichletAllocation(n_components=2, random_state=42)
lda.fit(X)

LDA 常见应用包括:

  • 文档聚类
  • 内容推荐
  • 主题发现
  • 摘要和分析前的探索性处理

它并不直接给出"语义向量",但可以给出"主题分布表示",这也是一种很有价值的文档表示方式。

六、如何选择合适的文本表示方法

文本表示方法并没有放之四海而皆准的"最优解",选择时通常要结合任务目标、数据规模和算力条件。

可以从下面几个角度来判断:

1. 看任务需求

  • 如果只是做简单分类、关键词统计、基线实验,词袋模型和 TF-IDF 往往已经够用
  • 如果需要一定语义信息,可以考虑 Word2Vec、FastText
  • 如果需要上下文理解、语义匹配、复杂推理,通常更适合 BERT 类模型

2. 看数据规模

  • 小数据集下,传统方法可能更稳定
  • 数据规模较大时,词向量和预训练模型优势更明显

3. 看计算资源

  • 传统方法成本最低
  • 词向量训练成本适中
  • BERT 类模型通常最吃显存和算力

4. 看语言特点

  • 对于中文,分词质量会直接影响传统表示效果
  • 对于词形变化丰富的语言,FastText 的子词信息往往更有帮助

因此,方法选择本质上是一个"效果、成本、任务需求"的平衡问题。

七、一个简明的演进总结

如果用一句话概括文本表示的发展,可以理解为:

  • 传统方法关注"这个词出现了多少次"
  • 词向量方法关注"这个词和哪些词更相似"
  • 上下文模型关注"这个词在当前句子里到底是什么意思"
  • 文档表示关注"这一整段文本整体表达了什么"

这条演进路线非常重要,因为它几乎贯穿了整个 NLP 技术发展史。

八、总结

文本表示是 NLP 中最核心的基础能力之一。没有合适的文本表示,就谈不上后续的建模和推理。

从方法演进上看:

  • 词袋模型、TF-IDF 和 N-gram 属于传统统计表示,简单高效,但语义能力有限
  • Word2Vec、GloVe 和 FastText 把文本表示推进到低维稠密向量时代
  • ELMo 和 BERT 让表示从"静态词向量"升级为"上下文相关表示"
  • Doc2Vec、Sentence-BERT 和 LDA 则进一步把表示扩展到句子和文档层面

在学习路径上,建议不要只盯着 BERT 或大模型,而忽略前面的基础方法。因为很多现代方法的改进方向,本质上都是在弥补传统表示的不足。把这些基础方法理解清楚,后面学检索增强、向量数据库、语义匹配和大语言模型时,思路会清晰很多。

相关推荐
千寻girling1 天前
机器学习 | 线性回归 | 尚硅谷学习
学习·机器学习·线性回归
风曦Kisaki1 天前
# LAMP 架构 + Discuz! 论坛实战笔记
笔记·架构
jasonblog1 天前
对小龙虾openclaw的关注、学习、使用和变化观察
人工智能·学习·ai
Hical_W1 天前
深入学习CPP17_PMR
c++·学习
xuanwenchao1 天前
ROS2学习笔记 - 1、编写运行第一个程序
笔记·学习
独小乐1 天前
018.使用I2C总线EEPROM|千篇笔记实现嵌入式全栈/裸机篇
linux·笔记·单片机·嵌入式硬件·arm·信息与通信
惠惠软件1 天前
豆包 AI 学习投喂与排名优化指南
人工智能·学习·语音识别
V搜xhliang02461 天前
OpenClaw、AI大模型赋能数据分析与学术科研 学习
人工智能·深度学习·学习·机器学习·数据挖掘·数据分析
YuanDaima20481 天前
二分查找基础原理与题目说明
开发语言·数据结构·人工智能·笔记·python·算法
里昆1 天前
【电力电子】某模拟量采集器的上位机设置和遇到的问题解决
学习