BOW
把文本看作 “一堆词的集合”,只统计词的出现频率,完全忽略词的顺序、语法、语义。CountVectorizer(纯词频)、TfidfVectorizer(带权重的词频)都属于 BOW 体系。
创建 BOW 的步骤:
- 第一步,将文本分词成句子。
- 第一步中分词的句子又进一步分词了单词。
- 删除所有停用词或标点符号。
- 将所有单词转换为小写。
- 创建词语的频率分布图。
TF
为了更好地理解文本向量化的基础逻辑,我们可以结合词袋模型(BOW)的例子辅助理解(词袋模型是早期的文本向量化方法,常被用作基础对比):
假设我们有 4 条灾难相关的推文文本:
- ‘kind true sadly’
- ‘swear jam set world ablaze’
- ‘swear true car accident’
- ‘car sadly car caught up fire’
我们可以用 scikit-learn 中的 CountVectorizer 工具实现词袋模型:它会自动对文本进行分词、构建全局词汇表,再将每条文本转换为 “词频向量”—— 向量的每一位对应词汇表中的一个词,值则是该词在当前文本中的出现次数。

最终输出的词频矩阵结构为:
- 行:对应每条文档(这里是 4 条推文);
- 列:对应全局词汇表中的所有唯一词;
- 值:对应词在当前文档中的出现次数。
所以,词袋模型的本质是构建全局词汇表 → 把每条文本映射为 “词频向量”。
类似地,我们可以将 CountVectorizer 应用到更大规模的训练数据(比如 11370 条推文),得到对应的词频矩阵,再结合目标变量训练后续的机器学习 / 深度学习模型。(CountVectorizer 处理后的词频矩阵是模型的 “输入特征”,目标变量是模型的 “学习目标”,二者结合才能完成分类 / 回归任务)
TF-IDF
TF-IDF 词频 - 逆文档频率,基于统计方法,用于查找文本中词语的相关性,文本可以是单个文档,也可以是称为语料库的各种文档。一个词在当前文档中出现得越频繁(TF 高),且在所有文档中出现得越稀少(IDF 高),这个词对当前文档的重要性 / 代表性就越高。
TF-IDF 算法可应用于解决较为简单的自然语言处理和机器学习问题,例如信息检索、停用词去除、关键词提取和基础文本分析。然而,它无法有效地捕捉词序列中的语义含义。
为了创建 TF-IDF 向量,可以使用 Scikit-learn 的 TF-IDF Vectorizer(TfidfVectorizer)。行代表每个文档,列代表词汇表,tf-idf(i,j) 的值通过上述公式计算得出。所得矩阵可与目标变量一起用于训练机器学习/深度学习模型。

词袋模型与 TF-IDF 面临的挑战
在词袋模型(BOW)中,向量的维度等于词汇表的规模 —— 若向量中大部分元素为 0,最终会形成一个稀疏矩阵。这种稀疏性会从计算效率和信息利用两方面,增加模型构建的难度。
此外,词袋模型还有两个核心缺陷:一是无法体现词与词之间的语义关联,二是完全忽略词的顺序。这些问题也让后续的词嵌入技术面临更多挑战,具体表现为:
- 参数规模过大:高维的输入向量会导致神经网络需要学习的权重参数数量剧增;
- 语义与语序缺失:既无法捕捉词之间的关联,也不考虑词在文本中的出现顺序;
- 计算成本高昂:参数规模越大,模型的训练与推理过程会消耗更多计算资源。
而 TF-IDF 模型虽然能区分词的重要程度,但并没有解决 “高维稀疏” 的核心问题;同时和词袋模型一样,它也无法捕捉词之间的语义相似性。
Word2Vec
Word2Vec 是谷歌于 2013 年提出的技术,如今已成为解决各类高级自然语言处理(NLP)任务的基础工具之一,其核心作用是训练词嵌入(Word Embedding),而技术底层则基于 “分布假设”。
在这一假设的框架下,Word2Vec 提供了两种实现模型:跳字模型(Skip-gram)与连续词袋模型(CBOW)。
这两种模型本质上是结构简单的浅层神经网络,仅包含输入层、输出层与投影层。它们的核心逻辑是:通过捕捉文本中 “历史与未来的词序信息”,来还原词语所处的语言上下文。
具体训练过程中,模型会迭代遍历大规模文本语料库,学习词语之间的关联关系 —— 其核心假设是:文本中相邻出现的词语,往往具有语义相似性。借助这一特性,Word2Vec 能将语义相近的词语,映射到向量空间中距离较近的嵌入向量上。
在衡量词向量的语义相似度时,Word2Vec 通常采用余弦相似度作为度量指标。余弦相似度的取值等于两个词(或文档)向量之间夹角的余弦值,其含义可简单理解为:
- 若余弦值为 1,说明两个词语的向量完全重合,语义高度一致;
- 若夹角为 90°(余弦值为 0),则说明两个词语在上下文语义上没有关联,彼此独立。
简言之,Word2Vec 的核心价值,就是让语义相似的词语,拥有相似的向量表示。
CBOW
词嵌入/连续词袋模型(CBOW)的核心逻辑是:通过多个上下文词作为输入,让神经网络预测中间的目标词。
CBOW 的优势是训练速度快,尤其适合为高频词学习更精准的向量表示。我们可以结合 “窗口大小” 的概念,理解 CBOW 中 “上下文” 与 “当前词” 的关系:

在 CBOW 算法中,我们会设定一个 “窗口大小”(如图中窗口大小为 5):窗口中间的词是 “当前词”,其周围(包含前后)的词则构成 “上下文”。CBOW 的任务就是利用这些上下文词,预测出中间的当前词。
具体处理时,每个词会先通过独热编码 (One Hot Encoding) 映射到词汇表对应的编码向量,再输入 CBOW 神经网络。

CBOW 的网络结构非常简洁:
- 输入层:接收上下文词的独热编码向量;
- 投影层:将所有上下文词的向量做求和(或平均)处理;
- 输出层:基于词汇表,输出 “当前词” 的概率分布(即预测当前词是什么)。 其中,投影层本质是一个全连接的密集层,负责将输入的上下文信息整合后传递给输出层。
Skip-gram
一种与 CBOW 略有不同的词嵌入技术,因为它不基于上下文预测当前词。相反,它将每个当前词作为输入,输入到一个对数线性分类器和一个连续投影层中。这样,它就能预测当前词前后一定范围内的词。
这种变体仅以一个词作为输入,然后预测与其密切相关的上下文词。这就是它能够高效表示罕见词的原因。

代码演示
Word2Vec 的两种核心变体(跳字模型与连续词袋模型),最终目标都是学习模型隐藏层的权重参数 —— 而隐藏层的输出结果,就是我们需要的词嵌入向量。
接下来,我们通过代码演示如何用 Word2Vec 构建自定义词嵌入。
- 依赖库
Import Libraries
from gensim.models import Word2Vec
import nltk
import re
from nltk.corpus import stopwords
- 文本预处理 Word2Vec 的输入需要是 “已分词的文本语料”,因此我们先对原始文本做预处理:
#Word2Vec inputs a corpus of documents split into constituent words.
corpus = []
for i in range(0,len(X)):
tweet = re.sub(“[^a-zA-Z]”,” “,X[i])
tweet = tweet.lower()
tweet = tweet.split()
corpus.append(tweet)
训练完成后,我们可以通过 “查找相似词” 来验证词嵌入的效果(相似词的向量距离更近):
# 查找与“disaster(灾难)”语义最相似的词
model.wv.most_similar('disaster')
执行上述代码后,会得到与 “disaster” 相似的词列表(包含词与对应的相似度):
[
('alert', 0.9997072219848633),
('military', 0.999704270553589),
('murder', 0.9996907501220703),
('ash', 0.9996492862701416),
('base', 0.9996055364687655),
('oil', 0.9996046423912048),
('along', 0.99959397315979),
('based', 0.9995934963226318),
('inundation', 0.9995468854904175),
('west', 0.9995380640029907)
]
以 “灾难” 对应的词嵌入向量为例,其输出是一个 100 维的稠密向量(维度由模型训练时的 vector_size 参数指定):
array([-0.5968882, -0.33868956, -0.32643065, ..., 0.68051434, 0.7790246, 0.5617202], dtype=float32)
- 完整代码
# ===================== 第一步:导入依赖库 =====================
from gensim.models import Word2Vec
import nltk
import re
import pandas as pd
from nltk.corpus import stopwords
# 下载nltk停用词(首次运行需要,后续可注释)
nltk.download('stopwords')
# ===================== 第二步:准备指定的示例数据 =====================
# 替换为你指定的4条文本
data = {
"text": [
'kind true sadly',
'swear jam set world ablaze',
'swear true car accident',
'car sadly car caught up fire'
],
"label": [0, 0, 1, 1] # 模拟标签:0=非灾难,1=灾难相关(仅示例)
}
df = pd.DataFrame(data)
X = df["text"].values # 文本数据
y = df["label"].values # 目标变量(训练Word2Vec无需标签,仅用于后续参考)
# ===================== 第三步:文本预处理 =====================
# 初始化停用词表(英文)
stop_words = set(stopwords.words('english'))
corpus = [] # 存储预处理后的分词语料
for i in range(len(X)):
# 1. 移除非字母字符(保留空格)
tweet = re.sub("[^a-zA-Z]", " ", X[i])
# 2. 统一转为小写
tweet = tweet.lower()
# 3. 按空格分词
tweet = tweet.split()
# 4. 过滤停用词(当前示例无停用词,可保留逻辑)
tweet = [word for word in tweet if not word in stop_words]
# 5. 加入语料库
corpus.append(tweet)
# 打印预处理结果,验证效果
print("=== 预处理后的分词语料 ===")
for i, doc in enumerate(corpus):
print(f"文本{i+1}:{doc}")
# ===================== 第四步:训练Word2Vec模型 =====================
# 初始化并训练Word2Vec
model = Word2Vec(
sentences=corpus, # 输入语料(分词后的列表)
vector_size=100, # 词嵌入向量维度
window=3, # 适配短文本,缩小窗口大小(原5→3)
min_count=1, # 最小词频:保留所有词(示例文本词频低)
sg=0, # 模型类型:0=CBOW,1=Skip-gram
hs=0, # 分层softmax:0=关闭
negative=5, # 负采样数量
workers=4, # 线程数
epochs=100 # 训练轮数
)
# 保存模型(可选)
model.save("custom_word2vec.model")
# ===================== 第五步:验证词嵌入效果 =====================
print("\n=== 词嵌入效果验证 ===")
# 1. 查找与"accident"(事故)语义相似的词
similar_words = model.wv.most_similar('accident', topn=5)
print(f"\n与'accident'最相似的词:")
for word, similarity in similar_words:
print(f" {word}: {similarity:.4f}")
# 2. 获取单个词的嵌入向量(如"fire")
print(f"\n'fire'的词嵌入向量(前10维):")
fire_vec = model.wv["fire"]
print(fire_vec[:10]) # 打印前10维,完整向量是100维
# 3. 计算两个词的语义相似度(如"car"和"accident")
similarity = model.wv.similarity('car', 'accident')
print(f"\n'car'与'accident'的相似度:{similarity:.4f}")
# 4. 额外验证:"sadly"和"fire"的相似度(灾难相关关联)
similarity_sadly_fire = model.wv.similarity('sadly', 'fire')
print(f"'sadly'与'fire'的相似度:{similarity_sadly_fire:.4f}")
# ===================== 第六步:生成文档向量(用于后续模型训练) =====================
# 方法:将文本中所有词的向量取平均,得到文档级向量
def get_doc_vector(text_seg, model):
"""
将分词后的文本转换为文档向量
:param text_seg: 分词后的文本列表
:param model: 训练好的Word2Vec模型
:return: 文档向量(100维)
"""
import numpy as np
vec_list = [model.wv[word] for word in text_seg if word in model.wv.key_to_index]
if len(vec_list) == 0:
return np.zeros(100) # 无有效词时返回全0向量
return np.mean(vec_list, axis=0)
# 生成所有文本的文档向量
import numpy as np
doc_vectors = np.array([get_doc_vector(seg, model) for seg in corpus])
print(f"\n文档向量形状:{doc_vectors.shape}") # (4, 100):4个文档,每个100维
print(f"第4条文本(car sadly car caught up fire)的文档向量(前10维):")
print(doc_vectors[3][:10])
GloVe
GloVe 词嵌入方法由 Pennington 等人在斯坦福大学开发,用于自然语言处理。它被称为全局向量,因为该模型直接捕获了全局语料库的统计信息。GloVe 在单词类比和命名实体识别问题上表现出色。
该技术由于采用了更简单的最小二乘代价函数或误差函数,降低了模型训练的计算成本,从而生成了不同且更优的词嵌入。它利用了局部上下文窗口方法(例如 Mikolov 的 skip-gram 模型)和全局矩阵分解方法来生成低维词表示。
潜在语义分析(LSA)是一种基于全局矩阵分解的方法,虽在单词类比任务中表现一般,但它利用统计信息来构建向量结构的思路具有参考性;而 Skip-gram 方法在类比任务中表现更好,却因未利用全局统计数据,无法充分捕捉词的关联信息。
与 Word2Vec(基于局部上下文构建词嵌入)不同,GloVe 聚焦全局上下文生成词嵌入,这是它与 Word2Vec 的核心差异 —— 在 GloVe 中,词与词的语义关系是通过共现矩阵来体现的。
以这两个句子为例:
- I am a data science enthusiast
- I am looking for a data science job
GloVe 会基于 “窗口大小 = 1”(单词连续出现的次数),构建如下共现矩阵:
矩阵中的值代表 “对应行的词出现在对应列的词的上下文中的次数”。若语料包含 100 万个不同单词,共现矩阵的规模会达到 100 万 ×100 万。
GloVe 的核心思路是:单词的共现信息,是学习词表示的关键依据。
我们再看斯坦福大学 GloVe 论文中的经典示例:以目标词 “ice” 和 “steam” 为例,统计它们与词汇表中其他词的共现概率,以下是来自一个包含 60 亿词的语料库的一些实际概率:

这里的核心公式是:
- 代表“词 出现在词 的上下文中的概率”。
- :共现矩阵中词 与词 的共现次数,即:
- :词 的上下文中出现的所有词的总次数,数学表达式为: 假设 k = solid,即与 ice 相关但与 steam 无关的词。预期 Pik / Pjk 比值会很大。类似地,对于与 steam 相关但与 ice 无关的词 k,例如 k = gas,该比值会很小。对于像 water 或 fashion 这样的词,它们要么分别与 ice 和 steam 都相关,要么都与两者都无关,该比值应该接近于 1。
概率比值比原始概率更能区分相关词(例如 solid 和 gas)和不相关词(例如 fashion 和 water)。它也能更好地区分两个相关词。因此,在 GloVe 中,词向量学习的出发点是共现概率的比值,而不是概率本身。
理论就讲到这里,现在开始写代码!
# ===================== 1. 导入所需库 =====================
# nltk:自然语言处理基础库,用于文本预处理
import nltk
# re:正则表达式库,用于文本清洗(移除非字母字符)
import re
# stopwords:nltk内置的停用词库(如the/a/an等无意义词汇)
from nltk.corpus import stopwords
# Glove相关库:Corpus用于构建语料库,Glove用于训练词嵌入
from glove import Corpus, Glove
# ===================== 2. 文本预处理(GloVe输入要求:分词后的文档列表) =====================
# 初始化空列表,用于存储预处理后的分词语料
corpus = []
# 遍历所有文本数据(X为原始文本列表,如推文/句子集合)
for i in range(0, len(X)):
# 步骤1:移除文本中所有非字母字符(仅保留字母和空格),例如将"storm!!123"转为"storm "
tweet = re.sub("[^a-zA-Z]", " ", X[i])
# 步骤2:将所有字符转为小写,统一格式(如"Storm"→"storm")
tweet = tweet.lower()
# 步骤3:按空格分词,将字符串转为单词列表(如"heavy storm"→["heavy", "storm"])
tweet = tweet.split()
# 步骤4:将预处理后的分词列表加入语料库(若需过滤停用词,可取消下方注释)
# tweet = [word for word in tweet if not word in set(stopwords.words('english'))]
corpus.append(tweet)
# ===================== 3. 训练GloVe词嵌入 =====================
# 初始化Corpus对象,用于构建GloVe所需的共现矩阵
corpus_model = Corpus()
# 构建语料库的共现矩阵(核心步骤)
# window=5:上下文窗口大小,即每个词的前后各5个词视为上下文
corpus_model.fit(corpus, window=5) # 修正原代码:text_corpus→corpus(变量名统一)
# 初始化GloVe模型
# no_components=100:词嵌入向量的维度(常用100/200/300)
# learning_rate=0.05:学习率,控制模型参数更新步长
glove = Glove(no_components=100, learning_rate=0.05)
# 训练GloVe模型(基于共现矩阵)
# corpus.matrix:构建好的共现矩阵
# epochs=100:训练轮数,迭代次数越多拟合效果越好(但易过拟合)
# no_threads=4:训练使用的线程数(根据CPU核心数调整)
# verbose=True:打印训练过程(如每轮损失值)
glove.fit(corpus_model.matrix, epochs=100, no_threads=4, verbose=True)
# 将语料库的词汇字典添加到GloVe模型中(关联单词与索引,方便后续查询)
glove.add_dictionary(corpus_model.dictionary)
# ===================== 4. 验证词嵌入效果:查找相似词 =====================
# 查找与"storm"语义最相似的10个词(返回词+相似度)
# number=10:指定返回相似词的数量
similar_words = glove.most_similar("storm", number=10)
# 打印结果,直观查看相似词
print("与'storm'最相似的10个词:")
for word, similarity in similar_words:
print(f" {word}: {similarity:.4f}")
单词元组及其预测概率列表:

BERT
BERT 是一种基于 Transformer 模型的自然语言处理(NLP)算法,包含两个核心变体:参数规模为 1.1 亿的 BERT-Base,以及参数达 3.4 亿的 BERT-Large。
它的核心优势是依赖注意力机制,生成与上下文强关联的高质量词嵌入:在训练过程中,输入会逐层经过 BERT 的网络结构,而每一层的注意力机制都能同时捕捉 “当前词左右两侧的语义关联”,这让它的词表示更贴合文本的实际语境。
相比词袋、Word2Vec 等传统方法,BERT 是更先进的技术 —— 它基于维基百科、海量语料预训练得到通用模型,再通过 “微调” 就能适配特定任务的数据集,因此生成的词嵌入效果更优。目前它在语言翻译等 NLP 任务中应用广泛。
它在语言翻译任务中有着广泛的应用。

总结
词嵌入技术是训练 GRU、LSTM、Transformer 等深度学习模型的基础,这些模型在情感分类、命名实体识别、语音识别等 NLP 任务中都取得了显著效果。
各类文本表示技术的典型应用场景总结如下:
- 词袋模型:文本特征初步提取
- TF-IDF:信息检索、关键词提取
- Word2Vec:语义分析类任务
- GloVe:词语类比、命名实体识别
- BERT:语言翻译、问答系统